]> git.sur5r.net Git - minitube/blobdiff - src/mainwindow.cpp
Upload 3.9.3-2 to unstable
[minitube] / src / mainwindow.cpp
index b03d59a8e5ced29be72f7a5bfca84fd7310a04b6..e4f09c6ba5dd724b4e4f03a7cea932a8f59d977f 100644 (file)
@@ -19,22 +19,25 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $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 Q_OS_LINUX
+#ifdef APP_LINUX
 #include "gnomeglobalshortcutbackend.h"
 #endif
 #ifdef Q_OS_MAC
@@ -44,62 +47,89 @@ $END_LICENSE */
 #include "macutils.h"
 #endif
 #include "downloadmanager.h"
-#include "ytsuggester.h"
-#include "updatechecker.h"
 #include "temporary.h"
-#ifdef APP_MAC
+#include "ytsuggester.h"
+#if defined(APP_MAC_SEARCHFIELD) && !defined(APP_MAC_QMACTOOLBAR)
 #include "searchlineedit_mac.h"
 #else
 #include "searchlineedit.h"
 #endif
+#ifdef APP_MAC_QMACTOOLBAR
+#include "mactoolbar.h"
+#endif
 #include <iostream>
 #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"
+#include "sidebarwidget.h"
+#include "toolbarmenu.h"
+#include "videoarea.h"
+#include "yt3.h"
+#include "ytregions.h"
+
+#include "invidious.h"
+#include "js.h"
+#include "videoapi.h"
 
-static MainWindow *singleton = 0;
+#ifdef MEDIA_QTAV
+#include "mediaqtav.h"
+#endif
+#ifdef MEDIA_MPV
+#include "mediampv.h"
+#endif
 
-MainWindow* MainWindow::instance() {
-    if (!singleton) singleton = new MainWindow();
-    return singleton;
+#ifdef UPDATER
+#include "updater.h"
+#endif
+
+#include "subscriptionimportview.h"
+
+namespace {
+MainWindow *mainWindowInstance;
 }
 
-MainWindow::MainWindow() :
-    updateChecker(0),
-    aboutView(0),
-    downloadView(0),
-    regionsView(0),
-    #ifdef APP_PHONON
-    mediaObject(0),
-    audioOutput(0),
-    #endif
-    m_fullscreen(false),
-    m_compact(false) {
+MainWindow *MainWindow::instance() {
+    return mainWindowInstance;
+}
 
-    singleton = this;
+MainWindow::MainWindow()
+    : aboutView(nullptr), downloadView(nullptr), regionsView(nullptr), mainToolBar(nullptr),
+      fullScreenActive(false), compactModeActive(false), initialized(false), toolbarMenu(nullptr),
+      media(nullptr) {
+    mainWindowInstance = this;
 
     // views mechanism
-    history = new QStack<QWidget*>();
     views = new QStackedWidget();
-    views->hide();
     setCentralWidget(views);
 
+#ifdef APP_EXTRA
+    Extra::windowSetup(this);
+#endif
+
+    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()), SLOT(hideMessage()));
+
     // views
-    homeView = new HomeView();
+    homeView = new HomeView(this);
     views->addWidget(homeView);
 
     // TODO make this lazy
@@ -110,7 +140,8 @@ MainWindow::MainWindow() :
     // build ui
     createActions();
     createMenus();
-    createToolBars();
+    createToolBar();
+    hideToolbar();
     createStatusBar();
 
     // remove that useless menu/toolbar context menu
@@ -129,36 +160,33 @@ MainWindow::MainWindow() :
         views->widget(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
     setMinimumWidth(0);
 
-    // show the initial view
-    showHome(false);
-
 #ifdef APP_ACTIVATION
-    if (!Activation::instance().isActivated())
-        showActivationView(false);
+    Activation::instance().initialCheck();
+#else
+    showHome();
 #endif
 
-    views->show();
-
-#ifdef APP_EXTRA
-    Extra::windowSetup(this);
-#endif
+    if (VideoAPI::impl() == VideoAPI::IV) {
+        Invidious::instance().initServers();
+    } else if (VideoAPI::impl() == VideoAPI::YT3) {
+        YT3::instance().initApiKeys();
+    } else if (VideoAPI::impl() == VideoAPI::JS) {
+        JS::instance().initialize(QUrl(QLatin1String(Constants::WEBSITE) + "-ws/bundle3.js"));
+        // JS::instance().initialize(QUrl("http://localhost:8000/bundle-test.js"));
+        Invidious::instance().initServers();
+    }
 
-    qApp->processEvents();
-    QTimer::singleShot(50, this, SLOT(lazyInit()));
-}
+    connect(JsFunctions::instance(), &JsFunctions::ready, this, [] {
+        auto ua = JsFunctions::instance()->string("userAgent()").toUtf8();
+        JS::instance().getNamFactory().setRequestHeaders({{"User-Agent", ua}});
+    });
 
-MainWindow::~MainWindow() {
-    delete history;
+    QTimer::singleShot(100, this, &MainWindow::lazyInit);
 }
 
 void MainWindow::lazyInit() {
-#ifdef APP_PHONON
-    initPhonon();
-#endif
     mediaView->initialize();
-#ifdef APP_PHONON
-    mediaView->setMediaObject(mediaObject);
-#endif
+    initMedia();
     qApp->processEvents();
 
     // CLI
@@ -172,11 +200,12 @@ void MainWindow::lazyInit() {
             searchParams->setKeywords(query);
             showMedia(searchParams);
         }
-    }
+    } else
+        showMessage(tr("Make yourself comfortable"));
 
     // Global shortcuts
     GlobalShortcuts &shortcuts = GlobalShortcuts::instance();
