blob: d33e5163741f22aa0b88e040037ee209736573f4 [file] [log] [blame]
Edric Milaret029b95a2015-06-09 09:51:44 -04001/***************************************************************************
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -05002 * Copyright (C) 2015-2016 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>
Edric Milaret029b95a2015-06-09 09:51:44 -040034
35#include <memory>
36
37#include "videooverlay.h"
38#include "selectareadialog.h"
39
Edric Milaret80e0b212015-10-16 10:07:43 -040040VideoView::VideoView(QWidget* parent) :
Edric Milaret029b95a2015-06-09 09:51:44 -040041 QWidget(parent),
42 ui(new Ui::VideoView)
43{
44 ui->setupUi(this);
45
Edric Milareta3e47282015-10-23 15:20:30 -040046 connect(&CallModel::instance(), SIGNAL(callStateChanged(Call*, Call::State)),
Edric Milaret029b95a2015-06-09 09:51:44 -040047 this, SLOT(callStateChanged(Call*, Call::State)));
48
49 overlay_ = new VideoOverlay(this);
50 auto effect = new QGraphicsOpacityEffect(overlay_);
51 effect->setOpacity(1.0);
52 overlay_->setGraphicsEffect(effect);
53 fadeAnim_ = new QPropertyAnimation(this);
54 fadeAnim_->setTargetObject(effect);
55 fadeAnim_->setPropertyName("opacity");
56 fadeAnim_->setDuration(fadeOverlayTime_);
57 fadeAnim_->setStartValue(effect->opacity());
58 fadeAnim_->setEndValue(0);
59 fadeAnim_->setEasingCurve(QEasingCurve::OutQuad);
60
Edric Milaret029b95a2015-06-09 09:51:44 -040061 this->setContextMenuPolicy(Qt::CustomContextMenu);
62 connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
63 this, SLOT(showContextMenu(const QPoint&)));
Edric Milaret3aca8e32015-06-12 10:01:40 -040064 connect(overlay_, &VideoOverlay::setChatVisibility, [=](bool visible) {
65 emit this->setChatVisibility(visible);
66 });
Edric Milaret029b95a2015-06-09 09:51:44 -040067}
68
69VideoView::~VideoView()
70{
71 delete ui;
Edric Milaret01f23842015-06-22 14:46:01 -040072 delete overlay_;
Edric Milaret01f23842015-06-22 14:46:01 -040073 delete fadeAnim_;
Edric Milaret029b95a2015-06-09 09:51:44 -040074}
75
76void
Edric Milaret80e0b212015-10-16 10:07:43 -040077VideoView::resizeEvent(QResizeEvent* event)
Edric Milaret029b95a2015-06-09 09:51:44 -040078{
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -050079 QRect& previewRect = ui->videoWidget->getPreviewRect();
80 int deltaW = event->size().width() - event->oldSize().width();
81 int deltaH = event->size().height() - event->oldSize().height();
82
83 QPoint previewCenter = ui->videoWidget->getPreviewRect().center();
84 float cx = (float)(event->oldSize().width()) / 2.f;
85 float cy = (float)(event->oldSize().height()) / 2.f;
86 QPoint center = QPoint(cx, cy);
87
88 // first we check if we want to displace the preview
89 if (previewRect.x() + deltaW > 0 && previewRect.y() + deltaH > 0) {
90 // then we check which way
91 if (center.x() - previewCenter.x() < 0 && center.y() - previewCenter.y() < 0)
92 ui->videoWidget->getPreviewRect().translate(deltaW, deltaH);
93 else if (center.x() - previewCenter.x() > 0 && center.y() - previewCenter.y() < 0)
94 ui->videoWidget->getPreviewRect().translate(0, deltaH);
95 else if (center.x() - previewCenter.x() < 0 && center.y() - previewCenter.y() > 0)
96 ui->videoWidget->getPreviewRect().translate(deltaW, 0);
97 }
98
99 if (previewRect.left() <= 0)
100 previewRect.moveLeft(1);
101
102 if (previewRect.right() >= width())
103 previewRect.moveRight(width() - 1);
104
105 if (previewRect.top() <= 0)
106 previewRect.moveTop(1);
107
108 if (previewRect.bottom() >= height())
109 previewRect.moveBottom(height() - 1);
110
Edric Milaret029b95a2015-06-09 09:51:44 -0400111 overlay_->resize(this->size());
112 overlay_->show();
113 overlay_->raise();
114}
115
116void
117VideoView::enterEvent(QEvent* event)
118{
119 Q_UNUSED(event)
120 fadeAnim_->stop();
121 fadeAnim_->targetObject()->setProperty(fadeAnim_->propertyName(), fadeAnim_->startValue());
122}
123
124void
125VideoView::leaveEvent(QEvent* event)
126{
127 Q_UNUSED(event)
Nicolas Jager0a9fc602016-03-11 18:35:42 -0500128 if(not overlay_->isDialogVisible())
129 fadeAnim_->start(QAbstractAnimation::KeepWhenStopped);
Edric Milaret029b95a2015-06-09 09:51:44 -0400130}
131
132void
133VideoView::callStateChanged(Call* call, Call::State previousState)
134{
135 Q_UNUSED(previousState)
Edric Milaret80e0b212015-10-16 10:07:43 -0400136
Edric Milaret029b95a2015-06-09 09:51:44 -0400137 if (call->state() == Call::State::CURRENT) {
138 ui->videoWidget->show();
Edric Milareted0b2802015-10-01 15:10:02 -0400139 timerConnection_ = connect(call, SIGNAL(changed()), this, SLOT(updateCall()));
Edric Milaret029b95a2015-06-09 09:51:44 -0400140 }
141 else {
Edric Milaret80e0b212015-10-16 10:07:43 -0400142 QObject::disconnect(timerConnection_);
Edric Milaret3aca8e32015-06-12 10:01:40 -0400143 emit setChatVisibility(false);
Edric Milaret029b95a2015-06-09 09:51:44 -0400144 if (isFullScreen())
145 toggleFullScreen();
Edric Milaret029b95a2015-06-09 09:51:44 -0400146 }
147}
148
149void
Edric Milareted0b2802015-10-01 15:10:02 -0400150VideoView::updateCall()
Edric Milaret029b95a2015-06-09 09:51:44 -0400151{
Edric Milaret80e0b212015-10-16 10:07:43 -0400152 if (auto call = CallModel::instance().selectedCall()) {
153 overlay_->setName(call->formattedName());
154 overlay_->setTime(call->length());
155 }
Edric Milaret029b95a2015-06-09 09:51:44 -0400156}
157
158void
159VideoView::mouseDoubleClickEvent(QMouseEvent* e) {
160 QWidget::mouseDoubleClickEvent(e);
161 toggleFullScreen();
162}
163
Edric Milaret80e0b212015-10-16 10:07:43 -0400164void
165VideoView::dragEnterEvent(QDragEnterEvent* event)
Edric Milaret303bbf12015-06-22 14:24:13 -0400166{
167 if (event->mimeData()->hasUrls())
168 event->acceptProposedAction();
169}
170
Edric Milaret80e0b212015-10-16 10:07:43 -0400171void
172VideoView::dropEvent(QDropEvent* event)
Edric Milaret303bbf12015-06-22 14:24:13 -0400173{
174 auto urls = event->mimeData()->urls();
Edric Milaret0e1074d2015-12-08 12:08:52 -0500175 if (auto call = CallModel::instance().selectedCall()) {
176 if (auto outVideo = call->firstMedia<Media::Video>(Media::Media::Direction::OUT)) {
177 outVideo->sourceModel()->setFile(urls.at(0));
178 }
179 }
Edric Milaret303bbf12015-06-22 14:24:13 -0400180}
181
Edric Milaret029b95a2015-06-09 09:51:44 -0400182void
183VideoView::toggleFullScreen()
184{
185 if(isFullScreen()) {
Nicolas Jagere0854512016-02-29 12:08:10 -0500186 dynamic_cast<QSplitter*>(oldParent_)->insertWidget(0,this);
Edric Milaret029b95a2015-06-09 09:51:44 -0400187 this->resize(oldSize_.width(), oldSize_.height());
Edric Milaret58f57622015-12-09 09:57:15 -0500188 this->showNormal();
Edric Milaret029b95a2015-06-09 09:51:44 -0400189 } else {
190 oldSize_ = this->size();
191 oldParent_ = static_cast<QWidget*>(this->parent());
192 this->setParent(0);
193 this->showFullScreen();
Edric Milaret029b95a2015-06-09 09:51:44 -0400194 }
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500195 ui->videoWidget->setResetPreview(true);
Edric Milaret029b95a2015-06-09 09:51:44 -0400196}
197
198void
199VideoView::showContextMenu(const QPoint& pos)
200{
201 QPoint globalPos = this->mapToGlobal(pos);
202
203 QMenu menu;
Edric Milaretc4b1a102016-02-02 11:48:30 -0500204 Media::Video* outVideo = nullptr;
205 int activeIndex = -1;
206
207 if (auto call = CallModel::instance().selectedCall()) {
208 outVideo = call->firstMedia<Media::Video>(Media::Media::Direction::OUT);
209 if (outVideo)
210 activeIndex = outVideo->sourceModel()->activeIndex();
211 }
Edric Milaret029b95a2015-06-09 09:51:44 -0400212
Edric Milareta3e47282015-10-23 15:20:30 -0400213 for (auto device : Video::DeviceModel::instance().devices()) {
Edric Milaret029b95a2015-06-09 09:51:44 -0400214 std::unique_ptr<QAction> deviceAction(new QAction(device->name(), this));
215 deviceAction->setCheckable(true);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500216 if (outVideo)
217 if (outVideo->sourceModel()->getDeviceIndex(device) == activeIndex)
218 deviceAction->setChecked(true);
Edric Milaret029b95a2015-06-09 09:51:44 -0400219 auto ptr = deviceAction.release();
220 menu.addAction(ptr);
221 connect(ptr, &QAction::toggled, [=](bool checked) {
222 if (checked == true) {
Edric Milaretc4b1a102016-02-02 11:48:30 -0500223 if (outVideo)
224 outVideo->sourceModel()->switchTo(device);
Edric Milareta3e47282015-10-23 15:20:30 -0400225 Video::DeviceModel::instance().setActive(device);
Edric Milaret029b95a2015-06-09 09:51:44 -0400226 }
227 });
228 }
229
230 menu.addSeparator();
231
Edric Milaret53ac6e52015-09-14 13:37:06 -0400232 auto shareAction = new QAction(tr("Share entire screen"), this);
Edric Milaret029b95a2015-06-09 09:51:44 -0400233 menu.addAction(shareAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500234 shareAction->setCheckable(true);
Edric Milaret53ac6e52015-09-14 13:37:06 -0400235 auto shareAreaAction = new QAction(tr("Share screen area"), this);
Edric Milaret029b95a2015-06-09 09:51:44 -0400236 menu.addAction(shareAreaAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500237 shareAreaAction->setCheckable(true);
Edric Milaret029b95a2015-06-09 09:51:44 -0400238 connect(shareAreaAction, &QAction::triggered, [=]() {
239 SelectAreaDialog selec;
240 selec.exec();
241 });
Edric Milaret53ac6e52015-09-14 13:37:06 -0400242 auto shareFileAction = new QAction(tr("Share file"), this);
Edric Milaret303bbf12015-06-22 14:24:13 -0400243 menu.addAction(shareFileAction);
Edric Milaretc4b1a102016-02-02 11:48:30 -0500244 shareFileAction->setCheckable(true);
245
246 switch(activeIndex) {
247
248 case Video::SourceModel::ExtendedDeviceList::SCREEN:
249 shareAction->setChecked(true);
250 break;
251 case Video::SourceModel::ExtendedDeviceList::FILE:
252 shareFileAction->setChecked(true);
253 break;
254
255 }
256
257 connect(shareAction, &QAction::triggered, [=]() {
258 if (outVideo)
259 outVideo->sourceModel()->setDisplay(0, QApplication::desktop()->rect());
260 });
Edric Milaret303bbf12015-06-22 14:24:13 -0400261 connect(shareFileAction, &QAction::triggered, [=]() {
262 QFileDialog dialog(this);
263 dialog.setFileMode(QFileDialog::AnyFile);
264 QStringList fileNames;
265 if (!dialog.exec())
266 return;
267 fileNames = dialog.selectedFiles();
Edric Milaretc4b1a102016-02-02 11:48:30 -0500268 if (outVideo)
269 outVideo->sourceModel()->setFile(QUrl::fromLocalFile(fileNames.at(0)));
Edric Milaret303bbf12015-06-22 14:24:13 -0400270 });
Edric Milaret029b95a2015-06-09 09:51:44 -0400271
272 menu.exec(globalPos);
273}
274
Edric Milaret80e0b212015-10-16 10:07:43 -0400275void
276VideoView::pushRenderer(Call* call) {
277 if (not call) {
278 disconnect(videoStartedConnection_);
279 return;
280 }
281 if (auto renderer = call->videoRenderer()) {
282 slotVideoStarted(renderer);
283 } else {
284 disconnect(videoStartedConnection_);
285 videoStartedConnection_ = connect(call,
286 SIGNAL(videoStarted(Video::Renderer*)),
287 this,
288 SLOT(slotVideoStarted(Video::Renderer*)));
289 }
290 ui->videoWidget->setPreviewDisplay(call->type() != Call::Type::CONFERENCE);
291}
292
293void
294VideoView::slotVideoStarted(Video::Renderer* renderer) {
295 ui->videoWidget->show();
296 ui->videoWidget->setDistantRenderer(renderer);
297}
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500298
299void
300VideoView::mousePressEvent(QMouseEvent* event)
301{
302 QPoint clickPosition = event->pos();
303 if (ui->videoWidget->getPreviewRect().contains(clickPosition)) {
304 QLine distance = QLine(clickPosition, ui->videoWidget->getPreviewRect().bottomRight());
305 if (distance.dy() < resizeGrip_ and distance.dx() < resizeGrip_) {
306 QApplication::setOverrideCursor(Qt::SizeFDiagCursor);
307 resizingPreview_ = true;
308 } else {
309 originMouseDisplacement_ = event->pos() - ui->videoWidget->getPreviewRect().topLeft();
310 QApplication::setOverrideCursor(Qt::SizeAllCursor);
311 draggingPreview_ = true;
312 }
313 }
314}
315
316void
317VideoView::mouseReleaseEvent(QMouseEvent* event)
318{
Edric Milaret6a785af2016-03-07 15:39:30 -0500319 Q_UNUSED(event)
320
Nicolas Jagerc7b8e4b2016-02-29 09:20:40 -0500321 draggingPreview_ = false;
322 resizingPreview_ = false;
323 QApplication::setOverrideCursor(Qt::ArrowCursor);
324}
325
326void
327VideoView::mouseMoveEvent(QMouseEvent* event)
328{
329 QRect& previewRect = ui->videoWidget->getPreviewRect();
330 if (draggingPreview_) {
331 if (previewRect.left() > 0
332 && previewRect.top() > 0
333 && previewRect.right() < width()
334 && previewRect.bottom() < height()) {
335
336 previewRect.moveTo(event->pos() - originMouseDisplacement_);
337 if (previewRect.left() <= 0)
338 previewRect.moveLeft(1);
339
340 if (previewRect.right() >= width())
341 previewRect.moveRight(width() - 1);
342
343 if (previewRect.top() <= 0)
344 previewRect.moveTop(1);
345
346 if (previewRect.bottom() >= height())
347 previewRect.moveBottom(height() - 1);
348 }
349 }
350
351 QLine distance = QLine(previewRect.topLeft(), event->pos());
352
353 if (resizingPreview_
354 and distance.dx() > minimalSize_
355 and distance.dy() > minimalSize_
356 and geometry().contains(event->pos()))
357 previewRect.setBottomRight(event->pos());
358}