Browse Source

Extract desktop integration stuff into separate class

PR #17313.
adaptive-webui-19844
Vladimir Golovnev 2 years ago committed by GitHub
parent
commit
890630944d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 56
      src/app/application.cpp
  2. 8
      src/app/application.h
  3. 2
      src/gui/CMakeLists.txt
  4. 23
      src/gui/advancedsettings.cpp
  5. 273
      src/gui/desktopintegration.cpp
  6. 93
      src/gui/desktopintegration.h
  7. 2
      src/gui/gui.pri
  8. 5
      src/gui/interfaces/iguiapplication.h
  9. 321
      src/gui/mainwindow.cpp
  10. 43
      src/gui/mainwindow.h
  11. 16
      src/gui/search/searchwidget.cpp
  12. 6
      src/gui/search/searchwidget.h

56
src/app/application.cpp

@ -88,6 +88,7 @@
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
#include "gui/addnewtorrentdialog.h" #include "gui/addnewtorrentdialog.h"
#include "gui/desktopintegration.h"
#include "gui/mainwindow.h" #include "gui/mainwindow.h"
#include "gui/shutdownconfirmdialog.h" #include "gui/shutdownconfirmdialog.h"
#include "gui/uithememanager.h" #include "gui/uithememanager.h"
@ -102,6 +103,7 @@ namespace
{ {
#define SETTINGS_KEY(name) u"Application/" name #define SETTINGS_KEY(name) u"Application/" name
#define FILELOGGER_SETTINGS_KEY(name) (SETTINGS_KEY(u"FileLogger/") name) #define FILELOGGER_SETTINGS_KEY(name) (SETTINGS_KEY(u"FileLogger/") name)
#define NOTIFICATIONS_SETTINGS_KEY(name) (SETTINGS_KEY(u"GUI/Notifications/"_qs) name)
const QString LOG_FOLDER = u"logs"_qs; const QString LOG_FOLDER = u"logs"_qs;
const QChar PARAMS_SEPARATOR = u'|'; const QChar PARAMS_SEPARATOR = u'|';
@ -112,7 +114,7 @@ namespace
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
const int DEFAULT_FILELOG_SIZE = 65 * 1024; // 65KiB const int DEFAULT_FILELOG_SIZE = 65 * 1024; // 65KiB
#if !defined(DISABLE_GUI) #ifndef DISABLE_GUI
const int PIXMAP_CACHE_SIZE = 64 * 1024 * 1024; // 64MiB const int PIXMAP_CACHE_SIZE = 64 * 1024 * 1024; // 64MiB
#endif #endif
} }
@ -132,6 +134,9 @@ Application::Application(int &argc, char **argv)
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
, m_processMemoryPriority(SETTINGS_KEY(u"ProcessMemoryPriority"_qs)) , m_processMemoryPriority(SETTINGS_KEY(u"ProcessMemoryPriority"_qs))
#endif #endif
#ifndef DISABLE_GUI
, m_storeNotificationTorrentAdded(NOTIFICATIONS_SETTINGS_KEY(u"TorrentAdded"_qs))
#endif
{ {
qRegisterMetaType<Log::Msg>("Log::Msg"); qRegisterMetaType<Log::Msg>("Log::Msg");
qRegisterMetaType<Log::Peer>("Log::Peer"); qRegisterMetaType<Log::Peer>("Log::Peer");
@ -198,10 +203,25 @@ Application::~Application()
} }
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
DesktopIntegration *Application::desktopIntegration()
{
return m_desktopIntegration;
}
MainWindow *Application::mainWindow() MainWindow *Application::mainWindow()
{ {
return m_window; return m_window;
} }
bool Application::isTorrentAddedNotificationsEnabled() const
{
return m_storeNotificationTorrentAdded;
}
void Application::setTorrentAddedNotificationsEnabled(const bool value)
{
m_storeNotificationTorrentAdded = value;
}
#endif #endif
const QBtCommandLineParameters &Application::commandLineArgs() const const QBtCommandLineParameters &Application::commandLineArgs() const
@ -683,6 +703,40 @@ int Application::exec(const QStringList &params)
#endif // DISABLE_WEBUI #endif // DISABLE_WEBUI
#else #else
UIThemeManager::initInstance(); UIThemeManager::initInstance();
const auto *btSession = BitTorrent::Session::instance();
m_desktopIntegration = new DesktopIntegration(this);
connect(btSession, &BitTorrent::Session::fullDiskError, this
, [this](const BitTorrent::Torrent *torrent, const QString &msg)
{
m_desktopIntegration->showNotification(tr("I/O Error", "i.e: Input/Output Error")
, tr("An I/O error occurred for torrent '%1'.\n Reason: %2"
, "e.g: An error occurred for torrent 'xxx.avi'.\n Reason: disk is full.").arg(torrent->name(), msg));
});
connect(btSession, &BitTorrent::Session::loadTorrentFailed, this
, [this](const QString &error)
{
m_desktopIntegration->showNotification(tr("Error"), tr("Failed to add torrent: %1").arg(error));
});
connect(btSession, &BitTorrent::Session::torrentAdded, this
, [this](const BitTorrent::Torrent *torrent)
{
if (isTorrentAddedNotificationsEnabled())
m_desktopIntegration->showNotification(tr("Torrent added"), tr("'%1' was added.", "e.g: xxx.avi was added.").arg(torrent->name()));
});
connect(btSession, &BitTorrent::Session::torrentFinished, this
, [this](const BitTorrent::Torrent *torrent)
{
m_desktopIntegration->showNotification(tr("Download completed"), tr("'%1' has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(torrent->name()));
});
connect(btSession, &BitTorrent::Session::downloadFromUrlFailed, this
, [this](const QString &url, const QString &reason)
{
m_desktopIntegration->showNotification(tr("URL download error")
, tr("Couldn't download file at URL '%1', reason: %2.").arg(url, reason));
});
m_window = new MainWindow(this); m_window = new MainWindow(this);
#endif // DISABLE_GUI #endif // DISABLE_GUI

8
src/app/application.h

@ -67,6 +67,7 @@ namespace RSS
} }
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
class DesktopIntegration;
class MainWindow; class MainWindow;
using BaseApplication = QApplication; using BaseApplication = QApplication;
@ -124,7 +125,11 @@ public:
#endif #endif
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
DesktopIntegration *desktopIntegration() override;
MainWindow *mainWindow() override; MainWindow *mainWindow() override;
bool isTorrentAddedNotificationsEnabled() const override;
void setTorrentAddedNotificationsEnabled(bool value) override;
#endif #endif
private slots: private slots:
@ -194,6 +199,9 @@ private:
#endif #endif
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
SettingValue<bool> m_storeNotificationTorrentAdded;
DesktopIntegration *m_desktopIntegration = nullptr;
MainWindow *m_window = nullptr; MainWindow *m_window = nullptr;
#endif #endif

2
src/gui/CMakeLists.txt

