/* * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * In addition, as a special exception, the copyright holders give permission to * link this program with the OpenSSL project's "OpenSSL" library (or with * modified versions of it that use the same license as the "OpenSSL" library), * and distribute the linked executables. You must obey the GNU General Public * License in all respects for all of the code used other than "OpenSSL". If you * modify file(s), you may extend this exception to your version of the file(s), * but you are not obligated to do so. If you do not wish to do so, delete this * exception statement from your version. */ #include "optionsdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "base/bittorrent/session.h" #include "base/exceptions.h" #include "base/global.h" #include "base/net/dnsupdater.h" #include "base/net/portforwarder.h" #include "base/net/proxyconfigurationmanager.h" #include "base/path.h" #include "base/preferences.h" #include "base/rss/rss_autodownloader.h" #include "base/rss/rss_session.h" #include "base/torrentfileguard.h" #include "base/torrentfileswatcher.h" #include "base/unicodestrings.h" #include "base/utils/fs.h" #include "base/utils/net.h" #include "base/utils/password.h" #include "base/utils/random.h" #include "addnewtorrentdialog.h" #include "advancedsettings.h" #include "banlistoptionsdialog.h" #include "interfaces/iguiapplication.h" #include "ipsubnetwhitelistoptionsdialog.h" #include "rss/automatedrssdownloader.h" #include "ui_optionsdialog.h" #include "uithememanager.h" #include "utils.h" #include "watchedfolderoptionsdialog.h" #include "watchedfoldersmodel.h" #define SETTINGS_KEY(name) u"OptionsDialog/" name namespace { QStringList translatedWeekdayNames() { // return translated strings from Monday to Sunday in user selected locale const QLocale locale {Preferences::instance()->getLocale()}; const QDate date {2018, 11, 5}; // Monday QStringList ret; for (int i = 0; i < 7; ++i) ret.append(locale.toString(date.addDays(i), u"dddd"_qs)); return ret; } QString languageToLocalizedString(const QLocale &locale) { switch (locale.language()) { case QLocale::Arabic: return C_LOCALE_ARABIC; case QLocale::Armenian: return C_LOCALE_ARMENIAN; case QLocale::Azerbaijani: return C_LOCALE_AZERBAIJANI; case QLocale::Basque: return C_LOCALE_BASQUE; case QLocale::Bulgarian: return C_LOCALE_BULGARIAN; case QLocale::Byelorussian: return C_LOCALE_BYELORUSSIAN; case QLocale::Catalan: return C_LOCALE_CATALAN; case QLocale::Chinese: switch (locale.country()) { case QLocale::China: return C_LOCALE_CHINESE_SIMPLIFIED; case QLocale::HongKong: return C_LOCALE_CHINESE_TRADITIONAL_HK; default: return C_LOCALE_CHINESE_TRADITIONAL_TW; } case QLocale::Croatian: return C_LOCALE_CROATIAN; case QLocale::Czech: return C_LOCALE_CZECH; case QLocale::Danish: return C_LOCALE_DANISH; case QLocale::Dutch: return C_LOCALE_DUTCH; case QLocale::English: switch (locale.country()) { case QLocale::Australia: return C_LOCALE_ENGLISH_AUSTRALIA; case QLocale::UnitedKingdom: return C_LOCALE_ENGLISH_UNITEDKINGDOM; default: return C_LOCALE_ENGLISH; } case QLocale::Estonian: return C_LOCALE_ESTONIAN; case QLocale::Finnish: return C_LOCALE_FINNISH; case QLocale::French: return C_LOCALE_FRENCH; case QLocale::Galician: return C_LOCALE_GALICIAN; case QLocale::Georgian: return C_LOCALE_GEORGIAN; case QLocale::German: return C_LOCALE_GERMAN; case QLocale::Greek: return C_LOCALE_GREEK; case QLocale::Hebrew: return C_LOCALE_HEBREW; case QLocale::Hindi: return C_LOCALE_HINDI; case QLocale::Hungarian: return C_LOCALE_HUNGARIAN; case QLocale::Icelandic: return C_LOCALE_ICELANDIC; case QLocale::Indonesian: return C_LOCALE_INDONESIAN; case QLocale::Italian: return C_LOCALE_ITALIAN; case QLocale::Japanese: return C_LOCALE_JAPANESE; case QLocale::Korean: return C_LOCALE_KOREAN; case QLocale::Latvian: return C_LOCALE_LATVIAN; case QLocale::Lithuanian: return C_LOCALE_LITHUANIAN; case QLocale::Malay: return C_LOCALE_MALAY; case QLocale::Mongolian: return C_LOCALE_MONGOLIAN; case QLocale::NorwegianBokmal: return C_LOCALE_NORWEGIAN; case QLocale::Occitan: return C_LOCALE_OCCITAN; case QLocale::Persian: return C_LOCALE_PERSIAN; case QLocale::Polish: return C_LOCALE_POLISH; case QLocale::Portuguese: if (locale.country() == QLocale::Brazil) return C_LOCALE_PORTUGUESE_BRAZIL; return C_LOCALE_PORTUGUESE; case QLocale::Romanian: return C_LOCALE_ROMANIAN; case QLocale::Russian: return C_LOCALE_RUSSIAN; case QLocale::Serbian: return C_LOCALE_SERBIAN; case QLocale::Slovak: return C_LOCALE_SLOVAK; case QLocale::Slovenian: return C_LOCALE_SLOVENIAN; case QLocale::Spanish: return C_LOCALE_SPANISH; case QLocale::Swedish: return C_LOCALE_SWEDISH; case QLocale::Thai: return C_LOCALE_THAI; case QLocale::Turkish: return C_LOCALE_TURKISH; case QLocale::Ukrainian: return C_LOCALE_UKRAINIAN; case QLocale::Uzbek: return C_LOCALE_UZBEK; case QLocale::Vietnamese: return C_LOCALE_VIETNAMESE; default: const QString lang = QLocale::languageToString(locale.language()); qWarning() << "Unrecognized language name: " << lang; return lang; } } } class WheelEventEater final : public QObject { public: using QObject::QObject; private: bool eventFilter(QObject *, QEvent *event) override { return (event->type() == QEvent::Wheel); } }; // Constructor OptionsDialog::OptionsDialog(QWidget *parent) : QDialog {parent} , m_ui {new Ui::OptionsDialog} , m_storeDialogSize {SETTINGS_KEY(u"Size"_qs)} , m_storeHSplitterSize {SETTINGS_KEY(u"HorizontalSplitterSizes"_qs)} , m_storeLastViewedPage {SETTINGS_KEY(u"LastViewedPage"_qs)} { qDebug("-> Constructing Options"); m_ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); setModal(true); #if (defined(Q_OS_UNIX)) setWindowTitle(tr("Preferences")); #endif // Icons m_ui->tabSelection->item(TAB_UI)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-desktop"_qs)); m_ui->tabSelection->item(TAB_BITTORRENT)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-system-network"_qs)); m_ui->tabSelection->item(TAB_CONNECTION)->setIcon(UIThemeManager::instance()->getIcon(u"network-wired"_qs)); m_ui->tabSelection->item(TAB_DOWNLOADS)->setIcon(UIThemeManager::instance()->getIcon(u"folder-download"_qs)); m_ui->tabSelection->item(TAB_SPEED)->setIcon(UIThemeManager::instance()->getIcon(u"speedometer"_qs, u"chronometer"_qs)); m_ui->tabSelection->item(TAB_RSS)->setIcon(UIThemeManager::instance()->getIcon(u"rss-config"_qs, u"application-rss+xml"_qs)); #ifndef DISABLE_WEBUI m_ui->tabSelection->item(TAB_WEBUI)->setIcon(UIThemeManager::instance()->getIcon(u"network-server"_qs)); #else m_ui->tabSelection->item(TAB_WEBUI)->setHidden(true); #endif m_ui->tabSelection->item(TAB_ADVANCED)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-other"_qs)); // set uniform size for all icons int maxHeight = -1; for (int i = 0; i < m_ui->tabSelection->count(); ++i) maxHeight = std::max(maxHeight, m_ui->tabSelection->visualItemRect(m_ui->tabSelection->item(i)).size().height()); for (int i = 0; i < m_ui->tabSelection->count(); ++i) { const QSize size(std::numeric_limits::max(), static_cast(maxHeight * 1.2)); m_ui->tabSelection->item(i)->setSizeHint(size); } m_ui->IpFilterRefreshBtn->setIcon(UIThemeManager::instance()->getIcon(u"view-refresh"_qs)); m_ui->labelGlobalRate->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(u"slow_off"_qs), this, Utils::Gui::mediumIconSize(this).height())); m_ui->labelAltRate->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(u"slow"_qs), this, Utils::Gui::mediumIconSize(this).height())); m_ui->deleteTorrentWarningIcon->setPixmap(QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical).pixmap(16, 16)); m_ui->deleteTorrentWarningIcon->hide(); m_ui->deleteTorrentWarningLabel->hide(); m_ui->deleteTorrentWarningLabel->setToolTip(u"

" + tr("By enabling these options, you can irrevocably lose your .torrent files!") + u"

" + tr("When these options are enabled, qBittorrent will delete .torrent files " "after they were successfully (the first option) or not (the second option) added to its " "download queue. This will be applied not only to the files opened via " "“Add torrent” menu action but to those opened via file type association as well") + u"

" + tr("If you enable the second option (“Also when addition is cancelled”) the " ".torrent file will be deleted even if you press “Cancel” in " "the “Add torrent” dialog") + u"

