]> git.sur5r.net Git - minitube/blob - src/MainWindow.cpp
Imported Upstream version 1.2
[minitube] / src / MainWindow.cpp
1 #include "MainWindow.h"
2 #include "spacer.h"
3 #include "constants.h"
4 #include "iconloader/qticonloader.h"
5 #include "global.h"
6 #include "videodefinition.h"
7 #include "fontutils.h"
8 #include "globalshortcuts.h"
9 #ifdef Q_WS_X11
10 #include "gnomeglobalshortcutbackend.h"
11 #endif
12 #ifdef APP_MAC
13 // #include "local/mac/mac_startup.h"
14 #endif
15 #include "downloadmanager.h"
16
17 MainWindow::MainWindow() :
18         aboutView(0),
19         downloadView(0),
20         mediaObject(0),
21         audioOutput(0),
22         m_fullscreen(false) {
23
24     // views mechanism
25     history = new QStack<QWidget*>();
26     views = new QStackedWidget(this);
27     setCentralWidget(views);
28
29     // views
30     searchView = new SearchView(this);
31     connect(searchView, SIGNAL(search(QString)), this, SLOT(showMedia(QString)));
32     views->addWidget(searchView);
33
34     mediaView = new MediaView(this);
35     views->addWidget(mediaView);
36
37     toolbarSearch = new SearchLineEdit(this);
38     toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize()*15);
39     connect(toolbarSearch, SIGNAL(search(const QString&)), searchView, SLOT(watch(const QString&)));
40
41     // build ui
42     createActions();
43     createMenus();
44     createToolBars();
45     createStatusBar();
46
47     initPhonon();
48     mediaView->setMediaObject(mediaObject);
49
50     // remove that useless menu/toolbar context menu
51     this->setContextMenuPolicy(Qt::NoContextMenu);
52
53     // mediaView init stuff thats needs actions
54     mediaView->initialize();
55
56     // event filter to block ugly toolbar tooltips
57     qApp->installEventFilter(this);
58
59     // restore window position
60     readSettings();
61
62     // show the initial view
63     showWidget(searchView);
64
65     // Global shortcuts
66     GlobalShortcuts &shortcuts = GlobalShortcuts::instance();
67 #ifdef Q_WS_X11
68     if (GnomeGlobalShortcutBackend::IsGsdAvailable())
69         shortcuts.setBackend(new GnomeGlobalShortcutBackend(&shortcuts));
70 #endif
71 #ifdef APP_MAC
72     // mac::MacSetup();
73 #endif
74     connect(&shortcuts, SIGNAL(PlayPause()), pauseAct, SLOT(trigger()));
75     connect(&shortcuts, SIGNAL(Stop()), this, SLOT(stop()));
76     connect(&shortcuts, SIGNAL(Next()), skipAct, SLOT(trigger()));
77
78     connect(DownloadManager::instance(), SIGNAL(statusMessageChanged(QString)),
79             SLOT(updateDownloadMessage(QString)));
80     connect(DownloadManager::instance(), SIGNAL(finished()),
81             SLOT(downloadsFinished()));
82 }
83
84 MainWindow::~MainWindow() {
85     delete history;
86 }
87
88 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
89     if (event->type() == QEvent::ToolTip) {
90         // kill tooltips
91         return true;
92     } else {
93         // standard event processing
94         return QObject::eventFilter(obj, event);
95     }
96 }
97
98 void MainWindow::createActions() {
99
100     QMap<QString, QAction*> *actions = The::globalActions();
101
102     stopAct = new QAction(QtIconLoader::icon("media-playback-stop"), tr("&Stop"), this);
103     stopAct->setStatusTip(tr("Stop playback and go back to the search view"));
104     stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
105     stopAct->setEnabled(false);
106     actions->insert("stop", stopAct);
107     connect(stopAct, SIGNAL(triggered()), this, SLOT(stop()));
108
109     skipAct = new QAction(QtIconLoader::icon("media-skip-forward"), tr("S&kip"), this);
110     skipAct->setStatusTip(tr("Skip to the next video"));
111     skipAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Right) << QKeySequence(Qt::Key_MediaNext));
112     skipAct->setEnabled(false);
113     actions->insert("skip", skipAct);
114     connect(skipAct, SIGNAL(triggered()), mediaView, SLOT(skip()));
115
116     pauseAct = new QAction(QtIconLoader::icon("media-playback-pause"), tr("&Pause"), this);
117     pauseAct->setStatusTip(tr("Pause playback"));
118     pauseAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Space) << QKeySequence(Qt::Key_MediaPlay));
119     pauseAct->setEnabled(false);
120     actions->insert("pause", pauseAct);
121     connect(pauseAct, SIGNAL(triggered()), mediaView, SLOT(pause()));
122
123     fullscreenAct = new QAction(QtIconLoader::icon("view-fullscreen"), tr("&Full Screen"), this);
124     fullscreenAct->setStatusTip(tr("Go full screen"));
125     fullscreenAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Return));
126     fullscreenAct->setShortcutContext(Qt::ApplicationShortcut);
127 #if QT_VERSION >= 0x040600
128     fullscreenAct->setPriority(QAction::LowPriority);
129 #endif
130     actions->insert("fullscreen", fullscreenAct);
131     connect(fullscreenAct, SIGNAL(triggered()), this, SLOT(fullscreen()));
132
133     compactViewAct = new QAction(tr("&Compact mode"), this);
134     compactViewAct->setStatusTip(tr("Hide the playlist and the toolbar"));
135     compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return));
136     compactViewAct->setCheckable(true);
137     compactViewAct->setChecked(false);
138     compactViewAct->setEnabled(false);
139     actions->insert("compactView", compactViewAct);
140     connect(compactViewAct, SIGNAL(toggled(bool)), this, SLOT(compactView(bool)));
141
142     webPageAct = new QAction(tr("Open the &YouTube page"), this);
143     webPageAct->setStatusTip(tr("Go to the YouTube video page and pause playback"));
144     webPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Y));
145     webPageAct->setEnabled(false);
146     actions->insert("webpage", webPageAct);
147     connect(webPageAct, SIGNAL(triggered()), mediaView, SLOT(openWebPage()));
148
149     copyPageAct = new QAction(tr("Copy the YouTube &link"), this);
150     copyPageAct->setStatusTip(tr("Copy the current video YouTube link to the clipboard"));
151     copyPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L));
152     copyPageAct->setEnabled(false);
153     actions->insert("pagelink", copyPageAct);
154     connect(copyPageAct, SIGNAL(triggered()), mediaView, SLOT(copyWebPage()));
155
156     copyLinkAct = new QAction(tr("Copy the video stream &URL"), this);
157     copyLinkAct->setStatusTip(tr("Copy the current video stream URL to the clipboard"));
158     copyLinkAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U));
159     copyLinkAct->setEnabled(false);
160     actions->insert("videolink", copyLinkAct);
161     connect(copyLinkAct, SIGNAL(triggered()), mediaView, SLOT(copyVideoLink()));
162
163     removeAct = new QAction(tr("&Remove"), this);
164     removeAct->setStatusTip(tr("Remove the selected videos from the playlist"));
165     removeAct->setShortcuts(QList<QKeySequence>() << QKeySequence("Del") << QKeySequence("Backspace"));
166     removeAct->setEnabled(false);
167     actions->insert("remove", removeAct);
168     connect(removeAct, SIGNAL(triggered()), mediaView, SLOT(removeSelected()));
169
170     moveUpAct = new QAction(tr("Move &Up"), this);
171     moveUpAct->setStatusTip(tr("Move up the selected videos in the playlist"));
172     moveUpAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up));
173     moveUpAct->setEnabled(false);
174     actions->insert("moveUp", moveUpAct);
175     connect(moveUpAct, SIGNAL(triggered()), mediaView, SLOT(moveUpSelected()));
176
177     moveDownAct = new QAction(tr("Move &Down"), this);
178     moveDownAct->setStatusTip(tr("Move down the selected videos in the playlist"));
179     moveDownAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down));
180     moveDownAct->setEnabled(false);
181     actions->insert("moveDown", moveDownAct);
182     connect(moveDownAct, SIGNAL(triggered()), mediaView, SLOT(moveDownSelected()));
183
184     clearAct = new QAction(tr("&Clear recent keywords"), this);
185     clearAct->setMenuRole(QAction::ApplicationSpecificRole);
186     clearAct->setShortcuts(QList<QKeySequence>()
187                            << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Delete)
188                            << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Backspace));
189     clearAct->setStatusTip(tr("Clear the search history. Cannot be undone."));
190     clearAct->setEnabled(true);
191     actions->insert("clearRecentKeywords", clearAct);
192     connect(clearAct, SIGNAL(triggered()), SLOT(clearRecentKeywords()));
193
194     quitAct = new QAction(tr("&Quit"), this);
195     quitAct->setMenuRole(QAction::QuitRole);
196     quitAct->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+Q")) << QKeySequence(Qt::CTRL + Qt::Key_W));
197     quitAct->setStatusTip(tr("Bye"));
198     actions->insert("quit", quitAct);
199     connect(quitAct, SIGNAL(triggered()), this, SLOT(close()));
200
201     siteAct = new QAction(tr("&Website"), this);
202     siteAct->setShortcut(QKeySequence::HelpContents);
203     siteAct->setStatusTip(tr("%1 on the Web").arg(Constants::APP_NAME));
204     actions->insert("site", siteAct);
205     connect(siteAct, SIGNAL(triggered()), this, SLOT(visitSite()));
206
207     donateAct = new QAction(tr("Make a &donation"), this);
208     donateAct->setStatusTip(tr("Please support the continued development of %1").arg(Constants::APP_NAME));
209     actions->insert("donate", donateAct);
210     connect(donateAct, SIGNAL(triggered()), this, SLOT(donate()));
211
212     aboutAct = new QAction(tr("&About"), this);
213     aboutAct->setMenuRole(QAction::AboutRole);
214     aboutAct->setStatusTip(tr("Info about %1").arg(Constants::APP_NAME));
215     actions->insert("about", aboutAct);
216     connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
217
218     // Invisible actions
219
220     searchFocusAct = new QAction(this);
221     searchFocusAct->setShortcut(QKeySequence::Find);
222     searchFocusAct->setStatusTip(tr("Search"));
223     actions->insert("search", searchFocusAct);
224     connect(searchFocusAct, SIGNAL(triggered()), this, SLOT(searchFocus()));
225     addAction(searchFocusAct);
226
227     volumeUpAct = new QAction(this);
228     volumeUpAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Plus) << QKeySequence(Qt::Key_VolumeUp));
229     actions->insert("volume-up", volumeUpAct);
230     connect(volumeUpAct, SIGNAL(triggered()), this, SLOT(volumeUp()));
231     addAction(volumeUpAct);
232
233     volumeDownAct = new QAction(this);
234     volumeDownAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Minus) << QKeySequence(Qt::Key_VolumeDown));
235     actions->insert("volume-down", volumeDownAct);
236     connect(volumeDownAct, SIGNAL(triggered()), this, SLOT(volumeDown()));
237     addAction(volumeDownAct);
238
239     volumeMuteAct = new QAction(this);
240     volumeMuteAct->setIcon(QtIconLoader::icon("audio-volume-high"));
241     volumeMuteAct->setStatusTip(tr("Mute volume"));
242     volumeMuteAct->setShortcuts(QList<QKeySequence>()
243                                 << QKeySequence(tr("Ctrl+M"))
244                                 << QKeySequence(Qt::Key_VolumeMute));
245     actions->insert("volume-mute", volumeMuteAct);
246     connect(volumeMuteAct, SIGNAL(triggered()), SLOT(volumeMute()));
247     addAction(volumeMuteAct);
248
249     QAction *definitionAct = new QAction(this);
250     definitionAct->setIcon(QtIconLoader::icon("video-display"));
251     definitionAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_D));
252     /*
253     QMenu *definitionMenu = new QMenu(this);
254     foreach (QString definition, VideoDefinition::getDefinitionNames()) {
255         definitionMenu->addAction(definition);
256     }
257     definitionAct->setMenu(definitionMenu);
258     */
259     actions->insert("definition", definitionAct);
260     connect(definitionAct, SIGNAL(triggered()), SLOT(toggleDefinitionMode()));
261     addAction(definitionAct);
262
263     QAction *action;
264
265     /*
266     action = new QAction(tr("&Autoplay"), this);
267     action->setStatusTip(tr("Automatically start playing videos"));
268     action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P));
269     action->setCheckable(true);
270     connect(action, SIGNAL(toggled(bool)), SLOT(setAutoplay(bool)));
271     actions->insert("autoplay", action);
272     */
273
274     action = new QAction(tr("&Downloads"), this);
275     action->setStatusTip(tr("Show details about video downloads"));
276     action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_J));
277     action->setCheckable(true);
278     action->setIcon(QtIconLoader::icon("go-down"));
279     action->setVisible(false);
280     connect(action, SIGNAL(toggled(bool)), SLOT(toggleDownloads(bool)));
281     actions->insert("downloads", action);
282
283     action = new QAction(tr("&Download"), this);
284     action->setStatusTip(tr("Download the current video"));
285     action->setShortcut(QKeySequence::Save);
286     action->setIcon(QtIconLoader::icon("go-down"));
287     action->setEnabled(false);
288 #if QT_VERSION >= 0x040600
289     action->setPriority(QAction::LowPriority);
290 #endif
291     connect(action, SIGNAL(triggered()), mediaView, SLOT(downloadVideo()));
292     actions->insert("download", action);
293
294     // common action properties
295     foreach (QAction *action, actions->values()) {
296
297         // add actions to the MainWindow so that they work
298         // when the menu is hidden
299         addAction(action);
300
301         // never autorepeat.
302         // unexperienced users tend to keep keys pressed for a "long" time
303         action->setAutoRepeat(false);
304
305         // set to something more meaningful then the toolbar text
306         // HELP! how to remove tooltips altogether?!
307         if (!action->statusTip().isEmpty())
308             action->setToolTip(action->statusTip());
309
310         // show keyboard shortcuts in the status bar
311         if (!action->shortcut().isEmpty())
312             action->setStatusTip(action->statusTip() + " (" + action->shortcut().toString(QKeySequence::NativeText) + ")");
313
314         // no icons in menus
315         action->setIconVisibleInMenu(false);
316
317     }
318
319 }
320
321 void MainWindow::createMenus() {
322
323     QMap<QString, QMenu*> *menus = The::globalMenus();
324
325     fileMenu = menuBar()->addMenu(tr("&Application"));
326     // menus->insert("file", fileMenu);
327     fileMenu->addAction(clearAct);
328 #ifndef APP_MAC
329     fileMenu->addSeparator();
330 #endif
331     fileMenu->addAction(quitAct);
332
333     playlistMenu = menuBar()->addMenu(tr("&Playlist"));
334     menus->insert("playlist", playlistMenu);
335     playlistMenu->addAction(removeAct);
336     playlistMenu->addSeparator();
337     playlistMenu->addAction(moveUpAct);
338     playlistMenu->addAction(moveDownAct);
339
340     viewMenu = menuBar()->addMenu(tr("&Video"));
341     menus->insert("video", viewMenu);
342     viewMenu->addAction(stopAct);
343     viewMenu->addAction(pauseAct);
344     viewMenu->addAction(skipAct);
345     viewMenu->addSeparator();
346     viewMenu->addAction(The::globalActions()->value("download"));
347     viewMenu->addSeparator();
348     viewMenu->addAction(webPageAct);
349     viewMenu->addAction(copyPageAct);
350     viewMenu->addAction(copyLinkAct);
351     viewMenu->addSeparator();
352     viewMenu->addAction(compactViewAct);
353     viewMenu->addAction(fullscreenAct);
354 #ifdef APP_MAC
355     extern void qt_mac_set_dock_menu(QMenu *);
356     qt_mac_set_dock_menu(viewMenu);
357 #endif
358
359     helpMenu = menuBar()->addMenu(tr("&Help"));
360     helpMenu->addAction(siteAct);
361     helpMenu->addAction(donateAct);
362     helpMenu->addAction(aboutAct);
363 }
364
365 void MainWindow::createToolBars() {
366
367     setUnifiedTitleAndToolBarOnMac(true);
368
369     mainToolBar = new QToolBar(this);
370 #if QT_VERSION < 0x040600 | defined(APP_MAC)
371     mainToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
372 #else
373     mainToolBar->setToolButtonStyle(Qt::ToolButtonFollowStyle);
374 #endif
375     mainToolBar->setFloatable(false);
376     mainToolBar->setMovable(false);
377
378 #ifdef APP_MAC
379     mainToolBar->setIconSize(QSize(32, 32));
380 #endif
381
382     mainToolBar->addAction(stopAct);
383     mainToolBar->addAction(pauseAct);
384     mainToolBar->addAction(skipAct);
385     mainToolBar->addAction(fullscreenAct);
386     mainToolBar->addAction(The::globalActions()->value("download"));
387
388     mainToolBar->addWidget(new Spacer());
389
390     QFont smallerFont = FontUtils::small();
391     currentTime = new QLabel(mainToolBar);
392     currentTime->setFont(smallerFont);
393     mainToolBar->addWidget(currentTime);
394
395     mainToolBar->addWidget(new Spacer());
396
397     seekSlider = new Phonon::SeekSlider(this);
398     seekSlider->setIconVisible(false);
399     seekSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
400     mainToolBar->addWidget(seekSlider);
401
402     mainToolBar->addWidget(new Spacer());
403
404     totalTime = new QLabel(mainToolBar);
405     totalTime->setFont(smallerFont);
406     mainToolBar->addWidget(totalTime);
407
408     mainToolBar->addWidget(new Spacer());
409
410     mainToolBar->addAction(volumeMuteAct);
411
412     volumeSlider = new Phonon::VolumeSlider(this);
413     volumeSlider->setMuteVisible(false);
414     // qDebug() << volumeSlider->children();
415     // status tip for the volume slider
416     QSlider* volumeQSlider = volumeSlider->findChild<QSlider*>();
417     if (volumeQSlider)
418         volumeQSlider->setStatusTip(tr("Press %1 to raise the volume, %2 to lower it").arg(
419                 volumeUpAct->shortcut().toString(QKeySequence::NativeText), volumeDownAct->shortcut().toString(QKeySequence::NativeText)));
420     // this makes the volume slider smaller
421     volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
422     mainToolBar->addWidget(volumeSlider);
423
424     mainToolBar->addWidget(new Spacer());
425
426     toolbarSearch->setStatusTip(searchFocusAct->statusTip());
427     mainToolBar->addWidget(toolbarSearch);
428
429     mainToolBar->addWidget(new Spacer());
430
431     addToolBar(mainToolBar);
432 }
433
434 void MainWindow::createStatusBar() {
435
436     // remove ugly borders on OSX
437     // also remove excessive spacing
438     statusBar()->setStyleSheet("::item{border:0 solid} QToolBar {padding:0;spacing:0;margin:0}");
439
440     QToolBar *toolBar = new QToolBar(this);
441     toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
442     toolBar->setIconSize(QSize(16, 16));
443     toolBar->addAction(The::globalActions()->value("downloads"));
444     // toolBar->addAction(The::globalActions()->value("autoplay"));
445     toolBar->addAction(The::globalActions()->value("definition"));
446     statusBar()->addPermanentWidget(toolBar);
447
448     statusBar()->show();
449 }
450
451 void MainWindow::readSettings() {
452     QSettings settings;
453     restoreGeometry(settings.value("geometry").toByteArray());
454 #ifdef APP_MAC
455     if (!isMaximized())
456         move(x(), y() + mainToolBar->height() + 8);
457 #endif
458     setDefinitionMode(settings.value("definition", VideoDefinition::getDefinitionNames().first()).toString());
459     audioOutput->setVolume(settings.value("volume", 1).toDouble());
460     audioOutput->setMuted(settings.value("volumeMute").toBool());
461 }
462
463 void MainWindow::writeSettings() {
464
465     QSettings settings;
466
467     // do not save geometry when in full screen
468     if (!m_fullscreen) {
469         settings.setValue("geometry", saveGeometry());
470     }
471
472     settings.setValue("volume", audioOutput->volume());
473     settings.setValue("volumeMute", audioOutput->isMuted());
474     mediaView->saveSplitterState();
475 }
476
477 void MainWindow::goBack() {
478     if ( history->size() > 1 ) {
479         history->pop();
480         QWidget *widget = history->pop();
481         showWidget(widget);
482     }
483 }
484
485 void MainWindow::showWidget ( QWidget* widget ) {
486
487     setUpdatesEnabled(false);
488
489     // call hide method on the current view
490     View* oldView = dynamic_cast<View *> (views->currentWidget());
491     if (oldView) {
492         oldView->disappear();
493     }
494
495     // call show method on the new view
496     View* newView = dynamic_cast<View *> (widget);
497     if (newView) {
498         newView->appear();
499         QMap<QString,QVariant> metadata = newView->metadata();
500         QString windowTitle = metadata.value("title").toString();
501         if (windowTitle.length())
502             windowTitle += " - ";
503         setWindowTitle(windowTitle + Constants::APP_NAME);
504         statusBar()->showMessage((metadata.value("description").toString()));
505     }
506
507     stopAct->setEnabled(widget == mediaView);
508     fullscreenAct->setEnabled(widget == mediaView);
509     compactViewAct->setEnabled(widget == mediaView);
510     webPageAct->setEnabled(widget == mediaView);
511     copyPageAct->setEnabled(widget == mediaView);
512     copyLinkAct->setEnabled(widget == mediaView);
513     aboutAct->setEnabled(widget != aboutView);
514     The::globalActions()->value("download")->setEnabled(widget == mediaView);
515     The::globalActions()->value("downloads")->setChecked(widget == downloadView);
516
517     // toolbar only for the mediaView
518     /* mainToolBar->setVisible(
519             (widget == mediaView && !compactViewAct->isChecked())
520             || widget == downloadView
521             ); */
522
523     setUpdatesEnabled(true);
524
525     QWidget *oldWidget = views->currentWidget();
526     views->setCurrentWidget(widget);
527
528 #ifdef APP_MAC
529     // crossfade only on OSX
530     // where we can be sure of video performance
531     fadeInWidget(oldWidget, widget);
532 #endif
533
534     history->push(widget);
535 }
536
537 void MainWindow::fadeInWidget(QWidget *oldWidget, QWidget *newWidget) {
538     if (faderWidget) faderWidget->close();
539     if (!oldWidget || !newWidget) {
540         // qDebug() << "no widgets";
541         return;
542     }
543     faderWidget = new FaderWidget(newWidget);
544     faderWidget->start(QPixmap::grabWidget(oldWidget));
545 }
546
547 void MainWindow::about() {
548     if (!aboutView) {
549         aboutView = new AboutView(this);
550         views->addWidget(aboutView);
551     }
552     showWidget(aboutView);
553 }
554
555 void MainWindow::visitSite() {
556     QUrl url(Constants::WEBSITE);
557     statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
558     QDesktopServices::openUrl(url);
559 }
560
561 void MainWindow::donate() {
562     QUrl url(QString(Constants::WEBSITE) + "#donate");
563     statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
564     QDesktopServices::openUrl(url);
565 }
566
567 void MainWindow::quit() {
568     writeSettings();
569     qApp->quit();
570 }
571
572 void MainWindow::closeEvent(QCloseEvent *event) {
573     if (DownloadManager::instance()->activeItems() > 0) {
574         QMessageBox msgBox;
575         msgBox.setIconPixmap(QPixmap(":/images/app.png").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
576         msgBox.setText(tr("Do you want to exit %1 with a download in progress?").arg(Constants::APP_NAME));
577         msgBox.setInformativeText(tr("If you close %1 now, this download will be cancelled.").arg(Constants::APP_NAME));
578         msgBox.setModal(true);
579
580         msgBox.addButton(tr("Close and cancel download"), QMessageBox::RejectRole);
581         QPushButton *waitButton = msgBox.addButton(tr("Wait for download to finish"), QMessageBox::ActionRole);
582
583         msgBox.exec();
584
585         if (msgBox.clickedButton() == waitButton) {
586             event->ignore();
587             return;
588         }
589
590     }
591     quit();
592     QWidget::closeEvent(event);
593 }
594
595 void MainWindow::showSearch() {
596     showWidget(searchView);
597     currentTime->clear();
598     totalTime->clear();
599 }
600
601 void MainWindow::showMedia(QString query) {
602     SearchParams *searchParams = new SearchParams();
603     searchParams->setKeywords(query);
604     mediaView->search(searchParams);
605     showWidget(mediaView);
606 }
607
608 void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState */) {
609
610     // qDebug() << "Phonon state: " << newState;
611
612     switch (newState) {
613
614     case Phonon::ErrorState:
615         if (mediaObject->errorType() == Phonon::FatalError) {
616             statusBar()->showMessage(tr("Fatal error: %1").arg(mediaObject->errorString()));
617         } else {
618             statusBar()->showMessage(tr("Error: %1").arg(mediaObject->errorString()));
619         }
620         break;
621
622          case Phonon::PlayingState:
623         pauseAct->setEnabled(true);
624         pauseAct->setIcon(QtIconLoader::icon("media-playback-pause"));
625         pauseAct->setText(tr("&Pause"));
626         pauseAct->setStatusTip(tr("Pause playback") + " (" +  pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
627         skipAct->setEnabled(true);
628         // stopAct->setEnabled(true);
629         break;
630
631          case Phonon::StoppedState:
632         pauseAct->setEnabled(false);
633         skipAct->setEnabled(false);
634         // stopAct->setEnabled(false);
635         break;
636
637          case Phonon::PausedState:
638         skipAct->setEnabled(true);
639         pauseAct->setEnabled(true);
640         pauseAct->setIcon(QtIconLoader::icon("media-playback-start"));
641         pauseAct->setText(tr("&Play"));
642         pauseAct->setStatusTip(tr("Resume playback") + " (" +  pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
643         // stopAct->setEnabled(true);
644         break;
645
646          case Phonon::BufferingState:
647          case Phonon::LoadingState:
648         skipAct->setEnabled(true);
649         pauseAct->setEnabled(false);
650         currentTime->clear();
651         totalTime->clear();
652         // stopAct->setEnabled(true);
653         break;
654
655          default:
656         ;
657     }
658 }
659
660 void MainWindow::stop() {
661     mediaView->stop();
662     showSearch();
663 }
664
665 void MainWindow::fullscreen() {
666
667     // No compact view action when in full screen
668     compactViewAct->setVisible(m_fullscreen);
669     compactViewAct->setChecked(false);
670
671     // Also no Youtube action since it opens a new window
672     webPageAct->setVisible(m_fullscreen);
673     copyPageAct->setVisible(m_fullscreen);
674     copyLinkAct->setVisible(m_fullscreen);
675
676     stopAct->setVisible(m_fullscreen);
677
678     // workaround: prevent focus on the search bar
679     // it steals the Space key needed for Play/Pause
680     toolbarSearch->setEnabled(m_fullscreen);
681
682     // Hide anything but the video
683     mediaView->setPlaylistVisible(m_fullscreen);
684     statusBar()->setVisible(m_fullscreen);
685
686 #ifndef APP_MAC
687     menuBar()->setVisible(m_fullscreen);
688 #endif
689
690 #ifdef APP_MAC
691     // make the actions work when video is fullscreen (on the Mac)
692     QMap<QString, QAction*> *actions = The::globalActions();
693     foreach (QAction *action, actions->values()) {
694         if (m_fullscreen) {
695             action->setShortcutContext(Qt::WindowShortcut);
696         } else {
697             action->setShortcutContext(Qt::ApplicationShortcut);
698         }
699     }
700 #endif
701
702     if (m_fullscreen) {
703
704         // Exit full screen
705
706         // use setShortcuts instead of setShortcut
707         // the latter seems not to work
708         fullscreenAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::ALT + Qt::Key_Return));
709         fullscreenAct->setText(tr("&Full Screen"));
710         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
711
712 #ifdef APP_MAC
713         setCentralWidget(views);
714         views->showNormal();
715         show();
716         mediaView->setFocus();
717 #else
718         mainToolBar->show();
719         if (m_maximized) showMaximized();
720         else showNormal();
721 #endif
722
723         // Make sure the window has focus
724         activateWindow();
725
726     } else {
727
728         // Enter full screen
729
730         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
731         fullscreenAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::ALT + Qt::Key_Return));
732         fullscreenAct->setText(tr("Exit &Full Screen"));
733         m_maximized = isMaximized();
734
735         // save geometry now, if the user quits when in full screen
736         // geometry won't be saved
737         writeSettings();
738
739 #ifdef APP_MAC
740         hide();
741         views->setParent(0);
742         QTimer::singleShot(0, views, SLOT(showFullScreen()));
743 #else
744         mainToolBar->hide();
745         showFullScreen();
746 #endif
747
748     }
749
750     m_fullscreen = !m_fullscreen;
751
752 }
753
754 void MainWindow::compactView(bool enable) {
755
756     mediaView->setPlaylistVisible(!enable);
757     statusBar()->setVisible(!enable);
758
759 #ifndef APP_MAC
760     menuBar()->setVisible(!enable);
761 #endif
762
763     if (enable) {
764         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
765         compactViewAct->setShortcuts(
766                 QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Return)
767                 << QKeySequence(Qt::Key_Escape));
768
769         // ensure focus does not end up to the search box
770         // as it would steal the Space shortcut
771         mediaView->setFocus();
772     } else {
773         compactViewAct->setShortcuts(QList<QKeySequence>() <<  QKeySequence(Qt::CTRL + Qt::Key_Return));
774         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
775     }
776
777 }
778
779 void MainWindow::searchFocus() {
780     QWidget *view = views->currentWidget();
781     if (view == mediaView) {
782         toolbarSearch->selectAll();
783         toolbarSearch->setFocus();
784     }
785 }
786
787 void MainWindow::initPhonon() {
788     // Phonon initialization
789     if (mediaObject) delete mediaObject;
790     if (audioOutput) delete audioOutput;
791     mediaObject = new Phonon::MediaObject(this);
792     mediaObject->setTickInterval(100);
793     connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
794             this, SLOT(stateChanged(Phonon::State, Phonon::State)));
795     connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64)));
796     connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64)));
797     seekSlider->setMediaObject(mediaObject);
798     audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this);
799     connect(audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(volumeChanged(qreal)));
800     connect(audioOutput, SIGNAL(mutedChanged(bool)), this, SLOT(volumeMutedChanged(bool)));
801     volumeSlider->setAudioOutput(audioOutput);
802     Phonon::createPath(mediaObject, audioOutput);
803 }
804
805 void MainWindow::tick(qint64 time) {
806     if (time <= 0) {
807         currentTime->clear();
808         return;
809     }
810
811     currentTime->setText(formatTime(time));
812
813     // remaining time
814     const qint64 remainingTime = mediaObject->remainingTime();
815     currentTime->setStatusTip(tr("Remaining time: %1").arg(formatTime(remainingTime)));
816
817 }
818
819 void MainWindow::totalTimeChanged(qint64 time) {
820     if (time <= 0) {
821         totalTime->clear();
822         return;
823     }
824     totalTime->setText(formatTime(time));
825 }
826
827 QString MainWindow::formatTime(qint64 time) {
828     QTime displayTime;
829     displayTime = displayTime.addMSecs(time);
830     QString timeString;
831     // 60 * 60 * 1000 = 3600000
832     if (time > 3600000)
833         timeString = displayTime.toString("h:mm:ss");
834     else
835         timeString = displayTime.toString("m:ss");
836     return timeString;
837 }
838
839 void MainWindow::volumeUp() {
840     qreal newVolume = volumeSlider->audioOutput()->volume() + .1;
841     if (newVolume > volumeSlider->maximumVolume())
842         newVolume = volumeSlider->maximumVolume();
843     volumeSlider->audioOutput()->setVolume(newVolume);
844 }
845
846 void MainWindow::volumeDown() {
847     qreal newVolume = volumeSlider->audioOutput()->volume() - .1;
848     if (newVolume < 0)
849         newVolume = 0;
850     volumeSlider->audioOutput()->setVolume(newVolume);
851 }
852
853 void MainWindow::volumeMute() {
854     volumeSlider->audioOutput()->setMuted(!volumeSlider->audioOutput()->isMuted());
855 }
856
857 void MainWindow::volumeChanged(qreal newVolume) {
858     // automatically unmute when volume changes
859     if (volumeSlider->audioOutput()->isMuted())
860         volumeSlider->audioOutput()->setMuted(false);
861     statusBar()->showMessage(tr("Volume at %1%").arg(newVolume*100));
862 }
863
864 void MainWindow::volumeMutedChanged(bool muted) {
865     if (muted) {
866         volumeMuteAct->setIcon(QtIconLoader::icon("audio-volume-muted"));
867         statusBar()->showMessage(tr("Volume is muted"));
868     } else {
869         volumeMuteAct->setIcon(QtIconLoader::icon("audio-volume-high"));
870         statusBar()->showMessage(tr("Volume is unmuted"));
871     }
872 }
873
874 void MainWindow::setDefinitionMode(QString definitionName) {
875     QAction *definitionAct = The::globalActions()->value("definition");
876     definitionAct->setText(definitionName);
877     definitionAct->setStatusTip(tr("Maximum video definition set to %1").arg(definitionAct->text())
878                                 + " (" +  definitionAct->shortcut().toString(QKeySequence::NativeText) + ")");
879     statusBar()->showMessage(definitionAct->statusTip());
880     QSettings settings;
881     settings.setValue("definition", definitionName);
882 }
883
884 void MainWindow::toggleDefinitionMode() {
885     QSettings settings;
886     QString currentDefinition = settings.value("definition").toString();
887     QStringList definitionNames = VideoDefinition::getDefinitionNames();
888     int currentIndex = definitionNames.indexOf(currentDefinition);
889     int nextIndex = 0;
890     if (currentIndex != definitionNames.size() - 1) {
891         nextIndex = currentIndex + 1;
892     }
893     QString nextDefinition = definitionNames.at(nextIndex);
894     setDefinitionMode(nextDefinition);
895 }
896
897 void MainWindow::showFullscreenToolbar(bool show) {
898     if (!m_fullscreen) return;
899     mainToolBar->setVisible(show);
900 }
901
902 void MainWindow::showFullscreenPlaylist(bool show) {
903     if (!m_fullscreen) return;
904     mediaView->setPlaylistVisible(show);
905 }
906
907 void MainWindow::clearRecentKeywords() {
908     QSettings settings;
909     settings.remove("recentKeywords");
910     searchView->updateRecentKeywords();
911     statusBar()->showMessage(tr("Your privacy is now safe"));
912 }
913
914 /*
915  void MainWindow::setAutoplay(bool enabled) {
916      QSettings settings;
917      settings.setValue("autoplay", QVariant::fromValue(enabled));
918  }
919  */
920
921 void MainWindow::updateDownloadMessage(QString message) {
922     The::globalActions()->value("downloads")->setText(message);
923 }
924
925 void MainWindow::downloadsFinished() {
926     The::globalActions()->value("downloads")->setText(tr("&Downloads"));
927     statusBar()->showMessage(tr("Downloads complete"));
928 }
929
930 void MainWindow::toggleDownloads(bool show) {
931
932     if (show) {
933         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
934         The::globalActions()->value("downloads")->setShortcuts(
935                 QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_J)
936                 << QKeySequence(Qt::Key_Escape));
937     } else {
938         The::globalActions()->value("downloads")->setShortcuts(
939                 QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_J));
940         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
941     }
942
943     if (!downloadView) {
944         downloadView = new DownloadView(this);
945         views->addWidget(downloadView);
946     }
947     if (show) showWidget(downloadView);
948     else goBack();
949 }