]> git.sur5r.net Git - minitube/blob - src/MainWindow.cpp
simplified() instead of trimmed()
[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     // lazy initialized views
25     aboutView = 0;
26     settingsView = 0;
27
28     toolbarSearch = new SearchLineEdit(this);
29     toolbarSearch->setFont(qApp->font());
30     // toolbarSearch->setMinimumWidth(200);
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     // top dock widget
54     /*
55     QLabel* message = new QLabel(this);
56     message->setText(QString("A new version of %1 is available.").arg(Constants::APP_NAME));
57     message->setMargin(10);
58     message->setAlignment(Qt::AlignCenter);
59     QPalette palette;
60     message->setBackgroundRole(QPalette::ToolTipBase);
61     message->setForegroundRole(QPalette::ToolTipText);
62     message->setAutoFillBackground(true);
63     QDockWidget *dockWidget = new QDockWidget("", this, 0);
64     dockWidget->setTitleBarWidget(0);
65     dockWidget->setWidget(message);
66     dockWidget->setFeatures(QDockWidget::DockWidgetClosable);
67     addDockWidget(Qt::TopDockWidgetArea, dockWidget);
68     */
69
70 }
71
72 MainWindow::~MainWindow() {
73     delete history;
74 }
75
76 void MainWindow::createActions() {
77
78     QMap<QString, QAction*> *actions = The::globalActions();
79
80     /*
81     settingsAct = new QAction(tr("&Preferences..."), this);
82     settingsAct->setStatusTip(tr(QString("Configure ").append(Constants::APP_NAME).toUtf8()));
83     // Mac integration
84     settingsAct->setMenuRole(QAction::PreferencesRole);
85     actions->insert("settings", settingsAct);
86     connect(settingsAct, SIGNAL(triggered()), this, SLOT(showSettings()));
87     */
88     
89     backAct = new QAction(QIcon(":/images/go-previous.png"), tr("&Back"), this);
90     backAct->setEnabled(false);
91     backAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Left));
92     backAct->setStatusTip(tr("Go to the previous view"));
93     actions->insert("back", backAct);
94     connect(backAct, SIGNAL(triggered()), this, SLOT(goBack()));
95
96     stopAct = new QAction(QtIconLoader::icon("media-stop", QIcon(":/images/stop.png")), tr("&Stop"), this);
97     stopAct->setStatusTip(tr("Stop playback and go back to the search view"));
98     stopAct->setShortcut(QKeySequence(Qt::Key_Escape));
99     actions->insert("stop", stopAct);
100     connect(stopAct, SIGNAL(triggered()), this, SLOT(stop()));
101
102     skipAct = new QAction(QtIconLoader::icon("media-skip-forward", QIcon(":/images/skip.png")), tr("S&kip"), this);
103     skipAct->setStatusTip(tr("Skip to the next video"));
104     skipAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Right));
105     skipAct->setEnabled(false);
106     actions->insert("skip", skipAct);
107     connect(skipAct, SIGNAL(triggered()), mediaView, SLOT(skip()));
108
109     pauseAct = new QAction(QtIconLoader::icon("media-pause", QIcon(":/images/pause.png")), tr("&Pause"), this);
110     pauseAct->setStatusTip(tr("Pause playback"));
111     pauseAct->setShortcut(QKeySequence(Qt::Key_Space));
112     pauseAct->setEnabled(false);
113     actions->insert("pause", pauseAct);
114     connect(pauseAct, SIGNAL(triggered()), mediaView, SLOT(pause()));
115
116     fullscreenAct = new QAction(QtIconLoader::icon("view-fullscreen", QIcon(":/images/view-fullscreen.png")), tr("&Full Screen"), this);
117     fullscreenAct->setStatusTip(tr("Go full screen"));
118     fullscreenAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Return));
119     fullscreenAct->setShortcutContext(Qt::ApplicationShortcut);
120     actions->insert("fullscreen", fullscreenAct);
121     connect(fullscreenAct, SIGNAL(triggered()), this, SLOT(fullscreen()));
122
123     compactViewAct = new QAction(tr("&Compact mode"), this);
124     compactViewAct->setStatusTip(tr("Hide the playlist and the toolbar"));
125     compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return));
126     compactViewAct->setCheckable(true);
127     compactViewAct->setChecked(false);
128     compactViewAct->setEnabled(false);
129     actions->insert("compactView", compactViewAct);
130     connect(compactViewAct, SIGNAL(toggled(bool)), this, SLOT(compactView(bool)));
131
132     /*
133     // icon should be document-save but it is ugly
134     downloadAct = new QAction(QtIconLoader::icon("go-down", QIcon(":/images/go-down.png")), tr("&Download"), this);
135     downloadAct->setStatusTip(tr("Download this video"));
136     downloadAct->setShortcut(tr("Ctrl+S"));
137     actions.insert("download", downloadAct);
138     connect(downloadAct, SIGNAL(triggered()), this, SLOT(download()));
139     */
140
141     webPageAct = new QAction(QtIconLoader::icon("internet-web-browser", QIcon(":/images/internet-web-browser.png")), tr("&YouTube"), this);
142     webPageAct->setStatusTip(tr("Open the YouTube video page"));
143     webPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Y));
144     webPageAct->setEnabled(false);
145     actions->insert("webpage", webPageAct);
146     connect(webPageAct, SIGNAL(triggered()), mediaView, SLOT(openWebPage()));
147
148     removeAct = new QAction(tr("&Remove"), this);
149     removeAct->setStatusTip(tr("Remove the selected videos from the playlist"));
150     QList<QKeySequence> shortcuts;
151     shortcuts << QKeySequence("Del") << QKeySequence("Backspace");
152     removeAct->setShortcuts(shortcuts);
153     removeAct->setEnabled(false);
154     actions->insert("remove", removeAct);
155     connect(removeAct, SIGNAL(triggered()), mediaView, SLOT(removeSelected()));
156
157     moveUpAct = new QAction(QtIconLoader::icon("go-up", QIcon(":/images/go-up.png")), tr("Move &Up"), this);
158     moveUpAct->setStatusTip(tr("Move up the selected videos in the playlist"));
159     moveUpAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up));
160     moveUpAct->setEnabled(false);
161     actions->insert("moveUp", moveUpAct);
162     connect(moveUpAct, SIGNAL(triggered()), mediaView, SLOT(moveUpSelected()));
163
164     moveDownAct = new QAction(QtIconLoader::icon("go-down", QIcon(":/images/go-down.png")), tr("Move &Down"), this);
165     moveDownAct->setStatusTip(tr("Move down the selected videos in the playlist"));
166     moveDownAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down));
167     moveDownAct->setEnabled(false);
168     actions->insert("moveDown", moveDownAct);
169     connect(moveDownAct, SIGNAL(triggered()), mediaView, SLOT(moveDownSelected()));
170
171     quitAct = new QAction(tr("&Quit"), this);
172     quitAct->setMenuRole(QAction::QuitRole);
173     quitAct->setShortcut(tr("Ctrl+Q"));
174     quitAct->setStatusTip(tr("Bye"));
175     actions->insert("quit", quitAct);
176     connect(quitAct, SIGNAL(triggered()), this, SLOT(quit()));
177
178     siteAct = new QAction(tr("&Website"), this);
179     siteAct->setShortcut(QKeySequence::HelpContents);
180     siteAct->setStatusTip(tr("%1 on the Web").arg(Constants::APP_NAME));
181     actions->insert("site", siteAct);
182     connect(siteAct, SIGNAL(triggered()), this, SLOT(visitSite()));
183
184     donateAct = new QAction(tr("&Donate via PayPal"), this);
185     donateAct->setStatusTip(tr("Please support the continued development of %1").arg(Constants::APP_NAME));
186     actions->insert("donate", donateAct);
187     connect(donateAct, SIGNAL(triggered()), this, SLOT(donate()));
188
189     aboutAct = new QAction(tr("&About"), this);
190     aboutAct->setMenuRole(QAction::AboutRole);
191     aboutAct->setStatusTip(tr("Info about %1").arg(Constants::APP_NAME));
192     actions->insert("about", aboutAct);
193     connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
194
195     searchFocusAct = new QAction(tr("&Search"), this);
196     searchFocusAct->setShortcut(QKeySequence::Find);
197     actions->insert("search", searchFocusAct);
198     connect(searchFocusAct, SIGNAL(triggered()), this, SLOT(searchFocus()));
199     addAction(searchFocusAct);
200
201     // common action properties
202     foreach (QAction *action, actions->values()) {
203
204         // add actions to the MainWindow so that they work
205         // when the menu is hidden
206         addAction(action);
207
208         // never autorepeat.
209         // unexperienced users tend to keep keys pressed for a "long" time
210         action->setAutoRepeat(false);
211         action->setToolTip(action->statusTip());
212
213         // make the actions work when video is fullscreen
214         action->setShortcutContext(Qt::ApplicationShortcut);
215
216 #ifdef Q_WS_MAC
217         // OSX does not use icons in menus
218         action->setIconVisibleInMenu(false);
219 #endif
220
221     }
222
223 }
224
225 void MainWindow::createMenus() {
226
227     QMap<QString, QMenu*> *menus = The::globalMenus();
228
229     fileMenu = menuBar()->addMenu(tr("&Application"));
230     // menus->insert("file", fileMenu);
231     /*
232     fileMenu->addAction(settingsAct);
233     fileMenu->addSeparator();
234     */
235     fileMenu->addAction(quitAct);
236
237     playlistMenu = menuBar()->addMenu(tr("&Playlist"));
238     menus->insert("playlist", playlistMenu);
239     playlistMenu->addAction(removeAct);
240     playlistMenu->addSeparator();
241     playlistMenu->addAction(moveUpAct);
242     playlistMenu->addAction(moveDownAct);
243
244     viewMenu = menuBar()->addMenu(tr("&Video"));
245     menus->insert("video", viewMenu);
246     // viewMenu->addAction(backAct);
247     viewMenu->addAction(stopAct);
248     viewMenu->addAction(pauseAct);
249     viewMenu->addAction(skipAct);
250     viewMenu->addSeparator();
251     viewMenu->addAction(webPageAct);
252     viewMenu->addSeparator();
253     viewMenu->addAction(compactViewAct);
254     viewMenu->addAction(fullscreenAct);
255
256     helpMenu = menuBar()->addMenu(tr("&Help"));
257     helpMenu->addAction(siteAct);
258     helpMenu->addAction(donateAct);
259     helpMenu->addAction(aboutAct);
260 }
261
262 void MainWindow::createToolBars() {
263
264     setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
265
266     mainToolBar = new QToolBar(this);
267     mainToolBar->setFloatable(false);
268     mainToolBar->setMovable(false);
269
270     QFont smallerFont;
271     smallerFont.setPointSize(smallerFont.pointSize()*.85);
272     mainToolBar->setFont(smallerFont);
273
274     mainToolBar->setIconSize(QSize(32,32));
275     // mainToolBar->addAction(backAct);
276     mainToolBar->addAction(stopAct);
277     mainToolBar->addAction(pauseAct);
278     mainToolBar->addAction(skipAct);
279     mainToolBar->addAction(fullscreenAct);
280
281     seekSlider = new Phonon::SeekSlider(this);
282     seekSlider->setIconVisible(false);
283     Spacer *seekSliderSpacer = new Spacer(mainToolBar, seekSlider);
284     seekSliderSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
285     mainToolBar->addWidget(seekSliderSpacer);
286
287     volumeSlider = new Phonon::VolumeSlider(this);
288     // this makes the volume slider smaller
289     volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
290     mainToolBar->addWidget(new Spacer(mainToolBar, volumeSlider));
291
292     mainToolBar->addWidget(new Spacer(mainToolBar, toolbarSearch));
293
294     addToolBar(mainToolBar);
295 }
296
297 void MainWindow::createStatusBar() {
298     currentTime = new QLabel(this);
299     statusBar()->addPermanentWidget(currentTime);
300
301     totalTime = new QLabel(this);
302     statusBar()->addPermanentWidget(totalTime);
303
304     // remove ugly borders on OSX
305     statusBar()->setStyleSheet("::item{border:0 solid}");
306
307     statusBar()->show();
308 }
309
310 void MainWindow::readSettings() {
311     QSettings settings;
312     restoreGeometry(settings.value("geometry").toByteArray());
313 }
314
315 void MainWindow::writeSettings() {
316     // do not save geometry when in full screen
317     if (m_fullscreen)
318         return;
319     QSettings settings;
320     settings.setValue("geometry", saveGeometry());
321 }
322
323 void MainWindow::goBack() {
324     if ( history->size() > 1 ) {
325         history->pop();
326         QWidget *widget = history->pop();
327         showWidget(widget);
328     }
329 }
330
331 void MainWindow::showWidget ( QWidget* widget ) {
332
333     setUpdatesEnabled(false);
334
335     // call hide method on the current view
336     View* oldView = dynamic_cast<View *> (views->currentWidget());
337     if (oldView != NULL) {
338         oldView->disappear();
339     }
340
341     // call show method on the new view
342     View* newView = dynamic_cast<View *> (widget);
343     if (newView != NULL) {
344         newView->appear();
345         QMap<QString,QVariant> metadata = newView->metadata();
346         QString windowTitle = metadata.value("title").toString();
347         if (windowTitle.length())
348             windowTitle += " - ";
349         setWindowTitle(windowTitle + Constants::APP_NAME);
350         statusBar()->showMessage((metadata.value("description").toString()));
351
352     }
353
354     // backAct->setEnabled(history->size() > 1);
355     // settingsAct->setEnabled(widget != settingsView);
356     stopAct->setEnabled(widget == mediaView);
357     fullscreenAct->setEnabled(widget == mediaView);
358     compactViewAct->setEnabled(widget == mediaView);
359     webPageAct->setEnabled(widget == mediaView);
360     aboutAct->setEnabled(widget != aboutView);
361
362     // cool toolbar on the Mac
363     // setUnifiedTitleAndToolBarOnMac(widget == mediaView);
364
365     // toolbar only for the mediaView
366     mainToolBar->setVisible(widget == mediaView && !compactViewAct->isChecked());
367
368     history->push(widget);
369     fadeInWidget(views->currentWidget(), widget);
370     views->setCurrentWidget(widget);
371
372     setUpdatesEnabled(true);
373 }
374
375 void MainWindow::fadeInWidget(QWidget *oldWidget, QWidget *newWidget) {
376     if (faderWidget) faderWidget->close();
377     if (!oldWidget || !newWidget || oldWidget == mediaView || newWidget == mediaView) return;
378     QPixmap frozenView = QPixmap::grabWidget(oldWidget);
379     faderWidget = new FaderWidget(newWidget);
380     faderWidget->start(frozenView);
381 }
382
383 void MainWindow::about() {
384     if (!aboutView) {
385         aboutView = new AboutView(this);
386         views->addWidget(aboutView);
387     }
388     showWidget(aboutView);
389 }
390
391 void MainWindow::visitSite() {
392     QUrl url(Constants::WEBSITE);
393     statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
394     QDesktopServices::openUrl(url);
395 }
396
397 void MainWindow::donate() {
398     QUrl url(QString(Constants::WEBSITE) + "#donate");
399     statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
400     QDesktopServices::openUrl(url);
401 }
402
403 void MainWindow::quit() {
404     writeSettings();
405     qApp->quit();
406 }
407
408 void MainWindow::closeEvent(QCloseEvent *event) {
409     quit();
410     QWidget::closeEvent(event);
411 }
412
413 void MainWindow::showSettings() {
414     if (!settingsView) {
415         settingsView = new SettingsView(this);
416         views->addWidget(settingsView);
417     }
418     showWidget(settingsView);
419 }
420
421 void MainWindow::showSearch() {
422     showWidget(searchView);
423     currentTime->clear();
424     totalTime->clear();
425 }
426
427 void MainWindow::showMedia(QString query) {
428     initPhonon();
429     mediaView->setMediaObject(mediaObject);
430     SearchParams *searchParams = new SearchParams();
431     searchParams->setKeywords(query);
432     mediaView->search(searchParams);
433     showWidget(mediaView);
434 }
435
436 void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState */) {
437
438     // qDebug() << "Phonon state: " << newState;
439
440     switch (newState) {
441
442          case Phonon::ErrorState:
443         if (mediaObject->errorType() == Phonon::FatalError) {
444             statusBar()->showMessage(tr("Fatal error: %1").arg(mediaObject->errorString()));
445         } else {
446             statusBar()->showMessage(tr("Error: %1").arg(mediaObject->errorString()));
447         }
448         break;
449
450          case Phonon::PlayingState:
451         pauseAct->setEnabled(true);
452         pauseAct->setIcon(QtIconLoader::icon("media-pause", QIcon(":/images/pause.png")));
453         pauseAct->setText(tr("&Pause"));
454         pauseAct->setStatusTip(tr("Pause playback"));
455         skipAct->setEnabled(true);
456         break;
457
458          case Phonon::StoppedState:
459         pauseAct->setEnabled(false);
460         skipAct->setEnabled(false);
461         break;
462
463          case Phonon::PausedState:
464         skipAct->setEnabled(true);
465         pauseAct->setEnabled(true);
466         pauseAct->setIcon(QtIconLoader::icon("media-play", QIcon(":/images/play.png")));
467         pauseAct->setText(tr("&Play"));
468         pauseAct->setStatusTip(tr("Resume playback"));
469         break;
470
471          case Phonon::BufferingState:
472          case Phonon::LoadingState:
473         skipAct->setEnabled(true);
474         pauseAct->setEnabled(false);
475         currentTime->clear();
476         totalTime->clear();
477         break;
478
479          default:
480         ;
481     }
482 }
483
484 void MainWindow::stop() {
485     mediaView->stop();
486     showSearch();
487 }
488
489 void MainWindow::fullscreen() {
490
491     if (m_fullscreen) {
492         fullscreenAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Return));
493         fullscreenAct->setText(tr("&Full Screen"));
494         stopAct->setShortcut(QKeySequence(Qt::Key_Escape));
495         if (m_maximized) showMaximized();
496         else showNormal();
497     } else {
498         stopAct->setShortcut(QString(""));
499         QList<QKeySequence> shortcuts;
500         // for some reason it is important that ESC comes first
501         shortcuts << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::ALT + Qt::Key_Return);
502         fullscreenAct->setShortcuts(shortcuts);
503         fullscreenAct->setText(tr("Exit &Full Screen"));
504         m_maximized = isMaximized();
505
506         // save geometry now, if the user quits when in full screen
507         // geometry won't be saved
508         writeSettings();
509
510         showFullScreen();
511     }
512
513     // No compact view action when in full screen
514     compactViewAct->setVisible(m_fullscreen);
515
516     // Hide anything but the video
517     mediaView->setPlaylistVisible(m_fullscreen);
518     mainToolBar->setVisible(m_fullscreen);
519     statusBar()->setVisible(m_fullscreen);
520     menuBar()->setVisible(m_fullscreen);
521
522     m_fullscreen = !m_fullscreen;
523
524 }
525
526 void MainWindow::compactView(bool enable) {
527
528     // setUnifiedTitleAndToolBarOnMac(!enable);
529     mediaView->setPlaylistVisible(!enable);
530     mainToolBar->setVisible(!enable);
531     statusBar()->setVisible(!enable);
532
533     // ensure focus does not end up to the search box
534     // as it would steal the Space shortcut
535     toolbarSearch->setEnabled(!enable);
536
537     if (enable) {
538         stopAct->setShortcut(QString(""));
539         QList<QKeySequence> shortcuts;
540         // for some reason it is important that ESC comes first
541         shortcuts << QKeySequence(Qt::CTRL + Qt::Key_Return) << QKeySequence(Qt::Key_Escape);
542         compactViewAct->setShortcuts(shortcuts);
543     } else {
544         compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return));
545         stopAct->setShortcut(QKeySequence(Qt::Key_Escape));
546     }
547
548 }
549
550 void MainWindow::searchFocus() {
551     QWidget *view = views->currentWidget();
552     if (view == mediaView) {
553         toolbarSearch->setFocus();
554     }
555 }
556
557 void MainWindow::initPhonon() {
558     // Phonon initialization
559     if (mediaObject) delete mediaObject;
560     if (audioOutput) delete audioOutput;
561     mediaObject = new Phonon::MediaObject(this);
562     mediaObject->setTickInterval(100);
563     connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
564             this, SLOT(stateChanged(Phonon::State, Phonon::State)));
565     connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64)));
566     connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64)));
567     seekSlider->setMediaObject(mediaObject);
568     audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this);
569     volumeSlider->setAudioOutput(audioOutput);
570     Phonon::createPath(mediaObject, audioOutput);
571 }
572
573 void MainWindow::tick(qint64 time) {
574     QTime displayTime(0, (time / 60000) % 60, (time / 1000) % 60);
575     currentTime->setText(displayTime.toString("mm:ss"));
576     // qDebug() << "currentTime" << time << displayTime.toString("mm:ss");
577 }
578
579 void MainWindow::totalTimeChanged(qint64 time) {
580     if (time <= 0) {
581         totalTime->clear();
582         return;
583     }
584     QTime displayTime(0, (time / 60000) % 60, (time / 1000) % 60);
585     totalTime->setText(displayTime.toString("/ mm:ss"));
586     // qDebug() << "totalTime" << time << displayTime.toString("mm:ss");
587 }
588