@ -45,6 +45,7 @@ add_library(qbt_gui STATIC
cookiesdialog.h cookiesdialog.h
cookiesmodel.h cookiesmodel.h
deletionconfirmationdialog.h deletionconfirmationdialog.h
desktopintegration.h
downloadfromurldialog.h downloadfromurldialog.h
executionlogwidget.h executionlogwidget.h
fspathedit.h fspathedit.h
@ -128,6 +129,7 @@ add_library(qbt_gui STATIC
cookiesdialog.cpp cookiesdialog.cpp
cookiesmodel.cpp cookiesmodel.cpp
deletionconfirmationdialog.cpp deletionconfirmationdialog.cpp
desktopintegration.cpp
downloadfromurldialog.cpp downloadfromurldialog.cpp
executionlogwidget.cpp executionlogwidget.cpp
fspathedit.cpp fspathedit.cpp

23
src/gui/advancedsettings.cpp

@ -40,6 +40,7 @@
#include "base/preferences.h" #include "base/preferences.h"
#include "base/unicodestrings.h" #include "base/unicodestrings.h"
#include "gui/addnewtorrentdialog.h" #include "gui/addnewtorrentdialog.h"
#include "gui/desktopintegration.h"
#include "gui/mainwindow.h" #include "gui/mainwindow.h"
#include "interfaces/iguiapplication.h" #include "interfaces/iguiapplication.h"
@ -271,16 +272,15 @@ void AdvancedSettings::saveAdvancedSettings() const
// Stop tracker timeout // Stop tracker timeout
session->setStopTrackerTimeout(m_spinBoxStopTrackerTimeout.value()); session->setStopTrackerTimeout(m_spinBoxStopTrackerTimeout.value());
// Program notification // Program notification
MainWindow *mainWindow = app()->mainWindow(); app()->desktopIntegration()->setNotificationsEnabled(m_checkBoxProgramNotifications.isChecked());
mainWindow->setNotificationsEnabled(m_checkBoxProgramNotifications.isChecked()); #ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
mainWindow->setTorrentAddedNotificationsEnabled(m_checkBoxTorrentAddedNotifications.isChecked()); app()->desktopIntegration()->setNotificationTimeout(m_spinBoxNotificationTimeout.value());
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
mainWindow->setNotificationTimeout(m_spinBoxNotificationTimeout.value());
#endif #endif
app()->setTorrentAddedNotificationsEnabled(m_checkBoxTorrentAddedNotifications.isChecked());
// Reannounce to all trackers when ip/port changed // Reannounce to all trackers when ip/port changed
session->setReannounceWhenAddressChangedEnabled(m_checkBoxReannounceWhenAddressChanged.isChecked()); session->setReannounceWhenAddressChangedEnabled(m_checkBoxReannounceWhenAddressChanged.isChecked());
// Misc GUI properties // Misc GUI properties
mainWindow->setDownloadTrackerFavicon(m_checkBoxTrackerFavicon.isChecked()); app()->mainWindow()->setDownloadTrackerFavicon(m_checkBoxTrackerFavicon.isChecked());
AddNewTorrentDialog::setSavePathHistoryLength(m_spinBoxSavePathHistoryLength.value()); AddNewTorrentDialog::setSavePathHistoryLength(m_spinBoxSavePathHistoryLength.value());
pref->setSpeedWidgetEnabled(m_checkBoxSpeedWidgetEnabled.isChecked()); pref->setSpeedWidgetEnabled(m_checkBoxSpeedWidgetEnabled.isChecked());
#ifndef Q_OS_MACOS #ifndef Q_OS_MACOS
@ -677,17 +677,16 @@ void AdvancedSettings::loadAdvancedSettings()
, &m_spinBoxStopTrackerTimeout); , &m_spinBoxStopTrackerTimeout);
// Program notifications // Program notifications
const MainWindow *mainWindow = app()->mainWindow(); m_checkBoxProgramNotifications.setChecked(app()->desktopIntegration()->isNotificationsEnabled());
m_checkBoxProgramNotifications.setChecked(mainWindow->isNotificationsEnabled());
addRow(PROGRAM_NOTIFICATIONS, tr("Display notifications"), &m_checkBoxProgramNotifications); addRow(PROGRAM_NOTIFICATIONS, tr("Display notifications"), &m_checkBoxProgramNotifications);
// Torrent added notifications // Torrent added notifications
m_checkBoxTorrentAddedNotifications.setChecked(mainWindow->isTorrentAddedNotificationsEnabled()); m_checkBoxTorrentAddedNotifications.setChecked(app()->isTorrentAddedNotificationsEnabled());
addRow(TORRENT_ADDED_NOTIFICATIONS, tr("Display notifications for added torrents"), &m_checkBoxTorrentAddedNotifications); addRow(TORRENT_ADDED_NOTIFICATIONS, tr("Display notifications for added torrents"), &m_checkBoxTorrentAddedNotifications);
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB) #ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
// Notification timeout // Notification timeout
m_spinBoxNotificationTimeout.setMinimum(-1); m_spinBoxNotificationTimeout.setMinimum(-1);
m_spinBoxNotificationTimeout.setMaximum(std::numeric_limits<int>::max()); m_spinBoxNotificationTimeout.setMaximum(std::numeric_limits<int>::max());
m_spinBoxNotificationTimeout.setValue(mainWindow->getNotificationTimeout()); m_spinBoxNotificationTimeout.setValue(app()->desktopIntegration()->notificationTimeout());
m_spinBoxNotificationTimeout.setSpecialValueText(tr("System default")); m_spinBoxNotificationTimeout.setSpecialValueText(tr("System default"));
m_spinBoxNotificationTimeout.setSuffix(tr(" ms", " milliseconds")); m_spinBoxNotificationTimeout.setSuffix(tr(" ms", " milliseconds"));
addRow(NOTIFICATION_TIMEOUT, tr("Notification timeout [0: infinite]"), &m_spinBoxNotificationTimeout); addRow(NOTIFICATION_TIMEOUT, tr("Notification timeout [0: infinite]"), &m_spinBoxNotificationTimeout);
@ -696,7 +695,7 @@ void AdvancedSettings::loadAdvancedSettings()
m_checkBoxReannounceWhenAddressChanged.setChecked(session->isReannounceWhenAddressChangedEnabled()); m_checkBoxReannounceWhenAddressChanged.setChecked(session->isReannounceWhenAddressChangedEnabled());
addRow(REANNOUNCE_WHEN_ADDRESS_CHANGED, tr("Reannounce to all trackers when IP or port changed"), &m_checkBoxReannounceWhenAddressChanged); addRow(REANNOUNCE_WHEN_ADDRESS_CHANGED, tr("Reannounce to all trackers when IP or port changed"), &m_checkBoxReannounceWhenAddressChanged);
// Download tracker's favicon // Download tracker's favicon
m_checkBoxTrackerFavicon.setChecked(mainWindow->isDownloadTrackerFavicon()); m_checkBoxTrackerFavicon.setChecked(app()->mainWindow()->isDownloadTrackerFavicon());
addRow(DOWNLOAD_TRACKER_FAVICON, tr("Download tracker's favicon"), &m_checkBoxTrackerFavicon); addRow(DOWNLOAD_TRACKER_FAVICON, tr("Download tracker's favicon"), &m_checkBoxTrackerFavicon);
// Save path history length // Save path history length
m_spinBoxSavePathHistoryLength.setRange(AddNewTorrentDialog::minPathHistoryLength, AddNewTorrentDialog::maxPathHistoryLength); m_spinBoxSavePathHistoryLength.setRange(AddNewTorrentDialog::minPathHistoryLength, AddNewTorrentDialog::maxPathHistoryLength);

273
src/gui/desktopintegration.cpp

