diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp
index 5983ab8a6..1dc302d0a 100644
--- a/src/gui/mainwindow.cpp
+++ b/src/gui/mainwindow.cpp
@@ -90,7 +90,6 @@
#include "statsdialog.h"
#include "statusbar.h"
#include "torrentcreatordialog.h"
-
#include "transferlistfilterswidget.h"
#include "transferlistmodel.h"
#include "transferlistwidget.h"
@@ -105,6 +104,8 @@
#include "programupdater.h"
#endif
+using namespace std::chrono_literals;
+
namespace
{
#define SETTINGS_KEY(name) "GUI/" name
@@ -148,9 +149,6 @@ MainWindow::MainWindow(QWidget *parent)
, m_posInitialized(false)
, m_forceExit(false)
, m_unlockDlgShowing(false)
-#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
- , m_wasUpdateCheckEnabled(false)
-#endif
, m_hasPython(false)
{
m_ui->setupUi(this);
@@ -317,11 +315,11 @@ MainWindow::MainWindow(QWidget *parent)
connect(m_ui->actionUseAlternativeSpeedLimits, &QAction::triggered, this, &MainWindow::toggleAlternativeSpeeds);
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
- m_programUpdateTimer = new QTimer(this);
- m_programUpdateTimer->setInterval(60 * 60 * 1000);
- m_programUpdateTimer->setSingleShot(true);
- connect(m_programUpdateTimer, &QTimer::timeout, this, &MainWindow::checkProgramUpdate);
- connect(m_ui->actionCheckForUpdates, &QAction::triggered, this, &MainWindow::checkProgramUpdate);
+ connect(m_ui->actionCheckForUpdates, &QAction::triggered, this, [this]() { checkProgramUpdate(true); });
+
+ // trigger an early check on startup
+ if (pref->isUpdateCheckEnabled())
+ checkProgramUpdate(false);
#else
m_ui->actionCheckForUpdates->setVisible(false);
#endif
@@ -804,7 +802,8 @@ void MainWindow::cleanup()
m_preventTimer->stop();
#if (defined(Q_OS_WIN) || defined(Q_OS_MACOS))
- m_programUpdateTimer->stop();
+ if (m_programUpdateTimer)
+ m_programUpdateTimer->stop();
#endif
// remove all child widgets
@@ -1583,15 +1582,21 @@ void MainWindow::loadPreferences(const bool configureSession)
m_propertiesWidget->reloadPreferences();
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
- if (pref->isUpdateCheckEnabled() && !m_wasUpdateCheckEnabled)
+ if (pref->isUpdateCheckEnabled())
{
- m_wasUpdateCheckEnabled = true;
- checkProgramUpdate();
+ if (!m_programUpdateTimer)
+ {
+ m_programUpdateTimer = new QTimer(this);
+ m_programUpdateTimer->setInterval(24h);
+ m_programUpdateTimer->setSingleShot(true);
+ connect(m_programUpdateTimer, &QTimer::timeout, this, [this]() { checkProgramUpdate(false); });
+ m_programUpdateTimer->start();
+ }
}
- else if (!pref->isUpdateCheckEnabled() && m_wasUpdateCheckEnabled)
+ else
{
- m_wasUpdateCheckEnabled = false;
- m_programUpdateTimer->stop();
+ delete m_programUpdateTimer;
+ m_programUpdateTimer = nullptr;
}
#endif
@@ -1897,15 +1902,21 @@ void MainWindow::on_actionDownloadFromURL_triggered()
}
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
-void MainWindow::handleUpdateCheckFinished(const bool updateAvailable, const QString &newVersion, const bool invokedByUser)
+void MainWindow::handleUpdateCheckFinished(ProgramUpdater *updater, const bool invokedByUser)
{
m_ui->actionCheckForUpdates->setEnabled(true);
m_ui->actionCheckForUpdates->setText(tr("&Check for Updates"));
m_ui->actionCheckForUpdates->setToolTip(tr("Check for program updates"));
- QObject *signalSender = sender();
+ const auto cleanup = [this, updater]()
+ {
+ if (m_programUpdateTimer)
+ m_programUpdateTimer->start();
+ updater->deleteLater();
+ };
- if (updateAvailable)
+ const QString newVersion = updater->getNewVersion();
+ if (!newVersion.isEmpty())
{
const QString msg {tr("A new version is available.") + "
"
+ tr("Do you want to download %1?").arg(newVersion) + "
"
@@ -1915,38 +1926,31 @@ void MainWindow::handleUpdateCheckFinished(const bool updateAvailable, const QSt
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setAttribute(Qt::WA_ShowWithoutActivating);
msgBox->setDefaultButton(QMessageBox::Yes);
- connect(msgBox, &QMessageBox::buttonClicked, this, [this, msgBox, signalSender](QAbstractButton *button)
+ connect(msgBox, &QMessageBox::buttonClicked, this, [this, msgBox, updater](QAbstractButton *button)
{
if (msgBox->buttonRole(button) == QMessageBox::YesRole)
{
- // The user want to update, let's download the update
- auto *updater = dynamic_cast(signalSender);
updater->updateProgram();
}
- else
- {
- if (Preferences::instance()->isUpdateCheckEnabled())
- m_programUpdateTimer->start();
- }
-
- signalSender->deleteLater();
});
+ connect(msgBox, &QDialog::finished, this, cleanup);
msgBox->open();
}
- else if (invokedByUser)
+ else
{
- auto *msgBox = new QMessageBox {QMessageBox::Information, QLatin1String("qBittorrent")
- , tr("No updates available.\nYou are already using the latest version.")
- , QMessageBox::Ok, this};
- msgBox->setAttribute(Qt::WA_DeleteOnClose);
- connect(msgBox, &QDialog::finished, this, [this, signalSender](const int)
+ if (invokedByUser)
{
- if (Preferences::instance()->isUpdateCheckEnabled())
- m_programUpdateTimer->start();
-
- signalSender->deleteLater();
- });
- msgBox->open();
+ auto *msgBox = new QMessageBox {QMessageBox::Information, QLatin1String("qBittorrent")
+ , tr("No updates available.\nYou are already using the latest version.")
+ , QMessageBox::Ok, this};
+ msgBox->setAttribute(Qt::WA_DeleteOnClose);
+ connect(msgBox, &QDialog::finished, this, cleanup);
+ msgBox->open();
+ }
+ else
+ {
+ cleanup();
+ }
}
}
#endif
@@ -2106,18 +2110,23 @@ QIcon MainWindow::getSystrayIcon() const
#endif // Q_OS_MACOS
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
-void MainWindow::checkProgramUpdate()
+void MainWindow::checkProgramUpdate(const bool invokedByUser)
{
- m_programUpdateTimer->stop(); // If the user had clicked the menu item
+ if (m_programUpdateTimer)
+ m_programUpdateTimer->stop();
+
m_ui->actionCheckForUpdates->setEnabled(false);
m_ui->actionCheckForUpdates->setText(tr("Checking for Updates..."));
m_ui->actionCheckForUpdates->setToolTip(tr("Already checking for program updates in the background"));
- bool invokedByUser = m_ui->actionCheckForUpdates == qobject_cast(sender());
- ProgramUpdater *updater = new ProgramUpdater(this, invokedByUser);
- connect(updater, &ProgramUpdater::updateCheckFinished, this, &MainWindow::handleUpdateCheckFinished);
+
+ auto *updater = new ProgramUpdater(this);
+ connect(updater, &ProgramUpdater::updateCheckFinished
+ , this, [this, invokedByUser, updater]()
+ {
+ handleUpdateCheckFinished(updater, invokedByUser);
+ });
updater->checkForUpdates();
}
-
#endif
#ifdef Q_OS_WIN
diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h
index 6f0ee5998..63a8096ba 100644
--- a/src/gui/mainwindow.h
+++ b/src/gui/mainwindow.h
@@ -47,6 +47,7 @@ class ExecutionLogWidget;
class LineEdit;
class OptionsDialog;
class PowerManagement;
+class ProgramUpdater;
class PropertiesWidget;
class RSSWidget;
class SearchWidget;
@@ -134,9 +135,6 @@ private slots:
void finishedTorrent(BitTorrent::Torrent *const torrent) const;
void askRecursiveTorrentDownloadConfirmation(BitTorrent::Torrent *const torrent);
void optionsSaved();
-#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
- void handleUpdateCheckFinished(bool updateAvailable, const QString &newVersion, bool invokedByUser);
-#endif
void toggleAlternativeSpeeds();
#ifdef Q_OS_WIN
@@ -177,9 +175,7 @@ private slots:
void on_actionLock_triggered();
// Check for unpaused downloading or seeding torrents and prevent system suspend/sleep according to preferences
void updatePowerManagementState();
-#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
- void checkProgramUpdate();
-#endif
+
void toolbarMenuRequested(const QPoint &point);
void toolbarIconsOnly();
void toolbarTextOnly();
@@ -252,10 +248,13 @@ private:
// Power Management
PowerManagement *m_pwr;
QTimer *m_preventTimer;
-#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
- QTimer *m_programUpdateTimer;
- bool m_wasUpdateCheckEnabled;
-#endif
bool m_hasPython;
QMenu *m_toolbarMenu;
+
+#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
+ void checkProgramUpdate(bool invokedByUser);
+ void handleUpdateCheckFinished(ProgramUpdater *updater, bool invokedByUser);
+
+ QTimer *m_programUpdateTimer = nullptr;
+#endif
};
diff --git a/src/gui/programupdater.cpp b/src/gui/programupdater.cpp
index ee18931eb..7301b83ea 100644
--- a/src/gui/programupdater.cpp
+++ b/src/gui/programupdater.cpp
@@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2021 Mike Tzou (Chocobo1)
* Copyright (C) 2010 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -36,7 +37,6 @@
#include
#include
#include
-#include
#include
#if defined(Q_OS_WIN)
@@ -44,56 +44,82 @@
#endif
#include "base/net/downloadmanager.h"
+#include "base/utils/version.h"
#include "base/version.h"
namespace
{
- const QString RSS_URL {QStringLiteral("https://www.fosshub.com/feed/5b8793a7f9ee5a5c3e97a3b2.xml")};
+ bool isVersionMoreRecent(const QString &remoteVersion)
+ {
+ using Version = Utils::Version;
- QString getStringValue(QXmlStreamReader &xml);
+ try
+ {
+ const Version newVersion {remoteVersion};
+ const Version currentVersion {QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD};
+ if (newVersion == currentVersion)
+ {
+ const bool isDevVersion = QString::fromLatin1(QBT_VERSION_STATUS).contains(
+ QRegularExpression(QLatin1String("(alpha|beta|rc)")));
+ if (isDevVersion)
+ return true;
+ }
+ return (newVersion > currentVersion);
+ }
+ catch (const std::runtime_error &)
+ {
+ return false;
+ }
+ }
}
-ProgramUpdater::ProgramUpdater(QObject *parent, bool invokedByUser)
- : QObject(parent)
- , m_invokedByUser(invokedByUser)
-{
-}
-
-void ProgramUpdater::checkForUpdates()
+void ProgramUpdater::checkForUpdates() const
{
+ const auto RSS_URL = QString::fromLatin1("https://www.fosshub.com/feed/5b8793a7f9ee5a5c3e97a3b2.xml");
// Don't change this User-Agent. In case our updater goes haywire,
// the filehost can identify it and contact us.
Net::DownloadManager::instance()->download(
- Net::DownloadRequest(RSS_URL).userAgent("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)")
- , this, &ProgramUpdater::rssDownloadFinished);
+ Net::DownloadRequest(RSS_URL).userAgent("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)")
+ , this, &ProgramUpdater::rssDownloadFinished);
+}
+
+QString ProgramUpdater::getNewVersion() const
+{
+ return m_newVersion;
}
void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
{
-
if (result.status != Net::DownloadStatus::Success)
{
qDebug() << "Downloading the new qBittorrent updates RSS failed:" << result.errorString;
- emit updateCheckFinished(false, QString(), m_invokedByUser);
+ emit updateCheckFinished();
return;
}
qDebug("Finished downloading the new qBittorrent updates RSS");
+ const auto getStringValue = [](QXmlStreamReader &xml) -> QString
+ {
+ xml.readNext();
+ return (xml.isCharacters() && !xml.isWhitespace())
+ ? xml.text().toString()
+ : QString {};
+ };
+
#ifdef Q_OS_MACOS
const QString OS_TYPE {"Mac OS X"};
#elif defined(Q_OS_WIN)
- const QString OS_TYPE
- {(::IsWindows7OrGreater()
- && QSysInfo::currentCpuArchitecture().endsWith("64"))
+ const QString OS_TYPE {(::IsWindows7OrGreater()
+ && QSysInfo::currentCpuArchitecture().endsWith("64"))
? "Windows x64" : "Windows"};
#endif
- QString version;
- QXmlStreamReader xml(result.data);
bool inItem = false;
+ QString version;
QString updateLink;
QString type;
+ QXmlStreamReader xml(result.data);
while (!xml.atEnd())
{
@@ -121,7 +147,10 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
{
qDebug("Detected version is %s", qUtf8Printable(version));
if (isVersionMoreRecent(version))
- m_updateUrl = updateLink;
+ {
+ m_newVersion = version;
+ m_updateURL = updateLink;
+ }
}
break;
}
@@ -134,51 +163,10 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
}
}
- emit updateCheckFinished(!m_updateUrl.isEmpty(), version, m_invokedByUser);
+ emit updateCheckFinished();
}
-void ProgramUpdater::updateProgram()
+bool ProgramUpdater::updateProgram() const
{
- Q_ASSERT(!m_updateUrl.isEmpty());
- QDesktopServices::openUrl(m_updateUrl);
- return;
-}
-
-bool ProgramUpdater::isVersionMoreRecent(const QString &remoteVersion) const
-{
- const QRegularExpressionMatch regVerMatch = QRegularExpression("([0-9.]+)").match(QBT_VERSION);
- if (regVerMatch.hasMatch())
- {
- const QString localVersion = regVerMatch.captured(1);
- const QVector remoteParts = remoteVersion.splitRef('.');
- const QVector localParts = localVersion.splitRef('.');
-
- for (int i = 0; i < qMin(remoteParts.size(), localParts.size()); ++i)
- {
- if (remoteParts[i].toInt() > localParts[i].toInt())
- return true;
- if (remoteParts[i].toInt() < localParts[i].toInt())
- return false;
- }
- // Compared parts were equal, if remote version is longer, then it's more recent (2.9.2.1 > 2.9.2)
- if (remoteParts.size() > localParts.size())
- return true;
- // versions are equal, check if the local version is a development release, in which case it is older (2.9.2beta < 2.9.2)
- const QRegularExpressionMatch regDevelMatch = QRegularExpression("(alpha|beta|rc)").match(QBT_VERSION);
- if (regDevelMatch.hasMatch())
- return true;
- }
- return false;
-}
-
-namespace
-{
- QString getStringValue(QXmlStreamReader &xml)
- {
- xml.readNext();
- if (xml.isCharacters() && !xml.isWhitespace())
- return xml.text().toString();
-
- return {};
- }
+ return QDesktopServices::openUrl(m_updateURL);
}
diff --git a/src/gui/programupdater.h b/src/gui/programupdater.h
index c843324ab..1a22886a4 100644
--- a/src/gui/programupdater.h
+++ b/src/gui/programupdater.h
@@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2021 Mike Tzou (Chocobo1)
* Copyright (C) 2010 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -29,32 +30,33 @@
#pragma once
#include
+#include
+#include
namespace Net
{
struct DownloadResult;
}
-class ProgramUpdater : public QObject
+class ProgramUpdater final : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(ProgramUpdater)
public:
- explicit ProgramUpdater(QObject *parent = nullptr, bool invokedByUser = false);
+ using QObject::QObject;
- void checkForUpdates();
- void updateProgram();
+ void checkForUpdates() const;
+ QString getNewVersion() const;
+ bool updateProgram() const;
signals:
- void updateCheckFinished(bool updateAvailable, QString version, bool invokedByUser);
+ void updateCheckFinished();
private slots:
void rssDownloadFinished(const Net::DownloadResult &result);
private:
- bool isVersionMoreRecent(const QString &remoteVersion) const;
-
- QString m_updateUrl;
- bool m_invokedByUser;
+ QString m_newVersion;
+ QUrl m_updateURL;
};