videodownloader/gui/mainwindow.cpp

926 lines
35 KiB
C++

#include "./mainwindow.h"
#include "./adddownloaddialog.h"
#include "./addmultipledownloadswizard.h"
#include "./downloadinteraction.h"
#include "./setrangedialog.h"
#include "./settings.h"
#include "../network/bitsharedownload.h"
#include "../network/download.h"
#include "../network/groovesharkdownload.h"
#include "../network/socksharedownload.h"
#include "../network/youtubedownload.h"
#ifdef CONFIG_TESTDOWNLOAD
#include "../network/testdownload.h"
#endif
#include "../model/downloadmodel.h"
#include "../itemdelegates/comboboxitemdelegate.h"
#include "../itemdelegates/progressbaritemdelegate.h"
#include "ui_mainwindow.h"
#include "resources/config.h"
#include <qtutilities/aboutdialog/aboutdialog.h>
#include <qtutilities/enterpassworddialog/enterpassworddialog.h>
#include <qtutilities/misc/desktoputils.h>
#include <c++utilities/chrono/timespan.h>
#include <c++utilities/conversion/stringconversion.h>
#include <c++utilities/io/path.h>
#include <QClipboard>
#include <QCloseEvent>
#include <QDesktopServices>
#include <QDir>
#include <QFileDialog>
#include <QFileSystemModel>
#include <QInputDialog>
#include <QLabel>
#include <QList>
#include <QMessageBox>
#include <QNetworkProxy>
#include <QSettings>
#include <QSpinBox>
#include <QSslError>
#include <QTimer>
#include <QToolButton>
#include <functional>
using namespace std;
using namespace CppUtilities;
using namespace QtUtilities;
using namespace Network;
namespace QtGui {
// C'tor
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, m_ui(new Ui::MainWindow)
, m_trayIcon(nullptr)
, m_trayIconMenu(nullptr)
, m_internalClipboardChange(false)
, m_activeDownloads(0)
, m_downloadsToStart(0)
, m_initiatingDownloads(0)
, m_totalSpeed(0)
, m_stillToReceive(0)
, m_downloadInteraction(new DownloadInteraction(this))
, m_addDownloadDlg(nullptr)
, m_addMultipleDownloadsWizard(nullptr)
, m_settingsDlg(nullptr)
, m_aboutDlg(nullptr)
{
// setup ui
m_ui->setupUi(this);
// load settings
restoreGeometry(UiPage::mainWindowGeometry());
// setup tray icon and its context menu
setupTrayIcon();
// setup main toolbar
// add downloads tool button
QToolButton *addDownloadToolButton = new QToolButton(m_ui->toolBar);
addDownloadToolButton->setMenu(m_ui->menuAdd);
addDownloadToolButton->setPopupMode(QToolButton::MenuButtonPopup);
addDownloadToolButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
addDownloadToolButton->setText(tr("Add download"));
addDownloadToolButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
m_ui->toolBar->insertWidget(m_ui->actionStart_selected, addDownloadToolButton);
#ifdef CONFIG_TESTDOWNLOAD
QAction *action = new QAction(QStringLiteral("Test download"), m_ui->menuAdd);
action->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
m_ui->menuAdd->addAction(action);
#endif
// add supervise clipboard tool button
m_superviseClipboardToolButton = new QToolButton(m_ui->toolBar);
m_superviseClipboardToolButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
m_superviseClipboardToolButton->setText(tr("Supervise clipboard"));
m_superviseClipboardToolButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste")));
m_superviseClipboardToolButton->setCheckable(true);
m_ui->toolBar->insertWidget(m_ui->actionStart_selected, m_superviseClipboardToolButton);
// setup auto spinbox
m_autoSpinBox = new QSpinBox(m_ui->autoToolBar);
m_autoSpinBox->setObjectName(QStringLiteral("autoSpinBox"));
m_autoSpinBox->setEnabled(true);
m_autoSpinBox->setMinimumSize(QSize(245, 0));
m_autoSpinBox->setMaximum(10);
m_autoSpinBox->setSuffix(tr(" download(s) automatically"));
m_autoSpinBox->setPrefix(tr("Start up to "));
QWidget *spacer = new QWidget(m_ui->autoToolBar);
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
spacer->setVisible(true);
m_ui->autoToolBar->addWidget(spacer);
m_ui->autoToolBar->addWidget(m_autoSpinBox);
// setup downloads tree view and model
// delegates, model
m_progressBarDelegate = new ProgressBarItemDelegate(this);
m_comboBoxDelegate = new ComboBoxItemDelegate(this);
m_model = new DownloadModel(this);
m_ui->downloadsTreeView->setItemDelegateForColumn(DownloadModel::optionsColumn(), m_comboBoxDelegate);
m_ui->downloadsTreeView->setItemDelegateForColumn(DownloadModel::progressColumn(), m_progressBarDelegate);
m_ui->downloadsTreeView->setModel(m_model);
updateSelectionMode();
// context menu
m_downloadsTreeViewContextMenu = new QMenu(this);
m_downloadsTreeViewContextMenu->addMenu(m_ui->menuAdd);
m_downloadsTreeViewContextMenu->addAction(m_ui->actionStart_selected);
m_downloadsTreeViewContextMenu->addAction(m_ui->actionResume_selected_downloads);
m_downloadsTreeViewContextMenu->addAction(m_ui->actionRemove_selected_downloads_from_list);
m_downloadsTreeViewContextMenu->addAction(m_ui->actionCopy_download_url);
m_downloadsTreeViewContextMenu->addAction(m_ui->actionSet_range);
m_downloadsTreeViewContextMenu->addAction(m_ui->actionSet_target);
m_downloadsTreeViewContextMenu->addAction(m_ui->actionClear_target);
m_ui->downloadsTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui->downloadsTreeView, &QTreeView::customContextMenuRequested, this, &MainWindow::showDownloadsTreeViewContextMenu);
// column widths
m_ui->downloadsTreeView->setColumnWidth(DownloadModel::initialUrlColumn(), 200);
m_ui->downloadsTreeView->setColumnWidth(DownloadModel::downloadUrlColumn(), 200);
m_ui->downloadsTreeView->setColumnWidth(DownloadModel::titleColumn(), 200);
m_ui->downloadsTreeView->setColumnWidth(DownloadModel::uploaderColumn(), 100);
m_ui->downloadsTreeView->setColumnWidth(DownloadModel::optionsColumn(), 80);
m_ui->downloadsTreeView->setColumnWidth(DownloadModel::progressColumn(), 50);
// update controls when selection changes
QItemSelectionModel *selectionModel = m_ui->downloadsTreeView->selectionModel();
connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &MainWindow::updateStartStopControls);
// setup download status label and timer for updating it
m_downloadStatusLabel = new QLabel(this);
m_downloadStatusLabel->setVisible(false);
m_ui->statusBar->addWidget(m_downloadStatusLabel);
m_elapsedTime.start();
// connect signals and slots
// application
connect(m_ui->actionSettings, &QAction::triggered, this, &MainWindow::showSettingsDialog);
connect(m_ui->actionQuit, &QAction::triggered, &QApplication::quit);
// add
connect(addDownloadToolButton, &QToolButton::clicked, this, &MainWindow::showAddDownloadDialog);
connect(m_ui->actionAdd_Download, &QAction::triggered, this, &MainWindow::showAddDownloadDialog);
connect(m_ui->actionAdd_multiple_downloads, &QAction::triggered, this, &MainWindow::showAddMultipleDownloadsDialog);
#ifdef CONFIG_TESTDOWNLOAD
connect(action, &QAction::triggered, [this] { this->addDownload(new TestDownload()); });
#endif
// downloads
connect(m_ui->actionStart_selected, &QAction::triggered, this, &MainWindow::startOrStopSelectedDownloads);
connect(m_ui->actionResume_selected_downloads, &QAction::triggered, this, &MainWindow::interruptOrResumeSelectedDownloads);
connect(m_ui->actionRemove_selected_downloads_from_list, &QAction::triggered, this, &MainWindow::removeSelectedDownloads);
connect(m_ui->actionCopy_download_url, &QAction::triggered, this, &MainWindow::copyDownloadUrl);
connect(m_ui->actionSet_range, &QAction::triggered, this, &MainWindow::setDownloadRange);
connect(m_ui->actionSet_target, &QAction::triggered, this, &MainWindow::setTargetPath);
connect(m_ui->actionClear_target, &QAction::triggered, this, &MainWindow::clearTargetPath);
connect(m_ui->actionReset_grooveshark_session, &QAction::triggered, this, &MainWindow::resetGroovesharkSession);
// ?
connect(m_ui->actionAbout, &QAction::triggered, this, &MainWindow::showAboutDialog);
connect(m_ui->actionYoutube_itags, &QAction::triggered, this, &MainWindow::showYoutubeItagsInfo);
// other
connect(m_autoSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &MainWindow::checkForDownloadsToStartAutomatically);
connect(m_ui->actionExplore_target_directory, &QAction::triggered, this, &MainWindow::exploreDownloadsDir);
connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &MainWindow::clipboardDataChanged);
}
MainWindow::~MainWindow()
{
}
// Methods to show several dialogs
void MainWindow::showAboutDialog()
{
if (!m_aboutDlg) {
m_aboutDlg = new AboutDialog(this, QString(),
QStringLiteral("<p>Developed by " APP_AUTHOR
"<br>Program icon and fallback icons from <a href=\"https://invent.kde.org/frameworks/breeze-icons\">KDE/Breeze</a> "
"project (copyright © 2014 Uri Herrera <uri_herrera@nitrux.in> and others, see the according "
"<a href=\"" APP_URL "/blob/master/LICENSE.LESSER\">LGPL-3.0 license</a>)</p>"),
QString(), QString(), tr("Simple video downloader with Qt GUI and backends for multiple platforms, e.g. YouTube and Vimeo."),
QImage(QStringLiteral(":/icons/hicolor/128x128/apps/videodownloader.png")));
}
if (m_aboutDlg->isHidden()) {
m_aboutDlg->showNormal();
} else {
m_aboutDlg->activateWindow();
}
}
void MainWindow::addDownload(Download *download)
{
// set parent, behavior and proxy
download->setParent(this);
connect(download, &Download::statusChanged, this, &MainWindow::downloadChangedStatus);
connect(download, &Download::progressChanged, this, &MainWindow::downloadChangedProgress);
m_downloadInteraction->connectDownload(download);
// add download to model
m_model->addDownload(download);
// Begin to fetch initial info
if (!download->isInitiated() && download->isInitiatingInstantlyRecommendable()) {
applySettingsToDownload(download);
download->init();
}
}
void MainWindow::showAddDownloadDialog()
{
if (!m_addDownloadDlg) {
m_addDownloadDlg = new AddDownloadDialog(this);
connect(m_addDownloadDlg, &AddDownloadDialog::addDownloadClicked, this, &MainWindow::addDownloadDialogResults);
}
if (m_addDownloadDlg->isHidden()) {
m_addDownloadDlg->showNormal();
} else {
m_addDownloadDlg->activateWindow();
}
}
void MainWindow::addDownloadDialogResults()
{
if (isHidden()) {
show();
}
if (m_addDownloadDlg) {
if (Download *result = m_addDownloadDlg->result()) {
addDownload(result);
}
m_addDownloadDlg->reset();
}
}
void MainWindow::showAddMultipleDownloadsDialog()
{
if (!m_addMultipleDownloadsWizard) {
m_addMultipleDownloadsWizard = new AddMultipleDownloadsWizard(this);
connect(m_addMultipleDownloadsWizard, &AddMultipleDownloadsWizard::finished, this, &MainWindow::addMultipleDownloadsWizardResults);
}
if (m_addMultipleDownloadsWizard->isHidden()) {
m_addMultipleDownloadsWizard->showNormal();
} else {
m_addMultipleDownloadsWizard->activateWindow();
}
}
void MainWindow::addMultipleDownloadsWizardResults()
{
if (isHidden()) {
show();
}
if (m_addMultipleDownloadsWizard) {
for (Download *const res : m_addMultipleDownloadsWizard->results()) {
addDownload(res);
}
m_addMultipleDownloadsWizard->restart();
}
}
void MainWindow::showSettingsDialog()
{
if (!m_settingsDlg) {
m_settingsDlg = new SettingsDialog(this);
connect(m_settingsDlg, &SettingsDialog::applied, this, &MainWindow::settingsAccepted);
}
if (m_settingsDlg->isHidden()) {
m_settingsDlg->showNormal();
} else {
m_settingsDlg->activateWindow();
}
}
void MainWindow::settingsAccepted()
{
updateSelectionMode();
}
void MainWindow::showTrayIconMessage()
{
m_trayIcon->showMessage(windowTitle(),
tr("The downloader will keep running in the system tray. To terminate the program, "
"choose \"Quit\" in the context menu of the system tray entry."));
}
void MainWindow::checkForDownloadsToStartAutomatically()
{
for (int row = 0, rowCount = m_model->rowCount(), toStart = m_autoSpinBox->value() - m_activeDownloads - m_initiatingDownloads;
row < rowCount && toStart > 0; ++row) {
Download *download = m_model->download(row);
switch (download->status()) {
case DownloadStatus::None:
applySettingsToDownload(download);
download->init();
--toStart;
break;
case DownloadStatus::Ready:
applySettingsToDownload(download);
download->start();
--toStart;
break;
default:;
}
}
}
// Methods to start and stop downloads
void MainWindow::startOrStopSelectedDownloads()
{
QList<Download *> selectedDownloads = this->selectedDownloads();
// check if there are downloads selected
if (!selectedDownloads.count()) {
QMessageBox::warning(this, windowTitle(), tr("There are no downloads selected."));
return;
}
for (Download *const download : selectedDownloads) {
switch (download->status()) {
case DownloadStatus::None:
// retrieve initial information when that still has to be done
applySettingsToDownload(download);
download->init();
break;
case DownloadStatus::Initiating:
// when the download is retrieving initial information it can be stopped
download->stop();
break;
case DownloadStatus::Downloading:
case DownloadStatus::FinishOuputFile:
// when the download is downloading it can be stopped
download->stop();
break;
case DownloadStatus::Ready:
case DownloadStatus::Failed:
case DownloadStatus::Finished:
case DownloadStatus::Interrupted:
// the download can be (re)started, but if initial information has been retireved has to be checked
if (download->isInitiated()) {
applySettingsToDownload(download);
// reset the current offset to ensure the download is (re)started from the beginning
download->range().resetCurrentOffset();
download->start();
} else {
QString downloadUrl;
if (selectedDownloads.count() > 1)
downloadUrl = tr("(%1) ").arg(download->initialUrl().toString());
switch (download->status()) {
case DownloadStatus::Ready:
// download without initial information shouldn't be prepared to start!
QMessageBox::warning(this, windowTitle(),
tr("The download %1can't be started because the initial information hasn't been retrieved yet. That shouldn't happen.")
.arg(downloadUrl));
break;
case DownloadStatus::Failed:
applySettingsToDownload(download);
download->init(); // try to retrieve initial information again
//QMessageBox::warning(this, windowTitle(), QStringLiteral("The download %1can't be started because it couldn't be initialized. Readd the download to try it again. See the description below for more detail:\n\n") + status.getDescription());
break;
default:;
}
}
break;
default:
break; // nothing to do here
}
}
// repeal selection to avoid double-click problem
m_ui->downloadsTreeView->selectionModel()->clearSelection();
}
void MainWindow::interruptOrResumeSelectedDownloads()
{
QList<Download *> selectedDownloads = this->selectedDownloads();
// check if there are downloads selected
if (!selectedDownloads.count()) {
QMessageBox::warning(this, windowTitle(), tr("There are no downloads selected."));
return;
}
for (Download *const download : selectedDownloads) {
switch (download->status()) {
case DownloadStatus::Downloading:
download->interrupt();
// the range will be set automatically by the download to be able to resume the download
break;
case DownloadStatus::Ready:
case DownloadStatus::Interrupted:
case DownloadStatus::Failed:
if (download->isInitiated()) {
download->start();
} else {
QMessageBox::warning(this, windowTitle(),
tr("The download %1 can't be started because it couldn't be initialized. Readd the download to try it again. See the description "
"below for more detail:\n\n")
.arg((download->title().isEmpty() ? download->initialUrl().toString() : download->title()))
+ download->statusInfo());
}
break;
default:
// nothing to do here
break;
}
}
// repeal selection to avoid double-click problem
m_ui->downloadsTreeView->selectionModel()->clearSelection();
}
void MainWindow::removeSelectedDownloads()
{
int removed = 0;
QModelIndexList selectedIndexes = m_ui->downloadsTreeView->selectionModel()->selectedRows();
QList<QPersistentModelIndex> persistendIndexes;
for (const QModelIndex &selectedIndex : selectedIndexes) {
persistendIndexes << QPersistentModelIndex(selectedIndex);
}
for (const QPersistentModelIndex &selectedIndex : persistendIndexes) {
if (Download *download = m_model->download(selectedIndex)) {
switch (download->status()) {
case DownloadStatus::Downloading:
case DownloadStatus::Initiating:
case DownloadStatus::FinishOuputFile:
break;
default:
m_model->removeDownload(download);
delete download;
--m_downloadsToStart;
++removed;
break;
}
}
}
if (removed == 1) {
m_downloadStatusLabel->setText(tr("the download has been removed"));
} else if (removed > 1) {
m_downloadStatusLabel->setText(tr("%1 downloads have been removed").arg(removed));
}
}
// methods for several gui features
void MainWindow::updateSelectionMode()
{
m_ui->downloadsTreeView->setSelectionMode(UiPage::multiSelection() ? QAbstractItemView::MultiSelection : QAbstractItemView::SingleSelection);
}
void MainWindow::updateStartStopControls()
{
QList<Download *> selectedDownloads = this->selectedDownloads();
int toStart = 0;
int toRestart = 0;
int toResume = 0;
int toInit = 0;
int toStop = 0;
int toInterrupt = 0;
int noCommand = 0;
int downloadLinksAvailable = 0;
int removable = 0;
int withTargetPath = 0;
bool downloadsSelected = !selectedDownloads.isEmpty();
if (downloadsSelected) {
for (Download *const download : selectedDownloads) {
switch (download->status()) {
case DownloadStatus::None:
++toInit;
++removable;
break;
case DownloadStatus::Initiating:
++toStop;
break;
case DownloadStatus::Downloading:
case DownloadStatus::FinishOuputFile:
++downloadLinksAvailable;
++toStop;
if (download->supportsRange()) {
++toInterrupt;
}
break;
case DownloadStatus::Ready:
++downloadLinksAvailable;
++removable;
if (download->supportsRange() && download->range().currentOffset() > 0) {
++toResume;
} else {
++toStart;
}
break;
case DownloadStatus::Failed:
if (download->isInitiated()) {
++toResume; // downloads that can be resumed can also be restarted, so toRestart is "included" here, too
++downloadLinksAvailable;
} else {
++toInit;
}
++removable;
break;
case DownloadStatus::Interrupted:
++removable;
++downloadLinksAvailable;
if (download->supportsRange()) {
++toResume;
}
break;
case DownloadStatus::Finished:
++downloadLinksAvailable;
++toRestart;
++removable;
break;
case DownloadStatus::Interrupting:
case DownloadStatus::Aborting:
++noCommand;
break;
default:
++noCommand;
}
if (!download->targetPath().isEmpty()) {
++withTargetPath;
}
}
}
QString text;
QString themeName;
QString interruptResumeText;
QString interruptResumeThemeName;
if (noCommand == 0 && toStart && toRestart == 0 && toInit == 0 && toStop == 0 && toResume == 0) {
// can start?
if (toStart == 1) {
text.append(tr("Start selected download"));
} else {
text = tr("Start selected downloads (%1)").arg(toStart);
}
themeName = QStringLiteral("media-playback-start");
} else if (noCommand == 0 && (toRestart || toResume) && toStart == 0 && toInit == 0 && toStop == 0) {
// can restart? (if a download can be resumed, it can be also restarted)
int sum = toRestart + toResume;
if (sum == 1) {
text.append(tr("Restart the selected download"));
} else {
text = tr("Restart the selected downloads (%1)").arg(sum);
}
themeName = QStringLiteral("media-playback-start");
if (!toRestart) {
// can resume?
if (toResume == 1) {
interruptResumeText.append(tr("Resume the selected download"));
} else if (toResume > 1) {
interruptResumeText = tr("Resume the selected downloads (%1)").arg(toInterrupt);
}
interruptResumeThemeName = QStringLiteral("media-playback-start");
}
} else if (noCommand == 0 && toInit && toStart == 0 && toRestart == 0 && toStop == 0 && toResume == 0) {
// can initialize?
if (toInit == 1) {
text.append(tr("Load initial information for selected download"));
} else {
text = tr("Load initial information for selected downloads (%1)").arg(toInit);
}
themeName = QStringLiteral("media-playback-start");
} else if (noCommand == 0 && toStop && toStart == 0 && toRestart == 0 && toInit == 0 && toResume == 0) {
// can stop?
if (toStop == 1) {
text.append(tr("Stop selected download"));
} else {
text = tr("Stop selected downloads (%1)").arg(toStop);
}
themeName = QStringLiteral("media-playback-stop");
// can interrupt?
if (toInterrupt) {
if (toInterrupt == 1) {
interruptResumeText.append(tr("Interrupt the selected download"));
} else {
interruptResumeText = tr("Interrupt the selected downloads (%1)").arg(toInterrupt);
}
interruptResumeThemeName = QStringLiteral("media-playback-pause");
}
}
if (text.isEmpty()) {
m_ui->actionStart_selected->setEnabled(false);
} else {
m_ui->actionStart_selected->setEnabled(true);
m_ui->actionStart_selected->setText(text);
m_ui->actionStart_selected->setIcon(QIcon::fromTheme(themeName));
}
if (interruptResumeText.isEmpty()) {
m_ui->actionResume_selected_downloads->setEnabled(false);
} else {
m_ui->actionResume_selected_downloads->setEnabled(true);
m_ui->actionResume_selected_downloads->setText(interruptResumeText);
m_ui->actionResume_selected_downloads->setIcon(QIcon::fromTheme(interruptResumeThemeName));
}
if (m_ui->toolBar->minimumWidth() < m_ui->toolBar->width()) {
m_ui->toolBar->setMinimumWidth(m_ui->toolBar->width());
}
if (removable == 1) {
m_ui->actionRemove_selected_downloads_from_list->setText(tr("Remove selected download from list"));
} else if (removable > 1) {
m_ui->actionRemove_selected_downloads_from_list->setText(tr("Remove selected downloads (%1) from list").arg(removable));
}
m_ui->actionRemove_selected_downloads_from_list->setEnabled(removable > 0);
if (downloadLinksAvailable > 0) {
if (downloadLinksAvailable == 1) {
m_ui->actionCopy_download_url->setText(tr("Copy download url"));
} else {
m_ui->actionCopy_download_url->setText(tr("Copy download urls (%1)").arg(downloadLinksAvailable));
}
m_ui->actionCopy_download_url->setEnabled(true);
} else {
m_ui->actionCopy_download_url->setEnabled(false);
}
bool exactlyOneNoneActiveDownloadSelected = selectedDownloads.size() == 1 && toStop == 0 && toInterrupt == 0;
m_ui->actionSet_range->setEnabled(exactlyOneNoneActiveDownloadSelected);
m_ui->actionSet_target->setEnabled(exactlyOneNoneActiveDownloadSelected);
m_ui->actionClear_target->setEnabled(downloadsSelected && toStop == 0 && toInterrupt == 0 && withTargetPath > 0);
}
void MainWindow::showDownloadsTreeViewContextMenu(const QPoint &pos)
{
m_downloadsTreeViewContextMenu->exec(m_ui->downloadsTreeView->mapToGlobal(pos));
}
void MainWindow::trayIconActivated()
{
trayIconActivated(QSystemTrayIcon::Trigger);
}
void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
switch (reason) {
case QSystemTrayIcon::Trigger:
case QSystemTrayIcon::DoubleClick:
showNormal();
#ifndef CONFIG_SHOW_TRAY_ICON_ALWAYS
if (m_trayIcon && m_trayIcon->isVisible()) {
m_trayIcon->hide();
}
#endif
break;
default:;
}
}
void MainWindow::trayIconDestroyed(QObject *)
{
m_trayIcon = nullptr;
setupTrayIcon();
}
void MainWindow::clipboardDataChanged()
{
if (!m_internalClipboardChange && m_superviseClipboardToolButton->isChecked()) {
QString data = QApplication::clipboard()->text();
QStringList lines = data.split(QChar('\n'), Qt::SkipEmptyParts);
for (const QString &line : lines) {
if (Download *const download = Download::fromUrl(line)) {
addDownload(download);
if (m_trayIcon && m_trayIcon->isVisible()) {
m_trayIcon->showMessage(windowTitle(), tr("The download \"%1\" has been added.").arg(data));
}
}
}
}
}
void MainWindow::copyDownloadUrl()
{
m_internalClipboardChange = true;
QList<Download *> downloads = selectedDownloads();
QString urls;
QListIterator<Download *> i(downloads);
while (i.hasNext()) {
if (i.hasPrevious()) {
urls.append("\n");
}
urls.append(i.next()->downloadUrl().toString());
}
if (!urls.isEmpty()) {
QApplication::clipboard()->setText(urls);
} else {
QMessageBox::warning(this, windowTitle(), tr("There are no downloads selected."));
}
m_internalClipboardChange = false;
}
void MainWindow::setDownloadRange()
{
QList<Download *> downloads = selectedDownloads();
if (downloads.size() == 1) {
if (SetRangeDialog(downloads.at(0)->range(), this).exec() == QDialog::Accepted) {
updateStartStopControls(); // download (not) might be resumable now
}
} else if (!downloads.size()) {
QMessageBox::warning(this, windowTitle(), tr("There is no download selected."));
} else {
QMessageBox::warning(this, windowTitle(), tr("You can only set the range of a single download at once."));
}
}
void MainWindow::setTargetPath()
{
QList<Download *> downloads = selectedDownloads();
if (downloads.size() == 1) {
QString path = QFileDialog::getSaveFileName(this, tr("Select target path for download"), downloads.front()->targetPath());
if (!path.isEmpty()) {
downloads.front()->setTargetPath(path);
updateStartStopControls();
}
} else if (!downloads.size()) {
QMessageBox::warning(this, windowTitle(), tr("There is no download selected."));
} else {
QMessageBox::warning(this, windowTitle(), tr("You can only set the target of a single download at once."));
}
}
void MainWindow::clearTargetPath()
{
QList<Download *> downloads = selectedDownloads();
if (!downloads.isEmpty()) {
for (Download *const download : downloads) {
download->setTargetPath(QString());
}
updateStartStopControls();
} else if (!downloads.size()) {
QMessageBox::warning(this, windowTitle(), tr("There is no download selected."));
}
}
void MainWindow::showYoutubeItagsInfo()
{
QDesktopServices::openUrl(QUrl(QStringLiteral("https://gist.github.com/sidneys/7095afe4da4ae58694d128b1034e01e2")));
}
void MainWindow::resetGroovesharkSession()
{
GroovesharkDownload::resetSession();
QMessageBox::information(this, windowTitle(), tr("Grooveshark session has been reset."));
}
void MainWindow::exploreDownloadsDir()
{
if (TargetPage::targetDirectory().isEmpty()) {
QMessageBox::warning(this, windowTitle(), tr("There is no download target selected."));
} else {
if (QDir(TargetPage::targetDirectory()).exists()) {
openLocalFileOrDir(TargetPage::targetDirectory());
} else {
QMessageBox::warning(this, windowTitle(), tr("The selected download directory doesn't exist anymore."));
}
}
}
void MainWindow::closeEvent(QCloseEvent *event)
{
if (m_activeDownloads) {
if (QSystemTrayIcon::isSystemTrayAvailable() && m_trayIcon) {
if (!m_trayIcon->isVisible()) {
m_trayIcon->show();
QApplication::sendPostedEvents(m_trayIcon);
}
QTimer::singleShot(1000, Qt::CoarseTimer, this, SLOT(showTrayIconMessage()));
event->ignore();
setHidden(true);
} else {
if (QMessageBox::warning(
this, windowTitle(), tr("Do you really want to exit?\nThere are still downloads running."), QMessageBox::No, QMessageBox::Yes)
== QMessageBox::No) {
event->ignore();
}
}
}
UiPage::mainWindowGeometry() = saveGeometry();
UiPage::mainWindowState() = saveState();
}
void MainWindow::downloadChangedStatus(Download *download)
{
// update overall download information
switch (download->status()) {
case DownloadStatus::None:
break;
case DownloadStatus::Downloading:
++m_activeDownloads;
break;
case DownloadStatus::Initiating:
++m_initiatingDownloads;
break;
default:
++m_downloadsToStart;
}
switch (download->lastStatus()) {
case DownloadStatus::None:
break;
case DownloadStatus::Downloading:
--m_activeDownloads;
if (m_activeDownloads <= 0 && m_trayIcon && m_trayIcon->isVisible()) {
m_trayIcon->showMessage(windowTitle(), tr("All downloads have been finished."));
}
break;
case DownloadStatus::Initiating:
--m_initiatingDownloads;
break;
default:
--m_downloadsToStart;
}
updateOverallStatus(download);
updateStartStopControls();
checkForDownloadsToStartAutomatically();
}
void MainWindow::downloadChangedProgress(Download *download)
{
if (download->status() == DownloadStatus::Downloading && m_elapsedTime.elapsed() > 1000) {
updateOverallStatus(download);
}
}
void MainWindow::updateOverallStatus(Download *download)
{
qint64 newBytesReceived = download->newBytesReceived();
qint64 newBytesToReceive = download->newBytesToReceive();
m_totalSpeed += download->shiftSpeed();
StatsPage::bytesReceived() += newBytesReceived;
m_stillToReceive += newBytesToReceive - newBytesReceived;
m_remainingTime = m_totalSpeed > 0 ? TimeSpan::fromSeconds(static_cast<double>(m_stillToReceive) / (m_totalSpeed * 125.0)) : TimeSpan();
if (m_activeDownloads >= 1) {
// if there are active downloads, show it in the status bar and as tray icon tooltip
QString status = tr("%1 active download").arg(m_activeDownloads);
if (m_activeDownloads != 1) {
status.append("s");
}
status.append(QStringLiteral(" - "));
status.append(QString::fromStdString(bitrateToString(m_totalSpeed, true)));
if (!m_remainingTime.isNull()) {
status.append(tr(", about %1 remaining").arg(QString::fromStdString(m_remainingTime.toString(TimeSpanOutputFormat::WithMeasures, true))));
}
if (m_trayIcon) {
m_trayIcon->setToolTip(status);
}
m_downloadStatusLabel->setText(status);
m_downloadStatusLabel->setVisible(true);
} else if (m_downloadsToStart >= 1) {
// if there are downloads that can be started, shot it in the status bar an as tray icon tooltip
QString status = tr("%1 download").arg(m_downloadsToStart);
if (m_downloadsToStart != 1)
status.append(tr("s"));
status.append(tr(" can be started"));
if (m_trayIcon) {
m_trayIcon->setToolTip(status);
}
m_downloadStatusLabel->setText(status);
m_downloadStatusLabel->setVisible(true);
} else {
// if not, hide the status bar and clear tooltip of tray icon
if (m_trayIcon) {
m_trayIcon->setToolTip(QString());
}
m_downloadStatusLabel->setVisible(false);
}
m_elapsedTime.restart();
}
// methods to get specific downloads/rows
QList<Download *> MainWindow::selectedDownloads() const
{
QList<Download *> res;
QModelIndexList selectedRows = m_ui->downloadsTreeView->selectionModel()->selectedRows();
for (const QModelIndex &index : selectedRows) {
if (Download *download = m_model->download(index)) {
res << download;
}
}
return res;
}
void MainWindow::setupTrayIcon()
{
#ifdef CONFIG_USE_TRAY_ICON
if (!m_trayIcon) {
m_trayIcon = new QSystemTrayIcon(QIcon(QStringLiteral(":/icons/hicolor/128x128/apps/videodownloader.png")));
connect(m_trayIcon, &QSystemTrayIcon::activated, this,
static_cast<void (MainWindow::*)(QSystemTrayIcon::ActivationReason)>(&MainWindow::trayIconActivated));
}
if (!m_trayIconMenu) {
m_trayIconMenu = new QMenu(this);
QAction *action;
m_trayIconMenu->addMenu(m_ui->menuAdd);
//m_trayIconMenu->addAction(m_ui->actionStart_selected);
//m_trayIconMenu->addAction(m_ui->actionResume_selected_downloads);
m_trayIconMenu->addSeparator();
action = m_trayIconMenu->addAction(tr("Show window"));
connect(action, &QAction::triggered, this, static_cast<void (MainWindow::*)(void)>(&MainWindow::trayIconActivated));
action = m_trayIconMenu->addAction(tr("Quit"));
connect(action, &QAction::triggered, &QApplication::quit);
connect(m_trayIconMenu, &QMenu::destroyed, this, &MainWindow::trayIconDestroyed);
}
m_trayIcon->setContextMenu(m_trayIconMenu);
#ifdef CONFIG_SHOW_TRAY_ICON_ALWAYS
m_trayIcon->show();
#endif
#endif
}
} // namespace QtGui