/*
* 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 <QClipboard>
# include <QCloseEvent>
# include <QCryptographicHash>
# include <QDebug>
# include <QDesktopServices>
# include <QFileDialog>
# include <QFileSystemWatcher>
# include <QMessageBox>
# include <QMimeData>
# include <QProcess>
# include <QPushButton>
# include <QRegularExpression>
# include <QScrollBar>
# include <QShortcut>
# include <QSplitter>
# include <QStatusBar>
# include <QSysInfo>
# include <QtGlobal>
# include <QTimer>
# ifdef Q_OS_MAC
# include <QtMac>
# include <QtMacExtras>
# endif
# if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
# include <QDBusConnection>
# include "notifications.h"
# endif
# include "base/bittorrent/session.h"
# include "base/bittorrent/sessionstatus.h"
# include "base/bittorrent/torrenthandle.h"
# include "base/global.h"
# include "base/logger.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 "aboutdialog.h"
# include "addnewtorrentdialog.h"
# include "application.h"
# include "autoexpandabledialog.h"
# include "cookiesdialog.h"
# include "downloadfromurldialog.h"
# include "executionlogwidget.h"
# include "guiiconprovider.h"
# include "hidabletabwidget.h"
# include "lineedit.h"
# include "optionsdialog.h"
# include "peerlistwidget.h"
# include "powermanagement.h"
# include "propertieswidget.h"
# include "rss/rsswidget.h"
# include "search/searchwidget.h"
# include "speedlimitdialog.h"
# include "statsdialog.h"
# include "statusbar.h"
# include "torrentcreatordialog.h"
# include "transferlistmodel.h"
# include "trackerlistwidget.h"
# include "transferlistfilterswidget.h"
# include "transferlistwidget.h"
# include "ui_mainwindow.h"
# include "utils.h"
# ifdef Q_OS_WIN
# include "base/net/downloadhandler.h"
# include "base/net/downloadmanager.h"
# endif
# ifdef Q_OS_MAC
# include "macutilities.h"
# endif
# if defined(Q_OS_WIN) || defined(Q_OS_MAC)
# include "programupdater.h"
# endif
# if LIBTORRENT_VERSION_NUM < 10100
# include "trackerlogindialog.h"
# endif
# ifdef Q_OS_MAC
void qt_mac_set_dock_menu ( QMenu * menu ) ;
# endif
# define TIME_TRAY_BALLOON 5000
# define PREVENT_SUSPEND_INTERVAL 60000
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 = NOTIFICATIONS_SETTINGS_KEY ( " DownloadTrackerFavicon " ) ;
// just a shortcut
inline SettingsStorage * settings ( )
{
return SettingsStorage : : instance ( ) ;
}
}
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_MAC)
, 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_MAC
# ifdef Q_OS_UNIX
const QIcon appLogo = Preferences : : instance ( ) - > useSystemIconTheme ( )
? QIcon : : fromTheme ( " qbittorrent " , QIcon ( " :/icons/skin/qbittorrent-tray.svg " ) )
: QIcon ( " :/icons/skin/qbittorrent-tray.svg " ) ;
# else
const QIcon appLogo ( " :/icons/skin/qbittorrent-tray.svg " ) ;
# endif // Q_OS_UNIX
setWindowIcon ( appLogo ) ;
# endif // Q_OS_MAC
# if (defined(Q_OS_UNIX))
m_ui - > actionOptions - > setText ( tr ( " Preferences " ) ) ;
# endif
addToolbarContextMenu ( ) ;
m_ui - > actionOpen - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " list-add " ) ) ;
m_ui - > actionDownloadFromURL - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " insert-link " ) ) ;
m_ui - > actionSetUploadLimit - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " kt-set-max-upload-speed " ) ) ;
m_ui - > actionSetDownloadLimit - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " kt-set-max-download-speed " ) ) ;
m_ui - > actionSetGlobalUploadLimit - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " kt-set-max-upload-speed " ) ) ;
m_ui - > actionSetGlobalDownloadLimit - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " kt-set-max-download-speed " ) ) ;
m_ui - > actionCreateTorrent - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " document-edit " ) ) ;
m_ui - > actionAbout - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " help-about " ) ) ;
m_ui - > actionStatistics - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " view-statistics " ) ) ;
m_ui - > actionDecreasePriority - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " go-down " ) ) ;
m_ui - > actionBottomPriority - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " go-bottom " ) ) ;
m_ui - > actionDelete - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " list-remove " ) ) ;
m_ui - > actionDocumentation - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " help-contents " ) ) ;
m_ui - > actionDonateMoney - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " wallet-open " ) ) ;
m_ui - > actionExit - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " application-exit " ) ) ;
m_ui - > actionIncreasePriority - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " go-up " ) ) ;
m_ui - > actionTopPriority - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " go-top " ) ) ;
m_ui - > actionLock - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " object-locked " ) ) ;
m_ui - > actionOptions - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " configure " , " preferences-system " ) ) ;
m_ui - > actionPause - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " media-playback-pause " ) ) ;
m_ui - > actionPauseAll - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " media-playback-pause " ) ) ;
m_ui - > actionStart - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " media-playback-start " ) ) ;
m_ui - > actionStartAll - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " media-playback-start " ) ) ;
m_ui - > menuAutoShutdownOnDownloadsCompletion - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " application-exit " ) ) ;
m_ui - > actionManageCookies - > setIcon ( GuiIconProvider : : instance ( ) - > getIcon ( " preferences-web-browser-cookies " ) ) ;
QMenu * 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 : : addTorrentFailed , this , & MainWindow : : addTorrentFailed ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : torrentNew , this , & MainWindow : : torrentNew ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : torrentFinished , this , & MainWindow : : finishedTorrent ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : trackerAuthenticationRequired , this , & MainWindow : : trackerAuthenticationRequired ) ;
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);
QSplitter * hSplitter = new QSplitter ( Qt : : Vertical , this ) ;
hSplitter - > setChildrenCollapsible ( false ) ;
hSplitter - > setFrameShape ( QFrame : : NoFrame ) ;
// Name filter
m_searchFilter = new LineEdit ( this ) ;
m_searchFilterAction = m_ui - > toolBar - > insertWidget ( m_ui - > actionLock , m_searchFilter ) ;
m_searchFilter - > setPlaceholderText ( tr ( " Filter torrent list... " ) ) ;
m_searchFilter - > setFixedWidth ( Utils : : Gui : : scaledSize ( this , 200 ) ) ;
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 ) ;
// transferList->setStyleSheet("QTreeView {border: none;}"); // borderless
m_propertiesWidget = new PropertiesWidget ( hSplitter , this , m_transferListWidget ) ;
m_transferListFiltersWidget = new TransferListFiltersWidget ( m_splitter , m_transferListWidget ) ;
m_transferListFiltersWidget - > setDownloadTrackerFavicon ( 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_MAC
GuiIconProvider : : 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 ) ;
using Func = void ( TransferListFiltersWidget : : * ) ( BitTorrent : : TorrentHandle * const , const QString & ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : trackerSuccess , m_transferListFiltersWidget , static_cast < Func > ( & TransferListFiltersWidget : : trackerSuccess ) ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : trackerError , m_transferListFiltersWidget , static_cast < Func > ( & TransferListFiltersWidget : : trackerError ) ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : trackerWarning , m_transferListFiltersWidget , static_cast < Func > ( & TransferListFiltersWidget : : trackerWarning ) ) ;
# ifdef Q_OS_MAC
// Increase top spacing to avoid tab overlapping
m_ui - > centralWidgetLayout - > addSpacing ( 8 ) ;
# endif
m_ui - > centralWidgetLayout - > addWidget ( m_tabs ) ;
m_prioSeparator = m_ui - > toolBar - > insertSeparator ( m_ui - > actionTopPriority ) ;
m_prioSeparatorMenu = m_ui - > menuEdit - > insertSeparator ( m_ui - > actionTopPriority ) ;
# ifdef Q_OS_MAC
foreach ( QAction * action , 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
// 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 - > actionTopPriority , & QAction : : triggered , m_transferListWidget , & TransferListWidget : : topPrioSelectedTorrents ) ;
connect ( m_ui - > actionIncreasePriority , & QAction : : triggered , m_transferListWidget , & TransferListWidget : : increasePrioSelectedTorrents ) ;
connect ( m_ui - > actionDecreasePriority , & QAction : : triggered , m_transferListWidget , & TransferListWidget : : decreasePrioSelectedTorrents ) ;
connect ( m_ui - > actionBottomPriority , & QAction : : triggered , m_transferListWidget , & TransferListWidget : : bottomPrioSelectedTorrents ) ;
# ifndef Q_OS_MAC
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_MAC)
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 : : checkForActiveTorrents ) ;
// Configure BT session according to options
loadPreferences ( false ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : torrentsUpdated , this , & MainWindow : : updateGUI ) ;
// Accept drag 'n drops
setAcceptDrops ( true ) ;
createKeyboardShortcuts ( ) ;
# ifdef Q_OS_MAC
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
QActionGroup * 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_MAC)) || 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_MAC
if ( m_systrayIcon ) {
if ( ! ( pref - > startMinimized ( ) | | m_uiLocked ) ) {
show ( ) ;
activateWindow ( ) ;
raise ( ) ;
}
else if ( pref - > startMinimized ( ) ) {
showMinimized ( ) ;
if ( pref - > minimizeToTray ( ) )
hide ( ) ;
}
}
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_MAC
}
# 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_MAC
setupDockClickHandler ( ) ;
qt_mac_set_dock_menu ( trayIconMenu ( ) ) ;
# endif
}
MainWindow : : ~ MainWindow ( )
{
delete m_ui ;
}
bool MainWindow : : isExecutionLogEnabled ( ) const
{
return settings ( ) - > loadValue ( KEY_EXECUTIONLOG_ENABLED , false ) . toBool ( ) ;
}
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 ) . toInt ( ) ;
}
void MainWindow : : setExecutionLogMsgTypes ( const int value )
{
m_executionLog - > showMsgTypes ( static_cast < Log : : MsgTypes > ( value ) ) ;
settings ( ) - > storeValue ( KEY_EXECUTIONLOG_TYPES , value ) ;
}
bool MainWindow : : isNotificationsEnabled ( ) const
{
return settings ( ) - > loadValue ( KEY_NOTIFICATIONS_ENABLED , true ) . toBool ( ) ;
}
void MainWindow : : setNotificationsEnabled ( bool value )
{
settings ( ) - > storeValue ( KEY_NOTIFICATIONS_ENABLED , value ) ;
}
bool MainWindow : : isTorrentAddedNotificationsEnabled ( ) const
{
return settings ( ) - > loadValue ( KEY_NOTIFICATIONS_TORRENTADDED , false ) . toBool ( ) ;
}
void MainWindow : : setTorrentAddedNotificationsEnabled ( bool value )
{
settings ( ) - > storeValue ( KEY_NOTIFICATIONS_TORRENTADDED , value ) ;
}
bool MainWindow : : isDownloadTrackerFavicon ( ) const
{
return settings ( ) - > loadValue ( KEY_DOWNLOAD_TRACKER_FAVICON , true ) . toBool ( ) ;
}
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 = new QAction ( tr ( " Icons Only " ) , m_toolbarMenu ) ;
connect ( iconsOnly , & QAction : : triggered , this , & MainWindow : : toolbarIconsOnly ) ;
QAction * textOnly = new QAction ( tr ( " Text Only " ) , m_toolbarMenu ) ;
connect ( textOnly , & QAction : : triggered , this , & MainWindow : : toolbarTextOnly ) ;
QAction * textBesideIcons = new QAction ( tr ( " Text Alongside Icons " ) , m_toolbarMenu ) ;
connect ( textBesideIcons , & QAction : : triggered , this , & MainWindow : : toolbarTextBeside ) ;
QAction * textUnderIcons = new QAction ( tr ( " Text Under Icons " ) , m_toolbarMenu ) ;
connect ( textUnderIcons , & QAction : : triggered , this , & MainWindow : : toolbarTextUnder ) ;
QAction * followSystemStyle = new QAction ( tr ( " Follow System Style " ) , m_toolbarMenu ) ;
connect ( followSystemStyle , & QAction : : triggered , this , & MainWindow : : toolbarFollowSystem ) ;
m_toolbarMenu - > addAction ( iconsOnly ) ;
m_toolbarMenu - > addAction ( textOnly ) ;
m_toolbarMenu - > addAction ( textBesideIcons ) ;
m_toolbarMenu - > addAction ( textUnderIcons ) ;
m_toolbarMenu - > addAction ( followSystemStyle ) ;
QActionGroup * 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 Qt : : ToolButtonStyle 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 ( )
{
CookiesDialog ( this ) . exec ( ) ;
}
void MainWindow : : toolbarMenuRequested ( QPoint point )
{
m_toolbarMenu - > exec ( 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 ) ;
}
void MainWindow : : defineUILockPassword ( )
{
QString oldPassMd5 = Preferences : : instance ( ) - > getUILockPasswordMD5 ( ) ;
if ( oldPassMd5 . isNull ( ) )
oldPassMd5 = " " ;
bool ok = false ;
QString newClearPassword = AutoExpandableDialog : : getText ( this , tr ( " UI lock password " ) , tr ( " Please type the UI lock password: " ) , QLineEdit : : Password , oldPassMd5 , & ok ) ;
if ( ok ) {
newClearPassword = newClearPassword . trimmed ( ) ;
if ( newClearPassword . size ( ) < 3 ) {
QMessageBox : : warning ( this , tr ( " Invalid password " ) , tr ( " The password should contain at least 3 characters " ) ) ;
}
else {
if ( newClearPassword ! = oldPassMd5 )
Preferences : : instance ( ) - > setUILockPassword ( newClearPassword ) ;
QMessageBox : : information ( this , tr ( " Password update " ) , tr ( " The UI lock password has been successfully updated " ) ) ;
}
}
}
void MainWindow : : clearUILockPassword ( )
{
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 ( ) - > clearUILockPassword ( ) ;
}
void MainWindow : : on_actionLock_triggered ( )
{
Preferences * const pref = Preferences : : instance ( ) ;
// Check if there is a password
if ( pref - > getUILockPasswordMD5 ( ) . isEmpty ( ) ) {
// Ask for a password
bool ok = false ;
QString clearPassword = AutoExpandableDialog : : getText ( this , tr ( " UI lock password " ) , tr ( " Please type the UI lock password: " ) , QLineEdit : : Password , " " , & ok ) ;
if ( ! ok ) return ;
pref - > setUILockPassword ( clearPassword ) ;
}
// 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_MAC
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 , GuiIconProvider : : instance ( ) - > getIcon ( " application-rss+xml " ) ) ;
# endif
}
}
else if ( m_rssWidget ) {
delete m_rssWidget ;
}
}
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_MAC
GuiIconProvider : : instance ( ) - > getIcon ( " edit-find " ) ,
# endif
tr ( " Search " ) ) ;
}
}
else if ( m_searchWidget ) {
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 ;
}
else {
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_MAC
if ( m_systrayCreator )
m_systrayCreator - > stop ( ) ;
# endif
if ( m_preventTimer )
m_preventTimer - > stop ( ) ;
# if (defined(Q_OS_WIN) || defined(Q_OS_MAC))
m_programUpdateTimer - > stop ( ) ;
# endif
delete m_searchFilterAction ;
// remove all child widgets
while ( QWidget * 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 : : TorrentHandle * 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 : : TorrentHandle * 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 : : TorrentHandle * const torrent , 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_MAC
m_ui - > actionCloseWindow - > setShortcut ( QKeySequence : : Close ) ;
# else
m_ui - > actionCloseWindow - > setVisible ( false ) ;
# endif
QShortcut * switchTransferShortcut = new QShortcut ( Qt : : ALT + Qt : : Key_1 , this ) ;
connect ( switchTransferShortcut , & QShortcut : : activated , this , & MainWindow : : displayTransferTab ) ;
using Func = void ( MainWindow : : * ) ( ) ;
QShortcut * switchSearchShortcut = new QShortcut ( Qt : : ALT + Qt : : Key_2 , this ) ;
connect ( switchSearchShortcut , & QShortcut : : activated , this , static_cast < Func > ( & MainWindow : : displaySearchTab ) ) ;
QShortcut * switchRSSShortcut = new QShortcut ( Qt : : ALT + Qt : : Key_3 , this ) ;
connect ( switchRSSShortcut , & QShortcut : : activated , this , static_cast < Func > ( & MainWindow : : displayRSSTab ) ) ;
QShortcut * switchExecutionLogShortcut = new QShortcut ( Qt : : ALT + Qt : : Key_4 , this ) ;
connect ( switchExecutionLogShortcut , & QShortcut : : activated , this , & MainWindow : : displayExecutionLogTab ) ;
QShortcut * switchSearchFilterShortcut = new QShortcut ( QKeySequence : : Find , this ) ;
connect ( switchSearchFilterShortcut , & QShortcut : : activated , this , & MainWindow : : focusSearchFilter ) ;
m_ui - > actionDocumentation - > setShortcut ( QKeySequence : : HelpContents ) ;
m_ui - > actionOptions - > setShortcut ( Qt : : ALT + Qt : : Key_O ) ;
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 - > actionBottomPriority - > setShortcut ( Qt : : CTRL + Qt : : SHIFT + Qt : : Key_Minus ) ;
m_ui - > actionDecreasePriority - > setShortcut ( Qt : : CTRL + Qt : : Key_Minus ) ;
m_ui - > actionIncreasePriority - > setShortcut ( Qt : : CTRL + Qt : : Key_Plus ) ;
m_ui - > actionTopPriority - > setShortcut ( Qt : : CTRL + Qt : : SHIFT + Qt : : Key_Plus ) ;
# ifdef Q_OS_MAC
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 : : TorrentHandle * 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 ( QString url , 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_actionSetGlobalUploadLimit_triggered ( )
{
qDebug ( ) < < Q_FUNC_INFO ;
BitTorrent : : Session * const session = BitTorrent : : Session : : instance ( ) ;
bool ok = false ;
const long newLimit = SpeedLimitDialog : : askSpeedLimit (
this , & ok , tr ( " Global Upload Speed Limit " ) , session - > uploadSpeedLimit ( ) ) ;
if ( ok ) {
qDebug ( " Setting global upload rate limit to %.1fKb/s " , newLimit / 1024. ) ;
session - > setUploadSpeedLimit ( newLimit ) ;
}
}
void MainWindow : : on_actionSetGlobalDownloadLimit_triggered ( )
{
qDebug ( ) < < Q_FUNC_INFO ;
BitTorrent : : Session * const session = BitTorrent : : Session : : instance ( ) ;
bool ok = false ;
const long newLimit = SpeedLimitDialog : : askSpeedLimit (
this , & ok , tr ( " Global Download Speed Limit " ) , session - > downloadSpeedLimit ( ) ) ;
if ( ok ) {
qDebug ( " Setting global download rate limit to %.1fKb/s " , newLimit / 1024. ) ;
session - > setDownloadSpeedLimit ( newLimit ) ;
}
}
// 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_MAC
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 0 ;
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 ;
else
m_unlockDlgShowing = true ;
bool ok = false ;
QString clearPassword = AutoExpandableDialog : : getText ( this , tr ( " UI lock password " ) , tr ( " Please type the UI lock password: " ) , QLineEdit : : Password , " " , & ok ) ;
m_unlockDlgShowing = false ;
if ( ! ok ) return false ;
Preferences * const pref = Preferences : : instance ( ) ;
QString realPassMd5 = pref - > getUILockPasswordMD5 ( ) ;
QCryptographicHash md5 ( QCryptographicHash : : Md5 ) ;
md5 . addData ( clearPassword . toLocal8Bit ( ) ) ;
QString passwordMd5 = md5 . result ( ) . toHex ( ) ;
if ( realPassMd5 = = passwordMd5 ) {
m_uiLocked = false ;
pref - > setUILocked ( false ) ;
m_trayIconMenu - > setEnabled ( true ) ;
return true ;
}
QMessageBox : : warning ( this , tr ( " Invalid password " ) , tr ( " The password is invalid " ) ) ;
return false ;
}
void MainWindow : : notifyOfUpdate ( 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_MAC
// 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
// 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 : : Misc : : screenCenter ( this ) ) ;
m_posInitialized = true ;
}
}
// Called when we close the program
void MainWindow : : closeEvent ( QCloseEvent * e )
{
Preferences * const pref = Preferences : : instance ( ) ;
# ifdef Q_OS_MAC
if ( ! m_forceExit ) {
hide ( ) ;
e - > accept ( ) ;
return ;
}
# else
const bool goToSystrayOnExit = pref - > closeToTray ( ) ;
if ( ! m_forceExit & & m_systrayIcon & & goToSystrayOnExit & & ! this - > isHidden ( ) ) {
hide ( ) ;
e - > accept ( ) ;
return ;
}
# endif // Q_OS_MAC
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
if ( m_searchWidget )
delete m_searchWidget ;
hide ( ) ;
# ifndef Q_OS_MAC
// 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_MAC
switch ( e - > type ( ) ) {
case QEvent : : WindowStateChange : {
qDebug ( " Window change event " ) ;
// Now check to see if the window is minimised
if ( isMinimized ( ) ) {
qDebug ( " minimisation " ) ;
if ( m_systrayIcon & & Preferences : : instance ( ) - > minimizeToTray ( ) ) {
qDebug ( ) < < " Has active window: " < < ( qApp - > activeWindow ( ) ! = nullptr ) ;
// Check if there is a modal window
bool hasModalWindow = false ;
foreach ( QWidget * widget , 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 ) ;
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
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 ( ) ) {
foreach ( const QUrl & url , 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 ;
foreach ( const QString & file , files ) {
const bool isTorrentLink = ( file . startsWith ( " magnet: " , Qt : : CaseInsensitive )
| | file . endsWith ( C_TORRENT_FILE_EXTENSION , Qt : : CaseInsensitive )
| | Utils : : Misc : : isUrl ( file ) ) ;
if ( isTorrentLink )
torrentFiles < < file ;
else
otherFiles < < file ;
}
// Download torrents
const bool useTorrentAdditionDialog = AddNewTorrentDialog : : isEnabled ( ) ;
foreach ( const QString & file , torrentFiles ) {
if ( useTorrentAdditionDialog )
AddNewTorrentDialog : : show ( file , this ) ;
else
BitTorrent : : Session : : instance ( ) - > addTorrent ( file ) ;
}
if ( ! torrentFiles . isEmpty ( ) ) return ;
// Create torrent
foreach ( const QString & file , otherFiles ) {
createTorrentTriggered ( file ) ;
// currently only hande 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 )
{
foreach ( const QString & mime , 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_MAC
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
/*****************************************************
* *
* 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 + ' ) ' ) ;
const bool useTorrentAdditionDialog = AddNewTorrentDialog : : isEnabled ( ) ;
if ( ! pathsList . isEmpty ( ) ) {
foreach ( QString file , pathsList ) {
qDebug ( " Dropped file %s on download list " , qUtf8Printable ( file ) ) ;
if ( useTorrentAdditionDialog )
AddNewTorrentDialog : : show ( file , this ) ;
else
BitTorrent : : Session : : instance ( ) - > addTorrent ( file ) ;
}
// Save last dir to remember it
QStringList topDir = Utils : : Fs : : fromNativePath ( pathsList . at ( 0 ) ) . split ( ' / ' ) ;
topDir . removeLast ( ) ;
pref - > setMainLastDir ( Utils : : Fs : : fromNativePath ( topDir . join ( ' / ' ) ) ) ;
}
}
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 ( bool configureSession )
{
Logger : : instance ( ) - > addMessage ( tr ( " Options were saved successfully. " ) ) ;
const Preferences * const pref = Preferences : : instance ( ) ;
# ifdef Q_OS_MAC
Q_UNUSED ( configureSession ) ;
# else
const bool newSystrayIntegration = pref - > systrayIntegration ( ) ;
m_ui - > actionLock - > setVisible ( newSystrayIntegration ) ;
if ( newSystrayIntegration ! = ( m_systrayIcon ! = 0 ) ) {
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 - > preventFromSuspend ( ) & & ! m_preventTimer - > isActive ( ) ) {
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 - > actionDecreasePriority - > isVisible ( ) ) {
m_transferListWidget - > hidePriorityColumn ( false ) ;
m_ui - > actionDecreasePriority - > setVisible ( true ) ;
m_ui - > actionIncreasePriority - > setVisible ( true ) ;
m_ui - > actionTopPriority - > setVisible ( true ) ;
m_ui - > actionBottomPriority - > setVisible ( true ) ;
# ifndef Q_OS_MAC
m_prioSeparator - > setVisible ( true ) ;
# endif
m_prioSeparatorMenu - > setVisible ( true ) ;
}
}
else {
if ( m_ui - > actionDecreasePriority - > isVisible ( ) ) {
m_transferListWidget - > hidePriorityColumn ( true ) ;
m_ui - > actionDecreasePriority - > setVisible ( false ) ;
m_ui - > actionIncreasePriority - > setVisible ( false ) ;
m_ui - > actionTopPriority - > setVisible ( false ) ;
m_ui - > actionBottomPriority - > setVisible ( false ) ;
# ifndef Q_OS_MAC
m_prioSeparator - > setVisible ( false ) ;
# endif
m_prioSeparatorMenu - > setVisible ( false ) ;
}
}
// Torrent properties
m_propertiesWidget - > reloadPreferences ( ) ;
# if defined(Q_OS_WIN) || defined(Q_OS_MAC)
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 : : addUnauthenticatedTracker ( const QPair < BitTorrent : : TorrentHandle * , QString > & tracker )
{
// Trackers whose authentication was cancelled
if ( m_unauthenticatedTrackers . indexOf ( tracker ) < 0 )
m_unauthenticatedTrackers < < tracker ;
}
// Called when a tracker requires authentication
void MainWindow : : trackerAuthenticationRequired ( BitTorrent : : TorrentHandle * const torrent )
{
# if LIBTORRENT_VERSION_NUM < 10100
if ( m_unauthenticatedTrackers . indexOf ( qMakePair ( torrent , torrent - > currentTracker ( ) ) ) < 0 )
// Tracker login
new TrackerLoginDialog ( this , torrent ) ;
# else
Q_UNUSED ( torrent ) ;
# endif
}
// Check connection status and display right icon
void MainWindow : : updateGUI ( )
{
const BitTorrent : : SessionStatus & status = BitTorrent : : Session : : instance ( ) - > status ( ) ;
// update global information
# ifndef Q_OS_MAC
if ( m_systrayIcon ) {
# ifdef Q_OS_UNIX
QString html = " <div style='background-color: #678db2; color: #fff;height: 18px; font-weight: bold; margin-bottom: 5px;'> " ;
html + = " qBittorrent " ;
html + = " </div> " ;
html + = " <div style='vertical-align: baseline; height: 18px;'> " ;
html + = " <img src=':/icons/skin/download.png' height='14'/> " + tr ( " DL speed: %1 " , " e.g: Download speed: 10 KiB/s " ) . arg ( Utils : : Misc : : friendlyUnit ( status . payloadDownloadRate , true ) ) ;
html + = " </div> " ;
html + = " <div style='vertical-align: baseline; height: 18px;'> " ;
html + = " <img src=':/icons/skin/seeding.png' height='14'/> " + tr ( " UP speed: %1 " , " e.g: Upload speed: 10 KiB/s " ) . arg ( Utils : : Misc : : friendlyUnit ( status . payloadUploadRate , true ) ) ;
html + = " </div> " ;
# else
// OSes such as Windows do not support html here
QString html = tr ( " DL speed: %1 " , " e.g: Download speed: 10 KiB/s " ) . arg ( Utils : : Misc : : friendlyUnit ( status . payloadDownloadRate , true ) ) ;
html + = ' \n ' ;
html + = tr ( " UP speed: %1 " , " e.g: Upload speed: 10 KiB/s " ) . arg ( Utils : : Misc : : friendlyUnit ( status . payloadUploadRate , true ) ) ;
# endif // Q_OS_UNIX
m_systrayIcon - > setToolTip ( html ) ; // tray icon
}
# else
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 ( " " ) ;
# endif // Q_OS_MAC
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 : : showNotificationBaloon ( QString title , QString msg ) const
{
if ( ! isNotificationsEnabled ( ) ) return ;
# if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && 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_MAC)
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 ( ) ;
foreach ( QString url , urlList ) {
if ( ( ( url . size ( ) = = 40 ) & & ! url . contains ( QRegularExpression ( " [^0-9A-Fa-f] " ) ) )
| | ( ( url . size ( ) = = 32 ) & & ! url . contains ( QRegularExpression ( " [^2-7A-Za-z] " ) ) ) )
url = " magnet:?xt=urn:btih: " + url ;
if ( useTorrentAdditionDialog )
AddNewTorrentDialog : : show ( url , this ) ;
else
BitTorrent : : Session : : instance ( ) - > addTorrent ( url ) ;
}
}
/*****************************************************
* *
* Options *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifndef Q_OS_MAC
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_MAC
QMenu * MainWindow : : trayIconMenu ( )
{
if ( m_trayIconMenu ) return m_trayIconMenu ;
m_trayIconMenu = new QMenu ( this ) ;
# ifndef Q_OS_MAC
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 - > actionSetGlobalDownloadLimit ) ;
m_trayIconMenu - > addAction ( m_ui - > actionSetGlobalUploadLimit ) ;
m_trayIconMenu - > addSeparator ( ) ;
m_trayIconMenu - > addAction ( m_ui - > actionStartAll ) ;
m_trayIconMenu - > addAction ( m_ui - > actionPauseAll ) ;
# ifndef Q_OS_MAC
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 )
updateGUI ( ) ;
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 ( ) ) {
int majorVersion = Utils : : ForeignApps : : pythonInfo ( ) . version . majorNumber ( ) ;
// Check if python is already in PATH
if ( majorVersion > 0 ) {
// Prevent translators from messing with PATH
Logger : : instance ( ) - > addMessage ( tr ( " Python found in %1: %2 " , " Python found in PATH: /usr/local/bin:/usr/bin:/etc/bin " )
. arg ( " PATH " , qgetenv ( " PATH " ) . constData ( ) ) , Log : : INFO ) ;
}
# ifdef Q_OS_WIN
else if ( addPythonPathToEnv ( ) ) {
majorVersion = Utils : : ForeignApps : : pythonInfo ( ) . version . majorNumber ( ) ;
}
# endif
else {
QMessageBox : : information ( this , tr ( " Undetermined Python version " ) , tr ( " Couldn't determine your Python version. Search engine disabled. " ) ) ;
m_ui - > actionSearchWidget - > setChecked ( false ) ;
Preferences : : instance ( ) - > setSearchEnabled ( false ) ;
return ;
}
bool res = false ;
if ( ( majorVersion = = 2 ) | | ( majorVersion = = 3 ) ) {
// Check Python minimum requirement: 2.7.9 / 3.3.0
using Version = Utils : : ForeignApps : : PythonInfo : : Version ;
const Version pyVersion = Utils : : ForeignApps : : pythonInfo ( ) . version ;
if ( ( ( majorVersion = = 2 ) & & ( pyVersion < Version { 2 , 7 , 9 } ) )
| | ( ( majorVersion = = 3 ) & & ( pyVersion < Version { 3 , 3 , 0 } ) ) ) {
QMessageBox : : information ( this , tr ( " Old Python Interpreter " ) , tr ( " Your Python version (%1) is outdated. Please upgrade to latest version for search engines to work. \n Minimum requirement: 2.7.9 / 3.3.0. " ) . arg ( pyVersion ) ) ;
m_ui - > actionSearchWidget - > setChecked ( false ) ;
Preferences : : instance ( ) - > setSearchEnabled ( false ) ;
return ;
}
res = true ;
}
if ( res ) {
m_hasPython = true ;
}
# ifdef Q_OS_WIN
else if ( QMessageBox : : question ( this , tr ( " Missing Python Interpreter " ) ,
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 ) = = QMessageBox : : Yes ) {
// Download and Install Python
installPython ( ) ;
m_ui - > actionSearchWidget - > setChecked ( false ) ;
Preferences : : instance ( ) - > setSearchEnabled ( false ) ;
return ;
}
# endif
else {
# ifndef Q_OS_WIN
QMessageBox : : information ( this , tr ( " Missing Python Interpreter " ) , tr ( " Python is required to use the search engine but it does not seem to be installed. " ) ) ;
# endif
m_ui - > actionSearchWidget - > setChecked ( false ) ;
Preferences : : instance ( ) - > setSearchEnabled ( false ) ;
return ;
}
}
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_MAC)
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 ( " <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 ( m_tabs , static_cast < Log : : MsgType > ( executionLogMsgTypes ( ) ) ) ;
# ifdef Q_OS_MAC
m_tabs - > addTab ( m_executionLog , tr ( " Execution Log " ) ) ;
# else
const int indexTab = m_tabs - > addTab ( m_executionLog , tr ( " Execution Log " ) ) ;
m_tabs - > setTabIcon ( indexTab , GuiIconProvider : : instance ( ) - > getIcon ( " view-calendar-journal " ) ) ;
# endif
}
else if ( m_executionLog ) {
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 : : checkForActiveTorrents ( )
{
m_pwr - > setActivityState ( BitTorrent : : Session : : instance ( ) - > hasActiveTorrents ( ) ) ;
}
# ifndef Q_OS_MAC
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_MAC))
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 QIcon ( QLatin1String ( " :/icons/skin/qbittorrent-tray.svg " ) ) ;
case TrayIcon : : MONO_DARK :
return QIcon ( QLatin1String ( " :/icons/skin/qbittorrent-tray-dark.svg " ) ) ;
case TrayIcon : : MONO_LIGHT :
return QIcon ( QLatin1String ( " :/icons/skin/qbittorrent-tray-light.svg " ) ) ;
default :
break ;
}
# endif
// As a failsafe in case the enum is invalid
return QIcon ( QLatin1String ( " :/icons/skin/qbittorrent-tray.svg " ) ) ;
}
# endif // Q_OS_MAC
# if defined(Q_OS_WIN) || defined(Q_OS_MAC)
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
bool MainWindow : : addPythonPathToEnv ( )
{
if ( m_hasPython ) return true ;
QString pythonPath = Preferences : : getPythonPath ( ) ;
if ( ! pythonPath . isEmpty ( ) ) {
Logger : : instance ( ) - > addMessage ( tr ( " Python found in '%1' " ) . arg ( Utils : : Fs : : toNativePath ( pythonPath ) ) , Log : : INFO ) ;
// Add it to PATH envvar
QString pathEnvar = QString : : fromLocal8Bit ( qgetenv ( " PATH " ) . constData ( ) ) ;
if ( pathEnvar . isNull ( ) )
pathEnvar = " " ;
pathEnvar = pythonPath + ' ; ' + pathEnvar ;
qDebug ( " New PATH envvar is: %s " , qUtf8Printable ( pathEnvar ) ) ;
qputenv ( " PATH " , Utils : : Fs : : toNativePath ( pathEnvar ) . toLocal8Bit ( ) ) ;
return true ;
}
return false ;
}
void MainWindow : : installPython ( )
{
setCursor ( QCursor ( Qt : : WaitCursor ) ) ;
// Download python
const QString installerURL = ( ( QSysInfo : : windowsVersion ( ) > = QSysInfo : : WV_VISTA )
? " https://www.python.org/ftp/python/3.5.2/python-3.5.2.exe "
: " https://www.python.org/ftp/python/3.4.4/python-3.4.4.msi " ) ;
Net : : DownloadHandler * handler = Net : : DownloadManager : : instance ( ) - > download (
Net : : DownloadRequest ( installerURL ) . saveToFile ( true ) ) ;
using Func = void ( Net : : DownloadHandler : : * ) ( const QString & , const QString & ) ;
connect ( handler , static_cast < Func > ( & Net : : DownloadHandler : : downloadFinished ) , this , & MainWindow : : pythonDownloadSuccess ) ;
connect ( handler , static_cast < Func > ( & Net : : DownloadHandler : : downloadFailed ) , this , & MainWindow : : pythonDownloadFailure ) ;
}
void MainWindow : : pythonDownloadSuccess ( const QString & url , const QString & filePath )
{
Q_UNUSED ( url )
setCursor ( QCursor ( Qt : : ArrowCursor ) ) ;
QProcess installer ;
qDebug ( " Launching Python installer in passive mode... " ) ;
if ( QSysInfo : : windowsVersion ( ) > = QSysInfo : : WV_VISTA ) {
QFile : : rename ( filePath , filePath + " .exe " ) ;
installer . start ( ' " ' + Utils : : Fs : : toNativePath ( filePath ) + " .exe \" /passive " ) ;
}
else {
QFile : : rename ( filePath , filePath + " .msi " ) ;
installer . start ( Utils : : Misc : : windowsSystemPath ( ) + " \\ msiexec.exe /passive /i \" " + Utils : : Fs : : toNativePath ( filePath ) + " .msi \" " ) ;
}
// 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
if ( QSysInfo : : windowsVersion ( ) > = QSysInfo : : WV_VISTA )
Utils : : Fs : : forceRemove ( filePath + " .exe " ) ;
else
Utils : : Fs : : forceRemove ( filePath + " .msi " ) ;
// Reload search engine
m_hasPython = addPythonPathToEnv ( ) ;
if ( m_hasPython ) {
// Make it print the version to Log
Utils : : ForeignApps : : pythonInfo ( ) ;
m_ui - > actionSearchWidget - > setChecked ( true ) ;
displaySearchTab ( true ) ;
}
}
void MainWindow : : pythonDownloadFailure ( const QString & url , const QString & error )
{
Q_UNUSED ( url )
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 ( error ) ) ;
}
# endif // Q_OS_WIN