-#ifdef Q_OS_LINUX
+#ifdef APP_LINUX
     if (GnomeGlobalShortcutBackend::IsGsdAvailable())
         shortcuts.setBackend(new GnomeGlobalShortcutBackend(&shortcuts));
 #endif
@@ -187,114 +216,169 @@ 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()));
-
-    JsFunctions::instance();
+    fullscreenTimer = new QTimer(this);
+    fullscreenTimer->setInterval(3000);
+    fullscreenTimer->setSingleShot(true);
+    connect(fullscreenTimer, SIGNAL(timeout()), SLOT(hideFullscreenUI()));
 
-    checkForUpdate();
+    // Hack to give focus to searchlineedit
+    View *view = qobject_cast<View *>(views->currentWidget());
+    if (view == homeView) {
+        QMetaObject::invokeMethod(views->currentWidget(), "appear");
+        const QString &desc = view->getDescription();
+        if (!desc.isEmpty()) showMessage(desc);
+    }
 
     ChannelAggregator::instance()->start();
+
+#ifdef UPDATER
+    Updater::instance().checkWithoutUI();
+#endif
+
+    initialized = true;
 }
 
-void MainWindow::changeEvent(QEvent* event) {
+void MainWindow::changeEvent(QEvent *e) {
 #ifdef APP_MAC
-    if (event->type() == QEvent::WindowStateChange) {
-        The::globalActions()->value("minimize")->setEnabled(!isMinimized());
+    if (e->type() == QEvent::WindowStateChange) {
+        getAction("minimize")->setEnabled(!isMinimized());
     }
 #endif
-    QMainWindow::changeEvent(event);
+    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 *event) {
+bool MainWindow::eventFilter(QObject *obj, QEvent *e) {
+    const QEvent::Type t = e->type();
 
-    if (m_fullscreen && event->type() == QEvent::MouseMove) {
+#ifndef APP_MAC
+    static bool altPressed = false;
+    if (t == QEvent::KeyRelease && altPressed) {
+        altPressed = false;
+        QKeyEvent *ke = static_cast<QKeyEvent *>(e);
+        if (ke->key() == Qt::Key_Alt) {
+            toggleMenuVisibility();
+            return true;
+        }
+    } else if (t == QEvent::KeyPress) {
+        QKeyEvent *ke = static_cast<QKeyEvent *>(e);
+        altPressed = ke->key() == Qt::Key_Alt;
+    }
+#endif
 
-        QMouseEvent *mouseEvent = static_cast<QMouseEvent*> (event);
-        const int x = mouseEvent->pos().x();
-        const QString className = QString(obj->metaObject()->className());
-        const bool isHoveringVideo =
-                (className == QLatin1String("QGLWidget")) ||
-                (className == QLatin1String("VideoAreaWidget"));
+    if (fullScreenActive && views->currentWidget() == mediaView && t == QEvent::MouseMove &&
+        obj->isWidgetType() && qobject_cast<QWidget *>(obj)->window() == this) {
+        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(e);
 
-        // qDebug() << obj << mouseEvent->pos() << isHoveringVideo << mediaView->isPlaylistVisible();
+        bool toolBarVisible = mainToolBar && mainToolBar->isVisible();
+        bool sidebarVisible = mediaView->isSidebarVisible();
 
-        if (mediaView->isPlaylistVisible()) {
-            if (isHoveringVideo && x > 5) mediaView->setPlaylistVisible(false);
-        } else {
-            if (isHoveringVideo && 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
-        const int y = mouseEvent->pos().y();
-        if (mainToolBar->isVisible()) {
-            if (isHoveringVideo && y > 5) mainToolBar->setVisible(false);
-        } else {
-            if (isHoveringVideo && y >= 0 && y < 5) mainToolBar->setVisible(true);
+        if (!toolBarVisible && !sidebarVisible) {
+            const int y = mouseEvent->pos().y();
+            if (y >= 0 && y < 5) {
+                mainToolBar->resize(width(), mainToolBar->sizeHint().height());
+                mainToolBar->setVisible(true);
+            }
         }
 #endif
 
         // show the normal cursor
         unsetCursor();
         // then hide it again after a few seconds
-        mouseTimer->start();
-
+        fullscreenTimer->start();
     }
 
-    if (event->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, event);
+    return QMainWindow::eventFilter(obj, e);
 }
 
 void MainWindow::createActions() {
-
-    QHash<QString, QAction*> *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>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
+    stopAct->setShortcuts(QList<QKeySequence>()
+                          << 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>() << QKeySequence(Qt::CTRL + Qt::Key_Right) << QKeySequence(Qt::Key_MediaNext));
+    skipAct->setShortcuts(QList<QKeySequence>() << 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>() << QKeySequence(Qt::Key_Space) << QKeySequence(Qt::Key_MediaPlay));
+    pauseAct->setShortcuts(QList<QKeySequence>()
+                           << 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<QKeySequence> fsShortcuts;
 #ifdef APP_MAC
@@ -305,41 +389,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);
@@ -347,27 +428,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>() << QKeySequence("Del") << QKeySequence("Backspace"));
+    removeAct->setShortcuts(QList<QKeySequence>()
+                            << 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);
@@ -377,33 +459,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
@@ -411,82 +494,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>() << 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>() << 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 Q_OS_LINUX
-    definitionAct->setIcon(IconUtils::tintedIcon("video-display", QColor(0, 0, 0),
-                                             QList<QSize>() << QSize(16, 16)));
-#else
-    definitionAct->setIcon(IconUtils::icon("video-display"));
-#endif
-    definitionAct->setShortcuts(QList<QKeySequence>() << 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>() << 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"));
-    action->setVisible(false);
     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
 
@@ -495,116 +589,141 @@ 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(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);
+    action = new QAction(YTRegions::defaultRegion().name, this);
+    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);
+    action->setVisible(VideoAPI::impl() != VideoAPI::IV);
+    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);
+
+    action = new QAction(tr("Import Subscriptions..."), this);
+    action->setMenuRole(QAction::ApplicationSpecificRole);
+    connect(action, &QAction::triggered, this, [this] {
+        if (!subscriptionImportView) {
+            subscriptionImportView = new SubscriptionImportView(this);
+            views->addWidget(subscriptionImportView);
+        }
+        showView(subscriptionImportView);
+    });
+    actionMap.insert("importSubscriptions", 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);
+        setupAction(action);
     }
 }
 
 void MainWindow::createMenus() {
-
-    QHash<QString, QMenu*> *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();
@@ -616,99 +735,142 @@ void MainWindow::createMenus() {
 #endif
     fileMenu->addAction(quitAct);
 
-    QMenuplaybackMenu = 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"));
 
-    QMenuvideoMenu = 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"));
+    videoMenu->addAction(getAction("openInBrowser"));
+    videoMenu->addAction(getAction("download"));
 
-    QMenu* viewMenu = menuBar()->addMenu(tr("&View"));
-    menus->insert("view", viewMenu);
-    viewMenu->addAction(fullscreenAct);
-    viewMenu->addAction(compactViewAct);
-    viewMenu->addSeparator();
-    viewMenu->addAction(The::globalActions()->value("ontop"));
-
-    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 UPDATER
+    helpMenu->addAction(Updater::instance().getAction());
+#endif
 
 #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);
+
+    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);
+    toolbarSearch = searchWrapper->getSearchLineEdit();
+#else
+    toolbarSearch = new SearchLineEdit(this);
+#endif
+    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 *)));
+    toolbarSearch->setStatusTip(searchFocusAct->statusTip());
+
+    // Add widgets to toolbar
 
-    setUnifiedTitleAndToolBarOnMac(true);
+#ifdef APP_MAC_QMACTOOLBAR
+    currentTimeLabel->hide();
+    toolbarSearch->hide();
+    volumeSlider->hide();
+    seekSlider->hide();
+    MacToolbar::instance().createToolbar(this);
+    return;
+#endif
 
     mainToolBar = new QToolBar(this);
     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<QToolButton *>(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
@@ -718,229 +880,246 @@ void MainWindow::createToolBars() {
 
     mainToolBar->addWidget(new Spacer());
 
-    QFont smallerFont = FontUtils::small();
-    currentTime = new QLabel(mainToolBar);
-    currentTime->setFont(smallerFont);
-    mainToolBar->addWidget(currentTime);
+    currentTimeLabel->setFont(FontUtils::small());
+    currentTimeLabel->setMinimumWidth(currentTimeLabel->fontInfo().pixelSize() * 4);
+    currentTimeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+    mainToolBar->addWidget(currentTimeLabel);
 
-    mainToolBar->addWidget(new Spacer());
+#ifdef APP_WIN
+    mainToolBar->addWidget(new Spacer(nullptr, 10));
+#endif
 
-#ifdef APP_PHONON_SEEK
-    seekSlider = new Phonon::SeekSlider(this);
-    seekSlider->setTracking(true);
-    seekSlider->setIconVisible(false);
-    seekSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
+    seekSlider->setOrientation(Qt::Horizontal);
+    seekSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+    seekSlider->setFocusPolicy(Qt::NoFocus);
     mainToolBar->addWidget(seekSlider);
-#else
-    slider = new SeekSlider(this);
-    slider->setEnabled(false);
-    slider->setTracking(false);
-    slider->setMaximum(1000);
-    slider->setOrientation(Qt::Horizontal);
-    slider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
-    mainToolBar->addWidget(slider);
-#endif
 
-    /*
     mainToolBar->addWidget(new Spacer());
-    totalTime = new QLabel(mainToolBar);
-    totalTime->setFont(smallerFont);
-    mainToolBar->addWidget(totalTime);
-    */
 
-    mainToolBar->addWidget(new Spacer());
     mainToolBar->addAction(volumeMuteAct);
-
-#ifdef APP_PHONON
-    volumeSlider = new Phonon::VolumeSlider(this);
-    volumeSlider->setMuteVisible(false);
-    // qDebug() << volumeSlider->children();
-    // status tip for the volume slider
-    QSlider* volumeQSlider = volumeSlider->findChild<QSlider*>();
-    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)));
+#ifndef APP_MAC_QMACTOOLBAR
+    QToolButton *volumeMuteButton =
+            qobject_cast<QToolButton *>(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());
 
-#ifdef APP_MAC
-    SearchWrapper* searchWrapper = new SearchWrapper(this);
-    toolbarSearch = searchWrapper->getSearchLineEdit();
-#else
-    toolbarSearch = new SearchLineEdit(this);
-#endif
-    toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize()*15);
-    toolbarSearch->setSuggester(new YTSuggester(this));
-    connect(toolbarSearch, SIGNAL(search(const QString&)), SLOT(search(const QString&)));
-    connect(toolbarSearch, SIGNAL(suggestionAccepted(Suggestion*)), SLOT(suggestionAccepted(Suggestion*)));
-    toolbarSearch->setStatusTip(searchFocusAct->statusTip());
-#ifdef APP_MAC
+#if defined(APP_MAC_SEARCHFIELD) && !defined(APP_MAC_QMACTOOLBAR)
     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<QToolButton *>(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));
