3 This file is part of Minitube.
4 Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
6 Minitube is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 Minitube is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Minitube. If not, see <http://www.gnu.org/licenses/>.
21 #include "mainwindow.h"
23 #include "aboutview.h"
24 #include "downloadview.h"
26 #include "mediaview.h"
27 #include "regionsview.h"
28 #include "searchview.h"
29 #include "standardfeedsview.h"
31 #include "constants.h"
32 #include "fontutils.h"
33 #include "globalshortcuts.h"
34 #include "iconutils.h"
35 #include "searchparams.h"
37 #include "videodefinition.h"
38 #include "videosource.h"
41 #include "gnomeglobalshortcutbackend.h"
44 #include "mac_startup.h"
45 #include "macfullscreen.h"
46 #include "macsupport.h"
49 #include "downloadmanager.h"
50 #include "temporary.h"
51 #include "updatechecker.h"
52 #include "ytsuggester.h"
53 #if defined(APP_MAC_SEARCHFIELD) && !defined(APP_MAC_QMACTOOLBAR)
54 #include "searchlineedit_mac.h"
56 #include "searchlineedit.h"
58 #ifdef APP_MAC_QMACTOOLBAR
59 #include "mactoolbar.h"
64 #include "updatedialog.h"
67 #include "activation.h"
68 #include "activationview.h"
70 #include "channelaggregator.h"
72 #include "httputils.h"
73 #include "jsfunctions.h"
74 #include "seekslider.h"
75 #include "sidebarwidget.h"
76 #include "toolbarmenu.h"
77 #include "videoareawidget.h"
79 #include "ytregions.h"
82 static MainWindow *mainWindowInstance;
85 MainWindow *MainWindow::instance() {
86 return mainWindowInstance;
89 MainWindow::MainWindow()
90 : updateChecker(0), aboutView(0), downloadView(0), regionsView(0), mainToolBar(0),
92 mediaObject(0), audioOutput(0),
94 fullScreenActive(false), compactModeActive(false), initialized(false), toolbarMenu(0) {
96 mainWindowInstance = this;
99 views = new QStackedWidget();
101 setCentralWidget(views);
104 Extra::windowSetup(this);
107 messageLabel = new QLabel();
108 messageLabel->setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint);
109 messageLabel->setStyleSheet("padding:5px;border:1px solid #808080;background:palette(window)");
110 messageLabel->hide();
111 adjustMessageLabelPosition();
112 messageTimer = new QTimer(this);
113 messageTimer->setInterval(5000);
114 messageTimer->setSingleShot(true);
115 connect(messageTimer, SIGNAL(timeout()), SLOT(hideMessage()));
118 homeView = new HomeView(this);
119 views->addWidget(homeView);
121 // TODO make this lazy
122 mediaView = MediaView::instance();
123 mediaView->setEnabled(false);
124 views->addWidget(mediaView);
133 // remove that useless menu/toolbar context menu
134 this->setContextMenuPolicy(Qt::NoContextMenu);
136 // event filter to block ugly toolbar tooltips
137 qApp->installEventFilter(this);
139 setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
141 // restore window position
144 // fix stacked widget minimum size
145 for (int i = 0; i < views->count(); i++)
146 views->widget(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
151 #ifdef APP_ACTIVATION
152 Activation::instance().initialCheck();
157 QTimer::singleShot(100, this, &MainWindow::lazyInit);
160 void MainWindow::lazyInit() {
164 mediaView->initialize();
166 mediaView->setMediaObject(mediaObject);
168 qApp->processEvents();
171 if (qApp->arguments().size() > 1) {
172 QString query = qApp->arguments().at(1);
173 if (query.startsWith(QLatin1String("--"))) {
174 messageReceived(query);
177 SearchParams *searchParams = new SearchParams();
178 searchParams->setKeywords(query);
179 showMedia(searchParams);
184 GlobalShortcuts &shortcuts = GlobalShortcuts::instance();
186 if (GnomeGlobalShortcutBackend::IsGsdAvailable())
187 shortcuts.setBackend(new GnomeGlobalShortcutBackend(&shortcuts));
192 connect(&shortcuts, SIGNAL(PlayPause()), pauseAct, SLOT(trigger()));
193 connect(&shortcuts, SIGNAL(Stop()), this, SLOT(stop()));
194 connect(&shortcuts, SIGNAL(Next()), skipAct, SLOT(trigger()));
195 connect(&shortcuts, SIGNAL(Previous()), skipBackwardAct, SLOT(trigger()));
196 // connect(&shortcuts, SIGNAL(StopAfter()), getAction("stopafterthis"), SLOT(toggle()));
198 connect(DownloadManager::instance(), SIGNAL(statusMessageChanged(QString)),
199 SLOT(updateDownloadMessage(QString)));
200 connect(DownloadManager::instance(), SIGNAL(finished()), SLOT(downloadsFinished()));
202 setAcceptDrops(true);
204 fullscreenTimer = new QTimer(this);
205 fullscreenTimer->setInterval(3000);
206 fullscreenTimer->setSingleShot(true);
207 connect(fullscreenTimer, SIGNAL(timeout()), SLOT(hideFullscreenUI()));
209 JsFunctions::instance();
211 // Hack to give focus to searchlineedit
212 View *view = qobject_cast<View *>(views->currentWidget());
213 if (view == homeView) {
214 QMetaObject::invokeMethod(views->currentWidget(), "appear");
215 const QString &desc = view->getDescription();
216 if (!desc.isEmpty()) showMessage(desc);
219 ChannelAggregator::instance()->start();
226 void MainWindow::changeEvent(QEvent *e) {
228 if (e->type() == QEvent::WindowStateChange) {
229 getAction("minimize")->setEnabled(!isMinimized());
232 QMainWindow::changeEvent(e);
235 bool MainWindow::eventFilter(QObject *obj, QEvent *e) {
236 const QEvent::Type t = e->type();
239 static bool altPressed = false;
240 if (t == QEvent::KeyRelease && altPressed) {
242 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
243 if (ke->key() == Qt::Key_Alt) {
244 toggleMenuVisibility();
247 } else if (t == QEvent::KeyPress) {
248 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
249 altPressed = ke->key() == Qt::Key_Alt;
253 if (fullScreenActive && views->currentWidget() == mediaView && t == QEvent::MouseMove &&
254 obj->isWidgetType() && qobject_cast<QWidget *>(obj)->window() == this) {
255 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(e);
257 bool toolBarVisible = mainToolBar && mainToolBar->isVisible();
258 bool sidebarVisible = mediaView->isSidebarVisible();
260 if (!sidebarVisible && !toolBarVisible) {
261 const int x = mouseEvent->pos().x();
262 if (x >= 0 && x < 5) {
264 SidebarWidget *sidebar = mediaView->getSidebar();
265 sidebar->resize(sidebar->width(), height());
267 mediaView->setSidebarVisibility(true);
268 sidebarVisible = true;
273 if (!toolBarVisible && !sidebarVisible) {
274 const int y = mouseEvent->pos().y();
275 if (y >= 0 && y < 5) {
276 mainToolBar->resize(width(), mainToolBar->sizeHint().height());
277 mainToolBar->setVisible(true);
282 // show the normal cursor
284 // then hide it again after a few seconds
285 fullscreenTimer->start();
288 if (t == QEvent::ToolTip) {
293 if (t == QEvent::Show && obj == toolbarMenu) {
295 int x = width() - toolbarMenu->sizeHint().width();
298 int x = toolbarMenuButton->x() + toolbarMenuButton->width() -
299 toolbarMenu->sizeHint().width();
300 int y = toolbarMenuButton->y() + toolbarMenuButton->height();
303 toolbarMenu->move(mapToGlobal(p));
306 // standard event processing
307 return QMainWindow::eventFilter(obj, e);
310 void MainWindow::createActions() {
311 stopAct = new QAction(IconUtils::icon("media-playback-stop"), tr("&Stop"), this);
312 stopAct->setStatusTip(tr("Stop playback and go back to the search view"));
313 stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape)
314 << QKeySequence(Qt::Key_MediaStop));
315 stopAct->setEnabled(false);
316 actionMap.insert("stop", stopAct);
317 connect(stopAct, SIGNAL(triggered()), SLOT(stop()));
319 skipBackwardAct = new QAction(IconUtils::icon("media-skip-backward"), tr("P&revious"), this);
320 skipBackwardAct->setStatusTip(tr("Go back to the previous track"));
321 skipBackwardAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Left));
322 skipBackwardAct->setEnabled(false);
323 actionMap.insert("previous", skipBackwardAct);
324 connect(skipBackwardAct, SIGNAL(triggered()), mediaView, SLOT(skipBackward()));
326 skipAct = new QAction(IconUtils::icon("media-skip-forward"), tr("S&kip"), this);
327 skipAct->setStatusTip(tr("Skip to the next video"));
328 skipAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Right)
329 << QKeySequence(Qt::Key_MediaNext));
330 skipAct->setEnabled(false);
331 actionMap.insert("skip", skipAct);
332 connect(skipAct, SIGNAL(triggered()), mediaView, SLOT(skip()));
334 pauseAct = new QAction(IconUtils::icon("media-playback-start"), tr("&Play"), this);
335 pauseAct->setStatusTip(tr("Resume playback"));
336 pauseAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Space)
337 << QKeySequence(Qt::Key_MediaPlay));
338 pauseAct->setEnabled(false);
339 actionMap.insert("pause", pauseAct);
340 connect(pauseAct, SIGNAL(triggered()), mediaView, SLOT(pause()));
342 fullscreenAct = new QAction(IconUtils::icon("view-fullscreen"), tr("&Full Screen"), this);
343 fullscreenAct->setStatusTip(tr("Go full screen"));
344 QList<QKeySequence> fsShortcuts;
346 fsShortcuts << QKeySequence(Qt::CTRL + Qt::META + Qt::Key_F);
348 fsShortcuts << QKeySequence(Qt::Key_F11) << QKeySequence(Qt::ALT + Qt::Key_Return);
350 fullscreenAct->setShortcuts(fsShortcuts);
351 fullscreenAct->setShortcutContext(Qt::ApplicationShortcut);
352 fullscreenAct->setPriority(QAction::LowPriority);
353 actionMap.insert("fullscreen", fullscreenAct);
354 connect(fullscreenAct, SIGNAL(triggered()), SLOT(toggleFullscreen()));
356 compactViewAct = new QAction(tr("&Compact Mode"), this);
357 compactViewAct->setStatusTip(tr("Hide the playlist and the toolbar"));
359 compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::META + Qt::Key_C));
361 compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C));
363 compactViewAct->setCheckable(true);
364 compactViewAct->setChecked(false);
365 compactViewAct->setEnabled(false);
366 actionMap.insert("compactView", compactViewAct);
367 connect(compactViewAct, SIGNAL(toggled(bool)), this, SLOT(compactView(bool)));
369 webPageAct = new QAction(tr("Open the &YouTube Page"), this);
370 webPageAct->setStatusTip(tr("Go to the YouTube video page and pause playback"));
371 webPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Y));
372 webPageAct->setEnabled(false);
373 actionMap.insert("webpage", webPageAct);
374 connect(webPageAct, SIGNAL(triggered()), mediaView, SLOT(openWebPage()));
376 copyPageAct = new QAction(IconUtils::icon("link"), tr("Copy the YouTube &Link"), this);
377 copyPageAct->setStatusTip(tr("Copy the current video YouTube link to the clipboard"));
378 copyPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L));
379 copyPageAct->setEnabled(false);
380 actionMap.insert("pagelink", copyPageAct);
381 connect(copyPageAct, SIGNAL(triggered()), mediaView, SLOT(copyWebPage()));
383 copyLinkAct = new QAction(tr("Copy the Video Stream &URL"), this);
384 copyLinkAct->setStatusTip(tr("Copy the current video stream URL to the clipboard"));
385 copyLinkAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U));
386 copyLinkAct->setEnabled(false);
387 actionMap.insert("videolink", copyLinkAct);
388 connect(copyLinkAct, SIGNAL(triggered()), mediaView, SLOT(copyVideoLink()));
390 findVideoPartsAct = new QAction(tr("Find Video &Parts"), this);
391 findVideoPartsAct->setStatusTip(tr("Find other video parts hopefully in the right order"));
392 findVideoPartsAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P));
393 findVideoPartsAct->setEnabled(false);
394 connect(findVideoPartsAct, SIGNAL(triggered()), mediaView, SLOT(findVideoParts()));
395 actionMap.insert("findVideoParts", findVideoPartsAct);
397 removeAct = new QAction(tr("&Remove"), this);
398 removeAct->setStatusTip(tr("Remove the selected videos from the playlist"));
399 removeAct->setShortcuts(QList<QKeySequence>() << QKeySequence("Del")
400 << QKeySequence("Backspace"));
401 removeAct->setEnabled(false);
402 actionMap.insert("remove", removeAct);
403 connect(removeAct, SIGNAL(triggered()), mediaView, SLOT(removeSelected()));
405 moveUpAct = new QAction(tr("Move &Up"), this);
406 moveUpAct->setStatusTip(tr("Move up the selected videos in the playlist"));
407 moveUpAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up));
408 moveUpAct->setEnabled(false);
409 actionMap.insert("moveUp", moveUpAct);
410 connect(moveUpAct, SIGNAL(triggered()), mediaView, SLOT(moveUpSelected()));
412 moveDownAct = new QAction(tr("Move &Down"), this);
413 moveDownAct->setStatusTip(tr("Move down the selected videos in the playlist"));
414 moveDownAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down));
415 moveDownAct->setEnabled(false);
416 actionMap.insert("moveDown", moveDownAct);
417 connect(moveDownAct, SIGNAL(triggered()), mediaView, SLOT(moveDownSelected()));
419 clearAct = new QAction(tr("&Clear Recent Searches"), this);
420 clearAct->setMenuRole(QAction::ApplicationSpecificRole);
421 clearAct->setShortcuts(QList<QKeySequence>()
422 << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Delete)
423 << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Backspace));
424 clearAct->setStatusTip(tr("Clear the search history. Cannot be undone."));
425 clearAct->setEnabled(true);
426 actionMap.insert("clearRecentKeywords", clearAct);
427 connect(clearAct, SIGNAL(triggered()), SLOT(clearRecentKeywords()));
429 quitAct = new QAction(tr("&Quit"), this);
430 quitAct->setMenuRole(QAction::QuitRole);
431 quitAct->setShortcut(QKeySequence(QKeySequence::Quit));
432 quitAct->setStatusTip(tr("Bye"));
433 actionMap.insert("quit", quitAct);
434 connect(quitAct, SIGNAL(triggered()), SLOT(quit()));
436 siteAct = new QAction(tr("&Website"), this);
437 siteAct->setShortcut(QKeySequence::HelpContents);
438 siteAct->setStatusTip(tr("%1 on the Web").arg(Constants::NAME));
439 actionMap.insert("site", siteAct);
440 connect(siteAct, SIGNAL(triggered()), this, SLOT(visitSite()));
442 #if !defined(APP_MAC) && !defined(APP_WIN)
443 donateAct = new QAction(tr("Make a &Donation"), this);
444 donateAct->setStatusTip(
445 tr("Please support the continued development of %1").arg(Constants::NAME));
446 actionMap.insert("donate", donateAct);
447 connect(donateAct, SIGNAL(triggered()), this, SLOT(donate()));
450 aboutAct = new QAction(tr("&About"), this);
451 aboutAct->setMenuRole(QAction::AboutRole);
452 aboutAct->setStatusTip(tr("Info about %1").arg(Constants::NAME));
453 actionMap.insert("about", aboutAct);
454 connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
458 searchFocusAct = new QAction(this);
459 searchFocusAct->setShortcut(QKeySequence::Find);
460 searchFocusAct->setStatusTip(tr("Search"));
461 actionMap.insert("search", searchFocusAct);
462 connect(searchFocusAct, SIGNAL(triggered()), this, SLOT(searchFocus()));
463 addAction(searchFocusAct);
465 volumeUpAct = new QAction(this);
466 volumeUpAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Plus));
467 actionMap.insert("volumeUp", volumeUpAct);
468 connect(volumeUpAct, SIGNAL(triggered()), this, SLOT(volumeUp()));
469 addAction(volumeUpAct);
471 volumeDownAct = new QAction(this);
472 volumeDownAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Minus));
473 actionMap.insert("volumeDown", volumeDownAct);
474 connect(volumeDownAct, SIGNAL(triggered()), this, SLOT(volumeDown()));
475 addAction(volumeDownAct);
477 volumeMuteAct = new QAction(this);
478 volumeMuteAct->setIcon(IconUtils::icon("audio-volume-high"));
479 volumeMuteAct->setStatusTip(tr("Mute volume"));
480 volumeMuteAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K));
481 actionMap.insert("volumeMute", volumeMuteAct);
482 connect(volumeMuteAct, SIGNAL(triggered()), SLOT(volumeMute()));
483 addAction(volumeMuteAct);
485 QAction *definitionAct = new QAction(this);
486 definitionAct->setIcon(IconUtils::icon("video-display"));
487 definitionAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_D));
488 actionMap.insert("definition", definitionAct);
489 connect(definitionAct, SIGNAL(triggered()), SLOT(toggleDefinitionMode()));
490 addAction(definitionAct);
494 action = new QAction(IconUtils::icon("media-playback-start"), tr("&Manually Start Playing"),
496 action->setStatusTip(tr("Manually start playing videos"));
497 action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_T));
498 action->setCheckable(true);
499 connect(action, SIGNAL(toggled(bool)), SLOT(setManualPlay(bool)));
500 actionMap.insert("manualplay", action);
502 action = new QAction(tr("&Downloads"), this);
503 action->setStatusTip(tr("Show details about video downloads"));
504 action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_J));
505 action->setCheckable(true);
506 action->setIcon(IconUtils::icon("document-save"));
507 connect(action, SIGNAL(toggled(bool)), SLOT(toggleDownloads(bool)));
508 actionMap.insert("downloads", action);
510 action = new QAction(tr("&Download"), this);
511 action->setStatusTip(tr("Download the current video"));
512 action->setShortcut(QKeySequence::Save);
513 action->setIcon(IconUtils::icon("document-save"));
514 action->setEnabled(false);
515 action->setVisible(false);
516 action->setPriority(QAction::LowPriority);
517 connect(action, SIGNAL(triggered()), mediaView, SLOT(downloadVideo()));
518 actionMap.insert("download", action);
521 action = new QAction(tr("Take &Snapshot"), this);
522 action->setShortcut(QKeySequence(Qt::Key_F9));
523 action->setEnabled(false);
524 actionMap.insert("snapshot", action);
525 connect(action, SIGNAL(triggered()), mediaView, SLOT(snapshot()));
528 action = new QAction(tr("&Subscribe to Channel"), this);
529 action->setProperty("originalText", action->text());
530 action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_S));
531 action->setEnabled(false);
532 connect(action, SIGNAL(triggered()), mediaView, SLOT(toggleSubscription()));
533 actionMap.insert("subscribeChannel", action);
534 mediaView->updateSubscriptionAction(0, false);
536 QString shareTip = tr("Share the current video using %1");
538 action = new QAction(IconUtils::icon("twitter"), "&Twitter", this);
539 action->setStatusTip(shareTip.arg("Twitter"));
540 action->setEnabled(false);
541 actionMap.insert("twitter", action);
542 connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaTwitter()));
544 action = new QAction(IconUtils::icon("facebook"), "&Facebook", this);
545 action->setStatusTip(shareTip.arg("Facebook"));
546 action->setEnabled(false);
547 actionMap.insert("facebook", action);
548 connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaFacebook()));
550 action = new QAction(IconUtils::icon("email"), tr("&Email"), this);
551 action->setStatusTip(shareTip.arg(tr("Email")));
552 action->setEnabled(false);
553 actionMap.insert("email", action);
554 connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaEmail()));
556 action = new QAction(tr("&Close"), this);
557 action->setShortcut(QKeySequence(QKeySequence::Close));
558 actionMap.insert("close", action);
559 connect(action, SIGNAL(triggered()), SLOT(close()));
561 action = new QAction(Constants::NAME, this);
562 action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_1));
563 actionMap.insert("restore", action);
564 connect(action, SIGNAL(triggered()), SLOT(restore()));
566 action = new QAction(IconUtils::icon("go-top"), tr("&Float on Top"), this);
567 action->setCheckable(true);
568 actionMap.insert("ontop", action);
569 connect(action, SIGNAL(toggled(bool)), SLOT(floatOnTop(bool)));
572 new QAction(IconUtils::icon("media-playback-stop"), tr("&Stop After This Video"), this);
573 action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Escape));
574 action->setCheckable(true);
575 action->setEnabled(false);
576 actionMap.insert("stopafterthis", action);
577 connect(action, SIGNAL(toggled(bool)), SLOT(showStopAfterThisInStatusBar(bool)));
579 action = new QAction(tr("&Report an Issue..."), this);
580 actionMap.insert("reportIssue", action);
581 connect(action, SIGNAL(triggered()), SLOT(reportIssue()));
583 action = new QAction(tr("&Refine Search..."), this);
584 action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E));
585 action->setCheckable(true);
586 action->setEnabled(false);
587 actionMap.insert("refineSearch", action);
589 action = new QAction(YTRegions::worldwideRegion().name, this);
590 actionMap.insert("worldwideRegion", action);
592 action = new QAction(YTRegions::localRegion().name, this);
593 actionMap.insert("localRegion", action);
595 action = new QAction(tr("More..."), this);
596 actionMap.insert("moreRegion", action);
598 action = new QAction(IconUtils::icon("view-list"), tr("&Related Videos"), this);
599 action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
600 action->setStatusTip(tr("Watch videos related to the current one"));
601 action->setEnabled(false);
602 action->setPriority(QAction::LowPriority);
603 connect(action, SIGNAL(triggered()), mediaView, SLOT(relatedVideos()));
604 actionMap.insert("relatedVideos", action);
606 action = new QAction(tr("Open in &Browser..."), this);
607 action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_B));
608 action->setEnabled(false);
609 actionMap.insert("openInBrowser", action);
610 connect(action, SIGNAL(triggered()), mediaView, SLOT(openInBrowser()));
612 action = new QAction(IconUtils::icon("safesearch"), tr("Restricted Mode"), this);
613 action->setStatusTip(tr("Hide videos that may contain inappropriate content"));
614 action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K));
615 action->setCheckable(true);
616 actionMap.insert("safeSearch", action);
618 action = new QAction(tr("Toggle &Menu Bar"), this);
619 connect(action, SIGNAL(triggered()), SLOT(toggleMenuVisibilityWithMessage()));
620 actionMap.insert("toggleMenu", action);
622 action = new QAction(IconUtils::icon("view-more"), tr("Menu"), this);
623 connect(action, SIGNAL(triggered()), SLOT(toggleToolbarMenu()));
624 actionMap.insert("toolbarMenu", action);
627 action = new QAction(tr("&Love %1? Rate it!").arg(Constants::NAME), this);
628 actionMap.insert("appStore", action);
629 connect(action, SIGNAL(triggered()), SLOT(rateOnAppStore()));
632 #ifdef APP_ACTIVATION
633 ActivationView::createActivationAction(tr("Buy %1...").arg(Constants::NAME));
636 // common action properties
637 for (QAction *action : actionMap) {
638 // add actions to the MainWindow so that they work
639 // when the menu is hidden
641 IconUtils::setupAction(action);
645 void MainWindow::createMenus() {
646 fileMenu = menuBar()->addMenu(tr("&Application"));
647 #ifdef APP_ACTIVATION
648 QAction *buyAction = getAction("buy");
649 if (buyAction) fileMenu->addAction(buyAction);
651 fileMenu->addSeparator();
654 fileMenu->addAction(clearAct);
656 fileMenu->addSeparator();
658 fileMenu->addAction(quitAct);
660 QMenu *playbackMenu = menuBar()->addMenu(tr("&Playback"));
661 menuMap.insert("playback", playbackMenu);
662 playbackMenu->addAction(pauseAct);
663 playbackMenu->addAction(stopAct);
664 playbackMenu->addAction(getAction("stopafterthis"));
665 playbackMenu->addSeparator();
666 playbackMenu->addAction(skipAct);
667 playbackMenu->addAction(skipBackwardAct);
668 playbackMenu->addSeparator();
669 playbackMenu->addAction(getAction("manualplay"));
671 MacSupport::dockMenu(playbackMenu);
674 playlistMenu = menuBar()->addMenu(tr("&Playlist"));
675 menuMap.insert("playlist", playlistMenu);
676 playlistMenu->addAction(removeAct);
677 playlistMenu->addSeparator();
678 playlistMenu->addAction(moveUpAct);
679 playlistMenu->addAction(moveDownAct);
680 playlistMenu->addSeparator();
681 playlistMenu->addAction(getAction("refineSearch"));
683 QMenu *videoMenu = menuBar()->addMenu(tr("&Video"));
684 menuMap.insert("video", videoMenu);
685 videoMenu->addAction(getAction("relatedVideos"));
686 videoMenu->addAction(findVideoPartsAct);
687 videoMenu->addSeparator();
688 videoMenu->addAction(getAction("subscribeChannel"));
690 videoMenu->addSeparator();
691 videoMenu->addAction(getAction("snapshot"));
693 videoMenu->addSeparator();
694 videoMenu->addAction(webPageAct);
695 videoMenu->addAction(copyLinkAct);
696 videoMenu->addAction(getAction("openInBrowser"));
697 videoMenu->addAction(getAction("download"));
699 QMenu *shareMenu = menuBar()->addMenu(tr("&Share"));
700 menuMap.insert("share", shareMenu);
701 shareMenu->addAction(copyPageAct);
702 shareMenu->addSeparator();
703 shareMenu->addAction(getAction("twitter"));
704 shareMenu->addAction(getAction("facebook"));
705 shareMenu->addSeparator();
706 shareMenu->addAction(getAction("email"));
708 QMenu *viewMenu = menuBar()->addMenu(tr("&View"));
709 menuMap.insert("view", viewMenu);
710 viewMenu->addAction(getAction("ontop"));
711 viewMenu->addAction(compactViewAct);
713 viewMenu->addAction(fullscreenAct);
714 viewMenu->addAction(getAction("toggleMenu"));
718 MacSupport::windowMenu(this);
721 helpMenu = menuBar()->addMenu(tr("&Help"));
722 menuMap.insert("help", helpMenu);
723 helpMenu->addAction(siteAct);
724 #if !defined(APP_MAC) && !defined(APP_WIN)
725 helpMenu->addAction(donateAct);
727 helpMenu->addAction(getAction("reportIssue"));
728 helpMenu->addAction(aboutAct);
731 helpMenu->addSeparator();
732 helpMenu->addAction(getAction("appStore"));
736 void MainWindow::createToolBars() {
739 currentTimeLabel = new QLabel("00:00");
740 currentTimeLabel->setFont(FontUtils::small());
742 #ifdef APP_PHONON_SEEK
743 seekSlider = new Phonon::SeekSlider();
745 seekSlider->setTracking(false);
747 seekSlider->setTracking(true);
749 // Phonon freezes the application with streaming videos if
750 // tracking is set to true and the seek slider is dragged.
751 seekSlider->setIconVisible(false);
752 seekSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
754 slider = new SeekSlider(this);
755 slider->setEnabled(false);
756 slider->setTracking(false);
757 slider->setMaximum(1000);
758 slider->setOrientation(Qt::Horizontal);
759 slider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
763 volumeSlider = new Phonon::VolumeSlider();
764 volumeSlider->setMuteVisible(false);
765 // qDebug() << volumeSlider->children();
766 // status tip for the volume slider
767 QSlider *volumeQSlider = volumeSlider->findChild<QSlider *>();
769 volumeQSlider->setStatusTip(
770 tr("Press %1 to raise the volume, %2 to lower it")
771 .arg(volumeUpAct->shortcut().toString(QKeySequence::NativeText),
772 volumeDownAct->shortcut().toString(QKeySequence::NativeText)));
773 // this makes the volume slider smaller
774 volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
777 #if defined(APP_MAC_SEARCHFIELD) && !defined(APP_MAC_QMACTOOLBAR)
778 SearchWrapper *searchWrapper = new SearchWrapper(this);
779 toolbarSearch = searchWrapper->getSearchLineEdit();
781 toolbarSearch = new SearchLineEdit(this);
783 toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize() * 15);
784 toolbarSearch->setSuggester(new YTSuggester(this));
785 connect(toolbarSearch, SIGNAL(search(const QString &)), SLOT(search(const QString &)));
786 connect(toolbarSearch, SIGNAL(suggestionAccepted(Suggestion *)),
787 SLOT(suggestionAccepted(Suggestion *)));
788 toolbarSearch->setStatusTip(searchFocusAct->statusTip());
790 // Add widgets to toolbar
792 #ifdef APP_MAC_QMACTOOLBAR
793 currentTimeLabel->hide();
794 toolbarSearch->hide();
795 volumeSlider->hide();
797 MacToolbar::instance().createToolbar(this);
801 mainToolBar = new QToolBar(this);
802 mainToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
803 mainToolBar->setFloatable(false);
804 mainToolBar->setMovable(false);
806 mainToolBar->setIconSize(QSize(32, 32));
808 mainToolBar->addAction(stopAct);
809 QToolButton *stopToolButton =
810 qobject_cast<QToolButton *>(mainToolBar->widgetForAction(stopAct));
811 if (stopToolButton) {
812 QMenu *stopMenu = new QMenu(this);
813 stopMenu->addAction(getAction("stopafterthis"));
814 stopToolButton->setMenu(stopMenu);
815 stopToolButton->setPopupMode(QToolButton::DelayedPopup);
817 mainToolBar->addAction(pauseAct);
818 mainToolBar->addAction(skipAct);
819 mainToolBar->addAction(getAction("relatedVideos"));
821 bool addFullScreenAct = true;
823 addFullScreenAct = !mac::CanGoFullScreen(winId());
825 if (addFullScreenAct) mainToolBar->addAction(fullscreenAct);
827 mainToolBar->addWidget(new Spacer());
829 mainToolBar->addWidget(currentTimeLabel);
830 mainToolBar->addWidget(new Spacer(this, currentTimeLabel->sizeHint().height() / 2));
831 #ifdef APP_PHONON_SEEK
832 mainToolBar->addWidget(seekSlider);
834 mainToolBar->addWidget(slider);
838 mainToolBar->addWidget(new Spacer());
839 totalTime = new QLabel(mainToolBar);
840 totalTime->setFont(smallerFont);
841 mainToolBar->addWidget(totalTime);
844 mainToolBar->addWidget(new Spacer());
845 mainToolBar->addAction(volumeMuteAct);
847 QToolButton *volumeMuteButton =
848 qobject_cast<QToolButton *>(mainToolBar->widgetForAction(volumeMuteAct));
849 volumeMuteButton->setIcon(volumeMuteButton->icon().pixmap(16));
853 mainToolBar->addWidget(volumeSlider);
856 mainToolBar->addWidget(new Spacer());
858 #if defined(APP_MAC_SEARCHFIELD) && !defined(APP_MAC_QMACTOOLBAR)
859 mainToolBar->addWidget(searchWrapper);
861 mainToolBar->addWidget(toolbarSearch);
862 mainToolBar->addWidget(new Spacer(this, toolbarSearch->height() / 2));
864 QAction *toolbarMenuAction = getAction("toolbarMenu");
865 mainToolBar->addAction(toolbarMenuAction);
867 qobject_cast<QToolButton *>(mainToolBar->widgetForAction(toolbarMenuAction));
870 addToolBar(mainToolBar);
873 void MainWindow::createStatusBar() {
874 statusToolBar = new QToolBar(statusBar());
875 statusToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
876 statusToolBar->setIconSize(QSize(16, 16));
878 regionAction = new QAction(this);
879 regionAction->setStatusTip(tr("Choose your content location"));
881 QAction *localAction = getAction("localRegion");
882 if (!localAction->text().isEmpty()) {
883 QMenu *regionMenu = new QMenu(this);
884 regionMenu->addAction(getAction("worldwideRegion"));
885 regionMenu->addAction(localAction);
886 regionMenu->addSeparator();
887 QAction *moreRegionsAction = getAction("moreRegion");
888 regionMenu->addAction(moreRegionsAction);
889 connect(moreRegionsAction, SIGNAL(triggered()), SLOT(showRegionsView()));
890 regionAction->setMenu(regionMenu);
892 connect(regionAction, SIGNAL(triggered()), SLOT(showRegionsView()));
895 /* Stupid code that generates the QRC items
896 foreach(YTRegion r, YTRegions::list())
897 qDebug() << QString("<file>flags/%1.png</file>").arg(r.id.toLower());
900 statusBar()->addPermanentWidget(statusToolBar);
904 void MainWindow::showStopAfterThisInStatusBar(bool show) {
905 QAction *action = getAction("stopafterthis");
906 showActionInStatusBar(action, show);
909 void MainWindow::showActionInStatusBar(QAction *action, bool show) {
911 Extra::fadeInWidget(statusBar(), statusBar());
914 if (statusToolBar->actions().contains(action)) return;
915 if (statusToolBar->actions().isEmpty()) {
916 statusToolBar->addAction(action);
918 statusToolBar->insertAction(statusToolBar->actions().at(0), action);
920 if (statusBar()->isHidden() && !fullScreenActive) setStatusBarVisibility(true);
922 statusToolBar->removeAction(action);
923 if (statusBar()->isVisible() && !needStatusBar()) setStatusBarVisibility(false);
927 void MainWindow::setStatusBarVisibility(bool show) {
928 statusBar()->setVisible(show);
929 if (views->currentWidget() == mediaView)
930 QTimer::singleShot(0, mediaView, SLOT(adjustWindowSize()));
933 void MainWindow::adjustStatusBarVisibility() {
934 setStatusBarVisibility(needStatusBar());
937 void MainWindow::hideToolbar() {
939 mac::showToolBar(winId(), false);
945 void MainWindow::showToolbar() {
947 mac::showToolBar(winId(), true);
953 void MainWindow::readSettings() {
955 if (settings.contains("geometry")) {
956 restoreGeometry(settings.value("geometry").toByteArray());
958 const QRect desktopSize = qApp->desktop()->availableGeometry();
959 int w = desktopSize.width() * .9;
960 int h = qMin(w / 2, desktopSize.height());
962 QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, QSize(w, h), desktopSize));
964 const VideoDefinition &firstDefinition = VideoDefinition::getDefinitions().at(0);
965 setDefinitionMode(settings.value("definition", firstDefinition.getName()).toString());
966 getAction("manualplay")->setChecked(settings.value("manualplay", false).toBool());
967 getAction("safeSearch")->setChecked(settings.value("safeSearch", false).toBool());
969 menuBar()->setVisible(settings.value("menuBar", false).toBool());
973 void MainWindow::writeSettings() {
976 if (!isReallyFullScreen()) {
977 settings.setValue("geometry", saveGeometry());
978 mediaView->saveSplitterState();
981 settings.setValue("manualplay", getAction("manualplay")->isChecked());
982 settings.setValue("safeSearch", getAction("safeSearch")->isChecked());
984 settings.setValue("menuBar", menuBar()->isVisible());
988 void MainWindow::goBack() {
989 if (history.size() > 1) {
991 QWidget *widget = history.pop();
996 void MainWindow::showWidget(QWidget *widget, bool transition) {
997 Q_UNUSED(transition);
999 setUpdatesEnabled(false);
1001 if (compactViewAct->isChecked()) compactViewAct->toggle();
1003 // call hide method on the current view
1004 View *oldView = qobject_cast<View *>(views->currentWidget());
1006 oldView->disappear();
1007 views->currentWidget()->setEnabled(false);
1009 qDebug() << "Cannot cast view";
1011 const bool isMediaView = widget == mediaView;
1013 stopAct->setEnabled(isMediaView);
1014 compactViewAct->setEnabled(isMediaView);
1015 toolbarSearch->setEnabled(widget == homeView || isMediaView || widget == downloadView);
1017 aboutAct->setEnabled(widget != aboutView);
1018 getAction("downloads")->setChecked(widget == downloadView);
1020 QWidget *oldWidget = views->currentWidget();
1021 if (oldWidget) oldWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
1023 views->setCurrentWidget(widget);
1024 widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
1026 // call show method on the new view
1027 View *newView = qobject_cast<View *>(widget);
1029 widget->setEnabled(true);
1031 QString title = newView->getTitle();
1032 if (title.isEmpty())
1033 title = Constants::NAME;
1035 title += QLatin1String(" - ") + Constants::NAME;
1036 setWindowTitle(title);
1038 statusToolBar->setUpdatesEnabled(false);
1040 // dynamic view actions
1041 /* Not currently used by any view
1042 foreach (QAction* action, viewActions)
1043 showActionInStatusBar(action, false);
1044 viewActions = newView->getViewActions();
1045 foreach (QAction* action, viewActions)
1046 showActionInStatusBar(action, true);
1049 adjustStatusBarVisibility();
1050 messageLabel->hide();
1054 statusToolBar->setUpdatesEnabled(true);
1057 QString desc = metadata.value("description").toString();
1058 if (!desc.isEmpty()) showMessage(desc);
1062 setUpdatesEnabled(true);
1065 // Workaround cursor bug on macOS
1067 mac::uncloseWindow(winId());
1070 history.push(widget);
1074 void MainWindow::about() {
1076 aboutView = new AboutView(this);
1077 views->addWidget(aboutView);
1079 showWidget(aboutView);
1082 void MainWindow::visitSite() {
1083 QUrl url(Constants::WEBSITE);
1084 showMessage(QString(tr("Opening %1").arg(url.toString())));
1085 QDesktopServices::openUrl(url);
1088 void MainWindow::donate() {
1089 QUrl url(QString(Constants::WEBSITE) + "#donate");
1090 showMessage(QString(tr("Opening %1").arg(url.toString())));
1091 QDesktopServices::openUrl(url);
1094 void MainWindow::reportIssue() {
1095 QUrl url("http://flavio.tordini.org/forums/forum/minitube-forums/minitube-troubleshooting");
1096 QDesktopServices::openUrl(url);
1099 void MainWindow::quit() {
1101 if (!confirmQuit()) {
1105 // do not save geometry when in full screen or in compact mode
1106 if (!fullScreenActive && !compactViewAct->isChecked()) {
1109 // mediaView->stop();
1110 Temporary::deleteAll();
1111 ChannelAggregator::instance()->stop();
1112 ChannelAggregator::instance()->cleanup();
1113 Database::shutdown();
1117 void MainWindow::closeEvent(QCloseEvent *e) {
1119 mac::closeWindow(winId());
1122 if (!confirmQuit()) {
1126 QWidget::closeEvent(e);
1129 messageLabel->hide();
1132 void MainWindow::showEvent(QShowEvent *e) {
1133 QWidget::showEvent(e);
1139 bool MainWindow::confirmQuit() {
1140 if (DownloadManager::instance()->activeItems() > 0) {
1141 QMessageBox msgBox(this);
1142 msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png"));
1144 tr("Do you want to exit %1 with a download in progress?").arg(Constants::NAME));
1145 msgBox.setInformativeText(
1146 tr("If you close %1 now, this download will be cancelled.").arg(Constants::NAME));
1147 msgBox.setModal(true);
1148 // make it a "sheet" on the Mac
1149 msgBox.setWindowModality(Qt::WindowModal);
1151 msgBox.addButton(tr("Close and cancel download"), QMessageBox::RejectRole);
1152 QPushButton *waitButton =
1153 msgBox.addButton(tr("Wait for download to finish"), QMessageBox::ActionRole);
1157 if (msgBox.clickedButton() == waitButton) {
1164 void MainWindow::showHome() {
1165 showWidget(homeView);
1166 currentTimeLabel->clear();
1169 void MainWindow::showMedia(SearchParams *searchParams) {
1170 showWidget(mediaView);
1171 if (getAction("safeSearch")->isChecked())
1172 searchParams->setSafeSearch(SearchParams::Strict);
1174 searchParams->setSafeSearch(SearchParams::None);
1175 mediaView->search(searchParams);
1178 void MainWindow::showMedia(VideoSource *videoSource) {
1179 showWidget(mediaView);
1180 mediaView->setVideoSource(videoSource);
1184 void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState */) {
1185 // qDebug() << "Phonon state: " << newState;
1188 case Phonon::ErrorState:
1189 if (mediaObject->errorType() == Phonon::FatalError) {
1190 // Do not display because we try to play incomplete video files and sometimes trigger
1192 // We retry automatically (in MediaView) so no need to show it
1193 // showMessage(tr("Fatal error: %1").arg(mediaObject->errorString()));
1195 showMessage(tr("Error: %1").arg(mediaObject->errorString()));
1199 case Phonon::PlayingState:
1200 pauseAct->setEnabled(true);
1201 pauseAct->setIcon(IconUtils::icon("media-playback-pause"));
1202 pauseAct->setText(tr("&Pause"));
1203 pauseAct->setStatusTip(tr("Pause playback") + " (" +
1204 pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
1205 // stopAct->setEnabled(true);
1208 case Phonon::StoppedState:
1209 pauseAct->setEnabled(false);
1210 pauseAct->setIcon(IconUtils::icon("media-playback-start"));
1211 pauseAct->setText(tr("&Play"));
1212 pauseAct->setStatusTip(tr("Resume playback") + " (" +
1213 pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
1214 // stopAct->setEnabled(false);
1217 case Phonon::PausedState:
1218 pauseAct->setEnabled(true);
1219 pauseAct->setIcon(IconUtils::icon("media-playback-start"));
1220 pauseAct->setText(tr("&Play"));
1221 pauseAct->setStatusTip(tr("Resume playback") + " (" +
1222 pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
1223 // stopAct->setEnabled(true);
1226 case Phonon::BufferingState:
1227 pauseAct->setEnabled(false);
1228 pauseAct->setIcon(IconUtils::icon("content-loading"));
1229 pauseAct->setText(tr("&Loading..."));
1230 pauseAct->setStatusTip(QString());
1233 case Phonon::LoadingState:
1234 pauseAct->setEnabled(false);
1235 currentTimeLabel->clear();
1236 // totalTime->clear();
1237 // stopAct->setEnabled(true);
1245 void MainWindow::stop() {
1250 void MainWindow::resizeEvent(QResizeEvent *e) {
1253 if (initialized && mac::CanGoFullScreen(winId())) {
1254 bool isFullscreen = mac::IsFullScreen(winId());
1255 if (isFullscreen != fullScreenActive) {
1256 if (compactViewAct->isChecked()) {
1257 compactViewAct->setChecked(false);
1260 fullScreenActive = isFullscreen;
1261 updateUIForFullscreen();
1265 #ifdef APP_MAC_QMACTOOLBAR
1266 int moreButtonWidth = 40;
1267 toolbarSearch->move(width() - toolbarSearch->width() - moreButtonWidth - 7, -38);
1272 void MainWindow::moveEvent(QMoveEvent *e) {
1277 void MainWindow::enterEvent(QEvent *e) {
1280 // Workaround cursor bug on macOS
1285 void MainWindow::leaveEvent(QEvent *e) {
1287 if (fullScreenActive) hideFullscreenUI();
1290 void MainWindow::toggleFullscreen() {
1291 if (compactViewAct->isChecked()) compactViewAct->toggle();
1294 WId handle = winId();
1295 if (mac::CanGoFullScreen(handle)) {
1296 if (mainToolBar) mainToolBar->setVisible(true);
1297 mac::ToggleFullScreen(handle);
1302 fullScreenActive = !fullScreenActive;
1304 if (fullScreenActive) {
1305 // Enter full screen
1307 maximizedBeforeFullScreen = isMaximized();
1309 // save geometry now, if the user quits when in full screen
1310 // geometry won't be saved
1314 MacSupport::enterFullScreen(this, views);
1316 menuVisibleBeforeFullScreen = menuBar()->isVisible();
1318 if (mainToolBar) mainToolBar->hide();
1326 MacSupport::exitFullScreen(this, views);
1328 menuBar()->setVisible(menuVisibleBeforeFullScreen);
1329 if (mainToolBar) mainToolBar->setVisible(views->currentWidget() == mediaView);
1330 if (maximizedBeforeFullScreen)
1336 // Make sure the window has focus
1340 qApp->processEvents();
1341 updateUIForFullscreen();
1344 void MainWindow::updateUIForFullscreen() {
1345 static QList<QKeySequence> fsShortcuts;
1346 static QString fsText;
1348 if (fullScreenActive) {
1349 fsShortcuts = fullscreenAct->shortcuts();
1350 fsText = fullscreenAct->text();
1351 if (fsText.isEmpty()) qDebug() << "[taking Empty!]";
1352 fullscreenAct->setShortcuts(QList<QKeySequence>(fsShortcuts)
1353 << QKeySequence(Qt::Key_Escape));
1354 fullscreenAct->setText(tr("Leave &Full Screen"));
1355 fullscreenAct->setIcon(IconUtils::icon("view-restore"));
1356 setStatusBarVisibility(false);
1359 removeToolBar(mainToolBar);
1360 mainToolBar->move(0, 0);
1363 mediaView->removeSidebar();
1366 fullscreenAct->setShortcuts(fsShortcuts);
1367 if (fsText.isEmpty()) fsText = "[Empty!]";
1368 fullscreenAct->setText(fsText);
1369 fullscreenAct->setIcon(IconUtils::icon("view-fullscreen"));
1371 if (needStatusBar()) setStatusBarVisibility(true);
1374 addToolBar(mainToolBar);
1377 mediaView->restoreSidebar();
1380 // No compact view action when in full screen
1381 compactViewAct->setVisible(!fullScreenActive);
1382 compactViewAct->setChecked(false);
1384 // Hide anything but the video
1385 mediaView->setSidebarVisibility(!fullScreenActive);
1387 if (fullScreenActive) {
1388 stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
1390 stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape)
1391 << QKeySequence(Qt::Key_MediaStop));
1395 MacSupport::fullScreenActions(actionMap, fullScreenActive);
1398 if (views->currentWidget() == mediaView) mediaView->setFocus();
1400 if (fullScreenActive) {
1401 if (views->currentWidget() == mediaView) hideFullscreenUI();
1403 fullscreenTimer->stop();
1408 bool MainWindow::isReallyFullScreen() {
1410 WId handle = winId();
1411 if (mac::CanGoFullScreen(handle))
1412 return mac::IsFullScreen(handle);
1414 return isFullScreen();
1416 return isFullScreen();
1420 void MainWindow::missingKeyWarning() {
1421 QMessageBox msgBox(this);
1422 msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png"));
1423 msgBox.setText(QString("%1 was built without a Google API key.").arg(Constants::NAME));
1424 msgBox.setInformativeText(QString("It won't work unless you enter one."
1425 "<p>In alternative you can get %1 from the developer site.")
1426 .arg(Constants::NAME));
1427 msgBox.setModal(true);
1428 msgBox.setWindowModality(Qt::WindowModal);
1429 QPushButton *enterKeyButton =
1430 msgBox.addButton(QString("Enter API key..."), QMessageBox::AcceptRole);
1431 QPushButton *devButton = msgBox.addButton(QString("Get from %1").arg(Constants::WEBSITE),
1432 QMessageBox::AcceptRole);
1433 QPushButton *helpButton = msgBox.addButton(QMessageBox::Help);
1435 if (msgBox.clickedButton() == helpButton) {
1436 QDesktopServices::openUrl(QUrl(
1437 "https://github.com/flaviotordini/minitube/blob/master/README.md#google-api-key"));
1438 } else if (msgBox.clickedButton() == enterKeyButton) {
1440 QString text = QInputDialog::getText(this, QString(), "Google API key:", QLineEdit::Normal,
1442 if (ok && !text.isEmpty()) {
1444 settings.setValue("googleApiKey", text);
1445 YT3::instance().initApiKeys();
1447 } else if (msgBox.clickedButton() == devButton) {
1448 QDesktopServices::openUrl(QUrl(Constants::WEBSITE));
1452 void MainWindow::compactView(bool enable) {
1453 compactModeActive = enable;
1455 static QList<QKeySequence> compactShortcuts;
1456 static QList<QKeySequence> stopShortcuts;
1458 const QString key = "compactGeometry";
1462 setMinimumSize(320, 180);
1464 mac::RemoveFullScreenWindow(winId());
1468 if (settings.contains(key))
1469 restoreGeometry(settings.value(key).toByteArray());
1473 #ifdef APP_MAC_QMACTOOLBAR
1474 mac::showToolBar(winId(), !enable);
1476 mainToolBar->setVisible(!enable);
1478 mediaView->setSidebarVisibility(!enable);
1479 statusBar()->hide();
1481 compactShortcuts = compactViewAct->shortcuts();
1482 stopShortcuts = stopAct->shortcuts();
1484 QList<QKeySequence> newStopShortcuts(stopShortcuts);
1485 newStopShortcuts.removeAll(QKeySequence(Qt::Key_Escape));
1486 stopAct->setShortcuts(newStopShortcuts);
1487 compactViewAct->setShortcuts(QList<QKeySequence>(compactShortcuts)
1488 << QKeySequence(Qt::Key_Escape));
1490 // ensure focus does not end up to the search box
1491 // as it would steal the Space shortcut
1492 mediaView->setFocus();
1495 // unset minimum size
1496 setMinimumSize(0, 0);
1498 mac::SetupFullScreenWindow(winId());
1500 settings.setValue(key, saveGeometry());
1501 #ifdef APP_MAC_QMACTOOLBAR
1502 mac::showToolBar(winId(), !enable);
1504 mainToolBar->setVisible(!enable);
1506 mediaView->setSidebarVisibility(!enable);
1507 if (needStatusBar()) setStatusBarVisibility(true);
1511 compactViewAct->setShortcuts(compactShortcuts);
1512 stopAct->setShortcuts(stopShortcuts);
1515 // auto float on top
1516 floatOnTop(enable, false);
1519 mac::compactMode(winId(), enable);
1522 menuVisibleBeforeCompactMode = menuBar()->isVisible();
1525 menuBar()->setVisible(menuVisibleBeforeCompactMode);
1530 void MainWindow::toggleToolbarMenu() {
1531 if (!toolbarMenu) toolbarMenu = new ToolbarMenu(this);
1532 if (toolbarMenu->isVisible())
1533 toolbarMenu->hide();
1535 toolbarMenu->show();
1538 void MainWindow::searchFocus() {
1539 toolbarSearch->selectAll();
1540 toolbarSearch->setFocus();
1544 void MainWindow::initPhonon() {
1545 // Phonon initialization
1546 if (mediaObject) delete mediaObject;
1547 if (audioOutput) delete audioOutput;
1548 mediaObject = new Phonon::MediaObject(this);
1549 mediaObject->setTickInterval(100);
1550 connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
1551 SLOT(stateChanged(Phonon::State, Phonon::State)));
1552 connect(mediaObject, SIGNAL(tick(qint64)), SLOT(tick(qint64)));
1553 connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), SLOT(totalTimeChanged(qint64)));
1555 audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this);
1556 connect(audioOutput, SIGNAL(mutedChanged(bool)), SLOT(volumeMutedChanged(bool)));
1557 Phonon::createPath(mediaObject, audioOutput);
1558 volumeSlider->setAudioOutput(audioOutput);
1560 #ifdef APP_PHONON_SEEK
1561 seekSlider->setMediaObject(mediaObject);
1565 audioOutput->setVolume(settings.value("volume", 1.).toReal());
1566 // audioOutput->setMuted(settings.value("volumeMute").toBool());
1568 mediaObject->stop();
1572 void MainWindow::tick(qint64 time) {
1573 const QString s = formatTime(time);
1574 if (s != currentTimeLabel->text()) {
1575 currentTimeLabel->setText(s);
1576 emit currentTimeChanged(s);
1581 const qint64 remainingTime = mediaObject->remainingTime();
1582 currentTimeLabel->setStatusTip(tr("Remaining time: %1").arg(formatTime(remainingTime)));
1584 #ifndef APP_PHONON_SEEK
1585 const qint64 totalTime = mediaObject->totalTime();
1586 slider->blockSignals(true);
1587 // qWarning() << totalTime << time << time * 100 / totalTime;
1588 if (totalTime > 0 && time > 0 && !slider->isSliderDown() &&
1589 mediaObject->state() == Phonon::PlayingState)
1590 slider->setValue(time * slider->maximum() / totalTime);
1591 slider->blockSignals(false);
1597 void MainWindow::totalTimeChanged(qint64 time) {
1599 // totalTime->clear();
1602 // totalTime->setText(formatTime(time));
1605 slider->blockSignals(true);
1606 slider->setMaximum(time/1000);
1607 slider->blockSignals(false);
1611 QString MainWindow::formatTime(qint64 duration) {
1614 int seconds = (int)(duration % 60);
1616 int minutes = (int)(duration % 60);
1618 int hours = (int)(duration % 24);
1619 if (hours == 0) return res.sprintf("%02d:%02d", minutes, seconds);
1620 return res.sprintf("%02d:%02d:%02d", hours, minutes, seconds);
1623 void MainWindow::volumeUp() {
1625 qreal newVolume = volumeSlider->audioOutput()->volume() + .1;
1626 if (newVolume > volumeSlider->maximumVolume()) newVolume = volumeSlider->maximumVolume();
1627 volumeSlider->audioOutput()->setVolume(newVolume);
1631 void MainWindow::volumeDown() {
1633 qreal newVolume = volumeSlider->audioOutput()->volume() - .1;
1634 if (newVolume < 0.) newVolume = 0.;
1635 volumeSlider->audioOutput()->setVolume(newVolume);
1639 void MainWindow::volumeMute() {
1641 bool muted = volumeSlider->audioOutput()->isMuted();
1642 volumeSlider->audioOutput()->setMuted(!muted);
1643 qApp->processEvents();
1644 if (muted && volumeSlider->audioOutput()->volume() == 0.) {
1645 volumeSlider->audioOutput()->setVolume(volumeSlider->maximumVolume());
1647 qDebug() << volumeSlider->audioOutput()->isMuted() << volumeSlider->audioOutput()->volume();
1651 void MainWindow::volumeMutedChanged(bool muted) {
1653 volumeMuteAct->setIcon(IconUtils::icon("audio-volume-muted"));
1654 showMessage(tr("Volume is muted"));
1656 volumeMuteAct->setIcon(IconUtils::icon("audio-volume-high"));
1657 showMessage(tr("Volume is unmuted"));
1660 QToolButton *volumeMuteButton =
1661 qobject_cast<QToolButton *>(mainToolBar->widgetForAction(volumeMuteAct));
1662 volumeMuteButton->setIcon(volumeMuteButton->icon().pixmap(16));
1666 void MainWindow::setDefinitionMode(const QString &definitionName) {
1667 QAction *definitionAct = getAction("definition");
1668 definitionAct->setText(definitionName);
1669 definitionAct->setStatusTip(
1670 tr("Maximum video definition set to %1").arg(definitionAct->text()) + " (" +
1671 definitionAct->shortcut().toString(QKeySequence::NativeText) + ")");
1672 showMessage(definitionAct->statusTip());
1674 settings.setValue("definition", definitionName);
1677 void MainWindow::toggleDefinitionMode() {
1678 const QString definitionName = QSettings().value("definition").toString();
1679 const QVector<VideoDefinition> &definitions = VideoDefinition::getDefinitions();
1680 const VideoDefinition ¤tDefinition = VideoDefinition::forName(definitionName);
1681 if (currentDefinition.isEmpty()) {
1682 setDefinitionMode(definitions.at(0).getName());
1686 int index = definitions.indexOf(currentDefinition);
1687 if (index != definitions.size() - 1) {
1692 // TODO: pass a VideoDefinition instead of QString.
1693 setDefinitionMode(definitions.at(index).getName());
1696 void MainWindow::clearRecentKeywords() {
1698 settings.remove("recentKeywords");
1699 settings.remove("recentChannels");
1700 if (views->currentWidget() == homeView) {
1701 SearchView *searchView = homeView->getSearchView();
1702 searchView->updateRecentKeywords();
1703 searchView->updateRecentChannels();
1705 HttpUtils::clearCaches();
1706 showMessage(tr("Your privacy is now safe"));
1709 void MainWindow::setManualPlay(bool enabled) {
1711 settings.setValue("manualplay", QVariant::fromValue(enabled));
1712 if (views->currentWidget() == homeView &&
1713 homeView->currentWidget() == homeView->getSearchView())
1715 showActionInStatusBar(getAction("manualplay"), enabled);
1718 void MainWindow::updateDownloadMessage(const QString &message) {
1719 getAction("downloads")->setText(message);
1722 void MainWindow::downloadsFinished() {
1723 getAction("downloads")->setText(tr("&Downloads"));
1724 showMessage(tr("Downloads complete"));
1727 void MainWindow::toggleDownloads(bool show) {
1729 stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
1730 getAction("downloads")
1731 ->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_J)
1732 << QKeySequence(Qt::Key_Escape));
1734 getAction("downloads")
1735 ->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_J));
1736 stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape)
1737 << QKeySequence(Qt::Key_MediaStop));
1740 if (!downloadView) {
1741 downloadView = new DownloadView(this);
1742 views->addWidget(downloadView);
1745 showWidget(downloadView);
1750 void MainWindow::suggestionAccepted(Suggestion *suggestion) {
1751 search(suggestion->value);
1754 void MainWindow::search(const QString &query) {
1755 QString q = query.simplified();
1756 if (q.isEmpty()) return;
1757 SearchParams *searchParams = new SearchParams();
1758 searchParams->setKeywords(q);
1759 showMedia(searchParams);
1762 void MainWindow::dragEnterEvent(QDragEnterEvent *e) {
1763 if (e->mimeData()->hasFormat("text/uri-list")) {
1764 QList<QUrl> urls = e->mimeData()->urls();
1765 if (urls.isEmpty()) return;
1766 const QUrl &url = urls.at(0);
1767 QString videoId = YTSearch::videoIdFromUrl(url.toString());
1768 if (!videoId.isEmpty()) e->acceptProposedAction();
1772 void MainWindow::dropEvent(QDropEvent *e) {
1773 if (!toolbarSearch->isEnabled()) return;
1775 QList<QUrl> urls = e->mimeData()->urls();
1776 if (urls.isEmpty()) return;
1777 const QUrl &url = urls.at(0);
1778 QString videoId = YTSearch::videoIdFromUrl(url.toString());
1779 if (!videoId.isEmpty()) {
1780 setWindowTitle(url.toString());
1781 SearchParams *searchParams = new SearchParams();
1782 searchParams->setKeywords(videoId);
1783 showMedia(searchParams);
1787 void MainWindow::checkForUpdate() {
1788 static const QString updateCheckKey = "updateCheck";
1792 uint unixTime = QDateTime::currentDateTime().toTime_t();
1793 int lastCheck = settings.value(updateCheckKey).toInt();
1794 int secondsSinceLastCheck = unixTime - lastCheck;
1795 // qDebug() << "secondsSinceLastCheck" << unixTime << lastCheck << secondsSinceLastCheck;
1796 if (secondsSinceLastCheck < 86400) return;
1799 if (updateChecker) delete updateChecker;
1800 updateChecker = new UpdateChecker();
1801 connect(updateChecker, SIGNAL(newVersion(QString)), this, SLOT(gotNewVersion(QString)));
1802 updateChecker->checkForUpdate();
1803 settings.setValue(updateCheckKey, unixTime);
1806 void MainWindow::gotNewVersion(const QString &version) {
1807 if (updateChecker) {
1808 delete updateChecker;
1813 QString checkedVersion = settings.value("checkedVersion").toString();
1814 if (checkedVersion == version) return;
1818 UpdateDialog *dialog = new UpdateDialog(version, this);
1822 simpleUpdateDialog(version);
1826 void MainWindow::simpleUpdateDialog(const QString &version) {
1827 QMessageBox msgBox(this);
1828 msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png"));
1829 msgBox.setText(tr("%1 version %2 is now available.").arg(Constants::NAME, version));
1830 msgBox.setModal(true);
1831 msgBox.setWindowModality(Qt::WindowModal);
1832 msgBox.addButton(QMessageBox::Close);
1833 QPushButton *laterButton = msgBox.addButton(tr("Remind me later"), QMessageBox::RejectRole);
1834 QPushButton *updateButton = msgBox.addButton(tr("Update"), QMessageBox::AcceptRole);
1836 if (msgBox.clickedButton() != laterButton) {
1838 settings.setValue("checkedVersion", version);
1840 if (msgBox.clickedButton() == updateButton) visitSite();
1843 bool MainWindow::needStatusBar() {
1844 return !statusToolBar->actions().isEmpty();
1847 void MainWindow::adjustMessageLabelPosition() {
1848 if (messageLabel->parent() == this)
1849 messageLabel->move(0, height() - messageLabel->height());
1851 messageLabel->move(mapToGlobal(QPoint(0, height() - messageLabel->height())));
1854 void MainWindow::floatOnTop(bool onTop, bool showAction) {
1855 if (showAction) showActionInStatusBar(getAction("ontop"), onTop);
1857 mac::floatOnTop(winId(), onTop);
1860 setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
1863 setWindowFlags(windowFlags() ^ Qt::WindowStaysOnTopHint);
1869 void MainWindow::restore() {
1871 mac::uncloseWindow(winId());
1875 void MainWindow::messageReceived(const QString &message) {
1876 if (message == QLatin1String("--toggle-playing")) {
1877 if (pauseAct->isEnabled()) pauseAct->trigger();
1878 } else if (message == QLatin1String("--next")) {
1879 if (skipAct->isEnabled()) skipAct->trigger();
1880 } else if (message == QLatin1String("--previous")) {
1881 if (skipBackwardAct->isEnabled()) skipBackwardAct->trigger();
1882 } else if (message == QLatin1String("--stop-after-this")) {
1883 getAction("stopafterthis")->toggle();
1884 } else if (message.startsWith("--")) {
1885 MainWindow::printHelp();
1886 } else if (!message.isEmpty()) {
1887 SearchParams *searchParams = new SearchParams();
1888 searchParams->setKeywords(message);
1889 showMedia(searchParams);
1893 void MainWindow::hideFullscreenUI() {
1894 if (views->currentWidget() != mediaView) return;
1895 setCursor(Qt::BlankCursor);
1897 QPoint p = mapFromGlobal(QCursor::pos());
1898 const int x = p.x();
1900 if (x > mediaView->getSidebar()->width()) mediaView->setSidebarVisibility(false);
1903 const int y = p.y();
1904 bool shouldHideToolbar = !toolbarSearch->hasFocus() && y > mainToolBar->height();
1905 if (shouldHideToolbar) mainToolBar->setVisible(false);
1909 void MainWindow::toggleMenuVisibility() {
1910 bool show = !menuBar()->isVisible();
1911 menuBar()->setVisible(show);
1914 void MainWindow::toggleMenuVisibilityWithMessage() {
1915 bool show = !menuBar()->isVisible();
1916 menuBar()->setVisible(show);
1918 QMessageBox msgBox(this);
1919 msgBox.setText(tr("You can still access the menu bar by pressing the ALT key"));
1920 msgBox.setModal(true);
1921 msgBox.setWindowModality(Qt::WindowModal);
1926 #ifdef APP_MAC_STORE
1927 void MainWindow::rateOnAppStore() {
1928 QDesktopServices::openUrl(QUrl("macappstore://userpub.itunes.apple.com"
1929 "/WebObjects/MZUserPublishing.woa/wa/addUserReview"
1930 "?id=422006190&type=Purple+Software"));
1934 void MainWindow::printHelp() {
1935 QString msg = QString("%1 %2\n\n").arg(Constants::NAME, Constants::VERSION);
1936 msg += "Usage: minitube [options]\n";
1937 msg += "Options:\n";
1938 msg += " --toggle-playing\t";
1939 msg += "Start or pause playback.\n";
1940 msg += " --next\t\t";
1941 msg += "Skip to the next video.\n";
1942 msg += " --previous\t\t";
1943 msg += "Go back to the previous video.\n";
1944 msg += " --stop-after-this\t";
1945 msg += "Stop playback at the end of the video.\n";
1946 std::cout << msg.toLocal8Bit().data();
1949 QAction *MainWindow::getAction(const char *name) {
1950 return actionMap.value(QByteArray::fromRawData(name, strlen(name)));
1953 void MainWindow::addNamedAction(const QByteArray &name, QAction *action) {
1954 actionMap.insert(name, action);
1957 QMenu *MainWindow::getMenu(const char *name) {
1958 return menuMap.value(QByteArray::fromRawData(name, strlen(name)));
1961 void MainWindow::showMessage(const QString &message) {
1962 if (!isVisible()) return;
1964 if (!mac::isVisible(winId())) return;
1966 if (statusBar()->isVisible())
1967 statusBar()->showMessage(message, 60000);
1969 messageLabel->setText(message);
1970 QSize size = messageLabel->sizeHint();
1971 // round width to nearest 10 to avoid flicker with fast changing messages (e.g. volume
1973 int w = size.width();
1974 const int multiple = 10;
1975 w = w + multiple / 2;
1978 messageLabel->resize(size);
1979 if (messageLabel->isHidden()) {
1980 adjustMessageLabelPosition();
1981 messageLabel->show();
1983 messageTimer->start();
1987 void MainWindow::hideMessage() {
1988 if (messageLabel->isVisible()) {
1989 messageLabel->hide();
1990 messageLabel->clear();
1994 #ifdef APP_ACTIVATION
1995 void MainWindow::showActivationView() {
1996 QWidget *activationView = ActivationView::instance();
1997 views->addWidget(activationView);
1998 if (views->currentWidget() != activationView) showWidget(activationView);
2002 void MainWindow::showRegionsView() {
2004 regionsView = new RegionsView(this);
2005 connect(regionsView, SIGNAL(regionChanged()), homeView->getStandardFeedsView(),
2007 views->addWidget(regionsView);
2009 showWidget(regionsView);