X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fmainwindow.cpp;h=4a3a480ef200d8594bcd428c9e254f5360d20c24;hb=434d88418722fd7717038e44bd74271ca1d92771;hp=437571638bc72698518282238b8f8b3b42d1ebc0;hpb=7ca7741e1eb9bc70617fbb4207738e291218c495;p=minitube diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4375716..4a3a480 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -19,19 +19,22 @@ along with Minitube. If not, see . $END_LICENSE */ #include "mainwindow.h" -#include "homeview.h" -#include "searchview.h" -#include "mediaview.h" + #include "aboutview.h" #include "downloadview.h" -#include "spacer.h" +#include "homeview.h" +#include "mediaview.h" +#include "regionsview.h" +#include "searchview.h" +#include "standardfeedsview.h" + #include "constants.h" -#include "iconutils.h" -#include "global.h" -#include "videodefinition.h" #include "fontutils.h" #include "globalshortcuts.h" +#include "iconutils.h" #include "searchparams.h" +#include "spacer.h" +#include "videodefinition.h" #include "videosource.h" #include "ytsearch.h" #ifdef APP_LINUX @@ -44,9 +47,9 @@ $END_LICENSE */ #include "macutils.h" #endif #include "downloadmanager.h" -#include "ytsuggester.h" -#include "updatechecker.h" #include "temporary.h" +#include "updatechecker.h" +#include "ytsuggester.h" #if defined(APP_MAC_SEARCHFIELD) && !defined(APP_MAC_QMACTOOLBAR) #include "searchlineedit_mac.h" #else @@ -57,72 +60,67 @@ $END_LICENSE */ #endif #include #ifdef APP_EXTRA +#include "compositefader.h" #include "extra.h" #include "updatedialog.h" #endif #ifdef APP_ACTIVATION #include "activation.h" #include "activationview.h" -#include "activationdialog.h" #endif -#include "ytregions.h" -#include "regionsview.h" -#include "standardfeedsview.h" #include "channelaggregator.h" #include "database.h" -#include "videoareawidget.h" +#include "httputils.h" #include "jsfunctions.h" #include "seekslider.h" -#ifdef APP_YT3 +#include "sidebarwidget.h" +#include "toolbarmenu.h" +#include "videoarea.h" #include "yt3.h" +#include "ytregions.h" + +#ifdef MEDIA_QTAV +#include "mediaqtav.h" +#endif +#ifdef MEDIA_MPV +#include "mediampv.h" #endif namespace { -static MainWindow *singleton = 0; +MainWindow *mainWindowInstance; } -MainWindow* MainWindow::instance() { - return singleton; +MainWindow *MainWindow::instance() { + return mainWindowInstance; } -MainWindow::MainWindow() : - updateChecker(0), - aboutView(0), - downloadView(0), - regionsView(0), - mainToolBar(0), - #ifdef APP_PHONON - mediaObject(0), - audioOutput(0), - #endif - fullscreenFlag(false), - m_compact(false), - initialized(false) { +MainWindow::MainWindow() + : aboutView(nullptr), downloadView(nullptr), regionsView(nullptr), mainToolBar(nullptr), + fullScreenActive(false), compactModeActive(false), initialized(false), toolbarMenu(nullptr), + media(nullptr) { + mainWindowInstance = this; - singleton = this; + // views mechanism + views = new QStackedWidget(); + setCentralWidget(views); #ifdef APP_EXTRA Extra::windowSetup(this); #endif - // views mechanism - views = new QStackedWidget(); - views->hide(); - setCentralWidget(views); - - messageLabel = new QLabel(); - messageLabel->setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint); - messageLabel->setStyleSheet("padding:5px;border:1px solid #808080;background:palette(window)"); + messageLabel = new QLabel(this); + messageLabel->setWordWrap(false); + messageLabel->setStyleSheet("padding:5px;border:0;background:palette(window)"); + messageLabel->setAlignment(Qt::AlignCenter); messageLabel->hide(); adjustMessageLabelPosition(); messageTimer = new QTimer(this); messageTimer->setInterval(5000); messageTimer->setSingleShot(true); - connect(messageTimer, SIGNAL(timeout()), messageLabel, SLOT(hide())); - connect(messageTimer, SIGNAL(timeout()), messageLabel, SLOT(clear())); + connect(messageTimer, SIGNAL(timeout()), SLOT(hideMessage())); // views - homeView = new HomeView(); + homeView = new HomeView(this); views->addWidget(homeView); // TODO make this lazy @@ -133,7 +131,8 @@ MainWindow::MainWindow() : // build ui createActions(); createMenus(); - createToolBars(); + createToolBar(); + hideToolbar(); createStatusBar(); // remove that useless menu/toolbar context menu @@ -152,27 +151,18 @@ MainWindow::MainWindow() : views->widget(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); setMinimumWidth(0); - views->show(); - - // show the initial view - showHome(false); - #ifdef APP_ACTIVATION - if (!Activation::instance().isActivated()) - showActivationView(false); + Activation::instance().initialCheck(); +#else + showHome(); #endif - QTimer::singleShot(0, this, SLOT(lazyInit())); + QTimer::singleShot(1000, this, &MainWindow::lazyInit); } void MainWindow::lazyInit() { -#ifdef APP_PHONON - initPhonon(); -#endif mediaView->initialize(); -#ifdef APP_PHONON - mediaView->setMediaObject(mediaObject); -#endif + initMedia(); qApp->processEvents(); // CLI @@ -201,34 +191,28 @@ void MainWindow::lazyInit() { connect(&shortcuts, SIGNAL(Stop()), this, SLOT(stop())); connect(&shortcuts, SIGNAL(Next()), skipAct, SLOT(trigger())); connect(&shortcuts, SIGNAL(Previous()), skipBackwardAct, SLOT(trigger())); - // connect(&shortcuts, SIGNAL(StopAfter()), The::globalActions()->value("stopafterthis"), SLOT(toggle())); + // connect(&shortcuts, SIGNAL(StopAfter()), getAction("stopafterthis"), SLOT(toggle())); connect(DownloadManager::instance(), SIGNAL(statusMessageChanged(QString)), SLOT(updateDownloadMessage(QString))); - connect(DownloadManager::instance(), SIGNAL(finished()), - SLOT(downloadsFinished())); + connect(DownloadManager::instance(), SIGNAL(finished()), SLOT(downloadsFinished())); setAcceptDrops(true); - mouseTimer = new QTimer(this); - mouseTimer->setInterval(5000); - mouseTimer->setSingleShot(true); - connect(mouseTimer, SIGNAL(timeout()), SLOT(hideMouse())); + fullscreenTimer = new QTimer(this); + fullscreenTimer->setInterval(3000); + fullscreenTimer->setSingleShot(true); + connect(fullscreenTimer, SIGNAL(timeout()), SLOT(hideFullscreenUI())); JsFunctions::instance(); // Hack to give focus to searchlineedit - QMetaObject::invokeMethod(views->currentWidget(), "appear"); - View* view = qobject_cast (views->currentWidget()); - QString desc = view->metadata().value("description").toString(); - if (!desc.isEmpty()) showMessage(desc); - -#ifdef APP_INTEGRITY - if (!Extra::integrityCheck()) { - deleteLater(); - return; + View *view = qobject_cast(views->currentWidget()); + if (view == homeView) { + QMetaObject::invokeMethod(views->currentWidget(), "appear"); + const QString &desc = view->getDescription(); + if (!desc.isEmpty()) showMessage(desc); } -#endif ChannelAggregator::instance()->start(); @@ -240,92 +224,136 @@ void MainWindow::lazyInit() { void MainWindow::changeEvent(QEvent *e) { #ifdef APP_MAC if (e->type() == QEvent::WindowStateChange) { - The::globalActions()->value("minimize")->setEnabled(!isMinimized()); + getAction("minimize")->setEnabled(!isMinimized()); } #endif + if (messageLabel->isVisible()) { + if (e->type() == QEvent::ActivationChange || e->type() == QEvent::WindowStateChange || + e->type() == QEvent::WindowDeactivate || e->type() == QEvent::ApplicationStateChange) { + hideMessage(); + } + } QMainWindow::changeEvent(e); } bool MainWindow::eventFilter(QObject *obj, QEvent *e) { + const QEvent::Type t = e->type(); - if (fullscreenFlag && e->type() == QEvent::MouseMove) { - const char *className = obj->metaObject()->className(); - const bool isHoveringVideo = - (className == QLatin1String("QGLWidget")) || - (className == QLatin1String("VideoAreaWidget")); +#ifndef APP_MAC + static bool altPressed = false; + if (t == QEvent::KeyRelease && altPressed) { + altPressed = false; + QKeyEvent *ke = static_cast(e); + if (ke->key() == Qt::Key_Alt) { + toggleMenuVisibility(); + return true; + } + } else if (t == QEvent::KeyPress) { + QKeyEvent *ke = static_cast(e); + altPressed = ke->key() == Qt::Key_Alt; + } +#endif - // qDebug() << obj << mouseEvent->pos() << isHoveringVideo << mediaView->isPlaylistVisible(); + if (fullScreenActive && views->currentWidget() == mediaView && t == QEvent::MouseMove && + obj->isWidgetType() && qobject_cast(obj)->window() == this) { + QMouseEvent *mouseEvent = static_cast(e); - if (isHoveringVideo) { - QMouseEvent *mouseEvent = static_cast (e); - const int x = mouseEvent->pos().x(); + bool toolBarVisible = mainToolBar && mainToolBar->isVisible(); + bool sidebarVisible = mediaView->isSidebarVisible(); - if (mediaView->isPlaylistVisible()) { - if (x > 5) mediaView->setPlaylistVisible(false); - } else { - if (x >= 0 && x < 5) mediaView->setPlaylistVisible(true); + if (!sidebarVisible && !toolBarVisible) { + const int x = mouseEvent->pos().x(); + if (x >= 0 && x < 5) { +#ifndef APP_MAC + SidebarWidget *sidebar = mediaView->getSidebar(); + sidebar->resize(sidebar->width(), height()); +#endif + mediaView->setSidebarVisibility(true); + sidebarVisible = true; } + } #ifndef APP_MAC + if (!toolBarVisible && !sidebarVisible) { const int y = mouseEvent->pos().y(); - if (mainToolBar->isVisible()) { - if (y > 5) mainToolBar->setVisible(false); - } else { - if (y >= 0 && y < 5) mainToolBar->setVisible(true); + if (y >= 0 && y < 5) { + mainToolBar->resize(width(), mainToolBar->sizeHint().height()); + mainToolBar->setVisible(true); } -#endif - } +#endif // show the normal cursor unsetCursor(); // then hide it again after a few seconds - mouseTimer->start(); + fullscreenTimer->start(); } - if (e->type() == QEvent::ToolTip) { + if (t == QEvent::ToolTip) { // kill tooltips return true; } + + if (t == QEvent::Show && obj == toolbarMenu) { +#ifdef APP_MAC + int x = width() - toolbarMenu->sizeHint().width(); + int y = views->y(); +#else + int x = toolbarMenuButton->x() + toolbarMenuButton->width() - + toolbarMenu->sizeHint().width(); + int y = toolbarMenuButton->y() + toolbarMenuButton->height(); +#endif + QPoint p(x, y); + toolbarMenu->move(mapToGlobal(p)); + } + + if (obj == this && t == QEvent::StyleChange) { + qDebug() << "Style change detected"; + qApp->paletteChanged(qApp->palette()); + return false; + } + // standard event processing return QMainWindow::eventFilter(obj, e); } void MainWindow::createActions() { - - QHash *actions = The::globalActions(); - - stopAct = new QAction(IconUtils::icon("media-playback-stop"), tr("&Stop"), this); + stopAct = new QAction(tr("&Stop"), this); + IconUtils::setIcon(stopAct, "media-playback-stop"); stopAct->setStatusTip(tr("Stop playback and go back to the search view")); - stopAct->setShortcuts(QList() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop)); + stopAct->setShortcuts(QList() + << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop)); stopAct->setEnabled(false); - actions->insert("stop", stopAct); + actionMap.insert("stop", stopAct); connect(stopAct, SIGNAL(triggered()), SLOT(stop())); - skipBackwardAct = new QAction( - IconUtils::icon("media-skip-backward"), - tr("P&revious"), this); + skipBackwardAct = new QAction(tr("P&revious"), this); skipBackwardAct->setStatusTip(tr("Go back to the previous track")); skipBackwardAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Left)); skipBackwardAct->setEnabled(false); - actions->insert("previous", skipBackwardAct); + actionMap.insert("previous", skipBackwardAct); connect(skipBackwardAct, SIGNAL(triggered()), mediaView, SLOT(skipBackward())); - skipAct = new QAction(IconUtils::icon("media-skip-forward"), tr("S&kip"), this); + skipAct = new QAction(tr("S&kip"), this); + IconUtils::setIcon(skipAct, "media-skip-forward"); skipAct->setStatusTip(tr("Skip to the next video")); - skipAct->setShortcuts(QList() << QKeySequence(Qt::CTRL + Qt::Key_Right) << QKeySequence(Qt::Key_MediaNext)); + skipAct->setShortcuts(QList() << QKeySequence(Qt::CTRL + Qt::Key_Right) + << QKeySequence(Qt::Key_MediaNext)); skipAct->setEnabled(false); - actions->insert("skip", skipAct); + actionMap.insert("skip", skipAct); connect(skipAct, SIGNAL(triggered()), mediaView, SLOT(skip())); - pauseAct = new QAction(IconUtils::icon("media-playback-start"), tr("&Play"), this); + pauseAct = new QAction(tr("&Play"), this); + IconUtils::setIcon(pauseAct, "media-playback-start"); pauseAct->setStatusTip(tr("Resume playback")); - pauseAct->setShortcuts(QList() << QKeySequence(Qt::Key_Space) << QKeySequence(Qt::Key_MediaPlay)); + pauseAct->setShortcuts(QList() + << QKeySequence(Qt::Key_Space) << QKeySequence(Qt::Key_MediaPlay)); pauseAct->setEnabled(false); - actions->insert("pause", pauseAct); + actionMap.insert("pause", pauseAct); connect(pauseAct, SIGNAL(triggered()), mediaView, SLOT(pause())); - fullscreenAct = new QAction(IconUtils::icon("view-fullscreen"), tr("&Full Screen"), this); + fullscreenAct = new QAction(tr("&Full Screen"), this); + IconUtils::setIcon(fullscreenAct, "view-fullscreen"); fullscreenAct->setStatusTip(tr("Go full screen")); QList fsShortcuts; #ifdef APP_MAC @@ -336,41 +364,38 @@ void MainWindow::createActions() { fullscreenAct->setShortcuts(fsShortcuts); fullscreenAct->setShortcutContext(Qt::ApplicationShortcut); fullscreenAct->setPriority(QAction::LowPriority); - actions->insert("fullscreen", fullscreenAct); - connect(fullscreenAct, SIGNAL(triggered()), this, SLOT(fullscreen())); + actionMap.insert("fullscreen", fullscreenAct); + connect(fullscreenAct, SIGNAL(triggered()), SLOT(toggleFullscreen())); compactViewAct = new QAction(tr("&Compact Mode"), this); compactViewAct->setStatusTip(tr("Hide the playlist and the toolbar")); -#ifdef APP_MAC - compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::META + Qt::Key_C)); -#else compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C)); -#endif compactViewAct->setCheckable(true); compactViewAct->setChecked(false); compactViewAct->setEnabled(false); - actions->insert("compactView", compactViewAct); + actionMap.insert("compactView", compactViewAct); connect(compactViewAct, SIGNAL(toggled(bool)), this, SLOT(compactView(bool))); webPageAct = new QAction(tr("Open the &YouTube Page"), this); webPageAct->setStatusTip(tr("Go to the YouTube video page and pause playback")); webPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Y)); webPageAct->setEnabled(false); - actions->insert("webpage", webPageAct); + actionMap.insert("webpage", webPageAct); connect(webPageAct, SIGNAL(triggered()), mediaView, SLOT(openWebPage())); copyPageAct = new QAction(tr("Copy the YouTube &Link"), this); + IconUtils::setIcon(copyPageAct, "link"); copyPageAct->setStatusTip(tr("Copy the current video YouTube link to the clipboard")); copyPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); copyPageAct->setEnabled(false); - actions->insert("pagelink", copyPageAct); + actionMap.insert("pagelink", copyPageAct); connect(copyPageAct, SIGNAL(triggered()), mediaView, SLOT(copyWebPage())); copyLinkAct = new QAction(tr("Copy the Video Stream &URL"), this); copyLinkAct->setStatusTip(tr("Copy the current video stream URL to the clipboard")); copyLinkAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U)); copyLinkAct->setEnabled(false); - actions->insert("videolink", copyLinkAct); + actionMap.insert("videolink", copyLinkAct); connect(copyLinkAct, SIGNAL(triggered()), mediaView, SLOT(copyVideoLink())); findVideoPartsAct = new QAction(tr("Find Video &Parts"), this); @@ -378,27 +403,28 @@ void MainWindow::createActions() { findVideoPartsAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P)); findVideoPartsAct->setEnabled(false); connect(findVideoPartsAct, SIGNAL(triggered()), mediaView, SLOT(findVideoParts())); - actions->insert("findVideoParts", findVideoPartsAct); + actionMap.insert("findVideoParts", findVideoPartsAct); removeAct = new QAction(tr("&Remove"), this); removeAct->setStatusTip(tr("Remove the selected videos from the playlist")); - removeAct->setShortcuts(QList() << QKeySequence("Del") << QKeySequence("Backspace")); + removeAct->setShortcuts(QList() + << QKeySequence("Del") << QKeySequence("Backspace")); removeAct->setEnabled(false); - actions->insert("remove", removeAct); + actionMap.insert("remove", removeAct); connect(removeAct, SIGNAL(triggered()), mediaView, SLOT(removeSelected())); moveUpAct = new QAction(tr("Move &Up"), this); moveUpAct->setStatusTip(tr("Move up the selected videos in the playlist")); moveUpAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up)); moveUpAct->setEnabled(false); - actions->insert("moveUp", moveUpAct); + actionMap.insert("moveUp", moveUpAct); connect(moveUpAct, SIGNAL(triggered()), mediaView, SLOT(moveUpSelected())); moveDownAct = new QAction(tr("Move &Down"), this); moveDownAct->setStatusTip(tr("Move down the selected videos in the playlist")); moveDownAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down)); moveDownAct->setEnabled(false); - actions->insert("moveDown", moveDownAct); + actionMap.insert("moveDown", moveDownAct); connect(moveDownAct, SIGNAL(triggered()), mediaView, SLOT(moveDownSelected())); clearAct = new QAction(tr("&Clear Recent Searches"), this); @@ -408,33 +434,34 @@ void MainWindow::createActions() { << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Backspace)); clearAct->setStatusTip(tr("Clear the search history. Cannot be undone.")); clearAct->setEnabled(true); - actions->insert("clearRecentKeywords", clearAct); + actionMap.insert("clearRecentKeywords", clearAct); connect(clearAct, SIGNAL(triggered()), SLOT(clearRecentKeywords())); quitAct = new QAction(tr("&Quit"), this); quitAct->setMenuRole(QAction::QuitRole); quitAct->setShortcut(QKeySequence(QKeySequence::Quit)); quitAct->setStatusTip(tr("Bye")); - actions->insert("quit", quitAct); + actionMap.insert("quit", quitAct); connect(quitAct, SIGNAL(triggered()), SLOT(quit())); siteAct = new QAction(tr("&Website"), this); siteAct->setShortcut(QKeySequence::HelpContents); siteAct->setStatusTip(tr("%1 on the Web").arg(Constants::NAME)); - actions->insert("site", siteAct); + actionMap.insert("site", siteAct); connect(siteAct, SIGNAL(triggered()), this, SLOT(visitSite())); #if !defined(APP_MAC) && !defined(APP_WIN) donateAct = new QAction(tr("Make a &Donation"), this); - donateAct->setStatusTip(tr("Please support the continued development of %1").arg(Constants::NAME)); - actions->insert("donate", donateAct); + donateAct->setStatusTip( + tr("Please support the continued development of %1").arg(Constants::NAME)); + actionMap.insert("donate", donateAct); connect(donateAct, SIGNAL(triggered()), this, SLOT(donate())); #endif aboutAct = new QAction(tr("&About"), this); aboutAct->setMenuRole(QAction::AboutRole); aboutAct->setStatusTip(tr("Info about %1").arg(Constants::NAME)); - actions->insert("about", aboutAct); + actionMap.insert("about", aboutAct); connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); // Invisible actions @@ -442,81 +469,93 @@ void MainWindow::createActions() { searchFocusAct = new QAction(this); searchFocusAct->setShortcut(QKeySequence::Find); searchFocusAct->setStatusTip(tr("Search")); - actions->insert("search", searchFocusAct); + actionMap.insert("search", searchFocusAct); connect(searchFocusAct, SIGNAL(triggered()), this, SLOT(searchFocus())); addAction(searchFocusAct); volumeUpAct = new QAction(this); volumeUpAct->setShortcuts(QList() << QKeySequence(Qt::CTRL + Qt::Key_Plus)); - actions->insert("volume-up", volumeUpAct); + actionMap.insert("volumeUp", volumeUpAct); connect(volumeUpAct, SIGNAL(triggered()), this, SLOT(volumeUp())); addAction(volumeUpAct); volumeDownAct = new QAction(this); volumeDownAct->setShortcuts(QList() << QKeySequence(Qt::CTRL + Qt::Key_Minus)); - actions->insert("volume-down", volumeDownAct); + actionMap.insert("volumeDown", volumeDownAct); connect(volumeDownAct, SIGNAL(triggered()), this, SLOT(volumeDown())); addAction(volumeDownAct); volumeMuteAct = new QAction(this); - volumeMuteAct->setIcon(IconUtils::icon("audio-volume-high")); + IconUtils::setIcon(volumeMuteAct, "audio-volume-high"); volumeMuteAct->setStatusTip(tr("Mute volume")); - volumeMuteAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K)); - actions->insert("volume-mute", volumeMuteAct); - connect(volumeMuteAct, SIGNAL(triggered()), SLOT(volumeMute())); + volumeMuteAct->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M)); + actionMap.insert("volumeMute", volumeMuteAct); + connect(volumeMuteAct, SIGNAL(triggered()), SLOT(toggleVolumeMute())); addAction(volumeMuteAct); - QAction *definitionAct = new QAction(this); -#ifdef APP_LINUX - definitionAct->setIcon(IconUtils::tintedIcon("video-display", QColor(0, 0, 0), - QList() << QSize(16, 16))); -#else - definitionAct->setIcon(IconUtils::icon("video-display")); -#endif - definitionAct->setShortcuts(QList() << QKeySequence(Qt::CTRL + Qt::Key_D)); - /* + QToolButton *definitionButton = new QToolButton(this); + definitionButton->setText(YT3::instance().maxVideoDefinition().getName()); + IconUtils::setIcon(definitionButton, "video-display"); + definitionButton->setIconSize(QSize(16, 16)); + definitionButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + definitionButton->setPopupMode(QToolButton::InstantPopup); QMenu *definitionMenu = new QMenu(this); - foreach (QString definition, VideoDefinition::getDefinitionNames()) { - definitionMenu->addAction(definition); + QActionGroup *group = new QActionGroup(this); + for (auto &defName : VideoDefinition::getDefinitionNames()) { + QAction *a = new QAction(defName); + a->setCheckable(true); + a->setActionGroup(group); + a->setChecked(defName == YT3::instance().maxVideoDefinition().getName()); + connect(a, &QAction::triggered, this, [this, defName, definitionButton] { + setDefinitionMode(defName); + definitionButton->setText(defName); + }); + connect(&YT3::instance(), &YT3::maxVideoDefinitionChanged, this, + [defName, definitionButton](const QString &name) { + if (defName == name) definitionButton->setChecked(true); + }); + definitionMenu->addAction(a); } - definitionAct->setMenu(definitionMenu); - */ - actions->insert("definition", definitionAct); - connect(definitionAct, SIGNAL(triggered()), SLOT(toggleDefinitionMode())); + definitionButton->setMenu(definitionMenu); + QWidgetAction *definitionAct = new QWidgetAction(this); + definitionAct->setDefaultWidget(definitionButton); + definitionAct->setShortcuts(QList() << QKeySequence(Qt::CTRL + Qt::Key_D)); + actionMap.insert("definition", definitionAct); addAction(definitionAct); QAction *action; - action = new QAction(IconUtils::icon("media-playback-start"), tr("&Manually Start Playing"), this); + action = new QAction(tr("&Manually Start Playing"), this); + IconUtils::setIcon(action, "media-playback-start"); action->setStatusTip(tr("Manually start playing videos")); action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_T)); action->setCheckable(true); connect(action, SIGNAL(toggled(bool)), SLOT(setManualPlay(bool))); - actions->insert("manualplay", action); + actionMap.insert("manualplay", action); action = new QAction(tr("&Downloads"), this); + IconUtils::setIcon(action, "document-save"); action->setStatusTip(tr("Show details about video downloads")); action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_J)); action->setCheckable(true); - action->setIcon(IconUtils::icon("document-save")); connect(action, SIGNAL(toggled(bool)), SLOT(toggleDownloads(bool))); - actions->insert("downloads", action); + actionMap.insert("downloads", action); action = new QAction(tr("&Download"), this); + IconUtils::setIcon(action, "document-save"); action->setStatusTip(tr("Download the current video")); action->setShortcut(QKeySequence::Save); - action->setIcon(IconUtils::icon("document-save")); action->setEnabled(false); action->setVisible(false); action->setPriority(QAction::LowPriority); connect(action, SIGNAL(triggered()), mediaView, SLOT(downloadVideo())); - actions->insert("download", action); + actionMap.insert("download", action); #ifdef APP_SNAPSHOT action = new QAction(tr("Take &Snapshot"), this); action->setShortcut(QKeySequence(Qt::Key_F9)); action->setEnabled(false); - actions->insert("snapshot", action); + actionMap.insert("snapshot", action); connect(action, SIGNAL(triggered()), mediaView, SLOT(snapshot())); #endif @@ -525,120 +564,129 @@ void MainWindow::createActions() { action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_S)); action->setEnabled(false); connect(action, SIGNAL(triggered()), mediaView, SLOT(toggleSubscription())); - actions->insert("subscribe-channel", action); - mediaView->updateSubscriptionAction(0, false); + actionMap.insert("subscribeChannel", action); + mediaView->updateSubscriptionActionForVideo(0, false); QString shareTip = tr("Share the current video using %1"); action = new QAction("&Twitter", this); + IconUtils::setIcon(action, "twitter"); action->setStatusTip(shareTip.arg("Twitter")); action->setEnabled(false); - actions->insert("twitter", action); + actionMap.insert("twitter", action); connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaTwitter())); action = new QAction("&Facebook", this); + IconUtils::setIcon(action, "facebook"); action->setStatusTip(shareTip.arg("Facebook")); action->setEnabled(false); - actions->insert("facebook", action); + actionMap.insert("facebook", action); connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaFacebook())); - action = new QAction("&Buffer", this); - action->setStatusTip(shareTip.arg("Buffer")); - action->setEnabled(false); - actions->insert("buffer", action); - connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaBuffer())); - action = new QAction(tr("&Email"), this); + IconUtils::setIcon(action, "email"); action->setStatusTip(shareTip.arg(tr("Email"))); action->setEnabled(false); - actions->insert("email", action); + actionMap.insert("email", action); connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaEmail())); action = new QAction(tr("&Close"), this); action->setShortcut(QKeySequence(QKeySequence::Close)); - actions->insert("close", action); + actionMap.insert("close", action); connect(action, SIGNAL(triggered()), SLOT(close())); action = new QAction(Constants::NAME, this); action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_1)); - actions->insert("restore", action); + actionMap.insert("restore", action); connect(action, SIGNAL(triggered()), SLOT(restore())); - action = new QAction(IconUtils::icon("go-top"), tr("&Float on Top"), this); + action = new QAction(tr("&Float on Top"), this); + IconUtils::setIcon(action, "go-top"); action->setCheckable(true); - actions->insert("ontop", action); + actionMap.insert("ontop", action); connect(action, SIGNAL(toggled(bool)), SLOT(floatOnTop(bool))); - action = new QAction(tr("&Adjust Window Size"), this); - action->setCheckable(true); - actions->insert("adjustwindowsize", action); - connect(action, SIGNAL(toggled(bool)), SLOT(adjustWindowSizeChanged(bool))); - - action = new QAction(IconUtils::icon("media-playback-stop"), tr("&Stop After This Video"), this); + action = new QAction(tr("&Stop After This Video"), this); + IconUtils::setIcon(action, "media-playback-stop"); action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Escape)); action->setCheckable(true); action->setEnabled(false); - actions->insert("stopafterthis", action); + actionMap.insert("stopafterthis", action); connect(action, SIGNAL(toggled(bool)), SLOT(showStopAfterThisInStatusBar(bool))); action = new QAction(tr("&Report an Issue..."), this); - actions->insert("report-issue", action); + actionMap.insert("reportIssue", action); connect(action, SIGNAL(triggered()), SLOT(reportIssue())); action = new QAction(tr("&Refine Search..."), this); action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E)); action->setCheckable(true); action->setEnabled(false); - actions->insert("refine-search", action); + actionMap.insert("refineSearch", action); action = new QAction(YTRegions::worldwideRegion().name, this); - actions->insert("worldwide-region", action); + actionMap.insert("worldwideRegion", action); action = new QAction(YTRegions::localRegion().name, this); - actions->insert("local-region", action); + actionMap.insert("localRegion", action); action = new QAction(tr("More..."), this); - actions->insert("more-region", action); + actionMap.insert("moreRegion", action); - action = new QAction(IconUtils::icon("view-list"), tr("&Related Videos"), this); + action = new QAction(tr("&Related Videos"), this); + IconUtils::setIcon(action, "view-list"); action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); action->setStatusTip(tr("Watch videos related to the current one")); action->setEnabled(false); action->setPriority(QAction::LowPriority); connect(action, SIGNAL(triggered()), mediaView, SLOT(relatedVideos())); - actions->insert("related-videos", action); + actionMap.insert("relatedVideos", action); action = new QAction(tr("Open in &Browser..."), this); action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_B)); action->setEnabled(false); - actions->insert("open-in-browser", action); + actionMap.insert("openInBrowser", action); connect(action, SIGNAL(triggered()), mediaView, SLOT(openInBrowser())); + action = new QAction(tr("Restricted Mode"), this); + IconUtils::setIcon(action, "safesearch"); + action->setStatusTip(tr("Hide videos that may contain inappropriate content")); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K)); + action->setCheckable(true); + actionMap.insert("safeSearch", action); + + action = new QAction(tr("Toggle &Menu Bar"), this); + connect(action, SIGNAL(triggered()), SLOT(toggleMenuVisibilityWithMessage())); + actionMap.insert("toggleMenu", action); + + action = new QAction(tr("Menu"), this); + IconUtils::setIcon(action, "open-menu"); + connect(action, SIGNAL(triggered()), SLOT(toggleToolbarMenu())); + actionMap.insert("toolbarMenu", action); + #ifdef APP_MAC_STORE action = new QAction(tr("&Love %1? Rate it!").arg(Constants::NAME), this); - actions->insert("app-store", action); + actionMap.insert("appStore", action); connect(action, SIGNAL(triggered()), SLOT(rateOnAppStore())); #endif #ifdef APP_ACTIVATION - Extra::createActivationAction(tr("Buy %1...").arg(Constants::NAME)); + ActivationView::createActivationAction(tr("Buy %1...").arg(Constants::NAME)); #endif // common action properties - foreach (QAction *action, actions->values()) { + for (QAction *action : qAsConst(actionMap)) { // add actions to the MainWindow so that they work // when the menu is hidden addAction(action); - IconUtils::setupAction(action); + MainWindow::instance()->setupAction(action); } } void MainWindow::createMenus() { - QHash *menus = The::globalMenus(); - fileMenu = menuBar()->addMenu(tr("&Application")); #ifdef APP_ACTIVATION - QAction *buyAction = The::globalActions()->value("buy"); + QAction *buyAction = getAction("buy"); if (buyAction) fileMenu->addAction(buyAction); #ifndef APP_MAC fileMenu->addSeparator(); @@ -650,132 +698,113 @@ void MainWindow::createMenus() { #endif fileMenu->addAction(quitAct); - QMenu* playbackMenu = menuBar()->addMenu(tr("&Playback")); - menus->insert("playback", playbackMenu); + QMenu *playbackMenu = menuBar()->addMenu(tr("&Playback")); + menuMap.insert("playback", playbackMenu); playbackMenu->addAction(pauseAct); playbackMenu->addAction(stopAct); - playbackMenu->addAction(The::globalActions()->value("stopafterthis")); + playbackMenu->addAction(getAction("stopafterthis")); playbackMenu->addSeparator(); playbackMenu->addAction(skipAct); playbackMenu->addAction(skipBackwardAct); playbackMenu->addSeparator(); - playbackMenu->addAction(The::globalActions()->value("manualplay")); + playbackMenu->addAction(getAction("manualplay")); #ifdef APP_MAC MacSupport::dockMenu(playbackMenu); #endif playlistMenu = menuBar()->addMenu(tr("&Playlist")); - menus->insert("playlist", playlistMenu); + menuMap.insert("playlist", playlistMenu); playlistMenu->addAction(removeAct); playlistMenu->addSeparator(); playlistMenu->addAction(moveUpAct); playlistMenu->addAction(moveDownAct); playlistMenu->addSeparator(); - playlistMenu->addAction(The::globalActions()->value("refine-search")); + playlistMenu->addAction(getAction("refineSearch")); - QMenu* videoMenu = menuBar()->addMenu(tr("&Video")); - menus->insert("video", videoMenu); - videoMenu->addAction(The::globalActions()->value("related-videos")); + QMenu *videoMenu = menuBar()->addMenu(tr("&Video")); + menuMap.insert("video", videoMenu); + videoMenu->addAction(getAction("relatedVideos")); videoMenu->addAction(findVideoPartsAct); videoMenu->addSeparator(); - videoMenu->addAction(The::globalActions()->value("subscribe-channel")); + videoMenu->addAction(getAction("subscribeChannel")); #ifdef APP_SNAPSHOT videoMenu->addSeparator(); - videoMenu->addAction(The::globalActions()->value("snapshot")); + videoMenu->addAction(getAction("snapshot")); #endif videoMenu->addSeparator(); videoMenu->addAction(webPageAct); videoMenu->addAction(copyLinkAct); - videoMenu->addAction(The::globalActions()->value("open-in-browser")); - videoMenu->addAction(The::globalActions()->value("download")); - - QMenu* viewMenu = menuBar()->addMenu(tr("&View")); - menus->insert("view", viewMenu); - viewMenu->addAction(fullscreenAct); - viewMenu->addAction(compactViewAct); - viewMenu->addSeparator(); - viewMenu->addAction(The::globalActions()->value("adjustwindowsize")); - viewMenu->addSeparator(); - viewMenu->addAction(The::globalActions()->value("ontop")); + videoMenu->addAction(getAction("openInBrowser")); + videoMenu->addAction(getAction("download")); - QMenu* shareMenu = menuBar()->addMenu(tr("&Share")); - menus->insert("share", shareMenu); + QMenu *shareMenu = menuBar()->addMenu(tr("&Share")); + menuMap.insert("share", shareMenu); shareMenu->addAction(copyPageAct); shareMenu->addSeparator(); - shareMenu->addAction(The::globalActions()->value("twitter")); - shareMenu->addAction(The::globalActions()->value("facebook")); - shareMenu->addAction(The::globalActions()->value("buffer")); + shareMenu->addAction(getAction("twitter")); + shareMenu->addAction(getAction("facebook")); shareMenu->addSeparator(); - shareMenu->addAction(The::globalActions()->value("email")); + shareMenu->addAction(getAction("email")); + + QMenu *viewMenu = menuBar()->addMenu(tr("&View")); + menuMap.insert("view", viewMenu); + viewMenu->addAction(getAction("ontop")); + viewMenu->addAction(compactViewAct); + viewMenu->addSeparator(); + viewMenu->addAction(fullscreenAct); +#ifndef APP_MAC + viewMenu->addSeparator(); + viewMenu->addAction(getAction("toggleMenu")); +#endif #ifdef APP_MAC MacSupport::windowMenu(this); #endif helpMenu = menuBar()->addMenu(tr("&Help")); + menuMap.insert("help", helpMenu); helpMenu->addAction(siteAct); #if !defined(APP_MAC) && !defined(APP_WIN) helpMenu->addAction(donateAct); #endif - helpMenu->addAction(The::globalActions()->value("report-issue")); + helpMenu->addAction(getAction("reportIssue")); helpMenu->addAction(aboutAct); #ifdef APP_MAC_STORE helpMenu->addSeparator(); - helpMenu->addAction(The::globalActions()->value("app-store")); + helpMenu->addAction(getAction("appStore")); #endif } -void MainWindow::createToolBars() { - +void MainWindow::createToolBar() { // Create widgets + currentTimeLabel = new QLabel("00:00", this); - currentTime = new QLabel("00:00"); - currentTime->setFont(FontUtils::small()); - -#ifdef APP_PHONON_SEEK - seekSlider = new Phonon::SeekSlider(); - seekSlider->setTracking(true); - seekSlider->setIconVisible(false); - seekSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); -#else - slider = new SeekSlider(this); - slider->setEnabled(false); - slider->setTracking(false); - slider->setMaximum(1000); - slider->setOrientation(Qt::Horizontal); - slider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); -#endif - -#ifdef APP_PHONON - volumeSlider = new Phonon::VolumeSlider(); - volumeSlider->setMuteVisible(false); - // qDebug() << volumeSlider->children(); - // status tip for the volume slider - QSlider* volumeQSlider = volumeSlider->findChild(); - if (volumeQSlider) - volumeQSlider->setStatusTip(tr("Press %1 to raise the volume, %2 to lower it").arg( - volumeUpAct->shortcut().toString(QKeySequence::NativeText), volumeDownAct->shortcut().toString(QKeySequence::NativeText))); - // this makes the volume slider smaller - volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); -#endif + seekSlider = new SeekSlider(this); + seekSlider->setEnabled(false); + seekSlider->setTracking(false); + seekSlider->setMaximum(1000); + volumeSlider = new SeekSlider(this); + volumeSlider->setValue(volumeSlider->maximum()); #if defined(APP_MAC_SEARCHFIELD) && !defined(APP_MAC_QMACTOOLBAR) - SearchWrapper* searchWrapper = new SearchWrapper(this); + SearchWrapper *searchWrapper = new SearchWrapper(this); toolbarSearch = searchWrapper->getSearchLineEdit(); #else toolbarSearch = new SearchLineEdit(this); #endif - toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize()*15); + toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize() * 15); + toolbarSearch->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); toolbarSearch->setSuggester(new YTSuggester(this)); - connect(toolbarSearch, SIGNAL(search(const QString&)), SLOT(search(const QString&))); - connect(toolbarSearch, SIGNAL(suggestionAccepted(Suggestion*)), SLOT(suggestionAccepted(Suggestion*))); + connect(toolbarSearch, SIGNAL(search(const QString &)), SLOT(search(const QString &))); + connect(toolbarSearch, SIGNAL(suggestionAccepted(Suggestion *)), + SLOT(suggestionAccepted(Suggestion *))); toolbarSearch->setStatusTip(searchFocusAct->statusTip()); // Add widgets to toolbar #ifdef APP_MAC_QMACTOOLBAR - currentTime->hide(); + currentTimeLabel->hide(); toolbarSearch->hide(); volumeSlider->hide(); seekSlider->hide(); @@ -787,15 +816,21 @@ void MainWindow::createToolBars() { mainToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); mainToolBar->setFloatable(false); mainToolBar->setMovable(false); -#if defined(APP_MAC) | defined(APP_WIN) +#ifndef APP_LINUX mainToolBar->setIconSize(QSize(32, 32)); #endif - mainToolBar->addAction(stopAct); + QToolButton *stopToolButton = + qobject_cast(mainToolBar->widgetForAction(stopAct)); + if (stopToolButton) { + QMenu *stopMenu = new QMenu(this); + stopMenu->addAction(getAction("stopafterthis")); + stopToolButton->setMenu(stopMenu); + stopToolButton->setPopupMode(QToolButton::DelayedPopup); + } mainToolBar->addAction(pauseAct); mainToolBar->addAction(skipAct); - mainToolBar->addAction(The::globalActions()->value("related-videos")); - mainToolBar->addAction(The::globalActions()->value("download")); + mainToolBar->addAction(getAction("relatedVideos")); bool addFullScreenAct = true; #ifdef Q_OS_MAC @@ -804,31 +839,46 @@ void MainWindow::createToolBars() { if (addFullScreenAct) mainToolBar->addAction(fullscreenAct); mainToolBar->addWidget(new Spacer()); - mainToolBar->addWidget(currentTime); - mainToolBar->addWidget(new Spacer()); -#ifdef APP_PHONON_SEEK - mainToolBar->addWidget(seekSlider); -#else - mainToolBar->addWidget(slider); + + currentTimeLabel->setFont(FontUtils::small()); + currentTimeLabel->setMinimumWidth(currentTimeLabel->fontInfo().pixelSize() * 4); + currentTimeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + mainToolBar->addWidget(currentTimeLabel); + +#ifdef APP_WIN + mainToolBar->addWidget(new Spacer(nullptr, 10)); #endif - /* - mainToolBar->addWidget(new Spacer()); - totalTime = new QLabel(mainToolBar); - totalTime->setFont(smallerFont); - mainToolBar->addWidget(totalTime); - */ + seekSlider->setOrientation(Qt::Horizontal); + seekSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + seekSlider->setFocusPolicy(Qt::NoFocus); + mainToolBar->addWidget(seekSlider); mainToolBar->addWidget(new Spacer()); - mainToolBar->addAction(volumeMuteAct); -#ifdef APP_LINUX - QToolButton *volumeMuteButton = qobject_cast(mainToolBar->widgetForAction(volumeMuteAct)); - volumeMuteButton->setIcon(volumeMuteButton->icon().pixmap(16)); -#endif -#ifdef APP_PHONON + mainToolBar->addAction(volumeMuteAct); +#ifndef APP_MAC_QMACTOOLBAR + QToolButton *volumeMuteButton = + qobject_cast(mainToolBar->widgetForAction(volumeMuteAct)); + volumeMuteButton->setIconSize(QSize(16, 16)); + auto fixVolumeMuteIconSize = [volumeMuteButton] { + volumeMuteButton->setIcon(volumeMuteButton->icon().pixmap(16)); + }; + fixVolumeMuteIconSize(); + volumeMuteButton->connect(volumeMuteAct, &QAction::changed, volumeMuteButton, + fixVolumeMuteIconSize); +#endif + + volumeSlider->setStatusTip( + tr("Press %1 to raise the volume, %2 to lower it") + .arg(volumeUpAct->shortcut().toString(QKeySequence::NativeText), + volumeDownAct->shortcut().toString(QKeySequence::NativeText))); + + volumeSlider->setOrientation(Qt::Horizontal); + // this makes the volume slider smaller + volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + volumeSlider->setFocusPolicy(Qt::NoFocus); mainToolBar->addWidget(volumeSlider); -#endif mainToolBar->addWidget(new Spacer()); @@ -836,33 +886,37 @@ void MainWindow::createToolBars() { mainToolBar->addWidget(searchWrapper); #else mainToolBar->addWidget(toolbarSearch); - mainToolBar->addWidget(new Spacer()); + mainToolBar->addWidget(new Spacer(this, toolbarSearch->height() / 2)); + + QAction *toolbarMenuAction = getAction("toolbarMenu"); + mainToolBar->addAction(toolbarMenuAction); + toolbarMenuButton = + qobject_cast(mainToolBar->widgetForAction(toolbarMenuAction)); #endif addToolBar(mainToolBar); } void MainWindow::createStatusBar() { - statusToolBar = new QToolBar(this); + statusToolBar = new QToolBar(statusBar()); statusToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); statusToolBar->setIconSize(QSize(16, 16)); regionAction = new QAction(this); regionAction->setStatusTip(tr("Choose your content location")); - QAction *localAction = The::globalActions()->value("local-region"); + QAction *localAction = getAction("localRegion"); if (!localAction->text().isEmpty()) { QMenu *regionMenu = new QMenu(this); - regionMenu->addAction(The::globalActions()->value("worldwide-region")); + regionMenu->addAction(getAction("worldwideRegion")); regionMenu->addAction(localAction); regionMenu->addSeparator(); - QAction *moreRegionsAction = The::globalActions()->value("more-region"); + QAction *moreRegionsAction = getAction("moreRegion"); regionMenu->addAction(moreRegionsAction); connect(moreRegionsAction, SIGNAL(triggered()), SLOT(showRegionsView())); regionAction->setMenu(regionMenu); - } else { - connect(regionAction, SIGNAL(triggered()), SLOT(showRegionsView())); } + connect(regionAction, SIGNAL(triggered()), SLOT(showRegionsView())); /* Stupid code that generates the QRC items foreach(YTRegion r, YTRegions::list()) @@ -870,150 +924,158 @@ void MainWindow::createStatusBar() { */ statusBar()->addPermanentWidget(statusToolBar); + statusBar()->hide(); } void MainWindow::showStopAfterThisInStatusBar(bool show) { - QAction* action = The::globalActions()->value("stopafterthis"); - showActionInStatusBar(action, show); + QAction *action = getAction("stopafterthis"); + showActionsInStatusBar({action}, show); } -void MainWindow::showActionInStatusBar(QAction* action, bool show) { +void MainWindow::showActionsInStatusBar(const QVector &actions, bool show) { #ifdef APP_EXTRA Extra::fadeInWidget(statusBar(), statusBar()); #endif + for (auto action : actions) { + if (show) { + if (statusToolBar->actions().contains(action)) continue; + if (statusToolBar->actions().isEmpty()) { + statusToolBar->addAction(action); + } else { + statusToolBar->insertAction(statusToolBar->actions().at(0), action); + } + } else { + statusToolBar->removeAction(action); + } + } + if (show) { - statusToolBar->insertAction(statusToolBar->actions().first(), action); - if (statusBar()->isHidden() && !fullscreenFlag) - setStatusBarVisibility(true); + if (statusBar()->isHidden() && !fullScreenActive) setStatusBarVisibility(true); } else { - statusToolBar->removeAction(action); - if (statusBar()->isVisible() && !needStatusBar()) - setStatusBarVisibility(false); + if (statusBar()->isVisible() && !needStatusBar()) setStatusBarVisibility(false); } } void MainWindow::setStatusBarVisibility(bool show) { - statusBar()->setVisible(show); - if (views->currentWidget() == mediaView) - QTimer::singleShot(0, mediaView, SLOT(maybeAdjustWindowSize())); + if (statusBar()->isVisible() != show) { + statusBar()->setVisible(show); + if (views->currentWidget() == mediaView) + QTimer::singleShot(0, mediaView, SLOT(adjustWindowSize())); + } } void MainWindow::adjustStatusBarVisibility() { setStatusBarVisibility(needStatusBar()); } -void MainWindow::readSettings() { - QSettings settings; - if (settings.contains("geometry")) { - restoreGeometry(settings.value("geometry").toByteArray()); +void MainWindow::hideToolbar() { +#ifdef APP_MAC + mac::showToolBar(winId(), false); +#else + mainToolBar->hide(); +#endif +} + +void MainWindow::showToolbar() { #ifdef APP_MAC - MacSupport::fixGeometry(this); + mac::showToolBar(winId(), true); +#else + mainToolBar->show(); #endif +} + +void MainWindow::readSettings() { + QSettings settings; + QByteArray geometrySettings = settings.value("geometry").toByteArray(); + if (!geometrySettings.isEmpty()) { + restoreGeometry(geometrySettings); } else { - const QRect desktopSize = qApp->desktop()->availableGeometry(); - int w = qMin(2000, desktopSize.width()); - int h = qMin(w / 3, desktopSize.height()); + const QRect desktopSize = QGuiApplication::primaryScreen()->availableGeometry(); + int w = desktopSize.width() * .9; + int h = qMin(w / 2, desktopSize.height()); setGeometry( - QStyle::alignedRect( - Qt::LeftToRight, - Qt::AlignTop, - QSize(w, h), - desktopSize) - ); + QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, QSize(w, h), desktopSize)); } - const VideoDefinition& firstDefinition = VideoDefinition::getDefinitions().first(); - setDefinitionMode(settings.value("definition", firstDefinition.getName()).toString()); - The::globalActions()->value("manualplay")->setChecked(settings.value("manualplay", false).toBool()); - The::globalActions()->value("adjustwindowsize")->setChecked(settings.value("adjustWindowSize", true).toBool()); + setDefinitionMode(settings.value("definition", YT3::instance().maxVideoDefinition().getName()) + .toString()); + getAction("manualplay")->setChecked(settings.value("manualplay", false).toBool()); + getAction("safeSearch")->setChecked(settings.value("safeSearch", false).toBool()); +#ifndef APP_MAC + menuBar()->setVisible(settings.value("menuBar", false).toBool()); +#endif } void MainWindow::writeSettings() { QSettings settings; - if (!isReallyFullScreen()) + if (!isReallyFullScreen()) { settings.setValue("geometry", saveGeometry()); - mediaView->saveSplitterState(); + if (mediaView) mediaView->saveSplitterState(); + } -#ifdef APP_PHONON - if (audioOutput->volume() > 0.1) - settings.setValue("volume", audioOutput->volume()); - // settings.setValue("volumeMute", audioOutput->isMuted()); + settings.setValue("manualplay", getAction("manualplay")->isChecked()); + settings.setValue("safeSearch", getAction("safeSearch")->isChecked()); +#ifndef APP_MAC + settings.setValue("menuBar", menuBar()->isVisible()); #endif - - settings.setValue("manualplay", The::globalActions()->value("manualplay")->isChecked()); } void MainWindow::goBack() { if (history.size() > 1) { history.pop(); - QWidget *widget = history.pop(); - showWidget(widget); + showView(history.pop()); } } -void MainWindow::showWidget(QWidget* widget, bool transition) { - Q_UNUSED(transition); +void MainWindow::showView(View *view, bool transition) { + if (!history.isEmpty() && view == history.top()) { + qDebug() << "Attempting to show same view" << view; + return; + } + +#ifdef APP_MAC + if (transition && !history.isEmpty()) CompositeFader::go(this, this->grab()); +#endif - if (compactViewAct->isChecked()) - compactViewAct->toggle(); + if (compactViewAct->isChecked()) compactViewAct->toggle(); // call hide method on the current view - View* oldView = qobject_cast (views->currentWidget()); + View *oldView = qobject_cast(views->currentWidget()); if (oldView) { oldView->disappear(); - views->currentWidget()->setEnabled(false); - } else qDebug() << "Cannot cast view"; - - const bool isMediaView = widget == mediaView; + oldView->setEnabled(false); + oldView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + } else + qDebug() << "Cannot cast old view"; + + view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + view->setEnabled(true); + views->setCurrentWidget(view); + view->appear(); + + QString title = view->getTitle(); + if (title.isEmpty()) + title = Constants::NAME; + else + title += QLatin1String(" - ") + Constants::NAME; + setWindowTitle(title); + const bool isMediaView = view == mediaView; stopAct->setEnabled(isMediaView); compactViewAct->setEnabled(isMediaView); - toolbarSearch->setEnabled(widget == homeView || isMediaView || widget == downloadView); - - aboutAct->setEnabled(widget != aboutView); - The::globalActions()->value("downloads")->setChecked(widget == downloadView); - - QWidget *oldWidget = views->currentWidget(); - if (oldWidget) - oldWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); - - views->setCurrentWidget(widget); - widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - // call show method on the new view - View* newView = qobject_cast (widget); - if (newView) { - widget->setEnabled(true); - QHash metadata = newView->metadata(); - - QString title = metadata.value("title").toString(); - if (title.isEmpty()) title = Constants::NAME; - else title += QLatin1String(" - ") + Constants::NAME; - setWindowTitle(title); - - statusToolBar->setUpdatesEnabled(false); - - // dynamic view actions - foreach (QAction* action, viewActions) - showActionInStatusBar(action, false); - viewActions = newView->getViewActions(); - foreach (QAction* action, viewActions) - showActionInStatusBar(action, true); - - newView->appear(); - - adjustStatusBarVisibility(); - messageLabel->hide(); - - statusToolBar->setUpdatesEnabled(true); - - /* - QString desc = metadata.value("description").toString(); - if (!desc.isEmpty()) showMessage(desc); - */ - } + toolbarSearch->setEnabled(isMediaView); + aboutAct->setEnabled(view != aboutView); + getAction("downloads")->setChecked(view == downloadView); + + // dynamic view actions + /* Not currently used by any view + showActionsInStatusBar(viewActions, false); + viewActions = newView->getViewActions(); + showActionsInStatusBar(viewActions, true); + */ - history.push(widget); + history.push(view); + emit viewChanged(); } void MainWindow::about() { @@ -1021,7 +1083,7 @@ void MainWindow::about() { aboutView = new AboutView(this); views->addWidget(aboutView); } - showWidget(aboutView); + showView(aboutView); } void MainWindow::visitSite() { @@ -1037,7 +1099,7 @@ void MainWindow::donate() { } void MainWindow::reportIssue() { - QUrl url("http://flavio.tordini.org/forums/forum/minitube-forums/minitube-troubleshooting"); + QUrl url("https://flavio.tordini.org/forums/forum/minitube-forums/minitube-troubleshooting"); QDesktopServices::openUrl(url); } @@ -1048,7 +1110,10 @@ void MainWindow::quit() { } #endif // do not save geometry when in full screen or in compact mode - if (!fullscreenFlag && !compactViewAct->isChecked()) { + if (!fullScreenActive && !compactViewAct->isChecked()) { +#ifdef APP_MAC + hideToolbar(); +#endif writeSettings(); } // mediaView->stop(); @@ -1056,6 +1121,7 @@ void MainWindow::quit() { ChannelAggregator::instance()->stop(); ChannelAggregator::instance()->cleanup(); Database::shutdown(); + HttpUtils::clearCaches(); qApp->quit(); } @@ -1084,15 +1150,18 @@ void MainWindow::showEvent(QShowEvent *e) { bool MainWindow::confirmQuit() { if (DownloadManager::instance()->activeItems() > 0) { QMessageBox msgBox(this); - msgBox.setIconPixmap(IconUtils::pixmap(":/images/app.png").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); - msgBox.setText(tr("Do you want to exit %1 with a download in progress?").arg(Constants::NAME)); - msgBox.setInformativeText(tr("If you close %1 now, this download will be cancelled.").arg(Constants::NAME)); + msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png", devicePixelRatioF())); + msgBox.setText( + tr("Do you want to exit %1 with a download in progress?").arg(Constants::NAME)); + msgBox.setInformativeText( + tr("If you close %1 now, this download will be cancelled.").arg(Constants::NAME)); msgBox.setModal(true); // make it a "sheet" on the Mac msgBox.setWindowModality(Qt::WindowModal); msgBox.addButton(tr("Close and cancel download"), QMessageBox::RejectRole); - QPushButton *waitButton = msgBox.addButton(tr("Wait for download to finish"), QMessageBox::ActionRole); + QPushButton *waitButton = + msgBox.addButton(tr("Wait for download to finish"), QMessageBox::ActionRole); msgBox.exec(); @@ -1103,82 +1172,75 @@ bool MainWindow::confirmQuit() { return true; } -void MainWindow::showHome(bool transition) { - showWidget(homeView, transition); - currentTime->clear(); - // totalTime->clear(); +void MainWindow::showHome() { + showView(homeView); + currentTimeLabel->clear(); + seekSlider->setValue(0); } void MainWindow::showMedia(SearchParams *searchParams) { - showWidget(mediaView); + showView(mediaView); + if (getAction("safeSearch")->isChecked()) + searchParams->setSafeSearch(SearchParams::Strict); + else + searchParams->setSafeSearch(SearchParams::None); mediaView->search(searchParams); } void MainWindow::showMedia(VideoSource *videoSource) { - showWidget(mediaView); + showView(mediaView); mediaView->setVideoSource(videoSource); } -#ifdef APP_PHONON -void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState */) { +void MainWindow::stateChanged(Media::State newState) { + qDebug() << newState; - // qDebug() << "Phonon state: " << newState; + seekSlider->setEnabled(newState != Media::StoppedState); switch (newState) { - - case Phonon::ErrorState: - if (mediaObject->errorType() == Phonon::FatalError) { - // Do not display because we try to play incomplete video files and sometimes trigger this - // We retry automatically (in MediaView) so no need to show it - // showMessage(tr("Fatal error: %1").arg(mediaObject->errorString())); - } else { - showMessage(tr("Error: %1").arg(mediaObject->errorString())); - } + case Media::ErrorState: + showMessage(tr("Error: %1").arg(media->errorString())); break; - case Phonon::PlayingState: + case Media::PlayingState: pauseAct->setEnabled(true); pauseAct->setIcon(IconUtils::icon("media-playback-pause")); pauseAct->setText(tr("&Pause")); - pauseAct->setStatusTip(tr("Pause playback") + " (" + pauseAct->shortcut().toString(QKeySequence::NativeText) + ")"); - // stopAct->setEnabled(true); + pauseAct->setStatusTip(tr("Pause playback") + " (" + + pauseAct->shortcut().toString(QKeySequence::NativeText) + ")"); break; - case Phonon::StoppedState: + case Media::StoppedState: pauseAct->setEnabled(false); pauseAct->setIcon(IconUtils::icon("media-playback-start")); pauseAct->setText(tr("&Play")); - pauseAct->setStatusTip(tr("Resume playback") + " (" + pauseAct->shortcut().toString(QKeySequence::NativeText) + ")"); - // stopAct->setEnabled(false); + pauseAct->setStatusTip(tr("Resume playback") + " (" + + pauseAct->shortcut().toString(QKeySequence::NativeText) + ")"); break; - case Phonon::PausedState: + case Media::PausedState: pauseAct->setEnabled(true); pauseAct->setIcon(IconUtils::icon("media-playback-start")); pauseAct->setText(tr("&Play")); - pauseAct->setStatusTip(tr("Resume playback") + " (" + pauseAct->shortcut().toString(QKeySequence::NativeText) + ")"); - // stopAct->setEnabled(true); + pauseAct->setStatusTip(tr("Resume playback") + " (" + + pauseAct->shortcut().toString(QKeySequence::NativeText) + ")"); break; - case Phonon::BufferingState: + case Media::BufferingState: pauseAct->setEnabled(false); pauseAct->setIcon(IconUtils::icon("content-loading")); pauseAct->setText(tr("&Loading...")); pauseAct->setStatusTip(QString()); break; - case Phonon::LoadingState: + case Media::LoadingState: pauseAct->setEnabled(false); - currentTime->clear(); - // totalTime->clear(); - // stopAct->setEnabled(true); + currentTimeLabel->clear(); break; - default: - ; + default:; } } -#endif void MainWindow::stop() { showHome(); @@ -1190,31 +1252,38 @@ void MainWindow::resizeEvent(QResizeEvent *e) { #ifdef APP_MAC if (initialized && mac::CanGoFullScreen(winId())) { bool isFullscreen = mac::IsFullScreen(winId()); - if (isFullscreen != fullscreenFlag) { + if (isFullscreen != fullScreenActive) { if (compactViewAct->isChecked()) { compactViewAct->setChecked(false); compactView(false); } - fullscreenFlag = isFullscreen; + fullScreenActive = isFullscreen; updateUIForFullscreen(); } } #endif #ifdef APP_MAC_QMACTOOLBAR - toolbarSearch->move(width() - toolbarSearch->width() - 13, -38); + int moreButtonWidth = 40; + toolbarSearch->move(width() - toolbarSearch->width() - moreButtonWidth - 7, -34); #endif - adjustMessageLabelPosition(); + hideMessage(); } -void MainWindow::moveEvent(QMoveEvent *e) { +void MainWindow::enterEvent(QEvent *e) { Q_UNUSED(e); - adjustMessageLabelPosition(); +#ifdef APP_MAC + // Workaround cursor bug on macOS + unsetCursor(); +#endif } -void MainWindow::fullscreen() { +void MainWindow::leaveEvent(QEvent *e) { + Q_UNUSED(e); + if (fullScreenActive) hideFullscreenUI(); +} - if (compactViewAct->isChecked()) - compactViewAct->toggle(); +void MainWindow::toggleFullscreen() { + if (compactViewAct->isChecked()) compactViewAct->toggle(); #ifdef APP_MAC WId handle = winId(); @@ -1225,12 +1294,12 @@ void MainWindow::fullscreen() { } #endif - fullscreenFlag = !fullscreenFlag; + fullScreenActive = !fullScreenActive; - if (fullscreenFlag) { + if (fullScreenActive) { // Enter full screen - m_maximized = isMaximized(); + maximizedBeforeFullScreen = isMaximized(); // save geometry now, if the user quits when in full screen // geometry won't be saved @@ -1239,6 +1308,8 @@ void MainWindow::fullscreen() { #ifdef APP_MAC MacSupport::enterFullScreen(this, views); #else + menuVisibleBeforeFullScreen = menuBar()->isVisible(); + menuBar()->hide(); if (mainToolBar) mainToolBar->hide(); showFullScreen(); #endif @@ -1249,25 +1320,27 @@ void MainWindow::fullscreen() { #ifdef APP_MAC MacSupport::exitFullScreen(this, views); #else - if (mainToolBar) mainToolBar->show(); - if (m_maximized) showMaximized(); - else showNormal(); + menuBar()->setVisible(menuVisibleBeforeFullScreen); + if (mainToolBar) mainToolBar->setVisible(views->currentWidget() == mediaView); + if (maximizedBeforeFullScreen) + showMaximized(); + else + showNormal(); #endif // Make sure the window has focus activateWindow(); - } + qApp->processEvents(); updateUIForFullscreen(); - } void MainWindow::updateUIForFullscreen() { static QList fsShortcuts; static QString fsText; - if (fullscreenFlag) { + if (fullScreenActive) { fsShortcuts = fullscreenAct->shortcuts(); fsText = fullscreenAct->text(); if (fsText.isEmpty()) qDebug() << "[taking Empty!]"; @@ -1276,6 +1349,14 @@ void MainWindow::updateUIForFullscreen() { fullscreenAct->setText(tr("Leave &Full Screen")); fullscreenAct->setIcon(IconUtils::icon("view-restore")); setStatusBarVisibility(false); + + if (mainToolBar) { + removeToolBar(mainToolBar); + mainToolBar->move(0, 0); + } + + mediaView->removeSidebar(); + } else { fullscreenAct->setShortcuts(fsShortcuts); if (fsText.isEmpty()) fsText = "[Empty!]"; @@ -1283,37 +1364,38 @@ void MainWindow::updateUIForFullscreen() { fullscreenAct->setIcon(IconUtils::icon("view-fullscreen")); if (needStatusBar()) setStatusBarVisibility(true); + + if (mainToolBar) { + addToolBar(mainToolBar); + } + + mediaView->restoreSidebar(); } // No compact view action when in full screen - compactViewAct->setVisible(!fullscreenFlag); + compactViewAct->setVisible(!fullScreenActive); compactViewAct->setChecked(false); // Hide anything but the video - mediaView->setPlaylistVisible(!fullscreenFlag); - if (mainToolBar) mainToolBar->setVisible(!fullscreenFlag); - -#ifndef APP_MAC - menuBar()->setVisible(!fullscreenFlag); -#endif + mediaView->setSidebarVisibility(!fullScreenActive); - if (fullscreenFlag) { + if (fullScreenActive) { stopAct->setShortcuts(QList() << QKeySequence(Qt::Key_MediaStop)); } else { - stopAct->setShortcuts(QList() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop)); + stopAct->setShortcuts(QList() + << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop)); } #ifdef Q_OS_MAC - MacSupport::fullScreenActions(The::globalActions()->values(), fullscreenFlag); + MacSupport::fullScreenActions(actionMap, fullScreenActive); #endif - if (views->currentWidget() == mediaView) - mediaView->setFocus(); + if (views->currentWidget() == mediaView) mediaView->setFocus(); - if (fullscreenFlag) { - hideMouse(); + if (fullScreenActive) { + if (views->currentWidget() == mediaView) hideFullscreenUI(); } else { - mouseTimer->stop(); + fullscreenTimer->stop(); unsetCursor(); } } @@ -1321,26 +1403,65 @@ void MainWindow::updateUIForFullscreen() { bool MainWindow::isReallyFullScreen() { #ifdef Q_OS_MAC WId handle = winId(); - if (mac::CanGoFullScreen(handle)) return mac::IsFullScreen(handle); - else return isFullScreen(); + if (mac::CanGoFullScreen(handle)) + return mac::IsFullScreen(handle); + else + return isFullScreen(); #else return isFullScreen(); #endif } +void MainWindow::missingKeyWarning() { + static bool shown = false; + if (shown) return; + shown = true; + QMessageBox msgBox(this); + msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png", devicePixelRatioF())); + msgBox.setText(QString("%1 was built without a Google API key.").arg(Constants::NAME)); + msgBox.setInformativeText(QString("It won't work unless you enter one." + "

