From 890630944d0b2d274f14cd34ca71719de6f339fc Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Sat, 9 Jul 2022 08:06:22 +0300 Subject: [PATCH] Extract desktop integration stuff into separate class PR #17313. --- src/app/application.cpp | 56 +++- src/app/application.h | 8 + src/gui/CMakeLists.txt | 2 + src/gui/advancedsettings.cpp | 23 +- src/gui/desktopintegration.cpp | 273 ++++++++++++++++ .../searchwidget.h => desktopintegration.h} | 96 +++--- src/gui/gui.pri | 2 + src/gui/interfaces/iguiapplication.h | 5 + src/gui/mainwindow.cpp | 361 +++++---------------- src/gui/mainwindow.h | 43 +-- src/gui/search/searchwidget.cpp | 16 +- src/gui/search/searchwidget.h | 6 +- 12 files changed, 499 insertions(+), 392 deletions(-) create mode 100644 src/gui/desktopintegration.cpp copy src/gui/{search/searchwidget.h => desktopintegration.h} (50%) diff --git a/src/app/application.cpp b/src/app/application.cpp index 935780cdb..9af9aee7c 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -88,6 +88,7 @@ #ifndef DISABLE_GUI #include "gui/addnewtorrentdialog.h" +#include "gui/desktopintegration.h" #include "gui/mainwindow.h" #include "gui/shutdownconfirmdialog.h" #include "gui/uithememanager.h" @@ -102,6 +103,7 @@ namespace { #define SETTINGS_KEY(name) u"Application/" 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 QChar PARAMS_SEPARATOR = u'|'; @@ -112,7 +114,7 @@ namespace const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB const int DEFAULT_FILELOG_SIZE = 65 * 1024; // 65KiB -#if !defined(DISABLE_GUI) +#ifndef DISABLE_GUI const int PIXMAP_CACHE_SIZE = 64 * 1024 * 1024; // 64MiB #endif } @@ -132,6 +134,9 @@ Application::Application(int &argc, char **argv) #ifdef Q_OS_WIN , m_processMemoryPriority(SETTINGS_KEY(u"ProcessMemoryPriority"_qs)) #endif +#ifndef DISABLE_GUI + , m_storeNotificationTorrentAdded(NOTIFICATIONS_SETTINGS_KEY(u"TorrentAdded"_qs)) +#endif { qRegisterMetaType("Log::Msg"); qRegisterMetaType("Log::Peer"); @@ -198,10 +203,25 @@ Application::~Application() } #ifndef DISABLE_GUI +DesktopIntegration *Application::desktopIntegration() +{ + return m_desktopIntegration; +} + MainWindow *Application::mainWindow() { return m_window; } + +bool Application::isTorrentAddedNotificationsEnabled() const +{ + return m_storeNotificationTorrentAdded; +} + +void Application::setTorrentAddedNotificationsEnabled(const bool value) +{ + m_storeNotificationTorrentAdded = value; +} #endif const QBtCommandLineParameters &Application::commandLineArgs() const @@ -683,6 +703,40 @@ int Application::exec(const QStringList ¶ms) #endif // DISABLE_WEBUI #else 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); #endif // DISABLE_GUI diff --git a/src/app/application.h b/src/app/application.h index 162cab62d..cc39684fb 100644 --- a/src/app/application.h +++ b/src/app/application.h @@ -67,6 +67,7 @@ namespace RSS } #ifndef DISABLE_GUI +class DesktopIntegration; class MainWindow; using BaseApplication = QApplication; @@ -124,7 +125,11 @@ public: #endif #ifndef DISABLE_GUI + DesktopIntegration *desktopIntegration() override; MainWindow *mainWindow() override; + + bool isTorrentAddedNotificationsEnabled() const override; + void setTorrentAddedNotificationsEnabled(bool value) override; #endif private slots: @@ -194,6 +199,9 @@ private: #endif #ifndef DISABLE_GUI + SettingValue m_storeNotificationTorrentAdded; + + DesktopIntegration *m_desktopIntegration = nullptr; MainWindow *m_window = nullptr; #endif diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index ad1c58122..05546614f 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -45,6 +45,7 @@ add_library(qbt_gui STATIC cookiesdialog.h cookiesmodel.h deletionconfirmationdialog.h + desktopintegration.h downloadfromurldialog.h executionlogwidget.h fspathedit.h @@ -128,6 +129,7 @@ add_library(qbt_gui STATIC cookiesdialog.cpp cookiesmodel.cpp deletionconfirmationdialog.cpp + desktopintegration.cpp downloadfromurldialog.cpp executionlogwidget.cpp fspathedit.cpp diff --git a/src/gui/advancedsettings.cpp b/src/gui/advancedsettings.cpp index 4a1201c6b..7ee5e38d3 100644 --- a/src/gui/advancedsettings.cpp +++ b/src/gui/advancedsettings.cpp @@ -40,6 +40,7 @@ #include "base/preferences.h" #include "base/unicodestrings.h" #include "gui/addnewtorrentdialog.h" +#include "gui/desktopintegration.h" #include "gui/mainwindow.h" #include "interfaces/iguiapplication.h" @@ -271,16 +272,15 @@ void AdvancedSettings::saveAdvancedSettings() const // Stop tracker timeout session->setStopTrackerTimeout(m_spinBoxStopTrackerTimeout.value()); // Program notification - MainWindow *mainWindow = app()->mainWindow(); - mainWindow->setNotificationsEnabled(m_checkBoxProgramNotifications.isChecked()); - mainWindow->setTorrentAddedNotificationsEnabled(m_checkBoxTorrentAddedNotifications.isChecked()); -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB) - mainWindow->setNotificationTimeout(m_spinBoxNotificationTimeout.value()); + app()->desktopIntegration()->setNotificationsEnabled(m_checkBoxProgramNotifications.isChecked()); +#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS + app()->desktopIntegration()->setNotificationTimeout(m_spinBoxNotificationTimeout.value()); #endif + app()->setTorrentAddedNotificationsEnabled(m_checkBoxTorrentAddedNotifications.isChecked()); // Reannounce to all trackers when ip/port changed session->setReannounceWhenAddressChangedEnabled(m_checkBoxReannounceWhenAddressChanged.isChecked()); // Misc GUI properties - mainWindow->setDownloadTrackerFavicon(m_checkBoxTrackerFavicon.isChecked()); + app()->mainWindow()->setDownloadTrackerFavicon(m_checkBoxTrackerFavicon.isChecked()); AddNewTorrentDialog::setSavePathHistoryLength(m_spinBoxSavePathHistoryLength.value()); pref->setSpeedWidgetEnabled(m_checkBoxSpeedWidgetEnabled.isChecked()); #ifndef Q_OS_MACOS @@ -677,17 +677,16 @@ void AdvancedSettings::loadAdvancedSettings() , &m_spinBoxStopTrackerTimeout); // Program notifications - const MainWindow *mainWindow = app()->mainWindow(); - m_checkBoxProgramNotifications.setChecked(mainWindow->isNotificationsEnabled()); + m_checkBoxProgramNotifications.setChecked(app()->desktopIntegration()->isNotificationsEnabled()); addRow(PROGRAM_NOTIFICATIONS, tr("Display notifications"), &m_checkBoxProgramNotifications); // 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); -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB) +#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS // Notification timeout m_spinBoxNotificationTimeout.setMinimum(-1); m_spinBoxNotificationTimeout.setMaximum(std::numeric_limits::max()); - m_spinBoxNotificationTimeout.setValue(mainWindow->getNotificationTimeout()); + m_spinBoxNotificationTimeout.setValue(app()->desktopIntegration()->notificationTimeout()); m_spinBoxNotificationTimeout.setSpecialValueText(tr("System default")); m_spinBoxNotificationTimeout.setSuffix(tr(" ms", " milliseconds")); addRow(NOTIFICATION_TIMEOUT, tr("Notification timeout [0: infinite]"), &m_spinBoxNotificationTimeout); @@ -696,7 +695,7 @@ void AdvancedSettings::loadAdvancedSettings() m_checkBoxReannounceWhenAddressChanged.setChecked(session->isReannounceWhenAddressChangedEnabled()); addRow(REANNOUNCE_WHEN_ADDRESS_CHANGED, tr("Reannounce to all trackers when IP or port changed"), &m_checkBoxReannounceWhenAddressChanged); // 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); // Save path history length m_spinBoxSavePathHistoryLength.setRange(AddNewTorrentDialog::minPathHistoryLength, AddNewTorrentDialog::maxPathHistoryLength); diff --git a/src/gui/desktopintegration.cpp b/src/gui/desktopintegration.cpp new file mode 100644 index 000000000..d5d8caf87 --- /dev/null +++ b/src/gui/desktopintegration.cpp @@ -0,0 +1,273 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2022 Vladimir Golovnev + * Copyright (C) 2006 Christophe Dumez + * + * 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 + +#include +#include + +#ifndef Q_OS_MACOS +#include +#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 diff --git a/src/gui/search/searchwidget.h b/src/gui/desktopintegration.h similarity index 50% copy from src/gui/search/searchwidget.h copy to src/gui/desktopintegration.h index e1e9bca18..acc29ec3b 100644 --- a/src/gui/search/searchwidget.h +++ b/src/gui/desktopintegration.h @@ -1,7 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2020, Will Da Silva - * Copyright (C) 2015, 2018 Vladimir Golovnev + * Copyright (C) 2022 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -30,58 +29,65 @@ #pragma once -#include -#include -#include +#include -class QEvent; -class QObject; -class QTabWidget; +#include "base/settingvalue.h" -class MainWindow; -class SearchJobWidget; +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 -namespace Ui -{ - class SearchWidget; -} - -class SearchWidget : public QWidget +class DesktopIntegration final : public QObject { Q_OBJECT - Q_DISABLE_COPY_MOVE(SearchWidget) + Q_DISABLE_COPY_MOVE(DesktopIntegration) public: - explicit SearchWidget(MainWindow *mainWindow); - ~SearchWidget() override; + explicit DesktopIntegration(QObject *parent = nullptr); + + bool isActive() const; + + QString toolTip() const; + void setToolTip(const QString &toolTip); + + QMenu *menu() const; + void setMenu(QMenu *menu); - void giveFocusToSearchInput(); + bool isNotificationsEnabled() const; + void setNotificationsEnabled(bool value); -private slots: - void on_searchButton_clicked(); - void on_pluginsButton_clicked(); + 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: - bool eventFilter(QObject *object, QEvent *event) override; - void tabChanged(int index); - void closeTab(int index); - void closeAllTabs(); - void tabStatusChanged(QWidget *tab); - void selectMultipleBox(int index); - void toggleFocusBetweenLineEdits(); - - void fillCatCombobox(); - void fillPluginComboBox(); - void selectActivePage(); - void searchTextEdited(const QString &); - - QString selectedCategory() const; - QString selectedPlugin() const; - - Ui::SearchWidget *m_ui = nullptr; - QPointer m_currentSearchTab; // Selected tab - QPointer m_activeSearchTab; // Tab with running search - QList m_allTabs; // To store all tabs - MainWindow *m_mainWindow = nullptr; - bool m_isNewQueryString = false; + void onPreferencesChanged(); +#ifndef Q_OS_MACOS + void createTrayIcon(int retries); +#endif // Q_OS_MACOS + + CachedSettingValue m_storeNotificationEnabled; + + QMenu *m_menu = nullptr; + QString m_toolTip; +#ifndef Q_OS_MACOS + QSystemTrayIcon *m_systrayIcon = nullptr; +#endif +#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS + CachedSettingValue m_storeNotificationTimeOut; + DBusNotifier *m_notifier = nullptr; +#endif }; diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 4459133b8..3a636c878 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -12,6 +12,7 @@ HEADERS += \ $$PWD/cookiesdialog.h \ $$PWD/cookiesmodel.h \ $$PWD/deletionconfirmationdialog.h \ + $$PWD/desktopintegration.h \ $$PWD/downloadfromurldialog.h \ $$PWD/executionlogwidget.h \ $$PWD/fspathedit.h \ @@ -95,6 +96,7 @@ SOURCES += \ $$PWD/cookiesdialog.cpp \ $$PWD/cookiesmodel.cpp \ $$PWD/deletionconfirmationdialog.cpp \ + $$PWD/desktopintegration.cpp \ $$PWD/downloadfromurldialog.cpp \ $$PWD/executionlogwidget.cpp \ $$PWD/fspathedit.cpp \ diff --git a/src/gui/interfaces/iguiapplication.h b/src/gui/interfaces/iguiapplication.h index fdac0e0e4..96daea2df 100644 --- a/src/gui/interfaces/iguiapplication.h +++ b/src/gui/interfaces/iguiapplication.h @@ -32,6 +32,7 @@ #include "base/interfaces/iapplication.h" +class DesktopIntegration; class MainWindow; class IGUIApplication : public IApplication @@ -39,5 +40,9 @@ class IGUIApplication : public IApplication public: virtual ~IGUIApplication() = default; + virtual DesktopIntegration *desktopIntegration() = 0; virtual MainWindow *mainWindow() = 0; + + virtual bool isTorrentAddedNotificationsEnabled() const = 0; + virtual void setTorrentAddedNotificationsEnabled(bool value) = 0; }; diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index b696f9393..c8f2fd505 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -71,6 +71,7 @@ #include "addnewtorrentdialog.h" #include "autoexpandabledialog.h" #include "cookiesdialog.h" +#include "desktopintegration.h" #include "downloadfromurldialog.h" #include "executionlogwidget.h" #include "hidabletabwidget.h" @@ -106,12 +107,8 @@ namespace { #define SETTINGS_KEY(name) u"GUI/" 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}; -#if !defined(Q_OS_MACOS) - const int TIME_TRAY_BALLOON = 5000; -#endif bool isTorrentLink(const QString &str) { @@ -120,21 +117,6 @@ namespace || (!str.startsWith(u"file:", Qt::CaseInsensitive) && 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) @@ -143,12 +125,7 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent) , m_ui(new Ui::MainWindow) , m_storeExecutionLogEnabled(EXECUTIONLOG_SETTINGS_KEY(u"Enabled"_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) -#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS - , m_storeNotificationTimeOut(NOTIFICATIONS_SETTINGS_KEY(u"Timeout"_qs)) -#endif { 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("&Clear Password"), this, &MainWindow::clearUILockPassword); 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 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::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->actionBottomQueuePos, &QAction::triggered, m_transferListWidget, &TransferListWidget::bottomQueuePosSelectedTorrents); #ifndef Q_OS_MACOS - connect(m_ui->actionToggleVisibility, &QAction::triggered, this, [this]() { toggleVisibility(); }); + connect(m_ui->actionToggleVisibility, &QAction::triggered, this, &MainWindow::toggleVisibility); #endif connect(m_ui->actionMinimize, &QAction::triggered, this, &MainWindow::minimizeWindow); connect(m_ui->actionUseAlternativeSpeedLimits, &QAction::triggered, this, &MainWindow::toggleAlternativeSpeeds); @@ -400,6 +360,25 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent) // Load Window state and sizes 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 // Make sure the Window is visible if we don't have a tray icon if (pref->startMinimized()) @@ -413,7 +392,7 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent) raise(); } #else - if (m_systrayIcon) + if (app->desktopIntegration()->isActive()) { if (!(pref->startMinimized() || m_uiLocked)) { @@ -429,7 +408,7 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent) hide(); 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); } } @@ -498,11 +477,6 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent) } } #endif -#ifdef Q_OS_MACOS - setupDockClickHandler(); - createTrayIconMenu(); - m_trayIconMenu->setAsDockMenu(); -#endif } MainWindow::~MainWindow() @@ -531,54 +505,6 @@ void MainWindow::setExecutionLogMsgTypes(const Log::MsgTypes 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 { return m_storeDownloadTrackerFavicon; @@ -721,7 +647,7 @@ void MainWindow::on_actionLock_triggered() // Lock the interface m_uiLocked = true; pref->setUILocked(true); - m_trayIconMenu->setEnabled(false); + app()->desktopIntegration()->menu()->setEnabled(false); hide(); } @@ -778,7 +704,7 @@ void MainWindow::displaySearchTab(bool enable) // RSS tab if (!m_searchWidget) { - m_searchWidget = new SearchWidget(this); + m_searchWidget = new SearchWidget(app(), this); m_tabs->insertTab(1, m_searchWidget, #ifndef Q_OS_MACOS UIThemeManager::instance()->getIcon(u"edit-find"_qs), @@ -873,7 +799,7 @@ void MainWindow::readSettings() m_posInitialized = true; } -void MainWindow::balloonClicked() +void MainWindow::desktopNotificationClicked() { if (isHidden()) { @@ -892,32 +818,6 @@ void MainWindow::balloonClicked() 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() { m_ui->actionCreateTorrent->setShortcut(QKeySequence::New); @@ -1026,13 +926,6 @@ void MainWindow::askRecursiveTorrentDownloadConfirmation(BitTorrent::Torrent *co 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() { auto dialog = new SpeedLimitDialog {this}; @@ -1098,7 +991,7 @@ bool MainWindow::unlockUI() m_uiLocked = false; pref->setUILocked(false); - m_trayIconMenu->setEnabled(true); + app()->desktopIntegration()->menu()->setEnabled(true); return true; } @@ -1115,32 +1008,24 @@ void MainWindow::notifyOfUpdate(const QString &) #ifndef Q_OS_MACOS // Toggle Main window visibility -void MainWindow::toggleVisibility(const QSystemTrayIcon::ActivationReason reason) +void MainWindow::toggleVisibility() { - switch (reason) + if (isHidden()) { - case QSystemTrayIcon::Trigger: - if (isHidden()) - { - if (m_uiLocked && !unlockUI()) // Ask for UI lock password - return; + if (m_uiLocked && !unlockUI()) // Ask for UI lock password + return; - // Make sure the window is not minimized - setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); + // Make sure the window is not minimized + setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); - // Then show it - show(); - raise(); - activateWindow(); - } - else - { - hide(); - } - break; - - default: - break; + // Then show it + show(); + raise(); + activateWindow(); + } + else + { + hide(); } } #endif // Q_OS_MACOS @@ -1245,13 +1130,13 @@ void MainWindow::closeEvent(QCloseEvent *e) } #else const bool goToSystrayOnExit = pref->closeToTray(); - if (!m_forceExit && m_systrayIcon && goToSystrayOnExit && !this->isHidden()) + if (!m_forceExit && app()->desktopIntegration()->isActive() && goToSystrayOnExit && !this->isHidden()) { e->ignore(); QMetaObject::invokeMethod(this, &QWidget::hide, Qt::QueuedConnection); 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); } return; @@ -1287,14 +1172,9 @@ void MainWindow::closeEvent(QCloseEvent *e) } // Disable some UI to prevent user interactions -#ifndef Q_OS_MACOS - if (m_systrayIcon) - { - m_systrayIcon->disconnect(); - m_systrayIcon->setToolTip(tr("qBittorrent is shutting down...")); - m_trayIconMenu->setEnabled(false); - } -#endif + app()->desktopIntegration()->disconnect(); + app()->desktopIntegration()->setToolTip(tr("qBittorrent is shutting down...")); + app()->desktopIntegration()->menu()->setEnabled(false); // Accept exit e->accept(); @@ -1328,14 +1208,13 @@ bool MainWindow::event(QEvent *e) switch (e->type()) { case QEvent::WindowStateChange: - { qDebug("Window change event"); // Now check to see if the window is minimised if (isMinimized()) { qDebug("minimisation"); Preferences *const pref = Preferences::instance(); - if (m_systrayIcon && pref->minimizeToTray()) + if (app()->desktopIntegration()->isActive() && pref->minimizeToTray()) { qDebug() << "Has active window:" << (qApp->activeWindow() != nullptr); // Check if there is a modal window @@ -1350,7 +1229,7 @@ bool MainWindow::event(QEvent *e) QMetaObject::invokeMethod(this, &QWidget::hide, Qt::QueuedConnection); 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); } return true; @@ -1358,17 +1237,16 @@ bool MainWindow::event(QEvent *e) } } break; - } case QEvent::ToolBarChange: - { - qDebug("MAC: Received a toolbar change event!"); - bool ret = QMainWindow::event(e); + { + qDebug("MAC: Received a toolbar change event!"); + const bool ret = QMainWindow::event(e); - qDebug("MAC: new toolbar visibility is %d", !m_ui->actionTopToolBar->isChecked()); - m_ui->actionTopToolBar->toggle(); - Preferences::instance()->setToolbarDisplayed(m_ui->actionTopToolBar->isChecked()); - return ret; - } + qDebug("MAC: new toolbar visibility is %d", !m_ui->actionTopToolBar->isChecked()); + m_ui->actionTopToolBar->toggle(); + Preferences::instance()->setToolbarDisplayed(m_ui->actionTopToolBar->isChecked()); + return ret; + } default: break; } @@ -1443,14 +1321,6 @@ void MainWindow::dragEnterEvent(QDragEnterEvent *event) event->acceptProposedAction(); } -#ifdef Q_OS_MACOS -void MainWindow::setupDockClickHandler() -{ - dockMainWindowHandle = this; - MacUtils::overrideDockClickHandler(dockClickHandler); -} -#endif // Q_OS_MACOS - /***************************************************** * * * Torrent * @@ -1552,28 +1422,6 @@ void MainWindow::loadPreferences() { 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 if (pref->isToolbarDisplayed()) { @@ -1680,12 +1528,12 @@ void MainWindow::reloadSessionStats() MacUtils::setBadgeLabelText({}); } #else - if (m_systrayIcon) + if (app()->desktopIntegration()->isActive()) { 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("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 @@ -1707,21 +1555,6 @@ void MainWindow::reloadTorrentStats(const QVector &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 * @@ -1746,82 +1579,40 @@ void MainWindow::downloadFromURLList(const QStringList &urlList) * * *****************************************************/ -#ifndef Q_OS_MACOS -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() +QMenu *MainWindow::createDesktopIntegrationMenu() { - if (m_trayIconMenu) - return; - - m_trayIconMenu = new QMenu(this); + auto *menu = new QMenu(this); #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_trayIconMenu->addAction(m_ui->actionToggleVisibility); - m_trayIconMenu->addSeparator(); + menu->addAction(m_ui->actionToggleVisibility); + menu->addSeparator(); #endif - m_trayIconMenu->addAction(m_ui->actionOpen); - m_trayIconMenu->addAction(m_ui->actionDownloadFromURL); - m_trayIconMenu->addSeparator(); + menu->addAction(m_ui->actionOpen); + menu->addAction(m_ui->actionDownloadFromURL); + menu->addSeparator(); - m_trayIconMenu->addAction(m_ui->actionUseAlternativeSpeedLimits); - m_trayIconMenu->addAction(m_ui->actionSetGlobalSpeedLimits); - m_trayIconMenu->addSeparator(); + menu->addAction(m_ui->actionUseAlternativeSpeedLimits); + menu->addAction(m_ui->actionSetGlobalSpeedLimits); + menu->addSeparator(); - m_trayIconMenu->addAction(m_ui->actionStartAll); - m_trayIconMenu->addAction(m_ui->actionPauseAll); + menu->addAction(m_ui->actionStartAll); + menu->addAction(m_ui->actionPauseAll); #ifndef Q_OS_MACOS - m_trayIconMenu->addSeparator(); - m_trayIconMenu->addAction(m_ui->actionExit); + menu->addSeparator(); + menu->addAction(m_ui->actionExit); #endif if (m_uiLocked) - m_trayIconMenu->setEnabled(false); + menu->setEnabled(false); + + return menu; } void MainWindow::updateAltSpeedsBtn(const bool alternative) diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h index 810d68b59..b7a527c09 100644 --- a/src/gui/mainwindow.h +++ b/src/gui/mainwindow.h @@ -31,10 +31,6 @@ #include #include -#ifndef Q_OS_MACOS -#include -#endif - #include "base/bittorrent/torrent.h" #include "base/logger.h" #include "base/settingvalue.h" @@ -97,14 +93,6 @@ public: void setExecutionLogMsgTypes(Log::MsgTypes value); // 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 bool isDownloadTrackerFavicon() const; @@ -113,19 +101,12 @@ public: void activate(); void cleanup(); - void showNotificationBalloon(const QString &title, const QString &msg) const; - -signals: - void systemTrayIconCreated(); - private slots: void showFilterContextMenu(); - void balloonClicked(); + void desktopNotificationClicked(); void writeSettings(); void writeSplitterSettings(); void readSettings(); - void fullDiskError(BitTorrent::Torrent *const torrent, const QString &msg) const; - void handleDownloadFromUrlFailure(const QString &, const QString &) const; void tabChanged(int newTab); bool defineUILockPassword(); void clearUILockPassword(); @@ -143,9 +124,6 @@ private slots: void reloadSessionStats(); void reloadTorrentStats(const QVector &torrents); 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 optionsSaved(); void toggleAlternativeSpeeds(); @@ -199,16 +177,11 @@ private slots: #ifdef Q_OS_MACOS void on_actionCloseWindow_triggered(); #else - void toggleVisibility(const QSystemTrayIcon::ActivationReason reason = QSystemTrayIcon::Trigger); + void toggleVisibility(); #endif private: - void createTrayIconMenu(); -#ifdef Q_OS_MACOS - void setupDockClickHandler(); -#else - void createTrayIcon(int retries); -#endif + QMenu *createDesktopIntegrationMenu(); #ifdef Q_OS_WIN void installPython(); #endif @@ -238,9 +211,6 @@ private: QPointer m_createTorrentDlg; QPointer m_downloadFromURLDialog; -#ifndef Q_OS_MACOS - QPointer m_systrayIcon; -#endif QPointer m_trayIconMenu; TransferListWidget *m_transferListWidget = nullptr; @@ -267,15 +237,8 @@ private: SettingValue m_storeExecutionLogEnabled; SettingValue m_storeDownloadTrackerFavicon; - SettingValue m_storeNotificationEnabled; - SettingValue m_storeNotificationTorrentAdded; CachedSettingValue m_storeExecutionLogTypes; -#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS - SettingValue m_storeNotificationTimeOut; - DBusNotifier *m_notifier = nullptr; -#endif - #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) void checkProgramUpdate(bool invokedByUser); void handleUpdateCheckFinished(ProgramUpdater *updater, bool invokedByUser); diff --git a/src/gui/search/searchwidget.cpp b/src/gui/search/searchwidget.cpp index efc4d2be2..03d4a39ff 100644 --- a/src/gui/search/searchwidget.cpp +++ b/src/gui/search/searchwidget.cpp @@ -52,6 +52,7 @@ #include "base/search/searchhandler.h" #include "base/search/searchpluginmanager.h" #include "base/utils/foreignapps.h" +#include "gui/desktopintegration.h" #include "gui/mainwindow.h" #include "gui/uithememanager.h" #include "pluginselectdialog.h" @@ -83,10 +84,11 @@ namespace } } -SearchWidget::SearchWidget(MainWindow *mainWindow) +SearchWidget::SearchWidget(IGUIApplication *app, MainWindow *mainWindow) : QWidget(mainWindow) - , m_ui(new Ui::SearchWidget()) - , m_mainWindow(mainWindow) + , GUIApplicationComponent(app) + , m_ui {new Ui::SearchWidget()} + , m_mainWindow {mainWindow} { m_ui->setupUi(this); m_ui->tabWidget->tabBar()->installEventFilter(this); @@ -304,7 +306,7 @@ void SearchWidget::on_searchButton_clicked() { 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; } @@ -370,12 +372,12 @@ void SearchWidget::tabStatusChanged(QWidget *tab) { 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) - m_mainWindow->showNotificationBalloon(tr("Search Engine"), tr("Search has failed")); + app()->desktopIntegration()->showNotification(tr("Search Engine"), tr("Search has failed")); else - m_mainWindow->showNotificationBalloon(tr("Search Engine"), tr("Search has finished")); + app()->desktopIntegration()->showNotification(tr("Search Engine"), tr("Search has finished")); } m_activeSearchTab = nullptr; diff --git a/src/gui/search/searchwidget.h b/src/gui/search/searchwidget.h index e1e9bca18..b3458a855 100644 --- a/src/gui/search/searchwidget.h +++ b/src/gui/search/searchwidget.h @@ -34,6 +34,8 @@ #include #include +#include "gui/guiapplicationcomponent.h" + class QEvent; class QObject; class QTabWidget; @@ -46,13 +48,13 @@ namespace Ui class SearchWidget; } -class SearchWidget : public QWidget +class SearchWidget : public QWidget, public GUIApplicationComponent { Q_OBJECT Q_DISABLE_COPY_MOVE(SearchWidget) public: - explicit SearchWidget(MainWindow *mainWindow); + explicit SearchWidget(IGUIApplication *app, MainWindow *mainWindow); ~SearchWidget() override; void giveFocusToSearchInput(); -- 2.11.4.GIT