@ -0,0 +1,273 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "desktopintegration.h"
#include <chrono>
#include <QMenu>
#include <QTimer>
#ifndef Q_OS_MACOS
#include <QSystemTrayIcon>
#endif
#include "base/logger.h"
#include "base/preferences.h"
#include "uithememanager.h"
#ifdef Q_OS_MACOS
#include "macutilities.h"
#endif
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
#include "notifications/dbusnotifier.h"
#endif
namespace
{
#ifdef Q_OS_MACOS
DesktopIntegration *desktopIntegrationInstance = nullptr;
bool handleDockClicked(id self, SEL cmd, ...)
{
Q_UNUSED(self);
Q_UNUSED(cmd);
Q_ASSERT(desktopIntegrationInstance);
emit desktopIntegrationInstance->activationRequested();
return true;
}
#endif
}
using namespace std::chrono_literals;
#define SETTINGS_KEY(name) u"GUI/" name
#define NOTIFICATIONS_SETTINGS_KEY(name) (SETTINGS_KEY(u"Notifications/"_qs) name)
DesktopIntegration::DesktopIntegration(QObject *parent)
: QObject(parent)
, m_storeNotificationEnabled {NOTIFICATIONS_SETTINGS_KEY(u"Enabled"_qs), true}
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
, m_storeNotificationTimeOut {NOTIFICATIONS_SETTINGS_KEY(u"Timeout"_qs), -1}
#endif
{
#ifdef Q_OS_MACOS
desktopIntegrationInstance = this;
MacUtils::overrideDockClickHandler(handleDockClicked);
#else
if (Preferences::instance()->systemTrayEnabled())
createTrayIcon(20);
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
if (isNotificationsEnabled())
{
m_notifier = new DBusNotifier(this);
connect(m_notifier, &DBusNotifier::messageClicked, this, &DesktopIntegration::notificationClicked);
}
#endif
#endif
connect(Preferences::instance(), &Preferences::changed, this, &DesktopIntegration::onPreferencesChanged);
}
bool DesktopIntegration::isActive() const
{
#ifdef Q_OS_MACOS
return true;
#else
return (m_systrayIcon != nullptr);
#endif
}
QString DesktopIntegration::toolTip() const
{
return m_toolTip;
}
void DesktopIntegration::setToolTip(const QString &toolTip)
{
if (m_toolTip == toolTip)
return;
#ifndef Q_OS_MACOS
if (m_systrayIcon)
m_systrayIcon->setToolTip(toolTip);
#endif
}
QMenu *DesktopIntegration::menu() const
{
return m_menu;
}
void DesktopIntegration::setMenu(QMenu *menu)
{
if (menu == m_menu)
return;
m_menu = menu;
#ifdef Q_OS_MACOS
if (m_menu)
m_menu->setAsDockMenu();
#else
if (m_systrayIcon)
m_systrayIcon->setContextMenu(m_menu);
#endif
}
bool DesktopIntegration::isNotificationsEnabled() const
{
return m_storeNotificationEnabled;
}
void DesktopIntegration::setNotificationsEnabled(const bool value)
{
if (m_storeNotificationEnabled == value)
return;
m_storeNotificationEnabled = value;
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
if (value)
{
m_notifier = new DBusNotifier(this);
connect(m_notifier, &DBusNotifier::messageClicked, this, &DesktopIntegration::notificationClicked);
}
else
{
delete m_notifier;
m_notifier = nullptr;
}
#endif
}
int DesktopIntegration::notificationTimeout() const
{
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
return m_storeNotificationTimeOut;
#else
return 5000;
#endif
}
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
void DesktopIntegration::setNotificationTimeout(const int value)
{
m_storeNotificationTimeOut = value;
}
#endif
void DesktopIntegration::showNotification(const QString &title, const QString &msg) const
{
if (!isNotificationsEnabled())
return;
#ifdef Q_OS_MACOS
MacUtils::displayNotification(title, msg);
#else
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
m_notifier->showMessage(title, msg, notificationTimeout());
#else
if (m_systrayIcon && QSystemTrayIcon::supportsMessages())
m_systrayIcon->showMessage(title, msg, QSystemTrayIcon::Information, notificationTimeout());
#endif
#endif
}
void DesktopIntegration::onPreferencesChanged()
{
#ifndef Q_OS_MACOS
if (Preferences::instance()->systemTrayEnabled())
{
if (m_systrayIcon)
{
// Reload systray icon
m_systrayIcon->setIcon(UIThemeManager::instance()->getSystrayIcon());
}
else
{
createTrayIcon(20);
}
}
else
{
delete m_systrayIcon;
m_systrayIcon = nullptr;
emit stateChanged();
}
#endif
}
#ifndef Q_OS_MACOS
void DesktopIntegration::createTrayIcon(const int retries)
{
Q_ASSERT(!m_systrayIcon);
if (QSystemTrayIcon::isSystemTrayAvailable())
{
m_systrayIcon = new QSystemTrayIcon(UIThemeManager::instance()->getSystrayIcon(), this);
m_systrayIcon->setToolTip(m_toolTip);
if (m_menu)
m_systrayIcon->setContextMenu(m_menu);
connect(m_systrayIcon, &QSystemTrayIcon::activated, this
, [this](const QSystemTrayIcon::ActivationReason reason)
{
if (reason == QSystemTrayIcon::Trigger)
emit activationRequested();
});
#ifndef QBT_USES_CUSTOMDBUSNOTIFICATIONS
connect(m_systrayIcon, &QSystemTrayIcon::messageClicked, this, &DesktopIntegration::notificationClicked);
#endif
m_systrayIcon->show();
emit stateChanged();
}
else if (retries > 0)
{
LogMsg(tr("System tray icon is not available, retrying..."), Log::WARNING);
QTimer::singleShot(2s, this, [this, retries]()
{
if (Preferences::instance()->systemTrayEnabled())
createTrayIcon(retries - 1);
});
}
else
{
LogMsg(tr("System tray icon is still not available after retries. Disabling it."), Log::WARNING);
Preferences::instance()->setSystemTrayEnabled(false);
}
}
#endif // Q_OS_MACOS

93
src/gui/desktopintegration.h

@ -0,0 +1,93 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <QObject>
#include "base/settingvalue.h"
class QMenu;
#ifndef Q_OS_MACOS
class QSystemTrayIcon;
#endif
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
#define QBT_USES_CUSTOMDBUSNOTIFICATIONS
class DBusNotifier;
#endif
class DesktopIntegration final : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(DesktopIntegration)
public:
explicit DesktopIntegration(QObject *parent = nullptr);
bool isActive() const;
QString toolTip() const;
void setToolTip(const QString &toolTip);
QMenu *menu() const;
void setMenu(QMenu *menu);
bool isNotificationsEnabled() const;
void setNotificationsEnabled(bool value);
int notificationTimeout() const;
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
void setNotificationTimeout(const int value);
#endif
void showNotification(const QString &title, const QString &msg) const;
signals:
void activationRequested();
void notificationClicked();
void stateChanged();
private:
void onPreferencesChanged();
#ifndef Q_OS_MACOS
void createTrayIcon(int retries);
#endif // Q_OS_MACOS
CachedSettingValue<bool> m_storeNotificationEnabled;
QMenu *m_menu = nullptr;
QString m_toolTip;
#ifndef Q_OS_MACOS
QSystemTrayIcon *m_systrayIcon = nullptr;
#endif
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
CachedSettingValue<int> m_storeNotificationTimeOut;
DBusNotifier *m_notifier = nullptr;
#endif
};

2
src/gui/gui.pri

