]> git.sur5r.net Git - minitube/blob - src/MainWindow.cpp
HD video support
[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
7 MainWindow::MainWindow() {
8
9     m_fullscreen = false;
10     mediaObject = 0;
11     audioOutput = 0;
12
13     // views mechanism
14     history = new QStack<QWidget*>();
15     views = new QStackedWidget(this);
16
17     // views
18     searchView = new SearchView(this);
19     connect(searchView, SIGNAL(search(QString)), this, SLOT(showMedia(QString)));
20     views->addWidget(searchView);
21     mediaView = new MediaView(this);
22     views->addWidget(mediaView);
23
24     // lazily initialized views
25     aboutView = 0;
26     settingsView = 0;
27
28     toolbarSearch = new SearchLineEdit(this);
29     toolbarSearch->setFont(qApp->font());
30     toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize()*15);
31     connect(toolbarSearch, SIGNAL(search(const QString&)), searchView, SLOT(watch(const QString&)));
32
33     // build ui
34     createActions();
35     createMenus();
36     createToolBars();
37     createStatusBar();
38
39     // remove that useless menu/toolbar context menu
40     this->setContextMenuPolicy(Qt::NoContextMenu);
41
42     // mediaView init stuff thats needs actions
43     mediaView->initialize();
44
45     // restore window position
46     readSettings();
47
48     // show the initial view
49     showWidget(searchView);
50
51     setCentralWidget(views);
52 }
53
54 MainWindow::~MainWindow() {
55     delete history;
56 }
57
58 void MainWindow::createActions() {
59
60     QMap<QString, QAction*> *actions = The::globalActions();
61
62     /*
63     settingsAct = new QAction(tr("&Preferences..."), this);
64     settingsAct->setStatusTip(tr(QString("Configure ").append(Constants::APP_NAME).toUtf8()));
65     // Mac integration
66     settingsAct->setMenuRole(QAction::PreferencesRole);
67     actions->insert("settings", settingsAct);
68     connect(settingsAct, SIGNAL(triggered()), this, SLOT(showSettings()));
69     */
70     
71     backAct = new QAction(QIcon(":/images/go-previous.png"), tr("&Back"), this);
72     backAct->setEnabled(false);
73     backAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Left));
74     backAct->setStatusTip(tr("Go to the previous view"));
75     actions->insert("back", backAct);
76     connect(backAct, SIGNAL(triggered()), this, SLOT(goBack()));
77
78     stopAct = new QAction(QtIconLoader::icon("media-playback-stop", QIcon(":/images/stop.png")), tr("&Stop"), this);
79     stopAct->setStatusTip(tr("Stop playback and go back to the search view"));
80     stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
81     actions->insert("stop", stopAct);
82     connect(stopAct, SIGNAL(triggered()), this, SLOT(stop()));
83
84     skipAct = new QAction(QtIconLoader::icon("media-skip-forward", QIcon(":/images/skip.png")), tr("S&kip"), this);
85     skipAct->setStatusTip(tr("Skip to the next video"));
86     skipAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Right) << QKeySequence(Qt::Key_MediaNext));
87     skipAct->setEnabled(false);
88     actions->insert("skip", skipAct);
89     connect(skipAct, SIGNAL(triggered()), mediaView, SLOT(skip()));
90
91     pauseAct = new QAction(QtIconLoader::icon("media-playback-pause", QIcon(":/images/pause.png")), tr("&Pause"), this);
92     pauseAct->setStatusTip(tr("Pause playback"));
93     pauseAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Space) << QKeySequence(Qt::Key_MediaPlay));
94     pauseAct->setEnabled(false);
95     actions->insert("pause", pauseAct);
96     connect(pauseAct, SIGNAL(triggered()), mediaView, SLOT(pause()));
97
98     fullscreenAct = new QAction(QtIconLoader::icon("view-fullscreen", QIcon(":/images/view-fullscreen.png")), tr("&Full Screen"), this);
99     fullscreenAct->setStatusTip(tr("Go full screen"));
100     fullscreenAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Return));
101     fullscreenAct->setShortcutContext(Qt::ApplicationShortcut);
102     actions->insert("fullscreen", fullscreenAct);
103     connect(fullscreenAct, SIGNAL(triggered()), this, SLOT(fullscreen()));
104
105     compactViewAct = new QAction(tr("&Compact mode"), this);
106     compactViewAct->setStatusTip(tr("Hide the playlist and the toolbar"));
107     compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return));
108     compactViewAct->setCheckable(true);
109     compactViewAct->setChecked(false);
110     compactViewAct->setEnabled(false);
111     actions->insert("compactView", compactViewAct);
112     connect(compactViewAct, SIGNAL(toggled(bool)), this, SLOT(compactView(bool)));
113
114     /*
115     // icon should be document-save but it is ugly
116     downloadAct = new QAction(QtIconLoader::icon("go-down", QIcon(":/images/go-down.png")), tr("&Download"), this);
117     downloadAct->setStatusTip(tr("Download this video"));
118     downloadAct->setShortcut(tr("Ctrl+S"));
119     downloadAct->setEnabled(false);
120     actions->insert("download", downloadAct);
121     connect(downloadAct, SIGNAL(triggered()), this, SLOT(download()));
122     */
123
124     webPageAct = new QAction(tr("&YouTube"), this);
125     webPageAct->setStatusTip(tr("Open the YouTube video page"));
126     webPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Y));
127     webPageAct->setEnabled(false);
128     actions->insert("webpage", webPageAct);
129     connect(webPageAct, SIGNAL(triggered()), mediaView, SLOT(openWebPage()));
130
131     removeAct = new QAction(tr("&Remove"), this);
132     removeAct->setStatusTip(tr("Remove the selected videos from the playlist"));
133     removeAct->setShortcuts(QList<QKeySequence>() << QKeySequence("Del") << QKeySequence("Backspace"));
134     removeAct->setEnabled(false);
135     actions->insert("remove", removeAct);
136     connect(removeAct, SIGNAL(triggered()), mediaView, SLOT(removeSelected()));
137
138     moveUpAct = new QAction(QtIconLoader::icon("go-up", QIcon(":/images/go-up.png")), tr("Move &Up"), this);
139     moveUpAct->setStatusTip(tr("Move up the selected videos in the playlist"));
140     moveUpAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up));
141     moveUpAct->setEnabled(false);
142     actions->insert("moveUp", moveUpAct);
143     connect(moveUpAct, SIGNAL(triggered()), mediaView, SLOT(moveUpSelected()));
144
145     moveDownAct = new QAction(QtIconLoader::icon("go-down", QIcon(":/images/go-down.png")), tr("Move &Down"), this);
146     moveDownAct->setStatusTip(tr("Move down the selected videos in the playlist"));
147     moveDownAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down));
148     moveDownAct->setEnabled(false);
149     actions->insert("moveDown", moveDownAct);
150     connect(moveDownAct, SIGNAL(triggered()), mediaView, SLOT(moveDownSelected()));
151
152     quitAct = new QAction(tr("&Quit"), this);
153     quitAct->setMenuRole(QAction::QuitRole);
154     quitAct->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+Q")) << QKeySequence(Qt::CTRL + Qt::Key_W));
155     quitAct->setStatusTip(tr("Bye"));
156     actions->insert("quit", quitAct);
157     connect(quitAct, SIGNAL(triggered()), this, SLOT(quit()));
158
159     siteAct = new QAction(tr("&Website"), this);
160     siteAct->setShortcut(QKeySequence::HelpContents);
161     siteAct->setStatusTip(tr("%1 on the Web").arg(Constants::APP_NAME));
162     actions->insert("site", siteAct);
163     connect(siteAct, SIGNAL(triggered()), this, SLOT(visitSite()));
164
165     donateAct = new QAction(tr("&Donate via PayPal"), this);
166     donateAct->setStatusTip(tr("Please support the continued development of %1").arg(Constants::APP_NAME));
167     actions->insert("donate", donateAct);
168     connect(donateAct, SIGNAL(triggered()), this, SLOT(donate()));
169
170     aboutAct = new QAction(tr("&About"), this);
171     aboutAct->setMenuRole(QAction::AboutRole);
172     aboutAct->setStatusTip(tr("Info about %1").arg(Constants::APP_NAME));
173     actions->insert("about", aboutAct);
174     connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
175
176     // Invisible actions
177
178     searchFocusAct = new QAction(this);
179     searchFocusAct->setShortcut(QKeySequence::Find);
180     searchFocusAct->setStatusTip(tr("Search"));
181     actions->insert("search", searchFocusAct);
182     connect(searchFocusAct, SIGNAL(triggered()), this, SLOT(searchFocus()));
183     addAction(searchFocusAct);
184
185     volumeUpAct = new QAction(this);
186     volumeUpAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Plus) << QKeySequence(Qt::Key_VolumeUp));
187     actions->insert("volume-up", volumeUpAct);
188     connect(volumeUpAct, SIGNAL(triggered()), this, SLOT(volumeUp()));
189     addAction(volumeUpAct);
190
191     volumeDownAct = new QAction(this);
192     volumeDownAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Minus) << QKeySequence(Qt::Key_VolumeDown));
193     actions->insert("volume-down", volumeDownAct);
194     connect(volumeDownAct, SIGNAL(triggered()), this, SLOT(volumeDown()));
195     addAction(volumeDownAct);
196
197     volumeMuteAct = new QAction(this);
198     volumeMuteAct->setStatusTip(tr("Mute volume"));
199     volumeMuteAct->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+M")) << QKeySequence(Qt::Key_VolumeMute));
200     actions->insert("volume-mute", volumeMuteAct);
201     connect(volumeMuteAct, SIGNAL(triggered()), this, SLOT(volumeMute()));
202     addAction(volumeMuteAct);
203
204     QAction *hdAct = new QAction(this);
205     hdAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_H));
206     hdAct->setIcon(createHDIcon());
207     hdAct->setCheckable(true);
208     actions->insert("hd", hdAct);
209     QSettings settings;
210     connect(hdAct, SIGNAL(toggled(bool)), this, SLOT(saveHdSetting(bool)));
211     addAction(hdAct);
212
213     // common action properties
214     foreach (QAction *action, actions->values()) {
215
216         // add actions to the MainWindow so that they work
217         // when the menu is hidden
218         addAction(action);
219
220         // never autorepeat.
221         // unexperienced users tend to keep keys pressed for a "long" time
222         action->setAutoRepeat(false);
223
224         // set to something more meaningful then the toolbar text
225         // HELP! how to remove tooltips altogether?!
226         if (!action->statusTip().isEmpty())
227             action->setToolTip(action->statusTip());
228
229         // show keyboard shortcuts in the status bar
230         if (!action->shortcut().isEmpty())
231             action->setStatusTip(action->statusTip() + " (" + action->shortcut().toString(QKeySequence::NativeText) + ")");
232
233         // make the actions work when video is fullscreen
234         action->setShortcutContext(Qt::ApplicationShortcut);
235
236         // no icons in menus
237         action->setIconVisibleInMenu(false);
238
239     }
240
241 }
242
243 void MainWindow::createMenus() {
244
245     QMap<QString, QMenu*> *menus = The::globalMenus();
246
247     /*
248     fileMenu = menuBar()->addMenu(tr("&Application"));
249     // menus->insert("file", fileMenu);
250     // fileMenu->addAction(settingsAct);
251     fileMenu->addSeparator();
252     fileMenu->addAction(quitAct);
253     */
254
255     playlistMenu = menuBar()->addMenu(tr("&Playlist"));
256     menus->insert("playlist", playlistMenu);
257     playlistMenu->addAction(removeAct);
258     playlistMenu->addSeparator();
259     playlistMenu->addAction(moveUpAct);
260     playlistMenu->addAction(moveDownAct);
261
262     viewMenu = menuBar()->addMenu(tr("&Video"));
263     menus->insert("video", viewMenu);
264     // viewMenu->addAction(backAct);
265     viewMenu->addAction(stopAct);
266     viewMenu->addAction(pauseAct);
267     viewMenu->addAction(skipAct);
268     viewMenu->addSeparator();
269     viewMenu->addAction(webPageAct);
270     viewMenu->addSeparator();
271     // viewMenu->addAction(downloadAct);
272     viewMenu->addAction(compactViewAct);
273     viewMenu->addAction(fullscreenAct);
274
275     helpMenu = menuBar()->addMenu(tr("&Help"));
276     helpMenu->addAction(siteAct);
277     helpMenu->addAction(donateAct);
278     helpMenu->addAction(aboutAct);
279 }
280
281 void MainWindow::createToolBars() {
282
283     mainToolBar = new QToolBar(this);
284     mainToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
285     mainToolBar->setFloatable(false);
286     mainToolBar->setMovable(false);
287
288     QFont smallerFont;
289     smallerFont.setPointSize(smallerFont.pointSize()*.85);
290     QFontInfo fontInfo(smallerFont);
291     if (fontInfo.pixelSize() < 10) {
292         smallerFont.setPixelSize(10);
293     }
294     mainToolBar->setFont(smallerFont);
295
296     mainToolBar->setIconSize(QSize(32,32));
297     // mainToolBar->addAction(backAct);
298     mainToolBar->addAction(stopAct);
299     mainToolBar->addAction(pauseAct);
300     mainToolBar->addAction(skipAct);
301     mainToolBar->addAction(fullscreenAct);
302
303     seekSlider = new Phonon::SeekSlider(this);
304     seekSlider->setIconVisible(false);
305     Spacer *seekSliderSpacer = new Spacer(mainToolBar, seekSlider);
306     seekSliderSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
307     mainToolBar->addWidget(seekSliderSpacer);
308
309     volumeSlider = new Phonon::VolumeSlider(this);
310     // qDebug() << volumeSlider->children();
311     // status tip for the volume slider
312     QSlider* volumeQSlider = volumeSlider->findChild<QSlider*>();
313     if (volumeQSlider)
314         volumeQSlider->setStatusTip(tr("Press %1 to raise the volume, %2 to lower it").arg(
315                 volumeUpAct->shortcut().toString(QKeySequence::NativeText), volumeDownAct->shortcut().toString(QKeySequence::NativeText)));
316     // status tip for the mute button
317     QToolButton* muteToolButton = volumeSlider->findChild<QToolButton*>();
318     if (muteToolButton)
319         muteToolButton->setStatusTip(volumeMuteAct->statusTip());
320     // this makes the volume slider smaller
321     volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
322     mainToolBar->addWidget(new Spacer(mainToolBar, volumeSlider));
323
324     toolbarSearch->setStatusTip(searchFocusAct->statusTip());
325     mainToolBar->addWidget(new Spacer(mainToolBar, toolbarSearch));
326
327     addToolBar(mainToolBar);
328 }
329
330 void MainWindow::createStatusBar() {
331     currentTime = new QLabel(this);
332     statusBar()->addPermanentWidget(currentTime);
333
334     totalTime = new QLabel(this);
335     statusBar()->addPermanentWidget(totalTime);
336
337     // remove ugly borders on OSX
338     // and remove some excessive padding
339     statusBar()->setStyleSheet("::item{border:0 solid} QStatusBar, QToolBar {padding:0;margin:0} QToolButton {padding:1px}");
340
341     QToolBar *toolBar = new QToolBar(this);
342     int iconHeight = 24; // statusBar()->height();
343     int iconWidth = 36; // iconHeight * 3 / 2;
344     toolBar->setIconSize(QSize(iconWidth, iconHeight));
345     toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
346     toolBar->addAction(The::globalActions()->value("hd"));
347     statusBar()->addPermanentWidget(toolBar);
348
349     statusBar()->show();
350 }
351
352 void MainWindow::readSettings() {
353     QSettings settings;
354     restoreGeometry(settings.value("geometry").toByteArray());
355     The::globalActions()->value("hd")->setChecked(settings.value("hd").toBool());
356 }
357
358 void MainWindow::writeSettings() {
359     // do not save geometry when in full screen
360     if (m_fullscreen)
361         return;
362     QSettings settings;
363     settings.setValue("geometry", saveGeometry());
364     mediaView->saveSplitterState();
365 }
366
367 void MainWindow::goBack() {
368     if ( history->size() > 1 ) {
369         history->pop();
370         QWidget *widget = history->pop();
371         showWidget(widget);
372     }
373 }
374
375 void MainWindow::showWidget ( QWidget* widget ) {
376
377     setUpdatesEnabled(false);
378
379     // call hide method on the current view
380     View* oldView = dynamic_cast<View *> (views->currentWidget());
381     if (oldView != NULL) {
382         oldView->disappear();
383     }
384
385     // call show method on the new view
386     View* newView = dynamic_cast<View *> (widget);
387     if (newView != NULL) {
388         newView->appear();
389         QMap<QString,QVariant> metadata = newView->metadata();
390         QString windowTitle = metadata.value("title").toString();
391         if (windowTitle.length())
392             windowTitle += " - ";
393         setWindowTitle(windowTitle + Constants::APP_NAME);
394         statusBar()->showMessage((metadata.value("description").toString()));
395
396     }
397
398     // backAct->setEnabled(history->size() > 1);
399     // settingsAct->setEnabled(widget != settingsView);
400     stopAct->setEnabled(widget == mediaView);
401     fullscreenAct->setEnabled(widget == mediaView);
402     compactViewAct->setEnabled(widget == mediaView);
403     webPageAct->setEnabled(widget == mediaView);
404     aboutAct->setEnabled(widget != aboutView);
405
406     /*
407     // this is not the best place to enable downloads, but the user is informed
408     // if there really is no video is playing
409     downloadAct->setEnabled(widget == mediaView);
410     */
411
412     // cool toolbar on the Mac
413     // setUnifiedTitleAndToolBarOnMac(widget == mediaView);
414
415     // toolbar only for the mediaView
416     mainToolBar->setVisible(widget == mediaView && !compactViewAct->isChecked());
417
418     setUpdatesEnabled(true);
419
420     history->push(widget);
421
422 #ifdef Q_WS_MAC
423     // crossfade only on OSX
424     // where we can be sure of video performance
425     fadeInWidget(views->currentWidget(), widget);
426 #endif
427
428     views->setCurrentWidget(widget);
429
430 }
431
432 void MainWindow::fadeInWidget(QWidget *oldWidget, QWidget *newWidget) {
433     if (faderWidget) faderWidget->close();
434     if (oldWidget == mediaView || newWidget == mediaView) return;
435     QPixmap frozenView = QPixmap::grabWidget(oldWidget);
436     faderWidget = new FaderWidget(newWidget);
437     faderWidget->start(frozenView);
438 }
439
440 void MainWindow::about() {
441     if (!aboutView) {
442         aboutView = new AboutView(this);
443         views->addWidget(aboutView);
444     }
445     showWidget(aboutView);
446 }
447
448 void MainWindow::visitSite() {
449     QUrl url(Constants::WEBSITE);
450     statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
451     QDesktopServices::openUrl(url);
452 }
453
454 void MainWindow::donate() {
455     QUrl url(QString(Constants::WEBSITE) + "#donate");
456     statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
457     QDesktopServices::openUrl(url);
458 }
459
460 void MainWindow::quit() {
461     writeSettings();
462     qApp->quit();
463 }
464
465 void MainWindow::closeEvent(QCloseEvent *event) {
466     quit();
467     QWidget::closeEvent(event);
468 }
469
470 void MainWindow::showSettings() {
471     if (!settingsView) {
472         settingsView = new SettingsView(this);
473         views->addWidget(settingsView);
474     }
475     showWidget(settingsView);
476 }
477
478 void MainWindow::showSearch() {
479     showWidget(searchView);
480     currentTime->clear();
481     totalTime->clear();
482 }
483
484 void MainWindow::showMedia(QString query) {
485     initPhonon();
486     mediaView->setMediaObject(mediaObject);
487     SearchParams *searchParams = new SearchParams();
488     searchParams->setKeywords(query);
489     mediaView->search(searchParams);
490     showWidget(mediaView);
491 }
492
493 void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState */) {
494
495     // qDebug() << "Phonon state: " << newState;
496
497     switch (newState) {
498
499          case Phonon::ErrorState:
500         if (mediaObject->errorType() == Phonon::FatalError) {
501             statusBar()->showMessage(tr("Fatal error: %1").arg(mediaObject->errorString()));
502         } else {
503             statusBar()->showMessage(tr("Error: %1").arg(mediaObject->errorString()));
504         }
505         break;
506
507          case Phonon::PlayingState:
508         pauseAct->setEnabled(true);
509         pauseAct->setIcon(QtIconLoader::icon("media-playback-pause", QIcon(":/images/pause.png")));
510         pauseAct->setText(tr("&Pause"));
511         pauseAct->setStatusTip(tr("Pause playback") + " (" +  pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
512         skipAct->setEnabled(true);
513         break;
514
515          case Phonon::StoppedState:
516         pauseAct->setEnabled(false);
517         skipAct->setEnabled(false);
518         break;
519
520          case Phonon::PausedState:
521         skipAct->setEnabled(true);
522         pauseAct->setEnabled(true);
523         pauseAct->setIcon(QtIconLoader::icon("media-playback-start", QIcon(":/images/play.png")));
524         pauseAct->setText(tr("&Play"));
525         pauseAct->setStatusTip(tr("Resume playback") + " (" +  pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
526         break;
527
528          case Phonon::BufferingState:
529          case Phonon::LoadingState:
530         skipAct->setEnabled(true);
531         pauseAct->setEnabled(false);
532         currentTime->clear();
533         totalTime->clear();
534         break;
535
536          default:
537         ;
538     }
539 }
540
541 void MainWindow::stop() {
542     mediaView->stop();
543     showSearch();
544 }
545
546 void MainWindow::fullscreen() {
547
548     setUpdatesEnabled(false);
549
550     if (m_fullscreen) {
551         // use setShortucs instead of setShortcut
552         // the latter seems not to work
553         fullscreenAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::ALT + Qt::Key_Return));
554         fullscreenAct->setText(tr("&Full Screen"));
555         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
556         if (m_maximized) showMaximized();
557         else showNormal();
558         // Make sure the window has focus (Mac)
559         activateWindow();
560     } else {
561         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
562         fullscreenAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::ALT + Qt::Key_Return));
563         fullscreenAct->setText(tr("Exit &Full Screen"));
564         m_maximized = isMaximized();
565
566         // save geometry now, if the user quits when in full screen
567         // geometry won't be saved
568         writeSettings();
569
570         showFullScreen();
571     }
572
573     // No compact view action when in full screen
574     compactViewAct->setVisible(m_fullscreen);
575     // Also no Youtube action since it opens a new window
576     webPageAct->setVisible(m_fullscreen);
577
578     // Hide anything but the video
579     mediaView->setPlaylistVisible(m_fullscreen);
580     mainToolBar->setVisible(m_fullscreen);
581     statusBar()->setVisible(m_fullscreen);
582     menuBar()->setVisible(m_fullscreen);
583
584     // workaround: prevent focus on the search bar
585     // it steals the Space key needed for Play/Pause
586     mainToolBar->setEnabled(m_fullscreen);
587
588     m_fullscreen = !m_fullscreen;
589
590     setUpdatesEnabled(true);
591 }
592
593 void MainWindow::compactView(bool enable) {
594
595     setUpdatesEnabled(false);
596
597     // setUnifiedTitleAndToolBarOnMac(!enable);
598     mediaView->setPlaylistVisible(!enable);
599     mainToolBar->setVisible(!enable);
600     statusBar()->setVisible(!enable);
601
602
603 #ifndef Q_WS_MAC
604     menuBar()->setVisible(!enable);
605 #endif
606
607     // ensure focus does not end up to the search box
608     // as it would steal the Space shortcut
609     toolbarSearch->setEnabled(!enable);
610
611     if (enable) {
612         stopAct->setShortcut(QString(""));
613         QList<QKeySequence> shortcuts;
614         // for some reason it is important that ESC comes first
615         shortcuts << QKeySequence(Qt::CTRL + Qt::Key_Return) << QKeySequence(Qt::Key_Escape);
616         compactViewAct->setShortcuts(shortcuts);
617     } else {
618         compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return));
619         stopAct->setShortcut(QKeySequence(Qt::Key_Escape));
620     }
621
622     setUpdatesEnabled(true);
623 }
624
625 void MainWindow::searchFocus() {
626     QWidget *view = views->currentWidget();
627     if (view == mediaView) {
628         toolbarSearch->setFocus();
629     }
630 }
631
632 void MainWindow::initPhonon() {
633     // Phonon initialization
634     if (mediaObject) delete mediaObject;
635     if (audioOutput) delete audioOutput;
636     mediaObject = new Phonon::MediaObject(this);
637     mediaObject->setTickInterval(100);
638     connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
639             this, SLOT(stateChanged(Phonon::State, Phonon::State)));
640     connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64)));
641     connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64)));
642     seekSlider->setMediaObject(mediaObject);
643     audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this);
644     connect(audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(volumeChanged(qreal)));
645     connect(audioOutput, SIGNAL(mutedChanged(bool)), this, SLOT(volumeMutedChanged(bool)));
646     volumeSlider->setAudioOutput(audioOutput);
647     Phonon::createPath(mediaObject, audioOutput);
648 }
649
650 void MainWindow::tick(qint64 time) {
651     if (time <= 0) {
652         currentTime->clear();
653         return;
654     }
655     QTime displayTime(0, (time / 60000) % 60, (time / 1000) % 60);
656     currentTime->setText(displayTime.toString("mm:ss"));
657
658     // remaining time tooltip
659     int remainingTimeInt = mediaObject->remainingTime();
660     QTime remainingTime(0, (remainingTimeInt / 60000) % 60, (remainingTimeInt / 1000) % 60);
661     currentTime->setStatusTip(tr("Remaining time: %1").arg(remainingTime.toString("mm:ss")));
662
663     // qDebug() << "currentTime" << time << displayTime.toString("mm:ss");
664 }
665
666 void MainWindow::totalTimeChanged(qint64 time) {
667     if (time <= 0) {
668         totalTime->clear();
669         return;
670     }
671     QTime displayTime(0, (time / 60000) % 60, (time / 1000) % 60);
672     totalTime->setText(displayTime.toString("/ mm:ss"));
673     // qDebug() << "totalTime" << time << displayTime.toString("mm:ss");
674 }
675
676 void MainWindow::volumeUp() {
677     qreal newVolume = volumeSlider->audioOutput()->volume() + .1;
678     if (newVolume > volumeSlider->maximumVolume())
679         newVolume = volumeSlider->maximumVolume();
680     volumeSlider->audioOutput()->setVolume(newVolume);
681 }
682
683 void MainWindow::volumeDown() {
684     qreal newVolume = volumeSlider->audioOutput()->volume() - .1;
685     if (newVolume < 0)
686         newVolume = 0;
687     volumeSlider->audioOutput()->setVolume(newVolume);
688 }
689
690 void MainWindow::volumeMute() {
691     volumeSlider->audioOutput()->setMuted(!volumeSlider->audioOutput()->isMuted());
692 }
693
694 void MainWindow::volumeChanged(qreal newVolume) {
695     // automatically unmute when volume changes
696     if (volumeSlider->audioOutput()->isMuted())
697         volumeSlider->audioOutput()->setMuted(false);
698     statusBar()->showMessage(tr("Volume at %1%").arg(newVolume*100));
699 }
700
701 void MainWindow::volumeMutedChanged(bool muted) {
702     if (muted)
703         statusBar()->showMessage(tr("Volume is muted"));
704     else
705         statusBar()->showMessage(tr("Volume is unmuted"));
706 }
707
708 /*
709 void MainWindow::abortDownload() {
710     QProgressDialog* dlg = dynamic_cast<QProgressDialog*>(this->sender());
711     QMap<QNetworkReply*, DownloadResource>::iterator cur;
712     QMap<QNetworkReply*, DownloadResource>::iterator end;
713     // locate the DownloadResource by its dialog address and trigger abortion
714     for(cur=m_downloads.begin(), end=m_downloads.end(); cur!=end; cur++){
715         if(cur.value().dialog == dlg) cur.key()->abort();
716     }
717 }
718
719 void MainWindow::download() {
720     if(mediaObject == NULL || mediaObject->currentSource().url().isEmpty()){
721         // complain unless video source apperas to be valid
722         QMessageBox::critical(this, tr("No Video playing"), tr("You must first play the video you intent to download !"));
723         return;
724     }
725     QString filename = QFileDialog::getSaveFileName(this,
726                                                     tr("Save video as..."),
727                                                     tr("minitube video.mp4"),
728                                                     "Video File(*.avi *.mp4)"
729                                                     );
730     if(!filename.isNull()) {
731         // open destination file and initialize download
732         DownloadResource res;
733         res.file = new QFile(filename);
734         if(res.file->open(QFile::WriteOnly) == true) {
735             res.dialog = new QProgressDialog(tr("Downloading: ") + res.file->fileName(),
736                                              tr("Abort Download"), 0, 100, this);
737             connect(res.dialog, SIGNAL(canceled()), this, SLOT(abortDownload()));
738             download(mediaObject->currentSource().url(), res);
739         }else{
740             QMessageBox::critical(this, tr("File creation failed"), res.file->errorString());
741             delete res.file;
742         }
743     }
744 }
745
746 void MainWindow::download(const QUrl& url, const DownloadResource& res) {
747     // create and store request and connect the reply signals
748     QNetworkReply *r = The::networkAccessManager()->get(QNetworkRequest(url));
749     m_downloads.insert(r, res);
750     connect(r, SIGNAL(finished()), this, SLOT(replyFinished()));
751     connect(r, SIGNAL(readyRead()), this, SLOT(replyReadyRead()));
752     connect(r, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError)));
753     connect(r, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(replyDownloadProgress(qint64,qint64)));
754     connect(r, SIGNAL(metaDataChanged()), this, SLOT(replyMetaDataChanged()));
755 }
756
757 void MainWindow::replyReadyRead() {
758     QNetworkReply* r = dynamic_cast<QNetworkReply*>(this->sender());
759     m_downloads[r].file->write(r->readAll());
760 }
761
762 void MainWindow::replyDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
763     QNetworkReply* r = dynamic_cast<QNetworkReply*>(this->sender());
764     if (bytesTotal > 0 && bytesReceived >0)
765         m_downloads[r].dialog->setValue( double(100.0/bytesTotal)*bytesReceived );  // pssst :-X
766 }
767
768 void MainWindow::replyError(QNetworkReply::NetworkError code) {
769     QNetworkReply* r = dynamic_cast<QNetworkReply*>(this->sender());
770     QMessageBox::critical(this, tr("Download failed"), r->errorString());
771 }
772
773 void MainWindow::replyFinished() {
774     QNetworkReply* r = dynamic_cast<QNetworkReply*>(this->sender());
775     m_downloads[r].dialog->close();
776     m_downloads[r].file->close();
777     delete m_downloads[r].file;
778     m_downloads.remove(r);
779 }
780
781 void MainWindow::replyMetaDataChanged() {
782     QNetworkReply* r = dynamic_cast<QNetworkReply*>(this->sender());
783     QUrl url = r->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
784     if(url.isValid()) {
785         // redirect - request new url, but keep the resources
786         qDebug() << "redirecting to: " << url.toString();
787         download(url, m_downloads[r]);
788         m_downloads.remove(r);
789     }
790 }
791
792 */
793
794
795 QPixmap MainWindow::createHDPixmap(bool enabled) {
796     QPixmap pixmap = QPixmap(24,24);
797     pixmap.fill(Qt::transparent);
798     QPainter painter(&pixmap);
799     painter.setRenderHints(QPainter::Antialiasing, true);
800
801     QRect rect(0, 3, 24, 18);
802
803     QPen pen;
804     pen.setColor(Qt::black);
805     painter.setPen(pen);
806
807     if (enabled) {
808         QPalette palette;
809         painter.setBrush(palette.highlight());
810     } else {
811         QLinearGradient gradient(QPointF(0, 0), QPointF(0, rect.height() / 2));
812         gradient.setColorAt(0, QColor(0x6d, 0x6d, 0x6d));
813         gradient.setColorAt(1, QColor(0x25, 0x25, 0x25));
814         painter.setBrush(QBrush(gradient));
815     }
816     painter.drawRoundedRect(rect, 5, 5);
817
818     if (enabled) {
819         pen.setColor(Qt::white);
820     } else {
821         QPalette palette;
822         pen.setColor(palette.highlightedText().color());
823     }
824     painter.setPen(pen);
825
826     QFont font;
827     font.setPixelSize(12);
828     font.setBold(true);
829     painter.setFont(font);
830     painter.drawText(rect, Qt::AlignCenter, "HD");
831
832     return pixmap;
833 }
834
835 static QIcon hdOnIcon;
836 static QIcon hdOffIcon;
837
838 QIcon MainWindow::createHDIcon() {
839     // QIcon icon;
840     hdOffIcon.addPixmap(createHDPixmap(false));
841     hdOnIcon.addPixmap(createHDPixmap(true));
842     return hdOffIcon;
843 }
844
845 void MainWindow::saveHdSetting(bool enabled) {
846     QSettings settings;
847     settings.setValue("hd", enabled);
848     QAction *hdAct = The::globalActions()->value("hd");
849     if (enabled) {
850         hdAct->setStatusTip(tr("High Definition video is enabled") + " (" +  hdAct->shortcut().toString(QKeySequence::NativeText) + ")");
851     } else {
852         hdAct->setStatusTip(tr("High Definition video is not enabled") + " (" +  hdAct->shortcut().toString(QKeySequence::NativeText) + ")");
853     }
854 }
855
856 void MainWindow::hdIndicator(bool isHd) {
857     QAction *hdAct = The::globalActions()->value("hd");
858     if (isHd) {
859         hdAct->setIcon(hdOnIcon);
860         hdAct->setToolTip(tr("The current video is in High Definition"));
861     } else {
862         hdAct->setIcon(hdOffIcon);
863         hdAct->setToolTip(tr("The current video is not in High Definition"));
864     }
865 }