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