"); m_ui->hsplitter->setCollapsible(0, false); m_ui->hsplitter->setCollapsible(1, false); // Get apply button in button box m_applyButton = m_ui->buttonBox->button(QDialogButtonBox::Apply); connect(m_applyButton, &QPushButton::clicked, this, &OptionsDialog::applySettings); auto watchedFoldersModel = new WatchedFoldersModel(TorrentFilesWatcher::instance(), this); connect(watchedFoldersModel, &QAbstractListModel::dataChanged, this, &ThisType::enableApplyButton); m_ui->scanFoldersView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); m_ui->scanFoldersView->setModel(watchedFoldersModel); connect(m_ui->scanFoldersView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ThisType::handleWatchedFolderViewSelectionChanged); connect(m_ui->scanFoldersView, &QTreeView::doubleClicked, this, &ThisType::editWatchedFolderOptions); // Languages supported initializeLanguageCombo(); m_ui->checkUseCustomTheme->setChecked(Preferences::instance()->useCustomUITheme()); m_ui->customThemeFilePath->setSelectedPath(Preferences::instance()->customUIThemePath()); m_ui->customThemeFilePath->setMode(FileSystemPathEdit::Mode::FileOpen); m_ui->customThemeFilePath->setDialogCaption(tr("Select qBittorrent UI Theme file")); m_ui->customThemeFilePath->setFileNameFilter(tr("qBittorrent UI Theme file (*.qbtheme config.json)")); #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) m_ui->checkUseSystemIcon->setChecked(Preferences::instance()->useSystemIconTheme()); #else m_ui->checkUseSystemIcon->setVisible(false); #endif // Load week days (scheduler) m_ui->comboBoxScheduleDays->addItems(translatedWeekdayNames()); // Load options loadOptions(); #ifdef Q_OS_MACOS m_ui->checkShowSystray->setVisible(false); #else // Disable systray integration if it is not supported by the system if (!QSystemTrayIcon::isSystemTrayAvailable()) { m_ui->checkShowSystray->setChecked(false); m_ui->checkShowSystray->setEnabled(false); m_ui->labelTrayIconStyle->setVisible(false); m_ui->comboTrayIcon->setVisible(false); } #endif #ifndef Q_OS_WIN m_ui->checkStartup->setVisible(false); #endif #if !(defined(Q_OS_WIN) || defined(Q_OS_MACOS)) m_ui->groupFileAssociation->setVisible(false); m_ui->checkProgramUpdates->setVisible(false); #endif m_ui->textWebUIRootFolder->setMode(FileSystemPathEdit::Mode::DirectoryOpen); m_ui->textWebUIRootFolder->setDialogCaption(tr("Choose Alternative UI files location")); // Connect signals / slots // Shortcuts for frequently used signals that have more than one overload. They would require // type casts and that is why we declare required member pointer here instead. void (QComboBox::*qComboBoxCurrentIndexChanged)(int) = &QComboBox::currentIndexChanged; void (QSpinBox::*qSpinBoxValueChanged)(int) = &QSpinBox::valueChanged; connect(m_ui->comboProxyType, qComboBoxCurrentIndexChanged, this, &ThisType::enableProxy); // Apply button is activated when a value is changed // Behavior tab connect(m_ui->comboI18n, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkUseCustomTheme, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->customThemeFilePath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton); #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) connect(m_ui->checkUseSystemIcon, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); #endif connect(m_ui->confirmDeletion, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkAltRowColors, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkHideZero, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkHideZero, &QAbstractButton::toggled, m_ui->comboHideZero, &QWidget::setEnabled); connect(m_ui->comboHideZero, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkShowSystray, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkCloseToSystray, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkMinimizeToSysTray, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkStartMinimized, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); #ifdef Q_OS_WIN connect(m_ui->checkStartup, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); #endif connect(m_ui->checkShowSplash, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkProgramExitConfirm, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkProgramAutoExitConfirm, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkPreventFromSuspendWhenDownloading, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkPreventFromSuspendWhenSeeding, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->comboTrayIcon, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && !defined(QT_DBUS_LIB) m_ui->checkPreventFromSuspendWhenDownloading->setDisabled(true); m_ui->checkPreventFromSuspendWhenSeeding->setDisabled(true); #endif #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) connect(m_ui->checkAssociateTorrents, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkAssociateMagnetLinks, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkProgramUpdates, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); #endif connect(m_ui->checkBoxPerformanceWarning, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkFileLog, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->textFileLogPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkFileLogBackup, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkFileLogBackup, &QAbstractButton::toggled, m_ui->spinFileLogSize, &QWidget::setEnabled); connect(m_ui->checkFileLogDelete, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkFileLogDelete, &QAbstractButton::toggled, m_ui->spinFileLogAge, &QWidget::setEnabled); connect(m_ui->checkFileLogDelete, &QAbstractButton::toggled, m_ui->comboFileLogAgeType, &QWidget::setEnabled); connect(m_ui->spinFileLogSize, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinFileLogAge, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->comboFileLogAgeType, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); // Downloads tab connect(m_ui->textSavePath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkUseSubcategories, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkUseCategoryPaths, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->comboSavingMode, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->comboTorrentCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->comboCategoryDefaultPathChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->comboCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->textDownloadPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkAppendqB, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkPreallocateAll, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkRecursiveDownload, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkAdditionDialog, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkAdditionDialogFront, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkStartPaused, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->contentLayoutComboBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->deleteTorrentBox, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->deleteCancelledTorrentBox, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkExportDir, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkExportDir, &QAbstractButton::toggled, m_ui->textExportDir, &QWidget::setEnabled); connect(m_ui->checkExportDirFin, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkExportDirFin, &QAbstractButton::toggled, m_ui->textExportDirFin, &QWidget::setEnabled); connect(m_ui->textExportDir, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->textExportDirFin, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->actionTorrentDlOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->actionTorrentFnOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, m_ui->textDownloadPath, &QWidget::setEnabled); connect(m_ui->addWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton); connect(m_ui->removeWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton); connect(m_ui->groupMailNotification, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->senderEmailTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->lineEditDestEmail, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->lineEditSmtpServer, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkSmtpSSL, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->groupMailNotifAuth, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->mailNotifUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->mailNotifPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->autoRunBox, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->lineEditAutoRun, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->autoRunConsole, &QCheckBox::toggled, this, &ThisType::enableApplyButton); 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"_qs .arg(tr("Supported parameters (case sensitive):") , tr("%N: Torrent name") , tr("%L: Category") , tr("%G: Tags (separated by comma)") , tr("%F: Content path (same as root path for multifile torrent)") , tr("%R: Root path (first torrent subdirectory path)") , tr("%D: Save path") , tr("%C: Number of files") , tr("%Z: Torrent size (bytes)")) .arg(tr("%T: Current tracker") , tr("%I: Info hash v1 (or '-' if unavailable)") , tr("%J: Info hash v2 (or '-' if unavailable)") , tr("%K: Torrent ID (either sha-1 info hash for v1 torrent or truncated sha-256 info hash for v2/hybrid torrent)") , tr("Tip: Encapsulate parameter with quotation marks to avoid text being cut off at whitespace (e.g., \"%N\")")); m_ui->labelAutoRunParam->setText(autoRunStr); // Connection tab connect(m_ui->comboProtocol, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkUPnP, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->spinUploadLimit, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinDownloadLimit, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinUploadLimitAlt, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinDownloadLimitAlt, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->groupBoxSchedule, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->timeEditScheduleFrom, &QDateTimeEdit::timeChanged, this, &ThisType::enableApplyButton); connect(m_ui->timeEditScheduleTo, &QDateTimeEdit::timeChanged, this, &ThisType::enableApplyButton); connect(m_ui->comboBoxScheduleDays, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkLimituTPConnections, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkLimitTransportOverhead, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkLimitLocalPeerRate, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); // Bittorrent tab connect(m_ui->checkMaxConnections, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkMaxConnectionsPerTorrent, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkMaxUploads, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkMaxUploadsPerTorrent, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->spinMaxConnec, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinMaxConnecPerTorrent, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinMaxUploads, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinMaxUploadsPerTorrent, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkDHT, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkAnonymousMode, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->spinBoxMaxActiveCheckingTorrents, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkPeX, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkLSD, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->comboEncryption, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkMaxRatio, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkMaxRatio, &QAbstractButton::toggled, this, &ThisType::toggleComboRatioLimitAct); connect(m_ui->spinMaxRatio, qOverload(&QDoubleSpinBox::valueChanged), this, &ThisType::enableApplyButton); connect(m_ui->comboRatioLimitAct, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkMaxSeedingMinutes, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkMaxSeedingMinutes, &QAbstractButton::toggled, this, &ThisType::toggleComboRatioLimitAct); connect(m_ui->spinMaxSeedingMinutes, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); // Proxy tab connect(m_ui->comboProxyType, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->textProxyIP, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinProxyPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkProxyPeerConnections, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->isProxyOnlyForTorrents, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkProxyAuth, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->textProxyUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->textProxyPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); // Misc tab connect(m_ui->checkIPFilter, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkIPFilter, &QAbstractButton::toggled, m_ui->textFilterPath, &QWidget::setEnabled); connect(m_ui->checkIPFilter, &QAbstractButton::toggled, m_ui->IpFilterRefreshBtn, &QWidget::setEnabled); connect(m_ui->checkIpFilterTrackers, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->textFilterPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkEnableQueueing, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->spinMaxActiveDownloads, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinMaxActiveUploads, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinMaxActiveTorrents, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkIgnoreSlowTorrentsForQueueing, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->spinDownloadRateForSlowTorrents, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinUploadRateForSlowTorrents, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinSlowTorrentsInactivityTimer, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkEnableAddTrackers, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->textTrackers, &QPlainTextEdit::textChanged, this, &ThisType::enableApplyButton); const QString slowTorrentsExplanation = u"

" + tr("A torrent will be considered slow if its download and upload rates stay below these values for \"Torrent inactivity timer\" seconds") + u"

