downloadmanager.cpp
上传用户:huahtool
上传日期:2015-12-10
资源大小:1089k
文件大小:19k
源码类别:

浏览器

开发平台:

Visual C++

  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
  4. ** Contact: Qt Software Information (qt-info@nokia.com)
  5. **
  6. ** This file is part of the demonstration applications of the Qt Toolkit.
  7. **
  8. ** Commercial Usage
  9. ** Licensees holding valid Qt Commercial licenses may use this file in
  10. ** accordance with the Qt Commercial License Agreement provided with the
  11. ** Software or, alternatively, in accordance with the terms contained in
  12. ** a written agreement between you and Nokia.
  13. **
  14. **
  15. ** GNU General Public License Usage
  16. ** Alternatively, this file may be used under the terms of the GNU
  17. ** General Public License versions 2.0 or 3.0 as published by the Free
  18. ** Software Foundation and appearing in the file LICENSE.GPL included in
  19. ** the packaging of this file.  Please review the following information
  20. ** to ensure GNU General Public Licensing requirements will be met:
  21. ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
  22. ** http://www.gnu.org/copyleft/gpl.html.  In addition, as a special
  23. ** exception, Nokia gives you certain additional rights. These rights
  24. ** are described in the Nokia Qt GPL Exception version 1.3, included in
  25. ** the file GPL_EXCEPTION.txt in this package.
  26. **
  27. ** Qt for Windows(R) Licensees
  28. ** As a special exception, Nokia, as the sole copyright holder for Qt
  29. ** Designer, grants users of the Qt/Eclipse Integration plug-in the
  30. ** right for the Qt/Eclipse Integration to link to functionality
  31. ** provided by Qt Designer and its related libraries.
  32. **
  33. ** If you are unsure which license is appropriate for your use, please
  34. ** contact the sales department at qt-sales@nokia.com.
  35. **
  36. ****************************************************************************/
  37. #include "downloadmanager.h"
  38. #include "autosaver.h"
  39. #include "browserapplication.h"
  40. #include "networkaccessmanager.h"
  41. #include <math.h>
  42. #include <QtCore/QMetaEnum>
  43. #include <QtCore/QSettings>
  44. #include <QtGui/QDesktopServices>
  45. #include <QtGui/QFileDialog>
  46. #include <QtGui/QHeaderView>
  47. #include <QtGui/QFileIconProvider>
  48. #include <QtCore/QDebug>
  49. #include <QtWebKit/QWebSettings>
  50. /*!
  51.     DownloadItem is a widget that is displayed in the download manager list.
  52.     It moves the data from the QNetworkReply into the QFile as well
  53.     as update the information/progressbar and report errors.
  54.  */
  55. DownloadItem::DownloadItem(QNetworkReply *reply, bool requestFileName, QWidget *parent)
  56.     : QWidget(parent)
  57.     , m_reply(reply)
  58.     , m_requestFileName(requestFileName)
  59.     , m_bytesReceived(0)
  60. {
  61.     setupUi(this);
  62.     QPalette p = downloadInfoLabel->palette();
  63.     p.setColor(QPalette::Text, Qt::darkGray);
  64.     downloadInfoLabel->setPalette(p);
  65.     progressBar->setMaximum(0);
  66.     tryAgainButton->hide();
  67.     connect(stopButton, SIGNAL(clicked()), this, SLOT(stop()));
  68.     connect(openButton, SIGNAL(clicked()), this, SLOT(open()));
  69.     connect(tryAgainButton, SIGNAL(clicked()), this, SLOT(tryAgain()));
  70.     init();
  71. }
  72. void DownloadItem::init()
  73. {
  74.     if (!m_reply)
  75.         return;
  76.     // attach to the m_reply
  77.     m_url = m_reply->url();
  78.     m_reply->setParent(this);
  79.     connect(m_reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead()));
  80.     connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
  81.             this, SLOT(error(QNetworkReply::NetworkError)));
  82.     connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)),
  83.             this, SLOT(downloadProgress(qint64, qint64)));
  84.     connect(m_reply, SIGNAL(metaDataChanged()),
  85.             this, SLOT(metaDataChanged()));
  86.     connect(m_reply, SIGNAL(finished()),
  87.             this, SLOT(finished()));
  88.     // reset info
  89.     downloadInfoLabel->clear();
  90.     progressBar->setValue(0);
  91.     getFileName();
  92.     // start timer for the download estimation
  93.     m_downloadTime.start();
  94.     if (m_reply->error() != QNetworkReply::NoError) {
  95.         error(m_reply->error());
  96.         finished();
  97.     }
  98. }
  99. void DownloadItem::getFileName()
  100. {
  101.     QSettings settings;
  102.     settings.beginGroup(QLatin1String("downloadmanager"));
  103.     QString defaultLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
  104.     QString downloadDirectory = settings.value(QLatin1String("downloadDirectory"), defaultLocation).toString();
  105.     if (!downloadDirectory.isEmpty())
  106.         downloadDirectory += QLatin1Char('/');
  107.     QString defaultFileName = saveFileName(downloadDirectory);
  108.     QString fileName = defaultFileName;
  109.     if (m_requestFileName) {
  110.         fileName = QFileDialog::getSaveFileName(this, tr("Save File"), defaultFileName);
  111.         if (fileName.isEmpty()) {
  112.             m_reply->close();
  113.             fileNameLabel->setText(tr("Download canceled: %1").arg(QFileInfo(defaultFileName).fileName()));
  114.             return;
  115.         }
  116.     }
  117.     m_output.setFileName(fileName);
  118.     fileNameLabel->setText(QFileInfo(m_output.fileName()).fileName());
  119.     if (m_requestFileName)
  120.         downloadReadyRead();
  121. }
  122. QString DownloadItem::saveFileName(const QString &directory) const
  123. {
  124.     // Move this function into QNetworkReply to also get file name sent from the server
  125.     QString path = m_url.path();
  126.     QFileInfo info(path);
  127.     QString baseName = info.completeBaseName();
  128.     QString endName = info.suffix();
  129.     if (baseName.isEmpty()) {
  130.         baseName = QLatin1String("unnamed_download");
  131.         qDebug() << "DownloadManager:: downloading unknown file:" << m_url;
  132.     }
  133.     QString name = directory + baseName + QLatin1Char('.') + endName;
  134.     if (QFile::exists(name)) {
  135.         // already exists, don't overwrite
  136.         int i = 1;
  137.         do {
  138.             name = directory + baseName + QLatin1Char('-') + QString::number(i++) + QLatin1Char('.') + endName;
  139.         } while (QFile::exists(name));
  140.     }
  141.     return name;
  142. }
  143. void DownloadItem::stop()
  144. {
  145.     setUpdatesEnabled(false);
  146.     stopButton->setEnabled(false);
  147.     stopButton->hide();
  148.     tryAgainButton->setEnabled(true);
  149.     tryAgainButton->show();
  150.     setUpdatesEnabled(true);
  151.     m_reply->abort();
  152. }
  153. void DownloadItem::open()
  154. {
  155.     QFileInfo info(m_output);
  156.     QUrl url = QUrl::fromLocalFile(info.absolutePath());
  157.     QDesktopServices::openUrl(url);
  158. }
  159. void DownloadItem::tryAgain()
  160. {
  161.     if (!tryAgainButton->isEnabled())
  162.         return;
  163.     tryAgainButton->setEnabled(false);
  164.     tryAgainButton->setVisible(false);
  165.     stopButton->setEnabled(true);
  166.     stopButton->setVisible(true);
  167.     progressBar->setVisible(true);
  168.     QNetworkReply *r = BrowserApplication::networkAccessManager()->get(QNetworkRequest(m_url));
  169.     if (m_reply)
  170.         m_reply->deleteLater();
  171.     if (m_output.exists())
  172.         m_output.remove();
  173.     m_reply = r;
  174.     init();
  175.     emit statusChanged();
  176. }
  177. void DownloadItem::downloadReadyRead()
  178. {
  179.     if (m_requestFileName && m_output.fileName().isEmpty())
  180.         return;
  181.     if (!m_output.isOpen()) {
  182.         // in case someone else has already put a file there
  183.         if (!m_requestFileName)
  184.             getFileName();
  185.         if (!m_output.open(QIODevice::WriteOnly)) {
  186.             downloadInfoLabel->setText(tr("Error opening save file: %1")
  187.                     .arg(m_output.errorString()));
  188.             stopButton->click();
  189.             emit statusChanged();
  190.             return;
  191.         }
  192.         emit statusChanged();
  193.     }
  194.     if (-1 == m_output.write(m_reply->readAll())) {
  195.         downloadInfoLabel->setText(tr("Error saving: %1")
  196.                 .arg(m_output.errorString()));
  197.         stopButton->click();
  198.     }
  199. }
  200. void DownloadItem::error(QNetworkReply::NetworkError)
  201. {
  202.     qDebug() << "DownloadItem::error" << m_reply->errorString() << m_url;
  203.     downloadInfoLabel->setText(tr("Network Error: %1").arg(m_reply->errorString()));
  204.     tryAgainButton->setEnabled(true);
  205.     tryAgainButton->setVisible(true);
  206. }
  207. void DownloadItem::metaDataChanged()
  208. {
  209.     qDebug() << "DownloadItem::metaDataChanged: not handled.";
  210. }
  211. void DownloadItem::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
  212. {
  213.     m_bytesReceived = bytesReceived;
  214.     if (bytesTotal == -1) {
  215.         progressBar->setValue(0);
  216.         progressBar->setMaximum(0);
  217.     } else {
  218.         progressBar->setValue(bytesReceived);
  219.         progressBar->setMaximum(bytesTotal);
  220.     }
  221.     updateInfoLabel();
  222. }
  223. void DownloadItem::updateInfoLabel()
  224. {
  225.     if (m_reply->error() == QNetworkReply::NoError)
  226.         return;
  227.     qint64 bytesTotal = progressBar->maximum();
  228.     bool running = !downloadedSuccessfully();
  229.     // update info label
  230.     double speed = m_bytesReceived * 1000.0 / m_downloadTime.elapsed();
  231.     double timeRemaining = ((double)(bytesTotal - m_bytesReceived)) / speed;
  232.     QString timeRemainingString = tr("seconds");
  233.     if (timeRemaining > 60) {
  234.         timeRemaining = timeRemaining / 60;
  235.         timeRemainingString = tr("minutes");
  236.     }
  237.     timeRemaining = floor(timeRemaining);
  238.     // When downloading the eta should never be 0
  239.     if (timeRemaining == 0)
  240.         timeRemaining = 1;
  241.     QString info;
  242.     if (running) {
  243.         QString remaining;
  244.         if (bytesTotal != 0)
  245.             remaining = tr("- %4 %5 remaining")
  246.             .arg(timeRemaining)
  247.             .arg(timeRemainingString);
  248.         info = QString(tr("%1 of %2 (%3/sec) %4"))
  249.             .arg(dataString(m_bytesReceived))
  250.             .arg(bytesTotal == 0 ? tr("?") : dataString(bytesTotal))
  251.             .arg(dataString((int)speed))
  252.             .arg(remaining);
  253.     } else {
  254.         if (m_bytesReceived == bytesTotal)
  255.             info = dataString(m_output.size());
  256.         else
  257.             info = tr("%1 of %2 - Stopped")
  258.                 .arg(dataString(m_bytesReceived))
  259.                 .arg(dataString(bytesTotal));
  260.     }
  261.     downloadInfoLabel->setText(info);
  262. }
  263. QString DownloadItem::dataString(int size) const
  264. {
  265.     QString unit;
  266.     if (size < 1024) {
  267.         unit = tr("bytes");
  268.     } else if (size < 1024*1024) {
  269.         size /= 1024;
  270.         unit = tr("kB");
  271.     } else {
  272.         size /= 1024*1024;
  273.         unit = tr("MB");
  274.     }
  275.     return QString(QLatin1String("%1 %2")).arg(size).arg(unit);
  276. }
  277. bool DownloadItem::downloading() const
  278. {
  279.     return (progressBar->isVisible());
  280. }
  281. bool DownloadItem::downloadedSuccessfully() const
  282. {
  283.     return (stopButton->isHidden() && tryAgainButton->isHidden());
  284. }
  285. void DownloadItem::finished()
  286. {
  287.     progressBar->hide();
  288.     stopButton->setEnabled(false);
  289.     stopButton->hide();
  290.     m_output.close();
  291.     updateInfoLabel();
  292.     emit statusChanged();
  293. }
  294. /*!
  295.     DownloadManager is a Dialog that contains a list of DownloadItems
  296.     It is a basic download manager.  It only downloads the file, doesn't do BitTorrent,
  297.     extract zipped files or anything fancy.
  298.   */
  299. DownloadManager::DownloadManager(QWidget *parent)
  300.     : QDialog(parent)
  301.     , m_autoSaver(new AutoSaver(this))
  302.     , m_manager(BrowserApplication::networkAccessManager())
  303.     , m_iconProvider(0)
  304.     , m_removePolicy(Never)
  305. {
  306.     setupUi(this);
  307.     downloadsView->setShowGrid(false);
  308.     downloadsView->verticalHeader()->hide();
  309.     downloadsView->horizontalHeader()->hide();
  310.     downloadsView->setAlternatingRowColors(true);
  311.     downloadsView->horizontalHeader()->setStretchLastSection(true);
  312.     m_model = new DownloadModel(this);
  313.     downloadsView->setModel(m_model);
  314.     connect(cleanupButton, SIGNAL(clicked()), this, SLOT(cleanup()));
  315.     load();
  316. }
  317. DownloadManager::~DownloadManager()
  318. {
  319.     m_autoSaver->changeOccurred();
  320.     m_autoSaver->saveIfNeccessary();
  321.     if (m_iconProvider)
  322.         delete m_iconProvider;
  323. }
  324. int DownloadManager::activeDownloads() const
  325. {
  326.     int count = 0;
  327.     for (int i = 0; i < m_downloads.count(); ++i) {
  328.         if (m_downloads.at(i)->stopButton->isEnabled())
  329.             ++count;
  330.     }
  331.     return count;
  332. }
  333. void DownloadManager::download(const QNetworkRequest &request, bool requestFileName)
  334. {
  335.     if (request.url().isEmpty())
  336.         return;
  337.     handleUnsupportedContent(m_manager->get(request), requestFileName);
  338. }
  339. void DownloadManager::handleUnsupportedContent(QNetworkReply *reply, bool requestFileName)
  340. {
  341.     if (!reply || reply->url().isEmpty())
  342.         return;
  343.     QVariant header = reply->header(QNetworkRequest::ContentLengthHeader);
  344.     bool ok;
  345.     int size = header.toInt(&ok);
  346.     if (ok && size == 0)
  347.         return;
  348.     qDebug() << "DownloadManager::handleUnsupportedContent" << reply->url() << "requestFileName" << requestFileName;
  349.     DownloadItem *item = new DownloadItem(reply, requestFileName, this);
  350.     addItem(item);
  351. }
  352. void DownloadManager::addItem(DownloadItem *item)
  353. {
  354.     connect(item, SIGNAL(statusChanged()), this, SLOT(updateRow()));
  355.     int row = m_downloads.count();
  356.     m_model->beginInsertRows(QModelIndex(), row, row);
  357.     m_downloads.append(item);
  358.     m_model->endInsertRows();
  359.     updateItemCount();
  360.     if (row == 0)
  361.         show();
  362.     downloadsView->setIndexWidget(m_model->index(row, 0), item);
  363.     QIcon icon = style()->standardIcon(QStyle::SP_FileIcon);
  364.     item->fileIcon->setPixmap(icon.pixmap(48, 48));
  365.     downloadsView->setRowHeight(row, item->sizeHint().height());
  366. }
  367. void DownloadManager::updateRow()
  368. {
  369.     DownloadItem *item = qobject_cast<DownloadItem*>(sender());
  370.     int row = m_downloads.indexOf(item);
  371.     if (-1 == row)
  372.         return;
  373.     if (!m_iconProvider)
  374.         m_iconProvider = new QFileIconProvider();
  375.     QIcon icon = m_iconProvider->icon(item->m_output.fileName());
  376.     if (icon.isNull())
  377.         icon = style()->standardIcon(QStyle::SP_FileIcon);
  378.     item->fileIcon->setPixmap(icon.pixmap(48, 48));
  379.     downloadsView->setRowHeight(row, item->minimumSizeHint().height());
  380.     bool remove = false;
  381.     QWebSettings *globalSettings = QWebSettings::globalSettings();
  382.     if (!item->downloading()
  383.         && globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled))
  384.         remove = true;
  385.     if (item->downloadedSuccessfully()
  386.         && removePolicy() == DownloadManager::SuccessFullDownload) {
  387.         remove = true;
  388.     }
  389.     if (remove)
  390.         m_model->removeRow(row);
  391.     cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0);
  392. }
  393. DownloadManager::RemovePolicy DownloadManager::removePolicy() const
  394. {
  395.     return m_removePolicy;
  396. }
  397. void DownloadManager::setRemovePolicy(RemovePolicy policy)
  398. {
  399.     if (policy == m_removePolicy)
  400.         return;
  401.     m_removePolicy = policy;
  402.     m_autoSaver->changeOccurred();
  403. }
  404. void DownloadManager::save() const
  405. {
  406.     QSettings settings;
  407.     settings.beginGroup(QLatin1String("downloadmanager"));
  408.     QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy"));
  409.     settings.setValue(QLatin1String("removeDownloadsPolicy"), QLatin1String(removePolicyEnum.valueToKey(m_removePolicy)));
  410.     settings.setValue(QLatin1String("size"), size());
  411.     if (m_removePolicy == Exit)
  412.         return;
  413.     for (int i = 0; i < m_downloads.count(); ++i) {
  414.         QString key = QString(QLatin1String("download_%1_")).arg(i);
  415.         settings.setValue(key + QLatin1String("url"), m_downloads[i]->m_url);
  416.         settings.setValue(key + QLatin1String("location"), QFileInfo(m_downloads[i]->m_output).filePath());
  417.         settings.setValue(key + QLatin1String("done"), m_downloads[i]->downloadedSuccessfully());
  418.     }
  419.     int i = m_downloads.count();
  420.     QString key = QString(QLatin1String("download_%1_")).arg(i);
  421.     while (settings.contains(key + QLatin1String("url"))) {
  422.         settings.remove(key + QLatin1String("url"));
  423.         settings.remove(key + QLatin1String("location"));
  424.         settings.remove(key + QLatin1String("done"));
  425.         key = QString(QLatin1String("download_%1_")).arg(++i);
  426.     }
  427. }
  428. void DownloadManager::load()
  429. {
  430.     QSettings settings;
  431.     settings.beginGroup(QLatin1String("downloadmanager"));
  432.     QSize size = settings.value(QLatin1String("size")).toSize();
  433.     if (size.isValid())
  434.         resize(size);
  435.     QByteArray value = settings.value(QLatin1String("removeDownloadsPolicy"), QLatin1String("Never")).toByteArray();
  436.     QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy"));
  437.     m_removePolicy = removePolicyEnum.keyToValue(value) == -1 ?
  438.                         Never :
  439.                         static_cast<RemovePolicy>(removePolicyEnum.keyToValue(value));
  440.     int i = 0;
  441.     QString key = QString(QLatin1String("download_%1_")).arg(i);
  442.     while (settings.contains(key + QLatin1String("url"))) {
  443.         QUrl url = settings.value(key + QLatin1String("url")).toUrl();
  444.         QString fileName = settings.value(key + QLatin1String("location")).toString();
  445.         bool done = settings.value(key + QLatin1String("done"), true).toBool();
  446.         if (!url.isEmpty() && !fileName.isEmpty()) {
  447.             DownloadItem *item = new DownloadItem(0, this);
  448.             item->m_output.setFileName(fileName);
  449.             item->fileNameLabel->setText(QFileInfo(item->m_output.fileName()).fileName());
  450.             item->m_url = url;
  451.             item->stopButton->setVisible(false);
  452.             item->stopButton->setEnabled(false);
  453.             item->tryAgainButton->setVisible(!done);
  454.             item->tryAgainButton->setEnabled(!done);
  455.             item->progressBar->setVisible(!done);
  456.             addItem(item);
  457.         }
  458.         key = QString(QLatin1String("download_%1_")).arg(++i);
  459.     }
  460.     cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0);
  461. }
  462. void DownloadManager::cleanup()
  463. {
  464.     if (m_downloads.isEmpty())
  465.         return;
  466.     m_model->removeRows(0, m_downloads.count());
  467.     updateItemCount();
  468.     if (m_downloads.isEmpty() && m_iconProvider) {
  469.         delete m_iconProvider;
  470.         m_iconProvider = 0;
  471.     }
  472.     m_autoSaver->changeOccurred();
  473. }
  474. void DownloadManager::updateItemCount()
  475. {
  476.     int count = m_downloads.count();
  477.     itemCount->setText(count == 1 ? tr("1 Download") : tr("%1 Downloads").arg(count));
  478. }
  479. DownloadModel::DownloadModel(DownloadManager *downloadManager, QObject *parent)
  480.     : QAbstractListModel(parent)
  481.     , m_downloadManager(downloadManager)
  482. {
  483. }
  484. QVariant DownloadModel::data(const QModelIndex &index, int role) const
  485. {
  486.     if (index.row() < 0 || index.row() >= rowCount(index.parent()))
  487.         return QVariant();
  488.     if (role == Qt::ToolTipRole)
  489.         if (!m_downloadManager->m_downloads.at(index.row())->downloadedSuccessfully())
  490.             return m_downloadManager->m_downloads.at(index.row())->downloadInfoLabel->text();
  491.     return QVariant();
  492. }
  493. int DownloadModel::rowCount(const QModelIndex &parent) const
  494. {
  495.     return (parent.isValid()) ? 0 : m_downloadManager->m_downloads.count();
  496. }
  497. bool DownloadModel::removeRows(int row, int count, const QModelIndex &parent)
  498. {
  499.     if (parent.isValid())
  500.         return false;
  501.     int lastRow = row + count - 1;
  502.     for (int i = lastRow; i >= row; --i) {
  503.         if (m_downloadManager->m_downloads.at(i)->downloadedSuccessfully()
  504.             || m_downloadManager->m_downloads.at(i)->tryAgainButton->isEnabled()) {
  505.             beginRemoveRows(parent, i, i);
  506.             m_downloadManager->m_downloads.takeAt(i)->deleteLater();
  507.             endRemoveRows();
  508.         }
  509.     }
  510.     m_downloadManager->m_autoSaver->changeOccurred();
  511.     return true;
  512. }