/*
* Bittorrent Client using Qt and libtorrent .
* 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 "mainwindow.h"
# include <chrono>
# include <QClipboard>
# include <QCloseEvent>
# include <QDebug>
# include <QDesktopServices>
# include <QFileDialog>
# include <QFileSystemWatcher>
# include <QKeyEvent>
# include <QMessageBox>
# include <QMimeData>
# include <QProcess>
# include <QPushButton>
# include <QShortcut>
# include <QSplitter>
# include <QStatusBar>
# include <QtGlobal>
# include <QTimer>
# ifdef Q_OS_MACOS
# include <QtMac>
# include <QtMacExtras>
# endif
# if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
# include <QDBusConnection>
# include "qtnotify/notifications.h"
# endif
# include "base/bittorrent/session.h"
# include "base/bittorrent/sessionstatus.h"
# include "base/bittorrent/torrent.h"
# include "base/global.h"
# include "base/logger.h"
# include "base/net/downloadmanager.h"
# include "base/preferences.h"
# include "base/rss/rss_folder.h"
# include "base/rss/rss_session.h"
# include "base/settingsstorage.h"
# include "base/utils/foreignapps.h"
# include "base/utils/fs.h"
# include "base/utils/misc.h"
# include "base/utils/password.h"
# include "base/version.h"
# include "aboutdialog.h"
# include "addnewtorrentdialog.h"
# include "autoexpandabledialog.h"
# include "cookiesdialog.h"
# include "downloadfromurldialog.h"
# include "executionlogwidget.h"
# include "hidabletabwidget.h"
# include "lineedit.h"
# include "optionsdialog.h"
# include "powermanagement/powermanagement.h"
# include "properties/peerlistwidget.h"
# include "properties/propertieswidget.h"
# include "properties/trackerlistwidget.h"
# include "rss/rsswidget.h"
# include "search/searchwidget.h"
# include "speedlimitdialog.h"
# include "statsdialog.h"
# include "statusbar.h"
# include "torrentcreatordialog.h"
# include "transferlistfilterswidget.h"
# include "transferlistmodel.h"
# include "transferlistwidget.h"
# include "ui_mainwindow.h"
# include "uithememanager.h"
# include "utils.h"
# ifdef Q_OS_MACOS
# include "macutilities.h"
# endif
# if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
# include "programupdater.h"
# endif
namespace
{
# define SETTINGS_KEY(name) "GUI / " name
// ExecutionLog properties keys
# define EXECUTIONLOG_SETTINGS_KEY(name) QStringLiteral(SETTINGS_KEY("Log / ") name)
const QString KEY_EXECUTIONLOG_ENABLED = EXECUTIONLOG_SETTINGS_KEY ( " Enabled " ) ;
const QString KEY_EXECUTIONLOG_TYPES = EXECUTIONLOG_SETTINGS_KEY ( " Types " ) ;
// Notifications properties keys
# define NOTIFICATIONS_SETTINGS_KEY(name) QStringLiteral(SETTINGS_KEY("Notifications / ") name)
const QString KEY_NOTIFICATIONS_ENABLED = NOTIFICATIONS_SETTINGS_KEY ( " Enabled " ) ;
const QString KEY_NOTIFICATIONS_TORRENTADDED = NOTIFICATIONS_SETTINGS_KEY ( " TorrentAdded " ) ;
// Misc
const QString KEY_DOWNLOAD_TRACKER_FAVICON = QStringLiteral ( SETTINGS_KEY ( " DownloadTrackerFavicon " ) ) ;
const std : : chrono : : seconds PREVENT_SUSPEND_INTERVAL { 60 } ;
# if !defined(Q_OS_MACOS)
const int TIME_TRAY_BALLOON = 5000 ;
# endif
// just a shortcut
inline SettingsStorage * settings ( )
{
return SettingsStorage : : instance ( ) ;
}
bool isTorrentLink ( const QString & str )
{
return str . startsWith ( QLatin1String ( " magnet: " ) , Qt : : CaseInsensitive )
| | str . endsWith ( QLatin1String ( C_TORRENT_FILE_EXTENSION ) , Qt : : CaseInsensitive )
| | ( ! str . startsWith ( QLatin1String ( " file: " ) , Qt : : CaseInsensitive )
& & Net : : DownloadManager : : hasSupportedScheme ( str ) ) ;
}
}
MainWindow : : MainWindow ( QWidget * parent )
: QMainWindow ( parent )
, m_ui ( new Ui : : MainWindow )
, m_posInitialized ( false )
, m_forceExit ( false )
, m_unlockDlgShowing ( false )
# if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
, m_wasUpdateCheckEnabled ( false )
# endif
, m_hasPython ( false )
{
m_ui - > setupUi ( this ) ;
Preferences * const pref = Preferences : : instance ( ) ;
m_uiLocked = pref - > isUILocked ( ) ;
setWindowTitle ( " qBittorrent " QBT_VERSION ) ;
m_displaySpeedInTitle = pref - > speedInTitleBar ( ) ;
// Setting icons
# ifndef Q_OS_MACOS
const QIcon appLogo ( UIThemeManager : : instance ( ) - > getIcon ( QLatin1String ( " qbittorrent " ) , QLatin1String ( " qbittorrent-tray " ) ) ) ;
setWindowIcon ( appLogo ) ;
# endif // Q_OS_MACOS
# if (defined(Q_OS_UNIX))
m_ui - > actionOptions - > setText ( tr ( " Preferences " ) ) ;
# endif
addToolbarContextMenu ( ) ;
m_ui - > actionOpen - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " list-add " ) ) ;
m_ui - > actionDownloadFromURL - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " insert-link " ) ) ;
m_ui - > actionSetGlobalSpeedLimits - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " speedometer " ) ) ;
m_ui - > actionCreateTorrent - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " document-edit " ) ) ;
m_ui - > actionAbout - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " help-about " ) ) ;
m_ui - > actionStatistics - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " view-statistics " ) ) ;
m_ui - > actionTopQueuePos - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " go-top " ) ) ;
m_ui - > actionIncreaseQueuePos - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " go-up " ) ) ;
m_ui - > actionDecreaseQueuePos - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " go-down " ) ) ;
m_ui - > actionBottomQueuePos - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " go-bottom " ) ) ;
m_ui - > actionDelete - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " list-remove " ) ) ;
m_ui - > actionDocumentation - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " help-contents " ) ) ;
m_ui - > actionDonateMoney - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " wallet-open " ) ) ;
m_ui - > actionExit - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " application-exit " ) ) ;
m_ui - > actionLock - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " object-locked " ) ) ;
m_ui - > actionOptions - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " configure " , " preferences-system " ) ) ;
m_ui - > actionPause - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " media-playback-pause " ) ) ;
m_ui - > actionPauseAll - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " media-playback-pause " ) ) ;
m_ui - > actionStart - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " media-playback-start " ) ) ;
m_ui - > actionStartAll - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " media-playback-start " ) ) ;
m_ui - > menuAutoShutdownOnDownloadsCompletion - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " application-exit " ) ) ;
m_ui - > actionManageCookies - > setIcon ( UIThemeManager : : instance ( ) - > getIcon ( " preferences-web-browser-cookies " ) ) ;
auto * lockMenu = new QMenu ( this ) ;
QAction * defineUiLockPasswdAct = lockMenu - > addAction ( tr ( " &Set Password " ) ) ;
connect ( defineUiLockPasswdAct , & QAction : : triggered , this , & MainWindow : : defineUILockPassword ) ;
QAction * clearUiLockPasswdAct = lockMenu - > addAction ( tr ( " &Clear Password " ) ) ;
connect ( clearUiLockPasswdAct , & QAction : : triggered , this , & MainWindow : : clearUILockPassword ) ;
m_ui - > actionLock - > setMenu ( lockMenu ) ;
// Creating Bittorrent session
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 ) ;
qDebug ( " create tabWidget " ) ;
m_tabs = new HidableTabWidget ( this ) ;
connect ( m_tabs . data ( ) , & QTabWidget : : currentChanged , this , & MainWindow : : tabChanged ) ;
m_splitter = new QSplitter ( Qt : : Horizontal , this ) ;
// vSplitter->setChildrenCollapsible(false);
auto * hSplitter = new QSplitter ( Qt : : Vertical , this ) ;
hSplitter - > setChildrenCollapsible ( false ) ;
hSplitter - > setFrameShape ( QFrame : : NoFrame ) ;
// Name filter
m_searchFilter = new LineEdit ( this ) ;
m_searchFilter - > setPlaceholderText ( tr ( " Filter torrent names... " ) ) ;
m_searchFilter - > setFixedWidth ( Utils : : Gui : : scaledSize ( this , 200 ) ) ;
m_searchFilter - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
connect ( m_searchFilter , & QWidget : : customContextMenuRequested , this , & MainWindow : : showFilterContextMenu ) ;
m_searchFilterAction = m_ui - > toolBar - > insertWidget ( m_ui - > actionLock , m_searchFilter ) ;
QWidget * spacer = new QWidget ( this ) ;
spacer - > setSizePolicy ( QSizePolicy : : Expanding , QSizePolicy : : Fixed ) ;
m_ui - > toolBar - > insertWidget ( m_searchFilterAction , spacer ) ;
// Transfer List tab
m_transferListWidget = new TransferListWidget ( hSplitter , this ) ;
// m_transferListWidget->setStyleSheet("QTreeView {border: none;}"); // borderless
m_propertiesWidget = new PropertiesWidget ( hSplitter ) ;
connect ( m_transferListWidget , & TransferListWidget : : currentTorrentChanged , m_propertiesWidget , & PropertiesWidget : : loadTorrentInfos ) ;
m_transferListFiltersWidget = new TransferListFiltersWidget ( m_splitter , m_transferListWidget , isDownloadTrackerFavicon ( ) ) ;
hSplitter - > addWidget ( m_transferListWidget ) ;
hSplitter - > addWidget ( m_propertiesWidget ) ;
m_splitter - > addWidget ( m_transferListFiltersWidget ) ;
m_splitter - > addWidget ( hSplitter ) ;
m_splitter - > setCollapsible ( 0 , true ) ;
m_splitter - > setCollapsible ( 1 , false ) ;
m_tabs - > addTab ( m_splitter ,
# ifndef Q_OS_MACOS
UIThemeManager : : instance ( ) - > getIcon ( " folder-remote " ) ,
# endif
tr ( " Transfers " ) ) ;
connect ( m_searchFilter , & LineEdit : : textChanged , m_transferListWidget , & TransferListWidget : : applyNameFilter ) ;
connect ( hSplitter , & QSplitter : : splitterMoved , this , & MainWindow : : writeSettings ) ;
connect ( m_splitter , & QSplitter : : splitterMoved , this , & MainWindow : : writeSettings ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : trackersChanged , m_propertiesWidget , & PropertiesWidget : : loadTrackers ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : trackersAdded , m_transferListFiltersWidget , & TransferListFiltersWidget : : addTrackers ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : trackersRemoved , m_transferListFiltersWidget , & TransferListFiltersWidget : : removeTrackers ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : trackerlessStateChanged , m_transferListFiltersWidget , & TransferListFiltersWidget : : changeTrackerless ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : trackerSuccess
, m_transferListFiltersWidget , qOverload < const BitTorrent : : Torrent * , const QString & > ( & TransferListFiltersWidget : : trackerSuccess ) ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : trackerError
, m_transferListFiltersWidget , qOverload < const BitTorrent : : Torrent * , const QString & > ( & TransferListFiltersWidget : : trackerError ) ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : trackerWarning
, m_transferListFiltersWidget , qOverload < const BitTorrent : : Torrent * , const QString & > ( & TransferListFiltersWidget : : trackerWarning ) ) ;
# ifdef Q_OS_MACOS
// Increase top spacing to avoid tab overlapping
m_ui - > centralWidgetLayout - > addSpacing ( 8 ) ;
# endif
m_ui - > centralWidgetLayout - > addWidget ( m_tabs ) ;
m_queueSeparator = m_ui - > toolBar - > insertSeparator ( m_ui - > actionTopQueuePos ) ;
m_queueSeparatorMenu = m_ui - > menuEdit - > insertSeparator ( m_ui - > actionTopQueuePos ) ;
# ifdef Q_OS_MACOS
for ( QAction * action : asConst ( m_ui - > toolBar - > actions ( ) ) )
{
if ( action - > isSeparator ( ) )
{
QWidget * spacer = new QWidget ( this ) ;
spacer - > setSizePolicy ( QSizePolicy : : Fixed , QSizePolicy : : Fixed ) ;
spacer - > setMinimumWidth ( 16 ) ;
m_ui - > toolBar - > insertWidget ( action , spacer ) ;
m_ui - > toolBar - > removeAction ( action ) ;
}
}
{
QWidget * spacer = new QWidget ( this ) ;
spacer - > setSizePolicy ( QSizePolicy : : Fixed , QSizePolicy : : Fixed ) ;
spacer - > setMinimumWidth ( 8 ) ;
m_ui - > toolBar - > insertWidget ( m_ui - > actionDownloadFromURL , spacer ) ;
}
{
QWidget * spacer = new QWidget ( this ) ;
spacer - > setSizePolicy ( QSizePolicy : : Fixed , QSizePolicy : : Fixed ) ;
spacer - > setMinimumWidth ( 8 ) ;
m_ui - > toolBar - > addWidget ( spacer ) ;
}
# endif // Q_OS_MACOS
// Transfer list slots
connect ( m_ui - > actionStart , & QAction : : triggered , m_transferListWidget , & TransferListWidget : : startSelectedTorrents ) ;
connect ( m_ui - > actionStartAll , & QAction : : triggered , m_transferListWidget , & TransferListWidget : : resumeAllTorrents ) ;
connect ( m_ui - > actionPause , & QAction : : triggered , m_transferListWidget , & TransferListWidget : : pauseSelectedTorrents ) ;
connect ( m_ui - > actionPauseAll , & QAction : : triggered , m_transferListWidget , & TransferListWidget : : pauseAllTorrents ) ;
connect ( m_ui - > actionDelete , & QAction : : triggered , m_transferListWidget , & TransferListWidget : : softDeleteSelectedTorrents ) ;
connect ( m_ui - > actionTopQueuePos , & QAction : : triggered , m_transferListWidget , & TransferListWidget : : topQueuePosSelectedTorrents ) ;
connect ( m_ui - > actionIncreaseQueuePos , & QAction : : triggered , m_transferListWidget , & TransferListWidget : : increaseQueuePosSelectedTorrents ) ;
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 ( ) ; } ) ;
# endif
connect ( m_ui - > actionMinimize , & QAction : : triggered , this , & MainWindow : : minimizeWindow ) ;
connect ( m_ui - > actionUseAlternativeSpeedLimits , & QAction : : triggered , this , & MainWindow : : toggleAlternativeSpeeds ) ;
# if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
m_programUpdateTimer = new QTimer ( this ) ;
m_programUpdateTimer - > setInterval ( 60 * 60 * 1000 ) ;
m_programUpdateTimer - > setSingleShot ( true ) ;
connect ( m_programUpdateTimer , & QTimer : : timeout , this , & MainWindow : : checkProgramUpdate ) ;
connect ( m_ui - > actionCheckForUpdates , & QAction : : triggered , this , & MainWindow : : checkProgramUpdate ) ;
# else
m_ui - > actionCheckForUpdates - > setVisible ( false ) ;
# endif
// Certain menu items should reside at specific places on macOS.
// Qt partially does it on its own, but updates and different languages require tuning.
m_ui - > actionExit - > setMenuRole ( QAction : : QuitRole ) ;
m_ui - > actionAbout - > setMenuRole ( QAction : : AboutRole ) ;
m_ui - > actionCheckForUpdates - > setMenuRole ( QAction : : ApplicationSpecificRole ) ;
m_ui - > actionOptions - > setMenuRole ( QAction : : PreferencesRole ) ;
connect ( m_ui - > actionManageCookies , & QAction : : triggered , this , & MainWindow : : manageCookies ) ;
m_pwr = new PowerManagement ( this ) ;
m_preventTimer = new QTimer ( this ) ;
connect ( m_preventTimer , & QTimer : : timeout , this , & MainWindow : : updatePowerManagementState ) ;
// Configure BT session according to options
loadPreferences ( false ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : statsUpdated , this , & MainWindow : : reloadSessionStats ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : torrentsUpdated , this , & MainWindow : : reloadTorrentStats ) ;
// Accept drag 'n drops
setAcceptDrops ( true ) ;
createKeyboardShortcuts ( ) ;
# ifdef Q_OS_MACOS
setUnifiedTitleAndToolBarOnMac ( true ) ;
# endif
// View settings
m_ui - > actionTopToolBar - > setChecked ( pref - > isToolbarDisplayed ( ) ) ;
m_ui - > actionShowStatusbar - > setChecked ( pref - > isStatusbarDisplayed ( ) ) ;
m_ui - > actionSpeedInTitleBar - > setChecked ( pref - > speedInTitleBar ( ) ) ;
m_ui - > actionRSSReader - > setChecked ( pref - > isRSSWidgetEnabled ( ) ) ;
m_ui - > actionSearchWidget - > setChecked ( pref - > isSearchEnabled ( ) ) ;
m_ui - > actionExecutionLogs - > setChecked ( isExecutionLogEnabled ( ) ) ;
Log : : MsgTypes flags ( executionLogMsgTypes ( ) ) ;
m_ui - > actionNormalMessages - > setChecked ( flags & Log : : NORMAL ) ;
m_ui - > actionInformationMessages - > setChecked ( flags & Log : : INFO ) ;
m_ui - > actionWarningMessages - > setChecked ( flags & Log : : WARNING ) ;
m_ui - > actionCriticalMessages - > setChecked ( flags & Log : : CRITICAL ) ;
displayRSSTab ( m_ui - > actionRSSReader - > isChecked ( ) ) ;
on_actionExecutionLogs_triggered ( m_ui - > actionExecutionLogs - > isChecked ( ) ) ;
on_actionNormalMessages_triggered ( m_ui - > actionNormalMessages - > isChecked ( ) ) ;
on_actionInformationMessages_triggered ( m_ui - > actionInformationMessages - > isChecked ( ) ) ;
on_actionWarningMessages_triggered ( m_ui - > actionWarningMessages - > isChecked ( ) ) ;
on_actionCriticalMessages_triggered ( m_ui - > actionCriticalMessages - > isChecked ( ) ) ;
if ( m_ui - > actionSearchWidget - > isChecked ( ) )
QTimer : : singleShot ( 0 , this , & MainWindow : : on_actionSearchWidget_triggered ) ;
// Auto shutdown actions
auto * autoShutdownGroup = new QActionGroup ( this ) ;
autoShutdownGroup - > setExclusive ( true ) ;
autoShutdownGroup - > addAction ( m_ui - > actionAutoShutdownDisabled ) ;
autoShutdownGroup - > addAction ( m_ui - > actionAutoExit ) ;
autoShutdownGroup - > addAction ( m_ui - > actionAutoShutdown ) ;
autoShutdownGroup - > addAction ( m_ui - > actionAutoSuspend ) ;
autoShutdownGroup - > addAction ( m_ui - > actionAutoHibernate ) ;
# if (!defined(Q_OS_UNIX) || defined(Q_OS_MACOS)) || defined(QT_DBUS_LIB)
m_ui - > actionAutoShutdown - > setChecked ( pref - > shutdownWhenDownloadsComplete ( ) ) ;
m_ui - > actionAutoSuspend - > setChecked ( pref - > suspendWhenDownloadsComplete ( ) ) ;
m_ui - > actionAutoHibernate - > setChecked ( pref - > hibernateWhenDownloadsComplete ( ) ) ;
# else
m_ui - > actionAutoShutdown - > setDisabled ( true ) ;
m_ui - > actionAutoSuspend - > setDisabled ( true ) ;
m_ui - > actionAutoHibernate - > setDisabled ( true ) ;
# endif
m_ui - > actionAutoExit - > setChecked ( pref - > shutdownqBTWhenDownloadsComplete ( ) ) ;
if ( ! autoShutdownGroup - > checkedAction ( ) )
m_ui - > actionAutoShutdownDisabled - > setChecked ( true ) ;
// Load Window state and sizes
readSettings ( ) ;
# ifndef Q_OS_MACOS
if ( m_systrayIcon )
{
if ( ! ( pref - > startMinimized ( ) | | m_uiLocked ) )
{
show ( ) ;
activateWindow ( ) ;
raise ( ) ;
}
else if ( pref - > startMinimized ( ) )
{
showMinimized ( ) ;
if ( pref - > minimizeToTray ( ) )
{
hide ( ) ;
if ( ! pref - > minimizeToTrayNotified ( ) )
{
showNotificationBaloon ( tr ( " qBittorrent is minimized to tray " ) , tr ( " This behavior can be changed in the settings. You won't be reminded again. " ) ) ;
pref - > setMinimizeToTrayNotified ( true ) ;
}
}
}
}
else
{
# endif
// Make sure the Window is visible if we don't have a tray icon
if ( pref - > startMinimized ( ) )
{
showMinimized ( ) ;
}
else
{
show ( ) ;
activateWindow ( ) ;
raise ( ) ;
}
# ifndef Q_OS_MACOS
}
# endif
m_propertiesWidget - > readSettings ( ) ;
// Start watching the executable for updates
m_executableWatcher = new QFileSystemWatcher ( this ) ;
connect ( m_executableWatcher , & QFileSystemWatcher : : fileChanged , this , & MainWindow : : notifyOfUpdate ) ;
m_executableWatcher - > addPath ( qApp - > applicationFilePath ( ) ) ;
m_transferListWidget - > setFocus ( ) ;
// Update the number of torrents (tab)
updateNbTorrents ( ) ;
connect ( m_transferListWidget - > getSourceModel ( ) , & QAbstractItemModel : : rowsInserted , this , & MainWindow : : updateNbTorrents ) ;
connect ( m_transferListWidget - > getSourceModel ( ) , & QAbstractItemModel : : rowsRemoved , this , & MainWindow : : updateNbTorrents ) ;
connect ( pref , & Preferences : : changed , this , & MainWindow : : optionsSaved ) ;
qDebug ( " GUI Built " ) ;
# ifdef Q_OS_WIN
if ( ! pref - > neverCheckFileAssoc ( ) & & ( ! Preferences : : isTorrentFileAssocSet ( ) | | ! Preferences : : isMagnetLinkAssocSet ( ) ) )
{
if ( QMessageBox : : question ( this , tr ( " Torrent file association " ) ,
tr ( " qBittorrent is not the default application to open torrent files or Magnet links. \n Do you want to associate qBittorrent to torrent files and Magnet links? " ) ,
QMessageBox : : Yes | QMessageBox : : No , QMessageBox : : Yes ) = = QMessageBox : : Yes )
{
Preferences : : setTorrentFileAssoc ( true ) ;
Preferences : : setMagnetLinkAssoc ( true ) ;
}
else
{
pref - > setNeverCheckFileAssoc ( ) ;
}
}
# endif
# ifdef Q_OS_MACOS
setupDockClickHandler ( ) ;
trayIconMenu ( ) - > setAsDockMenu ( ) ;
# endif
}
MainWindow : : ~ MainWindow ( )
{
delete m_ui ;
}
bool MainWindow : : isExecutionLogEnabled ( ) const
{
return settings ( ) - > loadValue ( KEY_EXECUTIONLOG_ENABLED , false ) ;
}
void MainWindow : : setExecutionLogEnabled ( bool value )
{
settings ( ) - > storeValue ( KEY_EXECUTIONLOG_ENABLED , value ) ;
}
int MainWindow : : executionLogMsgTypes ( ) const
{
// as default value we need all the bits set
// -1 is considered the portable way to achieve that
return settings ( ) - > loadValue ( KEY_EXECUTIONLOG_TYPES , - 1 ) ;
}
void MainWindow : : setExecutionLogMsgTypes ( const int value )
{
m_executionLog - > setMessageTypes ( static_cast < Log : : MsgTypes > ( value ) ) ;
settings ( ) - > storeValue ( KEY_EXECUTIONLOG_TYPES , value ) ;
}
bool MainWindow : : isNotificationsEnabled ( ) const
{
return settings ( ) - > loadValue ( KEY_NOTIFICATIONS_ENABLED , true ) ;
}
void MainWindow : : setNotificationsEnabled ( bool value )
{
settings ( ) - > storeValue ( KEY_NOTIFICATIONS_ENABLED , value ) ;
}
bool MainWindow : : isTorrentAddedNotificationsEnabled ( ) const
{
return settings ( ) - > loadValue ( KEY_NOTIFICATIONS_TORRENTADDED , false ) ;
}
void MainWindow : : setTorrentAddedNotificationsEnabled ( bool value )
{
settings ( ) - > storeValue ( KEY_NOTIFICATIONS_TORRENTADDED , value ) ;
}
bool MainWindow : : isDownloadTrackerFavicon ( ) const
{
return settings ( ) - > loadValue ( KEY_DOWNLOAD_TRACKER_FAVICON , false ) ;
}
void MainWindow : : setDownloadTrackerFavicon ( bool value )
{
m_transferListFiltersWidget - > setDownloadTrackerFavicon ( value ) ;
settings ( ) - > storeValue ( KEY_DOWNLOAD_TRACKER_FAVICON , value ) ;
}
void MainWindow : : addToolbarContextMenu ( )
{
const Preferences * const pref = Preferences : : instance ( ) ;
m_toolbarMenu = new QMenu ( this ) ;
m_ui - > toolBar - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
connect ( m_ui - > toolBar , & QWidget : : customContextMenuRequested , this , & MainWindow : : toolbarMenuRequested ) ;
QAction * iconsOnly = m_toolbarMenu - > addAction ( tr ( " Icons Only " ) ) ;
connect ( iconsOnly , & QAction : : triggered , this , & MainWindow : : toolbarIconsOnly ) ;
QAction * textOnly = m_toolbarMenu - > addAction ( tr ( " Text Only " ) ) ;
connect ( textOnly , & QAction : : triggered , this , & MainWindow : : toolbarTextOnly ) ;
QAction * textBesideIcons = m_toolbarMenu - > addAction ( tr ( " Text Alongside Icons " ) ) ;
connect ( textBesideIcons , & QAction : : triggered , this , & MainWindow : : toolbarTextBeside ) ;
QAction * textUnderIcons = m_toolbarMenu - > addAction ( tr ( " Text Under Icons " ) ) ;
connect ( textUnderIcons , & QAction : : triggered , this , & MainWindow : : toolbarTextUnder ) ;
QAction * followSystemStyle = m_toolbarMenu - > addAction ( tr ( " Follow System Style " ) ) ;
connect ( followSystemStyle , & QAction : : triggered , this , & MainWindow : : toolbarFollowSystem ) ;
auto * textPositionGroup = new QActionGroup ( m_toolbarMenu ) ;
textPositionGroup - > addAction ( iconsOnly ) ;
iconsOnly - > setCheckable ( true ) ;
textPositionGroup - > addAction ( textOnly ) ;
textOnly - > setCheckable ( true ) ;
textPositionGroup - > addAction ( textBesideIcons ) ;
textBesideIcons - > setCheckable ( true ) ;
textPositionGroup - > addAction ( textUnderIcons ) ;
textUnderIcons - > setCheckable ( true ) ;
textPositionGroup - > addAction ( followSystemStyle ) ;
followSystemStyle - > setCheckable ( true ) ;
const auto buttonStyle = static_cast < Qt : : ToolButtonStyle > ( pref - > getToolbarTextPosition ( ) ) ;
if ( ( buttonStyle > = Qt : : ToolButtonIconOnly ) & & ( buttonStyle < = Qt : : ToolButtonFollowStyle ) )
m_ui - > toolBar - > setToolButtonStyle ( buttonStyle ) ;
switch ( buttonStyle )
{
case Qt : : ToolButtonIconOnly :
iconsOnly - > setChecked ( true ) ;
break ;
case Qt : : ToolButtonTextOnly :
textOnly - > setChecked ( true ) ;
break ;
case Qt : : ToolButtonTextBesideIcon :
textBesideIcons - > setChecked ( true ) ;
break ;
case Qt : : ToolButtonTextUnderIcon :
textUnderIcons - > setChecked ( true ) ;
break ;
default :
followSystemStyle - > setChecked ( true ) ;
}
}
void MainWindow : : manageCookies ( )
{
auto * cookieDialog = new CookiesDialog ( this ) ;
cookieDialog - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
cookieDialog - > open ( ) ;
}
void MainWindow : : toolbarMenuRequested ( const QPoint & point )
{
m_toolbarMenu - > popup ( m_ui - > toolBar - > mapToGlobal ( point ) ) ;
}
void MainWindow : : toolbarIconsOnly ( )
{
m_ui - > toolBar - > setToolButtonStyle ( Qt : : ToolButtonIconOnly ) ;
Preferences : : instance ( ) - > setToolbarTextPosition ( Qt : : ToolButtonIconOnly ) ;
}
void MainWindow : : toolbarTextOnly ( )
{
m_ui - > toolBar - > setToolButtonStyle ( Qt : : ToolButtonTextOnly ) ;
Preferences : : instance ( ) - > setToolbarTextPosition ( Qt : : ToolButtonTextOnly ) ;
}
void MainWindow : : toolbarTextBeside ( )
{
m_ui - > toolBar - > setToolButtonStyle ( Qt : : ToolButtonTextBesideIcon ) ;
Preferences : : instance ( ) - > setToolbarTextPosition ( Qt : : ToolButtonTextBesideIcon ) ;
}
void MainWindow : : toolbarTextUnder ( )
{
m_ui - > toolBar - > setToolButtonStyle ( Qt : : ToolButtonTextUnderIcon ) ;
Preferences : : instance ( ) - > setToolbarTextPosition ( Qt : : ToolButtonTextUnderIcon ) ;
}
void MainWindow : : toolbarFollowSystem ( )
{
m_ui - > toolBar - > setToolButtonStyle ( Qt : : ToolButtonFollowStyle ) ;
Preferences : : instance ( ) - > setToolbarTextPosition ( Qt : : ToolButtonFollowStyle ) ;
}
bool MainWindow : : defineUILockPassword ( )
{
bool ok = false ;
const QString newPassword = AutoExpandableDialog : : getText ( this , tr ( " UI lock password " )
, tr ( " Please type the UI lock password: " ) , QLineEdit : : Password , { } , & ok ) ;
if ( ! ok )
return false ;
if ( newPassword . size ( ) < 3 )
{
QMessageBox : : warning ( this , tr ( " Invalid password " ) , tr ( " The password should contain at least 3 characters " ) ) ;
return false ;
}
Preferences : : instance ( ) - > setUILockPassword ( Utils : : Password : : PBKDF2 : : generate ( newPassword ) ) ;
return true ;
}
void MainWindow : : clearUILockPassword ( )
{
const QMessageBox : : StandardButton answer = QMessageBox : : question ( this , tr ( " Clear the password " )
, tr ( " Are you sure you want to clear the password? " ) , ( QMessageBox : : Yes | QMessageBox : : No ) , QMessageBox : : No ) ;
if ( answer = = QMessageBox : : Yes )
Preferences : : instance ( ) - > setUILockPassword ( { } ) ;
}
void MainWindow : : on_actionLock_triggered ( )
{
Preferences * const pref = Preferences : : instance ( ) ;
// Check if there is a password
if ( pref - > getUILockPassword ( ) . isEmpty ( ) )
{
if ( ! defineUILockPassword ( ) )
return ;
}
// Lock the interface
m_uiLocked = true ;
pref - > setUILocked ( true ) ;
m_trayIconMenu - > setEnabled ( false ) ;
hide ( ) ;
}
void MainWindow : : handleRSSUnreadCountUpdated ( int count )
{
m_tabs - > setTabText ( m_tabs - > indexOf ( m_rssWidget ) , tr ( " RSS (%1) " ) . arg ( count ) ) ;
}
void MainWindow : : displayRSSTab ( bool enable )
{
if ( enable )
{
// RSS tab
if ( ! m_rssWidget )
{
m_rssWidget = new RSSWidget ( m_tabs ) ;
connect ( m_rssWidget . data ( ) , & RSSWidget : : unreadCountUpdated , this , & MainWindow : : handleRSSUnreadCountUpdated ) ;
# ifdef Q_OS_MACOS
m_tabs - > addTab ( m_rssWidget , tr ( " RSS (%1) " ) . arg ( RSS : : Session : : instance ( ) - > rootFolder ( ) - > unreadCount ( ) ) ) ;
# else
const int indexTab = m_tabs - > addTab ( m_rssWidget , tr ( " RSS (%1) " ) . arg ( RSS : : Session : : instance ( ) - > rootFolder ( ) - > unreadCount ( ) ) ) ;
m_tabs - > setTabIcon ( indexTab , UIThemeManager : : instance ( ) - > getIcon ( " application-rss+xml " ) ) ;
# endif
}
}
else
{
delete m_rssWidget ;
}
}
void MainWindow : : showFilterContextMenu ( const QPoint & )
{
const Preferences * pref = Preferences : : instance ( ) ;
QMenu * menu = m_searchFilter - > createStandardContextMenu ( ) ;
menu - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
menu - > addSeparator ( ) ;
QAction * useRegexAct = menu - > addAction ( tr ( " Use regular expressions " ) ) ;
useRegexAct - > setCheckable ( true ) ;
useRegexAct - > setChecked ( pref - > getRegexAsFilteringPatternForTransferList ( ) ) ;
connect ( useRegexAct , & QAction : : toggled , pref , & Preferences : : setRegexAsFilteringPatternForTransferList ) ;
connect ( useRegexAct , & QAction : : toggled , this , [ this ] ( ) { m_transferListWidget - > applyNameFilter ( m_searchFilter - > text ( ) ) ; } ) ;
menu - > popup ( QCursor : : pos ( ) ) ;
}
void MainWindow : : displaySearchTab ( bool enable )
{
Preferences : : instance ( ) - > setSearchEnabled ( enable ) ;
if ( enable )
{
// RSS tab
if ( ! m_searchWidget )
{
m_searchWidget = new SearchWidget ( this ) ;
m_tabs - > insertTab ( 1 , m_searchWidget ,
# ifndef Q_OS_MACOS
UIThemeManager : : instance ( ) - > getIcon ( " edit-find " ) ,
# endif
tr ( " Search " ) ) ;
}
}
else
{
delete m_searchWidget ;
}
}
void MainWindow : : focusSearchFilter ( )
{
m_searchFilter - > setFocus ( ) ;
m_searchFilter - > selectAll ( ) ;
}
void MainWindow : : updateNbTorrents ( )
{
m_tabs - > setTabText ( 0 , tr ( " Transfers (%1) " ) . arg ( m_transferListWidget - > getSourceModel ( ) - > rowCount ( ) ) ) ;
}
void MainWindow : : on_actionDocumentation_triggered ( ) const
{
QDesktopServices : : openUrl ( QUrl ( " http://doc.qbittorrent.org " ) ) ;
}
void MainWindow : : tabChanged ( int newTab )
{
Q_UNUSED ( newTab ) ;
// We cannot rely on the index newTab
// because the tab order is undetermined now
if ( m_tabs - > currentWidget ( ) = = m_splitter )
{
qDebug ( " Changed tab to transfer list, refreshing the list " ) ;
m_propertiesWidget - > loadDynamicData ( ) ;
m_searchFilterAction - > setVisible ( true ) ;
return ;
}
m_searchFilterAction - > setVisible ( false ) ;
if ( m_tabs - > currentWidget ( ) = = m_searchWidget )
{
qDebug ( " Changed tab to search engine, giving focus to search input " ) ;
m_searchWidget - > giveFocusToSearchInput ( ) ;
}
}
void MainWindow : : writeSettings ( )
{
Preferences * const pref = Preferences : : instance ( ) ;
pref - > setMainGeometry ( saveGeometry ( ) ) ;
// Splitter size
pref - > setMainVSplitterState ( m_splitter - > saveState ( ) ) ;
m_propertiesWidget - > saveSettings ( ) ;
}
void MainWindow : : cleanup ( )
{
writeSettings ( ) ;
// delete RSSWidget explicitly to avoid crash in
// handleRSSUnreadCountUpdated() at application shutdown
delete m_rssWidget ;
delete m_executableWatcher ;
# ifndef Q_OS_MACOS
if ( m_systrayCreator )
m_systrayCreator - > stop ( ) ;
# endif
m_preventTimer - > stop ( ) ;
# if (defined(Q_OS_WIN) || defined(Q_OS_MACOS))
m_programUpdateTimer - > stop ( ) ;
# endif
// remove all child widgets
while ( auto * w = findChild < QWidget * > ( ) )
delete w ;
}
void MainWindow : : readSettings ( )
{
const Preferences * const pref = Preferences : : instance ( ) ;
const QByteArray mainGeo = pref - > getMainGeometry ( ) ;
if ( ! mainGeo . isEmpty ( ) & & restoreGeometry ( mainGeo ) )
m_posInitialized = true ;
const QByteArray splitterState = pref - > getMainVSplitterState ( ) ;
if ( splitterState . isEmpty ( ) )
// Default sizes
m_splitter - > setSizes ( { 120 , m_splitter - > width ( ) - 120 } ) ;
else
m_splitter - > restoreState ( splitterState ) ;
}
void MainWindow : : balloonClicked ( )
{
if ( isHidden ( ) )
{
if ( m_uiLocked )
{
// Ask for UI lock password
if ( ! unlockUI ( ) )
return ;
}
show ( ) ;
if ( isMinimized ( ) )
showNormal ( ) ;
}
raise ( ) ;
activateWindow ( ) ;
}
void MainWindow : : addTorrentFailed ( const QString & error ) const
{
showNotificationBaloon ( 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 ( ) )
showNotificationBaloon ( 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
{
showNotificationBaloon ( tr ( " Download completion " ) , 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
{
showNotificationBaloon ( 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 ) ;
m_ui - > actionOpen - > setShortcut ( QKeySequence : : Open ) ;
m_ui - > actionDelete - > setShortcut ( QKeySequence : : Delete ) ;
m_ui - > actionDelete - > setShortcutContext ( Qt : : WidgetShortcut ) ; // nullify its effect: delete key event is handled by respective widgets, not here
m_ui - > actionDownloadFromURL - > setShortcut ( Qt : : CTRL + Qt : : SHIFT + Qt : : Key_O ) ;
m_ui - > actionExit - > setShortcut ( Qt : : CTRL + Qt : : Key_Q ) ;
# ifdef Q_OS_MACOS
m_ui - > actionCloseWindow - > setShortcut ( QKeySequence : : Close ) ;
# else
m_ui - > actionCloseWindow - > setVisible ( false ) ;
# endif
const auto * switchTransferShortcut = new QShortcut ( Qt : : ALT + Qt : : Key_1 , this ) ;
connect ( switchTransferShortcut , & QShortcut : : activated , this , & MainWindow : : displayTransferTab ) ;
const auto * switchSearchShortcut = new QShortcut ( Qt : : ALT + Qt : : Key_2 , this ) ;
connect ( switchSearchShortcut , & QShortcut : : activated , this , qOverload < > ( & MainWindow : : displaySearchTab ) ) ;
const auto * switchRSSShortcut = new QShortcut ( Qt : : ALT + Qt : : Key_3 , this ) ;
connect ( switchRSSShortcut , & QShortcut : : activated , this , qOverload < > ( & MainWindow : : displayRSSTab ) ) ;
const auto * switchExecutionLogShortcut = new QShortcut ( Qt : : ALT + Qt : : Key_4 , this ) ;
connect ( switchExecutionLogShortcut , & QShortcut : : activated , this , & MainWindow : : displayExecutionLogTab ) ;
const auto * switchSearchFilterShortcut = new QShortcut ( QKeySequence : : Find , m_transferListWidget ) ;
connect ( switchSearchFilterShortcut , & QShortcut : : activated , this , & MainWindow : : focusSearchFilter ) ;
m_ui - > actionDocumentation - > setShortcut ( QKeySequence : : HelpContents ) ;
m_ui - > actionOptions - > setShortcut ( Qt : : ALT + Qt : : Key_O ) ;
m_ui - > actionStatistics - > setShortcut ( Qt : : CTRL + Qt : : Key_I ) ;
m_ui - > actionStart - > setShortcut ( Qt : : CTRL + Qt : : Key_S ) ;
m_ui - > actionStartAll - > setShortcut ( Qt : : CTRL + Qt : : SHIFT + Qt : : Key_S ) ;
m_ui - > actionPause - > setShortcut ( Qt : : CTRL + Qt : : Key_P ) ;
m_ui - > actionPauseAll - > setShortcut ( Qt : : CTRL + Qt : : SHIFT + Qt : : Key_P ) ;
m_ui - > actionBottomQueuePos - > setShortcut ( Qt : : CTRL + Qt : : SHIFT + Qt : : Key_Minus ) ;
m_ui - > actionDecreaseQueuePos - > setShortcut ( Qt : : CTRL + Qt : : Key_Minus ) ;
m_ui - > actionIncreaseQueuePos - > setShortcut ( Qt : : CTRL + Qt : : Key_Plus ) ;
m_ui - > actionTopQueuePos - > setShortcut ( Qt : : CTRL + Qt : : SHIFT + Qt : : Key_Plus ) ;
# ifdef Q_OS_MACOS
m_ui - > actionMinimize - > setShortcut ( Qt : : CTRL + Qt : : Key_M ) ;
addAction ( m_ui - > actionMinimize ) ;
# endif
}
// Keyboard shortcuts slots
void MainWindow : : displayTransferTab ( ) const
{
m_tabs - > setCurrentWidget ( m_transferListWidget ) ;
}
void MainWindow : : displaySearchTab ( )
{
if ( ! m_searchWidget )
{
m_ui - > actionSearchWidget - > setChecked ( true ) ;
displaySearchTab ( true ) ;
}
m_tabs - > setCurrentWidget ( m_searchWidget ) ;
}
void MainWindow : : displayRSSTab ( )
{
if ( ! m_rssWidget )
{
m_ui - > actionRSSReader - > setChecked ( true ) ;
displayRSSTab ( true ) ;
}
m_tabs - > setCurrentWidget ( m_rssWidget ) ;
}
void MainWindow : : displayExecutionLogTab ( )
{
if ( ! m_executionLog )
{
m_ui - > actionExecutionLogs - > setChecked ( true ) ;
on_actionExecutionLogs_triggered ( true ) ;
}
m_tabs - > setCurrentWidget ( m_executionLog ) ;
}
// End of keyboard shortcuts slots
void MainWindow : : askRecursiveTorrentDownloadConfirmation ( BitTorrent : : Torrent * const torrent )
{
Preferences * const pref = Preferences : : instance ( ) ;
if ( pref - > recursiveDownloadDisabled ( ) ) return ;
const auto torrentHash = torrent - > hash ( ) ;
QMessageBox * confirmBox = new QMessageBox ( QMessageBox : : Question , tr ( " Recursive download confirmation " )
, tr ( " The torrent '%1' contains torrent files, do you want to proceed with their download? " ) . arg ( torrent - > name ( ) )
, QMessageBox : : NoButton , this ) ;
confirmBox - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
confirmBox - > setModal ( true ) ;
const QPushButton * yes = confirmBox - > addButton ( tr ( " Yes " ) , QMessageBox : : YesRole ) ;
/*QPushButton *no = */ confirmBox - > addButton ( tr ( " No " ) , QMessageBox : : NoRole ) ;
const QPushButton * never = confirmBox - > addButton ( tr ( " Never " ) , QMessageBox : : NoRole ) ;
connect ( confirmBox , & QMessageBox : : buttonClicked , this , [ torrentHash , yes , never ] ( const QAbstractButton * button )
{
if ( button = = yes )
BitTorrent : : Session : : instance ( ) - > recursiveTorrentDownload ( torrentHash ) ;
if ( button = = never )
Preferences : : instance ( ) - > disableRecursiveDownload ( ) ;
} ) ;
confirmBox - > show ( ) ;
}
void MainWindow : : handleDownloadFromUrlFailure ( const QString & url , const QString & reason ) const
{
// Display a message box
showNotificationBaloon ( 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 } ;
dialog - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
dialog - > open ( ) ;
}
// Necessary if we want to close the window
// in one time if "close to systray" is enabled
void MainWindow : : on_actionExit_triggered ( )
{
// UI locking enforcement.
if ( isHidden ( ) & & m_uiLocked )
// Ask for UI lock password
if ( ! unlockUI ( ) ) return ;
m_forceExit = true ;
close ( ) ;
}
# ifdef Q_OS_MACOS
void MainWindow : : on_actionCloseWindow_triggered ( )
{
// On macOS window close is basically equivalent to window hide.
// If you decide to implement this functionality for other OS,
// then you will also need ui lock checks like in actionExit.
close ( ) ;
}
# endif
QWidget * MainWindow : : currentTabWidget ( ) const
{
if ( isMinimized ( ) | | ! isVisible ( ) )
return nullptr ;
if ( m_tabs - > currentIndex ( ) = = 0 )
return m_transferListWidget ;
return m_tabs - > currentWidget ( ) ;
}
TransferListWidget * MainWindow : : transferListWidget ( ) const
{
return m_transferListWidget ;
}
bool MainWindow : : unlockUI ( )
{
if ( m_unlockDlgShowing )
return false ;
bool ok = false ;
const QString password = AutoExpandableDialog : : getText ( this , tr ( " UI lock password " )
, tr ( " Please type the UI lock password: " ) , QLineEdit : : Password , { } , & ok ) ;
if ( ! ok ) return false ;
Preferences * const pref = Preferences : : instance ( ) ;
const QByteArray secret = pref - > getUILockPassword ( ) ;
if ( ! Utils : : Password : : PBKDF2 : : verify ( secret , password ) )
{
QMessageBox : : warning ( this , tr ( " Invalid password " ) , tr ( " The password is invalid " ) ) ;
return false ;
}
m_uiLocked = false ;
pref - > setUILocked ( false ) ;
m_trayIconMenu - > setEnabled ( true ) ;
return true ;
}
void MainWindow : : notifyOfUpdate ( const QString & )
{
// Show restart message
m_statusBar - > showRestartRequired ( ) ;
Logger : : instance ( ) - > addMessage ( tr ( " qBittorrent was just updated and needs to be restarted for the changes to be effective. " )
, Log : : CRITICAL ) ;
// Delete the executable watcher
delete m_executableWatcher ;
m_executableWatcher = nullptr ;
}
# ifndef Q_OS_MACOS
// Toggle Main window visibility
void MainWindow : : toggleVisibility ( const QSystemTrayIcon : : ActivationReason reason )
{
switch ( reason )
{
case QSystemTrayIcon : : Trigger :
if ( isHidden ( ) )
{
if ( m_uiLocked & & ! unlockUI ( ) ) // Ask for UI lock password
return ;
// Make sure the window is not minimized
setWindowState ( ( windowState ( ) & ~ Qt : : WindowMinimized ) | Qt : : WindowActive ) ;
// Then show it
show ( ) ;
raise ( ) ;
activateWindow ( ) ;
}
else
{
hide ( ) ;
}
break ;
default :
break ;
}
}
# endif // Q_OS_MACOS
// Display About Dialog
void MainWindow : : on_actionAbout_triggered ( )
{
// About dialog
if ( m_aboutDlg )
m_aboutDlg - > activateWindow ( ) ;
else
m_aboutDlg = new AboutDialog ( this ) ;
}
void MainWindow : : on_actionStatistics_triggered ( )
{
if ( m_statsDlg )
m_statsDlg - > activateWindow ( ) ;
else
m_statsDlg = new StatsDialog ( this ) ;
}
void MainWindow : : showEvent ( QShowEvent * e )
{
qDebug ( " ** Show Event ** " ) ;
if ( currentTabWidget ( ) = = m_transferListWidget )
m_propertiesWidget - > loadDynamicData ( ) ;
e - > accept ( ) ;
// Make sure the window is initially centered
if ( ! m_posInitialized )
{
move ( Utils : : Gui : : screenCenter ( this ) ) ;
m_posInitialized = true ;
}
}
void MainWindow : : keyPressEvent ( QKeyEvent * event )
{
if ( event - > matches ( QKeySequence : : Paste ) )
{
const QMimeData * mimeData { QGuiApplication : : clipboard ( ) - > mimeData ( ) } ;
if ( mimeData - > hasText ( ) )
{
const bool useTorrentAdditionDialog { AddNewTorrentDialog : : isEnabled ( ) } ;
const QStringList lines { mimeData - > text ( ) . split ( ' \n ' , QString : : SkipEmptyParts ) } ;
for ( QString line : lines )
{
line = line . trimmed ( ) ;
if ( ! isTorrentLink ( line ) )
continue ;
if ( useTorrentAdditionDialog )
AddNewTorrentDialog : : show ( line , this ) ;
else
BitTorrent : : Session : : instance ( ) - > addTorrent ( line ) ;
}
return ;
}
}
QMainWindow : : keyPressEvent ( event ) ;
}
// Called when we close the program
void MainWindow : : closeEvent ( QCloseEvent * e )
{
Preferences * const pref = Preferences : : instance ( ) ;
# ifdef Q_OS_MACOS
if ( ! m_forceExit )
{
hide ( ) ;
e - > accept ( ) ;
return ;
}
# else
const bool goToSystrayOnExit = pref - > closeToTray ( ) ;
if ( ! m_forceExit & & m_systrayIcon & & goToSystrayOnExit & & ! this - > isHidden ( ) )
{
e - > ignore ( ) ;
QTimer : : singleShot ( 0 , this , & QWidget : : hide ) ;
if ( ! pref - > closeToTrayNotified ( ) )
{
showNotificationBaloon ( 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 ;
}
# endif // Q_OS_MACOS
if ( pref - > confirmOnExit ( ) & & BitTorrent : : Session : : instance ( ) - > hasActiveTorrents ( ) )
{
if ( e - > spontaneous ( ) | | m_forceExit )
{
if ( ! isVisible ( ) )
show ( ) ;
QMessageBox confirmBox ( QMessageBox : : Question , tr ( " Exiting qBittorrent " ) ,
// Split it because the last sentence is used in the Web UI
tr ( " Some files are currently transferring. " ) + ' \n ' + tr ( " Are you sure you want to quit qBittorrent? " ) ,
QMessageBox : : NoButton , this ) ;
QPushButton * noBtn = confirmBox . addButton ( tr ( " &No " ) , QMessageBox : : NoRole ) ;
confirmBox . addButton ( tr ( " &Yes " ) , QMessageBox : : YesRole ) ;
QPushButton * alwaysBtn = confirmBox . addButton ( tr ( " &Always Yes " ) , QMessageBox : : YesRole ) ;
confirmBox . setDefaultButton ( noBtn ) ;
confirmBox . exec ( ) ;
if ( ! confirmBox . clickedButton ( ) | | ( confirmBox . clickedButton ( ) = = noBtn ) )
{
// Cancel exit
e - > ignore ( ) ;
m_forceExit = false ;
return ;
}
if ( confirmBox . clickedButton ( ) = = alwaysBtn )
// Remember choice
Preferences : : instance ( ) - > setConfirmOnExit ( false ) ;
}
}
// abort search if any
delete m_searchWidget ;
hide ( ) ;
# ifndef Q_OS_MACOS
// Hide tray icon
if ( m_systrayIcon )
m_systrayIcon - > hide ( ) ;
# endif
// Accept exit
e - > accept ( ) ;
qApp - > exit ( ) ;
}
// Display window to create a torrent
void MainWindow : : on_actionCreateTorrent_triggered ( )
{
createTorrentTriggered ( ) ;
}
void MainWindow : : createTorrentTriggered ( const QString & path )
{
if ( m_createTorrentDlg )
{
m_createTorrentDlg - > updateInputPath ( path ) ;
m_createTorrentDlg - > activateWindow ( ) ;
}
else
m_createTorrentDlg = new TorrentCreatorDialog ( this , path ) ;
}
bool MainWindow : : event ( QEvent * e )
{
# ifndef Q_OS_MACOS
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 ( ) )
{
qDebug ( ) < < " Has active window: " < < ( qApp - > activeWindow ( ) ! = nullptr ) ;
// Check if there is a modal window
bool hasModalWindow = false ;
for ( QWidget * widget : asConst ( QApplication : : allWidgets ( ) ) )
{
if ( widget - > isModal ( ) )
{
hasModalWindow = true ;
break ;
}
}
// Iconify if there is no modal window
if ( ! hasModalWindow )
{
qDebug ( " Minimize to Tray enabled, hiding! " ) ;
e - > ignore ( ) ;
QTimer : : singleShot ( 0 , this , & QWidget : : hide ) ;
if ( ! pref - > minimizeToTrayNotified ( ) )
{
showNotificationBaloon ( 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 ;
}
}
}
break ;
}
case QEvent : : ToolBarChange :
{
qDebug ( " MAC: Received a toolbar change event! " ) ;
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 ;
}
default :
break ;
}
# endif // Q_OS_MACOS
return QMainWindow : : event ( e ) ;
}
// action executed when a file is dropped
void MainWindow : : dropEvent ( QDropEvent * event )
{
event - > acceptProposedAction ( ) ;
// remove scheme
QStringList files ;
if ( event - > mimeData ( ) - > hasUrls ( ) )
{
for ( const QUrl & url : asConst ( event - > mimeData ( ) - > urls ( ) ) )
{
if ( url . isEmpty ( ) )
continue ;
files < < ( ( url . scheme ( ) . compare ( " file " , Qt : : CaseInsensitive ) = = 0 )
? url . toLocalFile ( )
: url . toString ( ) ) ;
}
}
else
{
files = event - > mimeData ( ) - > text ( ) . split ( ' \n ' ) ;
}
// differentiate ".torrent" files/links & magnet links from others
QStringList torrentFiles , otherFiles ;
for ( const QString & file : asConst ( files ) )
{
if ( isTorrentLink ( file ) )
torrentFiles < < file ;
else
otherFiles < < file ;
}
// Download torrents
const bool useTorrentAdditionDialog = AddNewTorrentDialog : : isEnabled ( ) ;
for ( const QString & file : asConst ( torrentFiles ) )
{
if ( useTorrentAdditionDialog )
AddNewTorrentDialog : : show ( file , this ) ;
else
BitTorrent : : Session : : instance ( ) - > addTorrent ( file ) ;
}
if ( ! torrentFiles . isEmpty ( ) ) return ;
// Create torrent
for ( const QString & file : asConst ( otherFiles ) )
{
createTorrentTriggered ( file ) ;
// currently only handle the first entry
// this is a stub that can be expanded later to create many torrents at once
break ;
}
}
// Decode if we accept drag 'n drop or not
void MainWindow : : dragEnterEvent ( QDragEnterEvent * event )
{
for ( const QString & mime : asConst ( event - > mimeData ( ) - > formats ( ) ) )
qDebug ( " mimeData: %s " , mime . toLocal8Bit ( ) . data ( ) ) ;
if ( event - > mimeData ( ) - > hasFormat ( " text/plain " ) | | event - > mimeData ( ) - > hasFormat ( " text/uri-list " ) )
event - > acceptProposedAction ( ) ;
}
# ifdef Q_OS_MACOS
static MainWindow * dockMainWindowHandle ;
static bool dockClickHandler ( id self , SEL cmd , . . . )
{
Q_UNUSED ( self )
Q_UNUSED ( cmd )
if ( dockMainWindowHandle & & ! dockMainWindowHandle - > isVisible ( ) )
{
dockMainWindowHandle - > activate ( ) ;
}
return true ;
}
void MainWindow : : setupDockClickHandler ( )
{
dockMainWindowHandle = this ;
MacUtils : : overrideDockClickHandler ( dockClickHandler ) ;
}
# endif // Q_OS_MACOS
/*****************************************************
* *
* Torrent *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Display a dialog to allow user to add
// torrents to download list
void MainWindow : : on_actionOpen_triggered ( )
{
Preferences * const pref = Preferences : : instance ( ) ;
// Open File Open Dialog
// Note: it is possible to select more than one file
const QStringList pathsList =
QFileDialog : : getOpenFileNames ( this , tr ( " Open Torrent Files " ) , pref - > getMainLastDir ( ) ,
tr ( " Torrent Files " ) + " (* " + C_TORRENT_FILE_EXTENSION + ' ) ' ) ;
if ( pathsList . isEmpty ( ) )
return ;
const bool useTorrentAdditionDialog = AddNewTorrentDialog : : isEnabled ( ) ;
for ( const QString & file : pathsList )
{
if ( useTorrentAdditionDialog )
AddNewTorrentDialog : : show ( file , this ) ;
else
BitTorrent : : Session : : instance ( ) - > addTorrent ( file ) ;
}
// Save last dir to remember it
QString topDir = Utils : : Fs : : toUniformPath ( pathsList . at ( 0 ) ) ;
topDir = topDir . left ( topDir . lastIndexOf ( ' / ' ) ) ;
pref - > setMainLastDir ( topDir ) ;
}
void MainWindow : : activate ( )
{
if ( ! m_uiLocked | | unlockUI ( ) )
{
show ( ) ;
activateWindow ( ) ;
raise ( ) ;
}
}
void MainWindow : : optionsSaved ( )
{
loadPreferences ( ) ;
}
void MainWindow : : showStatusBar ( bool show )
{
if ( ! show )
{
// Remove status bar
setStatusBar ( nullptr ) ;
}
else if ( ! m_statusBar )
{
// Create status bar
m_statusBar = new StatusBar ;
connect ( m_statusBar . data ( ) , & StatusBar : : connectionButtonClicked , this , & MainWindow : : showConnectionSettings ) ;
connect ( m_statusBar . data ( ) , & StatusBar : : alternativeSpeedsButtonClicked , this , & MainWindow : : toggleAlternativeSpeeds ) ;
setStatusBar ( m_statusBar ) ;
}
}
void MainWindow : : loadPreferences ( const bool configureSession )
{
Logger : : instance ( ) - > addMessage ( tr ( " Options were saved successfully. " ) ) ;
const Preferences * const pref = Preferences : : instance ( ) ;
# ifdef Q_OS_MACOS
Q_UNUSED ( configureSession ) ;
# else
const bool newSystrayIntegration = pref - > systrayIntegration ( ) ;
m_ui - > actionLock - > setVisible ( newSystrayIntegration ) ;
if ( newSystrayIntegration ! = ( m_systrayIcon ! = nullptr ) )
{
if ( newSystrayIntegration )
{
// create the trayicon
if ( ! QSystemTrayIcon : : isSystemTrayAvailable ( ) )
{
if ( ! configureSession )
{ // Program startup
m_systrayCreator = new QTimer ( this ) ;
connect ( m_systrayCreator . data ( ) , & QTimer : : timeout , this , & MainWindow : : createSystrayDelayed ) ;
m_systrayCreator - > setSingleShot ( true ) ;
m_systrayCreator - > start ( 2000 ) ;
qDebug ( " Info: System tray is unavailable, trying again later. " ) ;
}
else
{
qDebug ( " Warning: System tray is unavailable. " ) ;
}
}
else
{
createTrayIcon ( ) ;
}
}
else
{
// Destroy trayicon
delete m_systrayIcon ;
delete m_trayIconMenu ;
}
}
// Reload systray icon
if ( newSystrayIntegration & & m_systrayIcon )
m_systrayIcon - > setIcon ( getSystrayIcon ( ) ) ;
# endif
// General
if ( pref - > isToolbarDisplayed ( ) )
{
m_ui - > toolBar - > setVisible ( true ) ;
}
else
{
// Clear search filter before hiding the top toolbar
m_searchFilter - > clear ( ) ;
m_ui - > toolBar - > setVisible ( false ) ;
}
showStatusBar ( pref - > isStatusbarDisplayed ( ) ) ;
if ( pref - > preventFromSuspendWhenDownloading ( ) | | pref - > preventFromSuspendWhenSeeding ( ) )
{
if ( ! m_preventTimer - > isActive ( ) )
{
updatePowerManagementState ( ) ;
m_preventTimer - > start ( PREVENT_SUSPEND_INTERVAL ) ;
}
}
else
{
m_preventTimer - > stop ( ) ;
m_pwr - > setActivityState ( false ) ;
}
m_transferListWidget - > setAlternatingRowColors ( pref - > useAlternatingRowColors ( ) ) ;
m_propertiesWidget - > getFilesList ( ) - > setAlternatingRowColors ( pref - > useAlternatingRowColors ( ) ) ;
m_propertiesWidget - > getTrackerList ( ) - > setAlternatingRowColors ( pref - > useAlternatingRowColors ( ) ) ;
m_propertiesWidget - > getPeerList ( ) - > setAlternatingRowColors ( pref - > useAlternatingRowColors ( ) ) ;
// Queueing System
if ( BitTorrent : : Session : : instance ( ) - > isQueueingSystemEnabled ( ) )
{
if ( ! m_ui - > actionDecreaseQueuePos - > isVisible ( ) )
{
m_transferListWidget - > hideQueuePosColumn ( false ) ;
m_ui - > actionDecreaseQueuePos - > setVisible ( true ) ;
m_ui - > actionIncreaseQueuePos - > setVisible ( true ) ;
m_ui - > actionTopQueuePos - > setVisible ( true ) ;
m_ui - > actionBottomQueuePos - > setVisible ( true ) ;
# ifndef Q_OS_MACOS
m_queueSeparator - > setVisible ( true ) ;
# endif
m_queueSeparatorMenu - > setVisible ( true ) ;
}
}
else
{
if ( m_ui - > actionDecreaseQueuePos - > isVisible ( ) )
{
m_transferListWidget - > hideQueuePosColumn ( true ) ;
m_ui - > actionDecreaseQueuePos - > setVisible ( false ) ;
m_ui - > actionIncreaseQueuePos - > setVisible ( false ) ;
m_ui - > actionTopQueuePos - > setVisible ( false ) ;
m_ui - > actionBottomQueuePos - > setVisible ( false ) ;
# ifndef Q_OS_MACOS
m_queueSeparator - > setVisible ( false ) ;
# endif
m_queueSeparatorMenu - > setVisible ( false ) ;
}
}
// Torrent properties
m_propertiesWidget - > reloadPreferences ( ) ;
# if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
if ( pref - > isUpdateCheckEnabled ( ) & & ! m_wasUpdateCheckEnabled )
{
m_wasUpdateCheckEnabled = true ;
checkProgramUpdate ( ) ;
}
else if ( ! pref - > isUpdateCheckEnabled ( ) & & m_wasUpdateCheckEnabled )
{
m_wasUpdateCheckEnabled = false ;
m_programUpdateTimer - > stop ( ) ;
}
# endif
qDebug ( " GUI settings loaded " ) ;
}
void MainWindow : : reloadSessionStats ( )
{
const BitTorrent : : SessionStatus & status = BitTorrent : : Session : : instance ( ) - > status ( ) ;
// update global information
# ifdef Q_OS_MACOS
if ( status . payloadDownloadRate > 0 )
{
QtMac : : setBadgeLabelText ( tr ( " %1/s " , " s is a shorthand for seconds " )
. arg ( Utils : : Misc : : friendlyUnit ( status . payloadDownloadRate ) ) ) ;
}
else if ( ! QtMac : : badgeLabelText ( ) . isEmpty ( ) )
{
QtMac : : setBadgeLabelText ( " " ) ;
}
# else
if ( m_systrayIcon )
{
const QString toolTip = QString : : fromLatin1 ( " %1 \n %2 " ) . 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
}
# endif // Q_OS_MACOS
if ( m_displaySpeedInTitle )
{
setWindowTitle ( tr ( " [D: %1, U: %2] qBittorrent %3 " , " D = Download; U = Upload; %3 is qBittorrent version " )
. arg ( Utils : : Misc : : friendlyUnit ( status . payloadDownloadRate , true )
, Utils : : Misc : : friendlyUnit ( status . payloadUploadRate , true )
, QBT_VERSION ) ) ;
}
}
void MainWindow : : reloadTorrentStats ( const QVector < BitTorrent : : Torrent * > & torrents )
{
if ( currentTabWidget ( ) = = m_transferListWidget )
{
if ( torrents . contains ( m_propertiesWidget - > getCurrentTorrent ( ) ) )
m_propertiesWidget - > loadDynamicData ( ) ;
}
}
void MainWindow : : showNotificationBaloon ( const QString & title , const QString & msg ) const
{
if ( ! isNotificationsEnabled ( ) ) return ;
# if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
org : : freedesktop : : Notifications notifications ( " org.freedesktop.Notifications " ,
" /org/freedesktop/Notifications " ,
QDBusConnection : : sessionBus ( ) ) ;
// Testing for 'notifications.isValid()' isn't helpful here.
// If the notification daemon is configured to run 'as needed'
// the above check can be false if the daemon wasn't started
// by another application. In this case DBus will be able to
// start the notification daemon and complete our request. Such
// a daemon is xfce4-notifyd, DBus autostarts it and after
// some inactivity shuts it down. Other DEs, like GNOME, choose
// to start their daemons at the session startup and have it sit
// idling for the whole session.
QVariantMap hints ;
hints [ " desktop-entry " ] = " qBittorrent " ;
QDBusPendingReply < uint > reply = notifications . Notify ( " qBittorrent " , 0 , " qbittorrent " , title ,
msg , QStringList ( ) , hints , - 1 ) ;
reply . waitForFinished ( ) ;
if ( ! reply . isError ( ) )
return ;
# 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 *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void MainWindow : : downloadFromURLList ( const QStringList & urlList )
{
const bool useTorrentAdditionDialog = AddNewTorrentDialog : : isEnabled ( ) ;
for ( const QString & url : urlList )
{
if ( useTorrentAdditionDialog )
AddNewTorrentDialog : : show ( url , this ) ;
else
BitTorrent : : Session : : instance ( ) - > addTorrent ( url ) ;
}
}
/*****************************************************
* *
* Options *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifndef Q_OS_MACOS
void MainWindow : : createSystrayDelayed ( )
{
static int timeout = 20 ;
if ( QSystemTrayIcon : : isSystemTrayAvailable ( ) )
{
// Ok, systray integration is now supported
// Create systray icon
createTrayIcon ( ) ;
delete m_systrayCreator ;
}
else
{
if ( timeout )
{
// Retry a bit later
m_systrayCreator - > start ( 2000 ) ;
- - timeout ;
}
else
{
// Timed out, apparently system really does not
// support systray icon
delete m_systrayCreator ;
// Disable it in program preferences to
// avoid trying at each startup
Preferences : : instance ( ) - > setSystrayIntegration ( false ) ;
}
}
}
void MainWindow : : updateTrayIconMenu ( )
{
m_ui - > actionToggleVisibility - > setText ( isVisible ( ) ? tr ( " Hide " ) : tr ( " Show " ) ) ;
}
void MainWindow : : createTrayIcon ( )
{
// Tray icon
m_systrayIcon = new QSystemTrayIcon ( getSystrayIcon ( ) , this ) ;
m_systrayIcon - > setContextMenu ( trayIconMenu ( ) ) ;
connect ( m_systrayIcon . data ( ) , & QSystemTrayIcon : : messageClicked , this , & MainWindow : : balloonClicked ) ;
// End of Icon Menu
connect ( m_systrayIcon . data ( ) , & QSystemTrayIcon : : activated , this , & MainWindow : : toggleVisibility ) ;
m_systrayIcon - > show ( ) ;
}
# endif // Q_OS_MACOS
QMenu * MainWindow : : trayIconMenu ( )
{
if ( m_trayIconMenu ) return m_trayIconMenu ;
m_trayIconMenu = new QMenu ( this ) ;
# ifndef Q_OS_MACOS
connect ( m_trayIconMenu . data ( ) , & QMenu : : aboutToShow , this , & MainWindow : : updateTrayIconMenu ) ;
m_trayIconMenu - > addAction ( m_ui - > actionToggleVisibility ) ;
m_trayIconMenu - > addSeparator ( ) ;
# endif
m_trayIconMenu - > addAction ( m_ui - > actionOpen ) ;
m_trayIconMenu - > addAction ( m_ui - > actionDownloadFromURL ) ;
m_trayIconMenu - > addSeparator ( ) ;
const bool isAltBWEnabled = BitTorrent : : Session : : instance ( ) - > isAltGlobalSpeedLimitEnabled ( ) ;
updateAltSpeedsBtn ( isAltBWEnabled ) ;
m_ui - > actionUseAlternativeSpeedLimits - > setChecked ( isAltBWEnabled ) ;
m_trayIconMenu - > addAction ( m_ui - > actionUseAlternativeSpeedLimits ) ;
m_trayIconMenu - > addAction ( m_ui - > actionSetGlobalSpeedLimits ) ;
m_trayIconMenu - > addSeparator ( ) ;
m_trayIconMenu - > addAction ( m_ui - > actionStartAll ) ;
m_trayIconMenu - > addAction ( m_ui - > actionPauseAll ) ;
# ifndef Q_OS_MACOS
m_trayIconMenu - > addSeparator ( ) ;
m_trayIconMenu - > addAction ( m_ui - > actionExit ) ;
# endif
if ( m_uiLocked )
m_trayIconMenu - > setEnabled ( false ) ;
return m_trayIconMenu ;
}
void MainWindow : : updateAltSpeedsBtn ( bool alternative )
{
m_ui - > actionUseAlternativeSpeedLimits - > setChecked ( alternative ) ;
}
PropertiesWidget * MainWindow : : propertiesWidget ( ) const
{
return m_propertiesWidget ;
}
// Display Program Options
void MainWindow : : on_actionOptions_triggered ( )
{
if ( m_options )
m_options - > activateWindow ( ) ;
else
m_options = new OptionsDialog ( this ) ;
}
void MainWindow : : on_actionTopToolBar_triggered ( )
{
const bool isVisible = static_cast < QAction * > ( sender ( ) ) - > isChecked ( ) ;
m_ui - > toolBar - > setVisible ( isVisible ) ;
Preferences : : instance ( ) - > setToolbarDisplayed ( isVisible ) ;
}
void MainWindow : : on_actionShowStatusbar_triggered ( )
{
const bool isVisible = static_cast < QAction * > ( sender ( ) ) - > isChecked ( ) ;
Preferences : : instance ( ) - > setStatusbarDisplayed ( isVisible ) ;
showStatusBar ( isVisible ) ;
}
void MainWindow : : on_actionSpeedInTitleBar_triggered ( )
{
m_displaySpeedInTitle = static_cast < QAction * > ( sender ( ) ) - > isChecked ( ) ;
Preferences : : instance ( ) - > showSpeedInTitleBar ( m_displaySpeedInTitle ) ;
if ( m_displaySpeedInTitle )
reloadSessionStats ( ) ;
else
setWindowTitle ( " qBittorrent " QBT_VERSION ) ;
}
void MainWindow : : on_actionRSSReader_triggered ( )
{
Preferences : : instance ( ) - > setRSSWidgetVisible ( m_ui - > actionRSSReader - > isChecked ( ) ) ;
displayRSSTab ( m_ui - > actionRSSReader - > isChecked ( ) ) ;
}
void MainWindow : : on_actionSearchWidget_triggered ( )
{
if ( ! m_hasPython & & m_ui - > actionSearchWidget - > isChecked ( ) )
{
const Utils : : ForeignApps : : PythonInfo pyInfo = Utils : : ForeignApps : : pythonInfo ( ) ;
// Not installed
if ( ! pyInfo . isValid ( ) )
{
m_ui - > actionSearchWidget - > setChecked ( false ) ;
Preferences : : instance ( ) - > setSearchEnabled ( false ) ;
# ifdef Q_OS_WIN
const QMessageBox : : StandardButton buttonPressed = QMessageBox : : question ( this , tr ( " Missing Python Runtime " )
, tr ( " Python is required to use the search engine but it does not seem to be installed. \n Do you want to install it now? " )
, ( QMessageBox : : Yes | QMessageBox : : No ) , QMessageBox : : Yes ) ;
if ( buttonPressed = = QMessageBox : : Yes )
installPython ( ) ;
# else
QMessageBox : : information ( this , tr ( " Missing Python Runtime " )
, tr ( " Python is required to use the search engine but it does not seem to be installed. " ) ) ;
# endif
return ;
}
// Check version requirement
if ( ! pyInfo . isSupportedVersion ( ) )
{
m_ui - > actionSearchWidget - > setChecked ( false ) ;
Preferences : : instance ( ) - > setSearchEnabled ( false ) ;
# ifdef Q_OS_WIN
const QMessageBox : : StandardButton buttonPressed = QMessageBox : : question ( this , tr ( " Old Python Runtime " )
, tr ( " Your Python version (%1) is outdated. Minimum requirement: 3.5.0. \n Do you want to install a newer version now? " )
. arg ( pyInfo . version )
, ( QMessageBox : : Yes | QMessageBox : : No ) , QMessageBox : : Yes ) ;
if ( buttonPressed = = QMessageBox : : Yes )
installPython ( ) ;
# else
QMessageBox : : information ( this , tr ( " Old Python Runtime " )
, tr ( " Your Python version (%1) is outdated. Please upgrade to latest version for search engines to work. \n Minimum requirement: 3.3.0. " )
. arg ( pyInfo . version ) ) ;
# endif
return ;
}
m_hasPython = true ;
m_ui - > actionSearchWidget - > setChecked ( true ) ;
Preferences : : instance ( ) - > setSearchEnabled ( true ) ;
}
displaySearchTab ( m_ui - > actionSearchWidget - > isChecked ( ) ) ;
}
/*****************************************************
* *
* HTTP Downloader *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Display an input dialog to prompt user for
// an url
void MainWindow : : on_actionDownloadFromURL_triggered ( )
{
if ( ! m_downloadFromURLDialog )
{
m_downloadFromURLDialog = new DownloadFromURLDialog ( this ) ;
connect ( m_downloadFromURLDialog . data ( ) , & DownloadFromURLDialog : : urlsReadyToBeDownloaded , this , & MainWindow : : downloadFromURLList ) ;
}
}
# if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
void MainWindow : : handleUpdateCheckFinished ( bool updateAvailable , QString newVersion , bool invokedByUser )
{
QMessageBox : : StandardButton answer = QMessageBox : : Yes ;
if ( updateAvailable )
{
answer = QMessageBox : : question ( this , tr ( " qBittorrent Update Available " )
, tr ( " A new version is available. " ) + " <br/> "
+ tr ( " Do you want to download %1? " ) . arg ( newVersion ) + " <br/><br/> "
+ QString : : fromLatin1 ( " <a href= \" https://www.qbittorrent.org/news.php \" >%1</a> " ) . arg ( tr ( " Open changelog... " ) )
, QMessageBox : : Yes | QMessageBox : : No , QMessageBox : : Yes ) ;
if ( answer = = QMessageBox : : Yes )
{
// The user want to update, let's download the update
ProgramUpdater * updater = dynamic_cast < ProgramUpdater * > ( sender ( ) ) ;
updater - > updateProgram ( ) ;
}
}
else if ( invokedByUser )
{
QMessageBox : : information ( this , tr ( " Already Using the Latest qBittorrent Version " ) ,
tr ( " No updates available. \n You are already using the latest version. " ) ) ;
}
sender ( ) - > deleteLater ( ) ;
m_ui - > actionCheckForUpdates - > setEnabled ( true ) ;
m_ui - > actionCheckForUpdates - > setText ( tr ( " &Check for Updates " ) ) ;
m_ui - > actionCheckForUpdates - > setToolTip ( tr ( " Check for program updates " ) ) ;
// Don't bother the user again in this session if he chose to ignore the update
if ( Preferences : : instance ( ) - > isUpdateCheckEnabled ( ) & & ( answer = = QMessageBox : : Yes ) )
m_programUpdateTimer - > start ( ) ;
}
# endif
void MainWindow : : toggleAlternativeSpeeds ( )
{
BitTorrent : : Session * const session = BitTorrent : : Session : : instance ( ) ;
session - > setAltGlobalSpeedLimitEnabled ( ! session - > isAltGlobalSpeedLimitEnabled ( ) ) ;
}
void MainWindow : : on_actionDonateMoney_triggered ( )
{
QDesktopServices : : openUrl ( QUrl ( " https://www.qbittorrent.org/donate " ) ) ;
}
void MainWindow : : showConnectionSettings ( )
{
on_actionOptions_triggered ( ) ;
m_options - > showConnectionTab ( ) ;
}
void MainWindow : : minimizeWindow ( )
{
setWindowState ( windowState ( ) | Qt : : WindowMinimized ) ;
}
void MainWindow : : on_actionExecutionLogs_triggered ( bool checked )
{
if ( checked )
{
Q_ASSERT ( ! m_executionLog ) ;
m_executionLog = new ExecutionLogWidget ( static_cast < Log : : MsgType > ( executionLogMsgTypes ( ) ) , m_tabs ) ;
# ifdef Q_OS_MACOS
m_tabs - > addTab ( m_executionLog , tr ( " Execution Log " ) ) ;
# else
const int indexTab = m_tabs - > addTab ( m_executionLog , tr ( " Execution Log " ) ) ;
m_tabs - > setTabIcon ( indexTab , UIThemeManager : : instance ( ) - > getIcon ( " view-calendar-journal " ) ) ;
# endif
}
else
{
delete m_executionLog ;
}
m_ui - > actionNormalMessages - > setEnabled ( checked ) ;
m_ui - > actionInformationMessages - > setEnabled ( checked ) ;
m_ui - > actionWarningMessages - > setEnabled ( checked ) ;
m_ui - > actionCriticalMessages - > setEnabled ( checked ) ;
setExecutionLogEnabled ( checked ) ;
}
void MainWindow : : on_actionNormalMessages_triggered ( bool checked )
{
if ( ! m_executionLog )
return ;
Log : : MsgTypes flags ( executionLogMsgTypes ( ) ) ;
checked ? ( flags | = Log : : NORMAL ) : ( flags & = ~ Log : : NORMAL ) ;
setExecutionLogMsgTypes ( flags ) ;
}
void MainWindow : : on_actionInformationMessages_triggered ( bool checked )
{
if ( ! m_executionLog )
return ;
Log : : MsgTypes flags ( executionLogMsgTypes ( ) ) ;
checked ? ( flags | = Log : : INFO ) : ( flags & = ~ Log : : INFO ) ;
setExecutionLogMsgTypes ( flags ) ;
}
void MainWindow : : on_actionWarningMessages_triggered ( bool checked )
{
if ( ! m_executionLog )
return ;
Log : : MsgTypes flags ( executionLogMsgTypes ( ) ) ;
checked ? ( flags | = Log : : WARNING ) : ( flags & = ~ Log : : WARNING ) ;
setExecutionLogMsgTypes ( flags ) ;
}
void MainWindow : : on_actionCriticalMessages_triggered ( bool checked )
{
if ( ! m_executionLog )
return ;
Log : : MsgTypes flags ( executionLogMsgTypes ( ) ) ;
checked ? ( flags | = Log : : CRITICAL ) : ( flags & = ~ Log : : CRITICAL ) ;
setExecutionLogMsgTypes ( flags ) ;
}
void MainWindow : : on_actionAutoExit_toggled ( bool enabled )
{
qDebug ( ) < < Q_FUNC_INFO < < enabled ;
Preferences : : instance ( ) - > setShutdownqBTWhenDownloadsComplete ( enabled ) ;
}
void MainWindow : : on_actionAutoSuspend_toggled ( bool enabled )
{
qDebug ( ) < < Q_FUNC_INFO < < enabled ;
Preferences : : instance ( ) - > setSuspendWhenDownloadsComplete ( enabled ) ;
}
void MainWindow : : on_actionAutoHibernate_toggled ( bool enabled )
{
qDebug ( ) < < Q_FUNC_INFO < < enabled ;
Preferences : : instance ( ) - > setHibernateWhenDownloadsComplete ( enabled ) ;
}
void MainWindow : : on_actionAutoShutdown_toggled ( bool enabled )
{
qDebug ( ) < < Q_FUNC_INFO < < enabled ;
Preferences : : instance ( ) - > setShutdownWhenDownloadsComplete ( enabled ) ;
}
void MainWindow : : updatePowerManagementState ( )
{
const bool inhibitSuspend = ( Preferences : : instance ( ) - > preventFromSuspendWhenDownloading ( ) & & BitTorrent : : Session : : instance ( ) - > hasUnfinishedTorrents ( ) )
| | ( Preferences : : instance ( ) - > preventFromSuspendWhenSeeding ( ) & & BitTorrent : : Session : : instance ( ) - > hasRunningSeed ( ) ) ;
m_pwr - > setActivityState ( inhibitSuspend ) ;
}
# ifndef Q_OS_MACOS
QIcon MainWindow : : getSystrayIcon ( ) const
{
const TrayIcon : : Style style = Preferences : : instance ( ) - > trayIconStyle ( ) ;
// on Linux we use theme icons, and icons from resources everywhere else
# if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
switch ( style )
{
case TrayIcon : : NORMAL :
return QIcon : : fromTheme ( QLatin1String ( " qbittorrent-tray " ) ) ;
case TrayIcon : : MONO_DARK :
return QIcon : : fromTheme ( QLatin1String ( " qbittorrent-tray-dark " ) ) ;
case TrayIcon : : MONO_LIGHT :
return QIcon : : fromTheme ( QLatin1String ( " qbittorrent-tray-light " ) ) ;
default :
break ;
}
# else
switch ( style )
{
case TrayIcon : : NORMAL :
return UIThemeManager : : instance ( ) - > getIcon ( QLatin1String ( " qbittorrent-tray " ) ) ;
case TrayIcon : : MONO_DARK :
return UIThemeManager : : instance ( ) - > getIcon ( QLatin1String ( " qbittorrent-tray-dark " ) ) ;
case TrayIcon : : MONO_LIGHT :
return UIThemeManager : : instance ( ) - > getIcon ( QLatin1String ( " qbittorrent-tray-light " ) ) ;
default :
break ;
}
# endif
// As a failsafe in case the enum is invalid
return UIThemeManager : : instance ( ) - > getIcon ( QLatin1String ( " qbittorrent-tray " ) ) ;
}
# endif // Q_OS_MACOS
# if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
void MainWindow : : checkProgramUpdate ( )
{
m_programUpdateTimer - > stop ( ) ; // If the user had clicked the menu item
m_ui - > actionCheckForUpdates - > setEnabled ( false ) ;
m_ui - > actionCheckForUpdates - > setText ( tr ( " Checking for Updates... " ) ) ;
m_ui - > actionCheckForUpdates - > setToolTip ( tr ( " Already checking for program updates in the background " ) ) ;
bool invokedByUser = m_ui - > actionCheckForUpdates = = qobject_cast < QAction * > ( sender ( ) ) ;
ProgramUpdater * updater = new ProgramUpdater ( this , invokedByUser ) ;
connect ( updater , & ProgramUpdater : : updateCheckFinished , this , & MainWindow : : handleUpdateCheckFinished ) ;
updater - > checkForUpdates ( ) ;
}
# endif
# ifdef Q_OS_WIN
void MainWindow : : installPython ( )
{
setCursor ( QCursor ( Qt : : WaitCursor ) ) ;
// Download python
# ifdef QBT_APP_64BIT
const QString installerURL = " https://www.python.org/ftp/python/3.8.5/python-3.8.5-amd64.exe " ;
# else
const QString installerURL = " https://www.python.org/ftp/python/3.8.5/python-3.8.5.exe " ;
# endif
Net : : DownloadManager : : instance ( ) - > download (
Net : : DownloadRequest ( installerURL ) . saveToFile ( true )
, this , & MainWindow : : pythonDownloadFinished ) ;
}
void MainWindow : : pythonDownloadFinished ( const Net : : DownloadResult & result )
{
if ( result . status ! = Net : : DownloadStatus : : Success )
{
setCursor ( QCursor ( Qt : : ArrowCursor ) ) ;
QMessageBox : : warning (
this , tr ( " Download error " )
, tr ( " Python setup could not be downloaded, reason: %1. \n Please install it manually. " )
. arg ( result . errorString ) ) ;
return ;
}
setCursor ( QCursor ( Qt : : ArrowCursor ) ) ;
QProcess installer ;
qDebug ( " Launching Python installer in passive mode... " ) ;
QFile : : rename ( result . filePath , result . filePath + " .exe " ) ;
installer . start ( ' " ' + Utils : : Fs : : toNativePath ( result . filePath ) + " .exe \" /passive " ) ;
// Wait for setup to complete
installer . waitForFinished ( 10 * 60 * 1000 ) ;
qDebug ( " Installer stdout: %s " , installer . readAllStandardOutput ( ) . data ( ) ) ;
qDebug ( " Installer stderr: %s " , installer . readAllStandardError ( ) . data ( ) ) ;
qDebug ( " Setup should be complete! " ) ;
// Delete temp file
Utils : : Fs : : forceRemove ( result . filePath + " .exe " ) ;
// Reload search engine
if ( Utils : : ForeignApps : : pythonInfo ( ) . isSupportedVersion ( ) )
{
m_ui - > actionSearchWidget - > setChecked ( true ) ;
displaySearchTab ( true ) ;
}
}
# endif // Q_OS_WIN