-    statusToolBar->addAction(The::globalActions()->value("downloads"));
 
-    regionButton = new QToolButton(this);
-    regionButton->setStatusTip(tr("Choose your content location"));
-    regionButton->setIconSize(QSize(16, 16));
-    regionButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
-    regionAction = statusToolBar->addWidget(regionButton);
-    regionAction->setVisible(false);
+    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()) {
-        regionButton->setPopupMode(QToolButton::InstantPopup);
         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()));
-        regionButton->setMenu(regionMenu);
-    } else {
-        connect(regionButton, SIGNAL(clicked()), SLOT(showRegionsView()));
+        regionAction->setMenu(regionMenu);
     }
+    connect(regionAction, SIGNAL(triggered()), SLOT(showRegionsView()));
 
     /* Stupid code that generates the QRC items
     foreach(YTRegion r, YTRegions::list())
         qDebug() << QString("<file>flags/%1.png</file>").arg(r.id.toLower());
     */
 
-    statusToolBar->addAction(The::globalActions()->value("definition"));
-
     statusBar()->addPermanentWidget(statusToolBar);
-    statusBar()->show();
+    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<QAction *> &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() && !fullScreenActive) setStatusBarVisibility(true);
     } else {
-        statusToolBar->removeAction(action);
+        if (statusBar()->isVisible() && !needStatusBar()) setStatusBarVisibility(false);
     }
 }
 
