blob: 46d069f9de20ffda9416472840a25abaeed9cd4d [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
Edric Milaret029b95a2015-06-09 09:51:44 -040022#include "video/devicemodel.h"
23#include "video/sourcemodel.h"
Edric Milareted0b2802015-10-01 15:10:02 -040024#include "recentmodel.h"
Edric Milaret0e1074d2015-12-08 12:08:52 -050025#include "media/video.h"
Edric Milaret029b95a2015-06-09 09:51:44 -040026
27#include <QGraphicsOpacityEffect>
28#include <QPropertyAnimation>
29#include <QDesktopWidget>
30#include <QMenu>
Edric Milaret303bbf12015-06-22 14:24:13 -040031#include <QFileDialog>
32#include <QMimeData>
Nicolas Jagere0854512016-02-29 12:08:10 -050033#include <QSplitter>
Edricb09982a2016-05-19 16:28:38 -040034#include <QScreen>
Edric Milaret029b95a2015-06-09 09:51:44 -040035
36#include <memory>
37
38#include "videooverlay.h"
39#include "selectareadialog.h"
40
Edric Milaret80e0b212015-10-16 10:07:43 -040041VideoView::VideoView(QWidget* parent) :
Edric Milaret029b95a2015-06-09 09:51:44 -040042 QWidget(parent),
43 ui(new Ui::VideoView)
44{
45 ui->setupUi(this);
46
Edric Milareta3e47282015-10-23 15:20:30 -040047 connect(&CallModel::instance(), SIGNAL(callStateChanged(Call*, Call::State)),
Edric Milaret029b95a2015-06-09 09:51:44 -040048 this, SLOT(callStateChanged(Call*, Call::State)));
49
50 overlay_ = new VideoOverlay(this);
51 auto effect = new QGraphicsOpacityEffect(overlay_);
52 effect->setOpacity(1.0);
53 overlay_->setGraphicsEffect(effect);
54 fadeAnim_ = new QPropertyAnimation(this);
55 fadeAnim_->setTargetObject(effect);
56 fadeAnim_->setPropertyName("opacity");
57 fadeAnim_->setDuration(fadeOverlayTime_);
58 fadeAnim_->setStartValue(effect->opacity());
59 fadeAnim_->setEndValue(0);
60 fadeAnim_->setEasingCurve(QEasingCurve::OutQuad);
61
Edric Milaret029b95a2015-06-09 09:51:44 -040062 this->setContextMenuPolicy(Qt::CustomContextMenu);
63 connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
64 this, SLOT(showContextMenu(const QPoint&)));
Edric Milaret3aca8e32015-06-12 10:01:40 -040065 connect(overlay_, &VideoOverlay::setChatVisibility, [=](bool visible) {
66 emit this->setChatVisibility(visible);
67 });
Olivier SOLDANO2bcdfd72017-05-02 14:37:19 -040068 connect(overlay_, &VideoOverlay::videoCfgBtnClicked, [=](){emit videoSettingsClicked();});
69 connect(overlay_, &VideoOverlay::videoBackBtnClicked, [=](){emit videoBackClicked();});
Edric Milaret029b95a2015-06-09 09:51:44 -040070}
71
72VideoView::~VideoView()
73{
74 delete ui;
Edric Milaret01f23842015-06-22 14:46:01 -040075 delete overlay_;
Edric Milaret01f23842015-06-22 14:46:01 -040076 delete fadeAnim_;
Edric Milaret029b95a2015-06-09 09:51:44 -040077}
78
79void
Edric Milaret80e0b212015-10-16 10:07:43 -040080VideoView::resizeEvent(QResizeEvent* event)
Edric Milaret029b95a2015-06-09 09:51:44 -040081{
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -050082 QRect& previewRect = ui->videoWidget->getPreviewRect();
83 int deltaW = event->size().width() - event->oldSize().width();
84 int deltaH = event->size().height() - event->oldSize().height();
85
86 QPoint previewCenter = ui->videoWidget->getPreviewRect().center();
Edricb09982a2016-05-19 16:28:38 -040087 int cx = (event->oldSize().width()) / 2;
88 int cy = (event->oldSize().height()) / 2;
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -050089 QPoint center = QPoint(cx, cy);
90
91 // first we check if we want to displace the preview
92 if (previewRect.x() + deltaW > 0 && previewRect.y() + deltaH > 0) {
93 // then we check which way
94 if (center.x() - previewCenter.x() < 0 && center.y() - previewCenter.y() < 0)
95 ui->videoWidget->getPreviewRect().translate(deltaW, deltaH);
96 else if (center.x() - previewCenter.x() > 0 && center.y() - previewCenter.y() < 0)
97 ui->videoWidget->getPreviewRect().translate(0, deltaH);
98 else if (center.x() - previewCenter.x() < 0 && center.y() - previewCenter.y() > 0)
99 ui->videoWidget->getPreviewRect().translate(deltaW, 0);
100 }
101
102 if (previewRect.left() <= 0)
103 previewRect.moveLeft(1);
104
105 if (previewRect.right() >= width())
106 previewRect.moveRight(width() - 1);
107
108 if (previewRect.top() <= 0)
109 previewRect.moveTop(1);
110
111 if (previewRect.bottom() >= height())
112 previewRect.moveBottom(height() - 1);
113
Edric Milaret029b95a2015-06-09 09:51:44 -0400114 overlay_->resize(this->size());
115 overlay_->show();
116 overlay_->raise();
117}
118
119void
120VideoView::enterEvent(QEvent* event)
121{
122 Q_UNUSED(event)
123 fadeAnim_->stop();
124 fadeAnim_->targetObject()->setProperty(fadeAnim_->propertyName(), fadeAnim_->startValue());
125}
126
127void
128VideoView::leaveEvent(QEvent* event)
129{
130 Q_UNUSED(event)
Nicolas Jager0a9fc602016-03-11 18:35:42 -0500131 if(not overlay_->isDialogVisible())
132 fadeAnim_->start(QAbstractAnimation::KeepWhenStopped);
Edric Milaret029b95a2015-06-09 09:51:44 -0400133}
134
135void
136VideoView::callStateChanged(Call* call, Call::State previousState)
137{
138 Q_UNUSED(previousState)
Edric Milaret80e0b212015-10-16 10:07:43 -0400139
Edric Milaret029b95a2015-06-09 09:51:44 -0400140 if (call->state() == Call::State::CURRENT) {
141 ui->videoWidget->show();
Edric Milareted0b2802015-10-01 15:10:02 -0400142 timerConnection_ = connect(call, SIGNAL(changed()), this, SLOT(updateCall()));
Edric Milaret029b95a2015-06-09 09:51:44 -0400143 }
144 else {
Edric Milaret80e0b212015-10-16 10:07:43 -0400145 QObject::disconnect(timerConnection_);
Edric Milaret3aca8e32015-06-12 10:01:40 -0400146 emit setChatVisibility(false);
Edric Milaret029b95a2015-06-09 09:51:44 -0400147 if (isFullScreen())
148 toggleFullScreen();
Edric Milaret029b95a2015-06-09 09:51:44 -0400149 }
150}
151
152void
Edric Milareted0b2802015-10-01 15:10:02 -0400153VideoView::updateCall()
Edric Milaret029b95a2015-06-09 09:51:44 -0400154{
Edric Milaret80e0b212015-10-16 10:07:43 -0400155 if (auto call = CallModel::instance().selectedCall()) {
156 overlay_->setName(call->formattedName());
157 overlay_->setTime(call->length());
158 }
Edric Milaret029b95a2015-06-09 09:51:44 -0400159}
160
161void
162VideoView::mouseDoubleClickEvent(QMouseEvent* e) {
163 QWidget::mouseDoubleClickEvent(e);
164 toggleFullScreen();
165}
166
Edric Milaret80e0b212015-10-16 10:07:43 -0400167void
168VideoView::dragEnterEvent(QDragEnterEvent* event)
Edric Milaret303bbf12015-06-22 14:24:13 -0400169{
170 if (event->mimeData()->hasUrls())
171 event->acceptProposedAction();
172}
173
Edric Milaret80e0b212015-10-16 10:07:43 -0400174void
175VideoView::dropEvent(QDropEvent* event)
Edric Milaret303bbf12015-06-22 14:24:13 -0400176{
177 auto urls = event->mimeData()->urls();
Edric Milaret0e1074d2015-12-08 12:08:52 -0500178 if (auto call = CallModel::instance().selectedCall()) {
179 if (auto outVideo = call->firstMedia<Media::Video>(Media::Media::Direction::OUT)) {
180 outVideo->sourceModel()->setFile(urls.at(0));
181 }
182 }
Edric Milaret303bbf12015-06-22 14:24:13 -0400183}
184
Edric Milaret029b95a2015-06-09 09:51:44 -0400185void
186VideoView::toggleFullScreen()
187{
188 if(isFullScreen()) {
Nicolas Jagere0854512016-02-29 12:08:10 -0500189 dynamic_cast<QSplitter*>(oldParent_)->insertWidget(0,this);
Edric Milaret029b95a2015-06-09 09:51:44 -0400190 this->resize(oldSize_.width(), oldSize_.height());
Edric Milaret58f57622015-12-09 09:57:15 -0500191 this->showNormal();
Edric Milaret029b95a2015-06-09 09:51:44 -0400192 } else {
193 oldSize_ = this->size();
194 oldParent_ = static_cast<QWidget*>(this->parent());
195 this->setParent(0);
196 this->showFullScreen();
Edric Milaret029b95a2015-06-09 09:51:44 -0400197 }
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500198 ui->videoWidget->setResetPreview(true);
Edric Milaret029b95a2015-06-09 09:51:44 -0400199}
200
201void
202VideoView::showContextMenu(const QPoint& pos)
203{
204 QPoint globalPos = this->mapToGlobal(pos);
205
206 QMenu menu;
Edric Milaretc4b1a102016-02-02 11:48:30 -0500207 Media::Video* outVideo = nullptr;
208 int activeIndex = -1;
209
210 if (auto call = CallModel::instance().selectedCall()) {
211 outVideo = call->firstMedia<Media::Video>(Media::Media::Direction::OUT);
212 if (outVideo)
213 activeIndex = outVideo->sourceModel()->activeIndex();
214 }
Edric Milaret029b95a2015-06-09 09:51:44 -0400215
Edric Milareta3e47282015-10-23 15:20:30 -0400216 for (auto device : Video::DeviceModel::instance().devices()) {
Edric Milaret029b95a2015-06-09 09:51:44 -0400217 std::unique_ptr<QAction> deviceAction(new QAction(device->name(), this));
218 deviceAction->setCheckable(true);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500219 if (outVideo)
220 if (outVideo->sourceModel()->getDeviceIndex(device) == activeIndex)
221 deviceAction->setChecked(true);
Edric Milaret029b95a2015-06-09 09:51:44 -0400222 auto ptr = deviceAction.release();
223 menu.addAction(ptr);
224 connect(ptr, &QAction::toggled, [=](bool checked) {
225 if (checked == true) {
Edric Milaretc4b1a102016-02-02 11:48:30 -0500226 if (outVideo)
227 outVideo->sourceModel()->switchTo(device);
Edric Milareta3e47282015-10-23 15:20:30 -0400228 Video::DeviceModel::instance().setActive(device);
Edric Milaret029b95a2015-06-09 09:51:44 -0400229 }
230 });
231 }
232
233 menu.addSeparator();
234
Edric Milaret53ac6e52015-09-14 13:37:06 -0400235 auto shareAction = new QAction(tr("Share entire screen"), this);
Edric Milaret029b95a2015-06-09 09:51:44 -0400236 menu.addAction(shareAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500237 shareAction->setCheckable(true);
Edric Milaret53ac6e52015-09-14 13:37:06 -0400238 auto shareAreaAction = new QAction(tr("Share screen area"), this);
Edric Milaret029b95a2015-06-09 09:51:44 -0400239 menu.addAction(shareAreaAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500240 shareAreaAction->setCheckable(true);
Edric Milaret029b95a2015-06-09 09:51:44 -0400241 connect(shareAreaAction, &QAction::triggered, [=]() {
242 SelectAreaDialog selec;
243 selec.exec();
244 });
Edric Milaret53ac6e52015-09-14 13:37:06 -0400245 auto shareFileAction = new QAction(tr("Share file"), this);
Edric Milaret303bbf12015-06-22 14:24:13 -0400246 menu.addAction(shareFileAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500247 shareFileAction->setCheckable(true);
248
249 switch(activeIndex) {
250
251 case Video::SourceModel::ExtendedDeviceList::SCREEN:
252 shareAction->setChecked(true);
253 break;
254 case Video::SourceModel::ExtendedDeviceList::FILE:
255 shareFileAction->setChecked(true);
256 break;
257
258 }
259
260 connect(shareAction, &QAction::triggered, [=]() {
Edricb09982a2016-05-19 16:28:38 -0400261 if (outVideo) {
262 auto realRect = QApplication::desktop()->geometry();
263 realRect.setWidth(static_cast<int>(realRect.width() * QApplication::primaryScreen()->devicePixelRatio()));
264 realRect.setHeight(static_cast<int>(realRect.height() * QApplication::primaryScreen()->devicePixelRatio()));
265 outVideo->sourceModel()->setDisplay(0, realRect);
266 }
Edric Milaretc4b1a102016-02-02 11:48:30 -0500267 });
Edric Milaret303bbf12015-06-22 14:24:13 -0400268 connect(shareFileAction, &QAction::triggered, [=]() {
269 QFileDialog dialog(this);
270 dialog.setFileMode(QFileDialog::AnyFile);
271 QStringList fileNames;
272 if (!dialog.exec())
273 return;
274 fileNames = dialog.selectedFiles();
Edric Milaretc4b1a102016-02-02 11:48:30 -0500275 if (outVideo)
276 outVideo->sourceModel()->setFile(QUrl::fromLocalFile(fileNames.at(0)));
Edric Milaret303bbf12015-06-22 14:24:13 -0400277 });
Edric Milaret029b95a2015-06-09 09:51:44 -0400278
279 menu.exec(globalPos);
280}
281
Edric Milaret80e0b212015-10-16 10:07:43 -0400282void
283VideoView::pushRenderer(Call* call) {
284 if (not call) {
285 disconnect(videoStartedConnection_);
286 return;
287 }
288 if (auto renderer = call->videoRenderer()) {
289 slotVideoStarted(renderer);
290 } else {
291 disconnect(videoStartedConnection_);
292 videoStartedConnection_ = connect(call,
293 SIGNAL(videoStarted(Video::Renderer*)),
294 this,
295 SLOT(slotVideoStarted(Video::Renderer*)));
296 }
297 ui->videoWidget->setPreviewDisplay(call->type() != Call::Type::CONFERENCE);
298}
299
300void
301VideoView::slotVideoStarted(Video::Renderer* renderer) {
302 ui->videoWidget->show();
303 ui->videoWidget->setDistantRenderer(renderer);
304}
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500305
306void
307VideoView::mousePressEvent(QMouseEvent* event)
308{
309 QPoint clickPosition = event->pos();
310 if (ui->videoWidget->getPreviewRect().contains(clickPosition)) {
311 QLine distance = QLine(clickPosition, ui->videoWidget->getPreviewRect().bottomRight());
312 if (distance.dy() < resizeGrip_ and distance.dx() < resizeGrip_) {
313 QApplication::setOverrideCursor(Qt::SizeFDiagCursor);
314 resizingPreview_ = true;
315 } else {
316 originMouseDisplacement_ = event->pos() - ui->videoWidget->getPreviewRect().topLeft();
317 QApplication::setOverrideCursor(Qt::SizeAllCursor);
318 draggingPreview_ = true;
319 }
320 }
321}
322
323void
324VideoView::mouseReleaseEvent(QMouseEvent* event)
325{
Edric Milaret6a785af2016-03-07 15:39:30 -0500326 Q_UNUSED(event)
327
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500328 draggingPreview_ = false;
329 resizingPreview_ = false;
330 QApplication::setOverrideCursor(Qt::ArrowCursor);
331}
332
333void
334VideoView::mouseMoveEvent(QMouseEvent* event)
335{
336 QRect& previewRect = ui->videoWidget->getPreviewRect();
337 if (draggingPreview_) {
338 if (previewRect.left() > 0
339 && previewRect.top() > 0
340 && previewRect.right() < width()
341 && previewRect.bottom() < height()) {
342
343 previewRect.moveTo(event->pos() - originMouseDisplacement_);
344 if (previewRect.left() <= 0)
345 previewRect.moveLeft(1);
346
347 if (previewRect.right() >= width())
348 previewRect.moveRight(width() - 1);
349
350 if (previewRect.top() <= 0)
351 previewRect.moveTop(1);
352
353 if (previewRect.bottom() >= height())
354 previewRect.moveBottom(height() - 1);
355 }
356 }
357
358 QLine distance = QLine(previewRect.topLeft(), event->pos());
359
360 if (resizingPreview_
361 and distance.dx() > minimalSize_
362 and distance.dy() > minimalSize_
363 and geometry().contains(event->pos()))
364 previewRect.setBottomRight(event->pos());
365}