Display External IP Address in status bar
[qBittorrent.git] / src / gui / optionsdialog.cpp
bloba96b353a6191795189dacb97a829b81a7fa5e25d
1 /*
2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2023-2024 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2024 Jonathan Ketchker
5 * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * In addition, as a special exception, the copyright holders give permission to
22 * link this program with the OpenSSL project's "OpenSSL" library (or with
23 * modified versions of it that use the same license as the "OpenSSL" library),
24 * and distribute the linked executables. You must obey the GNU General Public
25 * License in all respects for all of the code used other than "OpenSSL". If you
26 * modify file(s), you may extend this exception to your version of the file(s),
27 * but you are not obligated to do so. If you do not wish to do so, delete this
28 * exception statement from your version.
31 #include "optionsdialog.h"
33 #include <algorithm>
34 #include <chrono>
35 #include <cstdlib>
36 #include <limits>
38 #include <QApplication>
39 #include <QDebug>
40 #include <QDesktopServices>
41 #include <QDialogButtonBox>
42 #include <QEvent>
43 #include <QFileDialog>
44 #include <QMessageBox>
45 #include <QSystemTrayIcon>
46 #include <QTranslator>
48 #ifdef Q_OS_WIN
49 #include <QStyleFactory>
50 #endif
52 #include "base/bittorrent/session.h"
53 #include "base/bittorrent/sharelimitaction.h"
54 #include "base/exceptions.h"
55 #include "base/global.h"
56 #include "base/net/portforwarder.h"
57 #include "base/net/proxyconfigurationmanager.h"
58 #include "base/path.h"
59 #include "base/preferences.h"
60 #include "base/rss/rss_autodownloader.h"
61 #include "base/rss/rss_session.h"
62 #include "base/torrentfileguard.h"
63 #include "base/torrentfileswatcher.h"
64 #include "base/utils/compare.h"
65 #include "base/utils/io.h"
66 #include "base/utils/misc.h"
67 #include "base/utils/net.h"
68 #include "base/utils/os.h"
69 #include "base/utils/password.h"
70 #include "base/utils/random.h"
71 #include "base/utils/sslkey.h"
72 #include "addnewtorrentdialog.h"
73 #include "advancedsettings.h"
74 #include "banlistoptionsdialog.h"
75 #include "interfaces/iguiapplication.h"
76 #include "ipsubnetwhitelistoptionsdialog.h"
77 #include "rss/automatedrssdownloader.h"
78 #include "ui_optionsdialog.h"
79 #include "uithemedialog.h"
80 #include "uithememanager.h"
81 #include "utils.h"
82 #include "watchedfolderoptionsdialog.h"
83 #include "watchedfoldersmodel.h"
84 #include "webui/webui.h"
86 #ifndef DISABLE_WEBUI
87 #include "base/net/dnsupdater.h"
88 #endif
90 #if defined Q_OS_MACOS || defined Q_OS_WIN
91 #include "base/utils/os.h"
92 #endif // defined Q_OS_MACOS || defined Q_OS_WIN
94 #define SETTINGS_KEY(name) u"OptionsDialog/" name
96 const int WEBUI_MIN_USERNAME_LENGTH = 3;
97 const int WEBUI_MIN_PASSWORD_LENGTH = 6;
99 namespace
101 QStringList translatedWeekdayNames()
103 // return translated strings from Monday to Sunday in user selected locale
105 const QLocale locale {Preferences::instance()->getLocale()};
106 const QDate date {2018, 11, 5}; // Monday
107 QStringList ret;
108 for (int i = 0; i < 7; ++i)
109 ret.append(locale.toString(date.addDays(i), u"dddd"_s));
110 return ret;
113 class WheelEventEater final : public QObject
115 public:
116 using QObject::QObject;
118 private:
119 bool eventFilter(QObject *, QEvent *event) override
121 return (event->type() == QEvent::Wheel);
125 bool isValidWebUIUsername(const QString &username)
127 return (username.length() >= WEBUI_MIN_USERNAME_LENGTH);
130 bool isValidWebUIPassword(const QString &password)
132 return (password.length() >= WEBUI_MIN_PASSWORD_LENGTH);
135 // Shortcuts for frequently used signals that have more than one overload. They would require
136 // type casts and that is why we declare required member pointer here instead.
137 void (QComboBox::*qComboBoxCurrentIndexChanged)(int) = &QComboBox::currentIndexChanged;
138 void (QSpinBox::*qSpinBoxValueChanged)(int) = &QSpinBox::valueChanged;
141 // Constructor
142 OptionsDialog::OptionsDialog(IGUIApplication *app, QWidget *parent)
143 : GUIApplicationComponent(app, parent)
144 , m_ui {new Ui::OptionsDialog}
145 , m_storeDialogSize {SETTINGS_KEY(u"Size"_s)}
146 , m_storeHSplitterSize {SETTINGS_KEY(u"HorizontalSplitterSizes"_s)}
147 , m_storeLastViewedPage {SETTINGS_KEY(u"LastViewedPage"_s)}
149 m_ui->setupUi(this);
150 m_applyButton = m_ui->buttonBox->button(QDialogButtonBox::Apply);
152 #ifdef Q_OS_UNIX
153 setWindowTitle(tr("Preferences"));
154 #endif
156 m_ui->hsplitter->setCollapsible(0, false);
157 m_ui->hsplitter->setCollapsible(1, false);
159 // Main icons
160 m_ui->tabSelection->item(TAB_UI)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-desktop"_s));
161 m_ui->tabSelection->item(TAB_BITTORRENT)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-bittorrent"_s, u"preferences-system-network"_s));
162 m_ui->tabSelection->item(TAB_CONNECTION)->setIcon(UIThemeManager::instance()->getIcon(u"network-connect"_s, u"network-wired"_s));
163 m_ui->tabSelection->item(TAB_DOWNLOADS)->setIcon(UIThemeManager::instance()->getIcon(u"download"_s, u"folder-download"_s));
164 m_ui->tabSelection->item(TAB_SPEED)->setIcon(UIThemeManager::instance()->getIcon(u"speedometer"_s, u"chronometer"_s));
165 m_ui->tabSelection->item(TAB_RSS)->setIcon(UIThemeManager::instance()->getIcon(u"application-rss"_s, u"application-rss+xml"_s));
166 #ifdef DISABLE_WEBUI
167 m_ui->tabSelection->item(TAB_WEBUI)->setHidden(true);
168 #else
169 m_ui->tabSelection->item(TAB_WEBUI)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-webui"_s, u"network-server"_s));
170 #endif
171 m_ui->tabSelection->item(TAB_ADVANCED)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-advanced"_s, u"preferences-other"_s));
173 // set uniform size for all icons
174 int maxHeight = -1;
175 for (int i = 0; i < m_ui->tabSelection->count(); ++i)
176 maxHeight = std::max(maxHeight, m_ui->tabSelection->visualItemRect(m_ui->tabSelection->item(i)).size().height());
177 for (int i = 0; i < m_ui->tabSelection->count(); ++i)
179 const QSize size(std::numeric_limits<int>::max(), static_cast<int>(maxHeight * 1.2));
180 m_ui->tabSelection->item(i)->setSizeHint(size);
183 connect(m_ui->tabSelection, &QListWidget::currentItemChanged, this, &ThisType::changePage);
185 // Load options
186 loadBehaviorTabOptions();
187 loadDownloadsTabOptions();
188 loadConnectionTabOptions();
189 loadSpeedTabOptions();
190 loadBittorrentTabOptions();
191 loadRSSTabOptions();
192 #ifndef DISABLE_WEBUI
193 loadWebUITabOptions();
194 #endif
196 // Load Advanced settings
197 m_advancedSettings = new AdvancedSettings(app, m_ui->tabAdvancedPage);
198 m_ui->advPageLayout->addWidget(m_advancedSettings);
199 connect(m_advancedSettings, &AdvancedSettings::settingsChanged, this, &ThisType::enableApplyButton);
201 // setup apply button
202 m_applyButton->setEnabled(false);
203 connect(m_applyButton, &QPushButton::clicked, this, [this]
205 if (applySettings())
206 m_applyButton->setEnabled(false);
209 // disable mouse wheel event on widgets to avoid misselection
210 auto *wheelEventEater = new WheelEventEater(this);
211 for (QComboBox *widget : asConst(findChildren<QComboBox *>()))
212 widget->installEventFilter(wheelEventEater);
213 for (QSpinBox *widget : asConst(findChildren<QSpinBox *>()))
214 widget->installEventFilter(wheelEventEater);
216 m_ui->tabSelection->setCurrentRow(m_storeLastViewedPage);
218 if (const QSize dialogSize = m_storeDialogSize; dialogSize.isValid())
219 resize(dialogSize);
222 OptionsDialog::~OptionsDialog()
224 // save dialog states
225 m_storeDialogSize = size();
227 QStringList hSplitterSizes;
228 for (const int size : asConst(m_ui->hsplitter->sizes()))
229 hSplitterSizes.append(QString::number(size));
230 m_storeHSplitterSize = hSplitterSizes;
232 m_storeLastViewedPage = m_ui->tabSelection->currentRow();
234 delete m_ui;
237 void OptionsDialog::loadBehaviorTabOptions()
239 const auto *pref = Preferences::instance();
240 const auto *session = BitTorrent::Session::instance();
242 initializeLanguageCombo();
243 setLocale(pref->getLocale());
245 initializeStyleCombo();
246 initializeColorSchemeOptions();
248 m_ui->checkUseCustomTheme->setChecked(Preferences::instance()->useCustomUITheme());
249 m_ui->customThemeFilePath->setSelectedPath(Preferences::instance()->customUIThemePath());
250 m_ui->customThemeFilePath->setMode(FileSystemPathEdit::Mode::FileOpen);
251 m_ui->customThemeFilePath->setDialogCaption(tr("Select qBittorrent UI Theme file"));
252 m_ui->customThemeFilePath->setFileNameFilter(tr("qBittorrent UI Theme file (*.qbtheme config.json)"));
253 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
254 m_ui->checkUseSystemIcon->setChecked(pref->useSystemIcons());
255 #else
256 m_ui->checkUseSystemIcon->setVisible(false);
257 #endif
259 m_ui->confirmDeletion->setChecked(pref->confirmTorrentDeletion());
260 m_ui->checkAltRowColors->setChecked(pref->useAlternatingRowColors());
261 m_ui->checkHideZero->setChecked(pref->getHideZeroValues());
262 m_ui->comboHideZero->setCurrentIndex(pref->getHideZeroComboValues());
263 m_ui->comboHideZero->setEnabled(m_ui->checkHideZero->isChecked());
265 m_ui->actionTorrentDlOnDblClBox->setItemData(0, TOGGLE_STOP);
266 m_ui->actionTorrentDlOnDblClBox->setItemData(1, OPEN_DEST);
267 m_ui->actionTorrentDlOnDblClBox->setItemData(2, PREVIEW_FILE);
268 m_ui->actionTorrentDlOnDblClBox->setItemData(3, SHOW_OPTIONS);
269 m_ui->actionTorrentDlOnDblClBox->setItemData(4, NO_ACTION);
270 int actionDownloading = pref->getActionOnDblClOnTorrentDl();
271 if ((actionDownloading < 0) || (actionDownloading >= m_ui->actionTorrentDlOnDblClBox->count()))
272 actionDownloading = TOGGLE_STOP;
273 m_ui->actionTorrentDlOnDblClBox->setCurrentIndex(m_ui->actionTorrentDlOnDblClBox->findData(actionDownloading));
275 m_ui->actionTorrentFnOnDblClBox->setItemData(0, TOGGLE_STOP);
276 m_ui->actionTorrentFnOnDblClBox->setItemData(1, OPEN_DEST);
277 m_ui->actionTorrentFnOnDblClBox->setItemData(2, PREVIEW_FILE);
278 m_ui->actionTorrentFnOnDblClBox->setItemData(3, SHOW_OPTIONS);
279 m_ui->actionTorrentFnOnDblClBox->setItemData(4, NO_ACTION);
280 int actionSeeding = pref->getActionOnDblClOnTorrentFn();
281 if ((actionSeeding < 0) || (actionSeeding >= m_ui->actionTorrentFnOnDblClBox->count()))
282 actionSeeding = OPEN_DEST;
283 m_ui->actionTorrentFnOnDblClBox->setCurrentIndex(m_ui->actionTorrentFnOnDblClBox->findData(actionSeeding));
285 m_ui->checkBoxHideZeroStatusFilters->setChecked(pref->getHideZeroStatusFilters());
287 #ifndef Q_OS_WIN
288 m_ui->checkStartup->setVisible(false);
289 #endif
290 m_ui->checkShowSplash->setChecked(!pref->isSplashScreenDisabled());
291 m_ui->checkProgramExitConfirm->setChecked(pref->confirmOnExit());
292 m_ui->checkProgramAutoExitConfirm->setChecked(!pref->dontConfirmAutoExit());
294 m_ui->windowStateComboBox->addItem(tr("Normal"), QVariant::fromValue(WindowState::Normal));
295 m_ui->windowStateComboBox->addItem(tr("Minimized"), QVariant::fromValue(WindowState::Minimized));
296 #ifndef Q_OS_MACOS
297 m_ui->windowStateComboBox->addItem(tr("Hidden"), QVariant::fromValue(WindowState::Hidden));
298 #endif
299 m_ui->windowStateComboBox->setCurrentIndex(m_ui->windowStateComboBox->findData(QVariant::fromValue(app()->startUpWindowState())));
301 #if !(defined(Q_OS_WIN) || defined(Q_OS_MACOS))
302 m_ui->groupFileAssociation->setVisible(false);
303 m_ui->checkProgramUpdates->setVisible(false);
304 #endif
306 #ifndef Q_OS_MACOS
307 // Disable systray integration if it is not supported by the system
308 if (!QSystemTrayIcon::isSystemTrayAvailable())
310 m_ui->checkShowSystray->setChecked(false);
311 m_ui->checkShowSystray->setEnabled(false);
312 m_ui->checkShowSystray->setToolTip(tr("Disabled due to failed to detect system tray presence"));
314 m_ui->checkShowSystray->setChecked(pref->systemTrayEnabled());
315 m_ui->checkMinimizeToSysTray->setChecked(pref->minimizeToTray());
316 m_ui->checkCloseToSystray->setChecked(pref->closeToTray());
317 m_ui->comboTrayIcon->setCurrentIndex(static_cast<int>(pref->trayIconStyle()));
318 #endif
320 #ifdef Q_OS_WIN
321 m_ui->checkStartup->setChecked(pref->WinStartup());
322 #endif
324 #ifdef Q_OS_MACOS
325 m_ui->checkShowSystray->setVisible(false);
326 m_ui->checkAssociateTorrents->setChecked(Utils::OS::isTorrentFileAssocSet());
327 m_ui->checkAssociateTorrents->setEnabled(!m_ui->checkAssociateTorrents->isChecked());
328 m_ui->checkAssociateMagnetLinks->setChecked(Utils::OS::isMagnetLinkAssocSet());
329 m_ui->checkAssociateMagnetLinks->setEnabled(!m_ui->checkAssociateMagnetLinks->isChecked());
330 #endif
332 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
333 m_ui->checkProgramUpdates->setChecked(pref->isUpdateCheckEnabled());
334 #endif
336 m_ui->checkPreventFromSuspendWhenDownloading->setChecked(pref->preventFromSuspendWhenDownloading());
337 m_ui->checkPreventFromSuspendWhenSeeding->setChecked(pref->preventFromSuspendWhenSeeding());
339 m_ui->textFileLogPath->setDialogCaption(tr("Choose a save directory"));
340 m_ui->textFileLogPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
341 m_ui->textFileLogPath->setSelectedPath(app()->fileLoggerPath());
342 const bool fileLogBackup = app()->isFileLoggerBackup();
343 m_ui->checkFileLogBackup->setChecked(fileLogBackup);
344 m_ui->spinFileLogSize->setEnabled(fileLogBackup);
345 const bool fileLogDelete = app()->isFileLoggerDeleteOld();
346 m_ui->checkFileLogDelete->setChecked(fileLogDelete);
347 m_ui->spinFileLogAge->setEnabled(fileLogDelete);
348 m_ui->comboFileLogAgeType->setEnabled(fileLogDelete);
349 m_ui->spinFileLogSize->setValue(app()->fileLoggerMaxSize() / 1024);
350 m_ui->spinFileLogAge->setValue(app()->fileLoggerAge());
351 m_ui->comboFileLogAgeType->setCurrentIndex(app()->fileLoggerAgeType());
352 // Groupbox's check state must be initialized after some of its children if they are manually enabled/disabled
353 m_ui->checkFileLog->setChecked(app()->isFileLoggerEnabled());
355 m_ui->checkBoxExternalIPStatusBar->setChecked(pref->isStatusbarExternalIPDisplayed());
356 m_ui->checkBoxPerformanceWarning->setChecked(session->isPerformanceWarningEnabled());
358 connect(m_ui->comboLanguage, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
360 #ifdef Q_OS_WIN
361 connect(m_ui->comboStyle, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
362 #endif
364 #ifdef QBT_HAS_COLORSCHEME_OPTION
365 connect(m_ui->comboColorScheme, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
366 #endif
368 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
369 connect(m_ui->checkUseSystemIcon, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
370 #endif
371 connect(m_ui->checkUseCustomTheme, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
372 connect(m_ui->customThemeFilePath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
374 m_ui->buttonCustomizeUITheme->setEnabled(!m_ui->checkUseCustomTheme->isChecked());
375 connect(m_ui->checkUseCustomTheme, &QGroupBox::toggled, this, [this]
377 m_ui->buttonCustomizeUITheme->setEnabled(!m_ui->checkUseCustomTheme->isChecked());
379 connect(m_ui->buttonCustomizeUITheme, &QPushButton::clicked, this, [this]
381 auto *dialog = new UIThemeDialog(this);
382 dialog->setAttribute(Qt::WA_DeleteOnClose);
383 dialog->open();
386 connect(m_ui->confirmDeletion, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
387 connect(m_ui->checkAltRowColors, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
388 connect(m_ui->checkHideZero, &QAbstractButton::toggled, m_ui->comboHideZero, &QWidget::setEnabled);
389 connect(m_ui->checkHideZero, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
390 connect(m_ui->comboHideZero, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
391 connect(m_ui->actionTorrentDlOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
392 connect(m_ui->actionTorrentFnOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
393 connect(m_ui->checkBoxHideZeroStatusFilters, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
395 #ifdef Q_OS_WIN
396 connect(m_ui->checkStartup, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
397 #endif
398 connect(m_ui->checkShowSplash, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
399 connect(m_ui->checkProgramExitConfirm, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
400 connect(m_ui->checkProgramAutoExitConfirm, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
401 connect(m_ui->checkShowSystray, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
402 connect(m_ui->checkMinimizeToSysTray, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
403 connect(m_ui->checkCloseToSystray, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
404 connect(m_ui->comboTrayIcon, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
405 connect(m_ui->windowStateComboBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
407 connect(m_ui->checkPreventFromSuspendWhenDownloading, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
408 connect(m_ui->checkPreventFromSuspendWhenSeeding, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
410 #if defined(Q_OS_MACOS)
411 connect(m_ui->checkAssociateTorrents, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
412 connect(m_ui->checkAssociateMagnetLinks, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
413 #endif
415 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
416 connect(m_ui->checkProgramUpdates, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
417 #endif
419 #ifdef Q_OS_WIN
420 m_ui->assocPanel->hide();
421 #endif
423 #ifdef Q_OS_MAC
424 m_ui->defaultProgramPanel->hide();
425 #endif
427 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && !defined(QBT_USES_DBUS)
428 m_ui->checkPreventFromSuspendWhenDownloading->setDisabled(true);
429 m_ui->checkPreventFromSuspendWhenSeeding->setDisabled(true);
430 #endif
432 connect(m_ui->checkFileLog, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
433 connect(m_ui->textFileLogPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
434 connect(m_ui->checkFileLogBackup, &QAbstractButton::toggled, m_ui->spinFileLogSize, &QWidget::setEnabled);
435 connect(m_ui->checkFileLogBackup, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
436 connect(m_ui->checkFileLogDelete, &QAbstractButton::toggled, m_ui->comboFileLogAgeType, &QWidget::setEnabled);
437 connect(m_ui->checkFileLogDelete, &QAbstractButton::toggled, m_ui->spinFileLogAge, &QWidget::setEnabled);
438 connect(m_ui->checkFileLogDelete, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
439 connect(m_ui->spinFileLogSize, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
440 connect(m_ui->spinFileLogAge, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
441 connect(m_ui->comboFileLogAgeType, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
443 connect(m_ui->checkBoxExternalIPStatusBar, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
444 connect(m_ui->checkBoxPerformanceWarning, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
447 void OptionsDialog::saveBehaviorTabOptions() const
449 auto *pref = Preferences::instance();
450 auto *session = BitTorrent::Session::instance();
452 // Load the translation
453 const QString locale = getLocale();
454 if (pref->getLocale() != locale)
456 auto *translator = new QTranslator;
457 if (translator->load(u":/lang/qbittorrent_"_s + locale))
458 qDebug("%s locale recognized, using translation.", qUtf8Printable(locale));
459 else
460 qDebug("%s locale unrecognized, using default (en).", qUtf8Printable(locale));
461 qApp->installTranslator(translator);
463 pref->setLocale(locale);
465 #ifdef Q_OS_WIN
466 if (const QVariant systemStyle = m_ui->comboStyle->currentData(); systemStyle.isValid())
467 pref->setStyle(systemStyle.toString());
468 else
469 pref->setStyle(m_ui->comboStyle->currentText());
470 #endif
472 #ifdef QBT_HAS_COLORSCHEME_OPTION
473 UIThemeManager::instance()->setColorScheme(m_ui->comboColorScheme->currentData().value<ColorScheme>());
474 #endif
476 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
477 pref->useSystemIcons(m_ui->checkUseSystemIcon->isChecked());
478 #endif
479 pref->setUseCustomUITheme(m_ui->checkUseCustomTheme->isChecked());
480 pref->setCustomUIThemePath(m_ui->customThemeFilePath->selectedPath());
482 pref->setConfirmTorrentDeletion(m_ui->confirmDeletion->isChecked());
483 pref->setAlternatingRowColors(m_ui->checkAltRowColors->isChecked());
484 pref->setHideZeroValues(m_ui->checkHideZero->isChecked());
485 pref->setHideZeroComboValues(m_ui->comboHideZero->currentIndex());
487 pref->setActionOnDblClOnTorrentDl(m_ui->actionTorrentDlOnDblClBox->currentData().toInt());
488 pref->setActionOnDblClOnTorrentFn(m_ui->actionTorrentFnOnDblClBox->currentData().toInt());
490 pref->setHideZeroStatusFilters(m_ui->checkBoxHideZeroStatusFilters->isChecked());
492 pref->setSplashScreenDisabled(isSplashScreenDisabled());
493 pref->setConfirmOnExit(m_ui->checkProgramExitConfirm->isChecked());
494 pref->setDontConfirmAutoExit(!m_ui->checkProgramAutoExitConfirm->isChecked());
496 #ifdef Q_OS_WIN
497 pref->setWinStartup(WinStartup());
498 #endif
500 #ifndef Q_OS_MACOS
501 pref->setSystemTrayEnabled(m_ui->checkShowSystray->isChecked());
502 pref->setTrayIconStyle(TrayIcon::Style(m_ui->comboTrayIcon->currentIndex()));
503 pref->setCloseToTray(m_ui->checkCloseToSystray->isChecked());
504 pref->setMinimizeToTray(m_ui->checkMinimizeToSysTray->isChecked());
505 #endif
507 #ifdef Q_OS_MACOS
508 if (m_ui->checkAssociateTorrents->isChecked())
510 Utils::OS::setTorrentFileAssoc();
511 m_ui->checkAssociateTorrents->setChecked(Utils::OS::isTorrentFileAssocSet());
512 m_ui->checkAssociateTorrents->setEnabled(!m_ui->checkAssociateTorrents->isChecked());
514 if (m_ui->checkAssociateMagnetLinks->isChecked())
516 Utils::OS::setMagnetLinkAssoc();
517 m_ui->checkAssociateMagnetLinks->setChecked(Utils::OS::isMagnetLinkAssocSet());
518 m_ui->checkAssociateMagnetLinks->setEnabled(!m_ui->checkAssociateMagnetLinks->isChecked());
520 #endif
522 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
523 pref->setUpdateCheckEnabled(m_ui->checkProgramUpdates->isChecked());
524 #endif
526 pref->setPreventFromSuspendWhenDownloading(m_ui->checkPreventFromSuspendWhenDownloading->isChecked());
527 pref->setPreventFromSuspendWhenSeeding(m_ui->checkPreventFromSuspendWhenSeeding->isChecked());
529 app()->setFileLoggerPath(m_ui->textFileLogPath->selectedPath());
530 app()->setFileLoggerBackup(m_ui->checkFileLogBackup->isChecked());
531 app()->setFileLoggerMaxSize(m_ui->spinFileLogSize->value() * 1024);
532 app()->setFileLoggerAge(m_ui->spinFileLogAge->value());
533 app()->setFileLoggerAgeType(m_ui->comboFileLogAgeType->currentIndex());
534 app()->setFileLoggerDeleteOld(m_ui->checkFileLogDelete->isChecked());
535 app()->setFileLoggerEnabled(m_ui->checkFileLog->isChecked());
537 app()->setStartUpWindowState(m_ui->windowStateComboBox->currentData().value<WindowState>());
539 pref->setStatusbarExternalIPDisplayed(m_ui->checkBoxExternalIPStatusBar->isChecked());
540 session->setPerformanceWarningEnabled(m_ui->checkBoxPerformanceWarning->isChecked());
543 void OptionsDialog::loadDownloadsTabOptions()
545 const auto *pref = Preferences::instance();
546 const auto *session = BitTorrent::Session::instance();
548 m_ui->checkAdditionDialog->setChecked(pref->isAddNewTorrentDialogEnabled());
549 m_ui->checkAdditionDialogFront->setChecked(pref->isAddNewTorrentDialogTopLevel());
551 m_ui->contentLayoutComboBox->setCurrentIndex(static_cast<int>(session->torrentContentLayout()));
552 m_ui->checkAddToQueueTop->setChecked(session->isAddTorrentToQueueTop());
553 m_ui->checkAddStopped->setChecked(session->isAddTorrentStopped());
555 m_ui->stopConditionComboBox->setToolTip(
556 u"<html><body><p><b>" + tr("None") + u"</b> - " + tr("No stop condition is set.") + u"</p><p><b>" +
557 tr("Metadata received") + u"</b> - " + tr("Torrent will stop after metadata is received.") +
558 u" <em>" + tr("Torrents that have metadata initially will be added as stopped.") + u"</em></p><p><b>" +
559 tr("Files checked") + u"</b> - " + tr("Torrent will stop after files are initially checked.") +
560 u" <em>" + tr("This will also download metadata if it wasn't there initially.") + u"</em></p></body></html>");
561 m_ui->stopConditionComboBox->setItemData(0, QVariant::fromValue(BitTorrent::Torrent::StopCondition::None));
562 m_ui->stopConditionComboBox->setItemData(1, QVariant::fromValue(BitTorrent::Torrent::StopCondition::MetadataReceived));
563 m_ui->stopConditionComboBox->setItemData(2, QVariant::fromValue(BitTorrent::Torrent::StopCondition::FilesChecked));
564 m_ui->stopConditionComboBox->setCurrentIndex(m_ui->stopConditionComboBox->findData(QVariant::fromValue(session->torrentStopCondition())));
565 m_ui->stopConditionLabel->setEnabled(!m_ui->checkAddStopped->isChecked());
566 m_ui->stopConditionComboBox->setEnabled(!m_ui->checkAddStopped->isChecked());
568 m_ui->checkMergeTrackers->setChecked(session->isMergeTrackersEnabled());
569 m_ui->checkConfirmMergeTrackers->setEnabled(m_ui->checkAdditionDialog->isChecked());
570 m_ui->checkConfirmMergeTrackers->setChecked(m_ui->checkConfirmMergeTrackers->isEnabled() ? pref->confirmMergeTrackers() : false);
571 connect(m_ui->checkAdditionDialog, &QGroupBox::toggled, this, [this, pref]
573 m_ui->checkConfirmMergeTrackers->setEnabled(m_ui->checkAdditionDialog->isChecked());
574 m_ui->checkConfirmMergeTrackers->setChecked(m_ui->checkConfirmMergeTrackers->isEnabled() ? pref->confirmMergeTrackers() : false);
577 const TorrentFileGuard::AutoDeleteMode autoDeleteMode = TorrentFileGuard::autoDeleteMode();
578 m_ui->deleteTorrentBox->setChecked(autoDeleteMode != TorrentFileGuard::Never);
579 m_ui->deleteCancelledTorrentBox->setChecked(autoDeleteMode == TorrentFileGuard::Always);
580 m_ui->deleteTorrentWarningIcon->setPixmap(QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical).pixmap(16, 16));
581 m_ui->deleteTorrentWarningIcon->hide();
582 m_ui->deleteTorrentWarningLabel->hide();
583 m_ui->deleteTorrentWarningLabel->setToolTip(u"<html><body><p>" +
584 tr("By enabling these options, you can <strong>irrevocably lose</strong> your .torrent files!") +
585 u"</p><p>" +
586 tr("When these options are enabled, qBittorrent will <strong>delete</strong> .torrent files "
587 "after they were successfully (the first option) or not (the second option) added to its "
588 "download queue. This will be applied <strong>not only</strong> to the files opened via "
589 "&ldquo;Add torrent&rdquo; menu action but to those opened via <strong>file type association</strong> as well") +
590 u"</p><p>" +
591 tr("If you enable the second option (&ldquo;Also when addition is cancelled&rdquo;) the "
592 ".torrent file <strong>will be deleted</strong> even if you press &ldquo;<strong>Cancel</strong>&rdquo; in "
593 "the &ldquo;Add torrent&rdquo; dialog") +
594 u"</p></body></html>");
596 m_ui->checkPreallocateAll->setChecked(session->isPreallocationEnabled());
597 m_ui->checkAppendqB->setChecked(session->isAppendExtensionEnabled());
598 m_ui->checkUnwantedFolder->setChecked(session->isUnwantedFolderEnabled());
599 m_ui->checkRecursiveDownload->setChecked(pref->isRecursiveDownloadEnabled());
601 m_ui->comboSavingMode->setCurrentIndex(!session->isAutoTMMDisabledByDefault());
602 m_ui->comboTorrentCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategoryChanged());
603 m_ui->comboCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategorySavePathChanged());
604 m_ui->comboCategoryDefaultPathChanged->setCurrentIndex(session->isDisableAutoTMMWhenDefaultSavePathChanged());
606 m_ui->checkUseSubcategories->setChecked(session->isSubcategoriesEnabled());
607 m_ui->checkUseCategoryPaths->setChecked(session->useCategoryPathsInManualMode());
609 m_ui->textSavePath->setDialogCaption(tr("Choose a save directory"));
610 m_ui->textSavePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
611 m_ui->textSavePath->setSelectedPath(session->savePath());
613 m_ui->checkUseDownloadPath->setChecked(session->isDownloadPathEnabled());
614 m_ui->textDownloadPath->setDialogCaption(tr("Choose a save directory"));
615 m_ui->textDownloadPath->setEnabled(m_ui->checkUseDownloadPath->isChecked());
616 m_ui->textDownloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
617 m_ui->textDownloadPath->setSelectedPath(session->downloadPath());
619 const bool isExportDirEmpty = session->torrentExportDirectory().isEmpty();
620 m_ui->checkExportDir->setChecked(!isExportDirEmpty);
621 m_ui->textExportDir->setDialogCaption(tr("Choose export directory"));
622 m_ui->textExportDir->setEnabled(m_ui->checkExportDir->isChecked());
623 m_ui->textExportDir->setMode(FileSystemPathEdit::Mode::DirectorySave);
624 if (!isExportDirEmpty)
625 m_ui->textExportDir->setSelectedPath(session->torrentExportDirectory());
627 const bool isExportDirFinEmpty = session->finishedTorrentExportDirectory().isEmpty();
628 m_ui->checkExportDirFin->setChecked(!isExportDirFinEmpty);
629 m_ui->textExportDirFin->setDialogCaption(tr("Choose export directory"));
630 m_ui->textExportDirFin->setEnabled(m_ui->checkExportDirFin->isChecked());
631 m_ui->textExportDirFin->setMode(FileSystemPathEdit::Mode::DirectorySave);
632 if (!isExportDirFinEmpty)
633 m_ui->textExportDirFin->setSelectedPath(session->finishedTorrentExportDirectory());
635 auto *watchedFoldersModel = new WatchedFoldersModel(TorrentFilesWatcher::instance(), this);
636 connect(watchedFoldersModel, &QAbstractListModel::dataChanged, this, &ThisType::enableApplyButton);
637 m_ui->scanFoldersView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
638 m_ui->scanFoldersView->setModel(watchedFoldersModel);
639 connect(m_ui->scanFoldersView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ThisType::handleWatchedFolderViewSelectionChanged);
640 connect(m_ui->scanFoldersView, &QTreeView::doubleClicked, this, &ThisType::editWatchedFolderOptions);
642 m_ui->groupExcludedFileNames->setChecked(session->isExcludedFileNamesEnabled());
643 m_ui->textExcludedFileNames->setPlainText(session->excludedFileNames().join(u'\n'));
645 m_ui->groupMailNotification->setChecked(pref->isMailNotificationEnabled());
646 m_ui->senderEmailTxt->setText(pref->getMailNotificationSender());
647 m_ui->lineEditDestEmail->setText(pref->getMailNotificationEmail());
648 m_ui->lineEditSmtpServer->setText(pref->getMailNotificationSMTP());
649 m_ui->checkSmtpSSL->setChecked(pref->getMailNotificationSMTPSSL());
650 m_ui->groupMailNotifAuth->setChecked(pref->getMailNotificationSMTPAuth());
651 m_ui->mailNotifUsername->setText(pref->getMailNotificationSMTPUsername());
652 m_ui->mailNotifPassword->setText(pref->getMailNotificationSMTPPassword());
654 m_ui->groupBoxRunOnAdded->setChecked(pref->isAutoRunOnTorrentAddedEnabled());
655 m_ui->groupBoxRunOnFinished->setChecked(pref->isAutoRunOnTorrentFinishedEnabled());
656 m_ui->lineEditRunOnAdded->setText(pref->getAutoRunOnTorrentAddedProgram());
657 m_ui->lineEditRunOnFinished->setText(pref->getAutoRunOnTorrentFinishedProgram());
658 #if defined(Q_OS_WIN)
659 m_ui->autoRunConsole->setChecked(pref->isAutoRunConsoleEnabled());
660 #else
661 m_ui->autoRunConsole->hide();
662 #endif
663 const auto autoRunStr = u"%1\n %2\n %3\n %4\n %5\n %6\n %7\n %8\n %9\n %10\n %11\n %12\n %13\n%14"_s
664 .arg(tr("Supported parameters (case sensitive):")
665 , tr("%N: Torrent name")
666 , tr("%L: Category")
667 , tr("%G: Tags (separated by comma)")
668 , tr("%F: Content path (same as root path for multifile torrent)")
669 , tr("%R: Root path (first torrent subdirectory path)")
670 , tr("%D: Save path")
671 , tr("%C: Number of files")
672 , tr("%Z: Torrent size (bytes)"))
673 .arg(tr("%T: Current tracker")
674 , tr("%I: Info hash v1 (or '-' if unavailable)")
675 , tr("%J: Info hash v2 (or '-' if unavailable)")
676 , tr("%K: Torrent ID (either sha-1 info hash for v1 torrent or truncated sha-256 info hash for v2/hybrid torrent)")
677 , tr("Tip: Encapsulate parameter with quotation marks to avoid text being cut off at whitespace (e.g., \"%N\")"));
678 m_ui->labelAutoRunParam->setText(autoRunStr);
680 connect(m_ui->checkAdditionDialog, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
681 connect(m_ui->checkAdditionDialogFront, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
683 connect(m_ui->contentLayoutComboBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
685 connect(m_ui->checkAddToQueueTop, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
686 connect(m_ui->checkAddStopped, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
687 connect(m_ui->checkAddStopped, &QAbstractButton::toggled, this, [this](const bool checked)
689 m_ui->stopConditionLabel->setEnabled(!checked);
690 m_ui->stopConditionComboBox->setEnabled(!checked);
692 connect(m_ui->stopConditionComboBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
693 connect(m_ui->checkMergeTrackers, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
694 connect(m_ui->checkConfirmMergeTrackers, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
695 connect(m_ui->deleteTorrentBox, &QGroupBox::toggled, m_ui->deleteTorrentWarningIcon, &QWidget::setVisible);
696 connect(m_ui->deleteTorrentBox, &QGroupBox::toggled, m_ui->deleteTorrentWarningLabel, &QWidget::setVisible);
697 connect(m_ui->deleteTorrentBox, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
698 connect(m_ui->deleteCancelledTorrentBox, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
700 connect(m_ui->checkPreallocateAll, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
701 connect(m_ui->checkAppendqB, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
702 connect(m_ui->checkUnwantedFolder, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
703 connect(m_ui->checkRecursiveDownload, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
705 connect(m_ui->comboSavingMode, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
706 connect(m_ui->comboTorrentCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
707 connect(m_ui->comboCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
708 connect(m_ui->comboCategoryDefaultPathChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
710 connect(m_ui->checkUseSubcategories, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
711 connect(m_ui->checkUseCategoryPaths, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
713 connect(m_ui->textSavePath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
714 connect(m_ui->textDownloadPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
716 connect(m_ui->checkExportDir, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
717 connect(m_ui->checkExportDir, &QAbstractButton::toggled, m_ui->textExportDir, &QWidget::setEnabled);
718 connect(m_ui->checkExportDirFin, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
719 connect(m_ui->checkExportDirFin, &QAbstractButton::toggled, m_ui->textExportDirFin, &QWidget::setEnabled);
720 connect(m_ui->textExportDir, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
721 connect(m_ui->textExportDirFin, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
722 connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
723 connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, m_ui->textDownloadPath, &QWidget::setEnabled);
725 connect(m_ui->addWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
727 connect(m_ui->groupExcludedFileNames, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
728 connect(m_ui->textExcludedFileNames, &QPlainTextEdit::textChanged, this, &ThisType::enableApplyButton);
729 connect(m_ui->removeWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
731 connect(m_ui->groupMailNotification, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
732 connect(m_ui->senderEmailTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
733 connect(m_ui->lineEditDestEmail, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
734 connect(m_ui->lineEditSmtpServer, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
735 connect(m_ui->checkSmtpSSL, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
736 connect(m_ui->groupMailNotifAuth, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
737 connect(m_ui->mailNotifUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
738 connect(m_ui->mailNotifPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
739 connect(m_ui->sendTestEmail, &QPushButton::clicked, this, [this]
741 app()->sendTestEmail();
742 QMessageBox::information(this, tr("Test email"), tr("Attempted to send email. Check your inbox to confirm success"));
745 connect(m_ui->groupBoxRunOnAdded, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
746 connect(m_ui->lineEditRunOnAdded, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
747 connect(m_ui->groupBoxRunOnFinished, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
748 connect(m_ui->lineEditRunOnFinished, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
749 connect(m_ui->autoRunConsole, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
752 void OptionsDialog::saveDownloadsTabOptions() const
754 auto *pref = Preferences::instance();
755 auto *session = BitTorrent::Session::instance();
757 pref->setAddNewTorrentDialogEnabled(useAdditionDialog());
758 pref->setAddNewTorrentDialogTopLevel(m_ui->checkAdditionDialogFront->isChecked());
760 session->setTorrentContentLayout(static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex()));
762 session->setAddTorrentToQueueTop(m_ui->checkAddToQueueTop->isChecked());
763 session->setAddTorrentStopped(addTorrentsStopped());
764 session->setTorrentStopCondition(m_ui->stopConditionComboBox->currentData().value<BitTorrent::Torrent::StopCondition>());
765 TorrentFileGuard::setAutoDeleteMode(!m_ui->deleteTorrentBox->isChecked() ? TorrentFileGuard::Never
766 : !m_ui->deleteCancelledTorrentBox->isChecked() ? TorrentFileGuard::IfAdded
767 : TorrentFileGuard::Always);
768 session->setMergeTrackersEnabled(m_ui->checkMergeTrackers->isChecked());
769 if (m_ui->checkConfirmMergeTrackers->isEnabled())
770 pref->setConfirmMergeTrackers(m_ui->checkConfirmMergeTrackers->isChecked());
772 session->setPreallocationEnabled(preAllocateAllFiles());
773 session->setAppendExtensionEnabled(m_ui->checkAppendqB->isChecked());
774 session->setUnwantedFolderEnabled(m_ui->checkUnwantedFolder->isChecked());
775 pref->setRecursiveDownloadEnabled(m_ui->checkRecursiveDownload->isChecked());
777 session->setAutoTMMDisabledByDefault(m_ui->comboSavingMode->currentIndex() == 0);
778 session->setDisableAutoTMMWhenCategoryChanged(m_ui->comboTorrentCategoryChanged->currentIndex() == 1);
779 session->setDisableAutoTMMWhenCategorySavePathChanged(m_ui->comboCategoryChanged->currentIndex() == 1);
780 session->setDisableAutoTMMWhenDefaultSavePathChanged(m_ui->comboCategoryDefaultPathChanged->currentIndex() == 1);
782 session->setSubcategoriesEnabled(m_ui->checkUseSubcategories->isChecked());
783 session->setUseCategoryPathsInManualMode(m_ui->checkUseCategoryPaths->isChecked());
785 session->setSavePath(Path(m_ui->textSavePath->selectedPath()));
786 session->setDownloadPathEnabled(m_ui->checkUseDownloadPath->isChecked());
787 session->setDownloadPath(m_ui->textDownloadPath->selectedPath());
788 session->setTorrentExportDirectory(getTorrentExportDir());
789 session->setFinishedTorrentExportDirectory(getFinishedTorrentExportDir());
791 auto *watchedFoldersModel = static_cast<WatchedFoldersModel *>(m_ui->scanFoldersView->model());
792 watchedFoldersModel->apply();
794 session->setExcludedFileNamesEnabled(m_ui->groupExcludedFileNames->isChecked());
795 session->setExcludedFileNames(m_ui->textExcludedFileNames->toPlainText().split(u'\n', Qt::SkipEmptyParts));
797 pref->setMailNotificationEnabled(m_ui->groupMailNotification->isChecked());
798 pref->setMailNotificationSender(m_ui->senderEmailTxt->text());
799 pref->setMailNotificationEmail(m_ui->lineEditDestEmail->text());
800 pref->setMailNotificationSMTP(m_ui->lineEditSmtpServer->text());
801 pref->setMailNotificationSMTPSSL(m_ui->checkSmtpSSL->isChecked());
802 pref->setMailNotificationSMTPAuth(m_ui->groupMailNotifAuth->isChecked());
803 pref->setMailNotificationSMTPUsername(m_ui->mailNotifUsername->text());
804 pref->setMailNotificationSMTPPassword(m_ui->mailNotifPassword->text());
806 pref->setAutoRunOnTorrentAddedEnabled(m_ui->groupBoxRunOnAdded->isChecked());
807 pref->setAutoRunOnTorrentAddedProgram(m_ui->lineEditRunOnAdded->text().trimmed());
808 pref->setAutoRunOnTorrentFinishedEnabled(m_ui->groupBoxRunOnFinished->isChecked());
809 pref->setAutoRunOnTorrentFinishedProgram(m_ui->lineEditRunOnFinished->text().trimmed());
810 #if defined(Q_OS_WIN)
811 pref->setAutoRunConsoleEnabled(m_ui->autoRunConsole->isChecked());
812 #endif
815 void OptionsDialog::loadConnectionTabOptions()
817 const auto *session = BitTorrent::Session::instance();
819 m_ui->comboProtocol->setCurrentIndex(static_cast<int>(session->btProtocol()));
820 m_ui->spinPort->setValue(session->port());
821 m_ui->checkUPnP->setChecked(Net::PortForwarder::instance()->isEnabled());
823 int intValue = session->maxConnections();
824 if (intValue > 0)
826 // enable
827 m_ui->checkMaxConnections->setChecked(true);
828 m_ui->spinMaxConnec->setEnabled(true);
829 m_ui->spinMaxConnec->setValue(intValue);
831 else
833 // disable
834 m_ui->checkMaxConnections->setChecked(false);
835 m_ui->spinMaxConnec->setEnabled(false);
837 intValue = session->maxConnectionsPerTorrent();
838 if (intValue > 0)
840 // enable
841 m_ui->checkMaxConnectionsPerTorrent->setChecked(true);
842 m_ui->spinMaxConnecPerTorrent->setEnabled(true);
843 m_ui->spinMaxConnecPerTorrent->setValue(intValue);
845 else
847 // disable
848 m_ui->checkMaxConnectionsPerTorrent->setChecked(false);
849 m_ui->spinMaxConnecPerTorrent->setEnabled(false);
851 intValue = session->maxUploads();
852 if (intValue > 0)
854 // enable
855 m_ui->checkMaxUploads->setChecked(true);
856 m_ui->spinMaxUploads->setEnabled(true);
857 m_ui->spinMaxUploads->setValue(intValue);
859 else
861 // disable
862 m_ui->checkMaxUploads->setChecked(false);
863 m_ui->spinMaxUploads->setEnabled(false);
865 intValue = session->maxUploadsPerTorrent();
866 if (intValue > 0)
868 // enable
869 m_ui->checkMaxUploadsPerTorrent->setChecked(true);
870 m_ui->spinMaxUploadsPerTorrent->setEnabled(true);
871 m_ui->spinMaxUploadsPerTorrent->setValue(intValue);
873 else
875 // disable
876 m_ui->checkMaxUploadsPerTorrent->setChecked(false);
877 m_ui->spinMaxUploadsPerTorrent->setEnabled(false);
880 #if defined(QBT_USES_LIBTORRENT2) && TORRENT_USE_I2P
881 m_ui->textI2PHost->setText(session->I2PAddress());
882 m_ui->spinI2PPort->setValue(session->I2PPort());
883 m_ui->checkI2PMixed->setChecked(session->I2PMixedMode());
884 m_ui->groupI2P->setChecked(session->isI2PEnabled());
885 #else
886 m_ui->groupI2P->hide();
887 #endif
889 const auto *proxyConfigManager = Net::ProxyConfigurationManager::instance();
890 const Net::ProxyConfiguration proxyConf = proxyConfigManager->proxyConfiguration();
892 m_ui->comboProxyType->addItem(tr("(None)"), QVariant::fromValue(Net::ProxyType::None));
893 m_ui->comboProxyType->addItem(tr("SOCKS4"), QVariant::fromValue(Net::ProxyType::SOCKS4));
894 m_ui->comboProxyType->addItem(tr("SOCKS5"), QVariant::fromValue(Net::ProxyType::SOCKS5));
895 m_ui->comboProxyType->addItem(tr("HTTP"), QVariant::fromValue(Net::ProxyType::HTTP));
896 m_ui->comboProxyType->setCurrentIndex(m_ui->comboProxyType->findData(QVariant::fromValue(proxyConf.type)));
897 adjustProxyOptions();
899 m_ui->textProxyIP->setText(proxyConf.ip);
900 m_ui->spinProxyPort->setValue(proxyConf.port);
901 m_ui->textProxyUsername->setText(proxyConf.username);
902 m_ui->textProxyPassword->setText(proxyConf.password);
903 m_ui->checkProxyAuth->setChecked(proxyConf.authEnabled);
904 m_ui->checkProxyHostnameLookup->setChecked(proxyConf.hostnameLookupEnabled);
906 m_ui->checkProxyPeerConnections->setChecked(session->isProxyPeerConnectionsEnabled());
907 m_ui->checkProxyBitTorrent->setChecked(Preferences::instance()->useProxyForBT());
908 m_ui->checkProxyRSS->setChecked(Preferences::instance()->useProxyForRSS());
909 m_ui->checkProxyMisc->setChecked(Preferences::instance()->useProxyForGeneralPurposes());
911 m_ui->checkIPFilter->setChecked(session->isIPFilteringEnabled());
912 m_ui->textFilterPath->setDialogCaption(tr("Choose an IP filter file"));
913 m_ui->textFilterPath->setEnabled(m_ui->checkIPFilter->isChecked());
914 m_ui->textFilterPath->setFileNameFilter(tr("All supported filters") + u" (*.dat *.p2p *.p2b);;.dat (*.dat);;.p2p (*.p2p);;.p2b (*.p2b)");
915 m_ui->textFilterPath->setSelectedPath(session->IPFilterFile());
917 m_ui->IpFilterRefreshBtn->setIcon(UIThemeManager::instance()->getIcon(u"view-refresh"_s));
918 m_ui->IpFilterRefreshBtn->setEnabled(m_ui->checkIPFilter->isChecked());
919 m_ui->checkIpFilterTrackers->setChecked(session->isTrackerFilteringEnabled());
921 connect(m_ui->comboProtocol, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
922 connect(m_ui->spinPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
923 connect(m_ui->checkUPnP, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
925 connect(m_ui->checkMaxConnections, &QAbstractButton::toggled, m_ui->spinMaxConnec, &QWidget::setEnabled);
926 connect(m_ui->checkMaxConnections, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
927 connect(m_ui->checkMaxConnectionsPerTorrent, &QAbstractButton::toggled, m_ui->spinMaxConnecPerTorrent, &QWidget::setEnabled);
928 connect(m_ui->checkMaxConnectionsPerTorrent, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
929 connect(m_ui->checkMaxUploads, &QAbstractButton::toggled, m_ui->spinMaxUploads, &QWidget::setEnabled);
930 connect(m_ui->checkMaxUploads, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
931 connect(m_ui->checkMaxUploadsPerTorrent, &QAbstractButton::toggled, m_ui->spinMaxUploadsPerTorrent, &QWidget::setEnabled);
932 connect(m_ui->checkMaxUploadsPerTorrent, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
933 connect(m_ui->spinMaxConnec, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
934 connect(m_ui->spinMaxConnecPerTorrent, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
935 connect(m_ui->spinMaxUploads, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
936 connect(m_ui->spinMaxUploadsPerTorrent, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
938 connect(m_ui->comboProxyType, qComboBoxCurrentIndexChanged, this, &ThisType::adjustProxyOptions);
939 connect(m_ui->comboProxyType, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
940 connect(m_ui->textProxyIP, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
941 connect(m_ui->spinProxyPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
943 #if defined(QBT_USES_LIBTORRENT2) && TORRENT_USE_I2P
944 connect(m_ui->textI2PHost, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
945 connect(m_ui->spinI2PPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
946 connect(m_ui->checkI2PMixed, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
947 connect(m_ui->groupI2P, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
948 #endif
950 connect(m_ui->checkProxyBitTorrent, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
951 connect(m_ui->checkProxyBitTorrent, &QGroupBox::toggled, this, &ThisType::adjustProxyOptions);
952 connect(m_ui->checkProxyPeerConnections, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
953 connect(m_ui->checkProxyHostnameLookup, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
954 connect(m_ui->checkProxyRSS, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
955 connect(m_ui->checkProxyMisc, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
957 connect(m_ui->checkProxyAuth, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
958 connect(m_ui->textProxyUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
959 connect(m_ui->textProxyPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
961 connect(m_ui->checkIPFilter, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
962 connect(m_ui->checkIPFilter, &QAbstractButton::toggled, m_ui->textFilterPath, &QWidget::setEnabled);
963 connect(m_ui->checkIPFilter, &QAbstractButton::toggled, m_ui->IpFilterRefreshBtn, &QWidget::setEnabled);
964 connect(m_ui->textFilterPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
965 connect(m_ui->checkIpFilterTrackers, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
968 void OptionsDialog::saveConnectionTabOptions() const
970 auto *session = BitTorrent::Session::instance();
972 session->setBTProtocol(static_cast<BitTorrent::BTProtocol>(m_ui->comboProtocol->currentIndex()));
973 session->setPort(getPort());
974 Net::PortForwarder::instance()->setEnabled(isUPnPEnabled());
976 session->setMaxConnections(getMaxConnections());
977 session->setMaxConnectionsPerTorrent(getMaxConnectionsPerTorrent());
978 session->setMaxUploads(getMaxUploads());
979 session->setMaxUploadsPerTorrent(getMaxUploadsPerTorrent());
981 #if defined(QBT_USES_LIBTORRENT2) && TORRENT_USE_I2P
982 session->setI2PEnabled(m_ui->groupI2P->isChecked());
983 session->setI2PAddress(m_ui->textI2PHost->text().trimmed());
984 session->setI2PPort(m_ui->spinI2PPort->value());
985 session->setI2PMixedMode(m_ui->checkI2PMixed->isChecked());
986 #endif
988 auto *proxyConfigManager = Net::ProxyConfigurationManager::instance();
989 Net::ProxyConfiguration proxyConf;
990 proxyConf.type = getProxyType();
991 proxyConf.ip = getProxyIp();
992 proxyConf.port = getProxyPort();
993 proxyConf.authEnabled = m_ui->checkProxyAuth->isChecked();
994 proxyConf.username = getProxyUsername();
995 proxyConf.password = getProxyPassword();
996 proxyConf.hostnameLookupEnabled = m_ui->checkProxyHostnameLookup->isChecked();
997 proxyConfigManager->setProxyConfiguration(proxyConf);
999 Preferences::instance()->setUseProxyForBT(m_ui->checkProxyBitTorrent->isChecked());
1000 Preferences::instance()->setUseProxyForRSS(m_ui->checkProxyRSS->isChecked());
1001 Preferences::instance()->setUseProxyForGeneralPurposes(m_ui->checkProxyMisc->isChecked());
1003 session->setProxyPeerConnectionsEnabled(m_ui->checkProxyPeerConnections->isChecked());
1005 // IPFilter
1006 session->setIPFilteringEnabled(isIPFilteringEnabled());
1007 session->setTrackerFilteringEnabled(m_ui->checkIpFilterTrackers->isChecked());
1008 session->setIPFilterFile(m_ui->textFilterPath->selectedPath());
1011 void OptionsDialog::loadSpeedTabOptions()
1013 const auto *pref = Preferences::instance();
1014 const auto *session = BitTorrent::Session::instance();
1016 m_ui->labelGlobalRate->setPixmap(UIThemeManager::instance()->getScaledPixmap(u"slow_off"_s, Utils::Gui::mediumIconSize(this).height()));
1017 m_ui->spinUploadLimit->setValue(session->globalUploadSpeedLimit() / 1024);
1018 m_ui->spinDownloadLimit->setValue(session->globalDownloadSpeedLimit() / 1024);
1020 m_ui->labelAltRate->setPixmap(UIThemeManager::instance()->getScaledPixmap(u"slow"_s, Utils::Gui::mediumIconSize(this).height()));
1021 m_ui->spinUploadLimitAlt->setValue(session->altGlobalUploadSpeedLimit() / 1024);
1022 m_ui->spinDownloadLimitAlt->setValue(session->altGlobalDownloadSpeedLimit() / 1024);
1024 m_ui->comboBoxScheduleDays->addItems(translatedWeekdayNames());
1026 m_ui->groupBoxSchedule->setChecked(session->isBandwidthSchedulerEnabled());
1027 m_ui->timeEditScheduleFrom->setTime(pref->getSchedulerStartTime());
1028 m_ui->timeEditScheduleTo->setTime(pref->getSchedulerEndTime());
1029 m_ui->comboBoxScheduleDays->setCurrentIndex(static_cast<int>(pref->getSchedulerDays()));
1031 m_ui->checkLimituTPConnections->setChecked(session->isUTPRateLimited());
1032 m_ui->checkLimitTransportOverhead->setChecked(session->includeOverheadInLimits());
1033 m_ui->checkLimitLocalPeerRate->setChecked(!session->ignoreLimitsOnLAN());
1035 connect(m_ui->spinUploadLimit, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1036 connect(m_ui->spinDownloadLimit, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1038 connect(m_ui->spinUploadLimitAlt, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1039 connect(m_ui->spinDownloadLimitAlt, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1041 connect(m_ui->groupBoxSchedule, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
1042 connect(m_ui->timeEditScheduleFrom, &QDateTimeEdit::timeChanged, this, &ThisType::enableApplyButton);
1043 connect(m_ui->timeEditScheduleTo, &QDateTimeEdit::timeChanged, this, &ThisType::enableApplyButton);
1044 connect(m_ui->comboBoxScheduleDays, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
1046 connect(m_ui->checkLimituTPConnections, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1047 connect(m_ui->checkLimitTransportOverhead, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1048 connect(m_ui->checkLimitLocalPeerRate, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1051 void OptionsDialog::saveSpeedTabOptions() const
1053 auto *pref = Preferences::instance();
1054 auto *session = BitTorrent::Session::instance();
1056 session->setGlobalUploadSpeedLimit(m_ui->spinUploadLimit->value() * 1024);
1057 session->setGlobalDownloadSpeedLimit(m_ui->spinDownloadLimit->value() * 1024);
1059 session->setAltGlobalUploadSpeedLimit(m_ui->spinUploadLimitAlt->value() * 1024);
1060 session->setAltGlobalDownloadSpeedLimit(m_ui->spinDownloadLimitAlt->value() * 1024);
1062 session->setBandwidthSchedulerEnabled(m_ui->groupBoxSchedule->isChecked());
1063 pref->setSchedulerStartTime(m_ui->timeEditScheduleFrom->time());
1064 pref->setSchedulerEndTime(m_ui->timeEditScheduleTo->time());
1065 pref->setSchedulerDays(static_cast<Scheduler::Days>(m_ui->comboBoxScheduleDays->currentIndex()));
1067 session->setUTPRateLimited(m_ui->checkLimituTPConnections->isChecked());
1068 session->setIncludeOverheadInLimits(m_ui->checkLimitTransportOverhead->isChecked());
1069 session->setIgnoreLimitsOnLAN(!m_ui->checkLimitLocalPeerRate->isChecked());
1072 void OptionsDialog::loadBittorrentTabOptions()
1074 const auto *session = BitTorrent::Session::instance();
1076 m_ui->checkDHT->setChecked(session->isDHTEnabled());
1077 m_ui->checkPeX->setChecked(session->isPeXEnabled());
1078 m_ui->checkLSD->setChecked(session->isLSDEnabled());
1079 m_ui->comboEncryption->setCurrentIndex(session->encryption());
1080 m_ui->checkAnonymousMode->setChecked(session->isAnonymousModeEnabled());
1082 m_ui->spinBoxMaxActiveCheckingTorrents->setValue(session->maxActiveCheckingTorrents());
1084 m_ui->checkEnableQueueing->setChecked(session->isQueueingSystemEnabled());
1085 m_ui->spinMaxActiveDownloads->setValue(session->maxActiveDownloads());
1086 m_ui->spinMaxActiveUploads->setValue(session->maxActiveUploads());
1087 m_ui->spinMaxActiveTorrents->setValue(session->maxActiveTorrents());
1089 m_ui->checkIgnoreSlowTorrentsForQueueing->setChecked(session->ignoreSlowTorrentsForQueueing());
1090 const QString slowTorrentsExplanation = u"<html><body><p>"
1091 + tr("A torrent will be considered slow if its download and upload rates stay below these values for \"Torrent inactivity timer\" seconds")
1092 + u"</p></body></html>";
1093 m_ui->labelDownloadRateForSlowTorrents->setToolTip(slowTorrentsExplanation);
1094 m_ui->labelUploadRateForSlowTorrents->setToolTip(slowTorrentsExplanation);
1095 m_ui->labelSlowTorrentInactivityTimer->setToolTip(slowTorrentsExplanation);
1096 m_ui->spinDownloadRateForSlowTorrents->setValue(session->downloadRateForSlowTorrents());
1097 m_ui->spinUploadRateForSlowTorrents->setValue(session->uploadRateForSlowTorrents());
1098 m_ui->spinSlowTorrentsInactivityTimer->setValue(session->slowTorrentsInactivityTimer());
1100 if (session->globalMaxRatio() >= 0.)
1102 // Enable
1103 m_ui->checkMaxRatio->setChecked(true);
1104 m_ui->spinMaxRatio->setEnabled(true);
1105 m_ui->comboRatioLimitAct->setEnabled(true);
1106 m_ui->spinMaxRatio->setValue(session->globalMaxRatio());
1108 else
1110 // Disable
1111 m_ui->checkMaxRatio->setChecked(false);
1112 m_ui->spinMaxRatio->setEnabled(false);
1114 if (session->globalMaxSeedingMinutes() >= 0)
1116 // Enable
1117 m_ui->checkMaxSeedingMinutes->setChecked(true);
1118 m_ui->spinMaxSeedingMinutes->setEnabled(true);
1119 m_ui->spinMaxSeedingMinutes->setValue(session->globalMaxSeedingMinutes());
1121 else
1123 // Disable
1124 m_ui->checkMaxSeedingMinutes->setChecked(false);
1125 m_ui->spinMaxSeedingMinutes->setEnabled(false);
1127 if (session->globalMaxInactiveSeedingMinutes() >= 0)
1129 // Enable
1130 m_ui->checkMaxInactiveSeedingMinutes->setChecked(true);
1131 m_ui->spinMaxInactiveSeedingMinutes->setEnabled(true);
1132 m_ui->spinMaxInactiveSeedingMinutes->setValue(session->globalMaxInactiveSeedingMinutes());
1134 else
1136 // Disable
1137 m_ui->checkMaxInactiveSeedingMinutes->setChecked(false);
1138 m_ui->spinMaxInactiveSeedingMinutes->setEnabled(false);
1140 m_ui->comboRatioLimitAct->setEnabled((session->globalMaxSeedingMinutes() >= 0) || (session->globalMaxRatio() >= 0.) || (session->globalMaxInactiveSeedingMinutes() >= 0));
1142 const QHash<BitTorrent::ShareLimitAction, int> actIndex =
1144 {BitTorrent::ShareLimitAction::Stop, 0},
1145 {BitTorrent::ShareLimitAction::Remove, 1},
1146 {BitTorrent::ShareLimitAction::RemoveWithContent, 2},
1147 {BitTorrent::ShareLimitAction::EnableSuperSeeding, 3}
1149 m_ui->comboRatioLimitAct->setCurrentIndex(actIndex.value(session->shareLimitAction()));
1151 m_ui->checkEnableAddTrackers->setChecked(session->isAddTrackersEnabled());
1152 m_ui->textTrackers->setPlainText(session->additionalTrackers());
1154 connect(m_ui->checkDHT, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1155 connect(m_ui->checkPeX, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1156 connect(m_ui->checkLSD, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1157 connect(m_ui->comboEncryption, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
1158 connect(m_ui->checkAnonymousMode, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1160 connect(m_ui->spinBoxMaxActiveCheckingTorrents, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1162 connect(m_ui->checkEnableQueueing, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
1163 connect(m_ui->spinMaxActiveDownloads, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1164 connect(m_ui->spinMaxActiveUploads, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1165 connect(m_ui->spinMaxActiveTorrents, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1166 connect(m_ui->checkIgnoreSlowTorrentsForQueueing, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
1167 connect(m_ui->spinDownloadRateForSlowTorrents, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1168 connect(m_ui->spinUploadRateForSlowTorrents, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1169 connect(m_ui->spinSlowTorrentsInactivityTimer, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1171 connect(m_ui->checkMaxRatio, &QAbstractButton::toggled, m_ui->spinMaxRatio, &QWidget::setEnabled);
1172 connect(m_ui->checkMaxRatio, &QAbstractButton::toggled, this, &ThisType::toggleComboRatioLimitAct);
1173 connect(m_ui->checkMaxRatio, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1174 connect(m_ui->spinMaxRatio, qOverload<double>(&QDoubleSpinBox::valueChanged),this, &ThisType::enableApplyButton);
1175 connect(m_ui->comboRatioLimitAct, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
1176 connect(m_ui->checkMaxSeedingMinutes, &QAbstractButton::toggled, m_ui->spinMaxSeedingMinutes, &QWidget::setEnabled);
1177 connect(m_ui->checkMaxSeedingMinutes, &QAbstractButton::toggled, this, &ThisType::toggleComboRatioLimitAct);
1178 connect(m_ui->checkMaxSeedingMinutes, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1179 connect(m_ui->spinMaxSeedingMinutes, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1180 connect(m_ui->checkMaxInactiveSeedingMinutes, &QAbstractButton::toggled, m_ui->spinMaxInactiveSeedingMinutes, &QWidget::setEnabled);
1181 connect(m_ui->checkMaxInactiveSeedingMinutes, &QAbstractButton::toggled, this, &ThisType::toggleComboRatioLimitAct);
1182 connect(m_ui->checkMaxInactiveSeedingMinutes, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1183 connect(m_ui->spinMaxInactiveSeedingMinutes, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1185 connect(m_ui->checkEnableAddTrackers, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
1186 connect(m_ui->textTrackers, &QPlainTextEdit::textChanged, this, &ThisType::enableApplyButton);
1189 void OptionsDialog::saveBittorrentTabOptions() const
1191 auto *session = BitTorrent::Session::instance();
1193 session->setDHTEnabled(isDHTEnabled());
1194 session->setPeXEnabled(m_ui->checkPeX->isChecked());
1195 session->setLSDEnabled(isLSDEnabled());
1196 session->setEncryption(getEncryptionSetting());
1197 session->setAnonymousModeEnabled(m_ui->checkAnonymousMode->isChecked());
1199 session->setMaxActiveCheckingTorrents(m_ui->spinBoxMaxActiveCheckingTorrents->value());
1200 // Queueing system
1201 session->setQueueingSystemEnabled(isQueueingSystemEnabled());
1202 session->setMaxActiveDownloads(m_ui->spinMaxActiveDownloads->value());
1203 session->setMaxActiveUploads(m_ui->spinMaxActiveUploads->value());
1204 session->setMaxActiveTorrents(m_ui->spinMaxActiveTorrents->value());
1205 session->setIgnoreSlowTorrentsForQueueing(m_ui->checkIgnoreSlowTorrentsForQueueing->isChecked());
1206 session->setDownloadRateForSlowTorrents(m_ui->spinDownloadRateForSlowTorrents->value());
1207 session->setUploadRateForSlowTorrents(m_ui->spinUploadRateForSlowTorrents->value());
1208 session->setSlowTorrentsInactivityTimer(m_ui->spinSlowTorrentsInactivityTimer->value());
1210 session->setGlobalMaxRatio(getMaxRatio());
1211 session->setGlobalMaxSeedingMinutes(getMaxSeedingMinutes());
1212 session->setGlobalMaxInactiveSeedingMinutes(getMaxInactiveSeedingMinutes());
1213 const QList<BitTorrent::ShareLimitAction> actIndex =
1215 BitTorrent::ShareLimitAction::Stop,
1216 BitTorrent::ShareLimitAction::Remove,
1217 BitTorrent::ShareLimitAction::RemoveWithContent,
1218 BitTorrent::ShareLimitAction::EnableSuperSeeding
1220 session->setShareLimitAction(actIndex.value(m_ui->comboRatioLimitAct->currentIndex()));
1222 session->setAddTrackersEnabled(m_ui->checkEnableAddTrackers->isChecked());
1223 session->setAdditionalTrackers(m_ui->textTrackers->toPlainText());
1226 void OptionsDialog::loadRSSTabOptions()
1228 const auto *rssSession = RSS::Session::instance();
1229 const auto *autoDownloader = RSS::AutoDownloader::instance();
1231 m_ui->checkRSSEnable->setChecked(rssSession->isProcessingEnabled());
1232 m_ui->spinRSSRefreshInterval->setValue(rssSession->refreshInterval());
1233 m_ui->spinRSSFetchDelay->setValue(rssSession->fetchDelay().count());
1234 m_ui->spinRSSMaxArticlesPerFeed->setValue(rssSession->maxArticlesPerFeed());
1235 m_ui->checkRSSAutoDownloaderEnable->setChecked(autoDownloader->isProcessingEnabled());
1236 m_ui->textSmartEpisodeFilters->setPlainText(autoDownloader->smartEpisodeFilters().join(u'\n'));
1237 m_ui->checkSmartFilterDownloadRepacks->setChecked(autoDownloader->downloadRepacks());
1239 connect(m_ui->checkRSSEnable, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton);
1240 connect(m_ui->checkRSSAutoDownloaderEnable, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton);
1241 connect(m_ui->btnEditRules, &QPushButton::clicked, this, [this]()
1243 auto *downloader = new AutomatedRssDownloader(this);
1244 downloader->setAttribute(Qt::WA_DeleteOnClose);
1245 downloader->open();
1247 connect(m_ui->textSmartEpisodeFilters, &QPlainTextEdit::textChanged, this, &OptionsDialog::enableApplyButton);
1248 connect(m_ui->checkSmartFilterDownloadRepacks, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton);
1249 connect(m_ui->spinRSSRefreshInterval, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton);
1250 connect(m_ui->spinRSSFetchDelay, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton);
1251 connect(m_ui->spinRSSMaxArticlesPerFeed, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton);
1254 void OptionsDialog::saveRSSTabOptions() const
1256 auto *rssSession = RSS::Session::instance();
1257 auto *autoDownloader = RSS::AutoDownloader::instance();
1259 rssSession->setProcessingEnabled(m_ui->checkRSSEnable->isChecked());
1260 rssSession->setRefreshInterval(m_ui->spinRSSRefreshInterval->value());
1261 rssSession->setFetchDelay(std::chrono::seconds(m_ui->spinRSSFetchDelay->value()));
1262 rssSession->setMaxArticlesPerFeed(m_ui->spinRSSMaxArticlesPerFeed->value());
1263 autoDownloader->setProcessingEnabled(m_ui->checkRSSAutoDownloaderEnable->isChecked());
1264 autoDownloader->setSmartEpisodeFilters(m_ui->textSmartEpisodeFilters->toPlainText().split(u'\n', Qt::SkipEmptyParts));
1265 autoDownloader->setDownloadRepacks(m_ui->checkSmartFilterDownloadRepacks->isChecked());
1268 #ifndef DISABLE_WEBUI
1269 void OptionsDialog::loadWebUITabOptions()
1271 const auto *pref = Preferences::instance();
1273 m_ui->textWebUIHttpsCert->setMode(FileSystemPathEdit::Mode::FileOpen);
1274 m_ui->textWebUIHttpsCert->setFileNameFilter(tr("Certificate") + u" (*.cer *.crt *.pem)");
1275 m_ui->textWebUIHttpsCert->setDialogCaption(tr("Select certificate"));
1276 m_ui->textWebUIHttpsKey->setMode(FileSystemPathEdit::Mode::FileOpen);
1277 m_ui->textWebUIHttpsKey->setFileNameFilter(tr("Private key") + u" (*.key *.pem)");
1278 m_ui->textWebUIHttpsKey->setDialogCaption(tr("Select private key"));
1279 m_ui->textWebUIRootFolder->setMode(FileSystemPathEdit::Mode::DirectoryOpen);
1280 m_ui->textWebUIRootFolder->setDialogCaption(tr("Choose Alternative UI files location"));
1282 if (app()->webUI()->isErrored())
1283 m_ui->labelWebUIError->setText(tr("WebUI configuration failed. Reason: %1").arg(app()->webUI()->errorMessage()));
1284 else
1285 m_ui->labelWebUIError->hide();
1287 m_ui->checkWebUI->setChecked(pref->isWebUIEnabled());
1288 m_ui->textWebUIAddress->setText(pref->getWebUIAddress());
1289 m_ui->spinWebUIPort->setValue(pref->getWebUIPort());
1290 m_ui->checkWebUIUPnP->setChecked(pref->useUPnPForWebUIPort());
1291 m_ui->checkWebUIHttps->setChecked(pref->isWebUIHttpsEnabled());
1292 webUIHttpsCertChanged(pref->getWebUIHttpsCertificatePath());
1293 webUIHttpsKeyChanged(pref->getWebUIHttpsKeyPath());
1294 m_ui->textWebUIUsername->setText(pref->getWebUIUsername());
1295 m_ui->checkBypassLocalAuth->setChecked(!pref->isWebUILocalAuthEnabled());
1296 m_ui->checkBypassAuthSubnetWhitelist->setChecked(pref->isWebUIAuthSubnetWhitelistEnabled());
1297 m_ui->IPSubnetWhitelistButton->setEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked());
1298 m_ui->spinBanCounter->setValue(pref->getWebUIMaxAuthFailCount());
1299 m_ui->spinBanDuration->setValue(pref->getWebUIBanDuration().count());
1300 m_ui->spinSessionTimeout->setValue(pref->getWebUISessionTimeout());
1301 // Alternative UI
1302 m_ui->groupAltWebUI->setChecked(pref->isAltWebUIEnabled());
1303 m_ui->textWebUIRootFolder->setSelectedPath(pref->getWebUIRootFolder());
1304 // Security
1305 m_ui->checkClickjacking->setChecked(pref->isWebUIClickjackingProtectionEnabled());
1306 m_ui->checkCSRFProtection->setChecked(pref->isWebUICSRFProtectionEnabled());
1307 m_ui->checkSecureCookie->setChecked(pref->isWebUISecureCookieEnabled());
1308 m_ui->groupHostHeaderValidation->setChecked(pref->isWebUIHostHeaderValidationEnabled());
1309 m_ui->textServerDomains->setText(pref->getServerDomains());
1310 // Custom HTTP headers
1311 m_ui->groupWebUIAddCustomHTTPHeaders->setChecked(pref->isWebUICustomHTTPHeadersEnabled());
1312 m_ui->textWebUICustomHTTPHeaders->setPlainText(pref->getWebUICustomHTTPHeaders());
1313 // Reverse proxy
1314 m_ui->groupEnableReverseProxySupport->setChecked(pref->isWebUIReverseProxySupportEnabled());
1315 m_ui->textTrustedReverseProxiesList->setText(pref->getWebUITrustedReverseProxiesList());
1316 // DynDNS
1317 m_ui->checkDynDNS->setChecked(pref->isDynDNSEnabled());
1318 m_ui->comboDNSService->setCurrentIndex(static_cast<int>(pref->getDynDNSService()));
1319 m_ui->domainNameTxt->setText(pref->getDynDomainName());
1320 m_ui->DNSUsernameTxt->setText(pref->getDynDNSUsername());
1321 m_ui->DNSPasswordTxt->setText(pref->getDynDNSPassword());
1323 connect(m_ui->checkWebUI, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
1324 connect(m_ui->textWebUIAddress, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
1325 connect(m_ui->spinWebUIPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1326 connect(m_ui->checkWebUIUPnP, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1327 connect(m_ui->checkWebUIHttps, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
1328 connect(m_ui->textWebUIHttpsCert, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
1329 connect(m_ui->textWebUIHttpsCert, &FileSystemPathLineEdit::selectedPathChanged, this, &OptionsDialog::webUIHttpsCertChanged);
1330 connect(m_ui->textWebUIHttpsKey, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
1331 connect(m_ui->textWebUIHttpsKey, &FileSystemPathLineEdit::selectedPathChanged, this, &OptionsDialog::webUIHttpsKeyChanged);
1333 connect(m_ui->textWebUIUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
1334 connect(m_ui->textWebUIPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
1336 connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1337 connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
1338 connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, m_ui->IPSubnetWhitelistButton, &QWidget::setEnabled);
1339 connect(m_ui->spinBanCounter, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1340 connect(m_ui->spinBanDuration, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1341 connect(m_ui->spinSessionTimeout, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
1343 connect(m_ui->groupAltWebUI, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
1344 connect(m_ui->textWebUIRootFolder, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
1346 connect(m_ui->checkClickjacking, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
1347 connect(m_ui->checkCSRFProtection, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
1348 connect(m_ui->checkSecureCookie, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
1349 connect(m_ui->groupHostHeaderValidation, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
1350 connect(m_ui->textServerDomains, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
1352 connect(m_ui->groupWebUIAddCustomHTTPHeaders, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
1353 connect(m_ui->textWebUICustomHTTPHeaders, &QPlainTextEdit::textChanged, this, &OptionsDialog::enableApplyButton);
1355 connect(m_ui->groupEnableReverseProxySupport, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
1356 connect(m_ui->textTrustedReverseProxiesList, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
1358 connect(m_ui->checkDynDNS, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
1359 connect(m_ui->comboDNSService, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
1360 connect(m_ui->domainNameTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
1361 connect(m_ui->DNSUsernameTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
1362 connect(m_ui->DNSPasswordTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
1365 void OptionsDialog::saveWebUITabOptions() const
1367 auto *pref = Preferences::instance();
1369 const bool webUIEnabled = isWebUIEnabled();
1371 pref->setWebUIEnabled(webUIEnabled);
1372 pref->setWebUIAddress(m_ui->textWebUIAddress->text());
1373 pref->setWebUIPort(m_ui->spinWebUIPort->value());
1374 pref->setUPnPForWebUIPort(m_ui->checkWebUIUPnP->isChecked());
1375 pref->setWebUIHttpsEnabled(m_ui->checkWebUIHttps->isChecked());
1376 pref->setWebUIHttpsCertificatePath(m_ui->textWebUIHttpsCert->selectedPath());
1377 pref->setWebUIHttpsKeyPath(m_ui->textWebUIHttpsKey->selectedPath());
1378 pref->setWebUIMaxAuthFailCount(m_ui->spinBanCounter->value());
1379 pref->setWebUIBanDuration(std::chrono::seconds {m_ui->spinBanDuration->value()});
1380 pref->setWebUISessionTimeout(m_ui->spinSessionTimeout->value());
1381 // Authentication
1382 if (const QString username = webUIUsername(); isValidWebUIUsername(username))
1383 pref->setWebUIUsername(username);
1384 if (const QString password = webUIPassword(); isValidWebUIPassword(password))
1385 pref->setWebUIPassword(Utils::Password::PBKDF2::generate(password));
1386 pref->setWebUILocalAuthEnabled(!m_ui->checkBypassLocalAuth->isChecked());
1387 pref->setWebUIAuthSubnetWhitelistEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked());
1388 // Alternative UI
1389 pref->setAltWebUIEnabled(m_ui->groupAltWebUI->isChecked());
1390 pref->setWebUIRootFolder(m_ui->textWebUIRootFolder->selectedPath());
1391 // Security
1392 pref->setWebUIClickjackingProtectionEnabled(m_ui->checkClickjacking->isChecked());
1393 pref->setWebUICSRFProtectionEnabled(m_ui->checkCSRFProtection->isChecked());
1394 pref->setWebUISecureCookieEnabled(m_ui->checkSecureCookie->isChecked());
1395 pref->setWebUIHostHeaderValidationEnabled(m_ui->groupHostHeaderValidation->isChecked());
1396 pref->setServerDomains(m_ui->textServerDomains->text());
1397 // Custom HTTP headers
1398 pref->setWebUICustomHTTPHeadersEnabled(m_ui->groupWebUIAddCustomHTTPHeaders->isChecked());
1399 pref->setWebUICustomHTTPHeaders(m_ui->textWebUICustomHTTPHeaders->toPlainText());
1400 // Reverse proxy
1401 pref->setWebUIReverseProxySupportEnabled(m_ui->groupEnableReverseProxySupport->isChecked());
1402 pref->setWebUITrustedReverseProxiesList(m_ui->textTrustedReverseProxiesList->text());
1403 // DynDNS
1404 pref->setDynDNSEnabled(m_ui->checkDynDNS->isChecked());
1405 pref->setDynDNSService(static_cast<DNS::Service>(m_ui->comboDNSService->currentIndex()));
1406 pref->setDynDomainName(m_ui->domainNameTxt->text());
1407 pref->setDynDNSUsername(m_ui->DNSUsernameTxt->text());
1408 pref->setDynDNSPassword(m_ui->DNSPasswordTxt->text());
1410 #endif // DISABLE_WEBUI
1412 void OptionsDialog::initializeLanguageCombo()
1414 // List language files
1415 const QStringList langFiles = QDir(u":/lang"_s).entryList({u"qbittorrent_*.qm"_s}, QDir::Files, QDir::Name);
1416 for (const QString &langFile : langFiles)
1418 const QString langCode = QStringView(langFile).sliced(12).chopped(3).toString(); // remove "qbittorrent_" and ".qm"
1419 m_ui->comboLanguage->addItem(Utils::Misc::languageToLocalizedString(langCode), langCode);
1423 void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
1425 if (!current)
1426 current = previous;
1427 m_ui->tabOption->setCurrentIndex(m_ui->tabSelection->row(current));
1430 void OptionsDialog::loadSplitterState()
1432 // width has been modified, use height as width reference instead
1433 const int width = m_ui->tabSelection->item(TAB_UI)->sizeHint().height() * 2;
1434 const QStringList defaultSizes = {QString::number(width), QString::number(m_ui->hsplitter->width() - width)};
1436 QList<int> splitterSizes;
1437 for (const QString &string : asConst(m_storeHSplitterSize.get(defaultSizes)))
1438 splitterSizes.append(string.toInt());
1440 m_ui->hsplitter->setSizes(splitterSizes);
1443 void OptionsDialog::showEvent(QShowEvent *e)
1445 QDialog::showEvent(e);
1447 loadSplitterState();
1450 void OptionsDialog::saveOptions() const
1452 auto *pref = Preferences::instance();
1454 saveBehaviorTabOptions();
1455 saveDownloadsTabOptions();
1456 saveConnectionTabOptions();
1457 saveSpeedTabOptions();
1458 saveBittorrentTabOptions();
1459 saveRSSTabOptions();
1460 #ifndef DISABLE_WEBUI
1461 saveWebUITabOptions();
1462 #endif
1463 m_advancedSettings->saveAdvancedSettings();
1465 // Assume that user changed multiple settings
1466 // so it's best to save immediately
1467 pref->apply();
1470 bool OptionsDialog::isIPFilteringEnabled() const
1472 return m_ui->checkIPFilter->isChecked();
1475 Net::ProxyType OptionsDialog::getProxyType() const
1477 return m_ui->comboProxyType->currentData().value<Net::ProxyType>();
1480 int OptionsDialog::getPort() const
1482 return m_ui->spinPort->value();
1485 void OptionsDialog::on_randomButton_clicked()
1487 // Range [1024: 65535]
1488 m_ui->spinPort->setValue(Utils::Random::rand(1024, 65535));
1491 int OptionsDialog::getEncryptionSetting() const
1493 return m_ui->comboEncryption->currentIndex();
1496 int OptionsDialog::getMaxActiveDownloads() const
1498 return m_ui->spinMaxActiveDownloads->value();
1501 int OptionsDialog::getMaxActiveUploads() const
1503 return m_ui->spinMaxActiveUploads->value();
1506 int OptionsDialog::getMaxActiveTorrents() const
1508 return m_ui->spinMaxActiveTorrents->value();
1511 bool OptionsDialog::isQueueingSystemEnabled() const
1513 return m_ui->checkEnableQueueing->isChecked();
1516 bool OptionsDialog::isDHTEnabled() const
1518 return m_ui->checkDHT->isChecked();
1521 bool OptionsDialog::isLSDEnabled() const
1523 return m_ui->checkLSD->isChecked();
1526 bool OptionsDialog::isUPnPEnabled() const
1528 return m_ui->checkUPnP->isChecked();
1531 // Return Share ratio
1532 qreal OptionsDialog::getMaxRatio() const
1534 if (m_ui->checkMaxRatio->isChecked())
1535 return m_ui->spinMaxRatio->value();
1536 return -1;
1539 // Return Seeding Minutes
1540 int OptionsDialog::getMaxSeedingMinutes() const
1542 if (m_ui->checkMaxSeedingMinutes->isChecked())
1543 return m_ui->spinMaxSeedingMinutes->value();
1544 return -1;
1547 // Return Inactive Seeding Minutes
1548 int OptionsDialog::getMaxInactiveSeedingMinutes() const
1550 return m_ui->checkMaxInactiveSeedingMinutes->isChecked()
1551 ? m_ui->spinMaxInactiveSeedingMinutes->value()
1552 : -1;
1555 // Return max connections number
1556 int OptionsDialog::getMaxConnections() const
1558 if (!m_ui->checkMaxConnections->isChecked())
1559 return -1;
1561 return m_ui->spinMaxConnec->value();
1564 int OptionsDialog::getMaxConnectionsPerTorrent() const
1566 if (!m_ui->checkMaxConnectionsPerTorrent->isChecked())
1567 return -1;
1569 return m_ui->spinMaxConnecPerTorrent->value();
1572 int OptionsDialog::getMaxUploads() const
1574 if (!m_ui->checkMaxUploads->isChecked())
1575 return -1;
1577 return m_ui->spinMaxUploads->value();
1580 int OptionsDialog::getMaxUploadsPerTorrent() const
1582 if (!m_ui->checkMaxUploadsPerTorrent->isChecked())
1583 return -1;
1585 return m_ui->spinMaxUploadsPerTorrent->value();
1588 void OptionsDialog::on_buttonBox_accepted()
1590 if (m_applyButton->isEnabled())
1592 if (!applySettings())
1593 return;
1595 m_applyButton->setEnabled(false);
1598 accept();
1601 bool OptionsDialog::applySettings()
1603 if (!schedTimesOk())
1605 m_ui->tabSelection->setCurrentRow(TAB_SPEED);
1606 return false;
1608 #ifndef DISABLE_WEBUI
1609 if (isWebUIEnabled() && !webUIAuthenticationOk())
1611 m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
1612 return false;
1614 if (!isAlternativeWebUIPathValid())
1616 m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
1617 return false;
1619 #endif
1621 saveOptions();
1622 return true;
1625 void OptionsDialog::on_buttonBox_rejected()
1627 reject();
1630 bool OptionsDialog::useAdditionDialog() const
1632 return m_ui->checkAdditionDialog->isChecked();
1635 void OptionsDialog::enableApplyButton()
1637 m_applyButton->setEnabled(true);
1640 void OptionsDialog::toggleComboRatioLimitAct()
1642 // Verify if the share action button must be enabled
1643 m_ui->comboRatioLimitAct->setEnabled(m_ui->checkMaxRatio->isChecked() || m_ui->checkMaxSeedingMinutes->isChecked() || m_ui->checkMaxInactiveSeedingMinutes->isChecked());
1646 void OptionsDialog::adjustProxyOptions()
1648 const auto currentProxyType = m_ui->comboProxyType->currentData().value<Net::ProxyType>();
1649 const bool isAuthSupported = ((currentProxyType == Net::ProxyType::SOCKS5)
1650 || (currentProxyType == Net::ProxyType::HTTP));
1652 m_ui->checkProxyAuth->setEnabled(isAuthSupported);
1654 if (currentProxyType == Net::ProxyType::None)
1656 m_ui->labelProxyTypeIncompatible->setVisible(false);
1658 m_ui->lblProxyIP->setEnabled(false);
1659 m_ui->textProxyIP->setEnabled(false);
1660 m_ui->lblProxyPort->setEnabled(false);
1661 m_ui->spinProxyPort->setEnabled(false);
1663 m_ui->checkProxyHostnameLookup->setEnabled(false);
1664 m_ui->checkProxyRSS->setEnabled(false);
1665 m_ui->checkProxyMisc->setEnabled(false);
1666 m_ui->checkProxyBitTorrent->setEnabled(false);
1667 m_ui->checkProxyPeerConnections->setEnabled(false);
1669 else
1671 m_ui->lblProxyIP->setEnabled(true);
1672 m_ui->textProxyIP->setEnabled(true);
1673 m_ui->lblProxyPort->setEnabled(true);
1674 m_ui->spinProxyPort->setEnabled(true);
1676 m_ui->checkProxyBitTorrent->setEnabled(true);
1677 m_ui->checkProxyPeerConnections->setEnabled(true);
1679 if (currentProxyType == Net::ProxyType::SOCKS4)
1681 m_ui->labelProxyTypeIncompatible->setVisible(true);
1683 m_ui->checkProxyHostnameLookup->setEnabled(false);
1684 m_ui->checkProxyRSS->setEnabled(false);
1685 m_ui->checkProxyMisc->setEnabled(false);
1687 else
1689 // SOCKS5 or HTTP
1690 m_ui->labelProxyTypeIncompatible->setVisible(false);
1692 m_ui->checkProxyHostnameLookup->setEnabled(true);
1693 m_ui->checkProxyRSS->setEnabled(true);
1694 m_ui->checkProxyMisc->setEnabled(true);
1699 bool OptionsDialog::isSplashScreenDisabled() const
1701 return !m_ui->checkShowSplash->isChecked();
1704 void OptionsDialog::initializeStyleCombo()
1706 #ifdef Q_OS_WIN
1707 m_ui->labelStyleHint->setText(tr("%1 is recommended for best compatibility with Windows dark mode"
1708 , "Fusion is recommended for best compatibility with Windows dark mode").arg(u"Fusion"_s));
1709 m_ui->comboStyle->addItem(tr("System", "System default Qt style"), u"system"_s);
1710 m_ui->comboStyle->setItemData(0, tr("Let Qt decide the style for this system"), Qt::ToolTipRole);
1711 m_ui->comboStyle->insertSeparator(1);
1713 QStringList styleNames = QStyleFactory::keys();
1714 std::sort(styleNames.begin(), styleNames.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
1715 m_ui->comboStyle->addItems(styleNames);
1717 const QString prefStyleName = Preferences::instance()->getStyle();
1718 const QString selectedStyleName = prefStyleName.isEmpty() ? QApplication::style()->name() : prefStyleName;
1719 m_ui->comboStyle->setCurrentIndex(m_ui->comboStyle->findText(selectedStyleName, Qt::MatchFixedString));
1720 #else
1721 m_ui->labelStyle->hide();
1722 m_ui->comboStyle->hide();
1723 m_ui->labelStyleHint->hide();
1724 m_ui->UISettingsBoxLayout->removeWidget(m_ui->labelStyle);
1725 m_ui->UISettingsBoxLayout->removeWidget(m_ui->comboStyle);
1726 m_ui->UISettingsBoxLayout->removeWidget(m_ui->labelStyleHint);
1727 #endif
1730 void OptionsDialog::initializeColorSchemeOptions()
1732 #ifdef QBT_HAS_COLORSCHEME_OPTION
1733 m_ui->comboColorScheme->addItem(tr("Dark", "Dark color scheme"), QVariant::fromValue(ColorScheme::Dark));
1734 m_ui->comboColorScheme->addItem(tr("Light", "Light color scheme"), QVariant::fromValue(ColorScheme::Light));
1735 m_ui->comboColorScheme->addItem(tr("System", "System color scheme"), QVariant::fromValue(ColorScheme::System));
1736 m_ui->comboColorScheme->setCurrentIndex(m_ui->comboColorScheme->findData(QVariant::fromValue(UIThemeManager::instance()->colorScheme())));
1737 #else
1738 m_ui->labelColorScheme->hide();
1739 m_ui->comboColorScheme->hide();
1740 m_ui->UISettingsBoxLayout->removeWidget(m_ui->labelColorScheme);
1741 m_ui->UISettingsBoxLayout->removeWidget(m_ui->comboColorScheme);
1742 m_ui->UISettingsBoxLayout->removeItem(m_ui->spacerColorScheme);
1743 #endif
1746 #ifdef Q_OS_WIN
1747 bool OptionsDialog::WinStartup() const
1749 return m_ui->checkStartup->isChecked();
1751 #endif
1753 bool OptionsDialog::preAllocateAllFiles() const
1755 return m_ui->checkPreallocateAll->isChecked();
1758 bool OptionsDialog::addTorrentsStopped() const
1760 return m_ui->checkAddStopped->isChecked();
1763 // Proxy settings
1764 bool OptionsDialog::isProxyEnabled() const
1766 return m_ui->comboProxyType->currentIndex();
1769 QString OptionsDialog::getProxyIp() const
1771 return m_ui->textProxyIP->text().trimmed();
1774 unsigned short OptionsDialog::getProxyPort() const
1776 return m_ui->spinProxyPort->value();
1779 QString OptionsDialog::getProxyUsername() const
1781 QString username = m_ui->textProxyUsername->text().trimmed();
1782 return username;
1785 QString OptionsDialog::getProxyPassword() const
1787 QString password = m_ui->textProxyPassword->text();
1788 password = password.trimmed();
1789 return password;
1792 // Locale Settings
1793 QString OptionsDialog::getLocale() const
1795 return m_ui->comboLanguage->itemData(m_ui->comboLanguage->currentIndex(), Qt::UserRole).toString();
1798 void OptionsDialog::setLocale(const QString &localeStr)
1800 QString name;
1801 if (localeStr.startsWith(u"eo", Qt::CaseInsensitive))
1803 name = u"eo"_s;
1805 else if (localeStr.startsWith(u"ltg", Qt::CaseInsensitive))
1807 name = u"ltg"_s;
1809 else
1811 QLocale locale(localeStr);
1812 if (locale.language() == QLocale::Uzbek)
1813 name = u"uz@Latn"_s;
1814 else if (locale.language() == QLocale::Azerbaijani)
1815 name = u"az@latin"_s;
1816 else
1817 name = locale.name();
1819 // Attempt to find exact match
1820 int index = m_ui->comboLanguage->findData(name, Qt::UserRole);
1821 if (index < 0)
1823 //Attempt to find a language match without a country
1824 int pos = name.indexOf(u'_');
1825 if (pos > -1)
1827 QString lang = name.left(pos);
1828 index = m_ui->comboLanguage->findData(lang, Qt::UserRole);
1831 if (index < 0)
1833 // Unrecognized, use US English
1834 index = m_ui->comboLanguage->findData(u"en"_s, Qt::UserRole);
1835 Q_ASSERT(index >= 0);
1837 m_ui->comboLanguage->setCurrentIndex(index);
1840 Path OptionsDialog::getTorrentExportDir() const
1842 if (m_ui->checkExportDir->isChecked())
1843 return m_ui->textExportDir->selectedPath();
1844 return {};
1847 Path OptionsDialog::getFinishedTorrentExportDir() const
1849 if (m_ui->checkExportDirFin->isChecked())
1850 return m_ui->textExportDirFin->selectedPath();
1851 return {};
1854 void OptionsDialog::on_addWatchedFolderButton_clicked()
1856 Preferences *const pref = Preferences::instance();
1857 const Path dir {QFileDialog::getExistingDirectory(
1858 this, tr("Select folder to monitor"), pref->getScanDirsLastPath().parentPath().toString())};
1859 if (dir.isEmpty())
1860 return;
1862 auto *dialog = new WatchedFolderOptionsDialog({}, this);
1863 dialog->setAttribute(Qt::WA_DeleteOnClose);
1864 connect(dialog, &QDialog::accepted, this, [this, dialog, dir, pref]()
1868 auto *watchedFoldersModel = static_cast<WatchedFoldersModel *>(m_ui->scanFoldersView->model());
1869 watchedFoldersModel->addFolder(dir, dialog->watchedFolderOptions());
1871 pref->setScanDirsLastPath(dir);
1873 for (int i = 0; i < watchedFoldersModel->columnCount(); ++i)
1874 m_ui->scanFoldersView->resizeColumnToContents(i);
1876 enableApplyButton();
1878 catch (const RuntimeError &err)
1880 QMessageBox::critical(this, tr("Adding entry failed"), err.message());
1884 dialog->open();
1887 void OptionsDialog::on_editWatchedFolderButton_clicked()
1889 const QModelIndex selected
1890 = m_ui->scanFoldersView->selectionModel()->selectedIndexes().at(0);
1892 editWatchedFolderOptions(selected);
1895 void OptionsDialog::on_removeWatchedFolderButton_clicked()
1897 const QModelIndexList selected
1898 = m_ui->scanFoldersView->selectionModel()->selectedIndexes();
1900 for (const QModelIndex &index : selected)
1901 m_ui->scanFoldersView->model()->removeRow(index.row());
1904 void OptionsDialog::handleWatchedFolderViewSelectionChanged()
1906 const QModelIndexList selectedIndexes = m_ui->scanFoldersView->selectionModel()->selectedIndexes();
1907 m_ui->removeWatchedFolderButton->setEnabled(!selectedIndexes.isEmpty());
1908 m_ui->editWatchedFolderButton->setEnabled(selectedIndexes.count() == 1);
1911 void OptionsDialog::editWatchedFolderOptions(const QModelIndex &index)
1913 if (!index.isValid())
1914 return;
1916 auto *watchedFoldersModel = static_cast<WatchedFoldersModel *>(m_ui->scanFoldersView->model());
1917 auto *dialog = new WatchedFolderOptionsDialog(watchedFoldersModel->folderOptions(index.row()), this);
1918 dialog->setAttribute(Qt::WA_DeleteOnClose);
1919 connect(dialog, &QDialog::accepted, this, [this, dialog, index, watchedFoldersModel]()
1921 if (index.isValid())
1923 // The index could be invalidated while the dialog was displayed,
1924 // for example, if you deleted the folder using the Web API.
1925 watchedFoldersModel->setFolderOptions(index.row(), dialog->watchedFolderOptions());
1926 enableApplyButton();
1930 dialog->open();
1933 // Return Filter object to apply to BT session
1934 Path OptionsDialog::getFilter() const
1936 return m_ui->textFilterPath->selectedPath();
1939 #ifndef DISABLE_WEBUI
1940 void OptionsDialog::webUIHttpsCertChanged(const Path &path)
1942 const auto readResult = Utils::IO::readFile(path, Utils::Net::MAX_SSL_FILE_SIZE);
1943 const bool isCertValid = Utils::Net::isSSLCertificatesValid(readResult.value_or(QByteArray()));
1945 m_ui->textWebUIHttpsCert->setSelectedPath(path);
1946 m_ui->lblSslCertStatus->setPixmap(UIThemeManager::instance()->getScaledPixmap(
1947 (isCertValid ? u"security-high"_s : u"security-low"_s), 24));
1950 void OptionsDialog::webUIHttpsKeyChanged(const Path &path)
1952 const auto readResult = Utils::IO::readFile(path, Utils::Net::MAX_SSL_FILE_SIZE);
1953 const bool isKeyValid = !Utils::SSLKey::load(readResult.value_or(QByteArray())).isNull();
1955 m_ui->textWebUIHttpsKey->setSelectedPath(path);
1956 m_ui->lblSslKeyStatus->setPixmap(UIThemeManager::instance()->getScaledPixmap(
1957 (isKeyValid ? u"security-high"_s : u"security-low"_s), 24));
1960 bool OptionsDialog::isWebUIEnabled() const
1962 return m_ui->checkWebUI->isChecked();
1965 QString OptionsDialog::webUIUsername() const
1967 return m_ui->textWebUIUsername->text();
1970 QString OptionsDialog::webUIPassword() const
1972 return m_ui->textWebUIPassword->text();
1975 bool OptionsDialog::webUIAuthenticationOk()
1977 if (!isValidWebUIUsername(webUIUsername()))
1979 QMessageBox::warning(this, tr("Length Error"), tr("The WebUI username must be at least 3 characters long."));
1980 return false;
1983 const bool dontChangePassword = webUIPassword().isEmpty() && !Preferences::instance()->getWebUIPassword().isEmpty();
1984 if (!isValidWebUIPassword(webUIPassword()) && !dontChangePassword)
1986 QMessageBox::warning(this, tr("Length Error"), tr("The WebUI password must be at least 6 characters long."));
1987 return false;
1989 return true;
1992 bool OptionsDialog::isAlternativeWebUIPathValid()
1994 if (m_ui->groupAltWebUI->isChecked() && m_ui->textWebUIRootFolder->selectedPath().isEmpty())
1996 QMessageBox::warning(this, tr("Location Error"), tr("The alternative WebUI files location cannot be blank."));
1997 return false;
1999 return true;
2001 #endif
2003 void OptionsDialog::showConnectionTab()
2005 m_ui->tabSelection->setCurrentRow(TAB_CONNECTION);
2008 #ifndef DISABLE_WEBUI
2009 void OptionsDialog::on_registerDNSBtn_clicked()
2011 const auto service = static_cast<DNS::Service>(m_ui->comboDNSService->currentIndex());
2012 QDesktopServices::openUrl(Net::DNSUpdater::getRegistrationUrl(service));
2014 #endif
2016 void OptionsDialog::on_IpFilterRefreshBtn_clicked()
2018 if (m_refreshingIpFilter) return;
2019 m_refreshingIpFilter = true;
2020 // Updating program preferences
2021 BitTorrent::Session *const session = BitTorrent::Session::instance();
2022 session->setIPFilteringEnabled(true);
2023 session->setIPFilterFile({}); // forcing Session reload filter file
2024 session->setIPFilterFile(getFilter());
2025 connect(session, &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed);
2026 setCursor(QCursor(Qt::WaitCursor));
2029 void OptionsDialog::handleIPFilterParsed(bool error, int ruleCount)
2031 setCursor(QCursor(Qt::ArrowCursor));
2032 if (error)
2033 QMessageBox::warning(this, tr("Parsing error"), tr("Failed to parse the provided IP filter"));
2034 else
2035 QMessageBox::information(this, tr("Successfully refreshed"), tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount));
2036 m_refreshingIpFilter = false;
2037 disconnect(BitTorrent::Session::instance(), &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed);
2040 bool OptionsDialog::schedTimesOk()
2042 if (m_ui->timeEditScheduleFrom->time() == m_ui->timeEditScheduleTo->time())
2044 QMessageBox::warning(this, tr("Time Error"), tr("The start time and the end time can't be the same."));
2045 return false;
2047 return true;
2050 void OptionsDialog::on_banListButton_clicked()
2052 auto *dialog = new BanListOptionsDialog(this);
2053 dialog->setAttribute(Qt::WA_DeleteOnClose);
2054 connect(dialog, &QDialog::accepted, this, &OptionsDialog::enableApplyButton);
2055 dialog->open();
2058 void OptionsDialog::on_IPSubnetWhitelistButton_clicked()
2060 auto *dialog = new IPSubnetWhitelistOptionsDialog(this);
2061 dialog->setAttribute(Qt::WA_DeleteOnClose);
2062 connect(dialog, &QDialog::accepted, this, &OptionsDialog::enableApplyButton);
2063 dialog->open();