@ -12,6 +12,7 @@ HEADERS += \
$$PWD/cookiesdialog.h \ $$PWD/cookiesdialog.h \
$$PWD/cookiesmodel.h \ $$PWD/cookiesmodel.h \
$$PWD/deletionconfirmationdialog.h \ $$PWD/deletionconfirmationdialog.h \
$$PWD/desktopintegration.h \
$$PWD/downloadfromurldialog.h \ $$PWD/downloadfromurldialog.h \
$$PWD/executionlogwidget.h \ $$PWD/executionlogwidget.h \
$$PWD/fspathedit.h \ $$PWD/fspathedit.h \
@ -95,6 +96,7 @@ SOURCES += \
$$PWD/cookiesdialog.cpp \ $$PWD/cookiesdialog.cpp \
$$PWD/cookiesmodel.cpp \ $$PWD/cookiesmodel.cpp \
$$PWD/deletionconfirmationdialog.cpp \ $$PWD/deletionconfirmationdialog.cpp \
$$PWD/desktopintegration.cpp \
$$PWD/downloadfromurldialog.cpp \ $$PWD/downloadfromurldialog.cpp \
$$PWD/executionlogwidget.cpp \ $$PWD/executionlogwidget.cpp \
$$PWD/fspathedit.cpp \ $$PWD/fspathedit.cpp \

5
src/gui/interfaces/iguiapplication.h

@ -32,6 +32,7 @@
#include "base/interfaces/iapplication.h" #include "base/interfaces/iapplication.h"
class DesktopIntegration;
class MainWindow; class MainWindow;
class IGUIApplication : public IApplication class IGUIApplication : public IApplication
@ -39,5 +40,9 @@ class IGUIApplication : public IApplication
public: public:
virtual ~IGUIApplication() = default; virtual ~IGUIApplication() = default;
virtual DesktopIntegration *desktopIntegration() = 0;
virtual MainWindow *mainWindow() = 0; virtual MainWindow *mainWindow() = 0;
virtual bool isTorrentAddedNotificationsEnabled() const = 0;
virtual void setTorrentAddedNotificationsEnabled(bool value) = 0;
}; };

321
src/gui/mainwindow.cpp

