qBittorrent
optionsdialog.cpp
Go to the documentation of this file.
1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2006 Christophe Dumez <[email protected]>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * In addition, as a special exception, the copyright holders give permission to
20  * link this program with the OpenSSL project's "OpenSSL" library (or with
21  * modified versions of it that use the same license as the "OpenSSL" library),
22  * and distribute the linked executables. You must obey the GNU General Public
23  * License in all respects for all of the code used other than "OpenSSL". If you
24  * modify file(s), you may extend this exception to your version of the file(s),
25  * but you are not obligated to do so. If you do not wish to do so, delete this
26  * exception statement from your version.
27  */
28 
29 #include "optionsdialog.h"
30 
31 #include <cstdlib>
32 #include <limits>
33 
34 #include <QApplication>
35 #include <QCloseEvent>
36 #include <QDebug>
37 #include <QDesktopServices>
38 #include <QDialogButtonBox>
39 #include <QEvent>
40 #include <QFileDialog>
41 #include <QMessageBox>
42 #include <QSystemTrayIcon>
43 #include <QTranslator>
44 
46 #include "base/exceptions.h"
47 #include "base/global.h"
48 #include "base/net/dnsupdater.h"
49 #include "base/net/portforwarder.h"
51 #include "base/preferences.h"
53 #include "base/rss/rss_session.h"
54 #include "base/torrentfileguard.h"
56 #include "base/unicodestrings.h"
57 #include "base/utils/fs.h"
58 #include "base/utils/net.h"
59 #include "base/utils/password.h"
60 #include "base/utils/random.h"
61 #include "addnewtorrentdialog.h"
62 #include "advancedsettings.h"
63 #include "app/application.h"
64 #include "banlistoptionsdialog.h"
67 #include "ui_optionsdialog.h"
68 #include "uithememanager.h"
69 #include "utils.h"
71 #include "watchedfoldersmodel.h"
72 
73 #define SETTINGS_KEY(name) "OptionsDialog/" name
74 
75 namespace
76 {
77  QStringList translatedWeekdayNames()
78  {
79  // return translated strings from Monday to Sunday in user selected locale
80 
81  const QLocale locale {Preferences::instance()->getLocale()};
82  const QDate date {2018, 11, 5}; // Monday
83  QStringList ret;
84  for (int i = 0; i < 7; ++i)
85  ret.append(locale.toString(date.addDays(i), "dddd"));
86  return ret;
87  }
88 
89  QString languageToLocalizedString(const QLocale &locale)
90  {
91  switch (locale.language())
92  {
93  case QLocale::Arabic: return QString::fromUtf8(C_LOCALE_ARABIC);
94  case QLocale::Armenian: return QString::fromUtf8(C_LOCALE_ARMENIAN);
95  case QLocale::Azerbaijani: return QString::fromUtf8(C_LOCALE_AZERBAIJANI);
96  case QLocale::Basque: return QString::fromUtf8(C_LOCALE_BASQUE);
97  case QLocale::Bulgarian: return QString::fromUtf8(C_LOCALE_BULGARIAN);
98  case QLocale::Byelorussian: return QString::fromUtf8(C_LOCALE_BYELORUSSIAN);
99  case QLocale::Catalan: return QString::fromUtf8(C_LOCALE_CATALAN);
100  case QLocale::Chinese:
101  switch (locale.country())
102  {
103  case QLocale::China: return QString::fromUtf8(C_LOCALE_CHINESE_SIMPLIFIED);
104  case QLocale::HongKong: return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_HK);
105  default: return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_TW);
106  }
107  case QLocale::Croatian: return QString::fromUtf8(C_LOCALE_CROATIAN);
108  case QLocale::Czech: return QString::fromUtf8(C_LOCALE_CZECH);
109  case QLocale::Danish: return QString::fromUtf8(C_LOCALE_DANISH);
110  case QLocale::Dutch: return QString::fromUtf8(C_LOCALE_DUTCH);
111  case QLocale::English:
112  switch (locale.country())
113  {
114  case QLocale::Australia: return QString::fromUtf8(C_LOCALE_ENGLISH_AUSTRALIA);
115  case QLocale::UnitedKingdom: return QString::fromUtf8(C_LOCALE_ENGLISH_UNITEDKINGDOM);
116  default: return QString::fromUtf8(C_LOCALE_ENGLISH);
117  }
118  case QLocale::Estonian: return QString::fromUtf8(C_LOCALE_ESTONIAN);
119  case QLocale::Finnish: return QString::fromUtf8(C_LOCALE_FINNISH);
120  case QLocale::French: return QString::fromUtf8(C_LOCALE_FRENCH);
121  case QLocale::Galician: return QString::fromUtf8(C_LOCALE_GALICIAN);
122  case QLocale::Georgian: return QString::fromUtf8(C_LOCALE_GEORGIAN);
123  case QLocale::German: return QString::fromUtf8(C_LOCALE_GERMAN);
124  case QLocale::Greek: return QString::fromUtf8(C_LOCALE_GREEK);
125  case QLocale::Hebrew: return QString::fromUtf8(C_LOCALE_HEBREW);
126  case QLocale::Hindi: return QString::fromUtf8(C_LOCALE_HINDI);
127  case QLocale::Hungarian: return QString::fromUtf8(C_LOCALE_HUNGARIAN);
128  case QLocale::Icelandic: return QString::fromUtf8(C_LOCALE_ICELANDIC);
129  case QLocale::Indonesian: return QString::fromUtf8(C_LOCALE_INDONESIAN);
130  case QLocale::Italian: return QString::fromUtf8(C_LOCALE_ITALIAN);
131  case QLocale::Japanese: return QString::fromUtf8(C_LOCALE_JAPANESE);
132  case QLocale::Korean: return QString::fromUtf8(C_LOCALE_KOREAN);
133  case QLocale::Latvian: return QString::fromUtf8(C_LOCALE_LATVIAN);
134  case QLocale::Lithuanian: return QString::fromUtf8(C_LOCALE_LITHUANIAN);
135  case QLocale::Malay: return QString::fromUtf8(C_LOCALE_MALAY);
136  case QLocale::Mongolian: return QString::fromUtf8(C_LOCALE_MONGOLIAN);
137  case QLocale::NorwegianBokmal: return QString::fromUtf8(C_LOCALE_NORWEGIAN);
138  case QLocale::Occitan: return QString::fromUtf8(C_LOCALE_OCCITAN);
139  case QLocale::Persian: return QString::fromUtf8(C_LOCALE_PERSIAN);
140  case QLocale::Polish: return QString::fromUtf8(C_LOCALE_POLISH);
141  case QLocale::Portuguese:
142  if (locale.country() == QLocale::Brazil)
143  return QString::fromUtf8(C_LOCALE_PORTUGUESE_BRAZIL);
144  return QString::fromUtf8(C_LOCALE_PORTUGUESE);
145  case QLocale::Romanian: return QString::fromUtf8(C_LOCALE_ROMANIAN);
146  case QLocale::Russian: return QString::fromUtf8(C_LOCALE_RUSSIAN);
147  case QLocale::Serbian: return QString::fromUtf8(C_LOCALE_SERBIAN);
148  case QLocale::Slovak: return QString::fromUtf8(C_LOCALE_SLOVAK);
149  case QLocale::Slovenian: return QString::fromUtf8(C_LOCALE_SLOVENIAN);
150  case QLocale::Spanish: return QString::fromUtf8(C_LOCALE_SPANISH);
151  case QLocale::Swedish: return QString::fromUtf8(C_LOCALE_SWEDISH);
152  case QLocale::Thai: return QString::fromUtf8(C_LOCALE_THAI);
153  case QLocale::Turkish: return QString::fromUtf8(C_LOCALE_TURKISH);
154  case QLocale::Ukrainian: return QString::fromUtf8(C_LOCALE_UKRAINIAN);
155  case QLocale::Uzbek: return QString::fromUtf8(C_LOCALE_UZBEK);
156  case QLocale::Vietnamese: return QString::fromUtf8(C_LOCALE_VIETNAMESE);
157  default:
158  const QString lang = QLocale::languageToString(locale.language());
159  qWarning() << "Unrecognized language name: " << lang;
160  return lang;
161  }
162  }
163 }
164 
165 class WheelEventEater final : public QObject
166 {
167 public:
168  using QObject::QObject;
169 
170 private:
171  bool eventFilter(QObject *, QEvent *event) override
172  {
173  return (event->type() == QEvent::Wheel);
174  }
175 };
176 
177 // Constructor
179  : QDialog {parent}
180  , m_ui {new Ui::OptionsDialog}
181  , m_storeDialogSize {SETTINGS_KEY("Size")}
182  , m_storeHSplitterSize {SETTINGS_KEY("HorizontalSplitterSizes")}
183  , m_storeLastViewedPage {SETTINGS_KEY("LastViewedPage")}
184 {
185  qDebug("-> Constructing Options");
186  m_ui->setupUi(this);
187  setAttribute(Qt::WA_DeleteOnClose);
188  setModal(true);
189 
190 #if (defined(Q_OS_UNIX))
191  setWindowTitle(tr("Preferences"));
192 #endif
193 
194  // Icons
195  m_ui->tabSelection->item(TAB_UI)->setIcon(UIThemeManager::instance()->getIcon("preferences-desktop"));
196  m_ui->tabSelection->item(TAB_BITTORRENT)->setIcon(UIThemeManager::instance()->getIcon("preferences-system-network"));
197  m_ui->tabSelection->item(TAB_CONNECTION)->setIcon(UIThemeManager::instance()->getIcon("network-wired"));
198  m_ui->tabSelection->item(TAB_DOWNLOADS)->setIcon(UIThemeManager::instance()->getIcon("folder-download"));
199  m_ui->tabSelection->item(TAB_SPEED)->setIcon(UIThemeManager::instance()->getIcon("speedometer", "chronometer"));
200  m_ui->tabSelection->item(TAB_RSS)->setIcon(UIThemeManager::instance()->getIcon("rss-config", "application-rss+xml"));
201 #ifndef DISABLE_WEBUI
202  m_ui->tabSelection->item(TAB_WEBUI)->setIcon(UIThemeManager::instance()->getIcon("network-server"));
203 #else
204  m_ui->tabSelection->item(TAB_WEBUI)->setHidden(true);
205 #endif
206  m_ui->tabSelection->item(TAB_ADVANCED)->setIcon(UIThemeManager::instance()->getIcon("preferences-other"));
207 
208  // set uniform size for all icons
209  int maxHeight = -1;
210  for (int i = 0; i < m_ui->tabSelection->count(); ++i)
211  maxHeight = std::max(maxHeight, m_ui->tabSelection->visualItemRect(m_ui->tabSelection->item(i)).size().height());
212  for (int i = 0; i < m_ui->tabSelection->count(); ++i)
213  {
214  const QSize size(std::numeric_limits<int>::max(), static_cast<int>(maxHeight * 1.2));
215  m_ui->tabSelection->item(i)->setSizeHint(size);
216  }
217 
218  m_ui->IpFilterRefreshBtn->setIcon(UIThemeManager::instance()->getIcon("view-refresh"));
219 
220  m_ui->labelGlobalRate->setPixmap(Utils::Gui::scaledPixmap(UIThemeManager::instance()->getIcon(QLatin1String("slow_off")), this, 24));
221  m_ui->labelAltRate->setPixmap(Utils::Gui::scaledPixmap(UIThemeManager::instance()->getIcon(QLatin1String("slow")), this, 24));
222 
223  m_ui->deleteTorrentWarningIcon->setPixmap(QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical).pixmap(16, 16));
224  m_ui->deleteTorrentWarningIcon->hide();
225  m_ui->deleteTorrentWarningLabel->hide();
226  m_ui->deleteTorrentWarningLabel->setToolTip(QLatin1String("<html><body><p>") +
227  tr("By enabling these options, you can <strong>irrevocably lose</strong> your .torrent files!") +
228  QLatin1String("</p><p>") +
229  tr("When these options are enabled, qBittorrent will <strong>delete</strong> .torrent files "
230  "after they were successfully (the first option) or not (the second option) added to its "
231  "download queue. This will be applied <strong>not only</strong> to the files opened via "
232  "&ldquo;Add torrent&rdquo; menu action but to those opened via <strong>file type association</strong> as well") +
233  QLatin1String("</p><p>") +
234  tr("If you enable the second option (&ldquo;Also when addition is cancelled&rdquo;) the "
235  ".torrent file <strong>will be deleted</strong> even if you press &ldquo;<strong>Cancel</strong>&rdquo; in "
236  "the &ldquo;Add torrent&rdquo; dialog") +
237  QLatin1String("</p></body></html>"));
238 
239  m_ui->hsplitter->setCollapsible(0, false);
240  m_ui->hsplitter->setCollapsible(1, false);
241  // Get apply button in button box
242  m_applyButton = m_ui->buttonBox->button(QDialogButtonBox::Apply);
243  connect(m_applyButton, &QPushButton::clicked, this, &OptionsDialog::applySettings);
244 
245  auto watchedFoldersModel = new WatchedFoldersModel(TorrentFilesWatcher::instance(), this);
246  connect(watchedFoldersModel, &QAbstractListModel::dataChanged, this, &ThisType::enableApplyButton);
247  m_ui->scanFoldersView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
248  m_ui->scanFoldersView->setModel(watchedFoldersModel);
249  connect(m_ui->scanFoldersView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ThisType::handleWatchedFolderViewSelectionChanged);
250  connect(m_ui->scanFoldersView, &QTreeView::doubleClicked, this, &ThisType::editWatchedFolderOptions);
251 
252  // Languages supported
254 
255  m_ui->checkUseCustomTheme->setChecked(Preferences::instance()->useCustomUITheme());
256  m_ui->customThemeFilePath->setSelectedPath(Preferences::instance()->customUIThemePath());
257  m_ui->customThemeFilePath->setMode(FileSystemPathEdit::Mode::FileOpen);
258  m_ui->customThemeFilePath->setDialogCaption(tr("Select qBittorrent UI Theme file"));
259  m_ui->customThemeFilePath->setFileNameFilter(tr("qBittorrent UI Theme file (*.qbtheme config.json)"));
260 
261 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
262  m_ui->checkUseSystemIcon->setChecked(Preferences::instance()->useSystemIconTheme());
263 #else
264  m_ui->checkUseSystemIcon->setVisible(false);
265 #endif
266 
267  // Load week days (scheduler)
268  m_ui->comboBoxScheduleDays->addItems(translatedWeekdayNames());
269 
270  // Load options
271  loadOptions();
272 #ifdef Q_OS_MACOS
273  m_ui->checkShowSystray->setVisible(false);
274 #else
275  // Disable systray integration if it is not supported by the system
276  if (!QSystemTrayIcon::isSystemTrayAvailable())
277  {
278  m_ui->checkShowSystray->setChecked(false);
279  m_ui->checkShowSystray->setEnabled(false);
280  m_ui->labelTrayIconStyle->setVisible(false);
281  m_ui->comboTrayIcon->setVisible(false);
282  }
283 #endif
284 
285 #ifndef Q_OS_WIN
286  m_ui->checkStartup->setVisible(false);
287 #endif
288 
289 #if !(defined(Q_OS_WIN) || defined(Q_OS_MACOS))
290  m_ui->groupFileAssociation->setVisible(false);
291  m_ui->checkProgramUpdates->setVisible(false);
292 #endif
293 
294  m_ui->textWebUIRootFolder->setMode(FileSystemPathEdit::Mode::DirectoryOpen);
295  m_ui->textWebUIRootFolder->setDialogCaption(tr("Choose Alternative UI files location"));
296 
297  // Connect signals / slots
298  // Shortcuts for frequently used signals that have more than one overload. They would require
299  // type casts and that is why we declare required member pointer here instead.
300  void (QComboBox::*qComboBoxCurrentIndexChanged)(int) = &QComboBox::currentIndexChanged;
301  void (QSpinBox::*qSpinBoxValueChanged)(int) = &QSpinBox::valueChanged;
302 
303  connect(m_ui->comboProxyType, qComboBoxCurrentIndexChanged, this, &ThisType::enableProxy);
304 
305  // Apply button is activated when a value is changed
306  // Behavior tab
307  connect(m_ui->comboI18n, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
308  connect(m_ui->checkUseCustomTheme, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
309  connect(m_ui->customThemeFilePath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
310 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
311  connect(m_ui->checkUseSystemIcon, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
312 #endif
313  connect(m_ui->confirmDeletion, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
314  connect(m_ui->checkAltRowColors, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
315  connect(m_ui->checkHideZero, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
316  connect(m_ui->checkHideZero, &QAbstractButton::toggled, m_ui->comboHideZero, &QWidget::setEnabled);
317  connect(m_ui->comboHideZero, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
318  connect(m_ui->checkShowSystray, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
319  connect(m_ui->checkCloseToSystray, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
320  connect(m_ui->checkMinimizeToSysTray, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
321  connect(m_ui->checkStartMinimized, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
322 #ifdef Q_OS_WIN
323  connect(m_ui->checkStartup, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
324 #endif
325  connect(m_ui->checkShowSplash, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
326  connect(m_ui->checkProgramExitConfirm, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
327  connect(m_ui->checkProgramAutoExitConfirm, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
328  connect(m_ui->checkPreventFromSuspendWhenDownloading, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
329  connect(m_ui->checkPreventFromSuspendWhenSeeding, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
330  connect(m_ui->comboTrayIcon, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
331 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && !defined(QT_DBUS_LIB)
332  m_ui->checkPreventFromSuspendWhenDownloading->setDisabled(true);
333  m_ui->checkPreventFromSuspendWhenSeeding->setDisabled(true);
334 #endif
335 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
336  connect(m_ui->checkAssociateTorrents, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
337  connect(m_ui->checkAssociateMagnetLinks, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
338  connect(m_ui->checkProgramUpdates, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
339 #endif
340  connect(m_ui->checkFileLog, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
342  connect(m_ui->checkFileLogBackup, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
343  connect(m_ui->checkFileLogBackup, &QAbstractButton::toggled, m_ui->spinFileLogSize, &QWidget::setEnabled);
344  connect(m_ui->checkFileLogDelete, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
345  connect(m_ui->checkFileLogDelete, &QAbstractButton::toggled, m_ui->spinFileLogAge, &QWidget::setEnabled);
346  connect(m_ui->checkFileLogDelete, &QAbstractButton::toggled, m_ui->comboFileLogAgeType, &QWidget::setEnabled);
347  connect(m_ui->spinFileLogSize, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
348  connect(m_ui->spinFileLogAge, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
349  connect(m_ui->comboFileLogAgeType, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
350  // Downloads tab
352  connect(m_ui->checkUseSubcategories, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
353  connect(m_ui->comboSavingMode, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
354  connect(m_ui->comboTorrentCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
355  connect(m_ui->comboCategoryDefaultPathChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
356  connect(m_ui->comboCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
357  connect(m_ui->textDownloadPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
358  connect(m_ui->checkAppendqB, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
359  connect(m_ui->checkPreallocateAll, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
360  connect(m_ui->checkRecursiveDownload, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
361  connect(m_ui->checkAdditionDialog, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
362  connect(m_ui->checkAdditionDialogFront, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
363  connect(m_ui->checkStartPaused, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
364  connect(m_ui->contentLayoutComboBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
365  connect(m_ui->deleteTorrentBox, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
366  connect(m_ui->deleteCancelledTorrentBox, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
367  connect(m_ui->checkExportDir, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
368  connect(m_ui->checkExportDir, &QAbstractButton::toggled, m_ui->textExportDir, &QWidget::setEnabled);
369  connect(m_ui->checkExportDirFin, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
370  connect(m_ui->checkExportDirFin, &QAbstractButton::toggled, m_ui->textExportDirFin, &QWidget::setEnabled);
372  connect(m_ui->textExportDirFin, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
373  connect(m_ui->actionTorrentDlOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
374  connect(m_ui->actionTorrentFnOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
375  connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
376  connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, m_ui->textDownloadPath, &QWidget::setEnabled);
377  connect(m_ui->addWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
378  connect(m_ui->removeWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
379  connect(m_ui->groupMailNotification, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
380  connect(m_ui->senderEmailTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
381  connect(m_ui->lineEditDestEmail, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
382  connect(m_ui->lineEditSmtpServer, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
383  connect(m_ui->checkSmtpSSL, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
384  connect(m_ui->groupMailNotifAuth, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
385  connect(m_ui->mailNotifUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
386  connect(m_ui->mailNotifPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
387  connect(m_ui->autoRunBox, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
388  connect(m_ui->lineEditAutoRun, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
389  connect(m_ui->autoRunConsole, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
390 
391  const QString autoRunStr = QString("%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")
392  .arg(tr("Supported parameters (case sensitive):")
393  , tr("%N: Torrent name")
394  , tr("%L: Category")
395  , tr("%G: Tags (separated by comma)")
396  , tr("%F: Content path (same as root path for multifile torrent)")
397  , tr("%R: Root path (first torrent subdirectory path)")
398  , tr("%D: Save path")
399  , tr("%C: Number of files")
400  , tr("%Z: Torrent size (bytes)"))
401  .arg(tr("%T: Current tracker")
402  , tr("%I: Info hash v1 (or '-' if unavailable)")
403  , tr("%J: Info hash v2 (or '-' if unavailable)")
404  , tr("%K: Torrent ID (either sha-1 info hash for v1 torrent or truncated sha-256 info hash for v2/hybrid torrent)")
405  , tr("Tip: Encapsulate parameter with quotation marks to avoid text being cut off at whitespace (e.g., \"%N\")"));
406  m_ui->labelAutoRunParam->setText(autoRunStr);
407 
408  // Connection tab
409  connect(m_ui->comboProtocol, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
410  connect(m_ui->spinPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
411  connect(m_ui->checkUPnP, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
412  connect(m_ui->spinUploadLimit, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
413  connect(m_ui->spinDownloadLimit, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
414  connect(m_ui->spinUploadLimitAlt, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
415  connect(m_ui->spinDownloadLimitAlt, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
416  connect(m_ui->groupBoxSchedule, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
417  connect(m_ui->timeEditScheduleFrom, &QDateTimeEdit::timeChanged, this, &ThisType::enableApplyButton);
418  connect(m_ui->timeEditScheduleTo, &QDateTimeEdit::timeChanged, this, &ThisType::enableApplyButton);
419  connect(m_ui->comboBoxScheduleDays, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
420  connect(m_ui->checkLimituTPConnections, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
421  connect(m_ui->checkLimitTransportOverhead, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
422  connect(m_ui->checkLimitLocalPeerRate, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
423  // Bittorrent tab
424  connect(m_ui->checkMaxConnections, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
425  connect(m_ui->checkMaxConnectionsPerTorrent, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
426  connect(m_ui->checkMaxUploads, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
427  connect(m_ui->checkMaxUploadsPerTorrent, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
428  connect(m_ui->spinMaxConnec, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
429  connect(m_ui->spinMaxConnecPerTorrent, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
430  connect(m_ui->spinMaxUploads, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
431  connect(m_ui->spinMaxUploadsPerTorrent, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
432  connect(m_ui->checkDHT, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
433  connect(m_ui->checkAnonymousMode, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
434  connect(m_ui->checkPeX, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
435  connect(m_ui->checkLSD, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
436  connect(m_ui->comboEncryption, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
437  connect(m_ui->checkMaxRatio, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
438  connect(m_ui->checkMaxRatio, &QAbstractButton::toggled, this, &ThisType::toggleComboRatioLimitAct);
439  connect(m_ui->spinMaxRatio, qOverload<double>(&QDoubleSpinBox::valueChanged),
441  connect(m_ui->comboRatioLimitAct, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
442  connect(m_ui->checkMaxSeedingMinutes, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
443  connect(m_ui->checkMaxSeedingMinutes, &QAbstractButton::toggled, this, &ThisType::toggleComboRatioLimitAct);
444  connect(m_ui->spinMaxSeedingMinutes, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
445  // Proxy tab
446  connect(m_ui->comboProxyType, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
447  connect(m_ui->textProxyIP, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
448  connect(m_ui->spinProxyPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
449  connect(m_ui->checkProxyPeerConnections, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
450  connect(m_ui->isProxyOnlyForTorrents, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
451  connect(m_ui->checkProxyAuth, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
452  connect(m_ui->textProxyUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
453  connect(m_ui->textProxyPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
454  // Misc tab
455  connect(m_ui->checkIPFilter, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
456  connect(m_ui->checkIPFilter, &QAbstractButton::toggled, m_ui->textFilterPath, &QWidget::setEnabled);
457  connect(m_ui->checkIPFilter, &QAbstractButton::toggled, m_ui->IpFilterRefreshBtn, &QWidget::setEnabled);
458  connect(m_ui->checkIpFilterTrackers, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
460  connect(m_ui->checkEnableQueueing, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
461  connect(m_ui->spinMaxActiveDownloads, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
462  connect(m_ui->spinMaxActiveUploads, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
463  connect(m_ui->spinMaxActiveTorrents, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
464  connect(m_ui->checkIgnoreSlowTorrentsForQueueing, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
465  connect(m_ui->spinDownloadRateForSlowTorrents, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
466  connect(m_ui->spinUploadRateForSlowTorrents, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
467  connect(m_ui->spinSlowTorrentsInactivityTimer, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
468  connect(m_ui->checkEnableAddTrackers, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
469  connect(m_ui->textTrackers, &QPlainTextEdit::textChanged, this, &ThisType::enableApplyButton);
470 
471  const QString slowTorrentsExplanation = QLatin1String("<html><body><p>")
472  + tr("A torrent will be considered slow if its download and upload rates stay below these values for \"Torrent inactivity timer\" seconds")
473  + QLatin1String("</p></body></html>");
474  m_ui->labelDownloadRateForSlowTorrents->setToolTip(slowTorrentsExplanation);
475  m_ui->labelUploadRateForSlowTorrents->setToolTip(slowTorrentsExplanation);
476  m_ui->labelSlowTorrentInactivityTimer->setToolTip(slowTorrentsExplanation);
477 
478 #ifndef DISABLE_WEBUI
479  // Web UI tab
480  m_ui->textWebUIHttpsCert->setMode(FileSystemPathEdit::Mode::FileOpen);
481  m_ui->textWebUIHttpsCert->setFileNameFilter(tr("Certificate") + QLatin1String(" (*.cer *.crt *.pem)"));
482  m_ui->textWebUIHttpsCert->setDialogCaption(tr("Select certificate"));
483  m_ui->textWebUIHttpsKey->setMode(FileSystemPathEdit::Mode::FileOpen);
484  m_ui->textWebUIHttpsKey->setFileNameFilter(tr("Private key") + QLatin1String(" (*.key *.pem)"));
485  m_ui->textWebUIHttpsKey->setDialogCaption(tr("Select private key"));
486 
487  connect(m_ui->textServerDomains, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
488  connect(m_ui->checkWebUi, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
489  connect(m_ui->textWebUiAddress, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
490  connect(m_ui->spinWebUiPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
491  connect(m_ui->checkWebUIUPnP, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
492  connect(m_ui->checkWebUiHttps, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
494  connect(m_ui->textWebUIHttpsCert, &FileSystemPathLineEdit::selectedPathChanged, this, [this](const QString &s) { webUIHttpsCertChanged(s, ShowError::Show); });
496  connect(m_ui->textWebUIHttpsKey, &FileSystemPathLineEdit::selectedPathChanged, this, [this](const QString &s) { webUIHttpsKeyChanged(s, ShowError::Show); });
497  connect(m_ui->textWebUiUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
498  connect(m_ui->textWebUiPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
499  connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
500  connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
501  connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, m_ui->IPSubnetWhitelistButton, &QPushButton::setEnabled);
502  connect(m_ui->spinBanCounter, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
503  connect(m_ui->spinBanDuration, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
504  connect(m_ui->spinSessionTimeout, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
505  connect(m_ui->checkClickjacking, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
506  connect(m_ui->checkCSRFProtection, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
507  connect(m_ui->checkWebUiHttps, &QGroupBox::toggled, m_ui->checkSecureCookie, &QWidget::setEnabled);
508  connect(m_ui->checkSecureCookie, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
509  connect(m_ui->groupHostHeaderValidation, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
510  connect(m_ui->checkDynDNS, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
511  connect(m_ui->comboDNSService, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
512  connect(m_ui->domainNameTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
513  connect(m_ui->DNSUsernameTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
514  connect(m_ui->DNSPasswordTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
515  connect(m_ui->groupAltWebUI, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
516  connect(m_ui->textWebUIRootFolder, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
517  connect(m_ui->groupWebUIAddCustomHTTPHeaders, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
518  connect(m_ui->textWebUICustomHTTPHeaders, &QPlainTextEdit::textChanged, this, &OptionsDialog::enableApplyButton);
519  connect(m_ui->groupEnableReverseProxySupport, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
520  connect(m_ui->textTrustedReverseProxiesList, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
521 #endif // DISABLE_WEBUI
522 
523  // RSS tab
524  connect(m_ui->checkRSSEnable, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton);
525  connect(m_ui->checkRSSAutoDownloaderEnable, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton);
526  connect(m_ui->textSmartEpisodeFilters, &QPlainTextEdit::textChanged, this, &OptionsDialog::enableApplyButton);
527  connect(m_ui->checkSmartFilterDownloadRepacks, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton);
528  connect(m_ui->spinRSSRefreshInterval, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton);
529  connect(m_ui->spinRSSMaxArticlesPerFeed, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton);
530  connect(m_ui->btnEditRules, &QPushButton::clicked, this, [this]()
531  {
532  auto *downloader = new AutomatedRssDownloader(this);
533  downloader->setAttribute(Qt::WA_DeleteOnClose);
534  downloader->open();
535  });
536 
537  // Disable apply Button
538  m_applyButton->setEnabled(false);
539  // Tab selection mechanism
540  connect(m_ui->tabSelection, &QListWidget::currentItemChanged, this, &ThisType::changePage);
541  // Load Advanced settings
542  m_advancedSettings = new AdvancedSettings(m_ui->tabAdvancedPage);
543  m_ui->advPageLayout->addWidget(m_advancedSettings);
545 
546  m_ui->textFileLogPath->setDialogCaption(tr("Choose a save directory"));
547  m_ui->textFileLogPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
548 
549  m_ui->textExportDir->setDialogCaption(tr("Choose export directory"));
550  m_ui->textExportDir->setMode(FileSystemPathEdit::Mode::DirectorySave);
551 
552  m_ui->textExportDirFin->setDialogCaption(tr("Choose export directory"));
553  m_ui->textExportDirFin->setMode(FileSystemPathEdit::Mode::DirectorySave);
554 
555  m_ui->textFilterPath->setDialogCaption(tr("Choose an IP filter file"));
556  m_ui->textFilterPath->setFileNameFilter(tr("All supported filters")
557  + QLatin1String(" (*.dat *.p2p *.p2b);;.dat (*.dat);;.p2p (*.p2p);;.p2b (*.p2b)"));
558 
559  m_ui->textSavePath->setDialogCaption(tr("Choose a save directory"));
560  m_ui->textSavePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
561 
562  m_ui->textDownloadPath->setDialogCaption(tr("Choose a save directory"));
563  m_ui->textDownloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
564 
565  // disable mouse wheel event on widgets to avoid mis-selection
566  auto *wheelEventEater = new WheelEventEater(this);
567  for (QComboBox *widget : asConst(findChildren<QComboBox *>()))
568  widget->installEventFilter(wheelEventEater);
569  for (QSpinBox *widget : asConst(findChildren<QSpinBox *>()))
570  widget->installEventFilter(wheelEventEater);
571 
572  m_ui->tabSelection->setCurrentRow(m_storeLastViewedPage);
573 
575  show();
576  // Have to be called after show(), because splitter width needed
578 }
579 
581 {
582  // List language files
583  const QDir langDir(":/lang");
584  const QStringList langFiles = langDir.entryList(QStringList("qbittorrent_*.qm"), QDir::Files);
585  for (const QString &langFile : langFiles)
586  {
587  QString localeStr = langFile.mid(12); // remove "qbittorrent_"
588  localeStr.chop(3); // Remove ".qm"
589  QString languageName;
590  if (localeStr.startsWith("eo", Qt::CaseInsensitive))
591  {
592  // QLocale doesn't work with that locale. Esperanto isn't a "real" language.
593  languageName = QString::fromUtf8(C_LOCALE_ESPERANTO);
594  }
595  else if (localeStr.startsWith("ltg", Qt::CaseInsensitive))
596  {
597  // QLocale doesn't work with that locale.
598  languageName = QString::fromUtf8(C_LOCALE_LATGALIAN);
599  }
600  else
601  {
602  QLocale locale(localeStr);
603  languageName = languageToLocalizedString(locale);
604  }
605  m_ui->comboI18n->addItem(/*QIcon(":/icons/flags/"+country+".svg"), */ languageName, localeStr);
606  qDebug() << "Supported locale:" << localeStr;
607  }
608 }
609 
610 // Main destructor
612 {
613  qDebug("-> destructing Options");
614 
615  // save dialog states
616  m_storeDialogSize = size();
617 
618  QStringList hSplitterSizes;
619  for (const int size : asConst(m_ui->hsplitter->sizes()))
620  hSplitterSizes.append(QString::number(size));
621  m_storeHSplitterSize = hSplitterSizes;
622 
623  m_storeLastViewedPage = m_ui->tabSelection->currentRow();
624 
625  delete m_ui;
626 }
627 
628 void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
629 {
630  if (!current)
631  current = previous;
632  m_ui->tabOption->setCurrentIndex(m_ui->tabSelection->row(current));
633 }
634 
636 {
637  // width has been modified, use height as width reference instead
638  const int width = Utils::Gui::scaledSize(this
639  , (m_ui->tabSelection->item(TAB_UI)->sizeHint().height() * 2));
640  const QStringList defaultSizes = {QString::number(width), QString::number(m_ui->hsplitter->width() - width)};
641 
642  QList<int> splitterSizes;
643  for (const QString &string : asConst(m_storeHSplitterSize.get(defaultSizes)))
644  splitterSizes.append(string.toInt());
645 
646  m_ui->hsplitter->setSizes(splitterSizes);
647 }
648 
650 {
651  m_applyButton->setEnabled(false);
652  Preferences *const pref = Preferences::instance();
653  // Load the translation
654  QString locale = getLocale();
655  if (pref->getLocale() != locale)
656  {
657  auto *translator = new QTranslator;
658  if (translator->load(QLatin1String(":/lang/qbittorrent_") + locale))
659  qDebug("%s locale recognized, using translation.", qUtf8Printable(locale));
660  else
661  qDebug("%s locale unrecognized, using default (en).", qUtf8Printable(locale));
662  qApp->installTranslator(translator);
663  }
664 
665  // Behavior preferences
666  pref->setLocale(locale);
667 
668  pref->setUseCustomUITheme(m_ui->checkUseCustomTheme->isChecked());
669  pref->setCustomUIThemePath(m_ui->customThemeFilePath->selectedPath());
670 
671 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
672  pref->useSystemIconTheme(m_ui->checkUseSystemIcon->isChecked());
673 #endif
674 
675  pref->setConfirmTorrentDeletion(m_ui->confirmDeletion->isChecked());
676  pref->setAlternatingRowColors(m_ui->checkAltRowColors->isChecked());
677  pref->setHideZeroValues(m_ui->checkHideZero->isChecked());
678  pref->setHideZeroComboValues(m_ui->comboHideZero->currentIndex());
679 #ifndef Q_OS_MACOS
681  pref->setTrayIconStyle(TrayIcon::Style(m_ui->comboTrayIcon->currentIndex()));
682  pref->setCloseToTray(closeToTray());
684 #endif
687  pref->setConfirmOnExit(m_ui->checkProgramExitConfirm->isChecked());
688  pref->setDontConfirmAutoExit(!m_ui->checkProgramAutoExitConfirm->isChecked());
689  pref->setPreventFromSuspendWhenDownloading(m_ui->checkPreventFromSuspendWhenDownloading->isChecked());
690  pref->setPreventFromSuspendWhenSeeding(m_ui->checkPreventFromSuspendWhenSeeding->isChecked());
691 #ifdef Q_OS_WIN
692  pref->setWinStartup(WinStartup());
693  // Windows: file association settings
694  Preferences::setTorrentFileAssoc(m_ui->checkAssociateTorrents->isChecked());
695  Preferences::setMagnetLinkAssoc(m_ui->checkAssociateMagnetLinks->isChecked());
696 #endif
697 #ifdef Q_OS_MACOS
698  if (m_ui->checkAssociateTorrents->isChecked())
699  {
700  Preferences::setTorrentFileAssoc();
701  m_ui->checkAssociateTorrents->setChecked(Preferences::isTorrentFileAssocSet());
702  m_ui->checkAssociateTorrents->setEnabled(!m_ui->checkAssociateTorrents->isChecked());
703  }
704  if (m_ui->checkAssociateMagnetLinks->isChecked())
705  {
706  Preferences::setMagnetLinkAssoc();
707  m_ui->checkAssociateMagnetLinks->setChecked(Preferences::isMagnetLinkAssocSet());
708  m_ui->checkAssociateMagnetLinks->setEnabled(!m_ui->checkAssociateMagnetLinks->isChecked());
709  }
710 #endif
711 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
712  pref->setUpdateCheckEnabled(m_ui->checkProgramUpdates->isChecked());
713 #endif
714  auto *const app = static_cast<Application *>(QCoreApplication::instance());
715  app->setFileLoggerPath(m_ui->textFileLogPath->selectedPath());
716  app->setFileLoggerBackup(m_ui->checkFileLogBackup->isChecked());
717  app->setFileLoggerMaxSize(m_ui->spinFileLogSize->value() * 1024);
718  app->setFileLoggerAge(m_ui->spinFileLogAge->value());
719  app->setFileLoggerAgeType(m_ui->comboFileLogAgeType->currentIndex());
720  app->setFileLoggerDeleteOld(m_ui->checkFileLogDelete->isChecked());
721  app->setFileLoggerEnabled(m_ui->checkFileLog->isChecked());
722  // End Behavior preferences
723 
724  RSS::Session::instance()->setRefreshInterval(m_ui->spinRSSRefreshInterval->value());
725  RSS::Session::instance()->setMaxArticlesPerFeed(m_ui->spinRSSMaxArticlesPerFeed->value());
726  RSS::Session::instance()->setProcessingEnabled(m_ui->checkRSSEnable->isChecked());
727  RSS::AutoDownloader::instance()->setProcessingEnabled(m_ui->checkRSSAutoDownloaderEnable->isChecked());
728  RSS::AutoDownloader::instance()->setSmartEpisodeFilters(m_ui->textSmartEpisodeFilters->toPlainText().split('\n', Qt::SkipEmptyParts));
729  RSS::AutoDownloader::instance()->setDownloadRepacks(m_ui->checkSmartFilterDownloadRepacks->isChecked());
730 
731  auto session = BitTorrent::Session::instance();
732 
733  // Downloads preferences
734  session->setSavePath(Utils::Fs::expandPathAbs(m_ui->textSavePath->selectedPath()));
735  session->setSubcategoriesEnabled(m_ui->checkUseSubcategories->isChecked());
736  session->setAutoTMMDisabledByDefault(m_ui->comboSavingMode->currentIndex() == 0);
737  session->setDisableAutoTMMWhenCategoryChanged(m_ui->comboTorrentCategoryChanged->currentIndex() == 1);
738  session->setDisableAutoTMMWhenCategorySavePathChanged(m_ui->comboCategoryChanged->currentIndex() == 1);
739  session->setDisableAutoTMMWhenDefaultSavePathChanged(m_ui->comboCategoryDefaultPathChanged->currentIndex() == 1);
740  session->setDownloadPathEnabled(m_ui->checkUseDownloadPath->isChecked());
741  session->setDownloadPath(Utils::Fs::expandPathAbs(m_ui->textDownloadPath->selectedPath()));
742  session->setAppendExtensionEnabled(m_ui->checkAppendqB->isChecked());
743  session->setPreallocationEnabled(preAllocateAllFiles());
744  pref->disableRecursiveDownload(!m_ui->checkRecursiveDownload->isChecked());
746  AddNewTorrentDialog::setTopLevel(m_ui->checkAdditionDialogFront->isChecked());
747  session->setAddTorrentPaused(addTorrentsInPause());
748  session->setTorrentContentLayout(static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex()));
749  auto watchedFoldersModel = static_cast<WatchedFoldersModel *>(m_ui->scanFoldersView->model());
750  watchedFoldersModel->apply();
751  session->setTorrentExportDirectory(getTorrentExportDir());
752  session->setFinishedTorrentExportDirectory(getFinishedTorrentExportDir());
753  pref->setMailNotificationEnabled(m_ui->groupMailNotification->isChecked());
754  pref->setMailNotificationSender(m_ui->senderEmailTxt->text());
755  pref->setMailNotificationEmail(m_ui->lineEditDestEmail->text());
756  pref->setMailNotificationSMTP(m_ui->lineEditSmtpServer->text());
757  pref->setMailNotificationSMTPSSL(m_ui->checkSmtpSSL->isChecked());
758  pref->setMailNotificationSMTPAuth(m_ui->groupMailNotifAuth->isChecked());
759  pref->setMailNotificationSMTPUsername(m_ui->mailNotifUsername->text());
760  pref->setMailNotificationSMTPPassword(m_ui->mailNotifPassword->text());
761  pref->setAutoRunEnabled(m_ui->autoRunBox->isChecked());
762  pref->setAutoRunProgram(m_ui->lineEditAutoRun->text().trimmed());
763 #if defined(Q_OS_WIN)
764  pref->setAutoRunConsoleEnabled(m_ui->autoRunConsole->isChecked());
765 #endif
766  pref->setActionOnDblClOnTorrentDl(m_ui->actionTorrentDlOnDblClBox->currentData().toInt());
767  pref->setActionOnDblClOnTorrentFn(m_ui->actionTorrentFnOnDblClBox->currentData().toInt());
768  TorrentFileGuard::setAutoDeleteMode(!m_ui->deleteTorrentBox->isChecked() ? TorrentFileGuard::Never
769  : !m_ui->deleteCancelledTorrentBox->isChecked() ? TorrentFileGuard::IfAdded
771  // End Downloads preferences
772 
773  // Connection preferences
774  session->setBTProtocol(static_cast<BitTorrent::BTProtocol>(m_ui->comboProtocol->currentIndex()));
775  session->setPort(getPort());
777  session->setGlobalDownloadSpeedLimit(m_ui->spinDownloadLimit->value() * 1024);
778  session->setGlobalUploadSpeedLimit(m_ui->spinUploadLimit->value() * 1024);
779  session->setAltGlobalDownloadSpeedLimit(m_ui->spinDownloadLimitAlt->value() * 1024);
780  session->setAltGlobalUploadSpeedLimit(m_ui->spinUploadLimitAlt->value() * 1024);
781  session->setUTPRateLimited(m_ui->checkLimituTPConnections->isChecked());
782  session->setIncludeOverheadInLimits(m_ui->checkLimitTransportOverhead->isChecked());
783  session->setIgnoreLimitsOnLAN(!m_ui->checkLimitLocalPeerRate->isChecked());
784  pref->setSchedulerStartTime(m_ui->timeEditScheduleFrom->time());
785  pref->setSchedulerEndTime(m_ui->timeEditScheduleTo->time());
786  pref->setSchedulerDays(static_cast<Scheduler::Days>(m_ui->comboBoxScheduleDays->currentIndex()));
787  session->setBandwidthSchedulerEnabled(m_ui->groupBoxSchedule->isChecked());
788 
789  auto proxyConfigManager = Net::ProxyConfigurationManager::instance();
790  Net::ProxyConfiguration proxyConf;
791  proxyConf.type = getProxyType();
792  proxyConf.ip = getProxyIp();
793  proxyConf.port = getProxyPort();
794  proxyConf.username = getProxyUsername();
795  proxyConf.password = getProxyPassword();
796  proxyConfigManager->setProxyOnlyForTorrents(m_ui->isProxyOnlyForTorrents->isChecked());
797  proxyConfigManager->setProxyConfiguration(proxyConf);
798 
799  session->setProxyPeerConnectionsEnabled(m_ui->checkProxyPeerConnections->isChecked());
800  // End Connection preferences
801 
802  // Bittorrent preferences
803  session->setMaxConnections(getMaxConnections());
804  session->setMaxConnectionsPerTorrent(getMaxConnectionsPerTorrent());
805  session->setMaxUploads(getMaxUploads());
806  session->setMaxUploadsPerTorrent(getMaxUploadsPerTorrent());
807  session->setDHTEnabled(isDHTEnabled());
808  session->setPeXEnabled(m_ui->checkPeX->isChecked());
809  session->setLSDEnabled(isLSDEnabled());
810  session->setEncryption(getEncryptionSetting());
811  session->setAnonymousModeEnabled(m_ui->checkAnonymousMode->isChecked());
812  session->setAddTrackersEnabled(m_ui->checkEnableAddTrackers->isChecked());
813  session->setAdditionalTrackers(m_ui->textTrackers->toPlainText());
814  session->setGlobalMaxRatio(getMaxRatio());
815  session->setGlobalMaxSeedingMinutes(getMaxSeedingMinutes());
816 
817  const QVector<MaxRatioAction> actIndex =
818  {
819  Pause,
820  Remove,
821  DeleteFiles,
823  };
824  session->setMaxRatioAction(actIndex.value(m_ui->comboRatioLimitAct->currentIndex()));
825  // End Bittorrent preferences
826 
827  // Misc preferences
828  // * IPFilter
829  session->setIPFilteringEnabled(isIPFilteringEnabled());
830  session->setTrackerFilteringEnabled(m_ui->checkIpFilterTrackers->isChecked());
831  session->setIPFilterFile(m_ui->textFilterPath->selectedPath());
832  // End IPFilter preferences
833  // Queueing system
834  session->setQueueingSystemEnabled(isQueueingSystemEnabled());
835  session->setMaxActiveDownloads(m_ui->spinMaxActiveDownloads->value());
836  session->setMaxActiveUploads(m_ui->spinMaxActiveUploads->value());
837  session->setMaxActiveTorrents(m_ui->spinMaxActiveTorrents->value());
838  session->setIgnoreSlowTorrentsForQueueing(m_ui->checkIgnoreSlowTorrentsForQueueing->isChecked());
839  session->setDownloadRateForSlowTorrents(m_ui->spinDownloadRateForSlowTorrents->value());
840  session->setUploadRateForSlowTorrents(m_ui->spinUploadRateForSlowTorrents->value());
841  session->setSlowTorrentsInactivityTimer(m_ui->spinSlowTorrentsInactivityTimer->value());
842  // End Queueing system preferences
843  // Web UI
845  if (isWebUiEnabled())
846  {
847  pref->setServerDomains(m_ui->textServerDomains->text());
848  pref->setWebUiAddress(m_ui->textWebUiAddress->text());
849  pref->setWebUiPort(m_ui->spinWebUiPort->value());
850  pref->setUPnPForWebUIPort(m_ui->checkWebUIUPnP->isChecked());
851  pref->setWebUiHttpsEnabled(m_ui->checkWebUiHttps->isChecked());
852  pref->setWebUIHttpsCertificatePath(m_ui->textWebUIHttpsCert->selectedPath());
853  pref->setWebUIHttpsKeyPath(m_ui->textWebUIHttpsKey->selectedPath());
854  pref->setWebUIMaxAuthFailCount(m_ui->spinBanCounter->value());
855  pref->setWebUIBanDuration(std::chrono::seconds {m_ui->spinBanDuration->value()});
856  pref->setWebUISessionTimeout(m_ui->spinSessionTimeout->value());
857  // Authentication
859  if (!webUiPassword().isEmpty())
861  pref->setWebUiLocalAuthEnabled(!m_ui->checkBypassLocalAuth->isChecked());
862  pref->setWebUiAuthSubnetWhitelistEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked());
863  // Security
864  pref->setWebUiClickjackingProtectionEnabled(m_ui->checkClickjacking->isChecked());
865  pref->setWebUiCSRFProtectionEnabled(m_ui->checkCSRFProtection->isChecked());
866  pref->setWebUiSecureCookieEnabled(m_ui->checkSecureCookie->isChecked());
867  pref->setWebUIHostHeaderValidationEnabled(m_ui->groupHostHeaderValidation->isChecked());
868  // DynDNS
869  pref->setDynDNSEnabled(m_ui->checkDynDNS->isChecked());
870  pref->setDynDNSService(static_cast<DNS::Service>(m_ui->comboDNSService->currentIndex()));
871  pref->setDynDomainName(m_ui->domainNameTxt->text());
872  pref->setDynDNSUsername(m_ui->DNSUsernameTxt->text());
873  pref->setDynDNSPassword(m_ui->DNSPasswordTxt->text());
874  // Alternative UI
875  pref->setAltWebUiEnabled(m_ui->groupAltWebUI->isChecked());
876  pref->setWebUiRootFolder(m_ui->textWebUIRootFolder->selectedPath());
877  // Custom HTTP headers
878  pref->setWebUICustomHTTPHeadersEnabled(m_ui->groupWebUIAddCustomHTTPHeaders->isChecked());
879  pref->setWebUICustomHTTPHeaders(m_ui->textWebUICustomHTTPHeaders->toPlainText());
880  // Reverse proxy
881  pref->setWebUIReverseProxySupportEnabled(m_ui->groupEnableReverseProxySupport->isChecked());
882  pref->setWebUITrustedReverseProxiesList(m_ui->textTrustedReverseProxiesList->text());
883  }
884  // End Web UI
885  // End preferences
886  // Save advanced settings
888  // Assume that user changed multiple settings
889  // so it's best to save immediately
890  pref->apply();
891 }
892 
894 {
895  return m_ui->checkIPFilter->isChecked();
896 }
897 
899 {
900  switch (m_ui->comboProxyType->currentIndex())
901  {
902  case 1:
903  return Net::ProxyType::SOCKS4;
904  case 2:
905  if (isProxyAuthEnabled())
907  return Net::ProxyType::SOCKS5;
908  case 3:
909  if (isProxyAuthEnabled())
911  return Net::ProxyType::HTTP;
912  default:
913  return Net::ProxyType::None;
914  }
915 }
916 
918 {
919  int intValue;
920  QString strValue;
921  bool fileLogBackup = true;
922  bool fileLogDelete = true;
923  const Preferences *const pref = Preferences::instance();
924 
925  // Behavior preferences
926  setLocale(pref->getLocale());
927  m_ui->confirmDeletion->setChecked(pref->confirmTorrentDeletion());
928  m_ui->checkAltRowColors->setChecked(pref->useAlternatingRowColors());
929  m_ui->checkHideZero->setChecked(pref->getHideZeroValues());
930  m_ui->comboHideZero->setEnabled(m_ui->checkHideZero->isChecked());
931  m_ui->comboHideZero->setCurrentIndex(pref->getHideZeroComboValues());
932 
933  m_ui->checkShowSplash->setChecked(!pref->isSplashScreenDisabled());
934  m_ui->checkStartMinimized->setChecked(pref->startMinimized());
935  m_ui->checkProgramExitConfirm->setChecked(pref->confirmOnExit());
936  m_ui->checkProgramAutoExitConfirm->setChecked(!pref->dontConfirmAutoExit());
937 
938 #ifndef Q_OS_MACOS
939  m_ui->checkShowSystray->setChecked(pref->systemTrayEnabled());
940  if (m_ui->checkShowSystray->isChecked())
941  {
942  m_ui->checkMinimizeToSysTray->setChecked(pref->minimizeToTray());
943  m_ui->checkCloseToSystray->setChecked(pref->closeToTray());
944  m_ui->comboTrayIcon->setCurrentIndex(static_cast<int>(pref->trayIconStyle()));
945  }
946 #endif
947 
948  m_ui->checkPreventFromSuspendWhenDownloading->setChecked(pref->preventFromSuspendWhenDownloading());
949  m_ui->checkPreventFromSuspendWhenSeeding->setChecked(pref->preventFromSuspendWhenSeeding());
950 
951 #ifdef Q_OS_WIN
952  m_ui->checkStartup->setChecked(pref->WinStartup());
953  m_ui->checkAssociateTorrents->setChecked(Preferences::isTorrentFileAssocSet());
954  m_ui->checkAssociateMagnetLinks->setChecked(Preferences::isMagnetLinkAssocSet());
955 #endif
956 #ifdef Q_OS_MACOS
957  m_ui->checkAssociateTorrents->setChecked(Preferences::isTorrentFileAssocSet());
958  m_ui->checkAssociateTorrents->setEnabled(!m_ui->checkAssociateTorrents->isChecked());
959  m_ui->checkAssociateMagnetLinks->setChecked(Preferences::isMagnetLinkAssocSet());
960  m_ui->checkAssociateMagnetLinks->setEnabled(!m_ui->checkAssociateMagnetLinks->isChecked());
961 #endif
962 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
963  m_ui->checkProgramUpdates->setChecked(pref->isUpdateCheckEnabled());
964 #endif
965 
966  const Application *const app = static_cast<Application*>(QCoreApplication::instance());
967  m_ui->checkFileLog->setChecked(app->isFileLoggerEnabled());
968  m_ui->textFileLogPath->setSelectedPath(app->fileLoggerPath());
969  fileLogBackup = app->isFileLoggerBackup();
970  m_ui->checkFileLogBackup->setChecked(fileLogBackup);
971  m_ui->spinFileLogSize->setEnabled(fileLogBackup);
972  fileLogDelete = app->isFileLoggerDeleteOld();
973  m_ui->checkFileLogDelete->setChecked(fileLogDelete);
974  m_ui->spinFileLogAge->setEnabled(fileLogDelete);
975  m_ui->comboFileLogAgeType->setEnabled(fileLogDelete);
976  m_ui->spinFileLogSize->setValue(app->fileLoggerMaxSize() / 1024);
977  m_ui->spinFileLogAge->setValue(app->fileLoggerAge());
978  m_ui->comboFileLogAgeType->setCurrentIndex(app->fileLoggerAgeType());
979  // End Behavior preferences
980 
981  m_ui->checkRSSEnable->setChecked(RSS::Session::instance()->isProcessingEnabled());
982  m_ui->checkRSSAutoDownloaderEnable->setChecked(RSS::AutoDownloader::instance()->isProcessingEnabled());
983  m_ui->textSmartEpisodeFilters->setPlainText(RSS::AutoDownloader::instance()->smartEpisodeFilters().join('\n'));
984  m_ui->checkSmartFilterDownloadRepacks->setChecked(RSS::AutoDownloader::instance()->downloadRepacks());
985 
986  m_ui->spinRSSRefreshInterval->setValue(RSS::Session::instance()->refreshInterval());
987  m_ui->spinRSSMaxArticlesPerFeed->setValue(RSS::Session::instance()->maxArticlesPerFeed());
988 
989  const auto *session = BitTorrent::Session::instance();
990 
991  // Downloads preferences
992  m_ui->checkAdditionDialog->setChecked(AddNewTorrentDialog::isEnabled());
993  m_ui->checkAdditionDialogFront->setChecked(AddNewTorrentDialog::isTopLevel());
994  m_ui->checkStartPaused->setChecked(session->isAddTorrentPaused());
995  m_ui->contentLayoutComboBox->setCurrentIndex(static_cast<int>(session->torrentContentLayout()));
997  m_ui->deleteTorrentBox->setChecked(autoDeleteMode != TorrentFileGuard::Never);
998  m_ui->deleteCancelledTorrentBox->setChecked(autoDeleteMode == TorrentFileGuard::Always);
999 
1000  m_ui->textSavePath->setSelectedPath(session->savePath());
1001  m_ui->checkUseSubcategories->setChecked(session->isSubcategoriesEnabled());
1002  m_ui->comboSavingMode->setCurrentIndex(!session->isAutoTMMDisabledByDefault());
1003  m_ui->comboTorrentCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategoryChanged());
1004  m_ui->comboCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategorySavePathChanged());
1005  m_ui->comboCategoryDefaultPathChanged->setCurrentIndex(session->isDisableAutoTMMWhenDefaultSavePathChanged());
1006  m_ui->checkUseDownloadPath->setChecked(session->isDownloadPathEnabled());
1007  m_ui->textDownloadPath->setEnabled(m_ui->checkUseDownloadPath->isChecked());
1008  m_ui->textDownloadPath->setEnabled(m_ui->checkUseDownloadPath->isChecked());
1009  m_ui->textDownloadPath->setSelectedPath(Utils::Fs::toNativePath(session->downloadPath()));
1010  m_ui->checkAppendqB->setChecked(session->isAppendExtensionEnabled());
1011  m_ui->checkPreallocateAll->setChecked(session->isPreallocationEnabled());
1012  m_ui->checkRecursiveDownload->setChecked(!pref->recursiveDownloadDisabled());
1013 
1014  strValue = session->torrentExportDirectory();
1015  if (strValue.isEmpty())
1016  {
1017  // Disable
1018  m_ui->checkExportDir->setChecked(false);
1019  m_ui->textExportDir->setEnabled(false);
1020  }
1021  else
1022  {
1023  // Enable
1024  m_ui->checkExportDir->setChecked(true);
1025  m_ui->textExportDir->setEnabled(true);
1026  m_ui->textExportDir->setSelectedPath(strValue);
1027  }
1028 
1029  strValue = session->finishedTorrentExportDirectory();
1030  if (strValue.isEmpty())
1031  {
1032  // Disable
1033  m_ui->checkExportDirFin->setChecked(false);
1034  m_ui->textExportDirFin->setEnabled(false);
1035  }
1036  else
1037  {
1038  // Enable
1039  m_ui->checkExportDirFin->setChecked(true);
1040  m_ui->textExportDirFin->setEnabled(true);
1041  m_ui->textExportDirFin->setSelectedPath(strValue);
1042  }
1043 
1044  m_ui->groupMailNotification->setChecked(pref->isMailNotificationEnabled());
1045  m_ui->senderEmailTxt->setText(pref->getMailNotificationSender());
1046  m_ui->lineEditDestEmail->setText(pref->getMailNotificationEmail());
1047  m_ui->lineEditSmtpServer->setText(pref->getMailNotificationSMTP());
1048  m_ui->checkSmtpSSL->setChecked(pref->getMailNotificationSMTPSSL());
1049  m_ui->groupMailNotifAuth->setChecked(pref->getMailNotificationSMTPAuth());
1050  m_ui->mailNotifUsername->setText(pref->getMailNotificationSMTPUsername());
1051  m_ui->mailNotifPassword->setText(pref->getMailNotificationSMTPPassword());
1052 
1053  m_ui->autoRunBox->setChecked(pref->isAutoRunEnabled());
1054  m_ui->lineEditAutoRun->setText(pref->getAutoRunProgram());
1055 #if defined(Q_OS_WIN)
1056  m_ui->autoRunConsole->setChecked(pref->isAutoRunConsoleEnabled());
1057 #else
1058  m_ui->autoRunConsole->hide();
1059 #endif
1060 
1061  m_ui->actionTorrentDlOnDblClBox->setItemData(0, TOGGLE_PAUSE);
1062  m_ui->actionTorrentDlOnDblClBox->setItemData(1, OPEN_DEST);
1063  m_ui->actionTorrentDlOnDblClBox->setItemData(2, PREVIEW_FILE);
1064  m_ui->actionTorrentDlOnDblClBox->setItemData(3, SHOW_OPTIONS);
1065  m_ui->actionTorrentDlOnDblClBox->setItemData(4, NO_ACTION);
1066  int actionDownloading = pref->getActionOnDblClOnTorrentDl();
1067  if ((actionDownloading < 0) || (actionDownloading >= m_ui->actionTorrentDlOnDblClBox->count()))
1068  actionDownloading = TOGGLE_PAUSE;
1069  m_ui->actionTorrentDlOnDblClBox->setCurrentIndex(m_ui->actionTorrentDlOnDblClBox->findData(actionDownloading));
1070 
1071  m_ui->actionTorrentFnOnDblClBox->setItemData(0, TOGGLE_PAUSE);
1072  m_ui->actionTorrentFnOnDblClBox->setItemData(1, OPEN_DEST);
1073  m_ui->actionTorrentFnOnDblClBox->setItemData(2, PREVIEW_FILE);
1074  m_ui->actionTorrentFnOnDblClBox->setItemData(3, SHOW_OPTIONS);
1075  m_ui->actionTorrentFnOnDblClBox->setItemData(4, NO_ACTION);
1076  int actionSeeding = pref->getActionOnDblClOnTorrentFn();
1077  if ((actionSeeding < 0) || (actionSeeding >= m_ui->actionTorrentFnOnDblClBox->count()))
1078  actionSeeding = OPEN_DEST;
1079  m_ui->actionTorrentFnOnDblClBox->setCurrentIndex(m_ui->actionTorrentFnOnDblClBox->findData(actionSeeding));
1080  // End Downloads preferences
1081 
1082  // Connection preferences
1083  m_ui->comboProtocol->setCurrentIndex(static_cast<int>(session->btProtocol()));
1084  m_ui->spinPort->setValue(session->port());
1085  m_ui->checkUPnP->setChecked(Net::PortForwarder::instance()->isEnabled());
1086 
1087  intValue = session->maxConnections();
1088  if (intValue > 0)
1089  {
1090  // enable
1091  m_ui->checkMaxConnections->setChecked(true);
1092  m_ui->spinMaxConnec->setEnabled(true);
1093  m_ui->spinMaxConnec->setValue(intValue);
1094  }
1095  else
1096  {
1097  // disable
1098  m_ui->checkMaxConnections->setChecked(false);
1099  m_ui->spinMaxConnec->setEnabled(false);
1100  }
1101  intValue = session->maxConnectionsPerTorrent();
1102  if (intValue > 0)
1103  {
1104  // enable
1105  m_ui->checkMaxConnectionsPerTorrent->setChecked(true);
1106  m_ui->spinMaxConnecPerTorrent->setEnabled(true);
1107  m_ui->spinMaxConnecPerTorrent->setValue(intValue);
1108  }
1109  else
1110  {
1111  // disable
1112  m_ui->checkMaxConnectionsPerTorrent->setChecked(false);
1113  m_ui->spinMaxConnecPerTorrent->setEnabled(false);
1114  }
1115  intValue = session->maxUploads();
1116  if (intValue > 0)
1117  {
1118  // enable
1119  m_ui->checkMaxUploads->setChecked(true);
1120  m_ui->spinMaxUploads->setEnabled(true);
1121  m_ui->spinMaxUploads->setValue(intValue);
1122  }
1123  else
1124  {
1125  // disable
1126  m_ui->checkMaxUploads->setChecked(false);
1127  m_ui->spinMaxUploads->setEnabled(false);
1128  }
1129  intValue = session->maxUploadsPerTorrent();
1130  if (intValue > 0)
1131  {
1132  // enable
1133  m_ui->checkMaxUploadsPerTorrent->setChecked(true);
1134  m_ui->spinMaxUploadsPerTorrent->setEnabled(true);
1135  m_ui->spinMaxUploadsPerTorrent->setValue(intValue);
1136  }
1137  else
1138  {
1139  // disable
1140  m_ui->checkMaxUploadsPerTorrent->setChecked(false);
1141  m_ui->spinMaxUploadsPerTorrent->setEnabled(false);
1142  }
1143 
1144  const auto *proxyConfigManager = Net::ProxyConfigurationManager::instance();
1145  Net::ProxyConfiguration proxyConf = proxyConfigManager->proxyConfiguration();
1146  using Net::ProxyType;
1147  bool useProxyAuth = false;
1148  switch (proxyConf.type)
1149  {
1150  case ProxyType::SOCKS4:
1151  m_ui->comboProxyType->setCurrentIndex(1);
1152  break;
1153 
1154  case ProxyType::SOCKS5_PW:
1155  useProxyAuth = true;
1156  // fallthrough
1157  case ProxyType::SOCKS5:
1158  m_ui->comboProxyType->setCurrentIndex(2);
1159  break;
1160 
1161  case ProxyType::HTTP_PW:
1162  useProxyAuth = true;
1163  // fallthrough
1164  case ProxyType::HTTP:
1165  m_ui->comboProxyType->setCurrentIndex(3);
1166  break;
1167 
1168  default:
1169  m_ui->comboProxyType->setCurrentIndex(0);
1170  }
1171  m_ui->textProxyIP->setText(proxyConf.ip);
1172  m_ui->spinProxyPort->setValue(proxyConf.port);
1173  m_ui->checkProxyAuth->setChecked(useProxyAuth);
1174  m_ui->textProxyUsername->setText(proxyConf.username);
1175  m_ui->textProxyPassword->setText(proxyConf.password);
1176 
1177  m_ui->checkProxyPeerConnections->setChecked(session->isProxyPeerConnectionsEnabled());
1178  m_ui->isProxyOnlyForTorrents->setChecked(proxyConfigManager->isProxyOnlyForTorrents());
1179  enableProxy(m_ui->comboProxyType->currentIndex());
1180 
1181  m_ui->checkIPFilter->setChecked(session->isIPFilteringEnabled());
1182  m_ui->textFilterPath->setEnabled(m_ui->checkIPFilter->isChecked());
1183  m_ui->textFilterPath->setSelectedPath(session->IPFilterFile());
1184  m_ui->IpFilterRefreshBtn->setEnabled(m_ui->checkIPFilter->isChecked());
1185  m_ui->checkIpFilterTrackers->setChecked(session->isTrackerFilteringEnabled());
1186  // End Connection preferences
1187 
1188  // Speed preferences
1189  m_ui->spinDownloadLimit->setValue(session->globalDownloadSpeedLimit() / 1024);
1190  m_ui->spinUploadLimit->setValue(session->globalUploadSpeedLimit() / 1024);
1191  m_ui->spinDownloadLimitAlt->setValue(session->altGlobalDownloadSpeedLimit() / 1024);
1192  m_ui->spinUploadLimitAlt->setValue(session->altGlobalUploadSpeedLimit() / 1024);
1193 
1194  m_ui->checkLimituTPConnections->setChecked(session->isUTPRateLimited());
1195  m_ui->checkLimitTransportOverhead->setChecked(session->includeOverheadInLimits());
1196  m_ui->checkLimitLocalPeerRate->setChecked(!session->ignoreLimitsOnLAN());
1197 
1198  m_ui->groupBoxSchedule->setChecked(session->isBandwidthSchedulerEnabled());
1199  m_ui->timeEditScheduleFrom->setTime(pref->getSchedulerStartTime());
1200  m_ui->timeEditScheduleTo->setTime(pref->getSchedulerEndTime());
1201  m_ui->comboBoxScheduleDays->setCurrentIndex(static_cast<int>(pref->getSchedulerDays()));
1202  // End Speed preferences
1203 
1204  // Bittorrent preferences
1205  m_ui->checkDHT->setChecked(session->isDHTEnabled());
1206  m_ui->checkPeX->setChecked(session->isPeXEnabled());
1207  m_ui->checkLSD->setChecked(session->isLSDEnabled());
1208  m_ui->comboEncryption->setCurrentIndex(session->encryption());
1209  m_ui->checkAnonymousMode->setChecked(session->isAnonymousModeEnabled());
1210  m_ui->checkEnableAddTrackers->setChecked(session->isAddTrackersEnabled());
1211  m_ui->textTrackers->setPlainText(session->additionalTrackers());
1212 
1213  m_ui->checkEnableQueueing->setChecked(session->isQueueingSystemEnabled());
1214  m_ui->spinMaxActiveDownloads->setValue(session->maxActiveDownloads());
1215  m_ui->spinMaxActiveUploads->setValue(session->maxActiveUploads());
1216  m_ui->spinMaxActiveTorrents->setValue(session->maxActiveTorrents());
1217  m_ui->checkIgnoreSlowTorrentsForQueueing->setChecked(session->ignoreSlowTorrentsForQueueing());
1218  m_ui->spinDownloadRateForSlowTorrents->setValue(session->downloadRateForSlowTorrents());
1219  m_ui->spinUploadRateForSlowTorrents->setValue(session->uploadRateForSlowTorrents());
1220  m_ui->spinSlowTorrentsInactivityTimer->setValue(session->slowTorrentsInactivityTimer());
1221 
1222  if (session->globalMaxRatio() >= 0.)
1223  {
1224  // Enable
1225  m_ui->checkMaxRatio->setChecked(true);
1226  m_ui->spinMaxRatio->setEnabled(true);
1227  m_ui->comboRatioLimitAct->setEnabled(true);
1228  m_ui->spinMaxRatio->setValue(session->globalMaxRatio());
1229  }
1230  else
1231  {
1232  // Disable
1233  m_ui->checkMaxRatio->setChecked(false);
1234  m_ui->spinMaxRatio->setEnabled(false);
1235  }
1236  if (session->globalMaxSeedingMinutes() >= 0)
1237  {
1238  // Enable
1239  m_ui->checkMaxSeedingMinutes->setChecked(true);
1240  m_ui->spinMaxSeedingMinutes->setEnabled(true);
1241  m_ui->spinMaxSeedingMinutes->setValue(session->globalMaxSeedingMinutes());
1242  }
1243  else
1244  {
1245  // Disable
1246  m_ui->checkMaxSeedingMinutes->setChecked(false);
1247  m_ui->spinMaxSeedingMinutes->setEnabled(false);
1248  }
1249  m_ui->comboRatioLimitAct->setEnabled((session->globalMaxSeedingMinutes() >= 0) || (session->globalMaxRatio() >= 0.));
1250 
1251  const QHash<MaxRatioAction, int> actIndex =
1252  {
1253  {Pause, 0},
1254  {Remove, 1},
1255  {DeleteFiles, 2},
1256  {EnableSuperSeeding, 3}
1257  };
1258  m_ui->comboRatioLimitAct->setCurrentIndex(actIndex.value(session->maxRatioAction()));
1259  // End Bittorrent preferences
1260 
1261  // Web UI preferences
1262  m_ui->textServerDomains->setText(pref->getServerDomains());
1263  m_ui->checkWebUi->setChecked(pref->isWebUiEnabled());
1264  m_ui->textWebUiAddress->setText(pref->getWebUiAddress());
1265  m_ui->spinWebUiPort->setValue(pref->getWebUiPort());
1266  m_ui->checkWebUIUPnP->setChecked(pref->useUPnPForWebUIPort());
1267  m_ui->checkWebUiHttps->setChecked(pref->isWebUiHttpsEnabled());
1270  m_ui->textWebUiUsername->setText(pref->getWebUiUsername());
1271  m_ui->checkBypassLocalAuth->setChecked(!pref->isWebUiLocalAuthEnabled());
1272  m_ui->checkBypassAuthSubnetWhitelist->setChecked(pref->isWebUiAuthSubnetWhitelistEnabled());
1273  m_ui->IPSubnetWhitelistButton->setEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked());
1274  m_ui->spinBanCounter->setValue(pref->getWebUIMaxAuthFailCount());
1275  m_ui->spinBanDuration->setValue(pref->getWebUIBanDuration().count());
1276  m_ui->spinSessionTimeout->setValue(pref->getWebUISessionTimeout());
1277 
1278  // Security
1279  m_ui->checkClickjacking->setChecked(pref->isWebUiClickjackingProtectionEnabled());
1280  m_ui->checkCSRFProtection->setChecked(pref->isWebUiCSRFProtectionEnabled());
1281  m_ui->checkSecureCookie->setEnabled(pref->isWebUiHttpsEnabled());
1282  m_ui->checkSecureCookie->setChecked(pref->isWebUiSecureCookieEnabled());
1283  m_ui->groupHostHeaderValidation->setChecked(pref->isWebUIHostHeaderValidationEnabled());
1284 
1285  m_ui->checkDynDNS->setChecked(pref->isDynDNSEnabled());
1286  m_ui->comboDNSService->setCurrentIndex(static_cast<int>(pref->getDynDNSService()));
1287  m_ui->domainNameTxt->setText(pref->getDynDomainName());
1288  m_ui->DNSUsernameTxt->setText(pref->getDynDNSUsername());
1289  m_ui->DNSPasswordTxt->setText(pref->getDynDNSPassword());
1290 
1291  m_ui->groupAltWebUI->setChecked(pref->isAltWebUiEnabled());
1292  m_ui->textWebUIRootFolder->setSelectedPath(pref->getWebUiRootFolder());
1293  // Custom HTTP headers
1294  m_ui->groupWebUIAddCustomHTTPHeaders->setChecked(pref->isWebUICustomHTTPHeadersEnabled());
1295  m_ui->textWebUICustomHTTPHeaders->setPlainText(pref->getWebUICustomHTTPHeaders());
1296  // Reverse proxy
1297  m_ui->groupEnableReverseProxySupport->setChecked(pref->isWebUIReverseProxySupportEnabled());
1298  m_ui->textTrustedReverseProxiesList->setText(pref->getWebUITrustedReverseProxiesList());
1299  // End Web UI preferences
1300 }
1301 
1302 // return min & max ports
1303 // [min, max]
1305 {
1306  return m_ui->spinPort->value();
1307 }
1308 
1310 {
1311  // Range [1024: 65535]
1312  m_ui->spinPort->setValue(Utils::Random::rand(1024, 65535));
1313 }
1314 
1316 {
1317  return m_ui->comboEncryption->currentIndex();
1318 }
1319 
1321 {
1322  return m_ui->spinMaxActiveDownloads->value();
1323 }
1324 
1326 {
1327  return m_ui->spinMaxActiveUploads->value();
1328 }
1329 
1331 {
1332  return m_ui->spinMaxActiveTorrents->value();
1333 }
1334 
1336 {
1337  return m_ui->checkEnableQueueing->isChecked();
1338 }
1339 
1341 {
1342  return m_ui->checkDHT->isChecked();
1343 }
1344 
1346 {
1347  return m_ui->checkLSD->isChecked();
1348 }
1349 
1351 {
1352  return m_ui->checkUPnP->isChecked();
1353 }
1354 
1356 {
1357  return m_ui->checkStartMinimized->isChecked();
1358 }
1359 
1360 #ifndef Q_OS_MACOS
1362 {
1363  return QSystemTrayIcon::isSystemTrayAvailable()
1364  ? m_ui->checkShowSystray->isChecked()
1365  : false;
1366 }
1367 
1369 {
1370  if (!m_ui->checkShowSystray->isChecked()) return false;
1371  return m_ui->checkMinimizeToSysTray->isChecked();
1372 }
1373 
1375 {
1376  if (!m_ui->checkShowSystray->isChecked()) return false;
1377  return m_ui->checkCloseToSystray->isChecked();
1378 }
1379 #endif // Q_OS_MACOS
1380 
1381 // Return Share ratio
1383 {
1384  if (m_ui->checkMaxRatio->isChecked())
1385  return m_ui->spinMaxRatio->value();
1386  return -1;
1387 }
1388 
1389 // Return Seeding Minutes
1391 {
1392  if (m_ui->checkMaxSeedingMinutes->isChecked())
1393  return m_ui->spinMaxSeedingMinutes->value();
1394  return -1;
1395 }
1396 
1397 // Return max connections number
1399 {
1400  if (!m_ui->checkMaxConnections->isChecked())
1401  return -1;
1402 
1403  return m_ui->spinMaxConnec->value();
1404 }
1405 
1407 {
1408  if (!m_ui->checkMaxConnectionsPerTorrent->isChecked())
1409  return -1;
1410 
1411  return m_ui->spinMaxConnecPerTorrent->value();
1412 }
1413 
1415 {
1416  if (!m_ui->checkMaxUploads->isChecked())
1417  return -1;
1418 
1419  return m_ui->spinMaxUploads->value();
1420 }
1421 
1423 {
1424  if (!m_ui->checkMaxUploadsPerTorrent->isChecked())
1425  return -1;
1426 
1427  return m_ui->spinMaxUploadsPerTorrent->value();
1428 }
1429 
1431 {
1432  if (m_applyButton->isEnabled())
1433  {
1434  if (!schedTimesOk())
1435  {
1436  m_ui->tabSelection->setCurrentRow(TAB_SPEED);
1437  return;
1438  }
1439  if (!webUIAuthenticationOk())
1440  {
1441  m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
1442  return;
1443  }
1445  {
1446  m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
1447  return;
1448  }
1449  m_applyButton->setEnabled(false);
1450  this->hide();
1451  saveOptions();
1452  }
1453 
1454  accept();
1455 }
1456 
1458 {
1459  if (!schedTimesOk())
1460  {
1461  m_ui->tabSelection->setCurrentRow(TAB_SPEED);
1462  return;
1463  }
1464  if (!webUIAuthenticationOk())
1465  {
1466  m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
1467  return;
1468  }
1470  {
1471  m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
1472  return;
1473  }
1474  saveOptions();
1475 }
1476 
1477 void OptionsDialog::closeEvent(QCloseEvent *e)
1478 {
1479  setAttribute(Qt::WA_DeleteOnClose);
1480  e->accept();
1481 }
1482 
1484 {
1485  setAttribute(Qt::WA_DeleteOnClose);
1486  reject();
1487 }
1488 
1490 {
1491  return m_ui->checkAdditionDialog->isChecked();
1492 }
1493 
1495 {
1496  m_applyButton->setEnabled(true);
1497 }
1498 
1500 {
1501  // Verify if the share action button must be enabled
1502  m_ui->comboRatioLimitAct->setEnabled(m_ui->checkMaxRatio->isChecked() || m_ui->checkMaxSeedingMinutes->isChecked());
1503 }
1504 
1505 void OptionsDialog::enableProxy(const int index)
1506 {
1507  if (index >= 1)
1508  { // Any proxy type is used
1509  //enable
1510  m_ui->lblProxyIP->setEnabled(true);
1511  m_ui->textProxyIP->setEnabled(true);
1512  m_ui->lblProxyPort->setEnabled(true);
1513  m_ui->spinProxyPort->setEnabled(true);
1514  m_ui->checkProxyPeerConnections->setEnabled(true);
1515  if (index >= 2)
1516  { // SOCKS5 or HTTP
1517  m_ui->checkProxyAuth->setEnabled(true);
1518  m_ui->isProxyOnlyForTorrents->setEnabled(true);
1519  }
1520  else
1521  {
1522  m_ui->checkProxyAuth->setEnabled(false);
1523  m_ui->isProxyOnlyForTorrents->setEnabled(false);
1524  m_ui->isProxyOnlyForTorrents->setChecked(true);
1525  }
1526  }
1527  else
1528  { // No proxy
1529  // disable
1530  m_ui->lblProxyIP->setEnabled(false);
1531  m_ui->textProxyIP->setEnabled(false);
1532  m_ui->lblProxyPort->setEnabled(false);
1533  m_ui->spinProxyPort->setEnabled(false);
1534  m_ui->checkProxyPeerConnections->setEnabled(false);
1535  m_ui->isProxyOnlyForTorrents->setEnabled(false);
1536  m_ui->checkProxyAuth->setEnabled(false);
1537  }
1538 }
1539 
1541 {
1542  return !m_ui->checkShowSplash->isChecked();
1543 }
1544 
1545 #ifdef Q_OS_WIN
1546 bool OptionsDialog::WinStartup() const
1547 {
1548  return m_ui->checkStartup->isChecked();
1549 }
1550 #endif
1551 
1553 {
1554  return m_ui->checkPreallocateAll->isChecked();
1555 }
1556 
1558 {
1559  return m_ui->checkStartPaused->isChecked();
1560 }
1561 
1562 // Proxy settings
1564 {
1565  return m_ui->comboProxyType->currentIndex();
1566 }
1567 
1569 {
1570  return m_ui->checkProxyAuth->isChecked();
1571 }
1572 
1574 {
1575  return m_ui->textProxyIP->text().trimmed();
1576 }
1577 
1578 unsigned short OptionsDialog::getProxyPort() const
1579 {
1580  return m_ui->spinProxyPort->value();
1581 }
1582 
1584 {
1585  QString username = m_ui->textProxyUsername->text().trimmed();
1586  return username;
1587 }
1588 
1590 {
1591  QString password = m_ui->textProxyPassword->text();
1592  password = password.trimmed();
1593  return password;
1594 }
1595 
1596 // Locale Settings
1598 {
1599  return m_ui->comboI18n->itemData(m_ui->comboI18n->currentIndex(), Qt::UserRole).toString();
1600 }
1601 
1602 void OptionsDialog::setLocale(const QString &localeStr)
1603 {
1604  QString name;
1605  if (localeStr.startsWith("eo", Qt::CaseInsensitive))
1606  {
1607  name = "eo";
1608  }
1609  else if (localeStr.startsWith("ltg", Qt::CaseInsensitive))
1610  {
1611  name = "ltg";
1612  }
1613  else
1614  {
1615  QLocale locale(localeStr);
1616  if (locale.language() == QLocale::Uzbek)
1617  name = "uz@Latn";
1618  else if (locale.language() == QLocale::Azerbaijani)
1619  name = "az@latin";
1620  else
1621  name = locale.name();
1622  }
1623  // Attempt to find exact match
1624  int index = m_ui->comboI18n->findData(name, Qt::UserRole);
1625  if (index < 0)
1626  {
1627  //Attempt to find a language match without a country
1628  int pos = name.indexOf('_');
1629  if (pos > -1)
1630  {
1631  QString lang = name.left(pos);
1632  index = m_ui->comboI18n->findData(lang, Qt::UserRole);
1633  }
1634  }
1635  if (index < 0)
1636  {
1637  // Unrecognized, use US English
1638  index = m_ui->comboI18n->findData("en", Qt::UserRole);
1639  Q_ASSERT(index >= 0);
1640  }
1641  m_ui->comboI18n->setCurrentIndex(index);
1642 }
1643 
1645 {
1646  if (m_ui->checkExportDir->isChecked())
1647  return Utils::Fs::expandPathAbs(m_ui->textExportDir->selectedPath());
1648  return {};
1649 }
1650 
1652 {
1653  if (m_ui->checkExportDirFin->isChecked())
1654  return Utils::Fs::expandPathAbs(m_ui->textExportDirFin->selectedPath());
1655  return {};
1656 }
1657 
1659 {
1660  Preferences *const pref = Preferences::instance();
1661  const QString dir = QFileDialog::getExistingDirectory(this, tr("Select folder to monitor"),
1663  if (dir.isEmpty())
1664  return;
1665 
1666  auto dialog = new WatchedFolderOptionsDialog({}, this);
1667  dialog->setModal(true);
1668  dialog->setAttribute(Qt::WA_DeleteOnClose);
1669  connect(dialog, &QDialog::accepted, this, [this, dialog, dir, pref]()
1670  {
1671  try
1672  {
1673  auto watchedFoldersModel = static_cast<WatchedFoldersModel *>(m_ui->scanFoldersView->model());
1674  watchedFoldersModel->addFolder(dir, dialog->watchedFolderOptions());
1675 
1676  pref->setScanDirsLastPath(dir);
1677 
1678  for (int i = 0; i < watchedFoldersModel->columnCount(); ++i)
1679  m_ui->scanFoldersView->resizeColumnToContents(i);
1680 
1682  }
1683  catch (const RuntimeError &err)
1684  {
1685  QMessageBox::critical(this, tr("Adding entry failed"), err.message());
1686  }
1687  });
1688 
1689  dialog->open();
1690 }
1691 
1693 {
1694  const QModelIndex selected
1695  = m_ui->scanFoldersView->selectionModel()->selectedIndexes().at(0);
1696 
1697  editWatchedFolderOptions(selected);
1698 }
1699 
1701 {
1702  const QModelIndexList selected
1703  = m_ui->scanFoldersView->selectionModel()->selectedIndexes();
1704 
1705  for (const QModelIndex &index : selected)
1706  m_ui->scanFoldersView->model()->removeRow(index.row());
1707 }
1708 
1710 {
1711  const QModelIndexList selectedIndexes = m_ui->scanFoldersView->selectionModel()->selectedIndexes();
1712  m_ui->removeWatchedFolderButton->setEnabled(!selectedIndexes.isEmpty());
1713  m_ui->editWatchedFolderButton->setEnabled(selectedIndexes.count() == 1);
1714 }
1715 
1716 void OptionsDialog::editWatchedFolderOptions(const QModelIndex &index)
1717 {
1718  if (!index.isValid())
1719  return;
1720 
1721  auto watchedFoldersModel = static_cast<WatchedFoldersModel *>(m_ui->scanFoldersView->model());
1722  auto dialog = new WatchedFolderOptionsDialog(watchedFoldersModel->folderOptions(index.row()), this);
1723  dialog->setModal(true);
1724  dialog->setAttribute(Qt::WA_DeleteOnClose);
1725  connect(dialog, &QDialog::accepted, this, [this, dialog, index, watchedFoldersModel]()
1726  {
1727  if (index.isValid())
1728  {
1729  // The index could be invalidated while the dialog was displayed,
1730  // for example, if you deleted the folder using the Web API.
1731  watchedFoldersModel->setFolderOptions(index.row(), dialog->watchedFolderOptions());
1732  enableApplyButton();
1733  }
1734  });
1735 
1736  dialog->open();
1737 }
1738 
1739 QString OptionsDialog::askForExportDir(const QString &currentExportPath)
1740 {
1741  QDir currentExportDir(Utils::Fs::expandPathAbs(currentExportPath));
1742  QString dir;
1743  if (!currentExportPath.isEmpty() && currentExportDir.exists())
1744  dir = QFileDialog::getExistingDirectory(this, tr("Choose export directory"), currentExportDir.absolutePath());
1745  else
1746  dir = QFileDialog::getExistingDirectory(this, tr("Choose export directory"), QDir::homePath());
1747  return dir;
1748 }
1749 
1750 // Return Filter object to apply to BT session
1752 {
1753  return m_ui->textFilterPath->selectedPath();
1754 }
1755 
1756 // Web UI
1757 
1759 {
1760  return m_ui->checkWebUi->isChecked();
1761 }
1762 
1764 {
1765  return m_ui->textWebUiUsername->text();
1766 }
1767 
1769 {
1770  return m_ui->textWebUiPassword->text();
1771 }
1772 
1773 void OptionsDialog::webUIHttpsCertChanged(const QString &path, const ShowError showError)
1774 {
1775  m_ui->textWebUIHttpsCert->setSelectedPath(path);
1776  m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(QLatin1String("security-low")), this, 24));
1777 
1778  if (path.isEmpty())
1779  return;
1780 
1781  QFile file(path);
1782  if (!file.open(QIODevice::ReadOnly))
1783  {
1784  if (showError == ShowError::Show)
1785  QMessageBox::warning(this, tr("Invalid path"), file.errorString());
1786  return;
1787  }
1788 
1790  {
1791  if (showError == ShowError::Show)
1792  QMessageBox::warning(this, tr("Invalid certificate"), tr("This is not a valid SSL certificate."));
1793  return;
1794  }
1795 
1796  m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(QLatin1String("security-high")), this, 24));
1797 }
1798 
1799 void OptionsDialog::webUIHttpsKeyChanged(const QString &path, const ShowError showError)
1800 {
1801  m_ui->textWebUIHttpsKey->setSelectedPath(path);
1802  m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(QLatin1String("security-low")), this, 24));
1803 
1804  if (path.isEmpty())
1805  return;
1806 
1807  QFile file(path);
1808  if (!file.open(QIODevice::ReadOnly))
1809  {
1810  if (showError == ShowError::Show)
1811  QMessageBox::warning(this, tr("Invalid path"), file.errorString());
1812  return;
1813  }
1814 
1816  {
1817  if (showError == ShowError::Show)
1818  QMessageBox::warning(this, tr("Invalid key"), tr("This is not a valid SSL key."));
1819  return;
1820  }
1821 
1822  m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(QLatin1String("security-high")), this, 24));
1823 }
1824 
1826 {
1827  m_ui->tabSelection->setCurrentRow(TAB_CONNECTION);
1828 }
1829 
1831 {
1832  const auto service = static_cast<DNS::Service>(m_ui->comboDNSService->currentIndex());
1833  QDesktopServices::openUrl(Net::DNSUpdater::getRegistrationUrl(service));
1834 }
1835 
1837 {
1838  if (m_refreshingIpFilter) return;
1839  m_refreshingIpFilter = true;
1840  // Updating program preferences
1842  session->setIPFilteringEnabled(true);
1843  session->setIPFilterFile(""); // forcing Session reload filter file
1844  session->setIPFilterFile(getFilter());
1846  setCursor(QCursor(Qt::WaitCursor));
1847 }
1848 
1849 void OptionsDialog::handleIPFilterParsed(bool error, int ruleCount)
1850 {
1851  setCursor(QCursor(Qt::ArrowCursor));
1852  if (error)
1853  QMessageBox::warning(this, tr("Parsing error"), tr("Failed to parse the provided IP filter"));
1854  else
1855  QMessageBox::information(this, tr("Successfully refreshed"), tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount));
1856  m_refreshingIpFilter = false;
1858 }
1859 
1861 {
1862  if (m_ui->timeEditScheduleFrom->time() == m_ui->timeEditScheduleTo->time())
1863  {
1864  QMessageBox::warning(this, tr("Time Error"), tr("The start time and the end time can't be the same."));
1865  return false;
1866  }
1867  return true;
1868 }
1869 
1871 {
1872  if (webUiUsername().length() < 3)
1873  {
1874  QMessageBox::warning(this, tr("Length Error"), tr("The Web UI username must be at least 3 characters long."));
1875  return false;
1876  }
1877  if (!webUiPassword().isEmpty() && (webUiPassword().length() < 6))
1878  {
1879  QMessageBox::warning(this, tr("Length Error"), tr("The Web UI password must be at least 6 characters long."));
1880  return false;
1881  }
1882  return true;
1883 }
1884 
1886 {
1887  if (m_ui->groupAltWebUI->isChecked() && m_ui->textWebUIRootFolder->selectedPath().trimmed().isEmpty())
1888  {
1889  QMessageBox::warning(this, tr("Location Error"), tr("The alternative Web UI files location cannot be blank."));
1890  return false;
1891  }
1892  return true;
1893 }
1894 
1896 {
1897  auto dialog = new BanListOptionsDialog(this);
1898  dialog->setAttribute(Qt::WA_DeleteOnClose);
1899  connect(dialog, &QDialog::accepted, this, &OptionsDialog::enableApplyButton);
1900  dialog->open();
1901 }
1902 
1904 {
1905  auto dialog = new IPSubnetWhitelistOptionsDialog(this);
1906  dialog->setAttribute(Qt::WA_DeleteOnClose);
1907  connect(dialog, &QDialog::accepted, this, &OptionsDialog::enableApplyButton);
1908  dialog->open();
1909 }
static void setEnabled(bool value)
static void setTopLevel(bool value)
void settingsChanged()
int fileLoggerAge() const
int fileLoggerAgeType() const
bool isFileLoggerEnabled() const
bool isFileLoggerDeleteOld() const
bool isFileLoggerBackup() const
QString fileLoggerPath() const
int fileLoggerMaxSize() const
void setFileLoggerPath(const QString &path)
void setIPFilterFile(QString path)
Definition: session.cpp:2938
static Session * instance()
Definition: session.cpp:997
void setIPFilteringEnabled(bool enabled)
Definition: session.cpp:2923
void IPFilterParsed(bool error, int ruleCount)
QString message() const noexcept
Definition: exceptions.cpp:36
@ DirectoryOpen
selecting existing directories
@ FileOpen
opening files, shows open file dialog
@ DirectorySave
selecting directories for saving
void selectedPathChanged(const QString &path)
static QUrl getRegistrationUrl(DNS::Service service)
Definition: dnsupdater.cpp:299
static PortForwarder * instance()
virtual void setEnabled(bool enabled)=0
static ProxyConfigurationManager * instance()
bool isAlternativeWebUIPathValid()
int getPort() const
bool addTorrentsInPause() const
QString getTorrentExportDir() const
void on_IpFilterRefreshBtn_clicked()
void initializeLanguageCombo()
bool isWebUiEnabled() const
void handleWatchedFolderViewSelectionChanged()
void on_banListButton_clicked()
bool isProxyEnabled() const
void setLocale(const QString &localeStr)
QString getLocale() const
void on_removeWatchedFolderButton_clicked()
int getMaxConnectionsPerTorrent() const
int getMaxUploadsPerTorrent() const
void changePage(QListWidgetItem *, QListWidgetItem *)
QString getProxyUsername() const
int getMaxConnections() const
bool isSplashScreenDisabled() const
QString getFinishedTorrentExportDir() const
bool systemTrayEnabled() const
bool isQueueingSystemEnabled() const
bool closeToTray() const
void on_randomButton_clicked()
int getMaxSeedingMinutes() const
QString getFilter() const
bool isDHTEnabled() const
void webUIHttpsKeyChanged(const QString &path, ShowError showError)
bool isLSDEnabled() const
~OptionsDialog() override
OptionsDialog(QWidget *parent=nullptr)
QString askForExportDir(const QString &currentExportPath)
void toggleComboRatioLimitAct()
bool isIPFilteringEnabled() const
void on_buttonBox_rejected()
AdvancedSettings * m_advancedSettings
int getMaxUploads() const
bool preAllocateAllFiles() const
void handleIPFilterParsed(bool error, int ruleCount)
void on_buttonBox_accepted()
bool useAdditionDialog() const
void on_IPSubnetWhitelistButton_clicked()
unsigned short getProxyPort() const
void showConnectionTab()
QString webUiUsername() const
void webUIHttpsCertChanged(const QString &path, ShowError showError)
Ui::OptionsDialog * m_ui
bool startMinimized() const
void on_addWatchedFolderButton_clicked()
int getMaxActiveTorrents() const
void on_registerDNSBtn_clicked()
qreal getMaxRatio() const
bool m_refreshingIpFilter
void enableApplyButton()
SettingValue< int > m_storeLastViewedPage
bool minimizeToTray() const
void on_editWatchedFolderButton_clicked()
void editWatchedFolderOptions(const QModelIndex &index)
void loadSplitterState()
SettingValue< QStringList > m_storeHSplitterSize
void closeEvent(QCloseEvent *e) override
bool isUPnPEnabled() const
QPushButton * m_applyButton
void enableProxy(int index)
QString webUiPassword() const
int getEncryptionSetting() const
SettingValue< QSize > m_storeDialogSize
int getMaxActiveUploads() const
QString getProxyPassword() const
int getMaxActiveDownloads() const
Net::ProxyType getProxyType() const
bool webUIAuthenticationOk()
bool isProxyAuthEnabled() const
QString getProxyIp() const
void setWebUIHttpsCertificatePath(const QString &path)
void disableRecursiveDownload(bool disable=true)
QString getMailNotificationSMTPUsername() const
void setWebUiRootFolder(const QString &path)
bool recursiveDownloadDisabled() const
void setActionOnDblClOnTorrentDl(int act)
QString getWebUICustomHTTPHeaders() const
QString getMailNotificationSMTPPassword() const
QString getAutoRunProgram() const
void setAltWebUiEnabled(bool enabled)
static Preferences * instance()
void setDynDNSEnabled(bool enabled)
int getWebUIMaxAuthFailCount() const
QString getWebUiRootFolder() const
QString getWebUIHttpsCertificatePath() const
void setMailNotificationSMTPUsername(const QString &username)
void setCloseToTray(bool b)
void setMinimizeToTray(bool b)
void setUPnPForWebUIPort(bool enabled)
void setHideZeroValues(bool b)
void setWebUiCSRFProtectionEnabled(bool enabled)
void setWebUiSecureCookieEnabled(bool enabled)
bool isSplashScreenDisabled() const
void setWebUIReverseProxySupportEnabled(bool enabled)
void setConfirmOnExit(bool confirm)
std::chrono::seconds getWebUIBanDuration() const
QString getMailNotificationEmail() const
bool preventFromSuspendWhenSeeding() const
bool isWebUiLocalAuthEnabled() const
void setStartMinimized(bool b)
QString getWebUiAddress() const
void setAlternatingRowColors(bool b)
void setWebUIBanDuration(std::chrono::seconds duration)
bool useAlternatingRowColors() const
bool getMailNotificationSMTPAuth() const
void setPreventFromSuspendWhenDownloading(bool b)
bool isWebUiSecureCookieEnabled() const
void setCustomUIThemePath(const QString &path)
QString getDynDNSUsername() const
void setServerDomains(const QString &str)
void setDynDNSUsername(const QString &username)
QString getMailNotificationSender() const
void setSchedulerEndTime(const QTime &time)
bool getMailNotificationSMTPSSL() const
bool confirmOnExit() const
void setConfirmTorrentDeletion(bool enabled)
void setWebUIHostHeaderValidationEnabled(bool enabled)
void setDontConfirmAutoExit(bool dontConfirmAutoExit)
bool systemTrayEnabled() const
void setWebUiAddress(const QString &addr)
void setSchedulerDays(Scheduler::Days days)
void setMailNotificationEnabled(bool enabled)
void setScanDirsLastPath(const QString &path)
QString getDynDNSPassword() const
bool isWebUIHostHeaderValidationEnabled() const
bool closeToTray() const
void setSystemTrayEnabled(bool enabled)
void setWebUIPassword(const QByteArray &password)
QTime getSchedulerStartTime() const
void setAutoRunEnabled(bool enabled)
bool useUPnPForWebUIPort() const
void setWebUiClickjackingProtectionEnabled(bool enabled)
int getActionOnDblClOnTorrentFn() const
void setTrayIconStyle(TrayIcon::Style style)
bool isAltWebUiEnabled() const
void setWebUiUsername(const QString &username)
bool isWebUiAuthSubnetWhitelistEnabled() const
QString getWebUITrustedReverseProxiesList() const
bool dontConfirmAutoExit() const
QTime getSchedulerEndTime() const
void setDynDomainName(const QString &name)
void setWebUISessionTimeout(int timeout)
bool isWebUICustomHTTPHeadersEnabled() const
bool isWebUiClickjackingProtectionEnabled() const
DNS::Service getDynDNSService() const
TrayIcon::Style trayIconStyle() const
void setActionOnDblClOnTorrentFn(int act)
void setWebUICustomHTTPHeaders(const QString &headers)
void setDynDNSPassword(const QString &password)
void setMailNotificationSMTPPassword(const QString &password)
void setUseCustomUITheme(bool use)
bool isDynDNSEnabled() const
void setWebUiLocalAuthEnabled(bool enabled)
void setHideZeroComboValues(int n)
bool confirmTorrentDeletion() const
void setWebUICustomHTTPHeadersEnabled(bool enabled)
void setLocale(const QString &locale)
void setWebUITrustedReverseProxiesList(const QString &addr)
QString getScanDirsLastPath() const
void setWebUIMaxAuthFailCount(int count)
int getHideZeroComboValues() const
QString getLocale() const
void setDynDNSService(DNS::Service service)
void setMailNotificationSender(const QString &mail)
void setSchedulerStartTime(const QTime &time)
void setWebUiPort(quint16 port)
void setAutoRunProgram(const QString &program)
quint16 getWebUiPort() const
bool isWebUiCSRFProtectionEnabled() const
bool getHideZeroValues() const
bool isWebUIReverseProxySupportEnabled() const
QString getWebUiUsername() const
bool minimizeToTray() const
Scheduler::Days getSchedulerDays() const
void setWebUIHttpsKeyPath(const QString &path)
void setMailNotificationSMTPSSL(bool use)
void setWebUiEnabled(bool enabled)
void setSplashScreenDisabled(bool b)
QString getServerDomains() const
QString getMailNotificationSMTP() const
int getWebUISessionTimeout() const
bool preventFromSuspendWhenDownloading() const
int getActionOnDblClOnTorrentDl() const
bool isWebUiEnabled() const
bool isMailNotificationEnabled() const
bool isWebUiHttpsEnabled() const
void setWebUiAuthSubnetWhitelistEnabled(bool enabled)
bool isAutoRunEnabled() const
void setPreventFromSuspendWhenSeeding(bool b)
void setWebUiHttpsEnabled(bool enabled)
void setMailNotificationEmail(const QString &mail)
QString getDynDomainName() const
void setMailNotificationSMTP(const QString &smtp_server)
void setMailNotificationSMTPAuth(bool use)
bool startMinimized() const
QString getWebUIHttpsKeyPath() const
void setDownloadRepacks(bool enabled)
bool isProcessingEnabled() const
void setSmartEpisodeFilters(const QStringList &filters)
static AutoDownloader * instance()
QStringList smartEpisodeFilters() const
void setProcessingEnabled(bool enabled)
int refreshInterval() const
bool isProcessingEnabled() const
static Session * instance()
int maxArticlesPerFeed() const
void setRefreshInterval(int refreshInterval)
void setMaxArticlesPerFeed(int n)
void setProcessingEnabled(bool enabled)
T get(const T &defaultValue={}) const
Definition: settingvalue.h:46
static AutoDeleteMode autoDeleteMode()
static void setAutoDeleteMode(AutoDeleteMode mode)
static TorrentFilesWatcher * instance()
static UIThemeManager * instance()
void addFolder(const QString &path, const TorrentFilesWatcher::WatchedFolderOptions &options)
bool eventFilter(QObject *, QEvent *event) override
constexpr std::add_const_t< T > & asConst(T &t) noexcept
Definition: global.h:42
Service
Definition: preferences.h:68
QString folderName(const QString &filePath)
Definition: fs.cpp:96
QString expandPathAbs(const QString &path)
Definition: fs.cpp:309
QString toNativePath(const QString &path)
Definition: fs.cpp:64
T scaledSize(const QWidget *widget, const T &size)
Definition: utils.h:46
QPixmap scaledPixmap(const QIcon &icon, const QWidget *widget, int height)
Definition: utils.cpp:68
void resize(QWidget *widget, const QSize &newSize={})
Definition: utils.cpp:54
QPixmap scaledPixmapSvg(const QString &path, const QWidget *widget, int baseHeight)
Definition: utils.cpp:82
bool isSSLKeyValid(const QByteArray &data)
Definition: net.cpp:153
const int MAX_SSL_FILE_SIZE
Definition: net.h:50
bool isSSLCertificatesValid(const QByteArray &data)
Definition: net.cpp:139
QByteArray generate(const QString &password)
Definition: password.cpp:68
uint32_t rand(uint32_t min=0, uint32_t max=std::numeric_limits< uint32_t >::max())
Definition: random.cpp:132
QString languageToLocalizedString(const QLocale &locale)
#define SETTINGS_KEY(name)
@ NO_ACTION
Definition: optionsdialog.h:46
@ TOGGLE_PAUSE
Definition: optionsdialog.h:43
@ OPEN_DEST
Definition: optionsdialog.h:44
@ PREVIEW_FILE
Definition: optionsdialog.h:45
@ SHOW_OPTIONS
Definition: optionsdialog.h:47
@ Remove
Definition: session.h:74
@ EnableSuperSeeding
Definition: session.h:76
@ DeleteFiles
Definition: session.h:75
@ Pause
Definition: session.h:73
file(GLOB QBT_TS_FILES "${qBittorrent_SOURCE_DIR}/src/lang/*.ts") set_source_files_properties($
Definition: CMakeLists.txt:5
const char C_LOCALE_SLOVAK[]
const char C_LOCALE_GEORGIAN[]
const char C_LOCALE_ENGLISH_AUSTRALIA[]
const char C_LOCALE_INDONESIAN[]
const char C_LOCALE_ESTONIAN[]
const char C_LOCALE_AZERBAIJANI[]
const char C_LOCALE_SLOVENIAN[]
const char C_LOCALE_BYELORUSSIAN[]
const char C_LOCALE_SWEDISH[]
const char C_LOCALE_SPANISH[]
const char C_LOCALE_PORTUGUESE_BRAZIL[]
const char C_LOCALE_LATVIAN[]
const char C_LOCALE_SERBIAN[]
const char C_LOCALE_MALAY[]
const char C_LOCALE_MONGOLIAN[]
const char C_LOCALE_NORWEGIAN[]
const char C_LOCALE_ENGLISH_UNITEDKINGDOM[]
const char C_LOCALE_THAI[]
const char C_LOCALE_ROMANIAN[]
const char C_LOCALE_ENGLISH[]
const char C_LOCALE_ARMENIAN[]
const char C_LOCALE_CROATIAN[]
const char C_LOCALE_HEBREW[]
const char C_LOCALE_HUNGARIAN[]
const char C_LOCALE_UKRAINIAN[]
const char C_LOCALE_CHINESE_TRADITIONAL_TW[]
const char C_LOCALE_KOREAN[]
const char C_LOCALE_ICELANDIC[]
const char C_LOCALE_LITHUANIAN[]
const char C_LOCALE_FRENCH[]
const char C_LOCALE_GALICIAN[]
const char C_LOCALE_POLISH[]
const char C_LOCALE_JAPANESE[]
const char C_LOCALE_DANISH[]
const char C_LOCALE_ITALIAN[]
const char C_LOCALE_BULGARIAN[]
const char C_LOCALE_DUTCH[]
const char C_LOCALE_CZECH[]
const char C_LOCALE_ARABIC[]
const char C_LOCALE_LATGALIAN[]
const char C_LOCALE_CATALAN[]
const char C_LOCALE_BASQUE[]
const char C_LOCALE_GREEK[]
const char C_LOCALE_TURKISH[]
const char C_LOCALE_PORTUGUESE[]
const char C_LOCALE_ESPERANTO[]
const char C_LOCALE_PERSIAN[]
const char C_LOCALE_UZBEK[]
const char C_LOCALE_CHINESE_SIMPLIFIED[]
const char C_LOCALE_RUSSIAN[]
const char C_LOCALE_VIETNAMESE[]
const char C_LOCALE_HINDI[]
const char C_LOCALE_FINNISH[]
const char C_LOCALE_GERMAN[]
const char C_LOCALE_CHINESE_TRADITIONAL_HK[]
const char C_LOCALE_OCCITAN[]