In alternative you can get %1 from the developer site.") + .arg(Constants::NAME)); + msgBox.setModal(true); + msgBox.setWindowModality(Qt::WindowModal); + msgBox.addButton(QMessageBox::Close); + QPushButton *enterKeyButton = + msgBox.addButton(QString("Enter API key..."), QMessageBox::AcceptRole); + QPushButton *devButton = msgBox.addButton(QString("Get from %1").arg(Constants::WEBSITE), + QMessageBox::AcceptRole); + QPushButton *helpButton = msgBox.addButton(QMessageBox::Help); + + msgBox.exec(); + + if (msgBox.clickedButton() == helpButton) { + QDesktopServices::openUrl(QUrl("https://github.com/flaviotordini/minitube/blob/master/" + "README.md#google-api-key")); + } else if (msgBox.clickedButton() == enterKeyButton) { + bool ok; + QString text = QInputDialog::getText(this, QString(), "Google API key:", QLineEdit::Normal, + QString(), &ok); + if (ok && !text.isEmpty()) { + QSettings settings; + settings.setValue("googleApiKey", text); + YT3::instance().initApiKeys(); + } + } else if (msgBox.clickedButton() == devButton) { + QDesktopServices::openUrl(QUrl(Constants::WEBSITE)); + } + shown = false; +} + void MainWindow::compactView(bool enable) { - m_compact = enable; + setUpdatesEnabled(false); + + compactModeActive = enable; static QList compactShortcuts; static QList stopShortcuts; - const static QString key = "compactGeometry"; + const QString key = "compactGeometry"; QSettings settings; -#ifndef APP_MAC - menuBar()->setVisible(!enable); -#endif - if (enable) { setMinimumSize(320, 180); #ifdef Q_OS_MAC @@ -1351,14 +1472,14 @@ void MainWindow::compactView(bool enable) { if (settings.contains(key)) restoreGeometry(settings.value(key).toByteArray()); else - resize(320, 180); + resize(480, 270); #ifdef APP_MAC_QMACTOOLBAR mac::showToolBar(winId(), !enable); #else mainToolBar->setVisible(!enable); #endif - mediaView->setPlaylistVisible(!enable); + mediaView->setSidebarVisibility(!enable); statusBar()->hide(); compactShortcuts = compactViewAct->shortcuts(); @@ -1367,25 +1488,28 @@ void MainWindow::compactView(bool enable) { QList newStopShortcuts(stopShortcuts); newStopShortcuts.removeAll(QKeySequence(Qt::Key_Escape)); stopAct->setShortcuts(newStopShortcuts); - compactViewAct->setShortcuts(QList(compactShortcuts) << QKeySequence(Qt::Key_Escape)); + compactViewAct->setShortcuts(QList(compactShortcuts) + << QKeySequence(Qt::Key_Escape)); // ensure focus does not end up to the search box // as it would steal the Space shortcut mediaView->setFocus(); } else { + settings.setValue(key, saveGeometry()); + // unset minimum size setMinimumSize(0, 0); + #ifdef Q_OS_MAC mac::SetupFullScreenWindow(winId()); #endif - settings.setValue(key, saveGeometry()); #ifdef APP_MAC_QMACTOOLBAR mac::showToolBar(winId(), !enable); #else mainToolBar->setVisible(!enable); #endif - mediaView->setPlaylistVisible(!enable); + mediaView->setSidebarVisibility(!enable); if (needStatusBar()) setStatusBarVisibility(true); readSettings(); @@ -1397,9 +1521,26 @@ void MainWindow::compactView(bool enable) { // auto float on top floatOnTop(enable, false); -#ifdef Q_OS_MAC +#ifdef APP_MAC mac::compactMode(winId(), enable); +#else + if (enable) { + menuVisibleBeforeCompactMode = menuBar()->isVisible(); + menuBar()->hide(); + } else { + menuBar()->setVisible(menuVisibleBeforeCompactMode); + } #endif + + setUpdatesEnabled(true); +} + +void MainWindow::toggleToolbarMenu() { + if (!toolbarMenu) toolbarMenu = new ToolbarMenu(this); + if (toolbarMenu->isVisible()) + toolbarMenu->hide(); + else + toolbarMenu->show(); } void MainWindow::searchFocus() { @@ -1407,138 +1548,119 @@ void MainWindow::searchFocus() { toolbarSearch->setFocus(); } -#ifdef APP_PHONON -void MainWindow::initPhonon() { - // Phonon initialization - if (mediaObject) delete mediaObject; - if (audioOutput) delete audioOutput; - mediaObject = new Phonon::MediaObject(this); - mediaObject->setTickInterval(100); - connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)), - SLOT(stateChanged(Phonon::State, Phonon::State))); - connect(mediaObject, SIGNAL(tick(qint64)), SLOT(tick(qint64))); - connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), SLOT(totalTimeChanged(qint64))); - - audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this); - connect(audioOutput, SIGNAL(volumeChanged(qreal)), SLOT(volumeChanged(qreal))); - connect(audioOutput, SIGNAL(mutedChanged(bool)), SLOT(volumeMutedChanged(bool))); - Phonon::createPath(mediaObject, audioOutput); - volumeSlider->setAudioOutput(audioOutput); - -#ifdef APP_PHONON_SEEK - seekSlider->setMediaObject(mediaObject); +void MainWindow::initMedia() { +#ifdef MEDIA_QTAV + qFatal("QtAV has a showstopper bug. Audio stops randomly. See bug " + "https://github.com/wang-bin/QtAV/issues/1184"); + media = new MediaQtAV(this); +#elif defined MEDIA_MPV + media = new MediaMPV(); +#else + qFatal("No media backend defined"); #endif + media->init(); + media->setUserAgent(HttpUtils::stealthUserAgent()); QSettings settings; - audioOutput->setVolume(settings.value("volume", 1.).toReal()); - // audioOutput->setMuted(settings.value("volumeMute").toBool()); - - mediaObject->stop(); + qreal volume = settings.value("volume", 1.).toReal(); + media->setVolume(volume); + + connect(media, &Media::error, this, &MainWindow::handleError); + connect(media, &Media::stateChanged, this, &MainWindow::stateChanged); + connect(media, &Media::positionChanged, this, &MainWindow::tick); + + connect(seekSlider, &QSlider::sliderMoved, this, [this](int value) { + // value : maxValue = posit ion : duration + qint64 ms = (value * media->duration()) / seekSlider->maximum(); + qDebug() << "Seeking to" << ms; + media->seek(ms); + if (media->state() == Media::PausedState) media->play(); + }); + connect(seekSlider, &QSlider::sliderPressed, this, [this]() { + // value : maxValue = position : duration + qint64 ms = (seekSlider->value() * media->duration()) / seekSlider->maximum(); + media->seek(ms); + if (media->state() == Media::PausedState) media->play(); + }); + connect(media, &Media::started, this, [this]() { seekSlider->setValue(0); }); + + connect(media, &Media::volumeChanged, this, &MainWindow::volumeChanged); + connect(media, &Media::volumeMutedChanged, this, &MainWindow::volumeMutedChanged); + connect(volumeSlider, &QSlider::sliderMoved, this, [this](int value) { + qreal volume = (qreal)value / volumeSlider->maximum(); + media->setVolume(volume); + }); + connect(volumeSlider, &QSlider::sliderPressed, this, [this]() { + qreal volume = (qreal)volumeSlider->value() / volumeSlider->maximum(); + media->setVolume(volume); + }); + + mediaView->setMedia(media); } -#endif void MainWindow::tick(qint64 time) { - const QString s = formatTime(time); - if (s != currentTime->text()) { - currentTime->setText(s); - emit currentTimeChanged(s); +#ifdef APP_MAC + bool isDown = seekSlider->property("down").isValid(); +#else + bool isDown = seekSlider->isSliderDown(); +#endif + if (!isDown && media->state() == Media::PlayingState) { + // value : maxValue = position : duration + qint64 duration = media->duration(); + if (duration <= 0) return; + int value = (seekSlider->maximum() * media->position()) / duration; + seekSlider->setValue(value); } - // remaining time -#ifdef APP_PHONON - const qint64 remainingTime = mediaObject->remainingTime(); - currentTime->setStatusTip(tr("Remaining time: %1").arg(formatTime(remainingTime))); - -#ifndef APP_PHONON_SEEK - const qint64 totalTime = mediaObject->totalTime(); - slider->blockSignals(true); - // qWarning() << totalTime << time << time * 100 / totalTime; - if (totalTime > 0 && time > 0 && !slider->isSliderDown() && mediaObject->state() == Phonon::PlayingState) - slider->setValue(time * slider->maximum() / totalTime); - slider->blockSignals(false); -#endif - -#endif -} + const QString s = formatTime(time); + if (s != currentTimeLabel->text()) { + currentTimeLabel->setText(s); + emit currentTimeChanged(s); -void MainWindow::totalTimeChanged(qint64 time) { - if (time <= 0) { - // totalTime->clear(); - return; + // remaining time + const qint64 remainingTime = media->remainingTime(); + currentTimeLabel->setStatusTip(tr("Remaining time: %1").arg(formatTime(remainingTime))); } - // totalTime->setText(formatTime(time)); - - /* - slider->blockSignals(true); - slider->setMaximum(time/1000); - slider->blockSignals(false); - */ } QString MainWindow::formatTime(qint64 duration) { duration /= 1000; QString res; - int seconds = (int) (duration % 60); + int seconds = (int)(duration % 60); duration /= 60; - int minutes = (int) (duration % 60); + int minutes = (int)(duration % 60); duration /= 60; - int hours = (int) (duration % 24); - if (hours == 0) - return res.sprintf("%02d:%02d", minutes, seconds); + int hours = (int)(duration % 24); + if (hours == 0) return res.sprintf("%02d:%02d", minutes, seconds); return res.sprintf("%02d:%02d:%02d", hours, minutes, seconds); } void MainWindow::volumeUp() { -#ifdef APP_PHONON - qreal newVolume = volumeSlider->audioOutput()->volume() + .1; - if (newVolume > volumeSlider->maximumVolume()) - newVolume = volumeSlider->maximumVolume(); - volumeSlider->audioOutput()->setVolume(newVolume); -#endif + qreal newVolume = media->volume() + .1; + if (newVolume > 1.) newVolume = 1.; + media->setVolume(newVolume); } void MainWindow::volumeDown() { -#ifdef APP_PHONON - qreal newVolume = volumeSlider->audioOutput()->volume() - .1; - if (newVolume < 0.) - newVolume = 0.; - volumeSlider->audioOutput()->setVolume(newVolume); -#endif + qreal newVolume = media->volume() - .1; + if (newVolume < 0) newVolume = 0; + media->setVolume(newVolume); } -void MainWindow::volumeMute() { -#ifdef APP_PHONON - bool muted = volumeSlider->audioOutput()->isMuted(); - volumeSlider->audioOutput()->setMuted(!muted); - qApp->processEvents(); - if (muted && volumeSlider->audioOutput()->volume() == 0) { - volumeSlider->audioOutput()->setVolume(volumeSlider->maximumVolume()); - } - qDebug() << volumeSlider->audioOutput()->isMuted() << volumeSlider->audioOutput()->volume(); -#endif +void MainWindow::toggleVolumeMute() { + bool muted = media->volumeMuted(); + media->setVolumeMuted(!muted); } void MainWindow::volumeChanged(qreal newVolume) { -#ifdef APP_PHONON // automatically unmute when volume changes - if (volumeSlider->audioOutput()->isMuted()) volumeSlider->audioOutput()->setMuted(false); - - bool isZero = volumeSlider->property("zero").toBool(); - bool styleChanged = false; - if (newVolume == 0. && !isZero) { - volumeSlider->setProperty("zero", true); - styleChanged = true; - } else if (newVolume > 0. && isZero) { - volumeSlider->setProperty("zero", false); - styleChanged = true; - } - if (styleChanged) { - QSlider* volumeQSlider = volumeSlider->findChild(); - style()->unpolish(volumeQSlider); - style()->polish(volumeQSlider); - } -#endif - showMessage(tr("Volume at %1%").arg((int)(newVolume*100))); + if (media->volumeMuted()) media->setVolumeMuted(false); + showMessage(tr("Volume at %1%").arg((int)(newVolume * 100))); + // newVolume : 1.0 = x : 1000 + int value = newVolume * volumeSlider->maximum(); + volumeSlider->blockSignals(true); + volumeSlider->setValue(value); + volumeSlider->blockSignals(false); } void MainWindow::volumeMutedChanged(bool muted) { @@ -1549,30 +1671,24 @@ void MainWindow::volumeMutedChanged(bool muted) { volumeMuteAct->setIcon(IconUtils::icon("audio-volume-high")); showMessage(tr("Volume is unmuted")); } -#ifdef APP_LINUX - QToolButton *volumeMuteButton = qobject_cast(mainToolBar->widgetForAction(volumeMuteAct)); - volumeMuteButton->setIcon(volumeMuteButton->icon().pixmap(16)); -#endif } void MainWindow::setDefinitionMode(const QString &definitionName) { - QAction *definitionAct = The::globalActions()->value("definition"); + QAction *definitionAct = getAction("definition"); definitionAct->setText(definitionName); - definitionAct->setStatusTip(tr("Maximum video definition set to %1").arg(definitionAct->text()) - + " (" + definitionAct->shortcut().toString(QKeySequence::NativeText) + ")"); + definitionAct->setStatusTip( + tr("Maximum video definition set to %1").arg(definitionAct->text()) + " (" + + definitionAct->shortcut().toString(QKeySequence::NativeText) + ")"); showMessage(definitionAct->statusTip()); - QSettings settings; - settings.setValue("definition", definitionName); + YT3::instance().setMaxVideoDefinition(definitionName); + if (views->currentWidget() == mediaView) { + mediaView->reloadCurrentVideo(); + } } void MainWindow::toggleDefinitionMode() { - const QString definitionName = QSettings().value("definition").toString(); - const QList& definitions = VideoDefinition::getDefinitions(); - const VideoDefinition& currentDefinition = VideoDefinition::getDefinitionFor(definitionName); - if (currentDefinition.isEmpty()) { - setDefinitionMode(definitions.first().getName()); - return; - } + const QVector &definitions = VideoDefinition::getDefinitions(); + const VideoDefinition ¤tDefinition = YT3::instance().maxVideoDefinition(); int index = definitions.indexOf(currentDefinition); if (index != definitions.size() - 1) { @@ -1580,20 +1696,9 @@ void MainWindow::toggleDefinitionMode() { } else { index = 0; } - // TODO: pass a VideoDefinition instead of QString. setDefinitionMode(definitions.at(index).getName()); } -void MainWindow::showFullscreenToolbar(bool show) { - if (!fullscreenFlag) return; - mainToolBar->setVisible(show); -} - -void MainWindow::showFullscreenPlaylist(bool show) { - if (!fullscreenFlag) return; - mediaView->setPlaylistVisible(show); -} - void MainWindow::clearRecentKeywords() { QSettings settings; settings.remove("recentKeywords"); @@ -1603,45 +1708,49 @@ void MainWindow::clearRecentKeywords() { searchView->updateRecentKeywords(); searchView->updateRecentChannels(); } - QAbstractNetworkCache *cache = The::networkAccessManager()->cache(); - if (cache) cache->clear(); + HttpUtils::clearCaches(); showMessage(tr("Your privacy is now safe")); } void MainWindow::setManualPlay(bool enabled) { QSettings settings; settings.setValue("manualplay", QVariant::fromValue(enabled)); - showActionInStatusBar(The::globalActions()->value("manualplay"), enabled); + if (views->currentWidget() == homeView && + homeView->currentWidget() == homeView->getSearchView()) + return; + showActionsInStatusBar({getAction("manualplay")}, enabled); } void MainWindow::updateDownloadMessage(const QString &message) { - The::globalActions()->value("downloads")->setText(message); + getAction("downloads")->setText(message); } void MainWindow::downloadsFinished() { - The::globalActions()->value("downloads")->setText(tr("&Downloads")); + getAction("downloads")->setText(tr("&Downloads")); showMessage(tr("Downloads complete")); } void MainWindow::toggleDownloads(bool show) { - if (show) { stopAct->setShortcuts(QList() << QKeySequence(Qt::Key_MediaStop)); - The::globalActions()->value("downloads")->setShortcuts( - QList() << QKeySequence(Qt::CTRL + Qt::Key_J) - << QKeySequence(Qt::Key_Escape)); + getAction("downloads") + ->setShortcuts(QList() << QKeySequence(Qt::CTRL + Qt::Key_J) + << QKeySequence(Qt::Key_Escape)); } else { - The::globalActions()->value("downloads")->setShortcuts( - QList() << QKeySequence(Qt::CTRL + Qt::Key_J)); - stopAct->setShortcuts(QList() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop)); + getAction("downloads") + ->setShortcuts(QList() << QKeySequence(Qt::CTRL + Qt::Key_J)); + stopAct->setShortcuts(QList() + << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop)); } if (!downloadView) { downloadView = new DownloadView(this); views->addWidget(downloadView); } - if (show) showWidget(downloadView); - else goBack(); + if (show) + showView(downloadView); + else + goBack(); } void MainWindow::suggestionAccepted(Suggestion *suggestion) { @@ -1649,8 +1758,8 @@ void MainWindow::suggestionAccepted(Suggestion *suggestion) { } void MainWindow::search(const QString &query) { - QString q = query.trimmed(); - if (q.length() == 0) return; + QString q = query.simplified(); + if (q.isEmpty()) return; SearchParams *searchParams = new SearchParams(); searchParams->setKeywords(q); showMedia(searchParams); @@ -1660,10 +1769,9 @@ void MainWindow::dragEnterEvent(QDragEnterEvent *e) { if (e->mimeData()->hasFormat("text/uri-list")) { QList urls = e->mimeData()->urls(); if (urls.isEmpty()) return; - QUrl url = urls.first(); + const QUrl &url = urls.at(0); QString videoId = YTSearch::videoIdFromUrl(url.toString()); - if (!videoId.isEmpty()) - e->acceptProposedAction(); + if (!videoId.isEmpty()) e->acceptProposedAction(); } } @@ -1671,9 +1779,8 @@ void MainWindow::dropEvent(QDropEvent *e) { if (!toolbarSearch->isEnabled()) return; QList urls = e->mimeData()->urls(); - if (urls.isEmpty()) - return; - QUrl url = urls.first(); + if (urls.isEmpty()) return; + const QUrl &url = urls.at(0); QString videoId = YTSearch::videoIdFromUrl(url.toString()); if (!videoId.isEmpty()) { setWindowTitle(url.toString()); @@ -1695,45 +1802,33 @@ void MainWindow::checkForUpdate() { if (secondsSinceLastCheck < 86400) return; // check it out - if (updateChecker) delete updateChecker; - updateChecker = new UpdateChecker(); - connect(updateChecker, SIGNAL(newVersion(QString)), - this, SLOT(gotNewVersion(QString))); + UpdateChecker *updateChecker = new UpdateChecker(); + connect(updateChecker, &UpdateChecker::newVersion, this, + [this, updateChecker](const QString &version) { + updateChecker->deleteLater(); + QSettings settings; + QString checkedVersion = settings.value("checkedVersion").toString(); + if (checkedVersion == version) return; +#ifdef APP_SIMPLEUPDATE + simpleUpdateDialog(version); +#elif defined(APP_EXTRA) && !defined(APP_MAC) + UpdateDialog *dialog = new UpdateDialog(version, this); + dialog->show(); +#endif + }); updateChecker->checkForUpdate(); settings.setValue(updateCheckKey, unixTime); } -void MainWindow::gotNewVersion(const QString &version) { - if (updateChecker) { - delete updateChecker; - updateChecker = 0; - } - - QSettings settings; - QString checkedVersion = settings.value("checkedVersion").toString(); - if (checkedVersion == version) return; - -#ifdef APP_EXTRA -#ifndef APP_MAC - UpdateDialog *dialog = new UpdateDialog(version, this); - dialog->show(); -#endif -#else - simpleUpdateDialog(version); -#endif -} - void MainWindow::simpleUpdateDialog(const QString &version) { QMessageBox msgBox(this); - msgBox.setIconPixmap( - IconUtils::pixmap(":/images/app.png") - .scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png", devicePixelRatioF())); msgBox.setText(tr("%1 version %2 is now available.").arg(Constants::NAME, version)); msgBox.setModal(true); msgBox.setWindowModality(Qt::WindowModal); msgBox.addButton(QMessageBox::Close); - QPushButton* laterButton = msgBox.addButton(tr("Remind me later"), QMessageBox::RejectRole); - QPushButton* updateButton = msgBox.addButton(tr("Update"), QMessageBox::AcceptRole); + QPushButton *laterButton = msgBox.addButton(tr("Remind me later"), QMessageBox::RejectRole); + QPushButton *updateButton = msgBox.addButton(tr("Update"), QMessageBox::AcceptRole); msgBox.exec(); if (msgBox.clickedButton() != laterButton) { QSettings settings; @@ -1754,11 +1849,10 @@ void MainWindow::adjustMessageLabelPosition() { } void MainWindow::floatOnTop(bool onTop, bool showAction) { - if (showAction) showActionInStatusBar(The::globalActions()->value("ontop"), onTop); + if (showAction) showActionsInStatusBar({getAction("ontop")}, onTop); #ifdef APP_MAC mac::floatOnTop(winId(), onTop); - return; -#endif +#else if (onTop) { setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); show(); @@ -1766,18 +1860,12 @@ void MainWindow::floatOnTop(bool onTop, bool showAction) { setWindowFlags(windowFlags() ^ Qt::WindowStaysOnTopHint); show(); } -} - -void MainWindow::adjustWindowSizeChanged(bool enabled) { - QSettings settings; - settings.setValue("adjustWindowSize", enabled); - if (enabled && views->currentWidget() == mediaView) - mediaView->adjustWindowSize(); +#endif } void MainWindow::restore() { #ifdef APP_MAC - mac::uncloseWindow(window()->winId()); + mac::uncloseWindow(winId()); #endif } @@ -1789,8 +1877,8 @@ void MainWindow::messageReceived(const QString &message) { } else if (message == QLatin1String("--previous")) { if (skipBackwardAct->isEnabled()) skipBackwardAct->trigger(); } else if (message == QLatin1String("--stop-after-this")) { - The::globalActions()->value("stopafterthis")->toggle(); - } else if (message.startsWith("--")) { + getAction("stopafterthis")->toggle(); + } else if (message.startsWith("--")) { MainWindow::printHelp(); } else if (!message.isEmpty()) { SearchParams *searchParams = new SearchParams(); @@ -1799,14 +1887,39 @@ void MainWindow::messageReceived(const QString &message) { } } -void MainWindow::hideMouse() { +void MainWindow::hideFullscreenUI() { + if (views->currentWidget() != mediaView) return; setCursor(Qt::BlankCursor); - mediaView->setPlaylistVisible(false); + + QPoint p = mapFromGlobal(QCursor::pos()); + const int x = p.x(); + + if (x > mediaView->getSidebar()->width()) mediaView->setSidebarVisibility(false); + #ifndef APP_MAC - mainToolBar->setVisible(false); + const int y = p.y(); + bool shouldHideToolbar = !toolbarSearch->hasFocus() && y > mainToolBar->height(); + if (shouldHideToolbar) mainToolBar->setVisible(false); #endif } +void MainWindow::toggleMenuVisibility() { + bool show = !menuBar()->isVisible(); + menuBar()->setVisible(show); +} + +void MainWindow::toggleMenuVisibilityWithMessage() { + bool show = !menuBar()->isVisible(); + menuBar()->setVisible(show); + if (!show) { + QMessageBox msgBox(this); + msgBox.setText(tr("You can still access the menu bar by pressing the ALT key")); + msgBox.setModal(true); + msgBox.setWindowModality(Qt::WindowModal); + msgBox.exec(); + } +} + #ifdef APP_MAC_STORE void MainWindow::rateOnAppStore() { QDesktopServices::openUrl(QUrl("macappstore://userpub.itunes.apple.com" @@ -1830,6 +1943,30 @@ void MainWindow::printHelp() { std::cout << msg.toLocal8Bit().data(); } +void MainWindow::setupAction(QAction *action) { + // never autorepeat. + // unexperienced users tend to keep keys pressed for a "long" time + action->setAutoRepeat(false); + + // show keyboard shortcuts in the status bar + if (!action->shortcut().isEmpty()) + action->setStatusTip(action->statusTip() + QLatin1String(" (") + + action->shortcut().toString(QKeySequence::NativeText) + + QLatin1String(")")); +} + +QAction *MainWindow::getAction(const char *name) { + return actionMap.value(QByteArray::fromRawData(name, strlen(name))); +} + +void MainWindow::addNamedAction(const QByteArray &name, QAction *action) { + actionMap.insert(name, action); +} + +QMenu *MainWindow::getMenu(const char *name) { + return menuMap.value(QByteArray::fromRawData(name, strlen(name))); +} + void MainWindow::showMessage(const QString &message) { if (!isVisible()) return; #ifdef APP_MAC @@ -1837,47 +1974,51 @@ void MainWindow::showMessage(const QString &message) { #endif if (statusBar()->isVisible()) statusBar()->showMessage(message, 60000); - else { + else if (isActiveWindow()) { messageLabel->setText(message); - messageLabel->resize(messageLabel->sizeHint()); - adjustMessageLabelPosition(); - messageLabel->show(); + QSize size = messageLabel->sizeHint(); + // round width to avoid flicker with fast changing messages (e.g. volume + // changes) + int w = size.width() + 10; + const int multiple = 15; + w = w + multiple / 2; + w -= w % multiple; + size.setWidth(w); + messageLabel->resize(size); + if (messageLabel->isHidden()) { + adjustMessageLabelPosition(); + messageLabel->show(); + } messageTimer->start(); } } -#ifdef APP_ACTIVATION -void MainWindow::showActivationView(bool transition) { - QWidget *activationView = ActivationView::instance(); - if (views->currentWidget() == activationView) { - buy(); - return; +void MainWindow::hideMessage() { + if (messageLabel->isVisible()) { + messageLabel->hide(); + messageLabel->clear(); } - views->addWidget(activationView); - showWidget(activationView, transition); -} - -void MainWindow::showActivationDialog() { - QTimer::singleShot(0, new ActivationDialog(this), SLOT(show())); } -void MainWindow::buy() { - Extra::buy(); +void MainWindow::handleError(const QString &message) { + qWarning() << message; + showMessage(message); } -void MainWindow::hideBuyAction() { - QAction *action = The::globalActions()->value("buy"); - action->setVisible(false); - action->setEnabled(false); +#ifdef APP_ACTIVATION +void MainWindow::showActivationView() { + View *activationView = ActivationView::instance(); + views->addWidget(activationView); + if (views->currentWidget() != activationView) showView(activationView); } #endif void MainWindow::showRegionsView() { if (!regionsView) { regionsView = new RegionsView(this); - connect(regionsView, SIGNAL(regionChanged()), - homeView->getStandardFeedsView(), SLOT(load())); + connect(regionsView, SIGNAL(regionChanged()), homeView->getStandardFeedsView(), + SLOT(load())); views->addWidget(regionsView); } - showWidget(regionsView); + showView(regionsView); }