blob: ab8b589f34380abb441fc4a9794be1b41dc205eb [file] [log] [blame]
Edric Milaret029b95a2015-06-09 09:51:44 -04001/***************************************************************************
Anthony LĂ©onard2fde81d2017-04-17 10:06:55 -04002 * Copyright (C) 2015-2017 by Savoir-faire Linux *
Edric Milaret029b95a2015-06-09 09:51:44 -04003 * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 3 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 **************************************************************************/
18
19#include "videoview.h"
20#include "ui_videoview.h"
21
Andreas Traczykb8b13ba2018-08-21 16:30:16 -040022#include "utils.h"
23#include "lrcinstance.h"
24
Edric Milaret029b95a2015-06-09 09:51:44 -040025#include <QGraphicsOpacityEffect>
26#include <QPropertyAnimation>
27#include <QDesktopWidget>
28#include <QMenu>
Edric Milaret303bbf12015-06-22 14:24:13 -040029#include <QFileDialog>
30#include <QMimeData>
Nicolas Jagere0854512016-02-29 12:08:10 -050031#include <QSplitter>
Edricb09982a2016-05-19 16:28:38 -040032#include <QScreen>
Edric Milaret029b95a2015-06-09 09:51:44 -040033
34#include <memory>
35
36#include "videooverlay.h"
37#include "selectareadialog.h"
38
Edric Milaret80e0b212015-10-16 10:07:43 -040039VideoView::VideoView(QWidget* parent) :
Edric Milaret029b95a2015-06-09 09:51:44 -040040 QWidget(parent),
41 ui(new Ui::VideoView)
42{
43 ui->setupUi(this);
44
Edric Milaret029b95a2015-06-09 09:51:44 -040045 overlay_ = new VideoOverlay(this);
46 auto effect = new QGraphicsOpacityEffect(overlay_);
Andreas Traczyk8d5e5492018-08-09 15:41:52 -040047 effect->setOpacity(maxOverlayOpacity_);
Edric Milaret029b95a2015-06-09 09:51:44 -040048 overlay_->setGraphicsEffect(effect);
49 fadeAnim_ = new QPropertyAnimation(this);
50 fadeAnim_->setTargetObject(effect);
51 fadeAnim_->setPropertyName("opacity");
52 fadeAnim_->setDuration(fadeOverlayTime_);
53 fadeAnim_->setStartValue(effect->opacity());
54 fadeAnim_->setEndValue(0);
55 fadeAnim_->setEasingCurve(QEasingCurve::OutQuad);
Andreas Traczykb8b13ba2018-08-21 16:30:16 -040056
Andreas Traczykca320d82018-08-09 18:02:07 -040057 // Setup the timer to start the fade when the mouse stops moving
58 this->setMouseTracking(true);
59 overlay_->setMouseTracking(true);
60 fadeTimer_.setSingleShot(true);
61 connect(&fadeTimer_, SIGNAL(timeout()), this, SLOT(fadeOverlayOut()));
Edric Milaret029b95a2015-06-09 09:51:44 -040062
Edric Milaret029b95a2015-06-09 09:51:44 -040063 this->setContextMenuPolicy(Qt::CustomContextMenu);
64 connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
65 this, SLOT(showContextMenu(const QPoint&)));
Edric Milaret3aca8e32015-06-12 10:01:40 -040066 connect(overlay_, &VideoOverlay::setChatVisibility, [=](bool visible) {
67 emit this->setChatVisibility(visible);
68 });
Olivier SOLDANO2bcdfd72017-05-02 14:37:19 -040069 connect(overlay_, &VideoOverlay::videoCfgBtnClicked, [=](){emit videoSettingsClicked();});
Isa Nanicca027ab2018-12-19 16:54:05 -050070
Edric Milaret029b95a2015-06-09 09:51:44 -040071}
72
73VideoView::~VideoView()
74{
75 delete ui;
Edric Milaret01f23842015-06-22 14:46:01 -040076 delete overlay_;
Edric Milaret01f23842015-06-22 14:46:01 -040077 delete fadeAnim_;
Edric Milaret029b95a2015-06-09 09:51:44 -040078}
79
80void
Edric Milaret80e0b212015-10-16 10:07:43 -040081VideoView::resizeEvent(QResizeEvent* event)
Edric Milaret029b95a2015-06-09 09:51:44 -040082{
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -050083 QRect& previewRect = ui->videoWidget->getPreviewRect();
84 int deltaW = event->size().width() - event->oldSize().width();
85 int deltaH = event->size().height() - event->oldSize().height();
86
87 QPoint previewCenter = ui->videoWidget->getPreviewRect().center();
Edricb09982a2016-05-19 16:28:38 -040088 int cx = (event->oldSize().width()) / 2;
89 int cy = (event->oldSize().height()) / 2;
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -050090 QPoint center = QPoint(cx, cy);
91
92 // first we check if we want to displace the preview
93 if (previewRect.x() + deltaW > 0 && previewRect.y() + deltaH > 0) {
94 // then we check which way
95 if (center.x() - previewCenter.x() < 0 && center.y() - previewCenter.y() < 0)
96 ui->videoWidget->getPreviewRect().translate(deltaW, deltaH);
97 else if (center.x() - previewCenter.x() > 0 && center.y() - previewCenter.y() < 0)
98 ui->videoWidget->getPreviewRect().translate(0, deltaH);
99 else if (center.x() - previewCenter.x() < 0 && center.y() - previewCenter.y() > 0)
100 ui->videoWidget->getPreviewRect().translate(deltaW, 0);
101 }
102
103 if (previewRect.left() <= 0)
104 previewRect.moveLeft(1);
105
106 if (previewRect.right() >= width())
107 previewRect.moveRight(width() - 1);
108
109 if (previewRect.top() <= 0)
110 previewRect.moveTop(1);
111
112 if (previewRect.bottom() >= height())
113 previewRect.moveBottom(height() - 1);
114
Edric Milaret029b95a2015-06-09 09:51:44 -0400115 overlay_->resize(this->size());
116 overlay_->show();
117 overlay_->raise();
118}
119
120void
121VideoView::enterEvent(QEvent* event)
122{
123 Q_UNUSED(event)
Andreas Traczykca320d82018-08-09 18:02:07 -0400124 showOverlay();
Edric Milaret029b95a2015-06-09 09:51:44 -0400125}
126
127void
128VideoView::leaveEvent(QEvent* event)
129{
130 Q_UNUSED(event)
Andreas Traczykca320d82018-08-09 18:02:07 -0400131 fadeOverlayOut();
132}
133
134void
135VideoView::showOverlay()
136{
137 fadeAnim_->stop();
138 fadeAnim_->targetObject()->setProperty(fadeAnim_->propertyName(), fadeAnim_->startValue());
139}
140
141void
142VideoView::fadeOverlayOut()
143{
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400144 if (!overlay_->isDialogVisible() && !overlay_->shouldShowOverlay()) {
Nicolas Jager0a9fc602016-03-11 18:35:42 -0500145 fadeAnim_->start(QAbstractAnimation::KeepWhenStopped);
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400146 }
Edric Milaret029b95a2015-06-09 09:51:44 -0400147}
148
149void
Andreas Traczyk7cc79c02019-01-18 12:48:16 -0500150VideoView::slotCallStatusChanged(const std::string& callId)
Edric Milaret029b95a2015-06-09 09:51:44 -0400151{
Andreas Traczyk7cc79c02019-01-18 12:48:16 -0500152 using namespace lrc::api::call;
153 auto call = LRCInstance::getCurrentCallModel()->getCall(callId);
154 switch (call.status) {
155 case Status::IN_PROGRESS:
156 {
Edric Milaret029b95a2015-06-09 09:51:44 -0400157 ui->videoWidget->show();
Andreas Traczyk7cc79c02019-01-18 12:48:16 -0500158 auto convInfo = Utils::getConversationFromCallId(call.id);
159 if (!convInfo.uid.empty()) {
160 auto contactInfo = LRCInstance::getCurrentAccountInfo().contactModel->getContact(convInfo.participants[0]);
161 auto contactName = Utils::bestNameForContact(contactInfo);
162 overlay_->setName(QString::fromStdString(contactName));
SĂ©bastien Blin93bd2062018-12-17 15:57:16 -0500163 }
Andreas Traczyk7cc79c02019-01-18 12:48:16 -0500164 return;
Edric Milaret029b95a2015-06-09 09:51:44 -0400165 }
Andreas Traczyk7cc79c02019-01-18 12:48:16 -0500166 default:
167 emit closing(call.id);
168 break;
169 }
170 QObject::disconnect(timerConnection_);
Edric Milaret029b95a2015-06-09 09:51:44 -0400171}
172
173void
SĂ©bastien Blin93bd2062018-12-17 15:57:16 -0500174VideoView::showChatviewIfToggled()
175{
176 emit setChatVisibility(overlay_->getShowChatView());
177}
178
179void
Andreas Traczyk4b4bfde2018-12-19 09:53:22 -0500180VideoView::simulateShowChatview(bool checked)
181{
Andreas Traczyk14c3e862018-12-26 14:02:30 -0500182 Q_UNUSED(checked);
Andreas Traczyk4b4bfde2018-12-19 09:53:22 -0500183 overlay_->simulateShowChatview(true);
184}
185
186void
Edric Milaret029b95a2015-06-09 09:51:44 -0400187VideoView::mouseDoubleClickEvent(QMouseEvent* e) {
188 QWidget::mouseDoubleClickEvent(e);
189 toggleFullScreen();
190}
191
Edric Milaret80e0b212015-10-16 10:07:43 -0400192void
193VideoView::dragEnterEvent(QDragEnterEvent* event)
Edric Milaret303bbf12015-06-22 14:24:13 -0400194{
195 if (event->mimeData()->hasUrls())
196 event->acceptProposedAction();
197}
198
Edric Milaret80e0b212015-10-16 10:07:43 -0400199void
200VideoView::dropEvent(QDropEvent* event)
Edric Milaret303bbf12015-06-22 14:24:13 -0400201{
202 auto urls = event->mimeData()->urls();
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400203 auto selectedConvUid = LRCInstance::getSelectedConvUid();
204 auto convModel = LRCInstance::getCurrentConversationModel();
205 auto conversation = Utils::getConversationFromUid(selectedConvUid, *convModel);
206 auto callList = CallModel::instance().getActiveCalls();
207 Call* thisCall = nullptr;
208 for (auto call : callList) {
209 if (call->historyId() == QString::fromStdString(conversation->callId)) {
210 thisCall = call;
211 break;
212 }
213 }
214 if (thisCall) {
215 if (auto outVideo = thisCall->firstMedia<media::Video>(media::Media::Direction::OUT)) {
Edric Milaret0e1074d2015-12-08 12:08:52 -0500216 outVideo->sourceModel()->setFile(urls.at(0));
217 }
218 }
Edric Milaret303bbf12015-06-22 14:24:13 -0400219}
220
Edric Milaret029b95a2015-06-09 09:51:44 -0400221void
222VideoView::toggleFullScreen()
223{
SĂ©bastien Blin93bd2062018-12-17 15:57:16 -0500224 emit toggleFullScreenClicked();
Edric Milaret029b95a2015-06-09 09:51:44 -0400225}
226
227void
228VideoView::showContextMenu(const QPoint& pos)
229{
230 QPoint globalPos = this->mapToGlobal(pos);
231
232 QMenu menu;
Andreas Traczyke86fb3c2018-08-02 17:38:34 -0400233 media::Video* outVideo = nullptr;
Edric Milaretc4b1a102016-02-02 11:48:30 -0500234 int activeIndex = -1;
235
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400236 auto selectedConvUid = LRCInstance::getSelectedConvUid();
237 auto convModel = LRCInstance::getCurrentConversationModel();
238 auto conversation = Utils::getConversationFromUid(selectedConvUid, *convModel);
239 auto callList = CallModel::instance().getActiveCalls();
240 Call* thisCall = nullptr;
241 for (auto call : callList) {
242 if (call->historyId() == QString::fromStdString(conversation->callId)) {
243 thisCall = call;
244 break;
245 }
246 }
247 if (thisCall) {
248 outVideo = thisCall->firstMedia<media::Video>(media::Media::Direction::OUT);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500249 if (outVideo)
250 activeIndex = outVideo->sourceModel()->activeIndex();
251 }
Edric Milaret029b95a2015-06-09 09:51:44 -0400252
Edric Milareta3e47282015-10-23 15:20:30 -0400253 for (auto device : Video::DeviceModel::instance().devices()) {
Edric Milaret029b95a2015-06-09 09:51:44 -0400254 std::unique_ptr<QAction> deviceAction(new QAction(device->name(), this));
255 deviceAction->setCheckable(true);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500256 if (outVideo)
257 if (outVideo->sourceModel()->getDeviceIndex(device) == activeIndex)
258 deviceAction->setChecked(true);
Edric Milaret029b95a2015-06-09 09:51:44 -0400259 auto ptr = deviceAction.release();
260 menu.addAction(ptr);
261 connect(ptr, &QAction::toggled, [=](bool checked) {
262 if (checked == true) {
Edric Milaretc4b1a102016-02-02 11:48:30 -0500263 if (outVideo)
264 outVideo->sourceModel()->switchTo(device);
Edric Milareta3e47282015-10-23 15:20:30 -0400265 Video::DeviceModel::instance().setActive(device);
Edric Milaret029b95a2015-06-09 09:51:44 -0400266 }
267 });
268 }
269
270 menu.addSeparator();
271
Edric Milaret53ac6e52015-09-14 13:37:06 -0400272 auto shareAction = new QAction(tr("Share entire screen"), this);
Edric Milaret029b95a2015-06-09 09:51:44 -0400273 menu.addAction(shareAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500274 shareAction->setCheckable(true);
Edric Milaret53ac6e52015-09-14 13:37:06 -0400275 auto shareAreaAction = new QAction(tr("Share screen area"), this);
Edric Milaret029b95a2015-06-09 09:51:44 -0400276 menu.addAction(shareAreaAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500277 shareAreaAction->setCheckable(true);
Edric Milaret029b95a2015-06-09 09:51:44 -0400278 connect(shareAreaAction, &QAction::triggered, [=]() {
279 SelectAreaDialog selec;
280 selec.exec();
281 });
Edric Milaret53ac6e52015-09-14 13:37:06 -0400282 auto shareFileAction = new QAction(tr("Share file"), this);
Edric Milaret303bbf12015-06-22 14:24:13 -0400283 menu.addAction(shareFileAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500284 shareFileAction->setCheckable(true);
285
286 switch(activeIndex) {
287
288 case Video::SourceModel::ExtendedDeviceList::SCREEN:
289 shareAction->setChecked(true);
290 break;
291 case Video::SourceModel::ExtendedDeviceList::FILE:
292 shareFileAction->setChecked(true);
293 break;
294
295 }
296
297 connect(shareAction, &QAction::triggered, [=]() {
Edricb09982a2016-05-19 16:28:38 -0400298 if (outVideo) {
299 auto realRect = QApplication::desktop()->geometry();
300 realRect.setWidth(static_cast<int>(realRect.width() * QApplication::primaryScreen()->devicePixelRatio()));
301 realRect.setHeight(static_cast<int>(realRect.height() * QApplication::primaryScreen()->devicePixelRatio()));
302 outVideo->sourceModel()->setDisplay(0, realRect);
303 }
Edric Milaretc4b1a102016-02-02 11:48:30 -0500304 });
Edric Milaret303bbf12015-06-22 14:24:13 -0400305 connect(shareFileAction, &QAction::triggered, [=]() {
306 QFileDialog dialog(this);
307 dialog.setFileMode(QFileDialog::AnyFile);
308 QStringList fileNames;
309 if (!dialog.exec())
310 return;
311 fileNames = dialog.selectedFiles();
Edric Milaretc4b1a102016-02-02 11:48:30 -0500312 if (outVideo)
313 outVideo->sourceModel()->setFile(QUrl::fromLocalFile(fileNames.at(0)));
Edric Milaret303bbf12015-06-22 14:24:13 -0400314 });
Edric Milaret029b95a2015-06-09 09:51:44 -0400315
316 menu.exec(globalPos);
317}
318
Edric Milaret80e0b212015-10-16 10:07:43 -0400319void
Andreas Traczyka493d5b2019-01-09 20:14:29 -0500320VideoView::pushRenderer(const std::string& callId) {
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400321 auto callModel = LRCInstance::getCurrentCallModel();
322
323 QObject::disconnect(videoStartedConnection_);
Andreas Traczyk7cc79c02019-01-18 12:48:16 -0500324 QObject::disconnect(callStatusChangedConnection_);
325
Andreas Traczyka493d5b2019-01-09 20:14:29 -0500326 if (!callModel->hasCall(callId)) {
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400327 return;
328 }
329
Andreas Traczyka493d5b2019-01-09 20:14:29 -0500330 auto call = callModel->getCall(callId);
331
332 this->overlay_->callStarted(callId);
333 this->overlay_->setVideoMuteVisibility(!LRCInstance::getCurrentCallModel()->getCall(callId).isAudioOnly);
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400334
Andreas Traczyk7cc79c02019-01-18 12:48:16 -0500335 callStatusChangedConnection_ = QObject::connect(callModel, &lrc::api::NewCallModel::callStatusChanged,
336 this, &VideoView::slotCallStatusChanged);
337
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400338 videoStartedConnection_ = QObject::connect(callModel, &lrc::api::NewCallModel::remotePreviewStarted,
339 [this](const std::string& callId, Video::Renderer* renderer) {
340 Q_UNUSED(callId);
341 slotVideoStarted(renderer);
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400342 });
343 ui->videoWidget->setPreviewDisplay(call.type != lrc::api::call::Type::CONFERENCE);
344}
345
346void
Edric Milaret80e0b212015-10-16 10:07:43 -0400347VideoView::slotVideoStarted(Video::Renderer* renderer) {
348 ui->videoWidget->show();
349 ui->videoWidget->setDistantRenderer(renderer);
350}
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500351
352void
353VideoView::mousePressEvent(QMouseEvent* event)
354{
355 QPoint clickPosition = event->pos();
356 if (ui->videoWidget->getPreviewRect().contains(clickPosition)) {
357 QLine distance = QLine(clickPosition, ui->videoWidget->getPreviewRect().bottomRight());
358 if (distance.dy() < resizeGrip_ and distance.dx() < resizeGrip_) {
359 QApplication::setOverrideCursor(Qt::SizeFDiagCursor);
360 resizingPreview_ = true;
361 } else {
362 originMouseDisplacement_ = event->pos() - ui->videoWidget->getPreviewRect().topLeft();
363 QApplication::setOverrideCursor(Qt::SizeAllCursor);
364 draggingPreview_ = true;
365 }
366 }
367}
368
369void
370VideoView::mouseReleaseEvent(QMouseEvent* event)
371{
Edric Milaret6a785af2016-03-07 15:39:30 -0500372 Q_UNUSED(event)
373
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500374 draggingPreview_ = false;
375 resizingPreview_ = false;
376 QApplication::setOverrideCursor(Qt::ArrowCursor);
377}
378
379void
380VideoView::mouseMoveEvent(QMouseEvent* event)
381{
Andreas Traczykca320d82018-08-09 18:02:07 -0400382 // start/restart the timer after which the overlay will fade
383 if (fadeTimer_.isActive()) {
384 showOverlay();
385 } else {
386 fadeTimer_.start(startfadeOverlayTime_);
387 }
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400388
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500389 QRect& previewRect = ui->videoWidget->getPreviewRect();
390 if (draggingPreview_) {
391 if (previewRect.left() > 0
392 && previewRect.top() > 0
393 && previewRect.right() < width()
394 && previewRect.bottom() < height()) {
395
396 previewRect.moveTo(event->pos() - originMouseDisplacement_);
397 if (previewRect.left() <= 0)
398 previewRect.moveLeft(1);
399
400 if (previewRect.right() >= width())
401 previewRect.moveRight(width() - 1);
402
403 if (previewRect.top() <= 0)
404 previewRect.moveTop(1);
405
406 if (previewRect.bottom() >= height())
407 previewRect.moveBottom(height() - 1);
408 }
409 }
410
411 QLine distance = QLine(previewRect.topLeft(), event->pos());
412
413 if (resizingPreview_
414 and distance.dx() > minimalSize_
415 and distance.dy() > minimalSize_
416 and geometry().contains(event->pos()))
417 previewRect.setBottomRight(event->pos());
418}