X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2FMainWindow.cpp;h=a7621f19ab5785848048526a2384240cebb9d9bd;hb=0817201dd8aa205f3532c3d850e63786b728e649;hp=00ce011497f91d16686b13f9b8e337cfc16c8baf;hpb=2f3954aea5a4a170cb5a541c83771b95cab1073e;p=minitube diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 00ce011..a7621f1 100755 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -21,13 +21,12 @@ MainWindow::MainWindow() { mediaView = new MediaView(this); views->addWidget(mediaView); - // lazy initialized views + // lazily initialized views aboutView = 0; settingsView = 0; toolbarSearch = new SearchLineEdit(this); toolbarSearch->setFont(qApp->font()); - // toolbarSearch->setMinimumWidth(200); connect(toolbarSearch, SIGNAL(search(const QString&)), searchView, SLOT(watch(const QString&))); // build ui @@ -49,24 +48,6 @@ MainWindow::MainWindow() { showWidget(searchView); setCentralWidget(views); - - // top dock widget - /* - QLabel* message = new QLabel(this); - message->setText(QString("A new version of %1 is available.").arg(Constants::APP_NAME)); - message->setMargin(10); - message->setAlignment(Qt::AlignCenter); - QPalette palette; - message->setBackgroundRole(QPalette::ToolTipBase); - message->setForegroundRole(QPalette::ToolTipText); - message->setAutoFillBackground(true); - QDockWidget *dockWidget = new QDockWidget("", this, 0); - dockWidget->setTitleBarWidget(0); - dockWidget->setWidget(message); - dockWidget->setFeatures(QDockWidget::DockWidgetClosable); - addDockWidget(Qt::TopDockWidgetArea, dockWidget); - */ - } MainWindow::~MainWindow() { @@ -95,52 +76,60 @@ void MainWindow::createActions() { stopAct = new QAction(QtIconLoader::icon("media-stop", QIcon(":/images/stop.png")), tr("&Stop"), this); stopAct->setStatusTip(tr("Stop playback and go back to the search view")); - stopAct->setShortcut(QKeySequence(Qt::Key_Escape)); + stopAct->setShortcuts(QList() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop)); actions->insert("stop", stopAct); connect(stopAct, SIGNAL(triggered()), this, SLOT(stop())); skipAct = new QAction(QtIconLoader::icon("media-skip-forward", QIcon(":/images/skip.png")), tr("S&kip"), this); skipAct->setStatusTip(tr("Skip to the next video")); - skipAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Right)); + skipAct->setShortcuts(QList() << QKeySequence(Qt::CTRL + Qt::Key_Right) << QKeySequence(Qt::Key_MediaNext)); skipAct->setEnabled(false); actions->insert("skip", skipAct); connect(skipAct, SIGNAL(triggered()), mediaView, SLOT(skip())); pauseAct = new QAction(QtIconLoader::icon("media-pause", QIcon(":/images/pause.png")), tr("&Pause"), this); pauseAct->setStatusTip(tr("Pause playback")); - pauseAct->setShortcut(QKeySequence(Qt::Key_Space)); + pauseAct->setShortcuts(QList() << QKeySequence(Qt::Key_Space) << QKeySequence(Qt::Key_MediaPlay)); pauseAct->setEnabled(false); actions->insert("pause", pauseAct); connect(pauseAct, SIGNAL(triggered()), mediaView, SLOT(pause())); fullscreenAct = new QAction(QtIconLoader::icon("view-fullscreen", QIcon(":/images/view-fullscreen.png")), tr("&Full Screen"), this); fullscreenAct->setStatusTip(tr("Go full screen")); - fullscreenAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Enter)); + fullscreenAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Return)); fullscreenAct->setShortcutContext(Qt::ApplicationShortcut); actions->insert("fullscreen", fullscreenAct); connect(fullscreenAct, SIGNAL(triggered()), this, SLOT(fullscreen())); + compactViewAct = new QAction(tr("&Compact mode"), this); + compactViewAct->setStatusTip(tr("Hide the playlist and the toolbar")); + compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return)); + compactViewAct->setCheckable(true); + compactViewAct->setChecked(false); + compactViewAct->setEnabled(false); + actions->insert("compactView", compactViewAct); + connect(compactViewAct, SIGNAL(toggled(bool)), this, SLOT(compactView(bool))); + /* // icon should be document-save but it is ugly downloadAct = new QAction(QtIconLoader::icon("go-down", QIcon(":/images/go-down.png")), tr("&Download"), this); downloadAct->setStatusTip(tr("Download this video")); downloadAct->setShortcut(tr("Ctrl+S")); - actions.insert("download", downloadAct); + downloadAct->setEnabled(false); + actions->insert("download", downloadAct); connect(downloadAct, SIGNAL(triggered()), this, SLOT(download())); */ webPageAct = new QAction(QtIconLoader::icon("internet-web-browser", QIcon(":/images/internet-web-browser.png")), tr("&YouTube"), this); webPageAct->setStatusTip(tr("Open the YouTube video page")); - webPageAct->setShortcut(tr("Ctrl+Y")); + webPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Y)); webPageAct->setEnabled(false); actions->insert("webpage", webPageAct); connect(webPageAct, SIGNAL(triggered()), mediaView, SLOT(openWebPage())); removeAct = new QAction(tr("&Remove"), this); removeAct->setStatusTip(tr("Remove the selected videos from the playlist")); - QList shortcuts; - shortcuts << QKeySequence("Del") << QKeySequence("Backspace"); - removeAct->setShortcuts(shortcuts); + removeAct->setShortcuts(QList() << QKeySequence("Del") << QKeySequence("Backspace")); removeAct->setEnabled(false); actions->insert("remove", removeAct); connect(removeAct, SIGNAL(triggered()), mediaView, SLOT(removeSelected())); @@ -183,19 +172,51 @@ void MainWindow::createActions() { actions->insert("about", aboutAct); connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); - searchFocusAct = new QAction(tr("&Search"), this); + // Invisible actions + + searchFocusAct = new QAction(this); searchFocusAct->setShortcut(QKeySequence::Find); actions->insert("search", searchFocusAct); connect(searchFocusAct, SIGNAL(triggered()), this, SLOT(searchFocus())); addAction(searchFocusAct); + volumeUpAct = new QAction(this); + volumeUpAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Plus)); + actions->insert("volume-up", volumeUpAct); + connect(volumeUpAct, SIGNAL(triggered()), this, SLOT(volumeUp())); + addAction(volumeUpAct); + + volumeDownAct = new QAction(this); + volumeDownAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Minus)); + actions->insert("volume-down", volumeDownAct); + connect(volumeDownAct, SIGNAL(triggered()), this, SLOT(volumeDown())); + addAction(volumeDownAct); + + volumeMuteAct = new QAction(this); + volumeMuteAct->setShortcut(tr("Ctrl+M")); + actions->insert("volume-mute", volumeMuteAct); + connect(volumeMuteAct, SIGNAL(triggered()), this, SLOT(volumeMute())); + addAction(volumeMuteAct); + // common action properties foreach (QAction *action, actions->values()) { + + // add actions to the MainWindow so that they work + // when the menu is hidden + addAction(action); + // never autorepeat. // unexperienced users tend to keep keys pressed for a "long" time action->setAutoRepeat(false); action->setToolTip(action->statusTip()); + // show keyboard shortcuts in the status bar + if (!action->shortcut().isEmpty()) + action->setStatusTip(action->statusTip() + " (" + action->shortcut().toString() + ")"); + + // make the actions work when video is fullscreen + action->setShortcutContext(Qt::ApplicationShortcut); + #ifdef Q_WS_MAC // OSX does not use icons in menus action->setIconVisibleInMenu(false); @@ -211,10 +232,8 @@ void MainWindow::createMenus() { fileMenu = menuBar()->addMenu(tr("&Application")); // menus->insert("file", fileMenu); - /* - fileMenu->addAction(settingsAct); + // fileMenu->addAction(settingsAct); fileMenu->addSeparator(); - */ fileMenu->addAction(quitAct); playlistMenu = menuBar()->addMenu(tr("&Playlist")); @@ -233,6 +252,8 @@ void MainWindow::createMenus() { viewMenu->addSeparator(); viewMenu->addAction(webPageAct); viewMenu->addSeparator(); + // viewMenu->addAction(downloadAct); + viewMenu->addAction(compactViewAct); viewMenu->addAction(fullscreenAct); helpMenu = menuBar()->addMenu(tr("&Help")); @@ -277,6 +298,15 @@ void MainWindow::createToolBars() { } void MainWindow::createStatusBar() { + currentTime = new QLabel(this); + statusBar()->addPermanentWidget(currentTime); + + totalTime = new QLabel(this); + statusBar()->addPermanentWidget(totalTime); + + // remove ugly borders on OSX + statusBar()->setStyleSheet("::item{border:0 solid}"); + statusBar()->show(); } @@ -286,6 +316,9 @@ void MainWindow::readSettings() { } void MainWindow::writeSettings() { + // do not save geometry when in full screen + if (m_fullscreen) + return; QSettings settings; settings.setValue("geometry", saveGeometry()); } @@ -300,6 +333,8 @@ void MainWindow::goBack() { void MainWindow::showWidget ( QWidget* widget ) { + setUpdatesEnabled(false); + // call hide method on the current view View* oldView = dynamic_cast (views->currentWidget()); if (oldView != NULL) { @@ -315,26 +350,41 @@ void MainWindow::showWidget ( QWidget* widget ) { if (windowTitle.length()) windowTitle += " - "; setWindowTitle(windowTitle + Constants::APP_NAME); - setStatusTip(metadata.value("description").toString()); + statusBar()->showMessage((metadata.value("description").toString())); + } // backAct->setEnabled(history->size() > 1); // settingsAct->setEnabled(widget != settingsView); stopAct->setEnabled(widget == mediaView); fullscreenAct->setEnabled(widget == mediaView); + compactViewAct->setEnabled(widget == mediaView); webPageAct->setEnabled(widget == mediaView); aboutAct->setEnabled(widget != aboutView); + /* + // this is not the best place to enable downloads, but the user is informed + // if there really is no video is playing + downloadAct->setEnabled(widget == mediaView); + */ + // cool toolbar on the Mac - setUnifiedTitleAndToolBarOnMac(widget == mediaView); + // setUnifiedTitleAndToolBarOnMac(widget == mediaView); // toolbar only for the mediaView - mainToolBar->setVisible(widget == mediaView); + mainToolBar->setVisible(widget == mediaView && !compactViewAct->isChecked()); history->push(widget); + +#ifdef Q_WS_MAC + // crossfade only on OSX + // where we can be sure of video performance fadeInWidget(views->currentWidget(), widget); +#endif + views->setCurrentWidget(widget); + setUpdatesEnabled(true); } void MainWindow::fadeInWidget(QWidget *oldWidget, QWidget *newWidget) { @@ -385,6 +435,8 @@ void MainWindow::showSettings() { void MainWindow::showSearch() { showWidget(searchView); + currentTime->clear(); + totalTime->clear(); } void MainWindow::showMedia(QString query) { @@ -404,9 +456,9 @@ void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState case Phonon::ErrorState: if (mediaObject->errorType() == Phonon::FatalError) { - setStatusTip("Something went REALLY wrong: " + mediaObject->errorString()); + statusBar()->showMessage(tr("Fatal error: %1").arg(mediaObject->errorString())); } else { - setStatusTip("Something went wrong: " + mediaObject->errorString()); + statusBar()->showMessage(tr("Error: %1").arg(mediaObject->errorString())); } break; @@ -414,7 +466,7 @@ void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState pauseAct->setEnabled(true); pauseAct->setIcon(QtIconLoader::icon("media-pause", QIcon(":/images/pause.png"))); pauseAct->setText(tr("&Pause")); - pauseAct->setStatusTip(tr("Pause playback")); + pauseAct->setStatusTip(tr("Pause playback") + " (" + pauseAct->shortcut().toString() + ")"); skipAct->setEnabled(true); break; @@ -428,13 +480,15 @@ void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState pauseAct->setEnabled(true); pauseAct->setIcon(QtIconLoader::icon("media-play", QIcon(":/images/play.png"))); pauseAct->setText(tr("&Play")); - pauseAct->setStatusTip(tr("Resume playback")); + pauseAct->setStatusTip(tr("Resume playback") + " (" + pauseAct->shortcut().toString() + ")"); break; case Phonon::BufferingState: case Phonon::LoadingState: skipAct->setEnabled(true); pauseAct->setEnabled(false); + currentTime->clear(); + totalTime->clear(); break; default: @@ -448,18 +502,71 @@ void MainWindow::stop() { } void MainWindow::fullscreen() { + + setUpdatesEnabled(false); + if (m_fullscreen) { - mediaView->exitFullscreen(); - fullscreenAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Enter)); + // use setShortucs instead of setShortcut + // the latter seems not to work + fullscreenAct->setShortcuts(QList() << QKeySequence(Qt::ALT + Qt::Key_Return)); fullscreenAct->setText(tr("&Full Screen")); - stopAct->setShortcut(QKeySequence(Qt::Key_Escape)); + stopAct->setShortcuts(QList() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop)); + if (m_maximized) showMaximized(); + else showNormal(); } else { - mediaView->fullscreen(); - stopAct->setShortcut(QString("")); - fullscreenAct->setShortcut(QKeySequence(Qt::Key_Escape)); + stopAct->setShortcuts(QList() << QKeySequence(Qt::Key_MediaStop)); + fullscreenAct->setShortcuts(QList() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::ALT + Qt::Key_Return)); fullscreenAct->setText(tr("Exit &Full Screen")); + m_maximized = isMaximized(); + + // save geometry now, if the user quits when in full screen + // geometry won't be saved + writeSettings(); + + showFullScreen(); } + + // No compact view action when in full screen + compactViewAct->setVisible(m_fullscreen); + // Also no Youtube action since it opens a new window + webPageAct->setVisible(m_fullscreen); + + // Hide anything but the video + mediaView->setPlaylistVisible(m_fullscreen); + mainToolBar->setVisible(m_fullscreen); + statusBar()->setVisible(m_fullscreen); + menuBar()->setVisible(m_fullscreen); + m_fullscreen = !m_fullscreen; + + setUpdatesEnabled(true); +} + +void MainWindow::compactView(bool enable) { + + setUpdatesEnabled(false); + + // setUnifiedTitleAndToolBarOnMac(!enable); + mediaView->setPlaylistVisible(!enable); + mainToolBar->setVisible(!enable); + statusBar()->setVisible(!enable); + + // ensure focus does not end up to the search box + // as it would steal the Space shortcut + toolbarSearch->setEnabled(!enable); + + if (enable) { + stopAct->setShortcut(QString("")); + QList shortcuts; + // for some reason it is important that ESC comes first + shortcuts << QKeySequence(Qt::CTRL + Qt::Key_Return) << QKeySequence(Qt::Key_Escape); + compactViewAct->setShortcuts(shortcuts); + } else { + compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return)); + stopAct->setShortcut(QKeySequence(Qt::Key_Escape)); + } + + setUpdatesEnabled(true); } void MainWindow::searchFocus() { @@ -474,10 +581,153 @@ void MainWindow::initPhonon() { if (mediaObject) delete mediaObject; if (audioOutput) delete audioOutput; mediaObject = new Phonon::MediaObject(this); + mediaObject->setTickInterval(100); connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)), this, SLOT(stateChanged(Phonon::State, Phonon::State))); + connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64))); + connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64))); seekSlider->setMediaObject(mediaObject); audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this); + connect(audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(volumeChanged(qreal))); + connect(audioOutput, SIGNAL(mutedChanged(bool)), this, SLOT(volumeMutedChanged(bool))); volumeSlider->setAudioOutput(audioOutput); Phonon::createPath(mediaObject, audioOutput); } + +void MainWindow::tick(qint64 time) { + if (time <= 0) { + currentTime->clear(); + return; + } + QTime displayTime(0, (time / 60000) % 60, (time / 1000) % 60); + currentTime->setText(displayTime.toString("mm:ss")); + // qDebug() << "currentTime" << time << displayTime.toString("mm:ss"); +} + +void MainWindow::totalTimeChanged(qint64 time) { + if (time <= 0) { + totalTime->clear(); + return; + } + QTime displayTime(0, (time / 60000) % 60, (time / 1000) % 60); + totalTime->setText(displayTime.toString("/ mm:ss")); + // qDebug() << "totalTime" << time << displayTime.toString("mm:ss"); +} + +void MainWindow::volumeUp() { + qreal newVolume = volumeSlider->audioOutput()->volume() + .1; + if (newVolume > volumeSlider->maximumVolume()) + newVolume = volumeSlider->maximumVolume(); + volumeSlider->audioOutput()->setVolume(newVolume); +} + +void MainWindow::volumeDown() { + qreal newVolume = volumeSlider->audioOutput()->volume() - .1; + if (newVolume < 0) + newVolume = 0; + volumeSlider->audioOutput()->setVolume(newVolume); +} + +void MainWindow::volumeMute() { + volumeSlider->audioOutput()->setMuted(!volumeSlider->audioOutput()->isMuted()); +} + +void MainWindow::volumeChanged(qreal newVolume) { + // automatically unmute when volume changes + if (volumeSlider->audioOutput()->isMuted()) + volumeSlider->audioOutput()->setMuted(false); + statusBar()->showMessage(tr("Volume at %1%").arg(newVolume*100)); +} + +void MainWindow::volumeMutedChanged(bool muted) { + if (muted) + statusBar()->showMessage(tr("Volume is muted")); + else + statusBar()->showMessage(tr("Volume is unmuted")); +} + +/* +void MainWindow::abortDownload() { + QProgressDialog* dlg = dynamic_cast(this->sender()); + QMap::iterator cur; + QMap::iterator end; + // locate the DownloadResource by its dialog address and trigger abortion + for(cur=m_downloads.begin(), end=m_downloads.end(); cur!=end; cur++){ + if(cur.value().dialog == dlg) cur.key()->abort(); + } +} + +void MainWindow::download() { + if(mediaObject == NULL || mediaObject->currentSource().url().isEmpty()){ + // complain unless video source apperas to be valid + QMessageBox::critical(this, tr("No Video playing"), tr("You must first play the video you intent to download !")); + return; + } + QString filename = QFileDialog::getSaveFileName(this, + tr("Save video as..."), + tr("minitube video.mp4"), + "Video File(*.avi *.mp4)" + ); + if(!filename.isNull()) { + // open destination file and initialize download + DownloadResource res; + res.file = new QFile(filename); + if(res.file->open(QFile::WriteOnly) == true) { + res.dialog = new QProgressDialog(tr("Downloading: ") + res.file->fileName(), + tr("Abort Download"), 0, 100, this); + connect(res.dialog, SIGNAL(canceled()), this, SLOT(abortDownload())); + download(mediaObject->currentSource().url(), res); + }else{ + QMessageBox::critical(this, tr("File creation failed"), res.file->errorString()); + delete res.file; + } + } +} + +void MainWindow::download(const QUrl& url, const DownloadResource& res) { + // create and store request and connect the reply signals + QNetworkReply *r = The::networkAccessManager()->get(QNetworkRequest(url)); + m_downloads.insert(r, res); + connect(r, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(r, SIGNAL(readyRead()), this, SLOT(replyReadyRead())); + connect(r, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError))); + connect(r, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(replyDownloadProgress(qint64,qint64))); + connect(r, SIGNAL(metaDataChanged()), this, SLOT(replyMetaDataChanged())); +} + +void MainWindow::replyReadyRead() { + QNetworkReply* r = dynamic_cast(this->sender()); + m_downloads[r].file->write(r->readAll()); +} + +void MainWindow::replyDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { + QNetworkReply* r = dynamic_cast(this->sender()); + if (bytesTotal > 0 && bytesReceived >0) + m_downloads[r].dialog->setValue( double(100.0/bytesTotal)*bytesReceived ); // pssst :-X +} + +void MainWindow::replyError(QNetworkReply::NetworkError code) { + QNetworkReply* r = dynamic_cast(this->sender()); + QMessageBox::critical(this, tr("Download failed"), r->errorString()); +} + +void MainWindow::replyFinished() { + QNetworkReply* r = dynamic_cast(this->sender()); + m_downloads[r].dialog->close(); + m_downloads[r].file->close(); + delete m_downloads[r].file; + m_downloads.remove(r); +} + +void MainWindow::replyMetaDataChanged() { + QNetworkReply* r = dynamic_cast(this->sender()); + QUrl url = r->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + if(url.isValid()) { + // redirect - request new url, but keep the resources + qDebug() << "redirecting to: " << url.toString(); + download(url, m_downloads[r]); + m_downloads.remove(r); + } +} + +*/