File indexing completed on 2024-05-12 04:58:05

0001 /* ============================================================
0002 * Falkon - Qt web browser
0003 * Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com>
0004 *
0005 * This program is free software: you can redistribute it and/or modify
0006 * it under the terms of the GNU General Public License as published by
0007 * the Free Software Foundation, either version 3 of the License, or
0008 * (at your option) any later version.
0009 *
0010 * This program is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 * GNU General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU General Public License
0016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017 * ============================================================ */
0018 #include "downloaditem.h"
0019 #include "ui_downloaditem.h"
0020 #include "mainapplication.h"
0021 #include "browserwindow.h"
0022 #include "tabwidget.h"
0023 #include "webpage.h"
0024 #include "downloadmanager.h"
0025 #include "networkmanager.h"
0026 #include "qztools.h"
0027 #include "datapaths.h"
0028 
0029 #include <QMenu>
0030 #include <QClipboard>
0031 #include <QListWidgetItem>
0032 #include <QMouseEvent>
0033 #include <QTimer>
0034 #include <QFileInfo>
0035 #include <QMessageBox>
0036 #include <QFileIconProvider>
0037 #include <QDesktopServices>
0038 #include <QWebEngineDownloadRequest>
0039 #include <QtWebEngineWidgetsVersion>
0040 
0041 #ifdef Q_OS_WIN
0042 #include "Shlwapi.h"
0043 #include "shellapi.h"
0044 #endif
0045 
0046 //#define DOWNMANAGER_DEBUG
0047 
0048 DownloadItem::DownloadItem(QListWidgetItem *item, QWebEngineDownloadRequest* downloadItem, const QString &path, const QString &fileName, bool openFile, DownloadManager* manager)
0049     : QWidget()
0050     , ui(new Ui::DownloadItem)
0051     , m_item(item)
0052     , m_download(downloadItem)
0053     , m_path(path)
0054     , m_fileName(fileName)
0055     , m_downUrl(downloadItem->url())
0056     , m_openFile(openFile)
0057     , m_downloading(false)
0058     , m_downloadStopped(false)
0059     , m_currSpeed(0)
0060     , m_received(downloadItem->receivedBytes())
0061     , m_total(downloadItem->totalBytes())
0062 {
0063 #ifdef DOWNMANAGER_DEBUG
0064     qDebug() << __FUNCTION__ << item << reply << path << fileName;
0065 #endif
0066 
0067     ui->setupUi(this);
0068     setMaximumWidth(525);
0069 
0070     ui->cancelButton->setPixmap(QIcon::fromTheme(QSL("process-stop")).pixmap(20, 20));
0071     ui->pauseResumeButton->setPixmap(QIcon::fromTheme(QSL("media-playback-pause")).pixmap(20, 20));
0072     ui->fileName->setText(m_fileName);
0073     ui->downloadInfo->setText(tr("Remaining time unavailable"));
0074 
0075     setContextMenuPolicy(Qt::CustomContextMenu);
0076     connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint)));
0077     connect(ui->cancelButton, &ClickableLabel::clicked, this, &DownloadItem::stop);
0078     connect(ui->pauseResumeButton, &ClickableLabel::clicked, this, &DownloadItem::pauseResume);
0079     connect(manager, &DownloadManager::resized, this, &DownloadItem::parentResized);
0080 }
0081 
0082 void DownloadItem::startDownloading()
0083 {
0084     connect(m_download, &QWebEngineDownloadRequest::isFinishedChanged, this, &DownloadItem::finished);
0085     connect(m_download, &QWebEngineDownloadRequest::receivedBytesChanged, this, &DownloadItem::receivedOrTotalBytesChanged);
0086     connect(m_download, &QWebEngineDownloadRequest::totalBytesChanged, this, &DownloadItem::receivedOrTotalBytesChanged);
0087 
0088     m_downloading = true;
0089     if (!m_downTimer.isValid()) {
0090         m_downTimer.start();
0091     }
0092 
0093     updateDownloadInfo(0, m_download->receivedBytes(), m_download->totalBytes());
0094 
0095 #ifdef Q_OS_LINUX
0096     // QFileIconProvider uses only suffix on Linux
0097     QFileIconProvider iconProvider;
0098     QIcon fileIcon = iconProvider.icon(QFileInfo(m_fileName));
0099     if (!fileIcon.isNull()) {
0100         ui->fileIcon->setPixmap(fileIcon.pixmap(30));
0101     } else {
0102         ui->fileIcon->setPixmap(style()->standardIcon(QStyle::SP_FileIcon).pixmap(30));
0103     }
0104 #else
0105     ui->fileIcon->hide();
0106 #endif
0107 }
0108 
0109 void DownloadItem::parentResized(const QSize &size)
0110 {
0111     if (size.width() < 200) {
0112         return;
0113     }
0114     setMaximumWidth(size.width());
0115 }
0116 
0117 void DownloadItem::finished()
0118 {
0119 #ifdef DOWNMANAGER_DEBUG
0120     qDebug() << __FUNCTION__ << m_reply;
0121 #endif
0122 
0123     bool success = false;
0124     QString host = m_download->url().host();
0125 
0126     switch (m_download->state()) {
0127     case QWebEngineDownloadRequest::DownloadCompleted:
0128         success = true;
0129         ui->downloadInfo->setText(tr("Done - %1 (%2)").arg(host, QLocale().toString(QDateTime::currentDateTime(), QLocale::ShortFormat)));
0130         break;
0131 
0132     case QWebEngineDownloadRequest::DownloadInterrupted:
0133         ui->downloadInfo->setText(tr("Error - %1").arg(host));
0134         break;
0135 
0136     case QWebEngineDownloadRequest::DownloadCancelled:
0137         ui->downloadInfo->setText(tr("Cancelled - %1").arg(host));
0138         break;
0139 
0140     default:
0141         break;
0142     }
0143 
0144     ui->progressBar->hide();
0145     ui->cancelButton->hide();
0146     ui->pauseResumeButton->hide();
0147     ui->frame->hide();
0148 
0149     m_item->setSizeHint(sizeHint());
0150     m_downloading = false;
0151 
0152     if (success && m_openFile)
0153         openFile();
0154 
0155     Q_EMIT downloadFinished(true);
0156 }
0157 
0158 void DownloadItem::receivedOrTotalBytesChanged()
0159 {
0160     qint64 received = m_download->receivedBytes();
0161     qint64 total = m_download->totalBytes();
0162 #ifdef DOWNMANAGER_DEBUG
0163     qDebug() << __FUNCTION__ << received << total;
0164 #endif
0165     qint64 currentValue = 0;
0166     qint64 totalValue = 0;
0167     if (total > 0) {
0168         currentValue = received * 100 / total;
0169         totalValue = 100;
0170     }
0171     ui->progressBar->setValue(currentValue);
0172     ui->progressBar->setMaximum(totalValue);
0173     m_currSpeed = received * 1000.0 / m_downTimer.elapsed();
0174     m_received = received;
0175     m_total = total;
0176 
0177     updateDownloadInfo(m_currSpeed, m_received, m_total);
0178     Q_EMIT progressChanged(m_currSpeed, m_received, m_total);
0179 }
0180 
0181 int DownloadItem::progress()
0182 {
0183     return ui->progressBar->value();
0184 }
0185 
0186 bool DownloadItem::isCancelled()
0187 {
0188     return ui->downloadInfo->text().startsWith(tr("Cancelled"));
0189 }
0190 
0191 QString DownloadItem::remaingTimeToString(QTime time)
0192 {
0193     if (time < QTime(0, 0, 10)) {
0194         return tr("few seconds");
0195     }
0196     else if (time < QTime(0, 1)) {
0197         //~ singular %n second
0198         //~ plural %n seconds
0199         return tr("%n seconds", "", time.second());
0200     }
0201     else if (time < QTime(1, 0)) {
0202         //~ singular %n minute
0203         //~ plural %n minutes
0204         return tr("%n minutes", "", time.minute());
0205     }
0206     else {
0207         //~ singular %n hour
0208         //~ plural %n hours
0209         return tr("%n hours", "", time.hour());
0210     }
0211 }
0212 
0213 QString DownloadItem::currentSpeedToString(double speed)
0214 {
0215     if (speed < 0) {
0216         return tr("Unknown speed");
0217     }
0218 
0219     speed /= 1024; // kB
0220     if (speed < 1000) {
0221         return QString::number(speed, 'f', 0) + QLatin1String(" ") + tr("kB/s");
0222     }
0223 
0224     speed /= 1024; //MB
0225     if (speed < 1000) {
0226         return QString::number(speed, 'f', 2) + QLatin1String(" ") + tr("MB/s");
0227     }
0228 
0229     speed /= 1024; //GB
0230     return QString::number(speed, 'f', 2) + QLatin1String(" ") + tr("GB/s");
0231 }
0232 
0233 void DownloadItem::updateDownloadInfo(double currSpeed, qint64 received, qint64 total)
0234 {
0235 #ifdef DOWNMANAGER_DEBUG
0236     qDebug() << __FUNCTION__ << currSpeed << received << total;
0237 #endif
0238     //            QString          QString       QString     QString
0239     //          | m_remTime |   |m_currSize|  |m_fileSize|  |m_speed|
0240     // Remaining 26 minutes -     339MB of      693 MB        (350kB/s)
0241 
0242     if (m_download->isPaused()) {
0243         return;
0244     }
0245 
0246     int estimatedTime = ((total - received) / 1024) / (currSpeed / 1024);
0247     QString speed = currentSpeedToString(currSpeed);
0248     // We have QString speed now
0249 
0250     QTime time(0, 0, 0);
0251     time = time.addSecs(estimatedTime);
0252     QString remTime = remaingTimeToString(time);
0253     m_remTime = time;
0254 
0255     QString currSize = QzTools::fileSizeToString(received);
0256     QString fileSize = QzTools::fileSizeToString(total);
0257 
0258     if (fileSize == tr("Unknown size")) {
0259         ui->downloadInfo->setText(tr("%2 - unknown size (%3)").arg(currSize, speed));
0260     }
0261     else {
0262         ui->downloadInfo->setText(tr("Remaining %1 - %2 of %3 (%4)").arg(remTime, currSize, fileSize, speed));
0263     }
0264 }
0265 
0266 void DownloadItem::stop()
0267 {
0268 #ifdef DOWNMANAGER_DEBUG
0269     qDebug() << __FUNCTION__;
0270 #endif
0271     if (m_downloadStopped) {
0272         return;
0273     }
0274     m_downloadStopped = true;
0275     ui->progressBar->hide();
0276     ui->cancelButton->hide();
0277     ui->pauseResumeButton->hide();
0278     m_item->setSizeHint(sizeHint());
0279     ui->downloadInfo->setText(tr("Cancelled - %1").arg(m_download->url().host()));
0280     m_download->cancel();
0281     m_downloading = false;
0282 
0283     Q_EMIT downloadFinished(false);
0284 }
0285 
0286 void DownloadItem::pauseResume()
0287 {
0288     if (m_download->isPaused()) {
0289         m_download->resume();
0290         ui->pauseResumeButton->setPixmap(QIcon::fromTheme(QSL("media-playback-pause")).pixmap(20, 20));
0291     } else {
0292         m_download->pause();
0293         ui->pauseResumeButton->setPixmap(QIcon::fromTheme(QSL("media-playback-start")).pixmap(20, 20));
0294         ui->downloadInfo->setText(tr("Paused - %1").arg(m_download->url().host()));
0295     }
0296 }
0297 
0298 void DownloadItem::mouseDoubleClickEvent(QMouseEvent* e)
0299 {
0300     openFile();
0301     e->accept();
0302 }
0303 
0304 void DownloadItem::customContextMenuRequested(const QPoint &pos)
0305 {
0306     QMenu menu;
0307     menu.addAction(QIcon::fromTheme(QSL("document-open")), tr("Open File"), this, &DownloadItem::openFile);
0308 
0309     menu.addAction(tr("Open Folder"), this, &DownloadItem::openFolder);
0310     menu.addSeparator();
0311     menu.addAction(QIcon::fromTheme(QSL("edit-copy")), tr("Copy Download Link"), this, &DownloadItem::copyDownloadLink);
0312     menu.addSeparator();
0313     menu.addAction(QIcon::fromTheme(QSL("process-stop")), tr("Cancel downloading"), this, &DownloadItem::stop)->setEnabled(m_downloading);
0314 
0315     if (m_download->isPaused()) {
0316         menu.addAction(QIcon::fromTheme(QSL("media-playback-start")), tr("Resume downloading"), this, &DownloadItem::pauseResume)->setEnabled(m_downloading);
0317     } else {
0318         menu.addAction(QIcon::fromTheme(QSL("media-playback-pause")), tr("Pause downloading"), this, &DownloadItem::pauseResume)->setEnabled(m_downloading);
0319     }
0320 
0321     menu.addAction(QIcon::fromTheme(QSL("list-remove")), tr("Remove From List"), this, &DownloadItem::clear)->setEnabled(!m_downloading);
0322 
0323     if (m_downloading || ui->downloadInfo->text().startsWith(tr("Cancelled")) || ui->downloadInfo->text().startsWith(tr("Error"))) {
0324         menu.actions().at(0)->setEnabled(false);
0325     }
0326     menu.exec(mapToGlobal(pos));
0327 }
0328 
0329 void DownloadItem::copyDownloadLink()
0330 {
0331     QApplication::clipboard()->setText(m_downUrl.toString());
0332 }
0333 
0334 void DownloadItem::clear()
0335 {
0336     Q_EMIT deleteItem(this);
0337 }
0338 
0339 void DownloadItem::openFile()
0340 {
0341     if (m_downloading) {
0342         return;
0343     }
0344     QFileInfo info(m_path, m_fileName);
0345     if (info.exists()) {
0346         QDesktopServices::openUrl(QUrl::fromLocalFile(info.absoluteFilePath()));
0347     }
0348     else {
0349         QMessageBox::warning(m_item->listWidget()->parentWidget(), tr("Not found"), tr("Sorry, the file \n %1 \n was not found!").arg(info.absoluteFilePath()));
0350     }
0351 }
0352 
0353 void DownloadItem::openFolder()
0354 {
0355 #ifdef Q_OS_WIN
0356     QString winFileName = QSL("%1/%2").arg(m_path, m_fileName);
0357 
0358     if (m_downloading) {
0359         winFileName.append(QSL(".download"));
0360     }
0361 
0362     winFileName.replace(QLatin1Char('/'), QSL("\\"));
0363     QString shExArg = QSL("/e,/select,\"") + winFileName + QSL("\"");
0364     ShellExecute(NULL, NULL, TEXT("explorer.exe"), shExArg.toStdWString().c_str(), NULL, SW_SHOW);
0365 #else
0366     QDesktopServices::openUrl(QUrl::fromLocalFile(m_path));
0367 #endif
0368 }
0369 
0370 QUrl DownloadItem::url() const
0371 {
0372     return m_downUrl;
0373 }
0374 
0375 QString DownloadItem::path() const
0376 {
0377     return m_path;
0378 }
0379 
0380 QString DownloadItem::fileName() const
0381 {
0382     return m_fileName;
0383 }
0384 
0385 DownloadItem::~DownloadItem()
0386 {
0387     delete ui;
0388     delete m_item;
0389 }