1 #include <sstream>
2 #include <stdexcept>
3 #include <cmath>
4 #include "mainwindow.h"
5 #include "ui_mainwindow.h"
6 #include "openfiledialog.h"
7 #include "helpers.h"
8 #include "platform/unify.h"
9 #include "platform/devicemanager.h"
10 #include <QDesktopWidget>
11 #include <QStyle>
12 #include <QWindow>
13 #include <QMenuBar>
14 #include <QSizeGrip>
15 #include <QMimeData>
16 #include <QJsonDocument>
17 #include <QFileDialog>
18 #include <QInputDialog>
19 #include <QTime>
20 #include <QDesktopServices>
21 #include <QMessageBox>
22 #include <QLibraryInfo>
23 #include <QToolTip>
24 #include <QStyle>
25 
26 using namespace Helpers;
27 static const char SKIPACTION[] = "Skip";
28 
29 
MainWindow(QWidget * parent)30 MainWindow::MainWindow(QWidget *parent) :
31     QMainWindow(parent),
32     ui(new Ui::MainWindow)
33 {
34     ui->setupUi(this);
35     setupMenu();
36     setupContextMenu();
37     setupActionGroups();
38     setupPositionSlider();
39     setupVolumeSlider();
40     setupMpvHost();
41     setupMpvObject();
42     setupPlaylist();
43     setupStatus();
44     setupSizing();
45     setupBottomArea();
46     setupHideTimer();
47     setupIconThemer();
48 
49     mpvw->installEventFilter(this);
50     playlistWindow_->installEventFilter(this);
51 
52     connectActionsToSignals();
53     connectActionsToSlots();
54     connectButtonsToActions();
55     connectPlaylistWindowToActions();
56     globalizeAllActions();
57     setUiEnabledState(false);
58     setDiscState(false);
59 
60     // Sync with X11
61     if (Platform::isUnix) {
62         setAttribute(Qt::WA_DontShowOnScreen, true);
63         show();
64         qApp->processEvents(QEventLoop::AllEvents |
65                             QEventLoop::WaitForMoreEvents,
66                             50);
67         hide();
68         setAttribute(Qt::WA_DontShowOnScreen, false);
69     }
70 }
71 
~MainWindow()72 MainWindow::~MainWindow()
73 {
74     mpvObject_->setWidgetType(Helpers::NullWidget);
75 
76     delete ui;
77     ui = nullptr;
78 }
79 
mpvObject()80 MpvObject *MainWindow::mpvObject()
81 {
82     return mpvObject_;
83 }
84 
mpvHost()85 QMainWindow *MainWindow::mpvHost()
86 {
87     return mpvHost_;
88 }
89 
playlistWindow()90 PlaylistWindow *MainWindow::playlistWindow()
91 {
92     return playlistWindow_;
93 }
94 
actionsToList(QList<QAction * > & actionList,const QList<QAction * > & actions)95 static void actionsToList(QList<QAction*> &actionList, const QList<QAction*> &actions) {
96     for (QAction *a : actions) {
97         if (a->data().toString() == SKIPACTION)
98             continue;
99         if (a->menu())
100             actionsToList(actionList, a->menu()->actions());
101         else if (!a->isSeparator())
102             actionList.append(a);
103     }
104 }
105 
editableActions()106 QList<QAction *> MainWindow::editableActions()
107 {
108     QList<QAction*> actionList;
109     actionsToList(actionList, actions());
110     return actionList;
111 }
112 
mouseMapDefaults()113 QVariantMap MainWindow::mouseMapDefaults()
114 {
115     QVariantMap commandMap;
116     auto actionToMap = [&commandMap](QAction *a, const MouseState &m) {
117         Command c;
118         c.fromAction(a);
119         c.mouseWindowed = m;
120         c.mouseFullscreen = m;
121         commandMap.insert(a->objectName(), c.toVMap());
122     };
123     actionToMap(ui->actionViewFullscreen, {MouseState::Left,0,MouseState::PressTwice});
124     actionToMap(ui->actionPlayPause, {MouseState::Left,0,MouseState::MouseDown});
125     actionToMap(ui->actionNavigateChaptersNext, {MouseState::Forward,0,MouseState::MouseDown});
126     actionToMap(ui->actionNavigateChaptersPrevious, {MouseState::Back,0,MouseState::MouseDown});
127     actionToMap(ui->actionPlayVolumeDown, {MouseState::Wheel,0,MouseState::MouseDown});
128     actionToMap(ui->actionPlayVolumeUp, {MouseState::Wheel,0,MouseState::MouseUp});
129     return commandMap;
130 }
131 
state()132 QVariantMap MainWindow::state()
133 {
134 #define WRAP(a) \
135     a->objectName(), a->isChecked()
136     return QVariantMap {
137         { "decorationState", int(decorationState_) },
138         { WRAP(ui->actionViewHideSeekbar) },
139         { WRAP(ui->actionViewHideControls) },
140         { WRAP(ui->actionViewHideInformation) },
141         { WRAP(ui->actionViewHideStatistics) },
142         { WRAP(ui->actionViewHideStatus) },
143         { WRAP(ui->actionViewHideSubresync) },
144         { WRAP(ui->actionViewHidePlaylist) },
145         { WRAP(ui->actionViewHideCapture) },
146         { WRAP(ui->actionViewHideNavigation) },
147         { WRAP(ui->actionViewHideLog) },
148         { WRAP(ui->actionViewHideLibrary) },
149         { WRAP(ui->actionViewOntopDefault) },
150         { WRAP(ui->actionViewOntopAlways) },
151         { WRAP(ui->actionViewOntopPlaying) },
152         { WRAP(ui->actionViewOntopVideo) },
153         { WRAP(ui->actionViewFullscreen) },
154         { WRAP(ui->actionPlaySubtitlesEnabled) },
155         { "shownStatsPage", mpvObject_->selectedStatsPage() }
156     };
157 #undef WRAP
158 }
159 
setState(const QVariantMap & map)160 void MainWindow::setState(const QVariantMap &map)
161 {
162 #define UNWRAP(a,b) \
163     a->setChecked(map.value(a->objectName(), b).toBool())
164 
165     setUiDecorationState(DecorationState(map["decorationState"].toInt()));
166     UNWRAP(ui->actionViewHideSeekbar, true);
167     UNWRAP(ui->actionViewHideControls, true);
168     UNWRAP(ui->actionViewHideInformation, false);
169     UNWRAP(ui->actionViewHideStatistics, false);
170     UNWRAP(ui->actionViewHideStatus, true);
171     UNWRAP(ui->actionViewHideSubresync, false);
172     UNWRAP(ui->actionViewHidePlaylist, true);
173     UNWRAP(ui->actionViewHideCapture, false);
174     UNWRAP(ui->actionViewHideNavigation, false);
175     UNWRAP(ui->actionViewHideLog, false);
176     UNWRAP(ui->actionViewHideLibrary, false);
177     UNWRAP(ui->actionViewOntopDefault, true);
178     UNWRAP(ui->actionViewOntopAlways, false);
179     UNWRAP(ui->actionViewOntopPlaying, false);
180     UNWRAP(ui->actionViewOntopVideo, false);
181     UNWRAP(ui->actionViewFullscreen, false);
182     UNWRAP(ui->actionPlaySubtitlesEnabled, true);
183     on_actionPlaySubtitlesEnabled_triggered(ui->actionPlaySubtitlesEnabled->isChecked());
184     setOSDPage(map.value("shownStatsPage",0).toInt());
185     updateOnTop();
186 
187 #undef UNWRAP
188 }
189 
setScreensaverAbilities(QSet<ScreenSaver::Ability> ab)190 void MainWindow::setScreensaverAbilities(QSet<ScreenSaver::Ability> ab)
191 {
192     ui->actionPlayAfterOnceStandby->setVisible(ab.contains(ScreenSaver::Suspend));
193     ui->actionPlayAfterOnceHibernate->setVisible(ab.contains(ScreenSaver::Hibernate));
194     ui->actionPlayAfterOnceShutdown->setVisible(ab.contains(ScreenSaver::Shutdown));
195     ui->actionPlayAfterOnceLogoff->setVisible(ab.contains(ScreenSaver::LogOff));
196     ui->actionPlayAfterOnceLock->setVisible(ab.contains(ScreenSaver::LockScreen));
197 }
198 
desirableSize(bool first_run)199 QSize MainWindow::desirableSize(bool first_run)
200 {
201     if (zoomMode == FitToWindow)
202         return size();
203 
204     // Grab device pixel ratio (2=HiDPI screen)
205     qreal ratio = devicePixelRatio();
206 
207     // calculate available area for the window
208     QDesktopWidget *desktop = qApp->desktop();
209     QRect available = first_run ? Helpers::availableGeometryFromPoint(QCursor::pos())
210                                 : desktop->availableGeometry(this);
211     QSize frameDiff = this->frameGeometry().size() - this->geometry().size();
212     available.adjust(0, 0, -frameDiff.width(), -frameDiff.height());
213 
214     // calculate player size
215     QSize player = mpvObject_->videoSize() / ratio;
216     double factor = sizeFactor();
217     if (!isPlaying || player.isEmpty()) {
218         player = noVideoSize_;
219         factor = std::max(1.0, sizeFactor());
220     }
221 
222     // calculate the amount taken by widgets outside the video frame
223     QSize mpvSize = mpvw ? mpvw->size() : noVideoSize_;
224     QSize fudgeFactor = size() - mpvSize;
225 
226     // calculate desired client size, depending upon the zoom mode
227     QSize wanted;
228     if (zoomMode == RegularZoom) {
229         wanted = QSize(int(player.width()*factor + 0.5),
230                        int(player.height()*factor + 0.5));
231     } else {
232         wanted = (available.size() - fudgeFactor) * fitFactor_;
233         if (zoomMode == Autofit) {
234             wanted = player.scaled(wanted.width(), wanted.height(),
235                                    Qt::KeepAspectRatio);
236         } else if (zoomMode == AutofitLarger) {
237             if (wanted.height() > player.height()
238                     || wanted.width() > player.width()) {
239                 // window is larger than player size, so set to player size
240                 wanted = player;
241             } else {
242                 // window is smaller than player size, so limit new window
243                 // size to contain a player rectangle
244                 wanted = player.scaled(wanted.width(), wanted.height(),
245                                        Qt::KeepAspectRatio);
246             }
247         } else { // zoomMode == AutofitSmaller
248             if (player.height() < wanted.height()
249                     || player.width() < wanted.width()) {
250                 // player size is smaller than wanted window, so make it larger
251                 wanted = player.scaled(wanted.width(), wanted.height(),
252                                        Qt::KeepAspectRatioByExpanding);
253             } else {
254                 // player size is larger than wanted, so use the player size
255                 wanted = player;
256             }
257         }
258     }
259     return wanted + fudgeFactor;
260 }
261 
desirablePosition(QSize & size,bool first_run)262 QPoint MainWindow::desirablePosition(QSize &size, bool first_run)
263 {
264     QDesktopWidget *desktop = qApp->desktop();
265 
266     QRect available = first_run ? Helpers::availableGeometryFromPoint(QCursor::pos())
267                                 : desktop->availableGeometry(this);
268     QSize frameDiff = this->frameGeometry().size() - this->geometry().size();
269     available.adjust(0, 0, -frameDiff.width(), -frameDiff.height());
270     if (size.height() > available.height())
271         size.setHeight(available.height());
272     if (size.width() > available.width())
273         size.setWidth(available.width());
274 
275     QPoint clientOffset = geometry().topLeft() - pos();
276     return QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter,
277                                size, available).topLeft() + clientOffset;
278 }
279 
unfreezeWindow()280 void MainWindow::unfreezeWindow()
281 {
282     frozenWindow = false;
283 }
284 
resizeEvent(QResizeEvent * event)285 void MainWindow::resizeEvent(QResizeEvent *event)
286 {
287     Q_UNUSED(event)
288     updateBottomAreaGeometry();
289     checkBottomArea(QCursor::pos());
290 }
291 
eventFilter(QObject * object,QEvent * event)292 bool MainWindow::eventFilter(QObject *object, QEvent *event)
293 {
294     bool insideMpv = mpvw ? object == mpvw : false;
295     if ((insideMpv || object == playlistWindow_) && event->type() == QEvent::MouseMove) {
296         this->mouseMoveEvent(static_cast<QMouseEvent*>(event));
297     }
298     return QMainWindow::eventFilter(object, event);
299 }
300 
closeEvent(QCloseEvent * event)301 void MainWindow::closeEvent(QCloseEvent *event)
302 {
303     event->accept();
304     emit instanceShouldQuit();
305 }
306 
mouseMoveEvent(QMouseEvent * event)307 void MainWindow::mouseMoveEvent(QMouseEvent *event)
308 {
309     if (fullscreenMode_)
310         checkBottomArea(event->globalPos());
311 }
312 
insideWidget(QPoint p,QWidget * mpvw)313 static bool insideWidget(QPoint p, QWidget *mpvw) {
314     if (mpvw == nullptr)
315         return false;
316     QRect rc(mpvw->mapToGlobal(QPoint()), mpvw->size());
317     return rc.contains(p);
318 }
319 
mousePressEvent(QMouseEvent * event)320 void MainWindow::mousePressEvent(QMouseEvent *event)
321 {
322     bool ok = mpvw ? insideWidget(event->globalPos(), mpvw) : false;
323     if (ok && mouseStateEvent(MouseState::fromMouseEvent(event, MouseState::MouseDown)))
324         event->accept();
325     else
326         QMainWindow::mousePressEvent(event);
327 }
328 
mouseDoubleClickEvent(QMouseEvent * event)329 void MainWindow::mouseDoubleClickEvent(QMouseEvent *event)
330 {
331     bool ok = mpvw ? insideWidget(event->globalPos(), mpvw) : false;
332     if (ok && mouseStateEvent(MouseState::fromMouseEvent(event, MouseState::PressTwice)))
333         event->accept();
334     mousePressEvent(event);
335 }
336 
mouseReleaseEvent(QMouseEvent * event)337 void MainWindow::mouseReleaseEvent(QMouseEvent *event)
338 {
339     bool ok = mpvw ? insideWidget(event->globalPos(), mpvw) : false;
340     if (ok && mouseStateEvent(MouseState::fromMouseEvent(event, MouseState::MouseUp)))
341         event->accept();
342     else
343         QMainWindow::mouseReleaseEvent(event);
344 }
345 
wheelEvent(QWheelEvent * event)346 void MainWindow::wheelEvent(QWheelEvent *event)
347 {
348     // Deprecated way
349     bool ok = mpvw ? insideWidget(event->globalPos(), mpvw) : false;
350     // Non-deprecated way since Qt 5.15
351     //QPointF gp = event->globalPosition();
352     //bool ok = mpvw ? insideWidget({int(gp.rx()),int(gp.ry())}, mpvw) : false;
353     if (ok && mouseStateEvent(MouseState::fromWheelEvent(event)))
354         event->accept();
355     else
356         QMainWindow::wheelEvent(event);
357 }
358 
dragEnterEvent(QDragEnterEvent * event)359 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
360 {
361     if (event->mimeData()->hasUrls())
362         event->accept();
363 }
364 
dropEvent(QDropEvent * event)365 void MainWindow::dropEvent(QDropEvent *event)
366 {
367     emit severalFilesOpened(event->mimeData()->urls(), true);
368 }
369 
mouseStateEvent(const MouseState & state)370 bool MainWindow::mouseStateEvent(const MouseState &state)
371 {
372     MouseStateMap &mouseMap = fullscreenMode_ ? mouseMapFullscreen
373                                               : mouseMapWindowed;
374 
375     if (mouseMap.contains(state)) {
376         QAction *action = mouseMap[state];
377         if (!action->isEnabled())
378             return true;
379         if (action->isCheckable())
380             action->setChecked(!action->isChecked());
381         emit action->triggered(action->isChecked());
382         return true;
383     }
384     return false;
385 }
386 
positionSlider()387 MediaSlider *MainWindow::positionSlider()
388 {
389     return positionSlider_;
390 }
391 
volumeSlider()392 VolumeSlider *MainWindow::volumeSlider()
393 {
394     return volumeSlider_;
395 }
396 
decorationState()397 MainWindow::DecorationState MainWindow::decorationState()
398 {
399     return decorationState_;
400 }
401 
fullscreenMode()402 bool MainWindow::fullscreenMode()
403 {
404     return fullscreenMode_;
405 }
406 
noVideoSize()407 QSize MainWindow::noVideoSize()
408 {
409     return noVideoSize_;
410 }
411 
sizeFactor()412 double MainWindow::sizeFactor()
413 {
414     return sizeFactor_;
415 }
416 
setFullscreenMode(bool fullscreenMode)417 void MainWindow::setFullscreenMode(bool fullscreenMode)
418 {
419     if (fullscreenMode_ == fullscreenMode)
420         return;
421 
422     // This is where the monitor settings should be honored.
423     // For now, use the current screen.
424     windowHandle()->setScreen(windowHandle()->screen());
425 
426     // save maximized state if changing from windowed to fullscreen
427     if (!fullscreenMode_ && fullscreenMode)
428         fullscreenMaximized = isMaximized();
429 
430     fullscreenMode_ = fullscreenMode;
431     if (fullscreenMode)
432         showFullScreen();
433     else if (fullscreenMaximized)
434         showMaximized();
435     else
436         showNormal();
437 
438     ui->actionViewFullscreenEscape->setEnabled(fullscreenMode);
439     updateMouseHideTime();
440 
441     //REMOVEME: work around OpenGL blackness bug after fullscreen
442     QTimer::singleShot(50, this, [this]() {
443         positionSlider_->update();
444         volumeSlider_->update();
445         timePosition->update();
446         timeDuration->update();
447     });
448     emit fullscreenModeChanged(fullscreenMode_);
449 }
450 
setDiscState(bool playingADisc)451 void MainWindow::setDiscState(bool playingADisc)
452 {
453     ui->actionNavigateMenuTitle->setEnabled(playingADisc);
454     ui->actionNavigateMenuRoot->setEnabled(playingADisc);
455     ui->actionNavigateMenuSubtitle->setEnabled(playingADisc);
456     ui->actionNavigateMenuAudio->setEnabled(playingADisc);
457     ui->actionNavigateMenuAngle->setEnabled(playingADisc);
458     ui->actionNavigateMenuChapter->setEnabled(playingADisc);
459 }
460 
setupMenu()461 void MainWindow::setupMenu()
462 {
463     // Work around separators with text in the designer not showing as
464     // sections
465     ui->menuPlayAfter->insertSection(ui->actionPlayAfterOnceExit,
466                                        tr("Once"));
467     ui->menuPlayAfter->insertSection(ui->actionPlayAfterAlwaysExit,
468                                       tr("Every time"));
469 
470     ui->infoStats->setVisible(false);
471 
472     QString quickOpenKeys = Platform::isMac ? tr("Alt+Q") : tr("Ctrl+Q");
473     ui->actionFileOpenQuick->setShortcut(quickOpenKeys);
474 
475     connect(Platform::deviceManager(), &DeviceManager::deviceListChanged,
476             this, [this]() { updateDiscList(); });
477 }
478 
setupContextMenu()479 void MainWindow::setupContextMenu()
480 {
481     contextMenu = new QMenu(this);
482 
483     contextMenu->addMenu(ui->menuFile);
484     contextMenu->addSeparator();
485     contextMenu->addAction(ui->actionPlayPause);
486     contextMenu->addAction(ui->actionPlayStop);
487     contextMenu->addSeparator();
488     contextMenu->addAction(ui->actionViewFullscreen);
489     contextMenu->addMenu(ui->menuNavigate);
490     contextMenu->addMenu(ui->menuFavorites);
491     contextMenu->addSeparator();
492     contextMenu->addMenu(ui->menuPlayAudio);
493     contextMenu->addMenu(ui->menuPlaySubtitles);
494     contextMenu->addMenu(ui->menuPlayVideo);
495     contextMenu->addSeparator();
496     contextMenu->addMenu(ui->menuPlayVolume);
497     contextMenu->addMenu(ui->menuPlayAfter);
498     contextMenu->addSeparator();
499 
500     QMenu *contextView = new QMenu(tr("View"), this);
501     contextView->addMenu(ui->menuViewOntop);
502     contextView->addSeparator();
503     contextView->addAction(ui->actionViewHideMenu);
504     contextView->addAction(ui->actionViewHideSeekbar);
505     contextView->addAction(ui->actionViewHideControls);
506     contextView->addAction(ui->actionViewHideInformation);
507     contextView->addAction(ui->actionViewHideStatistics);
508     contextView->addAction(ui->actionViewHideStatus);
509     contextView->addAction(ui->actionViewHideSubresync);
510     contextView->addAction(ui->actionViewHidePlaylist);
511     contextView->addAction(ui->actionViewHideCapture);
512     contextView->addAction(ui->actionViewHideNavigation);
513     contextView->addMenu(ui->menuViewPresets);
514 
515     contextMenu->addMenu(contextView);
516     contextMenu->addAction(ui->actionFileProperties);
517     contextMenu->addAction(ui->actionViewOptions);
518     contextMenu->addSeparator();
519     contextMenu->addAction(ui->actionFileExit);
520 }
521 
setupActionGroups()522 void MainWindow::setupActionGroups()
523 {
524     QActionGroup *ag;
525 
526     ag = new QActionGroup(this);
527     ag->addAction(ui->actionViewOSDNone);
528     ag->addAction(ui->actionViewOSDMessages);
529     ag->addAction(ui->actionViewOSDStatistics);
530     ag->addAction(ui->actionViewOSDFrameTimings);
531 
532     ag = new QActionGroup(this);
533     ag->addAction(ui->actionViewZoom025);
534     ag->addAction(ui->actionViewZoom050);
535     ag->addAction(ui->actionViewZoom075);
536     ag->addAction(ui->actionViewZoom100);
537     ag->addAction(ui->actionViewZoom150);
538     ag->addAction(ui->actionViewZoom200);
539     ag->addAction(ui->actionViewZoom300);
540     ag->addAction(ui->actionViewZoom400);
541     ag->addAction(ui->actionViewZoomAutofit);
542     ag->addAction(ui->actionViewZoomAutofitLarger);
543     ag->addAction(ui->actionViewZoomAutofitSmaller);
544     ag->addAction(ui->actionViewZoomDisable);
545 
546     ag = new QActionGroup(this);
547     ag->addAction(ui->actionViewOntopDefault);
548     ag->addAction(ui->actionViewOntopAlways);
549     ag->addAction(ui->actionViewOntopPlaying);
550     ag->addAction(ui->actionViewOntopVideo);
551 
552     ag = new QActionGroup(this);
553     ag->addAction(ui->actionPlayAfterOnceExit);
554     ag->addAction(ui->actionPlayAfterOnceHibernate);
555     ag->addAction(ui->actionPlayAfterOnceLock);
556     ag->addAction(ui->actionPlayAfterOnceLogoff);
557     ag->addAction(ui->actionPlayAfterOnceNothing);
558     ag->addAction(ui->actionPlayAfterOnceRepeat);
559     ag->addAction(ui->actionPlayAfterOnceShutdown);
560     ag->addAction(ui->actionPlayAfterOnceStandby);
561 
562     ag = new QActionGroup(this);
563     ag->addAction(ui->actionPlayAfterAlwaysExit);
564     ag->addAction(ui->actionPlayAfterAlwaysNext);
565     ag->addAction(ui->actionPlayAfterAlwaysNothing);
566     ag->addAction(ui->actionPlayAfterAlwaysRepeat);
567 }
568 
setupPositionSlider()569 void MainWindow::setupPositionSlider()
570 {
571     positionSlider_ = new MediaSlider();
572     ui->seekbar->layout()->addWidget(positionSlider_);
573     connect(positionSlider_, &MediaSlider::sliderMoved,
574             this, &MainWindow::position_sliderMoved);
575     connect(positionSlider_, &MediaSlider::hoverValue,
576             this, &MainWindow::position_hoverValue);
577 }
578 
setupVolumeSlider()579 void MainWindow::setupVolumeSlider()
580 {
581     volumeSlider_ = new VolumeSlider();
582     volumeSlider_->setMinimumWidth(50);
583     volumeSlider_->setMinimum(0);
584     volumeSlider_->setMaximum(130);
585     volumeSlider_->setValue(100);
586     ui->controlbar->layout()->addWidget(volumeSlider_);
587     connect(volumeSlider_, &VolumeSlider::sliderMoved,
588             this, &MainWindow::volume_sliderMoved);
589 }
590 
setupMpvHost()591 void MainWindow::setupMpvHost()
592 {
593     // Create a special QMainWindow widget so that the playlist window will
594     // dock around it rather than ourselves
595     mpvHost_ = new QMainWindow(this);
596     mpvHost_->setSizePolicy(QSizePolicy(QSizePolicy::Ignored,
597                                         QSizePolicy::Ignored));
598     ui->mpvWidget->layout()->addWidget(mpvHost_);
599 }
600 
setupMpvObject()601 void MainWindow::setupMpvObject()
602 {
603     mpvObject_ = new MpvObject(this, "Media Player Classic Qute Theater");
604     mpvObject_->setHostWindow(mpvHost_);
605     setupMpvWidget(Helpers::GlCbWidget);
606     connect(mpvObject_, &MpvObject::logoSizeChanged,
607             this, &MainWindow::setNoVideoSize);
608 }
609 
setupMpvWidget(Helpers::MpvWidgetType widgetType)610 void MainWindow::setupMpvWidget(Helpers::MpvWidgetType widgetType)
611 {
612     bool embeddedBottomArea = ui->bottomArea->parentWidget() == mpvw;
613     if (embeddedBottomArea) {
614         reparentBottomArea(false);
615     }
616 
617     mpvw = nullptr;
618     mpvObject_->setWidgetType(widgetType);
619     mpvw = mpvObject_->mpvWidget();
620     if (!mpvw)
621         throw(std::runtime_error("Video widget not created"));
622     // CHECKME: signal could be in mpvObject and reflected internally
623     connect(mpvw, &QWidget::customContextMenuRequested,
624             this, &MainWindow::mpvw_customContextMenuRequested);
625     // CHECKME: mouse tracking could be set by mpvObject's setWidgetType func
626     mpvw->setMouseTracking(true);
627 
628     if (embeddedBottomArea)
629         reparentBottomArea(true);
630 }
631 
setupPlaylist()632 void MainWindow::setupPlaylist()
633 {
634     playlistWindow_ = new PlaylistWindow(this);
635     mpvHost_->addDockWidget(Qt::RightDockWidgetArea, playlistWindow_);
636     connect(playlistWindow_, &PlaylistWindow::windowDocked,
637             this, &MainWindow::playlistWindow_windowDocked);
638     connect(playlistWindow_, &PlaylistWindow::playlistAddItem,
639             this, &MainWindow::playlistWindow_playlistAddItem);
640     connect(playlistWindow_, &PlaylistWindow::currentPlaylistHasItems,
641             ui->play, &QPushButton::setEnabled);
642     QList<QWidget *> playlistWidgets = playlistWindow_->findChildren<QWidget *>();
643     foreach(QWidget *w, playlistWidgets)
644         w->setMouseTracking(true);
645     playlistWindow_->setMouseTracking(true);
646 }
647 
setupStatus()648 void MainWindow::setupStatus()
649 {
650     timePosition = new StatusTime();
651     ui->statusbarLayout->insertWidget(2, timePosition);
652     timeDuration = new StatusTime();
653     ui->statusbarLayout->insertWidget(4, timeDuration);
654 }
655 
setupSizing()656 void MainWindow::setupSizing()
657 {
658     QSizeGrip *gripper = new QSizeGrip(this);
659     ui->statusbar->layout()->addWidget(gripper);
660 }
661 
setupBottomArea()662 void MainWindow::setupBottomArea()
663 {
664     QList<QWidget *> bottomWidgets = ui->bottomArea->findChildren<QWidget *>();
665     foreach(QWidget *w, bottomWidgets)
666         w->setMouseTracking(true);
667     ui->bottomArea->setMouseTracking(true);
668 }
669 
setupIconThemer()670 void MainWindow::setupIconThemer()
671 {
672     QVector<IconThemer::IconData> data {
673         { ui->play, "media-playback-start", {} },
674         { ui->pause, "media-playback-pause", {} },
675         { ui->stop, "media-playback-stop", {} },
676         { ui->skipBackward, "go-previous", {} },
677         { ui->skipForward, "go-next", {} },
678         { ui->speedDecrease, "media-seek-backward", {} },
679         { ui->speedIncrease, "media-seek-forward", {} },
680         { ui->stepBackward, "media-skip-backward", {} },
681         { ui->stepForward, "media-skip-forward", {} },
682         { ui->loopA, "zone-in", {} },
683         { ui->loopB, "zone-out", {} },
684         { ui->subs, "view-media-subtitles", "view-media-subtitles-hidden" },
685         { ui->mute, "player-volume", "player-volume-muted" }
686     };
687     for (auto &d : data)
688         themer.addIconData(d);
689 }
690 
setupHideTimer()691 void MainWindow::setupHideTimer()
692 {
693     hideTimer.setSingleShot(true);
694     connect(&hideTimer, &QTimer::timeout,
695             this, &MainWindow::hideTimer_timeout);
696 }
697 
connectActionsToSignals()698 void MainWindow::connectActionsToSignals()
699 {
700     connect(ui->actionFileSaveThumbnails, &QAction::triggered,
701             this, &MainWindow::takeThumbnails);
702     connect(ui->actionFileProperties, &QAction::triggered,
703             this, &MainWindow::showFileProperties);
704 }
705 
connectActionsToSlots()706 void MainWindow::connectActionsToSlots()
707 {
708     connect(ui->actionHelpAboutQt, &QAction::triggered,
709             qApp, &QApplication::aboutQt);
710 }
711 
connectButtonsToActions()712 void MainWindow::connectButtonsToActions()
713 {
714     connect(ui->pause, &QPushButton::clicked,
715             ui->actionPlayPause, &QAction::triggered);
716     connect(ui->stop, &QPushButton::clicked,
717             ui->actionPlayStop, &QAction::triggered);
718 
719     connect(ui->speedDecrease, &QPushButton::clicked,
720             ui->actionPlayRateDecrease, &QAction::triggered);
721     connect(ui->speedIncrease, &QPushButton::clicked,
722             ui->actionPlayRateIncrease, &QAction::triggered);
723 
724     connect(ui->skipBackward, &QPushButton::clicked,
725             ui->actionNavigateChaptersPrevious, &QAction::triggered);
726     connect(ui->stepBackward, &QPushButton::clicked,
727             ui->actionPlayFrameBackward, &QAction::triggered);
728     connect(ui->stepForward, &QPushButton::clicked,
729             ui->actionPlayFrameForward, &QAction::triggered);
730     connect(ui->skipForward, &QPushButton::clicked,
731             ui->actionNavigateChaptersNext, &QAction::triggered);
732 
733     connect(ui->loopA, &QPushButton::clicked,
734             ui->actionPlayLoopStart, &QAction::triggered);
735     connect(ui->loopB, &QPushButton::clicked,
736             ui->actionPlayLoopEnd, &QAction::triggered);
737 
738     connect(ui->subs, &QPushButton::toggled, this,
739             [this](bool checked) { on_actionPlaySubtitlesEnabled_triggered(!checked); });
740     connect(ui->mute, &QPushButton::toggled,
741             ui->actionPlayVolumeMute, &QAction::toggled);
742 }
743 
connectPlaylistWindowToActions()744 void MainWindow::connectPlaylistWindowToActions()
745 {
746     connect(ui->actionPlaylistCopy, &QAction::triggered,
747             playlistWindow_, &PlaylistWindow::copy);
748     connect(ui->actionPlaylistCopyQueue, &QAction::triggered,
749             playlistWindow_, &PlaylistWindow::copyQueue);
750     connect(ui->actionPlaylistPaste, &QAction::triggered,
751             playlistWindow_, &PlaylistWindow::paste);
752     connect(ui->actionPlaylistPasteQueue, &QAction::triggered,
753             playlistWindow_, &PlaylistWindow::pasteQueue);
754 
755     connect(ui->actionPlaylistNewTab, &QAction::triggered,
756             playlistWindow_, &PlaylistWindow::newTab);
757     connect(ui->actionPlaylistCloseTab, &QAction::triggered,
758             playlistWindow_, &PlaylistWindow::closeTab);
759     connect(ui->actionPlaylistDuplicateTab, &QAction::triggered,
760             playlistWindow_, &PlaylistWindow::duplicateTab);
761     connect(ui->actionPlaylistImport, &QAction::triggered,
762             playlistWindow_, &PlaylistWindow::importTab);
763     connect(ui->actionPlaylistExport, &QAction::triggered,
764             playlistWindow_, &PlaylistWindow::exportTab);
765     connect(ui->actionPlaylistPlayCurrent, &QAction::triggered,
766             playlistWindow_, &PlaylistWindow::playCurrentItem);
767     connect(ui->actionPlaylistShowQuickQueue, &QAction::triggered,
768             playlistWindow_, &PlaylistWindow::setQueueMode);
769     connect(ui->actionPlaylistQuickQueue, &QAction::triggered,
770             playlistWindow_, &PlaylistWindow::quickQueue);
771     connect(ui->actionPlaylistQueueVisible, &QAction::triggered,
772             playlistWindow_, &PlaylistWindow::visibleToQueue);
773     connect(ui->actionPlaylistFinishSearching, &QAction::triggered,
774             playlistWindow_, &PlaylistWindow::finishSearch);
775 
776     connect(ui->actionPlaylistExtraIncrement, &QAction::triggered,
777             playlistWindow_, &PlaylistWindow::incExtraPlayTimes);
778     connect(ui->actionPlaylistExtraDecrement, &QAction::triggered,
779             playlistWindow_, &PlaylistWindow::decExtraPlayTimes);
780     connect(ui->actionPlaylistExtraZero, &QAction::triggered,
781             playlistWindow_, &PlaylistWindow::zeroExtraPlayTimes);
782 }
783 
globalizeAllActions()784 void MainWindow::globalizeAllActions()
785 {
786     for (QAction *a : ui->menubar->actions()) {
787         addAction(a);
788     }
789     addAction(ui->actionPlaySeekForwards);
790     addAction(ui->actionPlaySeekForwardsFine);
791     addAction(ui->actionPlaySeekBackwards);
792     addAction(ui->actionPlaySeekBackwardsFine);
793 }
794 
setUiDecorationState(DecorationState state)795 void MainWindow::setUiDecorationState(DecorationState state)
796 {
797     QString actionTexts[] = { tr("Hide &Menu"), tr("Hide &Borders"),
798                               tr("Sho&w Caption and Menu") };
799     ui->actionViewHideMenu->setText(actionTexts[static_cast<int>(state)]);
800     decorationState_ = state;
801 
802     if (state == AllDecorations && !ui->menubar->isVisible()) {
803         ui->menubar->show();
804     } else if (state != AllDecorations && ui->menubar->isVisible()) {
805         ui->menubar->hide();
806     }
807 
808     updateWindowFlags();
809 }
810 
setOSDPage(int page)811 void MainWindow::setOSDPage(int page)
812 {
813     ui->actionViewOSDNone->setChecked(page == -1);
814     ui->actionViewOSDMessages->setChecked(page == 0);
815     ui->actionViewOSDStatistics->setChecked(page == 1);
816     ui->actionViewOSDFrameTimings->setChecked(page == 2);
817     mpvObject_->showStatsPage(page);
818 }
819 
setUiEnabledState(bool enabled)820 void MainWindow::setUiEnabledState(bool enabled)
821 {
822     positionSlider_->setEnabled(enabled);
823     if (!enabled) {
824         positionSlider_->setLoopA(-1);
825         positionSlider_->setLoopB(-1);
826     }
827 
828     ui->pause->setEnabled(enabled);
829     ui->stop->setEnabled(enabled);
830     ui->stepBackward->setEnabled(enabled);
831     ui->speedDecrease->setEnabled(enabled);
832     ui->speedIncrease->setEnabled(enabled);
833     ui->stepForward->setEnabled(enabled);
834     ui->skipBackward->setEnabled(enabled);
835     ui->skipForward->setEnabled(enabled);
836     ui->loopA->setEnabled(enabled);
837     ui->loopB->setEnabled(enabled);
838 
839     ui->mute->setEnabled(enabled);
840     volumeSlider()->setEnabled(enabled);
841 
842     ui->pause->setChecked(false);
843     ui->actionPlayPause->setChecked(false);
844 
845     ui->actionFileOpenDevice->setEnabled(false);
846     ui->actionFileClose->setEnabled(enabled);
847     ui->actionFileSaveCopy->setEnabled(enabled);
848     ui->actionFileSaveImage->setEnabled(enabled);
849     ui->actionFileSaveImageAuto->setEnabled(enabled);
850     ui->actionFileSavePlainImage->setEnabled(enabled);
851     ui->actionFileSavePlainImageAuto->setEnabled(enabled);
852     ui->actionFileSaveWindowImage->setEnabled(enabled);
853     ui->actionFileSaveWindowImageAuto->setEnabled(enabled);
854     ui->actionFileSaveThumbnails->setEnabled(enabled);
855     ui->actionFileExportEncode->setEnabled(enabled && false);
856     ui->actionFileLoadSubtitle->setEnabled(enabled);
857     ui->actionFileSaveSubtitle->setEnabled(enabled && false);
858     ui->actionFileSubtitleDatabaseDownload->setEnabled(enabled && false);
859     ui->actionPlayPause->setEnabled(enabled);
860     ui->actionPlayStop->setEnabled(enabled);
861     ui->actionPlayFrameBackward->setEnabled(enabled);
862     ui->actionPlayFrameForward->setEnabled(enabled);
863     ui->actionPlayRateDecrease->setEnabled(enabled);
864     ui->actionPlayRateIncrease->setEnabled(enabled);
865     ui->actionPlayRateReset->setEnabled(enabled);
866     ui->actionPlayVolumeUp->setEnabled(enabled);
867     ui->actionPlayVolumeDown->setEnabled(enabled);
868     ui->actionPlayVolumeMute->setEnabled(enabled);
869     ui->actionNavigateChaptersPrevious->setEnabled(enabled);
870     ui->actionNavigateChaptersNext->setEnabled(enabled);
871     ui->actionNavigateGoto->setEnabled(false);
872     ui->actionFavoritesAdd->setEnabled(enabled);
873 
874     ui->menuFileSubtitleDatabase->setEnabled(false);
875     ui->menuPlayLoop->setEnabled(enabled);
876     ui->menuPlayAudio->setEnabled(enabled);
877     ui->menuPlaySubtitles->setEnabled(enabled);
878     ui->menuPlayVideo->setEnabled(enabled);
879     ui->menuNavigateChapters->setEnabled(enabled && false);
880 }
881 
reparentBottomArea(bool overlay)882 void MainWindow::reparentBottomArea(bool overlay)
883 {
884     // TODO: changing the presentation method will need to check if the
885     // bottom area is inside the mpv widget!
886     bool inLayout = ui->centralwidget->layout()->indexOf(ui->bottomArea) >= 0;
887     if (overlay && inLayout) {
888         bottomAreaHeight = ui->bottomArea->height();
889         ui->centralwidget->layout()->removeWidget(ui->bottomArea);
890         ui->bottomArea->setParent(nullptr);
891         ui->bottomArea->setParent(mpvw ? mpvw : mpvHost_);
892     }
893     if (!overlay && !inLayout) {
894         ui->bottomArea->setParent(nullptr);
895         ui->centralwidget->layout()->addWidget(ui->bottomArea);
896         ui->bottomArea->show();
897     }
898 }
899 
checkBottomArea(QPoint mousePosition)900 void MainWindow::checkBottomArea(QPoint mousePosition)
901 {
902     if (!fullscreenMode_)
903         return;
904 
905     QPoint relMousePosition;
906     bool startTimer = false;
907     switch (bottomAreaBehavior) {
908     case Helpers::NeverShown:
909         if (ui->bottomArea->isVisible())
910             ui->bottomArea->hide();
911         break;
912     case Helpers::ShowWhenHovering:
913         if (mpvw == nullptr)
914             break;
915         relMousePosition = mpvw->mapFromGlobal(mousePosition);
916         if (ui->bottomArea->geometry().contains(relMousePosition)) {
917             if (ui->bottomArea->isHidden())
918                 ui->bottomArea->show();
919         } else if (ui->bottomArea->isVisible() && !hideTimer.isActive()) {
920             startTimer = true;
921         }
922         break;
923     case Helpers::ShowWhenMoving:
924         if (mpvw == nullptr)
925             break;
926         relMousePosition = mapFromGlobal(mousePosition);
927         if (mpvw->geometry().contains(relMousePosition)) {
928             if (ui->bottomArea->isHidden())
929                 ui->bottomArea->show();
930             startTimer = true;
931         } else if (ui->bottomArea->isVisible() && !hideTimer.isActive()) {
932             startTimer = true;
933         }
934         break;
935     case Helpers::AlwaysShow:
936         if (ui->bottomArea->isHidden())
937             ui->bottomArea->show();
938         break;
939     }
940 
941     if (startTimer) {
942         if (!bottomAreaHideTime)
943             ui->bottomArea->hide();
944         else
945             hideTimer.start();
946     }
947 }
948 
updateBottomAreaGeometry()949 void MainWindow::updateBottomAreaGeometry()
950 {
951     if (!fullscreenMode_)
952         return;
953 
954     QSize sz = size();
955     bottomAreaHeight = ui->bottomArea->height();
956     if (playlistWindow_->isVisible() && !fullscreenHidePanels)
957         sz -= QSize(playlistWindow_->width(), 0);
958     ui->bottomArea->setGeometry(0, sz.height() - bottomAreaHeight,
959                                 sz.width(), bottomAreaHeight);
960 }
961 
updateTime()962 void MainWindow::updateTime()
963 {
964     double length = mpvObject_->playLength();
965     double time = mpvObject_->playTime();
966     timeDuration->setTime(length, length);
967     timePosition->setTime(time, length);
968 }
969 
updateFramedrops()970 void MainWindow::updateFramedrops()
971 {
972     ui->framedrops->setText(QString("vo: %1, decoder: %2")
973                             .arg(displayDrops > 0 ? displayDrops : 0)
974                             .arg(decoderDrops > 0 ? decoderDrops : 0));
975 }
976 
updateBitrate()977 void MainWindow::updateBitrate()
978 {
979     ui->bitrate->setText(QString("v: %1 kb/s, a: %2 kb/s")
980                          .arg(std::lrint(videoBitrate / 1000))
981                          .arg(std::lrint(audioBitrate / 1000)));
982 }
983 
updateSize(bool first_run)984 void MainWindow::updateSize(bool first_run)
985 {
986     if (frozenWindow)
987         return;
988 
989     if (!first_run && (isMaximized() || isFullScreen()))
990         return;
991 
992     if (zoomMode == FitToWindow)
993         return;
994 
995     QSize desired = desirableSize(first_run);
996     QPoint desiredPlace = desirablePosition(desired, first_run);
997     QRect where = geometry();
998     if (zoomCenter)
999         where.moveTopLeft(desiredPlace);
1000     where.setSize(desired);
1001     setGeometry(where);
1002     show();
1003 }
1004 
updateInfostats()1005 void MainWindow::updateInfostats()
1006 {
1007     bool infoShow = ui->actionViewHideInformation->isChecked();
1008     bool statShow = ui->actionViewHideStatistics->isChecked();
1009 
1010     ui->chapter->setVisible(infoShow);
1011     ui->chapterLabel->setVisible(infoShow);
1012 
1013     ui->framerate->setVisible(statShow);
1014     ui->framerateLabel->setVisible(statShow);
1015     ui->avsync->setVisible(statShow);
1016     ui->avsyncLabel->setVisible(statShow);
1017     ui->framedrops->setVisible(statShow);
1018     ui->framedropsLabel->setVisible(statShow);
1019     ui->bitrate->setVisible(statShow);
1020     ui->bitrateLabel->setVisible(statShow);
1021 
1022     ui->infoStats->setVisible(infoShow || statShow);
1023 }
1024 
updateOnTop()1025 void MainWindow::updateOnTop()
1026 {
1027     switch (onTopMode) {
1028     case AlwaysOnTop:
1029         showOnTop = true;
1030         break;
1031     case OnTopWhenPlaying:
1032         showOnTop = isPlaying;
1033         break;
1034     case OnTopForVideos:
1035         showOnTop = isPlaying && hasVideo;
1036         break;
1037     default:
1038         showOnTop = false;
1039     }
1040     updateWindowFlags();
1041 }
1042 
updateWindowFlags()1043 void MainWindow::updateWindowFlags()
1044 {
1045     if (frozenWindow)
1046         return;
1047 
1048     Qt::WindowFlags flags = windowFlags();
1049     Qt::WindowFlags old = flags;
1050     bool isOnTop = flags.testFlag(Qt::WindowStaysOnTopHint);
1051     bool isFrameless = flags.testFlag(Qt::FramelessWindowHint);
1052 
1053     if (showOnTop && !isOnTop)
1054         flags |= Qt::WindowStaysOnTopHint;
1055     else if (!showOnTop && isOnTop)
1056         flags &= ~Qt::WindowStaysOnTopHint;
1057 
1058     if (decorationState_ == NoDecorations && !isFrameless)
1059         flags |= Qt::FramelessWindowHint;
1060     else if (decorationState_ != NoDecorations && isFrameless)
1061         flags &= ~Qt::FramelessWindowHint;
1062 
1063     if (old != flags) {
1064         QTimer::singleShot(50, this, [this,flags]() {
1065             setWindowFlags(flags);
1066             show();
1067         });
1068     }
1069 }
1070 
updateMouseHideTime()1071 void MainWindow::updateMouseHideTime()
1072 {
1073     mpvObject_->setMouseHideTime(fullscreenMode_
1074                                  ? mouseHideTimeFullscreen
1075                                  : mouseHideTimeWindowed);
1076 }
1077 
1078 
updateDiscList()1079 void MainWindow::updateDiscList()
1080 {
1081     bool addedSomething = false;
1082     auto func = [&](DeviceInfo *device) -> void {
1083         if (device->deviceType != DeviceInfo::OpticalDrive &&
1084             device->deviceType != DeviceInfo::RemovableDrive)
1085             return;
1086         QAction *a = new QAction(device->toDisplayString());
1087         connect(a, &QAction::triggered,
1088                 this, [this,device]() {
1089             if (Platform::deviceManager()->isDeviceValid(device)) {
1090                 device->mount();
1091                 if (!device->mountedPath.isEmpty())
1092                     emit dvdbdOpened(QUrl::fromLocalFile(device->mountedPath));
1093             }
1094         });
1095         a->setData(SKIPACTION);
1096         ui->menuFileOpenDisc->addAction(a);
1097         addedSomething = true;
1098     };
1099     ui->menuFileOpenDisc->clear();
1100     Platform::deviceManager()->iterateDevices(func);
1101     ui->menuFileOpenDisc->setEnabled(addedSomething);
1102 }
1103 
doQuickOpenFileDialog()1104 QList<QUrl> MainWindow::doQuickOpenFileDialog()
1105 {
1106     QList<QUrl> urls;
1107     static QUrl lastDir;
1108     static QString filter;
1109     if (filter.isEmpty())
1110         filter = Helpers::fileOpenFilter();
1111 
1112     urls = QFileDialog::getOpenFileUrls(this, tr("Quick Open"), lastDir, filter);
1113     if (!urls.isEmpty())
1114         lastDir = urls[0];
1115     return urls;
1116 }
1117 
setFreestanding(bool freestanding)1118 void MainWindow::setFreestanding(bool freestanding)
1119 {
1120     freestanding_ = freestanding;
1121     setMediaTitle(QString());
1122 }
1123 
setNoVideoSize(const QSize & size)1124 void MainWindow::setNoVideoSize(const QSize &size)
1125 {
1126     noVideoSize_ = size.expandedTo(QSize(500,270));
1127     updateSize();
1128 }
1129 
setWindowedMouseMap(const MouseStateMap & map)1130 void MainWindow::setWindowedMouseMap(const MouseStateMap &map)
1131 {
1132     mouseMapWindowed = map;
1133 }
1134 
setFullscreenMouseMap(const MouseStateMap & map)1135 void MainWindow::setFullscreenMouseMap(const MouseStateMap &map)
1136 {
1137     mouseMapFullscreen = map;
1138 }
1139 
setRecentDocuments(QList<TrackInfo> tracks)1140 void MainWindow::setRecentDocuments(QList<TrackInfo> tracks)
1141 {
1142     bool isEmpty = tracks.count() == 0;
1143     ui->menuFileRecent->clear();
1144     ui->menuFileRecent->setDisabled(isEmpty);
1145     if (isEmpty)
1146         return;
1147 
1148     for (int i = 0; i < tracks.count(); i++) {
1149         TrackInfo track = tracks[i];
1150         QString displayString = track.url.fileName();
1151         QAction *a = new QAction(QString("%1 - %2").arg(i).arg(displayString),
1152                                  this);
1153         connect(a, &QAction::triggered, this, [=]() {
1154             emit recentOpened(track);
1155         });
1156         ui->menuFileRecent->addAction(a);
1157     }
1158     ui->menuFileRecent->addSeparator();
1159     ui->menuFileRecent->addAction(ui->actionFileRecentClear);
1160 }
1161 
setFavoriteTracks(QList<TrackInfo> files,QList<TrackInfo> streams)1162 void MainWindow::setFavoriteTracks(QList<TrackInfo> files, QList<TrackInfo> streams)
1163 {
1164     auto addAction = [&](QAction *a) {
1165         menuFavoritesTail.append(a);
1166         ui->menuFavorites->addAction(a);
1167     };
1168     auto addSeperator = [&]() {
1169         auto a = new QAction(this);
1170         a->setSeparator(true);
1171         addAction(a);
1172     };
1173     auto addNotice = [&](QString notice) {
1174         auto a = new QAction(this);
1175         a->setText(notice);
1176         a->setEnabled(false);
1177         addAction(a);
1178     };
1179     auto addTracks = [&](const QList<TrackInfo> &tracks) {
1180         for (const auto &t : tracks) {
1181             auto a = new QAction(this);
1182             a->setText(t.text);
1183             connect(a, &QAction::triggered,
1184                     a, [t, this]() {
1185                 emit this->recentOpened(t);
1186             });
1187             addAction(a);
1188         }
1189     };
1190 
1191     for (auto a : qAsConst(menuFavoritesTail))
1192         delete a;
1193     menuFavoritesTail.clear();
1194 
1195     addSeperator();
1196     if (files.empty())
1197         addNotice(tr("No files favorited"));
1198     else
1199         addTracks(files);
1200     addSeperator();
1201     if (streams.empty())
1202         addNotice(tr("No streams favorited"));
1203     else
1204         addTracks(streams);
1205 }
1206 
setIconTheme(IconThemer::FolderMode mode,QString fallback,QString custom)1207 void MainWindow::setIconTheme(IconThemer::FolderMode mode, QString fallback, QString custom)
1208 {
1209     themer.setIconFolders(mode, fallback, custom);
1210 }
1211 
setInfoColors(const QColor & foreground,const QColor & background)1212 void MainWindow::setInfoColors(const QColor &foreground, const QColor &background)
1213 {
1214     QColor fg = foreground.isValid() ? foreground : QColor(255,255,255);
1215     QColor bg = background.isValid() ? background : QColor(0,0,0);
1216     QString css = QString("color: %1; background: %2;").arg(fg.name(), bg.name());
1217     ui->infoSection->setStyleSheet(css);
1218 }
1219 
setTime(double time,double length)1220 void MainWindow::setTime(double time, double length)
1221 {
1222     positionSlider_->setMaximum(length >= 0 ? length : 0);
1223     positionSlider_->setValue(time >= 0 ? time : 0);
1224     updateTime();
1225 }
1226 
setMediaTitle(QString title)1227 void MainWindow::setMediaTitle(QString title)
1228 {
1229     QString window_title("Media Player Classic Qute Theater");
1230 
1231     if (freestanding_)
1232         window_title.append(tr(" [Freestanding]"));
1233     if (!title.isEmpty())
1234         window_title.append(" - ").append(title);
1235     setWindowTitle(window_title);
1236 }
1237 
setChapterTitle(QString title)1238 void MainWindow::setChapterTitle(QString title)
1239 {
1240     ui->chapter->setText(!title.isEmpty() ? title : "-");
1241 }
1242 
setVideoSize(QSize size)1243 void MainWindow::setVideoSize(QSize size)
1244 {
1245     (void)size;
1246     updateSize();
1247 }
1248 
setVolumeStep(int stepSize)1249 void MainWindow::setVolumeStep(int stepSize)
1250 {
1251     volumeStep = stepSize;
1252 }
1253 
setSizeFactor(double factor)1254 void MainWindow::setSizeFactor(double factor)
1255 {
1256     sizeFactor_ = factor;
1257     if (sizeFactor_ != 0)
1258         updateSize();
1259 }
1260 
setFitFactor(double fitFactor)1261 void MainWindow::setFitFactor(double fitFactor)
1262 {
1263     fitFactor_ = fitFactor;
1264 }
1265 
setZoomMode(ZoomMode mode)1266 void MainWindow::setZoomMode(ZoomMode mode)
1267 {
1268     zoomMode = mode;
1269 }
1270 
setZoomPreset(int which,double fitFactor)1271 void MainWindow::setZoomPreset(int which, double fitFactor)
1272 {
1273     double factor[] = { 1.0, 1.0, 1.0, 1.0,
1274                         0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, 4.0 };
1275     ZoomMode mode[] = { Autofit, AutofitLarger, AutofitSmaller, FitToWindow,
1276                         RegularZoom, RegularZoom, RegularZoom, RegularZoom,
1277                         RegularZoom, RegularZoom, RegularZoom, RegularZoom };
1278     QAction *action[] = {
1279         ui->actionViewZoomAutofit, ui->actionViewZoomAutofitLarger,
1280         ui->actionViewZoomAutofitSmaller, ui->actionViewZoomDisable,
1281         ui->actionViewZoom025, ui->actionViewZoom050, ui->actionViewZoom075,
1282         ui->actionViewZoom100, ui->actionViewZoom150, ui->actionViewZoom200,
1283         ui->actionViewZoom300, ui->actionViewZoom400 };
1284 
1285     if (fitFactor >= 0.0)
1286         setFitFactor(fitFactor);
1287     setZoomMode(mode[which + 4]);
1288     setSizeFactor(factor[which + 4]);
1289     action[which+4]->setChecked(true);
1290 }
1291 
setZoomCenter(bool yes)1292 void MainWindow::setZoomCenter(bool yes)
1293 {
1294     zoomCenter = yes;
1295 }
1296 
setMouseHideTimeFullscreen(int msec)1297 void MainWindow::setMouseHideTimeFullscreen(int msec)
1298 {
1299     mouseHideTimeFullscreen = msec;
1300     updateMouseHideTime();
1301 }
1302 
setMouseHideTimeWindowed(int msec)1303 void MainWindow::setMouseHideTimeWindowed(int msec)
1304 {
1305     mouseHideTimeWindowed = msec;
1306     updateMouseHideTime();
1307 }
1308 
setBottomAreaBehavior(ControlHiding method)1309 void MainWindow::setBottomAreaBehavior(ControlHiding method)
1310 {
1311     bottomAreaBehavior = method;
1312     if (fullscreenMode_)
1313         checkBottomArea(QCursor::pos());
1314 }
1315 
setBottomAreaHideTime(int milliseconds)1316 void MainWindow::setBottomAreaHideTime(int milliseconds)
1317 {
1318     bottomAreaHideTime = milliseconds;
1319     hideTimer.setInterval(milliseconds);
1320 }
1321 
setTimeTooltip(bool shown,bool above)1322 void MainWindow::setTimeTooltip(bool shown, bool above)
1323 {
1324     timeTooltipShown = shown;
1325     timeTooltipAbove = above;
1326 }
1327 
setFullscreenHidePanels(bool hidden)1328 void MainWindow::setFullscreenHidePanels(bool hidden)
1329 {
1330     fullscreenHidePanels = hidden;
1331     if (fullscreenMode_ && !playlistWindow_->isFloating()) {
1332         if (hidden && playlistWindow_->isVisible()) {
1333             playlistWindow_->hide();
1334             updateBottomAreaGeometry();
1335         } else if (!hidden && playlistWindow_->isHidden()) {
1336             playlistWindow_->show();
1337             updateBottomAreaGeometry();
1338         }
1339     }
1340 }
1341 
setPlaybackState(PlaybackManager::PlaybackState state)1342 void MainWindow::setPlaybackState(PlaybackManager::PlaybackState state)
1343 {
1344     ui->status->setText(state==PlaybackManager::StoppedState ? tr("Stopped") :
1345                         state==PlaybackManager::PausedState ? tr("Paused") :
1346                         state==PlaybackManager::PlayingState ? tr("Playing") :
1347                         state==PlaybackManager::BufferingState ? tr("Buffering") :
1348                                                                  tr("Unknown"));
1349     isPlaying = state != PlaybackManager::StoppedState;
1350     isPaused = state == PlaybackManager::PausedState;
1351     setUiEnabledState(state != PlaybackManager::StoppedState);
1352     if (isPaused) {
1353         ui->actionPlayPause->setChecked(true);
1354         ui->pause->setChecked(true);
1355     }
1356     if (state == PlaybackManager::WaitingState) {
1357         mpvObject_->setLoopPoints(-1, -1);
1358         positionSlider_->setLoopA(-1);
1359         positionSlider_->setLoopB(-1);
1360     }
1361     updateOnTop();
1362 }
1363 
setPlaybackType(PlaybackManager::PlaybackType type)1364 void MainWindow::setPlaybackType(PlaybackManager::PlaybackType type)
1365 {
1366     setUiEnabledState(type != PlaybackManager::None);
1367 }
1368 
setChapters(QList<QPair<double,QString>> chapters)1369 void MainWindow::setChapters(QList<QPair<double, QString>> chapters)
1370 {
1371     positionSlider_->clearTicks();
1372     ui->menuNavigateChapters->clear();
1373     int64_t index = 0;
1374     for (const QPair<double,QString> &chapter : chapters) {
1375         positionSlider_->setTick(chapter.first, chapter.second);
1376         QAction *action = new QAction(this);
1377         action->setText(chapter.second);
1378         connect (action, &QAction::triggered, this, [this,index]() {
1379            emit chapterSelected(index);
1380         });
1381         ui->menuNavigateChapters->addAction(action);
1382         ++index;
1383     }
1384 }
1385 
setAudioTracks(QList<QPair<int64_t,QString>> tracks)1386 void MainWindow::setAudioTracks(QList<QPair<int64_t, QString>> tracks)
1387 {
1388     ui->menuPlayAudio->clear();
1389     for (const QPair<int64_t, QString> &track : tracks) {
1390         QAction *action = new QAction(this);
1391         action->setText(track.second);
1392         int64_t index = track.first;
1393         connect(action, &QAction::triggered, this, [this,index] {
1394             emit audioTrackSelected(index);
1395         });
1396         ui->menuPlayAudio->addAction(action);
1397     }
1398     hasAudio = !tracks.isEmpty();
1399 }
1400 
setVideoTracks(QList<QPair<int64_t,QString>> tracks)1401 void MainWindow::setVideoTracks(QList<QPair<int64_t, QString>> tracks)
1402 {
1403     ui->menuPlayVideo->clear();
1404     for (const QPair<int64_t, QString> &track : tracks) {
1405         QAction *action = new QAction(this);
1406         action->setText(track.second);
1407         int64_t index = track.first;
1408         connect(action, &QAction::triggered, this, [this,index]() {
1409            emit videoTrackSelected(index);
1410         });
1411         ui->menuPlayVideo->addAction(action);
1412     }
1413     hasVideo = !tracks.isEmpty();
1414     updateOnTop();
1415 }
1416 
setSubtitleTracks(QList<QPair<int64_t,QString>> tracks)1417 void MainWindow::setSubtitleTracks(QList<QPair<int64_t, QString> > tracks)
1418 {
1419     ui->menuPlaySubtitles->clear();
1420     hasSubs = !tracks.isEmpty();
1421     ui->actionPlaySubtitlesEnabled->setEnabled(hasSubs);
1422     ui->subs->setEnabled(hasSubs);
1423     ui->actionPlaySubtitlesNext->setEnabled(hasSubs);
1424     ui->actionPlaySubtitlesPrevious->setEnabled(hasSubs);
1425     if (!hasSubs)
1426         return;
1427     ui->menuPlaySubtitles->addAction(ui->actionPlaySubtitlesEnabled);
1428     ui->menuPlaySubtitles->addAction(ui->actionPlaySubtitlesNext);
1429     ui->menuPlaySubtitles->addAction(ui->actionPlaySubtitlesPrevious);
1430     ui->menuPlaySubtitles->addSeparator();
1431     for (const QPair<int64_t, QString> &track : tracks) {
1432         QAction *action = new QAction(this);
1433         action->setText(track.second);
1434         int64_t index = track.first;
1435         connect(action, &QAction::triggered, this, [this,index]() {
1436             emit subtitleTrackSelected(index);
1437         });
1438         ui->menuPlaySubtitles->addAction(action);
1439     }
1440 }
1441 
setVolume(int level)1442 void MainWindow::setVolume(int level)
1443 {
1444     volumeSlider_->setValue(level);
1445     emit volumeChanged(level);
1446 }
1447 
setVolumeDouble(double level)1448 void MainWindow::setVolumeDouble(double level)
1449 {
1450     volumeSlider_->setValue(level*100);
1451     emit volumeChanged(static_cast<int64_t>(level*100));
1452 }
1453 
setVolumeMax(int level)1454 void MainWindow::setVolumeMax(int level)
1455 {
1456     volumeSlider_->setMaximum(level);
1457 }
1458 
setTimeShortMode(bool shortened)1459 void MainWindow::setTimeShortMode(bool shortened)
1460 {
1461     timePosition->setShortMode(shortened);
1462     timeDuration->setShortMode(shortened);
1463 }
1464 
resetPlayAfterOnce()1465 void MainWindow::resetPlayAfterOnce()
1466 {
1467     ui->actionPlayAfterOnceNothing->setChecked(true);
1468 }
1469 
setPlayAfterAlways(AfterPlayback action)1470 void MainWindow::setPlayAfterAlways(AfterPlayback action)
1471 {
1472     QMap<Helpers::AfterPlayback, QAction*> map {
1473         { Helpers::DoNothingAfter, ui->actionPlayAfterAlwaysNothing },
1474         { Helpers::RepeatAfter, ui->actionPlayAfterAlwaysRepeat },
1475         { Helpers::PlayNextAfter, ui->actionPlayAfterAlwaysNext },
1476         { Helpers::ExitAfter, ui->actionPlayAfterAlwaysExit }
1477     };
1478     if (map.contains(action))
1479         map[action]->setChecked(true);
1480 }
1481 
setFps(double fps)1482 void MainWindow::setFps(double fps)
1483 {
1484     ui->framerate->setText(std::isnan(fps) ? "-" : QString::number(fps, 'f', 2));
1485 }
1486 
setAvsync(double sync)1487 void MainWindow::setAvsync(double sync)
1488 {
1489     ui->avsync->setText(std::isnan(sync) ? "-" : QString::number(sync, 'f', 3));
1490 }
1491 
setDisplayFramedrops(int64_t count)1492 void MainWindow::setDisplayFramedrops(int64_t count)
1493 {
1494     displayDrops = count;
1495     updateFramedrops();
1496 }
1497 
setDecoderFramedrops(int64_t count)1498 void MainWindow::setDecoderFramedrops(int64_t count)
1499 {
1500     decoderDrops = count;
1501     updateFramedrops();
1502 }
1503 
setAudioBitrate(double bitrate)1504 void MainWindow::setAudioBitrate(double bitrate)
1505 {
1506     audioBitrate = bitrate;
1507     updateBitrate();
1508 }
1509 
setVideoBitrate(double bitrate)1510 void MainWindow::setVideoBitrate(double bitrate)
1511 {
1512     videoBitrate = bitrate;
1513     updateBitrate();
1514 }
1515 
logWindowClosed()1516 void MainWindow::logWindowClosed()
1517 {
1518     ui->actionViewHideLog->setChecked(false);
1519 }
1520 
libraryWindowClosed()1521 void MainWindow::libraryWindowClosed()
1522 {
1523     ui->actionViewHideLibrary->setChecked(false);
1524 }
1525 
setPlaylistVisibleState(bool yes)1526 void MainWindow::setPlaylistVisibleState(bool yes) {
1527     if (fullscreenMode_)
1528         return;
1529     if (!ui)
1530         return;
1531     ui->actionFileOpenQuick->setText(yes ? tr("&Quick Add To Playlist")
1532                                          : tr("&Quick Open File"));
1533     ui->actionViewHidePlaylist->setChecked(yes);
1534 }
1535 
setPlaylistQuickQueueMode(bool yes)1536 void MainWindow::setPlaylistQuickQueueMode(bool yes)
1537 {
1538     if (!ui)
1539         return;
1540     ui->actionPlaylistShowQuickQueue->setChecked(yes);
1541     ui->actionPlaylistQuickQueue->setEnabled(!yes);
1542     ui->actionPlaylistQueueVisible->setEnabled(!yes);
1543 }
1544 
on_actionFileOpenQuick_triggered()1545 void MainWindow::on_actionFileOpenQuick_triggered()
1546 {
1547     QList<QUrl> urls = doQuickOpenFileDialog();
1548     if (urls.isEmpty())
1549         return;
1550     emit severalFilesOpened(urls);
1551 }
1552 
on_actionFileOpen_triggered()1553 void MainWindow::on_actionFileOpen_triggered()
1554 {
1555     OpenFileDialog d;
1556     if (!d.exec())
1557         return;
1558 
1559     emit fileOpened(d.file(), d.subs());
1560 }
1561 
on_actionFileOpenDvdbd_triggered()1562 void MainWindow::on_actionFileOpenDvdbd_triggered()
1563 {
1564     QUrl dir;
1565     static QUrl lastDir;
1566     dir = QFileDialog::getExistingDirectoryUrl(this, tr("Open Directory"),
1567                                                lastDir);
1568     if (dir.isEmpty())
1569         return;
1570     lastDir = dir;
1571     emit dvdbdOpened(dir);
1572 }
1573 
on_actionFileOpenDirectory_triggered()1574 void MainWindow::on_actionFileOpenDirectory_triggered()
1575 {
1576     QUrl url;
1577     static QUrl lastDir;
1578     url = QFileDialog::getExistingDirectoryUrl(this, tr("Open Directory"),
1579                                                lastDir);
1580     if (url.isEmpty())
1581         return;
1582     lastDir = url;
1583 
1584     // If we pass the directory and let Helpers::filterUrls handle it,
1585     // we may freeze if the user selects the root folder.  So only
1586     // pass through the folder contents for now.  To expand all
1587     // subfolders, Drag-and-drop or pass folders through the cli.
1588     QDir dir(url.toLocalFile());
1589     QList<QUrl> list;
1590     QFileInfoList f = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files);
1591     for (auto &file : f)
1592         list.append(QUrl::fromLocalFile(file.absoluteFilePath()));
1593     emit severalFilesOpened(list);
1594 }
1595 
on_actionFileOpenNetworkStream_triggered()1596 void MainWindow::on_actionFileOpenNetworkStream_triggered()
1597 {
1598     QInputDialog *qid = new QInputDialog(this);
1599     qid->setAttribute(Qt::WA_DeleteOnClose);
1600     qid->setWindowModality(Qt::WindowModal);
1601     qid->setWindowTitle(tr("Enter Network Stream"));
1602     qid->setLabelText(tr("Network Stream"));
1603     connect(qid, &QInputDialog::accepted, this, [=] () {
1604         emit streamOpened(QUrl::fromUserInput(qid->textValue()));
1605     });
1606     qid->show();
1607 }
1608 
on_actionFileRecentClear_triggered()1609 void MainWindow::on_actionFileRecentClear_triggered()
1610 {
1611     emit recentClear();
1612 }
1613 
on_actionFileSaveImage_triggered()1614 void MainWindow::on_actionFileSaveImage_triggered()
1615 {
1616     emit takeImage(Helpers::SubsRender);
1617 }
1618 
on_actionFileSaveImageAuto_triggered()1619 void MainWindow::on_actionFileSaveImageAuto_triggered()
1620 {
1621     emit takeImageAutomatically(Helpers::SubsRender);
1622 }
1623 
on_actionFileSavePlainImage_triggered()1624 void MainWindow::on_actionFileSavePlainImage_triggered()
1625 {
1626     emit takeImage(Helpers::VideoRender);
1627 }
1628 
on_actionFileSavePlainImageAuto_triggered()1629 void MainWindow::on_actionFileSavePlainImageAuto_triggered()
1630 {
1631     emit takeImageAutomatically(Helpers::VideoRender);
1632 }
1633 
on_actionFileSaveWindowImage_triggered()1634 void MainWindow::on_actionFileSaveWindowImage_triggered()
1635 {
1636     emit takeImage(Helpers::WindowRender);
1637 }
1638 
on_actionFileSaveWindowImageAuto_triggered()1639 void MainWindow::on_actionFileSaveWindowImageAuto_triggered()
1640 {
1641     emit takeImageAutomatically(Helpers::WindowRender);
1642 }
1643 
on_actionFileLoadSubtitle_triggered()1644 void MainWindow::on_actionFileLoadSubtitle_triggered()
1645 {
1646     QUrl url;
1647     static QUrl lastUrl;
1648     url = QFileDialog::getOpenFileUrl(this, tr("Open Subtitle"), lastUrl);
1649     if (url.isEmpty())
1650         return;
1651     lastUrl = url;
1652     emit subtitlesLoaded(url);
1653 }
1654 
on_actionFileClose_triggered()1655 void MainWindow::on_actionFileClose_triggered()
1656 {
1657     on_actionPlayStop_triggered();
1658 }
1659 
on_actionFileExit_triggered()1660 void MainWindow::on_actionFileExit_triggered()
1661 {
1662     close();
1663 }
1664 
on_actionViewHideMenu_triggered()1665 void MainWindow::on_actionViewHideMenu_triggered()
1666 {
1667     // View/hide are unmanaged when in fullscreen mode
1668     if (fullscreenMode_)
1669         return;
1670 
1671     DecorationState nextState[] = { NoMenu, NoDecorations, AllDecorations };
1672     setUiDecorationState(nextState[static_cast<int>(decorationState_)]);
1673     updateSize();
1674 }
1675 
on_actionViewHideSeekbar_toggled(bool checked)1676 void MainWindow::on_actionViewHideSeekbar_toggled(bool checked)
1677 {
1678     if (checked && ui->seekbar->isHidden())
1679         ui->seekbar->show();
1680     else if (!checked && ui->seekbar->isVisible())
1681         ui->seekbar->hide();
1682     ui->controlSection->adjustSize();
1683     updateSize();
1684 }
1685 
on_actionViewHideControls_toggled(bool checked)1686 void MainWindow::on_actionViewHideControls_toggled(bool checked)
1687 {
1688     if (checked && ui->controlbar->isHidden())
1689         ui->controlbar->show();
1690     else if (!checked && ui->controlbar->isVisible())
1691         ui->controlbar->hide();
1692     ui->controlSection->adjustSize();
1693     updateSize();
1694 }
1695 
on_actionViewHideInformation_toggled(bool checked)1696 void MainWindow::on_actionViewHideInformation_toggled(bool checked)
1697 {
1698     Q_UNUSED(checked)
1699     updateInfostats();
1700     updateSize();
1701 }
1702 
on_actionViewHideStatistics_toggled(bool checked)1703 void MainWindow::on_actionViewHideStatistics_toggled(bool checked)
1704 {
1705     Q_UNUSED(checked)
1706     updateInfostats();
1707     updateSize();
1708 }
1709 
on_actionViewHideStatus_toggled(bool checked)1710 void MainWindow::on_actionViewHideStatus_toggled(bool checked)
1711 {
1712     if (checked && ui->statusbar->isHidden())
1713         ui->statusbar->show();
1714     else if (!checked && ui->statusbar->isVisible())
1715         ui->statusbar->hide();
1716     updateSize();
1717 }
1718 
on_actionViewHideSubresync_toggled(bool checked)1719 void MainWindow::on_actionViewHideSubresync_toggled(bool checked)
1720 {
1721     (void)checked;
1722 
1723     updateSize();
1724 }
1725 
on_actionViewHidePlaylist_toggled(bool checked)1726 void MainWindow::on_actionViewHidePlaylist_toggled(bool checked)
1727 {
1728     if (fullscreenMode_ && fullscreenHidePanels)
1729         return;
1730 
1731     if (isMinimized())
1732         return;
1733 
1734     if (checked && playlistWindow_->isHidden())
1735         playlistWindow_->show();
1736     else if (!checked && playlistWindow_->isVisible())
1737         playlistWindow_->hide();
1738 
1739     if (fullscreenMode_ && !fullscreenHidePanels)
1740         updateBottomAreaGeometry();
1741 
1742     updateSize();
1743 }
1744 
on_actionViewHideCapture_toggled(bool checked)1745 void MainWindow::on_actionViewHideCapture_toggled(bool checked)
1746 {
1747     (void)checked;
1748 
1749     updateSize();
1750 }
1751 
on_actionViewHideNavigation_toggled(bool checked)1752 void MainWindow::on_actionViewHideNavigation_toggled(bool checked)
1753 {
1754     (void)checked;
1755 
1756     updateSize();
1757 }
1758 
1759 
on_actionViewHideLog_toggled(bool checked)1760 void MainWindow::on_actionViewHideLog_toggled(bool checked)
1761 {
1762     if (checked)
1763         emit showLogWindow();
1764     else
1765         emit hideLogWindow();
1766 }
1767 
on_actionViewHideLibrary_toggled(bool checked)1768 void MainWindow::on_actionViewHideLibrary_toggled(bool checked)
1769 {
1770     if (checked)
1771         emit showLibraryWindow();
1772     else
1773         emit hideLibraryWindow();
1774 }
1775 
on_actionViewPresetsMinimal_triggered()1776 void MainWindow::on_actionViewPresetsMinimal_triggered()
1777 {
1778     setUiDecorationState(NoDecorations);
1779     ui->actionViewHideSeekbar->setChecked(false);
1780     ui->actionViewHideControls->setChecked(false);
1781     ui->actionViewHideInformation->setChecked(false);
1782     ui->actionViewHideStatistics->setChecked(false);
1783     ui->actionViewHideStatus->setChecked(false);
1784     ui->actionViewHideSubresync->setChecked(false);
1785     ui->actionViewHideCapture->setChecked(false);
1786     ui->actionViewHideNavigation->setChecked(false);
1787 }
1788 
on_actionViewPresetsCompact_triggered()1789 void MainWindow::on_actionViewPresetsCompact_triggered()
1790 {
1791     // we should set our menu state to something like Framed, but we can't
1792     // reliably do that across window managers.
1793     setUiDecorationState(NoDecorations);
1794     ui->actionViewHideMenu->setChecked(true);
1795     ui->actionViewHideSeekbar->setChecked(true);
1796     ui->actionViewHideControls->setChecked(false);
1797     ui->actionViewHideInformation->setChecked(false);
1798     ui->actionViewHideStatistics->setChecked(false);
1799     ui->actionViewHideStatus->setChecked(false);
1800     ui->actionViewHideSubresync->setChecked(false);
1801     ui->actionViewHideCapture->setChecked(false);
1802     ui->actionViewHideNavigation->setChecked(false);
1803 }
1804 
on_actionViewPresetsNormal_triggered()1805 void MainWindow::on_actionViewPresetsNormal_triggered()
1806 {
1807     setUiDecorationState(AllDecorations);
1808     ui->actionViewHideMenu->setChecked(true);
1809     ui->actionViewHideSeekbar->setChecked(true);
1810     ui->actionViewHideControls->setChecked(true);
1811     ui->actionViewHideInformation->setChecked(false);
1812     ui->actionViewHideStatistics->setChecked(false);
1813     ui->actionViewHideStatus->setChecked(true);
1814     ui->actionViewHideSubresync->setChecked(false);
1815     ui->actionViewHideCapture->setChecked(false);
1816     ui->actionViewHideNavigation->setChecked(false);
1817 }
1818 
on_actionViewOSDNone_triggered()1819 void MainWindow::on_actionViewOSDNone_triggered()
1820 {
1821     mpvObject_->showStatsPage(-1);
1822 }
1823 
on_actionViewOSDMessages_triggered()1824 void MainWindow::on_actionViewOSDMessages_triggered()
1825 {
1826     mpvObject_->showStatsPage(0);
1827 }
1828 
on_actionViewOSDStatistics_triggered()1829 void MainWindow::on_actionViewOSDStatistics_triggered()
1830 {
1831     mpvObject_->showStatsPage(1);
1832 }
1833 
on_actionViewOSDFrameTimings_triggered()1834 void MainWindow::on_actionViewOSDFrameTimings_triggered()
1835 {
1836     mpvObject_->showStatsPage(2);
1837 }
1838 
on_actionViewOSDCycle_triggered()1839 void MainWindow::on_actionViewOSDCycle_triggered()
1840 {
1841     QActionGroup *osdActionGroup = ui->actionViewOSDMessages->actionGroup();
1842     QAction *nextOsdAction;
1843     int newpage;
1844 
1845     newpage = mpvObject_->cycleStatsPage();
1846     nextOsdAction = osdActionGroup->actions().value(newpage+1, nullptr);
1847     if (nextOsdAction)
1848         nextOsdAction->setChecked(true);
1849 }
1850 
on_actionViewFullscreen_toggled(bool checked)1851 void MainWindow::on_actionViewFullscreen_toggled(bool checked)
1852 {
1853     setFullscreenMode(checked);
1854     reparentBottomArea(checked);
1855 
1856     if (checked) {
1857         menuBar()->hide();
1858         if (fullscreenHidePanels && !playlistWindow_->isFloating())
1859             playlistWindow_->hide();
1860     } else {
1861         if (decorationState_ == AllDecorations)
1862             menuBar()->show();
1863         ui->bottomArea->show();
1864         if (ui->actionViewHidePlaylist->isChecked())
1865             playlistWindow_->show();
1866     }
1867 }
1868 
on_actionViewFullscreenEscape_triggered()1869 void MainWindow::on_actionViewFullscreenEscape_triggered()
1870 {
1871     ui->actionViewFullscreen->setChecked(false);
1872 }
1873 
on_actionViewZoom025_triggered()1874 void MainWindow::on_actionViewZoom025_triggered()
1875 {
1876     setZoomPreset(0);
1877     emit zoomPresetChanged(0);
1878 }
1879 
on_actionViewZoom050_triggered()1880 void MainWindow::on_actionViewZoom050_triggered()
1881 {
1882     setZoomPreset(1);
1883     emit zoomPresetChanged(1);
1884 }
1885 
on_actionViewZoom075_triggered()1886 void MainWindow::on_actionViewZoom075_triggered()
1887 {
1888     setZoomPreset(2);
1889     emit zoomPresetChanged(2);
1890 }
1891 
on_actionViewZoom100_triggered()1892 void MainWindow::on_actionViewZoom100_triggered()
1893 {
1894     setZoomPreset(3);
1895     emit zoomPresetChanged(3);
1896 }
1897 
on_actionViewZoom150_triggered()1898 void MainWindow::on_actionViewZoom150_triggered()
1899 {
1900     setZoomPreset(4);
1901     emit zoomPresetChanged(4);
1902 }
1903 
on_actionViewZoom200_triggered()1904 void MainWindow::on_actionViewZoom200_triggered()
1905 {
1906     setZoomPreset(5);
1907     emit zoomPresetChanged(5);
1908 }
1909 
on_actionViewZoom300_triggered()1910 void MainWindow::on_actionViewZoom300_triggered()
1911 {
1912     setZoomPreset(6);
1913     emit zoomPresetChanged(6);
1914 }
1915 
on_actionViewZoom400_triggered()1916 void MainWindow::on_actionViewZoom400_triggered()
1917 {
1918     setZoomPreset(7);
1919     emit zoomPresetChanged(7);
1920 }
1921 
on_actionViewZoomAutofit_triggered()1922 void MainWindow::on_actionViewZoomAutofit_triggered()
1923 {
1924     setZoomPreset(-4);
1925     emit zoomPresetChanged(-4);
1926 }
1927 
on_actionViewZoomAutofitLarger_triggered()1928 void MainWindow::on_actionViewZoomAutofitLarger_triggered()
1929 {
1930     setZoomPreset(-3);
1931     emit zoomPresetChanged(-3);
1932 }
1933 
on_actionViewZoomAutofitSmaller_triggered()1934 void MainWindow::on_actionViewZoomAutofitSmaller_triggered()
1935 {
1936     setZoomPreset(-2);
1937     emit zoomPresetChanged(-2);
1938 }
1939 
on_actionViewZoomDisable_triggered()1940 void MainWindow::on_actionViewZoomDisable_triggered()
1941 {
1942     setZoomPreset(-1);
1943     emit zoomPresetChanged(-1);
1944 }
1945 
on_actionViewOntopDefault_toggled(bool checked)1946 void MainWindow::on_actionViewOntopDefault_toggled(bool checked)
1947 {
1948     if (!checked)
1949         return;
1950     onTopMode = OnTopDefault;
1951     updateOnTop();
1952 }
1953 
on_actionViewOntopAlways_toggled(bool checked)1954 void MainWindow::on_actionViewOntopAlways_toggled(bool checked)
1955 {
1956     if (!checked)
1957         return;
1958     onTopMode = AlwaysOnTop;
1959     updateOnTop();
1960 }
1961 
on_actionViewOntopPlaying_toggled(bool checked)1962 void MainWindow::on_actionViewOntopPlaying_toggled(bool checked)
1963 {
1964     if (!checked)
1965         return;
1966     onTopMode = OnTopWhenPlaying;
1967     updateOnTop();
1968 }
1969 
on_actionViewOntopVideo_toggled(bool checked)1970 void MainWindow::on_actionViewOntopVideo_toggled(bool checked)
1971 {
1972     if (!checked)
1973         return;
1974     onTopMode = OnTopForVideos;
1975     updateOnTop();
1976 }
1977 
on_actionViewOptions_triggered()1978 void MainWindow::on_actionViewOptions_triggered()
1979 {
1980     emit optionsOpenRequested();
1981 }
1982 
on_actionPlayPause_triggered(bool checked)1983 void MainWindow::on_actionPlayPause_triggered(bool checked)
1984 {
1985     if (checked)
1986         emit paused();
1987     else
1988         emit unpaused();
1989 }
1990 
on_actionPlayStop_triggered()1991 void MainWindow::on_actionPlayStop_triggered()
1992 {
1993     emit stopped();
1994     isPlaying = false;
1995     updateSize();
1996 }
1997 
on_actionPlayFrameBackward_triggered()1998 void MainWindow::on_actionPlayFrameBackward_triggered()
1999 {
2000     emit stepBackward();
2001 }
2002 
on_actionPlayFrameForward_triggered()2003 void MainWindow::on_actionPlayFrameForward_triggered()
2004 {
2005     emit stepForward();
2006 }
2007 
on_actionPlayRateDecrease_triggered()2008 void MainWindow::on_actionPlayRateDecrease_triggered()
2009 {
2010     emit speedDown();
2011 }
2012 
on_actionPlayRateIncrease_triggered()2013 void MainWindow::on_actionPlayRateIncrease_triggered()
2014 {
2015     emit speedUp();
2016 }
2017 
on_actionPlayRateReset_triggered()2018 void MainWindow::on_actionPlayRateReset_triggered()
2019 {
2020     emit speedReset();
2021 }
2022 
on_actionPlaySeekForwards_triggered()2023 void MainWindow::on_actionPlaySeekForwards_triggered()
2024 {
2025     emit relativeSeek(true, false);
2026 }
2027 
on_actionPlaySeekBackwards_triggered()2028 void MainWindow::on_actionPlaySeekBackwards_triggered()
2029 {
2030     emit relativeSeek(false, false);
2031 }
2032 
on_actionPlaySeekForwardsFine_triggered()2033 void MainWindow::on_actionPlaySeekForwardsFine_triggered()
2034 {
2035     emit relativeSeek(true, true);
2036 }
2037 
on_actionPlaySeekBackwardsFine_triggered()2038 void MainWindow::on_actionPlaySeekBackwardsFine_triggered()
2039 {
2040     emit relativeSeek(false, true);
2041 }
2042 
on_actionPlaySubtitlesEnabled_triggered(bool checked)2043 void MainWindow::on_actionPlaySubtitlesEnabled_triggered(bool checked)
2044 {
2045     emit subtitlesEnabled(checked);
2046     ui->actionPlaySubtitlesEnabled->setChecked(checked);
2047     ui->subs->setChecked(!checked);
2048 }
2049 
on_actionPlaySubtitlesNext_triggered()2050 void MainWindow::on_actionPlaySubtitlesNext_triggered()
2051 {
2052     emit nextSubtitleSelected();
2053 }
2054 
on_actionPlaySubtitlesPrevious_triggered()2055 void MainWindow::on_actionPlaySubtitlesPrevious_triggered()
2056 {
2057     emit previousSubtitleSelected();
2058 }
2059 
on_actionPlayLoopStart_triggered()2060 void MainWindow::on_actionPlayLoopStart_triggered()
2061 {
2062     positionSlider_->setLoopA(mpvObject_->playTime());
2063     if (ui->actionPlayLoopUse->isChecked())
2064         mpvObject_->setLoopPoints(positionSlider_->loopA(),
2065                                   positionSlider_->loopB());
2066 }
2067 
on_actionPlayLoopEnd_triggered()2068 void MainWindow::on_actionPlayLoopEnd_triggered()
2069 {
2070     positionSlider_->setLoopB(mpvObject_->playTime());
2071     if (ui->actionPlayLoopUse->isChecked())
2072         mpvObject_->setLoopPoints(positionSlider_->loopA(),
2073                                   positionSlider_->loopB());
2074 }
2075 
on_actionPlayLoopUse_triggered(bool checked)2076 void MainWindow::on_actionPlayLoopUse_triggered(bool checked)
2077 {
2078     if (checked && !positionSlider_->isLoopEmpty()) {
2079         mpvObject_->setLoopPoints(positionSlider_->loopA(),
2080                                   positionSlider_->loopB());
2081     } else if (checked) {
2082         ui->actionPlayLoopUse->setChecked(false);
2083     } else {
2084         mpvObject_->setLoopPoints(-1, -1);
2085     }
2086 }
2087 
on_actionPlayLoopClear_triggered()2088 void MainWindow::on_actionPlayLoopClear_triggered()
2089 {
2090     ui->actionPlayLoopUse->setChecked(false);
2091     mpvObject_->setLoopPoints(-1, -1);
2092     positionSlider_->setLoopA(-1);
2093     positionSlider_->setLoopB(-1);
2094 }
2095 
on_actionPlayVolumeUp_triggered()2096 void MainWindow::on_actionPlayVolumeUp_triggered()
2097 {
2098     int newvol = int(std::min(volumeSlider_->value() + volumeStep, volumeSlider_->maximum()));
2099     emit volumeChanged(newvol);
2100     volumeSlider_->setValue(newvol);
2101 }
2102 
on_actionPlayVolumeDown_triggered()2103 void MainWindow::on_actionPlayVolumeDown_triggered()
2104 {
2105     int newvol = int(std::max(volumeSlider_->value() - volumeStep, volumeSlider_->minimum()));
2106     emit volumeChanged(newvol);
2107     volumeSlider_->setValue(newvol);
2108 }
2109 
on_actionPlayVolumeMute_toggled(bool checked)2110 void MainWindow::on_actionPlayVolumeMute_toggled(bool checked)
2111 {
2112     if (!isPlaying)
2113         return;
2114     emit volumeMuteChanged(checked);
2115     ui->actionPlayVolumeMute->setChecked(checked);
2116     ui->mute->setChecked(checked);
2117 }
2118 
on_actionPlayAfterOnceExit_triggered()2119 void MainWindow::on_actionPlayAfterOnceExit_triggered()
2120 {
2121     emit afterPlaybackOnce(Helpers::ExitAfter);
2122 }
2123 
on_actionPlayAfterOnceStandby_triggered()2124 void MainWindow::on_actionPlayAfterOnceStandby_triggered()
2125 {
2126     emit afterPlaybackOnce(Helpers::StandByAfter);
2127 }
2128 
on_actionPlayAfterOnceHibernate_triggered()2129 void MainWindow::on_actionPlayAfterOnceHibernate_triggered()
2130 {
2131     emit afterPlaybackOnce(Helpers::HibernateAfter);
2132 }
2133 
on_actionPlayAfterOnceShutdown_triggered()2134 void MainWindow::on_actionPlayAfterOnceShutdown_triggered()
2135 {
2136     emit afterPlaybackOnce(Helpers::ShutdownAfter);
2137 }
2138 
on_actionPlayAfterOnceLogoff_triggered()2139 void MainWindow::on_actionPlayAfterOnceLogoff_triggered()
2140 {
2141     emit afterPlaybackOnce(Helpers::LogOffAfter);
2142 }
2143 
on_actionPlayAfterOnceLock_triggered()2144 void MainWindow::on_actionPlayAfterOnceLock_triggered()
2145 {
2146     emit afterPlaybackOnce(Helpers::LockAfter);
2147 }
2148 
on_actionPlayAfterOnceNothing_triggered()2149 void MainWindow::on_actionPlayAfterOnceNothing_triggered()
2150 {
2151     emit afterPlaybackOnce(Helpers::DoNothingAfter);
2152 }
2153 
on_actionPlayAfterOnceRepeat_triggered()2154 void MainWindow::on_actionPlayAfterOnceRepeat_triggered()
2155 {
2156     emit afterPlaybackOnce(Helpers::RepeatAfter);
2157 }
2158 
on_actionPlayAfterAlwaysRepeat_triggered()2159 void MainWindow::on_actionPlayAfterAlwaysRepeat_triggered()
2160 {
2161     emit afterPlaybackAlways(Helpers::RepeatAfter);
2162 }
2163 
on_actionPlayAfterAlwaysExit_triggered()2164 void MainWindow::on_actionPlayAfterAlwaysExit_triggered()
2165 {
2166     emit afterPlaybackAlways(Helpers::ExitAfter);
2167 }
2168 
on_actionPlayAfterAlwaysNothing_triggered()2169 void MainWindow::on_actionPlayAfterAlwaysNothing_triggered()
2170 {
2171     emit afterPlaybackAlways(Helpers::DoNothingAfter);
2172 }
2173 
on_actionPlayAfterAlwaysNext_triggered()2174 void MainWindow::on_actionPlayAfterAlwaysNext_triggered()
2175 {
2176     emit afterPlaybackAlways(Helpers::PlayNextAfter);
2177 }
2178 
on_actionNavigateChaptersPrevious_triggered()2179 void MainWindow::on_actionNavigateChaptersPrevious_triggered()
2180 {
2181     emit chapterPrevious();
2182 }
2183 
on_actionNavigateChaptersNext_triggered()2184 void MainWindow::on_actionNavigateChaptersNext_triggered()
2185 {
2186     emit chapterNext();
2187 }
2188 
on_actionHelpHomepage_triggered()2189 void MainWindow::on_actionHelpHomepage_triggered()
2190 {
2191     QDesktopServices::openUrl(QUrl("https://github.com/cmdrkotori/mpc-qt-origin"));
2192 }
2193 
on_actionHelpAbout_triggered()2194 void MainWindow::on_actionHelpAbout_triggered()
2195 {
2196 #if defined(MPCQT_DEVELOPMENT)
2197 #define BUILD_VERSION_STR QString("%1 (%2)").arg(devBuild, MPCQT_VERSION_STR)
2198 #elif defined(MPCQT_VERSION_STR)
2199 #define BUILD_VERSION_STR versionFmt.arg(MPCQT_VERSION_STR)
2200 #else
2201 #define BUILD_VERSION_STR devBuild
2202 #endif
2203     QString devBuild = tr("Development Build");
2204     QString versionFmt = tr("Version %1");
2205     QString dateLineFmt = tr("Built on %1 at %2");
2206     QString dateLine;
2207 
2208 #if defined(MPCQT_DEVELOPMENT)
2209     QDate buildDate = Helpers::dateFromCFormat(__DATE__);
2210     QTime buildTime = Helpers::timeFromCFormat(__TIME__);
2211     dateLine = "<br>" + dateLineFmt.arg(buildDate.toString(Qt::DefaultLocaleShortDate),
2212                                         buildTime.toString(Qt::DefaultLocaleShortDate));
2213 #endif
2214 
2215     QMessageBox::about(this, tr("About Media Player Classic Qute Theater"),
2216       "<h2>" + tr("Media Player Classic Qute Theater") + "</h2>" +
2217       "<p>" +  tr("A clone of Media Player Classic written in Qt") +
2218       "<br>" + tr("Based on Qt %1 and %2").arg(QT_VERSION_STR, mpvObject_->mpvVersion()) +
2219       "<p>" +  BUILD_VERSION_STR +
2220       dateLine +
2221       "<h3>LICENSE</h3>"
2222       "<p>   Copyright (C) 2015"
2223       "<p>"
2224       "This program is free software; you can redistribute it and/or modify "
2225       "it under the terms of the GNU General Public License as published by "
2226       "the Free Software Foundation; either version 2 of the License, or "
2227       "(at your option) any later version."
2228       "<p>"
2229       "This program is distributed in the hope that it will be useful, "
2230       "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2231       "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
2232       "GNU General Public License for more details."
2233       "<p>"
2234       "You should have received a copy of the GNU General Public License "
2235       "along with this program; if not, write to the Free Software "
2236       "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA "
2237       "02110-1301 USA.");
2238 }
2239 
2240 
mpvw_customContextMenuRequested(const QPoint & pos)2241 void MainWindow::mpvw_customContextMenuRequested(const QPoint &pos)
2242 {
2243     if (mpvw == nullptr)
2244         return;
2245     contextMenu->popup(mpvw->mapToGlobal(pos));
2246 }
2247 
position_sliderMoved(int position)2248 void MainWindow::position_sliderMoved(int position)
2249 {
2250     emit timeSelected(position);
2251 }
2252 
position_hoverValue(double value,QString text,double x)2253 void MainWindow::position_hoverValue(double value, QString text, double x)
2254 {
2255     if (!timeTooltipShown)
2256         return;
2257     if (text.isEmpty())
2258         text = tr("<unknown>");
2259 
2260     QString t = QString("%1 - %2").arg(Helpers::toDateFormat(value), text);
2261     QPoint where = positionSlider_->mapToGlobal(QPoint(int(x), timeTooltipAbove ? -40 : 0));
2262     QToolTip::showText(where, t, positionSlider_);
2263 
2264     //FIXME: use a widget not the system tooltip?
2265 }
2266 
on_play_clicked()2267 void MainWindow::on_play_clicked()
2268 {
2269     if (!isPlaying) {
2270         emit playCurrentItemRequested();
2271         return;
2272     }
2273     if (isPaused) {
2274         emit unpaused();
2275         isPaused = false;
2276         ui->pause->setChecked(false);
2277     } else {
2278         emit paused();
2279         isPaused = true;
2280         ui->pause->setChecked(true);
2281     }
2282 }
2283 
volume_sliderMoved(double position)2284 void MainWindow::volume_sliderMoved(double position)
2285 {
2286     emit volumeChanged(int(position));
2287 }
2288 
playlistWindow_windowDocked()2289 void MainWindow::playlistWindow_windowDocked()
2290 {
2291     if (fullscreenMode_ && fullscreenHidePanels)
2292         playlistWindow_->hide();
2293 }
2294 
playlistWindow_playlistAddItem(const QUuid & playlistUuid)2295 void MainWindow::playlistWindow_playlistAddItem(const QUuid &playlistUuid)
2296 {
2297     QList<QUrl> urls = doQuickOpenFileDialog();
2298     if (urls.isEmpty())
2299         return;
2300     emit severalFilesOpenedForPlaylist(playlistUuid, urls);
2301 }
2302 
hideTimer_timeout()2303 void MainWindow::hideTimer_timeout()
2304 {
2305     if (mpvw == nullptr)
2306         return;
2307     if (fullscreenMode_ &&
2308             !ui->bottomArea->geometry().contains(mpvw->mapFromGlobal(QCursor::pos())))
2309         ui->bottomArea->hide();
2310 }
2311 
on_actionPlaylistSearch_triggered()2312 void MainWindow::on_actionPlaylistSearch_triggered()
2313 {
2314     if (playlistWindow_->isHidden())
2315         playlistWindow_->show();
2316     playlistWindow_->revealSearch();
2317 }
2318 
2319 
on_actionFavoritesAdd_triggered()2320 void MainWindow::on_actionFavoritesAdd_triggered()
2321 {
2322     emit favoriteCurrentTrack();
2323 }
2324 
on_actionFavoritesOrganize_triggered()2325 void MainWindow::on_actionFavoritesOrganize_triggered()
2326 {
2327     emit organizeFavorites();
2328 }
2329