]> git.sur5r.net Git - minitube/blobdiff - src/MainWindow.cpp
Fixed pause action status tip
[minitube] / src / MainWindow.cpp
index 1f9ed557833bb501210b2e18e15dbf26dfe0e2bf..a7621f19ab5785848048526a2384240cebb9d9bd 100755 (executable)
@@ -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() {
@@ -88,73 +69,81 @@ void MainWindow::createActions() {
     
     backAct = new QAction(QIcon(":/images/go-previous.png"), tr("&Back"), this);
     backAct->setEnabled(false);
-    backAct->setShortcut(tr("Alt+Left"));
+    backAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Left));
     backAct->setStatusTip(tr("Go to the previous view"));
     actions->insert("back", backAct);
     connect(backAct, SIGNAL(triggered()), this, SLOT(goBack()));
 
     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(tr("Esc"));
+    stopAct->setShortcuts(QList<QKeySequence>() << 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(tr("Ctrl+Right"));
+    skipAct->setShortcuts(QList<QKeySequence>() << 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(tr("Space"));
+    pauseAct->setShortcuts(QList<QKeySequence>() << 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(tr("Alt+Return"));
+    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<QKeySequence> shortcuts;
-    shortcuts << QKeySequence("Del") << QKeySequence("Backspace");
-    removeAct->setShortcuts(shortcuts);
+    removeAct->setShortcuts(QList<QKeySequence>() << QKeySequence("Del") << QKeySequence("Backspace"));
     removeAct->setEnabled(false);
     actions->insert("remove", removeAct);
     connect(removeAct, SIGNAL(triggered()), mediaView, SLOT(removeSelected()));
 
     moveUpAct = new QAction(QtIconLoader::icon("go-up", QIcon(":/images/go-up.png")), tr("Move &Up"), this);
     moveUpAct->setStatusTip(tr("Move up the selected videos in the playlist"));
-    moveUpAct->setShortcut(tr("Ctrl+Up"));
+    moveUpAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up));
     moveUpAct->setEnabled(false);
     actions->insert("moveUp", moveUpAct);
     connect(moveUpAct, SIGNAL(triggered()), mediaView, SLOT(moveUpSelected()));
 
     moveDownAct = new QAction(QtIconLoader::icon("go-down", QIcon(":/images/go-down.png")), tr("Move &Down"), this);
     moveDownAct->setStatusTip(tr("Move down the selected videos in the playlist"));
-    moveDownAct->setShortcut(tr("Ctrl+Down"));
+    moveDownAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down));
     moveDownAct->setEnabled(false);
     actions->insert("moveDown", moveDownAct);
     connect(moveDownAct, SIGNAL(triggered()), mediaView, SLOT(moveDownSelected()));
@@ -168,7 +157,7 @@ void MainWindow::createActions() {
 
     siteAct = new QAction(tr("&Website"), this);
     siteAct->setShortcut(QKeySequence::HelpContents);
-    siteAct->setStatusTip(tr("Minitube on the Web"));
+    siteAct->setStatusTip(tr("%1 on the Web").arg(Constants::APP_NAME));
     actions->insert("site", siteAct);
     connect(siteAct, SIGNAL(triggered()), this, SLOT(visitSite()));
 
@@ -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<View *> (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(tr("Alt+Return"));
+        // use setShortucs instead of setShortcut
+        // the latter seems not to work
+        fullscreenAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::ALT + Qt::Key_Return));
         fullscreenAct->setText(tr("&Full Screen"));
-        stopAct->setShortcut(tr("Esc"));
+        stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
+        if (m_maximized) showMaximized();
+        else showNormal();
     } else {
-        mediaView->fullscreen();
-        stopAct->setShortcut(tr(""));
-        fullscreenAct->setShortcut(tr("Esc"));
+        stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
+        fullscreenAct->setShortcuts(QList<QKeySequence>() << 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<QKeySequence> 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<QProgressDialog*>(this->sender());
+    QMap<QNetworkReply*, DownloadResource>::iterator cur;
+    QMap<QNetworkReply*, DownloadResource>::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<QNetworkReply*>(this->sender());
+    m_downloads[r].file->write(r->readAll());
+}
+
+void MainWindow::replyDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
+    QNetworkReply* r = dynamic_cast<QNetworkReply*>(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<QNetworkReply*>(this->sender());
+    QMessageBox::critical(this, tr("Download failed"), r->errorString());
+}
+
+void MainWindow::replyFinished() {
+    QNetworkReply* r = dynamic_cast<QNetworkReply*>(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<QNetworkReply*>(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);
+    }
+}
+
+*/