1 #include "MainWindow.h"
4 #include "iconloader/qticonloader.h"
7 MainWindow::MainWindow() {
14 history = new QStack<QWidget*>();
15 views = new QStackedWidget(this);
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);
24 // lazy initialized views
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&)));
39 // remove that useless menu/toolbar context menu
40 this->setContextMenuPolicy(Qt::NoContextMenu);
42 // mediaView init stuff thats needs actions
43 mediaView->initialize();
45 // restore window position
48 // show the initial view
49 showWidget(searchView);
51 setCentralWidget(views);
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);
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);
72 MainWindow::~MainWindow() {
76 void MainWindow::createActions() {
78 QMap<QString, QAction*> *actions = The::globalActions();
81 settingsAct = new QAction(tr("&Preferences..."), this);
82 settingsAct->setStatusTip(tr(QString("Configure ").append(Constants::APP_NAME).toUtf8()));
84 settingsAct->setMenuRole(QAction::PreferencesRole);
85 actions->insert("settings", settingsAct);
86 connect(settingsAct, SIGNAL(triggered()), this, SLOT(showSettings()));
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()));
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()));
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()));
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()));
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()));
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)));
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()));
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()));
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()));
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()));
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()));
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()));
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()));
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()));
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()));
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);
201 // common action properties
202 foreach (QAction *action, actions->values()) {
204 // add actions to the MainWindow so that they work
205 // when the menu is hidden
209 // unexperienced users tend to keep keys pressed for a "long" time
210 action->setAutoRepeat(false);
211 action->setToolTip(action->statusTip());
213 // make the actions work when video is fullscreen
214 action->setShortcutContext(Qt::ApplicationShortcut);
217 // OSX does not use icons in menus
218 action->setIconVisibleInMenu(false);
225 void MainWindow::createMenus() {
227 QMap<QString, QMenu*> *menus = The::globalMenus();
229 fileMenu = menuBar()->addMenu(tr("&Application"));
230 // menus->insert("file", fileMenu);
232 fileMenu->addAction(settingsAct);
233 fileMenu->addSeparator();
235 fileMenu->addAction(quitAct);
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);
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);
256 helpMenu = menuBar()->addMenu(tr("&Help"));
257 helpMenu->addAction(siteAct);
258 helpMenu->addAction(donateAct);
259 helpMenu->addAction(aboutAct);
262 void MainWindow::createToolBars() {
264 setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
266 mainToolBar = new QToolBar(this);
267 mainToolBar->setFloatable(false);
268 mainToolBar->setMovable(false);
271 smallerFont.setPointSize(smallerFont.pointSize()*.85);
272 mainToolBar->setFont(smallerFont);
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);
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);
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));
292 mainToolBar->addWidget(new Spacer(mainToolBar, toolbarSearch));
294 addToolBar(mainToolBar);
297 void MainWindow::createStatusBar() {
298 currentTime = new QLabel(this);
299 statusBar()->addPermanentWidget(currentTime);
301 totalTime = new QLabel(this);
302 statusBar()->addPermanentWidget(totalTime);
304 // remove ugly borders on OSX
305 statusBar()->setStyleSheet("::item{border:0 solid}");
310 void MainWindow::readSettings() {
312 restoreGeometry(settings.value("geometry").toByteArray());
315 void MainWindow::writeSettings() {
316 // do not save geometry when in full screen
320 settings.setValue("geometry", saveGeometry());
323 void MainWindow::goBack() {
324 if ( history->size() > 1 ) {
326 QWidget *widget = history->pop();
331 void MainWindow::showWidget ( QWidget* widget ) {
333 // call hide method on the current view
334 View* oldView = dynamic_cast<View *> (views->currentWidget());
335 if (oldView != NULL) {
336 oldView->disappear();
339 // call show method on the new view
340 View* newView = dynamic_cast<View *> (widget);
341 if (newView != NULL) {
343 QMap<QString,QVariant> metadata = newView->metadata();
344 QString windowTitle = metadata.value("title").toString();
345 if (windowTitle.length())
346 windowTitle += " - ";
347 setWindowTitle(windowTitle + Constants::APP_NAME);
348 statusBar()->showMessage((metadata.value("description").toString()));
352 // backAct->setEnabled(history->size() > 1);
353 // settingsAct->setEnabled(widget != settingsView);
354 stopAct->setEnabled(widget == mediaView);
355 fullscreenAct->setEnabled(widget == mediaView);
356 compactViewAct->setEnabled(widget == mediaView);
357 webPageAct->setEnabled(widget == mediaView);
358 aboutAct->setEnabled(widget != aboutView);
360 // cool toolbar on the Mac
361 // setUnifiedTitleAndToolBarOnMac(widget == mediaView);
363 // toolbar only for the mediaView
364 mainToolBar->setVisible(widget == mediaView && !compactViewAct->isChecked());
366 history->push(widget);
367 fadeInWidget(views->currentWidget(), widget);
368 views->setCurrentWidget(widget);
372 void MainWindow::fadeInWidget(QWidget *oldWidget, QWidget *newWidget) {
373 if (faderWidget) faderWidget->close();
374 if (!oldWidget || !newWidget || oldWidget == mediaView || newWidget == mediaView) return;
375 QPixmap frozenView = QPixmap::grabWidget(oldWidget);
376 faderWidget = new FaderWidget(newWidget);
377 faderWidget->start(frozenView);
380 void MainWindow::about() {
382 aboutView = new AboutView(this);
383 views->addWidget(aboutView);
385 showWidget(aboutView);
388 void MainWindow::visitSite() {
389 QUrl url(Constants::WEBSITE);
390 statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
391 QDesktopServices::openUrl(url);
394 void MainWindow::donate() {
395 QUrl url(QString(Constants::WEBSITE) + "#donate");
396 statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
397 QDesktopServices::openUrl(url);
400 void MainWindow::quit() {
405 void MainWindow::closeEvent(QCloseEvent *event) {
407 QWidget::closeEvent(event);
410 void MainWindow::showSettings() {
412 settingsView = new SettingsView(this);
413 views->addWidget(settingsView);
415 showWidget(settingsView);
418 void MainWindow::showSearch() {
419 showWidget(searchView);
420 currentTime->clear();
424 void MainWindow::showMedia(QString query) {
426 mediaView->setMediaObject(mediaObject);
427 SearchParams *searchParams = new SearchParams();
428 searchParams->setKeywords(query);
429 mediaView->search(searchParams);
430 showWidget(mediaView);
433 void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState */) {
435 // qDebug() << "Phonon state: " << newState;
439 case Phonon::ErrorState:
440 if (mediaObject->errorType() == Phonon::FatalError) {
441 statusBar()->showMessage(tr("Fatal error: %1").arg(mediaObject->errorString()));
443 statusBar()->showMessage(tr("Error: %1").arg(mediaObject->errorString()));
447 case Phonon::PlayingState:
448 pauseAct->setEnabled(true);
449 pauseAct->setIcon(QtIconLoader::icon("media-pause", QIcon(":/images/pause.png")));
450 pauseAct->setText(tr("&Pause"));
451 pauseAct->setStatusTip(tr("Pause playback"));
452 skipAct->setEnabled(true);
455 case Phonon::StoppedState:
456 pauseAct->setEnabled(false);
457 skipAct->setEnabled(false);
460 case Phonon::PausedState:
461 skipAct->setEnabled(true);
462 pauseAct->setEnabled(true);
463 pauseAct->setIcon(QtIconLoader::icon("media-play", QIcon(":/images/play.png")));
464 pauseAct->setText(tr("&Play"));
465 pauseAct->setStatusTip(tr("Resume playback"));
468 case Phonon::BufferingState:
469 case Phonon::LoadingState:
470 skipAct->setEnabled(true);
471 pauseAct->setEnabled(false);
472 currentTime->clear();
481 void MainWindow::stop() {
486 void MainWindow::fullscreen() {
489 fullscreenAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Return));
490 fullscreenAct->setText(tr("&Full Screen"));
491 stopAct->setShortcut(QKeySequence(Qt::Key_Escape));
492 if (m_maximized) showMaximized();
495 stopAct->setShortcut(QString(""));
496 QList<QKeySequence> shortcuts;
497 // for some reason it is important that ESC comes first
498 shortcuts << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::ALT + Qt::Key_Return);
499 fullscreenAct->setShortcuts(shortcuts);
500 fullscreenAct->setText(tr("Exit &Full Screen"));
501 m_maximized = isMaximized();
503 // save geometry now, if the user quits when in full screen
504 // geometry won't be saved
510 // No compact view action when in full screen
511 compactViewAct->setVisible(m_fullscreen);
513 // Hide anything but the video
514 mediaView->setPlaylistVisible(m_fullscreen);
515 mainToolBar->setVisible(m_fullscreen);
516 statusBar()->setVisible(m_fullscreen);
517 menuBar()->setVisible(m_fullscreen);
519 m_fullscreen = !m_fullscreen;
523 void MainWindow::compactView(bool enable) {
525 // setUnifiedTitleAndToolBarOnMac(!enable);
526 mediaView->setPlaylistVisible(!enable);
527 mainToolBar->setVisible(!enable);
528 statusBar()->setVisible(!enable);
530 // ensure focus does not end up to the search box
531 // as it would steal the Space shortcut
532 toolbarSearch->setEnabled(!enable);
535 stopAct->setShortcut(QString(""));
536 QList<QKeySequence> shortcuts;
537 // for some reason it is important that ESC comes first
538 shortcuts << QKeySequence(Qt::CTRL + Qt::Key_Return) << QKeySequence(Qt::Key_Escape);
539 compactViewAct->setShortcuts(shortcuts);
541 compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return));
542 stopAct->setShortcut(QKeySequence(Qt::Key_Escape));
547 void MainWindow::searchFocus() {
548 QWidget *view = views->currentWidget();
549 if (view == mediaView) {
550 toolbarSearch->setFocus();
554 void MainWindow::initPhonon() {
555 // Phonon initialization
556 if (mediaObject) delete mediaObject;
557 if (audioOutput) delete audioOutput;
558 mediaObject = new Phonon::MediaObject(this);
559 mediaObject->setTickInterval(100);
560 connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
561 this, SLOT(stateChanged(Phonon::State, Phonon::State)));
562 connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64)));
563 connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64)));
564 seekSlider->setMediaObject(mediaObject);
565 audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this);
566 volumeSlider->setAudioOutput(audioOutput);
567 Phonon::createPath(mediaObject, audioOutput);
570 void MainWindow::tick(qint64 time) {
571 QTime displayTime(0, (time / 60000) % 60, (time / 1000) % 60);
572 currentTime->setText(displayTime.toString("mm:ss"));
573 // qDebug() << "currentTime" << time << displayTime.toString("mm:ss");
576 void MainWindow::totalTimeChanged(qint64 time) {
581 QTime displayTime(0, (time / 60000) % 60, (time / 1000) % 60);
582 totalTime->setText(displayTime.toString("/ mm:ss"));
583 // qDebug() << "totalTime" << time << displayTime.toString("mm:ss");