-void MainWindow::readSettings() {
-    QSettings settings;
-    if (settings.contains("geometry")) {
-        restoreGeometry(settings.value("geometry").toByteArray());
+void MainWindow::setStatusBarVisibility(bool show) {
+    if (statusBar()->isVisible() != show) {
+        statusBar()->setVisible(show);
+        if (views->currentWidget() == mediaView)
+            QTimer::singleShot(0, mediaView, SLOT(adjustWindowSize()));
+    }
+}
+
+void MainWindow::adjustStatusBarVisibility() {
+    setStatusBarVisibility(needStatusBar());
+}
+
+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 {
-        setGeometry(100, 100, 1000, 500);
+        const QRect desktopSize = QGuiApplication::primaryScreen()->availableGeometry();
+        int w = desktopSize.width() * .9;
+        int h = qMin(w / 2, desktopSize.height());
+        setGeometry(
+                QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, QSize(w, h), desktopSize));
     }
-    setDefinitionMode(settings.value("definition", VideoDefinition::getDefinitionNames().first()).toString());
-    The::globalActions()->value("manualplay")->setChecked(settings.value("manualplay", false).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;
 
-    settings.setValue("geometry", saveGeometry());
-    mediaView->saveSplitterState();
+    if (!isReallyFullScreen()) {
+        settings.setValue("geometry", saveGeometry());
+        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);
+    if (history.size() > 1) {
+        history.pop();
+        showView(history.pop());
     }
 }
 
-void MainWindow::showWidget(QWidget* widget, bool transition) {
+void MainWindow::showView(View *view, bool transition) {
+    if (!history.isEmpty() && view == history.top()) {
+        qDebug() << "Attempting to show same view" << view;
+        return;
+    }
 
-    if (compactViewAct->isChecked())
-        compactViewAct->toggle();
+#ifdef APP_MAC
+    if (transition && !history.isEmpty()) CompositeFader::go(this, this->grab());
+#endif
 
-    setUpdatesEnabled(false);
+    if (compactViewAct->isChecked()) compactViewAct->toggle();
 
     // call hide method on the current view
-    View* oldView = dynamic_cast<View *> (views->currentWidget());
+    View *oldView = qobject_cast<View *>(views->currentWidget());
     if (oldView) {
+        oldView->willDisappear();
         oldView->disappear();
-        views->currentWidget()->setEnabled(false);
-    } else qDebug() << "Cannot cast view";
-
-    // call show method on the new view
-    View* newView = dynamic_cast<View *> (widget);
-    if (newView) {
-        widget->setEnabled(true);
-        QHash<QString,QVariant> metadata = newView->metadata();
-        QString title = metadata.value("title").toString();
-        if (title.isEmpty()) title = Constants::NAME;
-        else title += QLatin1String(" - ") + Constants::NAME;
-        setWindowTitle(title);
-        QString desc = metadata.value("description").toString();
-        if (!desc.isEmpty()) showMessage(desc);
-        newView->appear();
-
-        // dynamic view actions
-        foreach (QAction* action, viewActions)
-            showActionInStatusBar(action, false);
-        viewActions = newView->getViewActions();
-        foreach (QAction* action, viewActions)
-            showActionInStatusBar(action, true);
-
-    }
-
-    const bool isMediaView = widget == mediaView;
+        oldView->setEnabled(false);
+        oldView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+    } else
+        qDebug() << "Cannot cast old view";
+
+    view->willAppear();
+    view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    view->setEnabled(true);
+    views->setCurrentWidget(view);
+    view->appear();
+    view->didAppear();
+    if (oldView) oldView->didDisappear();
+
+    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);
-
-    setUpdatesEnabled(true);
-
-#ifdef APP_EXTRA
-    // if (transition && (oldWidget != mediaView || !mediaView->getVideoArea()->isVideoShown()))
-    if (transition)
-        Extra::fadeInWidget(oldWidget, widget);
-#endif
+    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() {
@@ -948,23 +1127,23 @@ void MainWindow::about() {
         aboutView = new AboutView(this);
         views->addWidget(aboutView);
     }
-    showWidget(aboutView);
+    showView(aboutView);
 }
 
 void MainWindow::visitSite() {
     QUrl url(Constants::WEBSITE);
-    statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
+    showMessage(QString(tr("Opening %1").arg(url.toString())));
     QDesktopServices::openUrl(url);
 }
 
 void MainWindow::donate() {
-    QUrl url(QString(Constants::WEBSITE) + "#donate");
-    statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
+    QUrl url("https://" + QLatin1String(Constants::ORG_DOMAIN) + "/donate");
+    showMessage(QString(tr("Opening %1").arg(url.toString())));
     QDesktopServices::openUrl(url);
 }
 
 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);
 }
 
