blob: b461e61f928a81b5fd1465622b5e654dddec7abf [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 Milareta3e47282015-10-23 15:20:30 -040045 connect(&CallModel::instance(), SIGNAL(callStateChanged(Call*, Call::State)),
Edric Milaret029b95a2015-06-09 09:51:44 -040046 this, SLOT(callStateChanged(Call*, Call::State)));
Andreas Traczykb8b13ba2018-08-21 16:30:16 -040047
Edric Milaret029b95a2015-06-09 09:51:44 -040048 overlay_ = new VideoOverlay(this);
49 auto effect = new QGraphicsOpacityEffect(overlay_);
Andreas Traczyk8d5e5492018-08-09 15:41:52 -040050 effect->setOpacity(maxOverlayOpacity_);
Edric Milaret029b95a2015-06-09 09:51:44 -040051 overlay_->setGraphicsEffect(effect);
52 fadeAnim_ = new QPropertyAnimation(this);
53 fadeAnim_->setTargetObject(effect);
54 fadeAnim_->setPropertyName("opacity");
55 fadeAnim_->setDuration(fadeOverlayTime_);
56 fadeAnim_->setStartValue(effect->opacity());
57 fadeAnim_->setEndValue(0);
58 fadeAnim_->setEasingCurve(QEasingCurve::OutQuad);
Andreas Traczykb8b13ba2018-08-21 16:30:16 -040059
Andreas Traczykca320d82018-08-09 18:02:07 -040060 // Setup the timer to start the fade when the mouse stops moving
61 this->setMouseTracking(true);
62 overlay_->setMouseTracking(true);
63 fadeTimer_.setSingleShot(true);
64 connect(&fadeTimer_, SIGNAL(timeout()), this, SLOT(fadeOverlayOut()));
Edric Milaret029b95a2015-06-09 09:51:44 -040065
Edric Milaret029b95a2015-06-09 09:51:44 -040066 this->setContextMenuPolicy(Qt::CustomContextMenu);
67 connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
68 this, SLOT(showContextMenu(const QPoint&)));
Edric Milaret3aca8e32015-06-12 10:01:40 -040069 connect(overlay_, &VideoOverlay::setChatVisibility, [=](bool visible) {
70 emit this->setChatVisibility(visible);
71 });
Olivier SOLDANO2bcdfd72017-05-02 14:37:19 -040072 connect(overlay_, &VideoOverlay::videoCfgBtnClicked, [=](){emit videoSettingsClicked();});
Edric Milaret029b95a2015-06-09 09:51:44 -040073}
74
75VideoView::~VideoView()
76{
77 delete ui;
Edric Milaret01f23842015-06-22 14:46:01 -040078 delete overlay_;
Edric Milaret01f23842015-06-22 14:46:01 -040079 delete fadeAnim_;
Edric Milaret029b95a2015-06-09 09:51:44 -040080}
81
82void
Edric Milaret80e0b212015-10-16 10:07:43 -040083VideoView::resizeEvent(QResizeEvent* event)
Edric Milaret029b95a2015-06-09 09:51:44 -040084{
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -050085 QRect& previewRect = ui->videoWidget->getPreviewRect();
86 int deltaW = event->size().width() - event->oldSize().width();
87 int deltaH = event->size().height() - event->oldSize().height();
88
89 QPoint previewCenter = ui->videoWidget->getPreviewRect().center();
Edricb09982a2016-05-19 16:28:38 -040090 int cx = (event->oldSize().width()) / 2;
91 int cy = (event->oldSize().height()) / 2;
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -050092 QPoint center = QPoint(cx, cy);
93
94 // first we check if we want to displace the preview
95 if (previewRect.x() + deltaW > 0 && previewRect.y() + deltaH > 0) {
96 // then we check which way
97 if (center.x() - previewCenter.x() < 0 && center.y() - previewCenter.y() < 0)
98 ui->videoWidget->getPreviewRect().translate(deltaW, deltaH);
99 else if (center.x() - previewCenter.x() > 0 && center.y() - previewCenter.y() < 0)
100 ui->videoWidget->getPreviewRect().translate(0, deltaH);
101 else if (center.x() - previewCenter.x() < 0 && center.y() - previewCenter.y() > 0)
102 ui->videoWidget->getPreviewRect().translate(deltaW, 0);
103 }
104
105 if (previewRect.left() <= 0)
106 previewRect.moveLeft(1);
107
108 if (previewRect.right() >= width())
109 previewRect.moveRight(width() - 1);
110
111 if (previewRect.top() <= 0)
112 previewRect.moveTop(1);
113
114 if (previewRect.bottom() >= height())
115 previewRect.moveBottom(height() - 1);
116
Edric Milaret029b95a2015-06-09 09:51:44 -0400117 overlay_->resize(this->size());
118 overlay_->show();
119 overlay_->raise();
120}
121
122void
123VideoView::enterEvent(QEvent* event)
124{
125 Q_UNUSED(event)
Andreas Traczykca320d82018-08-09 18:02:07 -0400126 showOverlay();
Edric Milaret029b95a2015-06-09 09:51:44 -0400127}
128
129void
130VideoView::leaveEvent(QEvent* event)
131{
132 Q_UNUSED(event)
Andreas Traczykca320d82018-08-09 18:02:07 -0400133 fadeOverlayOut();
134}
135
136void
137VideoView::showOverlay()
138{
139 fadeAnim_->stop();
140 fadeAnim_->targetObject()->setProperty(fadeAnim_->propertyName(), fadeAnim_->startValue());
141}
142
143void
144VideoView::fadeOverlayOut()
145{
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400146 if (!overlay_->isDialogVisible() && !overlay_->shouldShowOverlay()) {
Nicolas Jager0a9fc602016-03-11 18:35:42 -0500147 fadeAnim_->start(QAbstractAnimation::KeepWhenStopped);
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400148 }
Edric Milaret029b95a2015-06-09 09:51:44 -0400149}
150
151void
152VideoView::callStateChanged(Call* call, Call::State previousState)
153{
154 Q_UNUSED(previousState)
Edric Milaret80e0b212015-10-16 10:07:43 -0400155
Edric Milaret029b95a2015-06-09 09:51:44 -0400156 if (call->state() == Call::State::CURRENT) {
157 ui->videoWidget->show();
Edric Milareted0b2802015-10-01 15:10:02 -0400158 timerConnection_ = connect(call, SIGNAL(changed()), this, SLOT(updateCall()));
Edric Milaret029b95a2015-06-09 09:51:44 -0400159 }
160 else {
Edric Milaret80e0b212015-10-16 10:07:43 -0400161 QObject::disconnect(timerConnection_);
Edric Milaret3aca8e32015-06-12 10:01:40 -0400162 emit setChatVisibility(false);
Edric Milaret029b95a2015-06-09 09:51:44 -0400163 if (isFullScreen())
164 toggleFullScreen();
Edric Milaret029b95a2015-06-09 09:51:44 -0400165 }
166}
167
168void
Edric Milareted0b2802015-10-01 15:10:02 -0400169VideoView::updateCall()
Edric Milaret029b95a2015-06-09 09:51:44 -0400170{
Edric Milaret80e0b212015-10-16 10:07:43 -0400171 if (auto call = CallModel::instance().selectedCall()) {
172 overlay_->setName(call->formattedName());
173 overlay_->setTime(call->length());
174 }
Edric Milaret029b95a2015-06-09 09:51:44 -0400175}
176
177void
178VideoView::mouseDoubleClickEvent(QMouseEvent* e) {
179 QWidget::mouseDoubleClickEvent(e);
180 toggleFullScreen();
181}
182
Edric Milaret80e0b212015-10-16 10:07:43 -0400183void
184VideoView::dragEnterEvent(QDragEnterEvent* event)
Edric Milaret303bbf12015-06-22 14:24:13 -0400185{
186 if (event->mimeData()->hasUrls())
187 event->acceptProposedAction();
188}
189
Edric Milaret80e0b212015-10-16 10:07:43 -0400190void
191VideoView::dropEvent(QDropEvent* event)
Edric Milaret303bbf12015-06-22 14:24:13 -0400192{
193 auto urls = event->mimeData()->urls();
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400194 auto selectedConvUid = LRCInstance::getSelectedConvUid();
195 auto convModel = LRCInstance::getCurrentConversationModel();
196 auto conversation = Utils::getConversationFromUid(selectedConvUid, *convModel);
197 auto callList = CallModel::instance().getActiveCalls();
198 Call* thisCall = nullptr;
199 for (auto call : callList) {
200 if (call->historyId() == QString::fromStdString(conversation->callId)) {
201 thisCall = call;
202 break;
203 }
204 }
205 if (thisCall) {
206 if (auto outVideo = thisCall->firstMedia<media::Video>(media::Media::Direction::OUT)) {
Edric Milaret0e1074d2015-12-08 12:08:52 -0500207 outVideo->sourceModel()->setFile(urls.at(0));
208 }
209 }
Edric Milaret303bbf12015-06-22 14:24:13 -0400210}
211
Edric Milaret029b95a2015-06-09 09:51:44 -0400212void
213VideoView::toggleFullScreen()
214{
Andreas Traczyk43c08232018-10-31 13:42:09 -0400215 qDebug() << "toggle FS";
216 /*overlay_->toggleContextButtons(isFullScreen());
Edric Milaret029b95a2015-06-09 09:51:44 -0400217 if(isFullScreen()) {
Nicolas Jagere0854512016-02-29 12:08:10 -0500218 dynamic_cast<QSplitter*>(oldParent_)->insertWidget(0,this);
Edric Milaret029b95a2015-06-09 09:51:44 -0400219 this->resize(oldSize_.width(), oldSize_.height());
Edric Milaret58f57622015-12-09 09:57:15 -0500220 this->showNormal();
Edric Milaret029b95a2015-06-09 09:51:44 -0400221 } else {
222 oldSize_ = this->size();
223 oldParent_ = static_cast<QWidget*>(this->parent());
224 this->setParent(0);
225 this->showFullScreen();
Edric Milaret029b95a2015-06-09 09:51:44 -0400226 }
Andreas Traczyk43c08232018-10-31 13:42:09 -0400227 ui->videoWidget->setResetPreview(true);*/
Edric Milaret029b95a2015-06-09 09:51:44 -0400228}
229
230void
231VideoView::showContextMenu(const QPoint& pos)
232{
233 QPoint globalPos = this->mapToGlobal(pos);
234
235 QMenu menu;
Andreas Traczyke86fb3c2018-08-02 17:38:34 -0400236 media::Video* outVideo = nullptr;
Edric Milaretc4b1a102016-02-02 11:48:30 -0500237 int activeIndex = -1;
238
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400239 auto selectedConvUid = LRCInstance::getSelectedConvUid();
240 auto convModel = LRCInstance::getCurrentConversationModel();
241 auto conversation = Utils::getConversationFromUid(selectedConvUid, *convModel);
242 auto callList = CallModel::instance().getActiveCalls();
243 Call* thisCall = nullptr;
244 for (auto call : callList) {
245 if (call->historyId() == QString::fromStdString(conversation->callId)) {
246 thisCall = call;
247 break;
248 }
249 }
250 if (thisCall) {
251 outVideo = thisCall->firstMedia<media::Video>(media::Media::Direction::OUT);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500252 if (outVideo)
253 activeIndex = outVideo->sourceModel()->activeIndex();
254 }
Edric Milaret029b95a2015-06-09 09:51:44 -0400255
Edric Milareta3e47282015-10-23 15:20:30 -0400256 for (auto device : Video::DeviceModel::instance().devices()) {
Edric Milaret029b95a2015-06-09 09:51:44 -0400257 std::unique_ptr<QAction> deviceAction(new QAction(device->name(), this));
258 deviceAction->setCheckable(true);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500259 if (outVideo)
260 if (outVideo->sourceModel()->getDeviceIndex(device) == activeIndex)
261 deviceAction->setChecked(true);
Edric Milaret029b95a2015-06-09 09:51:44 -0400262 auto ptr = deviceAction.release();
263 menu.addAction(ptr);
264 connect(ptr, &QAction::toggled, [=](bool checked) {
265 if (checked == true) {
Edric Milaretc4b1a102016-02-02 11:48:30 -0500266 if (outVideo)
267 outVideo->sourceModel()->switchTo(device);
Edric Milareta3e47282015-10-23 15:20:30 -0400268 Video::DeviceModel::instance().setActive(device);
Edric Milaret029b95a2015-06-09 09:51:44 -0400269 }
270 });
271 }
272
273 menu.addSeparator();
274
Edric Milaret53ac6e52015-09-14 13:37:06 -0400275 auto shareAction = new QAction(tr("Share entire screen"), this);
Edric Milaret029b95a2015-06-09 09:51:44 -0400276 menu.addAction(shareAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500277 shareAction->setCheckable(true);
Edric Milaret53ac6e52015-09-14 13:37:06 -0400278 auto shareAreaAction = new QAction(tr("Share screen area"), this);
Edric Milaret029b95a2015-06-09 09:51:44 -0400279 menu.addAction(shareAreaAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500280 shareAreaAction->setCheckable(true);
Edric Milaret029b95a2015-06-09 09:51:44 -0400281 connect(shareAreaAction, &QAction::triggered, [=]() {
282 SelectAreaDialog selec;
283 selec.exec();
284 });
Edric Milaret53ac6e52015-09-14 13:37:06 -0400285 auto shareFileAction = new QAction(tr("Share file"), this);
Edric Milaret303bbf12015-06-22 14:24:13 -0400286 menu.addAction(shareFileAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500287 shareFileAction->setCheckable(true);
288
289 switch(activeIndex) {
290
291 case Video::SourceModel::ExtendedDeviceList::SCREEN:
292 shareAction->setChecked(true);
293 break;
294 case Video::SourceModel::ExtendedDeviceList::FILE:
295 shareFileAction->setChecked(true);
296 break;
297
298 }
299
300 connect(shareAction, &QAction::triggered, [=]() {
Edricb09982a2016-05-19 16:28:38 -0400301 if (outVideo) {
302 auto realRect = QApplication::desktop()->geometry();
303 realRect.setWidth(static_cast<int>(realRect.width() * QApplication::primaryScreen()->devicePixelRatio()));
304 realRect.setHeight(static_cast<int>(realRect.height() * QApplication::primaryScreen()->devicePixelRatio()));
305 outVideo->sourceModel()->setDisplay(0, realRect);
306 }
Edric Milaretc4b1a102016-02-02 11:48:30 -0500307 });
Edric Milaret303bbf12015-06-22 14:24:13 -0400308 connect(shareFileAction, &QAction::triggered, [=]() {
309 QFileDialog dialog(this);
310 dialog.setFileMode(QFileDialog::AnyFile);
311 QStringList fileNames;
312 if (!dialog.exec())
313 return;
314 fileNames = dialog.selectedFiles();
Edric Milaretc4b1a102016-02-02 11:48:30 -0500315 if (outVideo)
316 outVideo->sourceModel()->setFile(QUrl::fromLocalFile(fileNames.at(0)));
Edric Milaret303bbf12015-06-22 14:24:13 -0400317 });
Edric Milaret029b95a2015-06-09 09:51:44 -0400318
319 menu.exec(globalPos);
320}
321
Edric Milaret80e0b212015-10-16 10:07:43 -0400322void
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400323VideoView::pushRenderer(const std::string& callUid) {
324 auto callModel = LRCInstance::getCurrentCallModel();
325
326 QObject::disconnect(videoStartedConnection_);
327 if (!callModel->hasCall(callUid)) {
328 return;
329 }
330
331 auto call = callModel->getCall(callUid);
332
333 videoStartedConnection_ = QObject::connect(callModel, &lrc::api::NewCallModel::remotePreviewStarted,
334 [this](const std::string& callId, Video::Renderer* renderer) {
335 Q_UNUSED(callId);
336 slotVideoStarted(renderer);
Andreas Traczyk7548e732018-12-12 10:47:21 -0500337 this->overlay_->setVideoMuteVisibility(!LRCInstance::getCurrentCallModel()->getCall(callId).isAudioOnly);
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400338 });
339 ui->videoWidget->setPreviewDisplay(call.type != lrc::api::call::Type::CONFERENCE);
340}
341
342void
Edric Milaret80e0b212015-10-16 10:07:43 -0400343VideoView::slotVideoStarted(Video::Renderer* renderer) {
344 ui->videoWidget->show();
345 ui->videoWidget->setDistantRenderer(renderer);
346}
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500347
348void
349VideoView::mousePressEvent(QMouseEvent* event)
350{
351 QPoint clickPosition = event->pos();
352 if (ui->videoWidget->getPreviewRect().contains(clickPosition)) {
353 QLine distance = QLine(clickPosition, ui->videoWidget->getPreviewRect().bottomRight());
354 if (distance.dy() < resizeGrip_ and distance.dx() < resizeGrip_) {
355 QApplication::setOverrideCursor(Qt::SizeFDiagCursor);
356 resizingPreview_ = true;
357 } else {
358 originMouseDisplacement_ = event->pos() - ui->videoWidget->getPreviewRect().topLeft();
359 QApplication::setOverrideCursor(Qt::SizeAllCursor);
360 draggingPreview_ = true;
361 }
362 }
363}
364
365void
366VideoView::mouseReleaseEvent(QMouseEvent* event)
367{
Edric Milaret6a785af2016-03-07 15:39:30 -0500368 Q_UNUSED(event)
369
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500370 draggingPreview_ = false;
371 resizingPreview_ = false;
372 QApplication::setOverrideCursor(Qt::ArrowCursor);
373}
374
375void
376VideoView::mouseMoveEvent(QMouseEvent* event)
377{
Andreas Traczykca320d82018-08-09 18:02:07 -0400378 // start/restart the timer after which the overlay will fade
379 if (fadeTimer_.isActive()) {
380 showOverlay();
381 } else {
382 fadeTimer_.start(startfadeOverlayTime_);
383 }
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400384
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500385 QRect& previewRect = ui->videoWidget->getPreviewRect();
386 if (draggingPreview_) {
387 if (previewRect.left() > 0
388 && previewRect.top() > 0
389 && previewRect.right() < width()
390 && previewRect.bottom() < height()) {
391
392 previewRect.moveTo(event->pos() - originMouseDisplacement_);
393 if (previewRect.left() <= 0)
394 previewRect.moveLeft(1);
395
396 if (previewRect.right() >= width())
397 previewRect.moveRight(width() - 1);
398
399 if (previewRect.top() <= 0)
400 previewRect.moveTop(1);
401
402 if (previewRect.bottom() >= height())
403 previewRect.moveBottom(height() - 1);
404 }
405 }
406
407 QLine distance = QLine(previewRect.topLeft(), event->pos());
408
409 if (resizingPreview_
410 and distance.dx() > minimalSize_
411 and distance.dy() > minimalSize_
412 and geometry().contains(event->pos()))
413 previewRect.setBottomRight(event->pos());
414}