Enable customizing the save statistics time interval
[qBittorrent.git] / src / gui / torrentoptionsdialog.cpp
blob85b72ea7ce25ee93c5ecf60861767421811deea8
1 /*
2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2020 thalieht
5 * Copyright (C) 2011 Christian Kandeler
6 * Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * In addition, as a special exception, the copyright holders give permission to
23 * link this program with the OpenSSL project's "OpenSSL" library (or with
24 * modified versions of it that use the same license as the "OpenSSL" library),
25 * and distribute the linked executables. You must obey the GNU General Public
26 * License in all respects for all of the code used other than "OpenSSL". If you
27 * modify file(s), you may extend this exception to your version of the file(s),
28 * but you are not obligated to do so. If you do not wish to do so, delete this
29 * exception statement from your version.
32 #include "torrentoptionsdialog.h"
34 #include <algorithm>
36 #include <QLineEdit>
37 #include <QMessageBox>
38 #include <QString>
40 #include "base/bittorrent/infohash.h"
41 #include "base/bittorrent/session.h"
42 #include "base/bittorrent/torrent.h"
43 #include "base/global.h"
44 #include "base/unicodestrings.h"
45 #include "base/utils/fs.h"
46 #include "ui_torrentoptionsdialog.h"
47 #include "utils.h"
49 #define SETTINGS_KEY(name) u"TorrentOptionsDialog/" name
51 namespace
53 void updateSliderValue(QSlider *slider, const int value)
55 if (value > slider->maximum())
56 slider->setMaximum(value);
57 slider->setValue(value);
61 TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QList<BitTorrent::Torrent *> &torrents)
62 : QDialog {parent}
63 , m_ui {new Ui::TorrentOptionsDialog}
64 , m_storeDialogSize {SETTINGS_KEY(u"Size"_s)}
65 , m_currentCategoriesString {u"--%1--"_s.arg(tr("Currently used categories"))}
67 Q_ASSERT(!torrents.empty());
69 m_ui->setupUi(this);
71 connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
72 connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
74 m_ui->savePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
75 m_ui->savePath->setDialogCaption(tr("Choose save path"));
76 m_ui->downloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
77 m_ui->downloadPath->setDialogCaption(tr("Choose save path"));
79 const auto *session = BitTorrent::Session::instance();
80 bool allSameUpLimit = true;
81 bool allSameDownLimit = true;
82 bool allSameRatio = true;
83 bool allSameSeedingTime = true;
84 bool allSameInactiveSeedingTime = true;
85 bool allSameShareLimitAction = true;
86 bool allTorrentsArePrivate = true;
87 bool allSameDHT = true;
88 bool allSamePEX = true;
89 bool allSameLSD = true;
90 bool allSameSequential = true;
91 bool allSameFirstLastPieces = true;
92 bool allSameAutoTMM = true;
93 bool allSameSavePath = true;
94 bool allSameDownloadPath = true;
96 const bool isFirstTorrentAutoTMMEnabled = torrents[0]->isAutoTMMEnabled();
97 const Path firstTorrentSavePath = torrents[0]->savePath();
98 const Path firstTorrentDownloadPath = torrents[0]->downloadPath();
99 const QString firstTorrentCategory = torrents[0]->category();
101 const int firstTorrentUpLimit = std::max(0, torrents[0]->uploadLimit());
102 const int firstTorrentDownLimit = std::max(0, torrents[0]->downloadLimit());
104 const qreal firstTorrentRatio = torrents[0]->ratioLimit();
105 const int firstTorrentSeedingTime = torrents[0]->seedingTimeLimit();
106 const int firstTorrentInactiveSeedingTime = torrents[0]->inactiveSeedingTimeLimit();
107 const BitTorrent::ShareLimitAction firstTorrentShareLimitAction = torrents[0]->shareLimitAction();
109 const bool isFirstTorrentDHTDisabled = torrents[0]->isDHTDisabled();
110 const bool isFirstTorrentPEXDisabled = torrents[0]->isPEXDisabled();
111 const bool isFirstTorrentLSDDisabled = torrents[0]->isLSDDisabled();
112 const bool isFirstTorrentSequentialEnabled = torrents[0]->isSequentialDownload();
113 const bool isFirstTorrentFirstLastPiecesEnabled = torrents[0]->hasFirstLastPiecePriority();
115 m_torrentIDs.reserve(torrents.size());
116 for (const BitTorrent::Torrent *torrent : torrents)
118 m_torrentIDs << torrent->id();
120 if (allSameAutoTMM)
122 if (torrent->isAutoTMMEnabled() != isFirstTorrentAutoTMMEnabled)
123 allSameAutoTMM = false;
125 if (allSameSavePath)
127 if (torrent->savePath() != firstTorrentSavePath)
128 allSameSavePath = false;
130 if (allSameDownloadPath)
132 if (torrent->downloadPath() != firstTorrentDownloadPath)
133 allSameDownloadPath = false;
135 if (m_allSameCategory)
137 if (torrent->category() != firstTorrentCategory)
138 m_allSameCategory = false;
140 if (allSameUpLimit)
142 if (std::max(0, torrent->uploadLimit()) != firstTorrentUpLimit)
143 allSameUpLimit = false;
145 if (allSameDownLimit)
147 if (std::max(0, torrent->downloadLimit()) != firstTorrentDownLimit)
148 allSameDownLimit = false;
150 if (allSameRatio)
152 if (torrent->ratioLimit() != firstTorrentRatio)
153 allSameRatio = false;
155 if (allSameSeedingTime)
157 if (torrent->seedingTimeLimit() != firstTorrentSeedingTime)
158 allSameSeedingTime = false;
160 if (allSameInactiveSeedingTime)
162 if (torrent->inactiveSeedingTimeLimit() != firstTorrentInactiveSeedingTime)
163 allSameInactiveSeedingTime = false;
165 if (allSameShareLimitAction)
167 if (torrent->shareLimitAction() != firstTorrentShareLimitAction)
168 allSameShareLimitAction = false;
170 if (allTorrentsArePrivate)
172 if (!torrent->isPrivate())
173 allTorrentsArePrivate = false;
175 if (allSameDHT)
177 if (torrent->isDHTDisabled() != isFirstTorrentDHTDisabled)
178 allSameDHT = false;
180 if (allSamePEX)
182 if (torrent->isPEXDisabled() != isFirstTorrentPEXDisabled)
183 allSamePEX = false;
185 if (allSameLSD)
187 if (torrent->isLSDDisabled() != isFirstTorrentLSDDisabled)
188 allSameLSD = false;
190 if (allSameSequential)
192 if (torrent->isSequentialDownload() != isFirstTorrentSequentialEnabled)
193 allSameSequential = false;
195 if (allSameFirstLastPieces)
197 if (torrent->hasFirstLastPiecePriority() != isFirstTorrentFirstLastPiecesEnabled)
198 allSameFirstLastPieces = false;
202 if (allSameAutoTMM)
203 m_ui->checkAutoTMM->setChecked(isFirstTorrentAutoTMMEnabled);
204 else
205 m_ui->checkAutoTMM->setCheckState(Qt::PartiallyChecked);
207 if (allSameSavePath)
208 m_ui->savePath->setSelectedPath(firstTorrentSavePath);
210 if (allSameDownloadPath)
212 m_ui->downloadPath->setSelectedPath(firstTorrentDownloadPath);
213 m_ui->checkUseDownloadPath->setChecked(!firstTorrentDownloadPath.isEmpty());
215 else
217 m_ui->checkUseDownloadPath->setCheckState(Qt::PartiallyChecked);
220 if (!m_allSameCategory)
222 m_ui->comboCategory->addItem(m_currentCategoriesString);
223 m_ui->comboCategory->clearEditText();
224 m_ui->comboCategory->lineEdit()->setPlaceholderText(m_currentCategoriesString);
226 else if (!firstTorrentCategory.isEmpty())
228 m_ui->comboCategory->setCurrentText(firstTorrentCategory);
229 m_ui->comboCategory->addItem(firstTorrentCategory);
231 m_ui->comboCategory->addItem(QString());
233 m_categories = session->categories();
234 std::sort(m_categories.begin(), m_categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
235 for (const QString &category : asConst(m_categories))
237 if (m_allSameCategory && (category == firstTorrentCategory))
238 continue;
240 m_ui->comboCategory->addItem(category);
243 const bool isAltLimitEnabled = session->isAltGlobalSpeedLimitEnabled();
244 const int globalUploadLimit = isAltLimitEnabled
245 ? (session->altGlobalUploadSpeedLimit() / 1024)
246 : (session->globalUploadSpeedLimit() / 1024);
247 const int globalDownloadLimit = isAltLimitEnabled
248 ? (session->altGlobalDownloadSpeedLimit() / 1024)
249 : (session->globalDownloadSpeedLimit() / 1024);
251 const int uploadVal = std::max(0, (firstTorrentUpLimit / 1024));
252 const int downloadVal = std::max(0, (firstTorrentDownLimit / 1024));
253 int maxUpload = (globalUploadLimit <= 0) ? 10000 : globalUploadLimit;
254 int maxDownload = (globalDownloadLimit <= 0) ? 10000 : globalDownloadLimit;
256 // This can happen for example if global rate limit is lower than torrent rate limit.
257 if (uploadVal > maxUpload)
258 maxUpload = uploadVal;
259 if (downloadVal > maxDownload)
260 maxDownload = downloadVal;
262 m_ui->sliderUploadLimit->setMaximum(maxUpload);
263 m_ui->sliderUploadLimit->setValue(allSameUpLimit ? uploadVal : (maxUpload / 2));
264 if (allSameUpLimit)
266 m_ui->spinUploadLimit->setValue(uploadVal);
268 else
270 m_ui->spinUploadLimit->setSpecialValueText(C_INEQUALITY);
271 m_ui->spinUploadLimit->setMinimum(-1);
272 m_ui->spinUploadLimit->setValue(-1);
273 connect(m_ui->spinUploadLimit, qOverload<int>(&QSpinBox::valueChanged)
274 , this, &TorrentOptionsDialog::handleUpSpeedLimitChanged);
277 m_ui->sliderDownloadLimit->setMaximum(maxDownload);
278 m_ui->sliderDownloadLimit->setValue(allSameDownLimit ? downloadVal : (maxDownload / 2));
279 if (allSameDownLimit)
281 m_ui->spinDownloadLimit->setValue(downloadVal);
283 else
285 m_ui->spinDownloadLimit->setSpecialValueText(C_INEQUALITY);
286 m_ui->spinDownloadLimit->setMinimum(-1);
287 m_ui->spinDownloadLimit->setValue(-1);
288 connect(m_ui->spinDownloadLimit, qOverload<int>(&QSpinBox::valueChanged)
289 , this, &TorrentOptionsDialog::handleDownSpeedLimitChanged);
292 m_ui->torrentShareLimitsWidget->setDefaultLimits(session->globalMaxRatio(), session->globalMaxSeedingMinutes(), session->globalMaxInactiveSeedingMinutes());
293 if (allSameRatio)
294 m_ui->torrentShareLimitsWidget->setRatioLimit(firstTorrentRatio);
295 if (allSameSeedingTime)
296 m_ui->torrentShareLimitsWidget->setSeedingTimeLimit(firstTorrentSeedingTime);
297 if (allSameInactiveSeedingTime)
298 m_ui->torrentShareLimitsWidget->setInactiveSeedingTimeLimit(firstTorrentInactiveSeedingTime);
299 if (allSameShareLimitAction)
300 m_ui->torrentShareLimitsWidget->setShareLimitAction(firstTorrentShareLimitAction);
302 if (!allTorrentsArePrivate)
304 if (allSameDHT)
305 m_ui->checkDisableDHT->setChecked(isFirstTorrentDHTDisabled);
306 else
307 m_ui->checkDisableDHT->setCheckState(Qt::PartiallyChecked);
309 if (allSamePEX)
310 m_ui->checkDisablePEX->setChecked(isFirstTorrentPEXDisabled);
311 else
312 m_ui->checkDisablePEX->setCheckState(Qt::PartiallyChecked);
314 if (allSameLSD)
315 m_ui->checkDisableLSD->setChecked(isFirstTorrentLSDDisabled);
316 else
317 m_ui->checkDisableLSD->setCheckState(Qt::PartiallyChecked);
319 else
321 m_ui->checkDisableDHT->setChecked(true);
322 m_ui->checkDisableDHT->setEnabled(false);
323 m_ui->checkDisablePEX->setChecked(true);
324 m_ui->checkDisablePEX->setEnabled(false);
325 m_ui->checkDisableLSD->setChecked(true);
326 m_ui->checkDisableLSD->setEnabled(false);
329 const QString privateTorrentsTooltip = tr("Not applicable to private torrents");
330 m_ui->checkDisableDHT->setToolTip(privateTorrentsTooltip);
331 m_ui->checkDisablePEX->setToolTip(privateTorrentsTooltip);
332 m_ui->checkDisableLSD->setToolTip(privateTorrentsTooltip);
334 if (allSameSequential)
335 m_ui->checkSequential->setChecked(isFirstTorrentSequentialEnabled);
336 else
337 m_ui->checkSequential->setCheckState(Qt::PartiallyChecked);
339 if (allSameFirstLastPieces)
340 m_ui->checkFirstLastPieces->setChecked(isFirstTorrentFirstLastPiecesEnabled);
341 else
342 m_ui->checkFirstLastPieces->setCheckState(Qt::PartiallyChecked);
344 m_initialValues =
346 .savePath = m_ui->savePath->selectedPath(),
347 .downloadPath = m_ui->downloadPath->selectedPath(),
348 .category = m_ui->comboCategory->currentText(),
349 .ratio = m_ui->torrentShareLimitsWidget->ratioLimit(),
350 .seedingTime = m_ui->torrentShareLimitsWidget->seedingTimeLimit(),
351 .inactiveSeedingTime = m_ui->torrentShareLimitsWidget->inactiveSeedingTimeLimit(),
352 .shareLimitAction = m_ui->torrentShareLimitsWidget->shareLimitAction(),
353 .upSpeedLimit = m_ui->spinUploadLimit->value(),
354 .downSpeedLimit = m_ui->spinDownloadLimit->value(),
355 .autoTMM = m_ui->checkAutoTMM->checkState(),
356 .useDownloadPath = m_ui->checkUseDownloadPath->checkState(),
357 .disableDHT = m_ui->checkDisableDHT->checkState(),
358 .disablePEX = m_ui->checkDisablePEX->checkState(),
359 .disableLSD = m_ui->checkDisableLSD->checkState(),
360 .sequential = m_ui->checkSequential->checkState(),
361 .firstLastPieces = m_ui->checkFirstLastPieces->checkState()
364 // Needs to be called after the initial values struct is initialized
365 handleTMMChanged();
366 handleUseDownloadPathChanged();
368 connect(m_ui->checkAutoTMM, &QCheckBox::clicked, this, &TorrentOptionsDialog::handleTMMChanged);
369 connect(m_ui->checkUseDownloadPath, &QCheckBox::clicked, this, &TorrentOptionsDialog::handleUseDownloadPathChanged);
370 connect(m_ui->comboCategory, &QComboBox::activated, this, &TorrentOptionsDialog::handleCategoryChanged);
372 // Sync up/down speed limit sliders with their corresponding spinboxes
373 connect(m_ui->sliderUploadLimit, &QSlider::valueChanged, m_ui->spinUploadLimit, &QSpinBox::setValue);
374 connect(m_ui->sliderDownloadLimit, &QSlider::valueChanged, m_ui->spinDownloadLimit, &QSpinBox::setValue);
375 connect(m_ui->spinUploadLimit, qOverload<int>(&QSpinBox::valueChanged)
376 , this, [this](const int value) { updateSliderValue(m_ui->sliderUploadLimit, value); });
377 connect(m_ui->spinDownloadLimit, qOverload<int>(&QSpinBox::valueChanged)
378 , this, [this](const int value) { updateSliderValue(m_ui->sliderDownloadLimit, value); });
380 if (const QSize dialogSize = m_storeDialogSize; dialogSize.isValid())
381 resize(dialogSize);
384 TorrentOptionsDialog::~TorrentOptionsDialog()
386 m_storeDialogSize = size();
387 delete m_ui;
390 void TorrentOptionsDialog::accept()
392 auto *session = BitTorrent::Session::instance();
393 for (const BitTorrent::TorrentID &id : asConst(m_torrentIDs))
395 BitTorrent::Torrent *torrent = session->getTorrent(id);
396 if (!torrent) continue;
398 if (m_initialValues.autoTMM != m_ui->checkAutoTMM->checkState())
399 torrent->setAutoTMMEnabled(m_ui->checkAutoTMM->isChecked());
401 if (m_ui->checkAutoTMM->checkState() == Qt::Unchecked)
403 const Path savePath = m_ui->savePath->selectedPath();
404 if (m_initialValues.savePath != savePath)
405 torrent->setSavePath(savePath);
407 const Qt::CheckState useDownloadPathState = m_ui->checkUseDownloadPath->checkState();
408 if (useDownloadPathState == Qt::Checked)
410 const Path downloadPath = m_ui->downloadPath->selectedPath();
411 if (m_initialValues.downloadPath != downloadPath)
412 torrent->setDownloadPath(downloadPath);
414 else if (useDownloadPathState == Qt::Unchecked)
416 torrent->setDownloadPath({});
420 const QString category = m_ui->comboCategory->currentText();
421 // index 0 is always the current category
422 if ((m_initialValues.category != category) || (m_ui->comboCategory->currentIndex() != 0))
424 if (!m_categories.contains(category))
425 session->addCategory(category);
427 torrent->setCategory(category);
430 if (m_initialValues.upSpeedLimit != m_ui->spinUploadLimit->value())
431 torrent->setUploadLimit(m_ui->spinUploadLimit->value() * 1024);
432 if (m_initialValues.downSpeedLimit != m_ui->spinDownloadLimit->value())
433 torrent->setDownloadLimit(m_ui->spinDownloadLimit->value() * 1024);
435 if (const std::optional<qreal> ratioLimit = m_ui->torrentShareLimitsWidget->ratioLimit();
436 m_initialValues.ratio != ratioLimit)
438 torrent->setRatioLimit(ratioLimit.value());
441 if (const std::optional<int> seedingTimeLimit = m_ui->torrentShareLimitsWidget->seedingTimeLimit();
442 m_initialValues.seedingTime != seedingTimeLimit)
444 torrent->setSeedingTimeLimit(seedingTimeLimit.value());
447 if (const std::optional<int> inactiveSeedingTimeLimit = m_ui->torrentShareLimitsWidget->inactiveSeedingTimeLimit();
448 m_initialValues.inactiveSeedingTime != inactiveSeedingTimeLimit)
450 torrent->setInactiveSeedingTimeLimit(inactiveSeedingTimeLimit.value());
453 if (const std::optional<BitTorrent::ShareLimitAction> shareLimitAction = m_ui->torrentShareLimitsWidget->shareLimitAction();
454 m_initialValues.shareLimitAction != shareLimitAction)
456 torrent->setShareLimitAction(shareLimitAction.value());
459 if (!torrent->isPrivate())
461 if (m_initialValues.disableDHT != m_ui->checkDisableDHT->checkState())
462 torrent->setDHTDisabled(m_ui->checkDisableDHT->isChecked());
463 if (m_initialValues.disablePEX != m_ui->checkDisablePEX->checkState())
464 torrent->setPEXDisabled(m_ui->checkDisablePEX->isChecked());
465 if (m_initialValues.disableLSD != m_ui->checkDisableLSD->checkState())
466 torrent->setLSDDisabled(m_ui->checkDisableLSD->isChecked());
469 if (m_initialValues.sequential != m_ui->checkSequential->checkState())
470 torrent->setSequentialDownload(m_ui->checkSequential->isChecked());
471 if (m_initialValues.firstLastPieces != m_ui->checkFirstLastPieces->checkState())
472 torrent->setFirstLastPiecePriority(m_ui->checkFirstLastPieces->isChecked());
475 QDialog::accept();
478 void TorrentOptionsDialog::handleCategoryChanged([[maybe_unused]] const int index)
480 if (m_ui->checkAutoTMM->checkState() == Qt::Checked)
482 if (!m_allSameCategory && (m_ui->comboCategory->currentIndex() == 0))
484 m_ui->savePath->setSelectedPath({});
486 else
488 const Path savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->comboCategory->currentText());
489 m_ui->savePath->setSelectedPath(savePath);
490 const Path downloadPath = BitTorrent::Session::instance()->categoryDownloadPath(m_ui->comboCategory->currentText());
491 m_ui->downloadPath->setSelectedPath(downloadPath);
492 m_ui->checkUseDownloadPath->setChecked(!downloadPath.isEmpty());
496 if (!m_allSameCategory && (m_ui->comboCategory->currentIndex() == 0))
498 m_ui->comboCategory->clearEditText();
499 m_ui->comboCategory->lineEdit()->setPlaceholderText(m_currentCategoriesString);
501 else
503 m_ui->comboCategory->lineEdit()->setPlaceholderText(QString());
507 void TorrentOptionsDialog::handleTMMChanged()
509 if (m_ui->checkAutoTMM->checkState() == Qt::Unchecked)
511 m_ui->groupBoxSavePath->setEnabled(true);
512 m_ui->savePath->setSelectedPath(m_initialValues.savePath);
513 m_ui->downloadPath->setSelectedPath(m_initialValues.downloadPath);
514 m_ui->checkUseDownloadPath->setCheckState(m_initialValues.useDownloadPath);
516 else
518 m_ui->groupBoxSavePath->setEnabled(false);
519 if (m_ui->checkAutoTMM->checkState() == Qt::Checked)
521 if (!m_allSameCategory && (m_ui->comboCategory->currentIndex() == 0))
523 m_ui->savePath->setSelectedPath({});
524 m_ui->downloadPath->setSelectedPath({});
525 m_ui->checkUseDownloadPath->setCheckState(Qt::PartiallyChecked);
527 else
529 const Path savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->comboCategory->currentText());
530 m_ui->savePath->setSelectedPath(savePath);
531 const Path downloadPath = BitTorrent::Session::instance()->categoryDownloadPath(m_ui->comboCategory->currentText());
532 m_ui->downloadPath->setSelectedPath(downloadPath);
533 m_ui->checkUseDownloadPath->setChecked(!downloadPath.isEmpty());
536 else // partially checked
538 m_ui->savePath->setSelectedPath({});
539 m_ui->downloadPath->setSelectedPath({});
540 m_ui->checkUseDownloadPath->setCheckState(Qt::PartiallyChecked);
545 void TorrentOptionsDialog::handleUseDownloadPathChanged()
547 const bool isChecked = m_ui->checkUseDownloadPath->checkState() == Qt::Checked;
548 m_ui->downloadPath->setEnabled(isChecked);
549 if (isChecked && m_ui->downloadPath->selectedPath().isEmpty())
550 m_ui->downloadPath->setSelectedPath(BitTorrent::Session::instance()->downloadPath());
553 void TorrentOptionsDialog::handleUpSpeedLimitChanged()
555 m_ui->spinUploadLimit->setMinimum(0);
556 m_ui->spinUploadLimit->setSpecialValueText(C_INFINITY);
557 disconnect(m_ui->spinUploadLimit, qOverload<int>(&QSpinBox::valueChanged)
558 , this, &TorrentOptionsDialog::handleUpSpeedLimitChanged);
561 void TorrentOptionsDialog::handleDownSpeedLimitChanged()
563 m_ui->spinDownloadLimit->setMinimum(0);
564 m_ui->spinDownloadLimit->setSpecialValueText(C_INFINITY);
565 disconnect(m_ui->spinDownloadLimit, qOverload<int>(&QSpinBox::valueChanged)
566 , this, &TorrentOptionsDialog::handleDownSpeedLimitChanged);