@@ -975,10 +1154,13 @@ void MainWindow::quit() {
     }
 #endif
     // do not save geometry when in full screen or in compact mode
-    if (!m_fullscreen && !compactViewAct->isChecked()) {
+    if (!fullScreenActive && !compactViewAct->isChecked()) {
+#ifdef APP_MAC
+        hideToolbar();
+#endif
         writeSettings();
     }
-    mediaView->stop();
+    // mediaView->stop();
     Temporary::deleteAll();
     ChannelAggregator::instance()->stop();
     ChannelAggregator::instance()->cleanup();
@@ -986,32 +1168,43 @@ void MainWindow::quit() {
     qApp->quit();
 }
 
-void MainWindow::closeEvent(QCloseEvent *event) {
+void MainWindow::closeEvent(QCloseEvent *e) {
 #ifdef APP_MAC
     mac::closeWindow(winId());
-    event->ignore();
+    e->ignore();
 #else
     if (!confirmQuit()) {
-        event->ignore();
+        e->ignore();
         return;
     }
-    QWidget::closeEvent(event);
+    QWidget::closeEvent(e);
     quit();
+#endif
+    messageLabel->hide();
+}
+
+void MainWindow::showEvent(QShowEvent *e) {
+    QWidget::showEvent(e);
+#ifdef APP_MAC
+    restore();
 #endif
 }
 
 bool MainWindow::confirmQuit() {
     if (DownloadManager::instance()->activeItems() > 0) {
         QMessageBox msgBox(this);
-        msgBox.setIconPixmap(QPixmap(":/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();
 
@@ -1022,202 +1215,230 @@ 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
-            // statusBar()->showMessage(tr("Fatal error: %1").arg(mediaObject->errorString()));
-        } else {
-            statusBar()->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();
     mediaView->stop();
 }
 
-void MainWindow::resizeEvent(QResizeEvent*) {
-#ifdef Q_OS_MAC
-    if (mac::CanGoFullScreen(winId())) {
+void MainWindow::resizeEvent(QResizeEvent *e) {
+    Q_UNUSED(e);
+#ifdef APP_MAC
+    if (initialized && mac::CanGoFullScreen(winId())) {
         bool isFullscreen = mac::IsFullScreen(winId());
-        if (isFullscreen != m_fullscreen) {
+        if (isFullscreen != fullScreenActive) {
             if (compactViewAct->isChecked()) {
                 compactViewAct->setChecked(false);
                 compactView(false);
             }
-            m_fullscreen = isFullscreen;
+            fullScreenActive = isFullscreen;
             updateUIForFullscreen();
         }
     }
 #endif
+#ifdef APP_MAC_QMACTOOLBAR
+    int moreButtonWidth = 40;
+    toolbarSearch->move(width() - toolbarSearch->width() - moreButtonWidth - 7, -34);
+#endif
+    hideMessage();
 }
 
-void MainWindow::fullscreen() {
+void MainWindow::enterEvent(QEvent *e) {
+    Q_UNUSED(e);
+#ifdef APP_MAC
+    // Workaround cursor bug on macOS
+    unsetCursor();
+#endif
+}
 
-    if (compactViewAct->isChecked())
-        compactViewAct->toggle();
+void MainWindow::leaveEvent(QEvent *e) {
+    Q_UNUSED(e);
+    if (fullScreenActive) hideFullscreenUI();
+}
 
-#ifdef Q_OS_MAC
+void MainWindow::toggleFullscreen() {
+    if (compactViewAct->isChecked()) compactViewAct->toggle();
+
+#ifdef APP_MAC
     WId handle = winId();
     if (mac::CanGoFullScreen(handle)) {
-        mainToolBar->setVisible(true);
+        if (mainToolBar) mainToolBar->setVisible(true);
         mac::ToggleFullScreen(handle);
         return;
     }
 #endif
 
-    m_fullscreen = !m_fullscreen;
+    fullScreenActive = !fullScreenActive;
 
-    if (m_fullscreen) {
+    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
         writeSettings();
 
-#ifdef Q_OS_MAC
+#ifdef APP_MAC
         MacSupport::enterFullScreen(this, views);
 #else
-        mainToolBar->hide();
+        menuVisibleBeforeFullScreen = menuBar()->isVisible();
+        menuBar()->hide();
+        if (mainToolBar) mainToolBar->hide();
         showFullScreen();
 #endif
 
     } else {
         // Exit full screen
 
-#ifdef Q_OS_MAC
+#ifdef APP_MAC
         MacSupport::exitFullScreen(this, views);
 #else
-        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<QKeySequence> fsShortcuts;
     static QString fsText;
 
-    if (m_fullscreen) {
+    if (fullScreenActive) {
         fsShortcuts = fullscreenAct->shortcuts();
         fsText = fullscreenAct->text();
+        if (fsText.isEmpty()) qDebug() << "[taking Empty!]";
         fullscreenAct->setShortcuts(QList<QKeySequence>(fsShortcuts)
                                     << QKeySequence(Qt::Key_Escape));
         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!]";
         fullscreenAct->setText(fsText);
         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(!m_fullscreen);
+    compactViewAct->setVisible(!fullScreenActive);
     compactViewAct->setChecked(false);
 
     // Hide anything but the video
-    mediaView->setPlaylistVisible(!m_fullscreen);
-    statusBar()->setVisible(!m_fullscreen);
+    mediaView->setSidebarVisibility(!fullScreenActive);
 
-#ifndef APP_MAC
-    menuBar()->setVisible(!m_fullscreen);
-#endif
-
-    if (m_fullscreen) {
+    if (fullScreenActive) {
         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
     } else {
-        stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
+        stopAct->setShortcuts(QList<QKeySequence>()
+                              << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
     }
 
 #ifdef Q_OS_MAC
-    MacSupport::fullScreenActions(The::globalActions()->values(), m_fullscreen);
+    MacSupport::fullScreenActions(actionMap, fullScreenActive);
 #endif
 
-    if (views->currentWidget() == mediaView)
-        mediaView->setFocus();
+    if (views->currentWidget() == mediaView) mediaView->setFocus();
 
-    if (m_fullscreen) {
-        hideMouse();
+    if (fullScreenActive) {
+        if (views->currentWidget() == mediaView) hideFullscreenUI();
     } else {
-        mouseTimer->stop();
+        fullscreenTimer->stop();
         unsetCursor();
     }
 }
@@ -1225,26 +1446,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."
+                                      "<p>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<QKeySequence> compactShortcuts;
     static QList<QKeySequence> 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
@@ -1255,11 +1515,15 @@ 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);
-        mediaView->setPlaylistVisible(!enable);
-        statusBar()->setVisible(!enable);
+#endif
+        mediaView->setSidebarVisibility(!enable);
+        statusBar()->hide();
 
         compactShortcuts = compactViewAct->shortcuts();
         stopShortcuts = stopAct->shortcuts();
@@ -1267,22 +1531,30 @@ void MainWindow::compactView(bool enable) {
         QList<QKeySequence> newStopShortcuts(stopShortcuts);
         newStopShortcuts.removeAll(QKeySequence(Qt::Key_Escape));
         stopAct->setShortcuts(newStopShortcuts);
-        compactViewAct->setShortcuts(QList<QKeySequence>(compactShortcuts) << QKeySequence(Qt::Key_Escape));
+        compactViewAct->setShortcuts(QList<QKeySequence>(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);
-        mediaView->setPlaylistVisible(!enable);
-        statusBar()->setVisible(!enable);
+#endif
+        mediaView->setSidebarVisibility(!enable);
+        if (needStatusBar()) setStatusBarVisibility(true);
+
         readSettings();
 
         compactViewAct->setShortcuts(compactShortcuts);
@@ -1290,11 +1562,28 @@ void MainWindow::compactView(bool enable) {
     }
 
     // auto float on top
-    floatOnTop(enable);
+    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() {
@@ -1302,174 +1591,155 @@ 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)),
-            this, SLOT(stateChanged(Phonon::State, Phonon::State)));
-    connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64)));
-    connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64)));
-#ifdef APP_PHONON_SEEK
-    seekSlider->setMediaObject(mediaObject);
-#endif
-    audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this);
-    connect(audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(volumeChanged(qreal)));
-    connect(audioOutput, SIGNAL(mutedChanged(bool)), this, SLOT(volumeMutedChanged(bool)));
-    volumeSlider->setAudioOutput(audioOutput);
-    Phonon::createPath(mediaObject, audioOutput);
+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).toDouble());
-    // audioOutput->setMuted(settings.value("volumeMute").toBool());
+    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) {
-    if (time <= 0) {
-        // the "if" is important because tick is continually called
-        // and we don't want to paint the toolbar every 100ms
-        if (!currentTime->text().isEmpty()) currentTime->clear();
-        return;
+#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);
     }
 
-    currentTime->setText(formatTime(time));
-
-    // remaining time
-#ifdef APP_PHONON
-    const qint64 remainingTime = mediaObject->remainingTime();
-    currentTime->setStatusTip(tr("Remaining time: %1").arg(formatTime(remainingTime)));
+    const QString s = formatTime(time);
+    if (s != currentTimeLabel->text()) {
+        currentTimeLabel->setText(s);
+        emit currentTimeChanged(s);
 
-#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
-}
-
-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 time) {
-    QTime displayTime;
-    displayTime = displayTime.addMSecs(time);
-    QString timeString;
-    // 60 * 60 * 1000 = 3600000
-    if (time > 3600000)
-        timeString = displayTime.toString("h:mm:ss");
-    else
-        timeString = displayTime.toString("m:ss");
-    return timeString;
+QString MainWindow::formatTime(qint64 duration) {
+    duration /= 1000;
+    QString res;
+    int seconds = (int)(duration % 60);
+    duration /= 60;
+    int minutes = (int)(duration % 60);
+    duration /= 60;
+    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
-    volumeSlider->audioOutput()->setMuted(!volumeSlider->audioOutput()->isMuted());
-#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<QSlider*>();
-        style()->unpolish(volumeQSlider);
-        style()->polish(volumeQSlider);
-    }
-#endif
-    statusBar()->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) {
     if (muted) {
         volumeMuteAct->setIcon(IconUtils::icon("audio-volume-muted"));
-        statusBar()->showMessage(tr("Volume is muted"));
+        showMessage(tr("Volume is muted"));
     } else {
         volumeMuteAct->setIcon(IconUtils::icon("audio-volume-high"));
-        statusBar()->showMessage(tr("Volume is unmuted"));
+        showMessage(tr("Volume is unmuted"));
     }
 }
 
-void MainWindow::setDefinitionMode(QString definitionName) {
-    QAction *definitionAct = The::globalActions()->value("definition");
+void MainWindow::setDefinitionMode(const QString &definitionName) {
+    QAction *definitionAct = getAction("definition");
     definitionAct->setText(definitionName);
-    definitionAct->setStatusTip(tr("Maximum video definition set to %1").arg(definitionAct->text())
-                                + " (" +  definitionAct->shortcut().toString(QKeySequence::NativeText) + ")");
-    statusBar()->showMessage(definitionAct->statusTip());
-    QSettings settings;
-    settings.setValue("definition", definitionName);
-}
-
-void MainWindow::toggleDefinitionMode() {
-    QSettings settings;
-    QString currentDefinition = settings.value("definition").toString();
-    QStringList definitionNames = VideoDefinition::getDefinitionNames();
-    int currentIndex = definitionNames.indexOf(currentDefinition);
-    int nextIndex = 0;
-    if (currentIndex != definitionNames.size() - 1) {
-        nextIndex = currentIndex + 1;
+    definitionAct->setStatusTip(
+            tr("Maximum video definition set to %1").arg(definitionAct->text()) + " (" +
+            definitionAct->shortcut().toString(QKeySequence::NativeText) + ")");
+    showMessage(definitionAct->statusTip());
+    YT3::instance().setMaxVideoDefinition(definitionName);
+    if (views->currentWidget() == mediaView) {
+        mediaView->reloadCurrentVideo();
     }
-    QString nextDefinition = definitionNames.at(nextIndex);
-    setDefinitionMode(nextDefinition);
 }
 
-void MainWindow::showFullscreenToolbar(bool show) {
-    if (!m_fullscreen) return;
-    mainToolBar->setVisible(show);
-}
+void MainWindow::toggleDefinitionMode() {
+    const QVector<VideoDefinition> &definitions = VideoDefinition::getDefinitions();
+    const VideoDefinition &currentDefinition = YT3::instance().maxVideoDefinition();
 
-void MainWindow::showFullscreenPlaylist(bool show) {
-    if (!m_fullscreen) return;
-    mediaView->setPlaylistVisible(show);
+    int index = definitions.indexOf(currentDefinition);
+    if (index != definitions.size() - 1) {
+        index++;
+    } else {
+        index = 0;
+    }
+    setDefinitionMode(definitions.at(index).getName());
 }
 
 void MainWindow::clearRecentKeywords() {
@@ -1481,45 +1751,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(QString message) {
-    The::globalActions()->value("downloads")->setText(message);
+void MainWindow::updateDownloadMessage(const QString &message) {
+    getAction("downloads")->setText(message);
 }
 
 void MainWindow::downloadsFinished() {
-    The::globalActions()->value("downloads")->setText(tr("&Downloads"));
-    statusBar()->showMessage(tr("Downloads complete"));
+    getAction("downloads")->setText(tr("&Downloads"));
+    showMessage(tr("Downloads complete"));
 }
 
 void MainWindow::toggleDownloads(bool show) {
-
     if (show) {
         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
-        The::globalActions()->value("downloads")->setShortcuts(
-                    QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_J)
-                    << QKeySequence(Qt::Key_Escape));
+        getAction("downloads")
+                ->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_J)
+                                                     << QKeySequence(Qt::Key_Escape));
     } else {
-        The::globalActions()->value("downloads")->setShortcuts(
-                    QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_J));
-        stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
+        getAction("downloads")
+                ->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_J));
+        stopAct->setShortcuts(QList<QKeySequence>()
+                              << 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) {
@@ -1527,31 +1801,29 @@ 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);
 }
 
-void MainWindow::dragEnterEvent(QDragEnterEvent *event) {
-    if (event->mimeData()->hasFormat("text/uri-list")) {
-        QList<QUrl> urls = event->mimeData()->urls();
+void MainWindow::dragEnterEvent(QDragEnterEvent *e) {
+    if (e->mimeData()->hasFormat("text/uri-list")) {
+        QList<QUrl> 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())
-            event->acceptProposedAction();
+        if (!videoId.isEmpty()) e->acceptProposedAction();
     }
 }
 
-void MainWindow::dropEvent(QDropEvent *event) {
+void MainWindow::dropEvent(QDropEvent *e) {
     if (!toolbarSearch->isEnabled()) return;
 
-    QList<QUrl> urls = event->mimeData()->urls();
-    if (urls.isEmpty())
-        return;
-    QUrl url = urls.first();
+    QList<QUrl> urls = e->mimeData()->urls();
+    if (urls.isEmpty()) return;
+    const QUrl &url = urls.at(0);
     QString videoId = YTSearch::videoIdFromUrl(url.toString());
     if (!videoId.isEmpty()) {
         setWindowTitle(url.toString());
@@ -1561,69 +1833,22 @@ void MainWindow::dropEvent(QDropEvent *event) {
     }
 }
 
-void MainWindow::checkForUpdate() {
-    static const QString updateCheckKey = "updateCheck";
-
-    // check every 24h
-    QSettings settings;
-    uint unixTime = QDateTime::currentDateTime().toTime_t();
-    int lastCheck = settings.value(updateCheckKey).toInt();
-    int secondsSinceLastCheck = unixTime - lastCheck;
-    // qDebug() << "secondsSinceLastCheck" << unixTime << lastCheck << secondsSinceLastCheck;
-    if (secondsSinceLastCheck < 86400) return;
-
-    // check it out
-    if (updateChecker) delete updateChecker;
-    updateChecker = new UpdateChecker();
-    connect(updateChecker, SIGNAL(newVersion(QString)),
-            this, SLOT(gotNewVersion(QString)));
-    updateChecker->checkForUpdate();
-    settings.setValue(updateCheckKey, unixTime);
-}
-
-void MainWindow::gotNewVersion(QString version) {
-    if (updateChecker) {
-        delete updateChecker;
-        updateChecker = 0;
-    }
-
-    QSettings settings;
-    QString checkedVersion = settings.value("checkedVersion").toString();
-    if (checkedVersion == version) return;
-
-#ifdef APP_SIMPLEUPDATE
-    simpleUpdateDialog(version);
-#elif defined(APP_ACTIVATION) && !defined(APP_MAC)
-    UpdateDialog *dialog = new UpdateDialog(version, this);
-    dialog->show();
-#endif
+bool MainWindow::needStatusBar() {
+    return !statusToolBar->actions().isEmpty();
 }
 
-void MainWindow::simpleUpdateDialog(QString version) {
-    QMessageBox msgBox(this);
-    msgBox.setIconPixmap(
-                QPixmap(":/images/app.png")
-                .scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
-    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);
-    msgBox.exec();
-    if (msgBox.clickedButton() != laterButton) {
-        QSettings settings;
-        settings.setValue("checkedVersion", version);
-    }
-    if (msgBox.clickedButton() == updateButton) visitSite();
+void MainWindow::adjustMessageLabelPosition() {
+    if (messageLabel->parent() == this)
+        messageLabel->move(0, height() - messageLabel->height());
+    else
+        messageLabel->move(mapToGlobal(QPoint(0, height() - messageLabel->height())));
 }
 
-void MainWindow::floatOnTop(bool onTop) {
-    showActionInStatusBar(The::globalActions()->value("ontop"), onTop);
+void MainWindow::floatOnTop(bool onTop, bool showAction) {
+    if (showAction) showActionsInStatusBar({getAction("ontop")}, onTop);
 #ifdef APP_MAC
     mac::floatOnTop(winId(), onTop);
-    return;
-#endif
+#else
     if (onTop) {
         setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
         show();
@@ -1631,11 +1856,12 @@ void MainWindow::floatOnTop(bool onTop) {
         setWindowFlags(windowFlags() ^ Qt::WindowStaysOnTopHint);
         show();
     }
+#endif
 }
 
 void MainWindow::restore() {
 #ifdef APP_MAC
-    mac::uncloseWindow(window()->winId());
+    mac::uncloseWindow(winId());
 #endif
 }
 
@@ -1647,8 +1873,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();
@@ -1657,14 +1883,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"
@@ -1688,42 +1939,82 @@ void MainWindow::printHelp() {
     std::cout << msg.toLocal8Bit().data();
 }
 
-void MainWindow::showMessage(QString message) {
-    statusBar()->showMessage(message, 60000);
+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(")"));
 }
 
-#ifdef APP_ACTIVATION
-void MainWindow::showActivationView(bool transition) {
-    QWidget *activationView = ActivationView::instance();
-    if (views->currentWidget() == activationView) {
-        buy();
-        return;
+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
+    if (!mac::isVisible(winId())) return;
+#endif
+    if (statusBar()->isVisible())
+        statusBar()->showMessage(message, 60000);
+    else if (isActiveWindow()) {
+        messageLabel->setText(message);
+        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();
     }
-    views->addWidget(activationView);
-    showWidget(activationView, transition);
 }
 
-void MainWindow::showActivationDialog() {
-    QTimer::singleShot(0, new ActivationDialog(this), SLOT(show()));
+void MainWindow::hideMessage() {
+    if (messageLabel->isVisible()) {
+        messageLabel->hide();
+        messageLabel->clear();
+    }
 }
 
-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);
 }