"; m_ui->labelDownloadRateForSlowTorrents->setToolTip(slowTorrentsExplanation); m_ui->labelUploadRateForSlowTorrents->setToolTip(slowTorrentsExplanation); m_ui->labelSlowTorrentInactivityTimer->setToolTip(slowTorrentsExplanation); #ifndef DISABLE_WEBUI // Web UI tab m_ui->textWebUIHttpsCert->setMode(FileSystemPathEdit::Mode::FileOpen); m_ui->textWebUIHttpsCert->setFileNameFilter(tr("Certificate") + u" (*.cer *.crt *.pem)"); m_ui->textWebUIHttpsCert->setDialogCaption(tr("Select certificate")); m_ui->textWebUIHttpsKey->setMode(FileSystemPathEdit::Mode::FileOpen); m_ui->textWebUIHttpsKey->setFileNameFilter(tr("Private key") + u" (*.key *.pem)"); m_ui->textWebUIHttpsKey->setDialogCaption(tr("Select private key")); connect(m_ui->textServerDomains, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkWebUi, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->textWebUiAddress, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinWebUiPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkWebUIUPnP, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkWebUiHttps, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->textWebUIHttpsCert, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->textWebUIHttpsCert, &FileSystemPathLineEdit::selectedPathChanged, this, [this](const Path &path) { webUIHttpsCertChanged(path, ShowError::Show); }); connect(m_ui->textWebUIHttpsKey, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->textWebUIHttpsKey, &FileSystemPathLineEdit::selectedPathChanged, this, [this](const Path &path) { webUIHttpsKeyChanged(path, ShowError::Show); }); connect(m_ui->textWebUiUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->textWebUiPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, m_ui->IPSubnetWhitelistButton, &QPushButton::setEnabled); connect(m_ui->spinBanCounter, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinBanDuration, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinSessionTimeout, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkClickjacking, &QCheckBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkCSRFProtection, &QCheckBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkWebUiHttps, &QGroupBox::toggled, m_ui->checkSecureCookie, &QWidget::setEnabled); connect(m_ui->checkSecureCookie, &QCheckBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->groupHostHeaderValidation, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkDynDNS, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->comboDNSService, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->domainNameTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->DNSUsernameTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->DNSPasswordTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->groupAltWebUI, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->textWebUIRootFolder, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->groupWebUIAddCustomHTTPHeaders, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->textWebUICustomHTTPHeaders, &QPlainTextEdit::textChanged, this, &OptionsDialog::enableApplyButton); connect(m_ui->groupEnableReverseProxySupport, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->textTrustedReverseProxiesList, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); #endif // DISABLE_WEBUI // RSS tab connect(m_ui->checkRSSEnable, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton); connect(m_ui->checkRSSAutoDownloaderEnable, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton); connect(m_ui->textSmartEpisodeFilters, &QPlainTextEdit::textChanged, this, &OptionsDialog::enableApplyButton); connect(m_ui->checkSmartFilterDownloadRepacks, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton); connect(m_ui->spinRSSRefreshInterval, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton); connect(m_ui->spinRSSMaxArticlesPerFeed, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton); connect(m_ui->btnEditRules, &QPushButton::clicked, this, [this]() { auto *downloader = new AutomatedRssDownloader(this); downloader->setAttribute(Qt::WA_DeleteOnClose); downloader->open(); }); // Disable apply Button m_applyButton->setEnabled(false); // Tab selection mechanism connect(m_ui->tabSelection, &QListWidget::currentItemChanged, this, &ThisType::changePage); // Load Advanced settings m_advancedSettings = new AdvancedSettings(m_ui->tabAdvancedPage); m_ui->advPageLayout->addWidget(m_advancedSettings); connect(m_advancedSettings, &AdvancedSettings::settingsChanged, this, &ThisType::enableApplyButton); m_ui->textFileLogPath->setDialogCaption(tr("Choose a save directory")); m_ui->textFileLogPath->setMode(FileSystemPathEdit::Mode::DirectorySave); m_ui->textExportDir->setDialogCaption(tr("Choose export directory")); m_ui->textExportDir->setMode(FileSystemPathEdit::Mode::DirectorySave); m_ui->textExportDirFin->setDialogCaption(tr("Choose export directory")); m_ui->textExportDirFin->setMode(FileSystemPathEdit::Mode::DirectorySave); m_ui->textFilterPath->setDialogCaption(tr("Choose an IP filter file")); m_ui->textFilterPath->setFileNameFilter(tr("All supported filters") + u" (*.dat *.p2p *.p2b);;.dat (*.dat);;.p2p (*.p2p);;.p2b (*.p2b)"); m_ui->textSavePath->setDialogCaption(tr("Choose a save directory")); m_ui->textSavePath->setMode(FileSystemPathEdit::Mode::DirectorySave); m_ui->textDownloadPath->setDialogCaption(tr("Choose a save directory")); m_ui->textDownloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave); // disable mouse wheel event on widgets to avoid mis-selection auto *wheelEventEater = new WheelEventEater(this); for (QComboBox *widget : asConst(findChildren())) widget->installEventFilter(wheelEventEater); for (QSpinBox *widget : asConst(findChildren())) widget->installEventFilter(wheelEventEater); m_ui->tabSelection->setCurrentRow(m_storeLastViewedPage); resize(m_storeDialogSize); show(); // Have to be called after show(), because splitter width needed loadSplitterState(); } void OptionsDialog::initializeLanguageCombo() { // List language files const QDir langDir(u":/lang"_qs); const QStringList langFiles = langDir.entryList(QStringList(u"qbittorrent_*.qm"_qs), QDir::Files); for (const QString &langFile : langFiles) { QString localeStr = langFile.mid(12); // remove "qbittorrent_" localeStr.chop(3); // Remove ".qm" QString languageName; if (localeStr.startsWith(u"eo", Qt::CaseInsensitive)) { // QLocale doesn't work with that locale. Esperanto isn't a "real" language. languageName = C_LOCALE_ESPERANTO; } else if (localeStr.startsWith(u"ltg", Qt::CaseInsensitive)) { // QLocale doesn't work with that locale. languageName = C_LOCALE_LATGALIAN; } else { QLocale locale(localeStr); languageName = languageToLocalizedString(locale); } m_ui->comboI18n->addItem(/*QIcon(":/icons/flags/"+country+".svg"), */ languageName, localeStr); qDebug() << "Supported locale:" << localeStr; } } // Main destructor OptionsDialog::~OptionsDialog() { qDebug("-> destructing Options"); // save dialog states m_storeDialogSize = size(); QStringList hSplitterSizes; for (const int size : asConst(m_ui->hsplitter->sizes())) hSplitterSizes.append(QString::number(size)); m_storeHSplitterSize = hSplitterSizes; m_storeLastViewedPage = m_ui->tabSelection->currentRow(); delete m_ui; } void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) { if (!current) current = previous; m_ui->tabOption->setCurrentIndex(m_ui->tabSelection->row(current)); } void OptionsDialog::loadSplitterState() { // width has been modified, use height as width reference instead const int width = m_ui->tabSelection->item(TAB_UI)->sizeHint().height() * 2; const QStringList defaultSizes = {QString::number(width), QString::number(m_ui->hsplitter->width() - width)}; QList splitterSizes; for (const QString &string : asConst(m_storeHSplitterSize.get(defaultSizes))) splitterSizes.append(string.toInt()); m_ui->hsplitter->setSizes(splitterSizes); } void OptionsDialog::saveOptions() { auto *pref = Preferences::instance(); auto *session = BitTorrent::Session::instance(); m_applyButton->setEnabled(false); // Load the translation QString locale = getLocale(); if (pref->getLocale() != locale) { auto *translator = new QTranslator; if (translator->load(u":/lang/qbittorrent_"_qs + locale)) qDebug("%s locale recognized, using translation.", qUtf8Printable(locale)); else qDebug("%s locale unrecognized, using default (en).", qUtf8Printable(locale)); qApp->installTranslator(translator); } // Behavior preferences pref->setLocale(locale); pref->setUseCustomUITheme(m_ui->checkUseCustomTheme->isChecked()); pref->setCustomUIThemePath(m_ui->customThemeFilePath->selectedPath()); #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) pref->useSystemIconTheme(m_ui->checkUseSystemIcon->isChecked()); #endif pref->setConfirmTorrentDeletion(m_ui->confirmDeletion->isChecked()); pref->setAlternatingRowColors(m_ui->checkAltRowColors->isChecked()); pref->setHideZeroValues(m_ui->checkHideZero->isChecked()); pref->setHideZeroComboValues(m_ui->comboHideZero->currentIndex()); #ifndef Q_OS_MACOS pref->setSystemTrayEnabled(systemTrayEnabled()); pref->setTrayIconStyle(TrayIcon::Style(m_ui->comboTrayIcon->currentIndex())); pref->setCloseToTray(closeToTray()); pref->setMinimizeToTray(minimizeToTray()); #endif pref->setStartMinimized(startMinimized()); pref->setSplashScreenDisabled(isSplashScreenDisabled()); pref->setConfirmOnExit(m_ui->checkProgramExitConfirm->isChecked()); pref->setDontConfirmAutoExit(!m_ui->checkProgramAutoExitConfirm->isChecked()); pref->setPreventFromSuspendWhenDownloading(m_ui->checkPreventFromSuspendWhenDownloading->isChecked()); pref->setPreventFromSuspendWhenSeeding(m_ui->checkPreventFromSuspendWhenSeeding->isChecked()); #ifdef Q_OS_WIN pref->setWinStartup(WinStartup()); // Windows: file association settings Preferences::setTorrentFileAssoc(m_ui->checkAssociateTorrents->isChecked()); Preferences::setMagnetLinkAssoc(m_ui->checkAssociateMagnetLinks->isChecked()); #endif #ifdef Q_OS_MACOS if (m_ui->checkAssociateTorrents->isChecked()) { Preferences::setTorrentFileAssoc(); m_ui->checkAssociateTorrents->setChecked(Preferences::isTorrentFileAssocSet()); m_ui->checkAssociateTorrents->setEnabled(!m_ui->checkAssociateTorrents->isChecked()); } if (m_ui->checkAssociateMagnetLinks->isChecked()) { Preferences::setMagnetLinkAssoc(); m_ui->checkAssociateMagnetLinks->setChecked(Preferences::isMagnetLinkAssocSet()); m_ui->checkAssociateMagnetLinks->setEnabled(!m_ui->checkAssociateMagnetLinks->isChecked()); } #endif #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) pref->setUpdateCheckEnabled(m_ui->checkProgramUpdates->isChecked()); #endif session->setPerformanceWarningEnabled(m_ui->checkBoxPerformanceWarning->isChecked()); auto *app = dynamic_cast(QCoreApplication::instance()); app->setFileLoggerPath(m_ui->textFileLogPath->selectedPath()); app->setFileLoggerBackup(m_ui->checkFileLogBackup->isChecked()); app->setFileLoggerMaxSize(m_ui->spinFileLogSize->value() * 1024); app->setFileLoggerAge(m_ui->spinFileLogAge->value()); app->setFileLoggerAgeType(m_ui->comboFileLogAgeType->currentIndex()); app->setFileLoggerDeleteOld(m_ui->checkFileLogDelete->isChecked()); app->setFileLoggerEnabled(m_ui->checkFileLog->isChecked()); // End Behavior preferences RSS::Session::instance()->setRefreshInterval(m_ui->spinRSSRefreshInterval->value()); RSS::Session::instance()->setMaxArticlesPerFeed(m_ui->spinRSSMaxArticlesPerFeed->value()); RSS::Session::instance()->setProcessingEnabled(m_ui->checkRSSEnable->isChecked()); RSS::AutoDownloader::instance()->setProcessingEnabled(m_ui->checkRSSAutoDownloaderEnable->isChecked()); RSS::AutoDownloader::instance()->setSmartEpisodeFilters(m_ui->textSmartEpisodeFilters->toPlainText().split(u'\n', Qt::SkipEmptyParts)); RSS::AutoDownloader::instance()->setDownloadRepacks(m_ui->checkSmartFilterDownloadRepacks->isChecked()); // Downloads preferences session->setSavePath(Path(m_ui->textSavePath->selectedPath())); session->setSubcategoriesEnabled(m_ui->checkUseSubcategories->isChecked()); session->setUseCategoryPathsInManualMode(m_ui->checkUseCategoryPaths->isChecked()); session->setAutoTMMDisabledByDefault(m_ui->comboSavingMode->currentIndex() == 0); session->setDisableAutoTMMWhenCategoryChanged(m_ui->comboTorrentCategoryChanged->currentIndex() == 1); session->setDisableAutoTMMWhenCategorySavePathChanged(m_ui->comboCategoryChanged->currentIndex() == 1); session->setDisableAutoTMMWhenDefaultSavePathChanged(m_ui->comboCategoryDefaultPathChanged->currentIndex() == 1); session->setDownloadPathEnabled(m_ui->checkUseDownloadPath->isChecked()); session->setDownloadPath(m_ui->textDownloadPath->selectedPath()); session->setAppendExtensionEnabled(m_ui->checkAppendqB->isChecked()); session->setPreallocationEnabled(preAllocateAllFiles()); pref->disableRecursiveDownload(!m_ui->checkRecursiveDownload->isChecked()); AddNewTorrentDialog::setEnabled(useAdditionDialog()); AddNewTorrentDialog::setTopLevel(m_ui->checkAdditionDialogFront->isChecked()); session->setAddTorrentPaused(addTorrentsInPause()); session->setTorrentContentLayout(static_cast(m_ui->contentLayoutComboBox->currentIndex())); auto watchedFoldersModel = static_cast(m_ui->scanFoldersView->model()); watchedFoldersModel->apply(); session->setTorrentExportDirectory(getTorrentExportDir()); session->setFinishedTorrentExportDirectory(getFinishedTorrentExportDir()); pref->setMailNotificationEnabled(m_ui->groupMailNotification->isChecked()); pref->setMailNotificationSender(m_ui->senderEmailTxt->text()); pref->setMailNotificationEmail(m_ui->lineEditDestEmail->text()); pref->setMailNotificationSMTP(m_ui->lineEditSmtpServer->text()); pref->setMailNotificationSMTPSSL(m_ui->checkSmtpSSL->isChecked()); pref->setMailNotificationSMTPAuth(m_ui->groupMailNotifAuth->isChecked()); pref->setMailNotificationSMTPUsername(m_ui->mailNotifUsername->text()); pref->setMailNotificationSMTPPassword(m_ui->mailNotifPassword->text()); pref->setAutoRunEnabled(m_ui->autoRunBox->isChecked()); pref->setAutoRunProgram(m_ui->lineEditAutoRun->text().trimmed()); #if defined(Q_OS_WIN) pref->setAutoRunConsoleEnabled(m_ui->autoRunConsole->isChecked()); #endif pref->setActionOnDblClOnTorrentDl(m_ui->actionTorrentDlOnDblClBox->currentData().toInt()); pref->setActionOnDblClOnTorrentFn(m_ui->actionTorrentFnOnDblClBox->currentData().toInt()); TorrentFileGuard::setAutoDeleteMode(!m_ui->deleteTorrentBox->isChecked() ? TorrentFileGuard::Never : !m_ui->deleteCancelledTorrentBox->isChecked() ? TorrentFileGuard::IfAdded : TorrentFileGuard::Always); // End Downloads preferences // Connection preferences session->setBTProtocol(static_cast(m_ui->comboProtocol->currentIndex())); session->setPort(getPort()); Net::PortForwarder::instance()->setEnabled(isUPnPEnabled()); session->setGlobalDownloadSpeedLimit(m_ui->spinDownloadLimit->value() * 1024); session->setGlobalUploadSpeedLimit(m_ui->spinUploadLimit->value() * 1024); session->setAltGlobalDownloadSpeedLimit(m_ui->spinDownloadLimitAlt->value() * 1024); session->setAltGlobalUploadSpeedLimit(m_ui->spinUploadLimitAlt->value() * 1024); session->setUTPRateLimited(m_ui->checkLimituTPConnections->isChecked()); session->setIncludeOverheadInLimits(m_ui->checkLimitTransportOverhead->isChecked()); session->setIgnoreLimitsOnLAN(!m_ui->checkLimitLocalPeerRate->isChecked()); pref->setSchedulerStartTime(m_ui->timeEditScheduleFrom->time()); pref->setSchedulerEndTime(m_ui->timeEditScheduleTo->time()); pref->setSchedulerDays(static_cast(m_ui->comboBoxScheduleDays->currentIndex())); session->setBandwidthSchedulerEnabled(m_ui->groupBoxSchedule->isChecked()); auto proxyConfigManager = Net::ProxyConfigurationManager::instance(); Net::ProxyConfiguration proxyConf; proxyConf.type = getProxyType(); proxyConf.ip = getProxyIp(); proxyConf.port = getProxyPort(); proxyConf.username = getProxyUsername(); proxyConf.password = getProxyPassword(); proxyConfigManager->setProxyOnlyForTorrents(m_ui->isProxyOnlyForTorrents->isChecked()); proxyConfigManager->setProxyConfiguration(proxyConf); session->setProxyPeerConnectionsEnabled(m_ui->checkProxyPeerConnections->isChecked()); // End Connection preferences // Bittorrent preferences session->setMaxConnections(getMaxConnections()); session->setMaxConnectionsPerTorrent(getMaxConnectionsPerTorrent()); session->setMaxUploads(getMaxUploads()); session->setMaxUploadsPerTorrent(getMaxUploadsPerTorrent()); session->setDHTEnabled(isDHTEnabled()); session->setPeXEnabled(m_ui->checkPeX->isChecked()); session->setLSDEnabled(isLSDEnabled()); session->setEncryption(getEncryptionSetting()); session->setAnonymousModeEnabled(m_ui->checkAnonymousMode->isChecked()); session->setMaxActiveCheckingTorrents(m_ui->spinBoxMaxActiveCheckingTorrents->value()); session->setAddTrackersEnabled(m_ui->checkEnableAddTrackers->isChecked()); session->setAdditionalTrackers(m_ui->textTrackers->toPlainText()); session->setGlobalMaxRatio(getMaxRatio()); session->setGlobalMaxSeedingMinutes(getMaxSeedingMinutes()); const QVector actIndex = { Pause, Remove, DeleteFiles, EnableSuperSeeding }; session->setMaxRatioAction(actIndex.value(m_ui->comboRatioLimitAct->currentIndex())); // End Bittorrent preferences // Misc preferences // * IPFilter session->setIPFilteringEnabled(isIPFilteringEnabled()); session->setTrackerFilteringEnabled(m_ui->checkIpFilterTrackers->isChecked()); session->setIPFilterFile(m_ui->textFilterPath->selectedPath()); // End IPFilter preferences // Queueing system session->setQueueingSystemEnabled(isQueueingSystemEnabled()); session->setMaxActiveDownloads(m_ui->spinMaxActiveDownloads->value()); session->setMaxActiveUploads(m_ui->spinMaxActiveUploads->value()); session->setMaxActiveTorrents(m_ui->spinMaxActiveTorrents->value()); session->setIgnoreSlowTorrentsForQueueing(m_ui->checkIgnoreSlowTorrentsForQueueing->isChecked()); session->setDownloadRateForSlowTorrents(m_ui->spinDownloadRateForSlowTorrents->value()); session->setUploadRateForSlowTorrents(m_ui->spinUploadRateForSlowTorrents->value()); session->setSlowTorrentsInactivityTimer(m_ui->spinSlowTorrentsInactivityTimer->value()); // End Queueing system preferences // Web UI pref->setWebUiEnabled(isWebUiEnabled()); if (isWebUiEnabled()) { pref->setServerDomains(m_ui->textServerDomains->text()); pref->setWebUiAddress(m_ui->textWebUiAddress->text()); pref->setWebUiPort(m_ui->spinWebUiPort->value()); pref->setUPnPForWebUIPort(m_ui->checkWebUIUPnP->isChecked()); pref->setWebUiHttpsEnabled(m_ui->checkWebUiHttps->isChecked()); pref->setWebUIHttpsCertificatePath(m_ui->textWebUIHttpsCert->selectedPath()); pref->setWebUIHttpsKeyPath(m_ui->textWebUIHttpsKey->selectedPath()); pref->setWebUIMaxAuthFailCount(m_ui->spinBanCounter->value()); pref->setWebUIBanDuration(std::chrono::seconds {m_ui->spinBanDuration->value()}); pref->setWebUISessionTimeout(m_ui->spinSessionTimeout->value()); // Authentication pref->setWebUiUsername(webUiUsername()); if (!webUiPassword().isEmpty()) pref->setWebUIPassword(Utils::Password::PBKDF2::generate(webUiPassword())); pref->setWebUiLocalAuthEnabled(!m_ui->checkBypassLocalAuth->isChecked()); pref->setWebUiAuthSubnetWhitelistEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked()); // Security pref->setWebUiClickjackingProtectionEnabled(m_ui->checkClickjacking->isChecked()); pref->setWebUiCSRFProtectionEnabled(m_ui->checkCSRFProtection->isChecked()); pref->setWebUiSecureCookieEnabled(m_ui->checkSecureCookie->isChecked()); pref->setWebUIHostHeaderValidationEnabled(m_ui->groupHostHeaderValidation->isChecked()); // DynDNS pref->setDynDNSEnabled(m_ui->checkDynDNS->isChecked()); pref->setDynDNSService(static_cast(m_ui->comboDNSService->currentIndex())); pref->setDynDomainName(m_ui->domainNameTxt->text()); pref->setDynDNSUsername(m_ui->DNSUsernameTxt->text()); pref->setDynDNSPassword(m_ui->DNSPasswordTxt->text()); // Alternative UI pref->setAltWebUiEnabled(m_ui->groupAltWebUI->isChecked()); pref->setWebUiRootFolder(m_ui->textWebUIRootFolder->selectedPath()); // Custom HTTP headers pref->setWebUICustomHTTPHeadersEnabled(m_ui->groupWebUIAddCustomHTTPHeaders->isChecked()); pref->setWebUICustomHTTPHeaders(m_ui->textWebUICustomHTTPHeaders->toPlainText()); // Reverse proxy pref->setWebUIReverseProxySupportEnabled(m_ui->groupEnableReverseProxySupport->isChecked()); pref->setWebUITrustedReverseProxiesList(m_ui->textTrustedReverseProxiesList->text()); } // End Web UI // End preferences // Save advanced settings m_advancedSettings->saveAdvancedSettings(); // Assume that user changed multiple settings // so it's best to save immediately pref->apply(); } bool OptionsDialog::isIPFilteringEnabled() const { return m_ui->checkIPFilter->isChecked(); } Net::ProxyType OptionsDialog::getProxyType() const { switch (m_ui->comboProxyType->currentIndex()) { case 1: return Net::ProxyType::SOCKS4; case 2: if (isProxyAuthEnabled()) return Net::ProxyType::SOCKS5_PW; return Net::ProxyType::SOCKS5; case 3: if (isProxyAuthEnabled()) return Net::ProxyType::HTTP_PW; return Net::ProxyType::HTTP; default: return Net::ProxyType::None; } } void OptionsDialog::loadOptions() { const auto *pref = Preferences::instance(); const auto *session = BitTorrent::Session::instance(); // Behavior preferences setLocale(pref->getLocale()); m_ui->confirmDeletion->setChecked(pref->confirmTorrentDeletion()); m_ui->checkAltRowColors->setChecked(pref->useAlternatingRowColors()); m_ui->checkHideZero->setChecked(pref->getHideZeroValues()); m_ui->comboHideZero->setEnabled(m_ui->checkHideZero->isChecked()); m_ui->comboHideZero->setCurrentIndex(pref->getHideZeroComboValues()); m_ui->checkShowSplash->setChecked(!pref->isSplashScreenDisabled()); m_ui->checkStartMinimized->setChecked(pref->startMinimized()); m_ui->checkProgramExitConfirm->setChecked(pref->confirmOnExit()); m_ui->checkProgramAutoExitConfirm->setChecked(!pref->dontConfirmAutoExit()); #ifndef Q_OS_MACOS m_ui->checkShowSystray->setChecked(pref->systemTrayEnabled()); if (m_ui->checkShowSystray->isChecked()) { m_ui->checkMinimizeToSysTray->setChecked(pref->minimizeToTray()); m_ui->checkCloseToSystray->setChecked(pref->closeToTray()); m_ui->comboTrayIcon->setCurrentIndex(static_cast(pref->trayIconStyle())); } #endif m_ui->checkPreventFromSuspendWhenDownloading->setChecked(pref->preventFromSuspendWhenDownloading()); m_ui->checkPreventFromSuspendWhenSeeding->setChecked(pref->preventFromSuspendWhenSeeding()); #ifdef Q_OS_WIN m_ui->checkStartup->setChecked(pref->WinStartup()); m_ui->checkAssociateTorrents->setChecked(Preferences::isTorrentFileAssocSet()); m_ui->checkAssociateMagnetLinks->setChecked(Preferences::isMagnetLinkAssocSet()); #endif #ifdef Q_OS_MACOS m_ui->checkAssociateTorrents->setChecked(Preferences::isTorrentFileAssocSet()); m_ui->checkAssociateTorrents->setEnabled(!m_ui->checkAssociateTorrents->isChecked()); m_ui->checkAssociateMagnetLinks->setChecked(Preferences::isMagnetLinkAssocSet()); m_ui->checkAssociateMagnetLinks->setEnabled(!m_ui->checkAssociateMagnetLinks->isChecked()); #endif #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) m_ui->checkProgramUpdates->setChecked(pref->isUpdateCheckEnabled()); #endif m_ui->checkBoxPerformanceWarning->setChecked(session->isPerformanceWarningEnabled()); const auto *app = dynamic_cast(QCoreApplication::instance()); m_ui->checkFileLog->setChecked(app->isFileLoggerEnabled()); m_ui->textFileLogPath->setSelectedPath(app->fileLoggerPath()); const bool fileLogBackup = app->isFileLoggerBackup(); m_ui->checkFileLogBackup->setChecked(fileLogBackup); m_ui->spinFileLogSize->setEnabled(fileLogBackup); const bool fileLogDelete = app->isFileLoggerDeleteOld(); m_ui->checkFileLogDelete->setChecked(fileLogDelete); m_ui->spinFileLogAge->setEnabled(fileLogDelete); m_ui->comboFileLogAgeType->setEnabled(fileLogDelete); m_ui->spinFileLogSize->setValue(app->fileLoggerMaxSize() / 1024); m_ui->spinFileLogAge->setValue(app->fileLoggerAge()); m_ui->comboFileLogAgeType->setCurrentIndex(app->fileLoggerAgeType()); // End Behavior preferences m_ui->checkRSSEnable->setChecked(RSS::Session::instance()->isProcessingEnabled()); m_ui->checkRSSAutoDownloaderEnable->setChecked(RSS::AutoDownloader::instance()->isProcessingEnabled()); m_ui->textSmartEpisodeFilters->setPlainText(RSS::AutoDownloader::instance()->smartEpisodeFilters().join(u'\n')); m_ui->checkSmartFilterDownloadRepacks->setChecked(RSS::AutoDownloader::instance()->downloadRepacks()); m_ui->spinRSSRefreshInterval->setValue(RSS::Session::instance()->refreshInterval()); m_ui->spinRSSMaxArticlesPerFeed->setValue(RSS::Session::instance()->maxArticlesPerFeed()); // Downloads preferences m_ui->checkAdditionDialog->setChecked(AddNewTorrentDialog::isEnabled()); m_ui->checkAdditionDialogFront->setChecked(AddNewTorrentDialog::isTopLevel()); m_ui->checkStartPaused->setChecked(session->isAddTorrentPaused()); m_ui->contentLayoutComboBox->setCurrentIndex(static_cast(session->torrentContentLayout())); const TorrentFileGuard::AutoDeleteMode autoDeleteMode = TorrentFileGuard::autoDeleteMode(); m_ui->deleteTorrentBox->setChecked(autoDeleteMode != TorrentFileGuard::Never); m_ui->deleteCancelledTorrentBox->setChecked(autoDeleteMode == TorrentFileGuard::Always); m_ui->textSavePath->setSelectedPath(session->savePath()); m_ui->checkUseSubcategories->setChecked(session->isSubcategoriesEnabled()); m_ui->checkUseCategoryPaths->setChecked(session->useCategoryPathsInManualMode()); m_ui->comboSavingMode->setCurrentIndex(!session->isAutoTMMDisabledByDefault()); m_ui->comboTorrentCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategoryChanged()); m_ui->comboCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategorySavePathChanged()); m_ui->comboCategoryDefaultPathChanged->setCurrentIndex(session->isDisableAutoTMMWhenDefaultSavePathChanged()); m_ui->checkUseDownloadPath->setChecked(session->isDownloadPathEnabled()); m_ui->textDownloadPath->setEnabled(m_ui->checkUseDownloadPath->isChecked()); m_ui->textDownloadPath->setSelectedPath(session->downloadPath()); m_ui->checkAppendqB->setChecked(session->isAppendExtensionEnabled()); m_ui->checkPreallocateAll->setChecked(session->isPreallocationEnabled()); m_ui->checkRecursiveDownload->setChecked(!pref->recursiveDownloadDisabled()); if (session->torrentExportDirectory().isEmpty()) { // Disable m_ui->checkExportDir->setChecked(false); m_ui->textExportDir->setEnabled(false); } else { // Enable m_ui->checkExportDir->setChecked(true); m_ui->textExportDir->setEnabled(true); m_ui->textExportDir->setSelectedPath(session->torrentExportDirectory()); } if (session->finishedTorrentExportDirectory().isEmpty()) { // Disable m_ui->checkExportDirFin->setChecked(false); m_ui->textExportDirFin->setEnabled(false); } else { // Enable m_ui->checkExportDirFin->setChecked(true); m_ui->textExportDirFin->setEnabled(true); m_ui->textExportDirFin->setSelectedPath(session->finishedTorrentExportDirectory()); } m_ui->groupMailNotification->setChecked(pref->isMailNotificationEnabled()); m_ui->senderEmailTxt->setText(pref->getMailNotificationSender()); m_ui->lineEditDestEmail->setText(pref->getMailNotificationEmail()); m_ui->lineEditSmtpServer->setText(pref->getMailNotificationSMTP()); m_ui->checkSmtpSSL->setChecked(pref->getMailNotificationSMTPSSL()); m_ui->groupMailNotifAuth->setChecked(pref->getMailNotificationSMTPAuth()); m_ui->mailNotifUsername->setText(pref->getMailNotificationSMTPUsername()); m_ui->mailNotifPassword->setText(pref->getMailNotificationSMTPPassword()); m_ui->autoRunBox->setChecked(pref->isAutoRunEnabled()); m_ui->lineEditAutoRun->setText(pref->getAutoRunProgram()); #if defined(Q_OS_WIN) m_ui->autoRunConsole->setChecked(pref->isAutoRunConsoleEnabled()); #else m_ui->autoRunConsole->hide(); #endif m_ui->actionTorrentDlOnDblClBox->setItemData(0, TOGGLE_PAUSE); m_ui->actionTorrentDlOnDblClBox->setItemData(1, OPEN_DEST); m_ui->actionTorrentDlOnDblClBox->setItemData(2, PREVIEW_FILE); m_ui->actionTorrentDlOnDblClBox->setItemData(3, SHOW_OPTIONS); m_ui->actionTorrentDlOnDblClBox->setItemData(4, NO_ACTION); int actionDownloading = pref->getActionOnDblClOnTorrentDl(); if ((actionDownloading < 0) || (actionDownloading >= m_ui->actionTorrentDlOnDblClBox->count())) actionDownloading = TOGGLE_PAUSE; m_ui->actionTorrentDlOnDblClBox->setCurrentIndex(m_ui->actionTorrentDlOnDblClBox->findData(actionDownloading)); m_ui->actionTorrentFnOnDblClBox->setItemData(0, TOGGLE_PAUSE); m_ui->actionTorrentFnOnDblClBox->setItemData(1, OPEN_DEST); m_ui->actionTorrentFnOnDblClBox->setItemData(2, PREVIEW_FILE); m_ui->actionTorrentFnOnDblClBox->setItemData(3, SHOW_OPTIONS); m_ui->actionTorrentFnOnDblClBox->setItemData(4, NO_ACTION); int actionSeeding = pref->getActionOnDblClOnTorrentFn(); if ((actionSeeding < 0) || (actionSeeding >= m_ui->actionTorrentFnOnDblClBox->count())) actionSeeding = OPEN_DEST; m_ui->actionTorrentFnOnDblClBox->setCurrentIndex(m_ui->actionTorrentFnOnDblClBox->findData(actionSeeding)); // End Downloads preferences // Connection preferences m_ui->comboProtocol->setCurrentIndex(static_cast(session->btProtocol())); m_ui->spinPort->setValue(session->port()); m_ui->checkUPnP->setChecked(Net::PortForwarder::instance()->isEnabled()); int intValue = session->maxConnections(); if (intValue > 0) { // enable m_ui->checkMaxConnections->setChecked(true); m_ui->spinMaxConnec->setEnabled(true); m_ui->spinMaxConnec->setValue(intValue); } else { // disable m_ui->checkMaxConnections->setChecked(false); m_ui->spinMaxConnec->setEnabled(false); } intValue = session->maxConnectionsPerTorrent(); if (intValue > 0) { // enable m_ui->checkMaxConnectionsPerTorrent->setChecked(true); m_ui->spinMaxConnecPerTorrent->setEnabled(true); m_ui->spinMaxConnecPerTorrent->setValue(intValue); } else { // disable m_ui->checkMaxConnectionsPerTorrent->setChecked(false); m_ui->spinMaxConnecPerTorrent->setEnabled(false); } intValue = session->maxUploads(); if (intValue > 0) { // enable m_ui->checkMaxUploads->setChecked(true); m_ui->spinMaxUploads->setEnabled(true); m_ui->spinMaxUploads->setValue(intValue); } else { // disable m_ui->checkMaxUploads->setChecked(false); m_ui->spinMaxUploads->setEnabled(false); } intValue = session->maxUploadsPerTorrent(); if (intValue > 0) { // enable m_ui->checkMaxUploadsPerTorrent->setChecked(true); m_ui->spinMaxUploadsPerTorrent->setEnabled(true); m_ui->spinMaxUploadsPerTorrent->setValue(intValue); } else { // disable m_ui->checkMaxUploadsPerTorrent->setChecked(false); m_ui->spinMaxUploadsPerTorrent->setEnabled(false); } const auto *proxyConfigManager = Net::ProxyConfigurationManager::instance(); Net::ProxyConfiguration proxyConf = proxyConfigManager->proxyConfiguration(); using Net::ProxyType; bool useProxyAuth = false; switch (proxyConf.type) { case ProxyType::SOCKS4: m_ui->comboProxyType->setCurrentIndex(1); break; case ProxyType::SOCKS5_PW: useProxyAuth = true; // fallthrough case ProxyType::SOCKS5: m_ui->comboProxyType->setCurrentIndex(2); break; case ProxyType::HTTP_PW: useProxyAuth = true; // fallthrough case ProxyType::HTTP: m_ui->comboProxyType->setCurrentIndex(3); break; default: m_ui->comboProxyType->setCurrentIndex(0); } m_ui->textProxyIP->setText(proxyConf.ip); m_ui->spinProxyPort->setValue(proxyConf.port); m_ui->checkProxyAuth->setChecked(useProxyAuth); m_ui->textProxyUsername->setText(proxyConf.username); m_ui->textProxyPassword->setText(proxyConf.password); m_ui->checkProxyPeerConnections->setChecked(session->isProxyPeerConnectionsEnabled()); m_ui->isProxyOnlyForTorrents->setChecked(proxyConfigManager->isProxyOnlyForTorrents()); enableProxy(m_ui->comboProxyType->currentIndex()); m_ui->checkIPFilter->setChecked(session->isIPFilteringEnabled()); m_ui->textFilterPath->setEnabled(m_ui->checkIPFilter->isChecked()); m_ui->textFilterPath->setSelectedPath(session->IPFilterFile()); m_ui->IpFilterRefreshBtn->setEnabled(m_ui->checkIPFilter->isChecked()); m_ui->checkIpFilterTrackers->setChecked(session->isTrackerFilteringEnabled()); // End Connection preferences // Speed preferences m_ui->spinDownloadLimit->setValue(session->globalDownloadSpeedLimit() / 1024); m_ui->spinUploadLimit->setValue(session->globalUploadSpeedLimit() / 1024); m_ui->spinDownloadLimitAlt->setValue(session->altGlobalDownloadSpeedLimit() / 1024); m_ui->spinUploadLimitAlt->setValue(session->altGlobalUploadSpeedLimit() / 1024); m_ui->checkLimituTPConnections->setChecked(session->isUTPRateLimited()); m_ui->checkLimitTransportOverhead->setChecked(session->includeOverheadInLimits()); m_ui->checkLimitLocalPeerRate->setChecked(!session->ignoreLimitsOnLAN()); m_ui->groupBoxSchedule->setChecked(session->isBandwidthSchedulerEnabled()); m_ui->timeEditScheduleFrom->setTime(pref->getSchedulerStartTime()); m_ui->timeEditScheduleTo->setTime(pref->getSchedulerEndTime()); m_ui->comboBoxScheduleDays->setCurrentIndex(static_cast(pref->getSchedulerDays())); // End Speed preferences // Bittorrent preferences m_ui->checkDHT->setChecked(session->isDHTEnabled()); m_ui->checkPeX->setChecked(session->isPeXEnabled()); m_ui->checkLSD->setChecked(session->isLSDEnabled()); m_ui->comboEncryption->setCurrentIndex(session->encryption()); m_ui->checkAnonymousMode->setChecked(session->isAnonymousModeEnabled()); m_ui->spinBoxMaxActiveCheckingTorrents->setValue(session->maxActiveCheckingTorrents()); m_ui->checkEnableAddTrackers->setChecked(session->isAddTrackersEnabled()); m_ui->textTrackers->setPlainText(session->additionalTrackers()); m_ui->checkEnableQueueing->setChecked(session->isQueueingSystemEnabled()); m_ui->spinMaxActiveDownloads->setValue(session->maxActiveDownloads()); m_ui->spinMaxActiveUploads->setValue(session->maxActiveUploads()); m_ui->spinMaxActiveTorrents->setValue(session->maxActiveTorrents()); m_ui->checkIgnoreSlowTorrentsForQueueing->setChecked(session->ignoreSlowTorrentsForQueueing()); m_ui->spinDownloadRateForSlowTorrents->setValue(session->downloadRateForSlowTorrents()); m_ui->spinUploadRateForSlowTorrents->setValue(session->uploadRateForSlowTorrents()); m_ui->spinSlowTorrentsInactivityTimer->setValue(session->slowTorrentsInactivityTimer()); if (session->globalMaxRatio() >= 0.) { // Enable m_ui->checkMaxRatio->setChecked(true); m_ui->spinMaxRatio->setEnabled(true); m_ui->comboRatioLimitAct->setEnabled(true); m_ui->spinMaxRatio->setValue(session->globalMaxRatio()); } else { // Disable m_ui->checkMaxRatio->setChecked(false); m_ui->spinMaxRatio->setEnabled(false); } if (session->globalMaxSeedingMinutes() >= 0) { // Enable m_ui->checkMaxSeedingMinutes->setChecked(true); m_ui->spinMaxSeedingMinutes->setEnabled(true); m_ui->spinMaxSeedingMinutes->setValue(session->globalMaxSeedingMinutes()); } else { // Disable m_ui->checkMaxSeedingMinutes->setChecked(false); m_ui->spinMaxSeedingMinutes->setEnabled(false); } m_ui->comboRatioLimitAct->setEnabled((session->globalMaxSeedingMinutes() >= 0) || (session->globalMaxRatio() >= 0.)); const QHash actIndex = { {Pause, 0}, {Remove, 1}, {DeleteFiles, 2}, {EnableSuperSeeding, 3} }; m_ui->comboRatioLimitAct->setCurrentIndex(actIndex.value(session->maxRatioAction())); // End Bittorrent preferences // Web UI preferences m_ui->textServerDomains->setText(pref->getServerDomains()); m_ui->checkWebUi->setChecked(pref->isWebUiEnabled()); m_ui->textWebUiAddress->setText(pref->getWebUiAddress()); m_ui->spinWebUiPort->setValue(pref->getWebUiPort()); m_ui->checkWebUIUPnP->setChecked(pref->useUPnPForWebUIPort()); m_ui->checkWebUiHttps->setChecked(pref->isWebUiHttpsEnabled()); webUIHttpsCertChanged(pref->getWebUIHttpsCertificatePath(), ShowError::NotShow); webUIHttpsKeyChanged(pref->getWebUIHttpsKeyPath(), ShowError::NotShow); m_ui->textWebUiUsername->setText(pref->getWebUiUsername()); m_ui->checkBypassLocalAuth->setChecked(!pref->isWebUiLocalAuthEnabled()); m_ui->checkBypassAuthSubnetWhitelist->setChecked(pref->isWebUiAuthSubnetWhitelistEnabled()); m_ui->IPSubnetWhitelistButton->setEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked()); m_ui->spinBanCounter->setValue(pref->getWebUIMaxAuthFailCount()); m_ui->spinBanDuration->setValue(pref->getWebUIBanDuration().count()); m_ui->spinSessionTimeout->setValue(pref->getWebUISessionTimeout()); // Security m_ui->checkClickjacking->setChecked(pref->isWebUiClickjackingProtectionEnabled()); m_ui->checkCSRFProtection->setChecked(pref->isWebUiCSRFProtectionEnabled()); m_ui->checkSecureCookie->setEnabled(pref->isWebUiHttpsEnabled()); m_ui->checkSecureCookie->setChecked(pref->isWebUiSecureCookieEnabled()); m_ui->groupHostHeaderValidation->setChecked(pref->isWebUIHostHeaderValidationEnabled()); m_ui->checkDynDNS->setChecked(pref->isDynDNSEnabled()); m_ui->comboDNSService->setCurrentIndex(static_cast(pref->getDynDNSService())); m_ui->domainNameTxt->setText(pref->getDynDomainName()); m_ui->DNSUsernameTxt->setText(pref->getDynDNSUsername()); m_ui->DNSPasswordTxt->setText(pref->getDynDNSPassword()); m_ui->groupAltWebUI->setChecked(pref->isAltWebUiEnabled()); m_ui->textWebUIRootFolder->setSelectedPath(pref->getWebUiRootFolder()); // Custom HTTP headers m_ui->groupWebUIAddCustomHTTPHeaders->setChecked(pref->isWebUICustomHTTPHeadersEnabled()); m_ui->textWebUICustomHTTPHeaders->setPlainText(pref->getWebUICustomHTTPHeaders()); // Reverse proxy m_ui->groupEnableReverseProxySupport->setChecked(pref->isWebUIReverseProxySupportEnabled()); m_ui->textTrustedReverseProxiesList->setText(pref->getWebUITrustedReverseProxiesList()); // End Web UI preferences } // return min & max ports // [min, max] int OptionsDialog::getPort() const { return m_ui->spinPort->value(); } void OptionsDialog::on_randomButton_clicked() { // Range [1024: 65535] m_ui->spinPort->setValue(Utils::Random::rand(1024, 65535)); } int OptionsDialog::getEncryptionSetting() const { return m_ui->comboEncryption->currentIndex(); } int OptionsDialog::getMaxActiveDownloads() const { return m_ui->spinMaxActiveDownloads->value(); } int OptionsDialog::getMaxActiveUploads() const { return m_ui->spinMaxActiveUploads->value(); } int OptionsDialog::getMaxActiveTorrents() const { return m_ui->spinMaxActiveTorrents->value(); } bool OptionsDialog::isQueueingSystemEnabled() const { return m_ui->checkEnableQueueing->isChecked(); } bool OptionsDialog::isDHTEnabled() const { return m_ui->checkDHT->isChecked(); } bool OptionsDialog::isLSDEnabled() const { return m_ui->checkLSD->isChecked(); } bool OptionsDialog::isUPnPEnabled() const { return m_ui->checkUPnP->isChecked(); } bool OptionsDialog::startMinimized() const { return m_ui->checkStartMinimized->isChecked(); } #ifndef Q_OS_MACOS bool OptionsDialog::systemTrayEnabled() const { return QSystemTrayIcon::isSystemTrayAvailable() ? m_ui->checkShowSystray->isChecked() : false; } bool OptionsDialog::minimizeToTray() const { if (!m_ui->checkShowSystray->isChecked()) return false; return m_ui->checkMinimizeToSysTray->isChecked(); } bool OptionsDialog::closeToTray() const { if (!m_ui->checkShowSystray->isChecked()) return false; return m_ui->checkCloseToSystray->isChecked(); } #endif // Q_OS_MACOS // Return Share ratio qreal OptionsDialog::getMaxRatio() const { if (m_ui->checkMaxRatio->isChecked()) return m_ui->spinMaxRatio->value(); return -1; } // Return Seeding Minutes int OptionsDialog::getMaxSeedingMinutes() const { if (m_ui->checkMaxSeedingMinutes->isChecked()) return m_ui->spinMaxSeedingMinutes->value(); return -1; } // Return max connections number int OptionsDialog::getMaxConnections() const { if (!m_ui->checkMaxConnections->isChecked()) return -1; return m_ui->spinMaxConnec->value(); } int OptionsDialog::getMaxConnectionsPerTorrent() const { if (!m_ui->checkMaxConnectionsPerTorrent->isChecked()) return -1; return m_ui->spinMaxConnecPerTorrent->value(); } int OptionsDialog::getMaxUploads() const { if (!m_ui->checkMaxUploads->isChecked()) return -1; return m_ui->spinMaxUploads->value(); } int OptionsDialog::getMaxUploadsPerTorrent() const { if (!m_ui->checkMaxUploadsPerTorrent->isChecked()) return -1; return m_ui->spinMaxUploadsPerTorrent->value(); } void OptionsDialog::on_buttonBox_accepted() { if (m_applyButton->isEnabled()) { if (!schedTimesOk()) { m_ui->tabSelection->setCurrentRow(TAB_SPEED); return; } if (!webUIAuthenticationOk()) { m_ui->tabSelection->setCurrentRow(TAB_WEBUI); return; } if (!isAlternativeWebUIPathValid()) { m_ui->tabSelection->setCurrentRow(TAB_WEBUI); return; } m_applyButton->setEnabled(false); this->hide(); saveOptions(); } accept(); } void OptionsDialog::applySettings() { if (!schedTimesOk()) { m_ui->tabSelection->setCurrentRow(TAB_SPEED); return; } if (!webUIAuthenticationOk()) { m_ui->tabSelection->setCurrentRow(TAB_WEBUI); return; } if (!isAlternativeWebUIPathValid()) { m_ui->tabSelection->setCurrentRow(TAB_WEBUI); return; } saveOptions(); } void OptionsDialog::closeEvent(QCloseEvent *e) { setAttribute(Qt::WA_DeleteOnClose); e->accept(); } void OptionsDialog::on_buttonBox_rejected() { setAttribute(Qt::WA_DeleteOnClose); reject(); } bool OptionsDialog::useAdditionDialog() const { return m_ui->checkAdditionDialog->isChecked(); } void OptionsDialog::enableApplyButton() { m_applyButton->setEnabled(true); } void OptionsDialog::toggleComboRatioLimitAct() { // Verify if the share action button must be enabled m_ui->comboRatioLimitAct->setEnabled(m_ui->checkMaxRatio->isChecked() || m_ui->checkMaxSeedingMinutes->isChecked()); } void OptionsDialog::enableProxy(const int index) { if (index >= 1) { // Any proxy type is used //enable m_ui->lblProxyIP->setEnabled(true); m_ui->textProxyIP->setEnabled(true); m_ui->lblProxyPort->setEnabled(true); m_ui->spinProxyPort->setEnabled(true); m_ui->checkProxyPeerConnections->setEnabled(true); if (index >= 2) { // SOCKS5 or HTTP m_ui->checkProxyAuth->setEnabled(true); m_ui->isProxyOnlyForTorrents->setEnabled(true); } else { m_ui->checkProxyAuth->setEnabled(false); m_ui->isProxyOnlyForTorrents->setEnabled(false); m_ui->isProxyOnlyForTorrents->setChecked(true); } } else { // No proxy // disable m_ui->lblProxyIP->setEnabled(false); m_ui->textProxyIP->setEnabled(false); m_ui->lblProxyPort->setEnabled(false); m_ui->spinProxyPort->setEnabled(false); m_ui->checkProxyPeerConnections->setEnabled(false); m_ui->isProxyOnlyForTorrents->setEnabled(false); m_ui->checkProxyAuth->setEnabled(false); } } bool OptionsDialog::isSplashScreenDisabled() const { return !m_ui->checkShowSplash->isChecked(); } #ifdef Q_OS_WIN bool OptionsDialog::WinStartup() const { return m_ui->checkStartup->isChecked(); } #endif bool OptionsDialog::preAllocateAllFiles() const { return m_ui->checkPreallocateAll->isChecked(); } bool OptionsDialog::addTorrentsInPause() const { return m_ui->checkStartPaused->isChecked(); } // Proxy settings bool OptionsDialog::isProxyEnabled() const { return m_ui->comboProxyType->currentIndex(); } bool OptionsDialog::isProxyAuthEnabled() const { return m_ui->checkProxyAuth->isChecked(); } QString OptionsDialog::getProxyIp() const { return m_ui->textProxyIP->text().trimmed(); } unsigned short OptionsDialog::getProxyPort() const { return m_ui->spinProxyPort->value(); } QString OptionsDialog::getProxyUsername() const { QString username = m_ui->textProxyUsername->text().trimmed(); return username; } QString OptionsDialog::getProxyPassword() const { QString password = m_ui->textProxyPassword->text(); password = password.trimmed(); return password; } // Locale Settings QString OptionsDialog::getLocale() const { return m_ui->comboI18n->itemData(m_ui->comboI18n->currentIndex(), Qt::UserRole).toString(); } void OptionsDialog::setLocale(const QString &localeStr) { QString name; if (localeStr.startsWith(u"eo", Qt::CaseInsensitive)) { name = u"eo"_qs; } else if (localeStr.startsWith(u"ltg", Qt::CaseInsensitive)) { name = u"ltg"_qs; } else { QLocale locale(localeStr); if (locale.language() == QLocale::Uzbek) name = u"uz@Latn"_qs; else if (locale.language() == QLocale::Azerbaijani) name = u"az@latin"_qs; else name = locale.name(); } // Attempt to find exact match int index = m_ui->comboI18n->findData(name, Qt::UserRole); if (index < 0) { //Attempt to find a language match without a country int pos = name.indexOf(u'_'); if (pos > -1) { QString lang = name.left(pos); index = m_ui->comboI18n->findData(lang, Qt::UserRole); } } if (index < 0) { // Unrecognized, use US English index = m_ui->comboI18n->findData(u"en"_qs, Qt::UserRole); Q_ASSERT(index >= 0); } m_ui->comboI18n->setCurrentIndex(index); } Path OptionsDialog::getTorrentExportDir() const { if (m_ui->checkExportDir->isChecked()) return m_ui->textExportDir->selectedPath(); return {}; } Path OptionsDialog::getFinishedTorrentExportDir() const { if (m_ui->checkExportDirFin->isChecked()) return m_ui->textExportDirFin->selectedPath(); return {}; } void OptionsDialog::on_addWatchedFolderButton_clicked() { Preferences *const pref = Preferences::instance(); const Path dir {QFileDialog::getExistingDirectory( this, tr("Select folder to monitor"), pref->getScanDirsLastPath().parentPath().toString())}; if (dir.isEmpty()) return; auto dialog = new WatchedFolderOptionsDialog({}, this); dialog->setModal(true); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, &QDialog::accepted, this, [this, dialog, dir, pref]() { try { auto watchedFoldersModel = static_cast(m_ui->scanFoldersView->model()); watchedFoldersModel->addFolder(dir, dialog->watchedFolderOptions()); pref->setScanDirsLastPath(dir); for (int i = 0; i < watchedFoldersModel->columnCount(); ++i) m_ui->scanFoldersView->resizeColumnToContents(i); enableApplyButton(); } catch (const RuntimeError &err) { QMessageBox::critical(this, tr("Adding entry failed"), err.message()); } }); dialog->open(); } void OptionsDialog::on_editWatchedFolderButton_clicked() { const QModelIndex selected = m_ui->scanFoldersView->selectionModel()->selectedIndexes().at(0); editWatchedFolderOptions(selected); } void OptionsDialog::on_removeWatchedFolderButton_clicked() { const QModelIndexList selected = m_ui->scanFoldersView->selectionModel()->selectedIndexes(); for (const QModelIndex &index : selected) m_ui->scanFoldersView->model()->removeRow(index.row()); } void OptionsDialog::handleWatchedFolderViewSelectionChanged() { const QModelIndexList selectedIndexes = m_ui->scanFoldersView->selectionModel()->selectedIndexes(); m_ui->removeWatchedFolderButton->setEnabled(!selectedIndexes.isEmpty()); m_ui->editWatchedFolderButton->setEnabled(selectedIndexes.count() == 1); } void OptionsDialog::editWatchedFolderOptions(const QModelIndex &index) { if (!index.isValid()) return; auto watchedFoldersModel = static_cast(m_ui->scanFoldersView->model()); auto dialog = new WatchedFolderOptionsDialog(watchedFoldersModel->folderOptions(index.row()), this); dialog->setModal(true); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, &QDialog::accepted, this, [this, dialog, index, watchedFoldersModel]() { if (index.isValid()) { // The index could be invalidated while the dialog was displayed, // for example, if you deleted the folder using the Web API. watchedFoldersModel->setFolderOptions(index.row(), dialog->watchedFolderOptions()); enableApplyButton(); } }); dialog->open(); } // Return Filter object to apply to BT session Path OptionsDialog::getFilter() const { return m_ui->textFilterPath->selectedPath(); } // Web UI bool OptionsDialog::isWebUiEnabled() const { return m_ui->checkWebUi->isChecked(); } QString OptionsDialog::webUiUsername() const { return m_ui->textWebUiUsername->text(); } QString OptionsDialog::webUiPassword() const { return m_ui->textWebUiPassword->text(); } void OptionsDialog::webUIHttpsCertChanged(const Path &path, const ShowError showError) { m_ui->textWebUIHttpsCert->setSelectedPath(path); m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(u"security-low"_qs), this, 24)); if (path.isEmpty()) return; QFile file {path.data()}; if (!file.open(QIODevice::ReadOnly)) { if (showError == ShowError::Show) QMessageBox::warning(this, tr("Invalid path"), file.errorString()); return; } if (!Utils::Net::isSSLCertificatesValid(file.read(Utils::Net::MAX_SSL_FILE_SIZE))) { if (showError == ShowError::Show) QMessageBox::warning(this, tr("Invalid certificate"), tr("This is not a valid SSL certificate.")); return; } m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(u"security-high"_qs), this, 24)); } void OptionsDialog::webUIHttpsKeyChanged(const Path &path, const ShowError showError) { m_ui->textWebUIHttpsKey->setSelectedPath(path); m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(u"security-low"_qs), this, 24)); if (path.isEmpty()) return; QFile file {path.data()}; if (!file.open(QIODevice::ReadOnly)) { if (showError == ShowError::Show) QMessageBox::warning(this, tr("Invalid path"), file.errorString()); return; } if (!Utils::Net::isSSLKeyValid(file.read(Utils::Net::MAX_SSL_FILE_SIZE))) { if (showError == ShowError::Show) QMessageBox::warning(this, tr("Invalid key"), tr("This is not a valid SSL key.")); return; } m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(u"security-high"_qs), this, 24)); } void OptionsDialog::showConnectionTab() { m_ui->tabSelection->setCurrentRow(TAB_CONNECTION); } void OptionsDialog::on_registerDNSBtn_clicked() { const auto service = static_cast(m_ui->comboDNSService->currentIndex()); QDesktopServices::openUrl(Net::DNSUpdater::getRegistrationUrl(service)); } void OptionsDialog::on_IpFilterRefreshBtn_clicked() { if (m_refreshingIpFilter) return; m_refreshingIpFilter = true; // Updating program preferences BitTorrent::Session *const session = BitTorrent::Session::instance(); session->setIPFilteringEnabled(true); session->setIPFilterFile({}); // forcing Session reload filter file session->setIPFilterFile(getFilter()); connect(session, &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed); setCursor(QCursor(Qt::WaitCursor)); } void OptionsDialog::handleIPFilterParsed(bool error, int ruleCount) { setCursor(QCursor(Qt::ArrowCursor)); if (error) QMessageBox::warning(this, tr("Parsing error"), tr("Failed to parse the provided IP filter")); else QMessageBox::information(this, tr("Successfully refreshed"), tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount)); m_refreshingIpFilter = false; disconnect(BitTorrent::Session::instance(), &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed); } bool OptionsDialog::schedTimesOk() { if (m_ui->timeEditScheduleFrom->time() == m_ui->timeEditScheduleTo->time()) { QMessageBox::warning(this, tr("Time Error"), tr("The start time and the end time can't be the same.")); return false; } return true; } bool OptionsDialog::webUIAuthenticationOk() { if (webUiUsername().length() < 3) { QMessageBox::warning(this, tr("Length Error"), tr("The Web UI username must be at least 3 characters long.")); return false; } if (!webUiPassword().isEmpty() && (webUiPassword().length() < 6)) { QMessageBox::warning(this, tr("Length Error"), tr("The Web UI password must be at least 6 characters long.")); return false; } return true; } bool OptionsDialog::isAlternativeWebUIPathValid() { if (m_ui->groupAltWebUI->isChecked() && m_ui->textWebUIRootFolder->selectedPath().isEmpty()) { QMessageBox::warning(this, tr("Location Error"), tr("The alternative Web UI files location cannot be blank.")); return false; } return true; } void OptionsDialog::on_banListButton_clicked() { auto dialog = new BanListOptionsDialog(this); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, &QDialog::accepted, this, &OptionsDialog::enableApplyButton); dialog->open(); } void OptionsDialog::on_IPSubnetWhitelistButton_clicked() { auto dialog = new IPSubnetWhitelistOptionsDialog(this); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, &QDialog::accepted, this, &OptionsDialog::enableApplyButton); dialog->open(); }