@ -71,6 +71,7 @@
#include "addnewtorrentdialog.h" #include "addnewtorrentdialog.h"
#include "autoexpandabledialog.h" #include "autoexpandabledialog.h"
#include "cookiesdialog.h" #include "cookiesdialog.h"
#include "desktopintegration.h"
#include "downloadfromurldialog.h" #include "downloadfromurldialog.h"
#include "executionlogwidget.h" #include "executionlogwidget.h"
#include "hidabletabwidget.h" #include "hidabletabwidget.h"
@ -106,12 +107,8 @@ namespace
{ {
#define SETTINGS_KEY(name) u"GUI/" name #define SETTINGS_KEY(name) u"GUI/" name
#define EXECUTIONLOG_SETTINGS_KEY(name) (SETTINGS_KEY(u"Log/"_qs) name) #define EXECUTIONLOG_SETTINGS_KEY(name) (SETTINGS_KEY(u"Log/"_qs) name)
#define NOTIFICATIONS_SETTINGS_KEY(name) (SETTINGS_KEY(u"Notifications/"_qs) name)
const std::chrono::seconds PREVENT_SUSPEND_INTERVAL {60}; const std::chrono::seconds PREVENT_SUSPEND_INTERVAL {60};
#if !defined(Q_OS_MACOS)
const int TIME_TRAY_BALLOON = 5000;
#endif
bool isTorrentLink(const QString &str) bool isTorrentLink(const QString &str)
{ {
@ -120,21 +117,6 @@ namespace
|| (!str.startsWith(u"file:", Qt::CaseInsensitive) || (!str.startsWith(u"file:", Qt::CaseInsensitive)
&& Net::DownloadManager::hasSupportedScheme(str)); && Net::DownloadManager::hasSupportedScheme(str));
} }
#ifdef Q_OS_MACOS
MainWindow *dockMainWindowHandle = nullptr;
bool dockClickHandler(id self, SEL cmd, ...)
{
Q_UNUSED(self)
Q_UNUSED(cmd)
if (dockMainWindowHandle && !dockMainWindowHandle->isVisible())
dockMainWindowHandle->activate();
return true;
}
#endif
} }
MainWindow::MainWindow(IGUIApplication *app, QWidget *parent) MainWindow::MainWindow(IGUIApplication *app, QWidget *parent)
@ -143,12 +125,7 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent)
, m_ui(new Ui::MainWindow) , m_ui(new Ui::MainWindow)
, m_storeExecutionLogEnabled(EXECUTIONLOG_SETTINGS_KEY(u"Enabled"_qs)) , m_storeExecutionLogEnabled(EXECUTIONLOG_SETTINGS_KEY(u"Enabled"_qs))
, m_storeDownloadTrackerFavicon(SETTINGS_KEY(u"DownloadTrackerFavicon"_qs)) , m_storeDownloadTrackerFavicon(SETTINGS_KEY(u"DownloadTrackerFavicon"_qs))
, m_storeNotificationEnabled(NOTIFICATIONS_SETTINGS_KEY(u"Enabled"_qs))
, m_storeNotificationTorrentAdded(NOTIFICATIONS_SETTINGS_KEY(u"TorrentAdded"_qs))
, m_storeExecutionLogTypes(EXECUTIONLOG_SETTINGS_KEY(u"Types"_qs), Log::MsgType::ALL) , m_storeExecutionLogTypes(EXECUTIONLOG_SETTINGS_KEY(u"Types"_qs), Log::MsgType::ALL)
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
, m_storeNotificationTimeOut(NOTIFICATIONS_SETTINGS_KEY(u"Timeout"_qs))
#endif
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
@ -197,27 +174,10 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent)
lockMenu->addAction(tr("&Set Password"), this, &MainWindow::defineUILockPassword); lockMenu->addAction(tr("&Set Password"), this, &MainWindow::defineUILockPassword);
lockMenu->addAction(tr("&Clear Password"), this, &MainWindow::clearUILockPassword); lockMenu->addAction(tr("&Clear Password"), this, &MainWindow::clearUILockPassword);
m_ui->actionLock->setMenu(lockMenu); m_ui->actionLock->setMenu(lockMenu);
connect(this, &MainWindow::systemTrayIconCreated, this, [this]()
{
m_ui->actionLock->setVisible(true);
});
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
if (isNotificationsEnabled())
{
m_notifier = new DBusNotifier(this);
connect(m_notifier, &DBusNotifier::messageClicked, this, &MainWindow::balloonClicked);
}
#endif
// Creating Bittorrent session // Creating Bittorrent session
updateAltSpeedsBtn(BitTorrent::Session::instance()->isAltGlobalSpeedLimitEnabled()); updateAltSpeedsBtn(BitTorrent::Session::instance()->isAltGlobalSpeedLimitEnabled());
connect(BitTorrent::Session::instance(), &BitTorrent::Session::fullDiskError, this, &MainWindow::fullDiskError);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::loadTorrentFailed, this, &MainWindow::addTorrentFailed);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentAdded,this, &MainWindow::torrentNew);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentFinished, this, &MainWindow::finishedTorrent);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::downloadFromUrlFailed, this, &MainWindow::handleDownloadFromUrlFailure);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::speedLimitModeChanged, this, &MainWindow::updateAltSpeedsBtn); connect(BitTorrent::Session::instance(), &BitTorrent::Session::speedLimitModeChanged, this, &MainWindow::updateAltSpeedsBtn);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::recursiveTorrentDownloadPossible, this, &MainWindow::askRecursiveTorrentDownloadConfirmation); connect(BitTorrent::Session::instance(), &BitTorrent::Session::recursiveTorrentDownloadPossible, this, &MainWindow::askRecursiveTorrentDownloadConfirmation);
@ -311,7 +271,7 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent)
connect(m_ui->actionDecreaseQueuePos, &QAction::triggered, m_transferListWidget, &TransferListWidget::decreaseQueuePosSelectedTorrents); connect(m_ui->actionDecreaseQueuePos, &QAction::triggered, m_transferListWidget, &TransferListWidget::decreaseQueuePosSelectedTorrents);
connect(m_ui->actionBottomQueuePos, &QAction::triggered, m_transferListWidget, &TransferListWidget::bottomQueuePosSelectedTorrents); connect(m_ui->actionBottomQueuePos, &QAction::triggered, m_transferListWidget, &TransferListWidget::bottomQueuePosSelectedTorrents);
#ifndef Q_OS_MACOS #ifndef Q_OS_MACOS
connect(m_ui->actionToggleVisibility, &QAction::triggered, this, [this]() { toggleVisibility(); }); connect(m_ui->actionToggleVisibility, &QAction::triggered, this, &MainWindow::toggleVisibility);
#endif #endif
connect(m_ui->actionMinimize, &QAction::triggered, this, &MainWindow::minimizeWindow); connect(m_ui->actionMinimize, &QAction::triggered, this, &MainWindow::minimizeWindow);
connect(m_ui->actionUseAlternativeSpeedLimits, &QAction::triggered, this, &MainWindow::toggleAlternativeSpeeds); connect(m_ui->actionUseAlternativeSpeedLimits, &QAction::triggered, this, &MainWindow::toggleAlternativeSpeeds);
@ -400,6 +360,25 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent)
// Load Window state and sizes // Load Window state and sizes
readSettings(); readSettings();
app->desktopIntegration()->setMenu(createDesktopIntegrationMenu());
#ifndef Q_OS_MACOS
m_ui->actionLock->setVisible(app->desktopIntegration()->isActive());
connect(app->desktopIntegration(), &DesktopIntegration::stateChanged, this, [this, app]()
{
m_ui->actionLock->setVisible(app->desktopIntegration()->isActive());
});
#endif
connect(app->desktopIntegration(), &DesktopIntegration::notificationClicked, this, &MainWindow::desktopNotificationClicked);
connect(app->desktopIntegration(), &DesktopIntegration::activationRequested, this, [this]()
{
#ifdef Q_OS_MACOS
if (!isVisible())
activate();
#else
toggleVisibility();
#endif
});
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
// Make sure the Window is visible if we don't have a tray icon // Make sure the Window is visible if we don't have a tray icon
if (pref->startMinimized()) if (pref->startMinimized())
@ -413,7 +392,7 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent)
raise(); raise();
} }
#else #else
if (m_systrayIcon) if (app->desktopIntegration()->isActive())
{ {
if (!(pref->startMinimized() || m_uiLocked)) if (!(pref->startMinimized() || m_uiLocked))
{ {
@ -429,7 +408,7 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent)
hide(); hide();
if (!pref->minimizeToTrayNotified()) if (!pref->minimizeToTrayNotified())
{ {
showNotificationBalloon(tr("qBittorrent is minimized to tray"), tr("This behavior can be changed in the settings. You won't be reminded again.")); app->desktopIntegration()->showNotification(tr("qBittorrent is minimized to tray"), tr("This behavior can be changed in the settings. You won't be reminded again."));
pref->setMinimizeToTrayNotified(true); pref->setMinimizeToTrayNotified(true);
} }
} }
@ -498,11 +477,6 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent)
} }
} }
#endif #endif
#ifdef Q_OS_MACOS
setupDockClickHandler();
createTrayIconMenu();
m_trayIconMenu->setAsDockMenu();
#endif
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
@ -531,54 +505,6 @@ void MainWindow::setExecutionLogMsgTypes(const Log::MsgTypes value)
m_storeExecutionLogTypes = value; m_storeExecutionLogTypes = value;
} }
bool MainWindow::isNotificationsEnabled() const
{
return m_storeNotificationEnabled.get(true);
}
void MainWindow::setNotificationsEnabled(const bool value)
{
if (m_storeNotificationEnabled == value)
return;
m_storeNotificationEnabled = value;
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
if (value)
{
m_notifier = new DBusNotifier(this);
connect(m_notifier, &DBusNotifier::messageClicked, this, &MainWindow::balloonClicked);
}
else
{
delete m_notifier;
m_notifier = nullptr;
}
#endif
}
bool MainWindow::isTorrentAddedNotificationsEnabled() const
{
return m_storeNotificationTorrentAdded;
}
void MainWindow::setTorrentAddedNotificationsEnabled(const bool value)
{
m_storeNotificationTorrentAdded = value;
}
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
int MainWindow::getNotificationTimeout() const
{
return m_storeNotificationTimeOut.get(-1);
}
void MainWindow::setNotificationTimeout(const int value)
{
m_storeNotificationTimeOut = value;
}
#endif
bool MainWindow::isDownloadTrackerFavicon() const bool MainWindow::isDownloadTrackerFavicon() const
{ {
return m_storeDownloadTrackerFavicon; return m_storeDownloadTrackerFavicon;
@ -721,7 +647,7 @@ void MainWindow::on_actionLock_triggered()
// Lock the interface // Lock the interface
m_uiLocked = true; m_uiLocked = true;
pref->setUILocked(true); pref->setUILocked(true);
m_trayIconMenu->setEnabled(false); app()->desktopIntegration()->menu()->setEnabled(false);
hide(); hide();
} }
@ -778,7 +704,7 @@ void MainWindow::displaySearchTab(bool enable)
// RSS tab // RSS tab
if (!m_searchWidget) if (!m_searchWidget)
{ {
m_searchWidget = new SearchWidget(this); m_searchWidget = new SearchWidget(app(), this);
m_tabs->insertTab(1, m_searchWidget, m_tabs->insertTab(1, m_searchWidget,
#ifndef Q_OS_MACOS #ifndef Q_OS_MACOS
UIThemeManager::instance()->getIcon(u"edit-find"_qs), UIThemeManager::instance()->getIcon(u"edit-find"_qs),
@ -873,7 +799,7 @@ void MainWindow::readSettings()
m_posInitialized = true; m_posInitialized = true;
} }
void MainWindow::balloonClicked() void MainWindow::desktopNotificationClicked()
{ {
if (isHidden()) if (isHidden())
{ {
@ -892,32 +818,6 @@ void MainWindow::balloonClicked()
activateWindow(); activateWindow();
} }
void MainWindow::addTorrentFailed(const QString &error) const
{
showNotificationBalloon(tr("Error"), tr("Failed to add torrent: %1").arg(error));
}
// called when a torrent was added
void MainWindow::torrentNew(BitTorrent::Torrent *const torrent) const
{
if (isTorrentAddedNotificationsEnabled())
showNotificationBalloon(tr("Torrent added"), tr("'%1' was added.", "e.g: xxx.avi was added.").arg(torrent->name()));
}
// called when a torrent has finished
void MainWindow::finishedTorrent(BitTorrent::Torrent *const torrent) const
{
showNotificationBalloon(tr("Download completed"), tr("'%1' has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(torrent->name()));
}
// Notification when disk is full
void MainWindow::fullDiskError(BitTorrent::Torrent *const torrent, const QString &msg) const
{
showNotificationBalloon(tr("I/O Error", "i.e: Input/Output Error")
, tr("An I/O error occurred for torrent '%1'.\n Reason: %2"
, "e.g: An error occurred for torrent 'xxx.avi'.\n Reason: disk is full.").arg(torrent->name(), msg));
}
void MainWindow::createKeyboardShortcuts() void MainWindow::createKeyboardShortcuts()
{ {
m_ui->actionCreateTorrent->setShortcut(QKeySequence::New); m_ui->actionCreateTorrent->setShortcut(QKeySequence::New);
@ -1026,13 +926,6 @@ void MainWindow::askRecursiveTorrentDownloadConfirmation(BitTorrent::Torrent *co
confirmBox->open(); confirmBox->open();
} }
void MainWindow::handleDownloadFromUrlFailure(const QString &url, const QString &reason) const
{
// Display a message box
showNotificationBalloon(tr("URL download error")
, tr("Couldn't download file at URL '%1', reason: %2.").arg(url, reason));
}
void MainWindow::on_actionSetGlobalSpeedLimits_triggered() void MainWindow::on_actionSetGlobalSpeedLimits_triggered()
{ {
auto dialog = new SpeedLimitDialog {this}; auto dialog = new SpeedLimitDialog {this};
@ -1098,7 +991,7 @@ bool MainWindow::unlockUI()
m_uiLocked = false; m_uiLocked = false;
pref->setUILocked(false); pref->setUILocked(false);
m_trayIconMenu->setEnabled(true); app()->desktopIntegration()->menu()->setEnabled(true);
return true; return true;
} }
@ -1115,11 +1008,8 @@ void MainWindow::notifyOfUpdate(const QString &)
#ifndef Q_OS_MACOS #ifndef Q_OS_MACOS
// Toggle Main window visibility // Toggle Main window visibility
void MainWindow::toggleVisibility(const QSystemTrayIcon::ActivationReason reason) void MainWindow::toggleVisibility()
{
switch (reason)
{ {
case QSystemTrayIcon::Trigger:
if (isHidden()) if (isHidden())
{ {
if (m_uiLocked && !unlockUI()) // Ask for UI lock password if (m_uiLocked && !unlockUI()) // Ask for UI lock password
@ -1137,11 +1027,6 @@ void MainWindow::toggleVisibility(const QSystemTrayIcon::ActivationReason reason
{ {
hide(); hide();
} }
break;
default:
break;
}
} }
#endif // Q_OS_MACOS #endif // Q_OS_MACOS
@ -1245,13 +1130,13 @@ void MainWindow::closeEvent(QCloseEvent *e)
} }
#else #else
const bool goToSystrayOnExit = pref->closeToTray(); const bool goToSystrayOnExit = pref->closeToTray();
if (!m_forceExit && m_systrayIcon && goToSystrayOnExit && !this->isHidden()) if (!m_forceExit && app()->desktopIntegration()->isActive() && goToSystrayOnExit && !this->isHidden())
{ {
e->ignore(); e->ignore();
QMetaObject::invokeMethod(this, &QWidget::hide, Qt::QueuedConnection); QMetaObject::invokeMethod(this, &QWidget::hide, Qt::QueuedConnection);
if (!pref->closeToTrayNotified()) if (!pref->closeToTrayNotified())
{ {
showNotificationBalloon(tr("qBittorrent is closed to tray"), tr("This behavior can be changed in the settings. You won't be reminded again.")); app()->desktopIntegration()->showNotification(tr("qBittorrent is closed to tray"), tr("This behavior can be changed in the settings. You won't be reminded again."));
pref->setCloseToTrayNotified(true); pref->setCloseToTrayNotified(true);
} }
return; return;
@ -1287,14 +1172,9 @@ void MainWindow::closeEvent(QCloseEvent *e)
} }
// Disable some UI to prevent user interactions // Disable some UI to prevent user interactions
#ifndef Q_OS_MACOS app()->desktopIntegration()->disconnect();
if (m_systrayIcon) app()->desktopIntegration()->setToolTip(tr("qBittorrent is shutting down..."));
{ app()->desktopIntegration()->menu()->setEnabled(false);
m_systrayIcon->disconnect();
m_systrayIcon->setToolTip(tr("qBittorrent is shutting down..."));
m_trayIconMenu->setEnabled(false);
}
#endif
// Accept exit // Accept exit
e->accept(); e->accept();
@ -1328,14 +1208,13 @@ bool MainWindow::event(QEvent *e)
switch (e->type()) switch (e->type())
{ {
case QEvent::WindowStateChange: case QEvent::WindowStateChange:
{
qDebug("Window change event"); qDebug("Window change event");
// Now check to see if the window is minimised // Now check to see if the window is minimised
if (isMinimized()) if (isMinimized())
{ {
qDebug("minimisation"); qDebug("minimisation");
Preferences *const pref = Preferences::instance(); Preferences *const pref = Preferences::instance();
if (m_systrayIcon && pref->minimizeToTray()) if (app()->desktopIntegration()->isActive() && pref->minimizeToTray())
{ {
qDebug() << "Has active window:" << (qApp->activeWindow() != nullptr); qDebug() << "Has active window:" << (qApp->activeWindow() != nullptr);
// Check if there is a modal window // Check if there is a modal window
@ -1350,7 +1229,7 @@ bool MainWindow::event(QEvent *e)
QMetaObject::invokeMethod(this, &QWidget::hide, Qt::QueuedConnection); QMetaObject::invokeMethod(this, &QWidget::hide, Qt::QueuedConnection);
if (!pref->minimizeToTrayNotified()) if (!pref->minimizeToTrayNotified())
{ {
showNotificationBalloon(tr("qBittorrent is minimized to tray"), tr("This behavior can be changed in the settings. You won't be reminded again.")); app()->desktopIntegration()->showNotification(tr("qBittorrent is minimized to tray"), tr("This behavior can be changed in the settings. You won't be reminded again."));
pref->setMinimizeToTrayNotified(true); pref->setMinimizeToTrayNotified(true);
} }
return true; return true;
@ -1358,11 +1237,10 @@ bool MainWindow::event(QEvent *e)
} }
} }
break; break;
}
case QEvent::ToolBarChange: case QEvent::ToolBarChange:
{ {
qDebug("MAC: Received a toolbar change event!"); qDebug("MAC: Received a toolbar change event!");
bool ret = QMainWindow::event(e); const bool ret = QMainWindow::event(e);
qDebug("MAC: new toolbar visibility is %d", !m_ui->actionTopToolBar->isChecked()); qDebug("MAC: new toolbar visibility is %d", !m_ui->actionTopToolBar->isChecked());
m_ui->actionTopToolBar->toggle(); m_ui->actionTopToolBar->toggle();
@ -1443,14 +1321,6 @@ void MainWindow::dragEnterEvent(QDragEnterEvent *event)
event->acceptProposedAction(); event->acceptProposedAction();
} }
#ifdef Q_OS_MACOS
void MainWindow::setupDockClickHandler()
{
dockMainWindowHandle = this;
MacUtils::overrideDockClickHandler(dockClickHandler);
}
#endif // Q_OS_MACOS
/***************************************************** /*****************************************************
* * * *
* Torrent * * Torrent *
@ -1552,28 +1422,6 @@ void MainWindow::loadPreferences()
{ {
const Preferences *pref = Preferences::instance(); const Preferences *pref = Preferences::instance();
#ifndef Q_OS_MACOS
// system tray icon
if (pref->systemTrayEnabled())
{
if (m_systrayIcon)
{
// Reload systray icon
m_systrayIcon->setIcon(UIThemeManager::instance()->getSystrayIcon());
}
else
{
createTrayIcon(20);
}
}
else
{
delete m_systrayIcon;
delete m_trayIconMenu;
m_ui->actionLock->setVisible(false);
}
#endif
// General // General
if (pref->isToolbarDisplayed()) if (pref->isToolbarDisplayed())
{ {
@ -1680,12 +1528,12 @@ void MainWindow::reloadSessionStats()
MacUtils::setBadgeLabelText({}); MacUtils::setBadgeLabelText({});
} }
#else #else
if (m_systrayIcon) if (app()->desktopIntegration()->isActive())
{ {
const auto toolTip = u"%1\n%2"_qs.arg( const auto toolTip = u"%1\n%2"_qs.arg(
tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate, true)) tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate, true))
, tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadUploadRate, true))); , tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadUploadRate, true)));
m_systrayIcon->setToolTip(toolTip); // tray icon app()->desktopIntegration()->setToolTip(toolTip); // tray icon
} }
#endif // Q_OS_MACOS #endif // Q_OS_MACOS
@ -1707,21 +1555,6 @@ void MainWindow::reloadTorrentStats(const QVector<BitTorrent::Torrent *> &torren
} }
} }
void MainWindow::showNotificationBalloon(const QString &title, const QString &msg) const
{
if (!isNotificationsEnabled())
return;
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
m_notifier->showMessage(title, msg, getNotificationTimeout());
#elif defined(Q_OS_MACOS)
MacUtils::displayNotification(title, msg);
#else
if (m_systrayIcon && QSystemTrayIcon::supportsMessages())
m_systrayIcon->showMessage(title, msg, QSystemTrayIcon::Information, TIME_TRAY_BALLOON);
#endif
}
/***************************************************** /*****************************************************
* * * *
* Utils * * Utils *
@ -1746,82 +1579,40 @@ void MainWindow::downloadFromURLList(const QStringList &urlList)
* * * *
*****************************************************/ *****************************************************/
#ifndef Q_OS_MACOS QMenu *MainWindow::createDesktopIntegrationMenu()
void MainWindow::createTrayIcon(const int retries)
{
if (m_systrayIcon)
return;
if (QSystemTrayIcon::isSystemTrayAvailable())
{
m_systrayIcon = new QSystemTrayIcon(UIThemeManager::instance()->getSystrayIcon(), this);
createTrayIconMenu();
m_systrayIcon->setContextMenu(m_trayIconMenu);
connect(m_systrayIcon, &QSystemTrayIcon::activated, this, &MainWindow::toggleVisibility);
#ifndef QBT_USES_CUSTOMDBUSNOTIFICATIONS
connect(m_systrayIcon, &QSystemTrayIcon::messageClicked, this, &MainWindow::balloonClicked);
#endif
m_systrayIcon->show();
emit systemTrayIconCreated();
}
else
{
if (retries > 0)
{
LogMsg(tr("System tray icon is not available, retrying..."), Log::WARNING);
QTimer::singleShot(2s, this, [this, retries]()
{
if (Preferences::instance()->systemTrayEnabled())
createTrayIcon(retries - 1);
});
}
else
{
LogMsg(tr("System tray icon is still not available after retries. Disabling it."), Log::WARNING);
Preferences::instance()->setSystemTrayEnabled(false);
}
}
}
#endif // Q_OS_MACOS
void MainWindow::createTrayIconMenu()
{ {
if (m_trayIconMenu) auto *menu = new QMenu(this);
return;
m_trayIconMenu = new QMenu(this);
#ifndef Q_OS_MACOS #ifndef Q_OS_MACOS
connect(m_trayIconMenu, &QMenu::aboutToShow, this, [this]() connect(menu, &QMenu::aboutToShow, this, [this]()
{ {
m_ui->actionToggleVisibility->setText(isVisible() ? tr("Hide") : tr("Show")); m_ui->actionToggleVisibility->setText(isVisible() ? tr("Hide") : tr("Show"));
}); });
m_trayIconMenu->addAction(m_ui->actionToggleVisibility); menu->addAction(m_ui->actionToggleVisibility);
m_trayIconMenu->addSeparator(); menu->addSeparator();
#endif #endif
m_trayIconMenu->addAction(m_ui->actionOpen); menu->addAction(m_ui->actionOpen);
m_trayIconMenu->addAction(m_ui->actionDownloadFromURL); menu->addAction(m_ui->actionDownloadFromURL);
m_trayIconMenu->addSeparator(); menu->addSeparator();
m_trayIconMenu->addAction(m_ui->actionUseAlternativeSpeedLimits); menu->addAction(m_ui->actionUseAlternativeSpeedLimits);
m_trayIconMenu->addAction(m_ui->actionSetGlobalSpeedLimits); menu->addAction(m_ui->actionSetGlobalSpeedLimits);
m_trayIconMenu->addSeparator(); menu->addSeparator();
m_trayIconMenu->addAction(m_ui->actionStartAll); menu->addAction(m_ui->actionStartAll);
m_trayIconMenu->addAction(m_ui->actionPauseAll); menu->addAction(m_ui->actionPauseAll);
#ifndef Q_OS_MACOS #ifndef Q_OS_MACOS
m_trayIconMenu->addSeparator(); menu->addSeparator();
m_trayIconMenu->addAction(m_ui->actionExit); menu->addAction(m_ui->actionExit);
#endif #endif
if (m_uiLocked) if (m_uiLocked)
m_trayIconMenu->setEnabled(false); menu->setEnabled(false);
return menu;
} }
void MainWindow::updateAltSpeedsBtn(const bool alternative) void MainWindow::updateAltSpeedsBtn(const bool alternative)

43
src/gui/mainwindow.h

@ -31,10 +31,6 @@
#include <QMainWindow> #include <QMainWindow>
#include <QPointer> #include <QPointer>
#ifndef Q_OS_MACOS
#include <QSystemTrayIcon>
#endif
#include "base/bittorrent/torrent.h" #include "base/bittorrent/torrent.h"
#include "base/logger.h" #include "base/logger.h"
#include "base/settingvalue.h" #include "base/settingvalue.h"
@ -97,14 +93,6 @@ public:
void setExecutionLogMsgTypes(Log::MsgTypes value); void setExecutionLogMsgTypes(Log::MsgTypes value);
// Notifications properties // Notifications properties
bool isNotificationsEnabled() const;
void setNotificationsEnabled(bool value);
bool isTorrentAddedNotificationsEnabled() const;
void setTorrentAddedNotificationsEnabled(bool value);
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
int getNotificationTimeout() const;
void setNotificationTimeout(int value);
#endif
// Misc properties // Misc properties
bool isDownloadTrackerFavicon() const; bool isDownloadTrackerFavicon() const;
@ -113,19 +101,12 @@ public:
void activate(); void activate();
void cleanup(); void cleanup();
void showNotificationBalloon(const QString &title, const QString &msg) const;
signals:
void systemTrayIconCreated();
private slots: private slots:
void showFilterContextMenu(); void showFilterContextMenu();
void balloonClicked(); void desktopNotificationClicked();
void writeSettings(); void writeSettings();
void writeSplitterSettings(); void writeSplitterSettings();
void readSettings(); void readSettings();
void fullDiskError(BitTorrent::Torrent *const torrent, const QString &msg) const;
void handleDownloadFromUrlFailure(const QString &, const QString &) const;
void tabChanged(int newTab); void tabChanged(int newTab);
bool defineUILockPassword(); bool defineUILockPassword();
void clearUILockPassword(); void clearUILockPassword();
@ -143,9 +124,6 @@ private slots:
void reloadSessionStats(); void reloadSessionStats();
void reloadTorrentStats(const QVector<BitTorrent::Torrent *> &torrents); void reloadTorrentStats(const QVector<BitTorrent::Torrent *> &torrents);
void loadPreferences(); void loadPreferences();
void addTorrentFailed(const QString &error) const;
void torrentNew(BitTorrent::Torrent *const torrent) const;
void finishedTorrent(BitTorrent::Torrent *const torrent) const;
void askRecursiveTorrentDownloadConfirmation(BitTorrent::Torrent *const torrent); void askRecursiveTorrentDownloadConfirmation(BitTorrent::Torrent *const torrent);
void optionsSaved(); void optionsSaved();
void toggleAlternativeSpeeds(); void toggleAlternativeSpeeds();
@ -199,16 +177,11 @@ private slots:
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
void on_actionCloseWindow_triggered(); void on_actionCloseWindow_triggered();
#else #else
void toggleVisibility(const QSystemTrayIcon::ActivationReason reason = QSystemTrayIcon::Trigger); void toggleVisibility();
#endif #endif
private: private:
void createTrayIconMenu(); QMenu *createDesktopIntegrationMenu();
#ifdef Q_OS_MACOS
void setupDockClickHandler();
#else
void createTrayIcon(int retries);
#endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
void installPython(); void installPython();
#endif #endif
@ -238,9 +211,6 @@ private:
QPointer<TorrentCreatorDialog> m_createTorrentDlg; QPointer<TorrentCreatorDialog> m_createTorrentDlg;
QPointer<DownloadFromURLDialog> m_downloadFromURLDialog; QPointer<DownloadFromURLDialog> m_downloadFromURLDialog;
#ifndef Q_OS_MACOS
QPointer<QSystemTrayIcon> m_systrayIcon;
#endif
QPointer<QMenu> m_trayIconMenu; QPointer<QMenu> m_trayIconMenu;
TransferListWidget *m_transferListWidget = nullptr; TransferListWidget *m_transferListWidget = nullptr;
@ -267,15 +237,8 @@ private:
SettingValue<bool> m_storeExecutionLogEnabled; SettingValue<bool> m_storeExecutionLogEnabled;
SettingValue<bool> m_storeDownloadTrackerFavicon; SettingValue<bool> m_storeDownloadTrackerFavicon;
SettingValue<bool> m_storeNotificationEnabled;
SettingValue<bool> m_storeNotificationTorrentAdded;
CachedSettingValue<Log::MsgTypes> m_storeExecutionLogTypes; CachedSettingValue<Log::MsgTypes> m_storeExecutionLogTypes;
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
SettingValue<int> m_storeNotificationTimeOut;
DBusNotifier *m_notifier = nullptr;
#endif
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
void checkProgramUpdate(bool invokedByUser); void checkProgramUpdate(bool invokedByUser);
void handleUpdateCheckFinished(ProgramUpdater *updater, bool invokedByUser); void handleUpdateCheckFinished(ProgramUpdater *updater, bool invokedByUser);

16
src/gui/search/searchwidget.cpp

@ -52,6 +52,7 @@
#include "base/search/searchhandler.h" #include "base/search/searchhandler.h"
#include "base/search/searchpluginmanager.h" #include "base/search/searchpluginmanager.h"
#include "base/utils/foreignapps.h" #include "base/utils/foreignapps.h"
#include "gui/desktopintegration.h"
#include "gui/mainwindow.h" #include "gui/mainwindow.h"
#include "gui/uithememanager.h" #include "gui/uithememanager.h"
#include "pluginselectdialog.h" #include "pluginselectdialog.h"
@ -83,10 +84,11 @@ namespace
} }
} }
SearchWidget::SearchWidget(MainWindow *mainWindow) SearchWidget::SearchWidget(IGUIApplication *app, MainWindow *mainWindow)
: QWidget(mainWindow) : QWidget(mainWindow)
, m_ui(new Ui::SearchWidget()) , GUIApplicationComponent(app)
, m_mainWindow(mainWindow) , m_ui {new Ui::SearchWidget()}
, m_mainWindow {mainWindow}
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
m_ui->tabWidget->tabBar()->installEventFilter(this); m_ui->tabWidget->tabBar()->installEventFilter(this);
@ -304,7 +306,7 @@ void SearchWidget::on_searchButton_clicked()
{ {
if (!Utils::ForeignApps::pythonInfo().isValid()) if (!Utils::ForeignApps::pythonInfo().isValid())
{ {
m_mainWindow->showNotificationBalloon(tr("Search Engine"), tr("Please install Python to use the Search Engine.")); app()->desktopIntegration()->showNotification(tr("Search Engine"), tr("Please install Python to use the Search Engine."));
return; return;
} }
@ -370,12 +372,12 @@ void SearchWidget::tabStatusChanged(QWidget *tab)
{ {
Q_ASSERT(m_activeSearchTab->status() != SearchJobWidget::Status::Ongoing); Q_ASSERT(m_activeSearchTab->status() != SearchJobWidget::Status::Ongoing);
if (m_mainWindow->isNotificationsEnabled() && (m_mainWindow->currentTabWidget() != this)) if (app()->desktopIntegration()->isNotificationsEnabled() && (m_mainWindow->currentTabWidget() != this))
{ {
if (m_activeSearchTab->status() == SearchJobWidget::Status::Error) if (m_activeSearchTab->status() == SearchJobWidget::Status::Error)
m_mainWindow->showNotificationBalloon(tr("Search Engine"), tr("Search has failed")); app()->desktopIntegration()->showNotification(tr("Search Engine"), tr("Search has failed"));
else else
m_mainWindow->showNotificationBalloon(tr("Search Engine"), tr("Search has finished")); app()->desktopIntegration()->showNotification(tr("Search Engine"), tr("Search has finished"));
} }
m_activeSearchTab = nullptr; m_activeSearchTab = nullptr;

6
src/gui/search/searchwidget.h

@ -34,6 +34,8 @@
#include <QPointer> #include <QPointer>
#include <QWidget> #include <QWidget>
#include "gui/guiapplicationcomponent.h"
class QEvent; class QEvent;
class QObject; class QObject;
class QTabWidget; class QTabWidget;
@ -46,13 +48,13 @@ namespace Ui
class SearchWidget; class SearchWidget;
} }
class SearchWidget : public QWidget class SearchWidget : public QWidget, public GUIApplicationComponent
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY_MOVE(SearchWidget) Q_DISABLE_COPY_MOVE(SearchWidget)
public: public:
explicit SearchWidget(MainWindow *mainWindow); explicit SearchWidget(IGUIApplication *app, MainWindow *mainWindow);
~SearchWidget() override; ~SearchWidget() override;
void giveFocusToSearchInput(); void giveFocusToSearchInput();

Loading…
Cancel
Save