Sync translations from Transifex and run lupdate
[qBittorrent.git] / src / gui / advancedsettings.cpp
blob0b160a3c719f18f6a3ab9d6592a0fa0d03b3b7b7
1 /*
2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2016 qBittorrent project
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.
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.
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.
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.
29 #include "advancedsettings.h"
31 #include <limits>
33 #include <QHeaderView>
34 #include <QHostAddress>
35 #include <QLabel>
36 #include <QNetworkInterface>
38 #include "base/bittorrent/session.h"
39 #include "base/global.h"
40 #include "base/preferences.h"
41 #include "base/unicodestrings.h"
42 #include "app/application.h"
43 #include "gui/addnewtorrentdialog.h"
44 #include "gui/mainwindow.h"
46 namespace
48 QString makeLink(const QString &url, const QString &linkLabel)
50 return QStringLiteral("<a href=\"%1\">%2</a>").arg(url, linkLabel);
53 enum AdvSettingsCols
55 PROPERTY,
56 VALUE,
57 COL_COUNT
60 enum AdvSettingsRows
62 // qBittorrent section
63 QBITTORRENT_HEADER,
64 RESUME_DATA_STORAGE,
65 #if defined(Q_OS_WIN)
66 OS_MEMORY_PRIORITY,
67 #endif
68 // network interface
69 NETWORK_IFACE,
70 //Optional network address
71 NETWORK_IFACE_ADDRESS,
72 // behavior
73 SAVE_RESUME_DATA_INTERVAL,
74 CONFIRM_RECHECK_TORRENT,
75 RECHECK_COMPLETED,
76 // UI related
77 LIST_REFRESH,
78 RESOLVE_HOSTS,
79 RESOLVE_COUNTRIES,
80 PROGRAM_NOTIFICATIONS,
81 TORRENT_ADDED_NOTIFICATIONS,
82 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
83 NOTIFICATION_TIMEOUT,
84 #endif
85 CONFIRM_REMOVE_ALL_TAGS,
86 REANNOUNCE_WHEN_ADDRESS_CHANGED,
87 DOWNLOAD_TRACKER_FAVICON,
88 SAVE_PATH_HISTORY_LENGTH,
89 ENABLE_SPEED_WIDGET,
90 #ifndef Q_OS_MACOS
91 ENABLE_ICONS_IN_MENUS,
92 #endif
93 // embedded tracker
94 TRACKER_STATUS,
95 TRACKER_PORT,
96 // libtorrent section
97 LIBTORRENT_HEADER,
98 ASYNC_IO_THREADS,
99 #ifdef QBT_USES_LIBTORRENT2
100 HASHING_THREADS,
101 #endif
102 FILE_POOL_SIZE,
103 CHECKING_MEM_USAGE,
104 #ifndef QBT_USES_LIBTORRENT2
105 // cache
106 DISK_CACHE,
107 DISK_CACHE_TTL,
108 #endif
109 OS_CACHE,
110 #ifndef QBT_USES_LIBTORRENT2
111 COALESCE_RW,
112 #endif
113 PIECE_EXTENT_AFFINITY,
114 SUGGEST_MODE,
115 SEND_BUF_WATERMARK,
116 SEND_BUF_LOW_WATERMARK,
117 SEND_BUF_WATERMARK_FACTOR,
118 // networking & ports
119 CONNECTION_SPEED,
120 SOCKET_BACKLOG_SIZE,
121 OUTGOING_PORT_MIN,
122 OUTGOING_PORT_MAX,
123 UPNP_LEASE_DURATION,
124 PEER_TOS,
125 UTP_MIX_MODE,
126 IDN_SUPPORT,
127 MULTI_CONNECTIONS_PER_IP,
128 VALIDATE_HTTPS_TRACKER_CERTIFICATE,
129 SSRF_MITIGATION,
130 BLOCK_PEERS_ON_PRIVILEGED_PORTS,
131 // seeding
132 CHOKING_ALGORITHM,
133 SEED_CHOKING_ALGORITHM,
134 // tracker
135 ANNOUNCE_ALL_TRACKERS,
136 ANNOUNCE_ALL_TIERS,
137 ANNOUNCE_IP,
138 MAX_CONCURRENT_HTTP_ANNOUNCES,
139 STOP_TRACKER_TIMEOUT,
140 PEER_TURNOVER,
141 PEER_TURNOVER_CUTOFF,
142 PEER_TURNOVER_INTERVAL,
144 ROW_COUNT
148 AdvancedSettings::AdvancedSettings(QWidget *parent)
149 : QTableWidget(parent)
151 // column
152 setColumnCount(COL_COUNT);
153 QStringList header = {tr("Setting"), tr("Value", "Value set for this setting")};
154 setHorizontalHeaderLabels(header);
155 // row
156 setRowCount(ROW_COUNT);
157 verticalHeader()->setVisible(false);
158 // etc.
159 setAlternatingRowColors(true);
160 setSelectionMode(QAbstractItemView::NoSelection);
161 setEditTriggers(QAbstractItemView::NoEditTriggers);
162 // Load settings
163 loadAdvancedSettings();
164 resizeColumnToContents(0);
165 horizontalHeader()->setStretchLastSection(true);
168 void AdvancedSettings::saveAdvancedSettings()
170 Preferences *const pref = Preferences::instance();
171 BitTorrent::Session *const session = BitTorrent::Session::instance();
173 session->setResumeDataStorageType((m_comboBoxResumeDataStorage.currentIndex() == 0)
174 ? BitTorrent::ResumeDataStorageType::Legacy
175 : BitTorrent::ResumeDataStorageType::SQLite);
177 #if defined(Q_OS_WIN)
178 BitTorrent::OSMemoryPriority prio = BitTorrent::OSMemoryPriority::Normal;
179 switch (m_comboBoxOSMemoryPriority.currentIndex())
181 case 0:
182 default:
183 prio = BitTorrent::OSMemoryPriority::Normal;
184 break;
185 case 1:
186 prio = BitTorrent::OSMemoryPriority::BelowNormal;
187 break;
188 case 2:
189 prio = BitTorrent::OSMemoryPriority::Medium;
190 break;
191 case 3:
192 prio = BitTorrent::OSMemoryPriority::Low;
193 break;
194 case 4:
195 prio = BitTorrent::OSMemoryPriority::VeryLow;
196 break;
198 session->setOSMemoryPriority(prio);
199 #endif
200 // Async IO threads
201 session->setAsyncIOThreads(m_spinBoxAsyncIOThreads.value());
202 #ifdef QBT_USES_LIBTORRENT2
203 // Hashing threads
204 session->setHashingThreads(m_spinBoxHashingThreads.value());
205 #endif
206 // File pool size
207 session->setFilePoolSize(m_spinBoxFilePoolSize.value());
208 // Checking Memory Usage
209 session->setCheckingMemUsage(m_spinBoxCheckingMemUsage.value());
210 #ifndef QBT_USES_LIBTORRENT2
211 // Disk write cache
212 session->setDiskCacheSize(m_spinBoxCache.value());
213 session->setDiskCacheTTL(m_spinBoxCacheTTL.value());
214 #endif
215 // Enable OS cache
216 session->setUseOSCache(m_checkBoxOsCache.isChecked());
217 #ifndef QBT_USES_LIBTORRENT2
218 // Coalesce reads & writes
219 session->setCoalesceReadWriteEnabled(m_checkBoxCoalesceRW.isChecked());
220 #endif
221 // Piece extent affinity
222 session->setPieceExtentAffinity(m_checkBoxPieceExtentAffinity.isChecked());
223 // Suggest mode
224 session->setSuggestMode(m_checkBoxSuggestMode.isChecked());
225 // Send buffer watermark
226 session->setSendBufferWatermark(m_spinBoxSendBufferWatermark.value());
227 session->setSendBufferLowWatermark(m_spinBoxSendBufferLowWatermark.value());
228 session->setSendBufferWatermarkFactor(m_spinBoxSendBufferWatermarkFactor.value());
229 // Outgoing connections per second
230 session->setConnectionSpeed(m_spinBoxConnectionSpeed.value());
231 // Socket listen backlog size
232 session->setSocketBacklogSize(m_spinBoxSocketBacklogSize.value());
233 // Save resume data interval
234 session->setSaveResumeDataInterval(m_spinBoxSaveResumeDataInterval.value());
235 // Outgoing ports
236 session->setOutgoingPortsMin(m_spinBoxOutgoingPortsMin.value());
237 session->setOutgoingPortsMax(m_spinBoxOutgoingPortsMax.value());
238 // UPnP lease duration
239 session->setUPnPLeaseDuration(m_spinBoxUPnPLeaseDuration.value());
240 // Type of service
241 session->setPeerToS(m_spinBoxPeerToS.value());
242 // uTP-TCP mixed mode
243 session->setUtpMixedMode(static_cast<BitTorrent::MixedModeAlgorithm>(m_comboBoxUtpMixedMode.currentIndex()));
244 // Support internationalized domain name (IDN)
245 session->setIDNSupportEnabled(m_checkBoxIDNSupport.isChecked());
246 // multiple connections per IP
247 session->setMultiConnectionsPerIpEnabled(m_checkBoxMultiConnectionsPerIp.isChecked());
248 // Validate HTTPS tracker certificate
249 session->setValidateHTTPSTrackerCertificate(m_checkBoxValidateHTTPSTrackerCertificate.isChecked());
250 // SSRF mitigation
251 session->setSSRFMitigationEnabled(m_checkBoxSSRFMitigation.isChecked());
252 // Disallow connection to peers on privileged ports
253 session->setBlockPeersOnPrivilegedPorts(m_checkBoxBlockPeersOnPrivilegedPorts.isChecked());
254 // Recheck torrents on completion
255 pref->recheckTorrentsOnCompletion(m_checkBoxRecheckCompleted.isChecked());
256 // Transfer list refresh interval
257 session->setRefreshInterval(m_spinBoxListRefresh.value());
258 // Peer resolution
259 pref->resolvePeerCountries(m_checkBoxResolveCountries.isChecked());
260 pref->resolvePeerHostNames(m_checkBoxResolveHosts.isChecked());
261 // Network interface
262 if (m_comboBoxInterface.currentIndex() == 0)
264 // All interfaces (default)
265 session->setNetworkInterface(QString());
266 session->setNetworkInterfaceName(QString());
268 else
270 session->setNetworkInterface(m_comboBoxInterface.itemData(m_comboBoxInterface.currentIndex()).toString());
271 session->setNetworkInterfaceName(m_comboBoxInterface.currentText());
274 // Interface address
275 // Construct a QHostAddress to filter malformed strings
276 const QHostAddress ifaceAddr(m_comboBoxInterfaceAddress.currentData().toString().trimmed());
277 session->setNetworkInterfaceAddress(ifaceAddr.toString());
279 // Announce IP
280 // Construct a QHostAddress to filter malformed strings
281 const QHostAddress addr(m_lineEditAnnounceIP.text().trimmed());
282 session->setAnnounceIP(addr.toString());
283 // Max concurrent HTTP announces
284 session->setMaxConcurrentHTTPAnnounces(m_spinBoxMaxConcurrentHTTPAnnounces.value());
285 // Stop tracker timeout
286 session->setStopTrackerTimeout(m_spinBoxStopTrackerTimeout.value());
287 // Program notification
288 MainWindow *const mainWindow = static_cast<Application*>(QCoreApplication::instance())->mainWindow();
289 mainWindow->setNotificationsEnabled(m_checkBoxProgramNotifications.isChecked());
290 mainWindow->setTorrentAddedNotificationsEnabled(m_checkBoxTorrentAddedNotifications.isChecked());
291 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
292 mainWindow->setNotificationTimeout(m_spinBoxNotificationTimeout.value());
293 #endif
294 // Reannounce to all trackers when ip/port changed
295 session->setReannounceWhenAddressChangedEnabled(m_checkBoxReannounceWhenAddressChanged.isChecked());
296 // Misc GUI properties
297 mainWindow->setDownloadTrackerFavicon(m_checkBoxTrackerFavicon.isChecked());
298 AddNewTorrentDialog::setSavePathHistoryLength(m_spinBoxSavePathHistoryLength.value());
299 pref->setSpeedWidgetEnabled(m_checkBoxSpeedWidgetEnabled.isChecked());
300 #ifndef Q_OS_MACOS
301 pref->setIconsInMenusEnabled(m_checkBoxIconsInMenusEnabled.isChecked());
302 #endif
304 // Tracker
305 pref->setTrackerPort(m_spinBoxTrackerPort.value());
306 session->setTrackerEnabled(m_checkBoxTrackerStatus.isChecked());
307 // Choking algorithm
308 session->setChokingAlgorithm(static_cast<BitTorrent::ChokingAlgorithm>(m_comboBoxChokingAlgorithm.currentIndex()));
309 // Seed choking algorithm
310 session->setSeedChokingAlgorithm(static_cast<BitTorrent::SeedChokingAlgorithm>(m_comboBoxSeedChokingAlgorithm.currentIndex()));
312 pref->setConfirmTorrentRecheck(m_checkBoxConfirmTorrentRecheck.isChecked());
314 pref->setConfirmRemoveAllTags(m_checkBoxConfirmRemoveAllTags.isChecked());
316 session->setAnnounceToAllTrackers(m_checkBoxAnnounceAllTrackers.isChecked());
317 session->setAnnounceToAllTiers(m_checkBoxAnnounceAllTiers.isChecked());
319 session->setPeerTurnover(m_spinBoxPeerTurnover.value());
320 session->setPeerTurnoverCutoff(m_spinBoxPeerTurnoverCutoff.value());
321 session->setPeerTurnoverInterval(m_spinBoxPeerTurnoverInterval.value());
324 #ifndef QBT_USES_LIBTORRENT2
325 void AdvancedSettings::updateCacheSpinSuffix(int value)
327 if (value == 0)
328 m_spinBoxCache.setSuffix(tr(" (disabled)"));
329 else if (value < 0)
330 m_spinBoxCache.setSuffix(tr(" (auto)"));
331 else
332 m_spinBoxCache.setSuffix(tr(" MiB"));
334 #endif
336 void AdvancedSettings::updateSaveResumeDataIntervalSuffix(const int value)
338 if (value > 0)
339 m_spinBoxSaveResumeDataInterval.setSuffix(tr(" min", " minutes"));
340 else
341 m_spinBoxSaveResumeDataInterval.setSuffix(tr(" (disabled)"));
344 void AdvancedSettings::updateInterfaceAddressCombo()
346 // Try to get the currently selected interface name
347 const QString ifaceName = m_comboBoxInterface.itemData(m_comboBoxInterface.currentIndex()).toString(); // Empty string for the first element
348 const QString currentAddress = BitTorrent::Session::instance()->networkInterfaceAddress();
350 // Clear all items and reinsert them, default to all
351 m_comboBoxInterfaceAddress.clear();
352 m_comboBoxInterfaceAddress.addItem(tr("All addresses"), {});
353 m_comboBoxInterfaceAddress.addItem(tr("All IPv4 addresses"), QLatin1String("0.0.0.0"));
354 m_comboBoxInterfaceAddress.addItem(tr("All IPv6 addresses"), QLatin1String("::"));
356 const auto populateCombo = [this](const QHostAddress &addr)
358 if (addr.protocol() == QAbstractSocket::IPv4Protocol)
360 const QString str = addr.toString();
361 m_comboBoxInterfaceAddress.addItem(str, str);
363 else if (addr.protocol() == QAbstractSocket::IPv6Protocol)
365 const QString str = Utils::Net::canonicalIPv6Addr(addr).toString();
366 m_comboBoxInterfaceAddress.addItem(str, str);
370 if (ifaceName.isEmpty())
372 for (const QHostAddress &addr : asConst(QNetworkInterface::allAddresses()))
373 populateCombo(addr);
375 else
377 const QNetworkInterface iface = QNetworkInterface::interfaceFromName(ifaceName);
378 const QList<QNetworkAddressEntry> addresses = iface.addressEntries();
379 for (const QNetworkAddressEntry &entry : addresses)
380 populateCombo(entry.ip());
383 const int index = m_comboBoxInterfaceAddress.findData(currentAddress);
384 m_comboBoxInterfaceAddress.setCurrentIndex(std::max(index, 0));
387 void AdvancedSettings::loadAdvancedSettings()
389 const Preferences *const pref = Preferences::instance();
390 const BitTorrent::Session *const session = BitTorrent::Session::instance();
392 // add section headers
393 auto *labelQbtLink = new QLabel(
394 makeLink(QLatin1String("https://github.com/qbittorrent/qBittorrent/wiki/Explanation-of-Options-in-qBittorrent#Advanced")
395 , tr("Open documentation"))
396 , this);
397 labelQbtLink->setOpenExternalLinks(true);
398 addRow(QBITTORRENT_HEADER, QString::fromLatin1("<b>%1</b>").arg(tr("qBittorrent Section")), labelQbtLink);
399 static_cast<QLabel *>(cellWidget(QBITTORRENT_HEADER, PROPERTY))->setAlignment(Qt::AlignCenter | Qt::AlignVCenter);
401 auto *labelLibtorrentLink = new QLabel(
402 makeLink(QLatin1String("https://www.libtorrent.org/reference-Settings.html")
403 , tr("Open documentation"))
404 , this);
405 labelLibtorrentLink->setOpenExternalLinks(true);
406 addRow(LIBTORRENT_HEADER, QString::fromLatin1("<b>%1</b>").arg(tr("libtorrent Section")), labelLibtorrentLink);
407 static_cast<QLabel *>(cellWidget(LIBTORRENT_HEADER, PROPERTY))->setAlignment(Qt::AlignCenter | Qt::AlignVCenter);
409 m_comboBoxResumeDataStorage.addItems({tr("Fastresume files"), tr("SQLite database (experimental)")});
410 m_comboBoxResumeDataStorage.setCurrentIndex((session->resumeDataStorageType() == BitTorrent::ResumeDataStorageType::Legacy) ? 0 : 1);
411 addRow(RESUME_DATA_STORAGE, tr("Resume data storage type (requires restart)"), &m_comboBoxResumeDataStorage);
413 #if defined(Q_OS_WIN)
414 m_comboBoxOSMemoryPriority.addItems({tr("Normal"), tr("Below normal"), tr("Medium"), tr("Low"), tr("Very low")});
415 int OSMemoryPriorityIndex = 0;
416 switch (session->getOSMemoryPriority())
418 default:
419 case BitTorrent::OSMemoryPriority::Normal:
420 OSMemoryPriorityIndex = 0;
421 break;
422 case BitTorrent::OSMemoryPriority::BelowNormal:
423 OSMemoryPriorityIndex = 1;
424 break;
425 case BitTorrent::OSMemoryPriority::Medium:
426 OSMemoryPriorityIndex = 2;
427 break;
428 case BitTorrent::OSMemoryPriority::Low:
429 OSMemoryPriorityIndex = 3;
430 break;
431 case BitTorrent::OSMemoryPriority::VeryLow:
432 OSMemoryPriorityIndex = 4;
433 break;
435 m_comboBoxOSMemoryPriority.setCurrentIndex(OSMemoryPriorityIndex);
436 addRow(OS_MEMORY_PRIORITY, (tr("Process memory priority (Windows >= 8 only)")
437 + ' ' + makeLink("https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-memory_priority_information", "(?)"))
438 , &m_comboBoxOSMemoryPriority);
439 #endif
441 // Async IO threads
442 m_spinBoxAsyncIOThreads.setMinimum(1);
443 m_spinBoxAsyncIOThreads.setMaximum(1024);
444 m_spinBoxAsyncIOThreads.setValue(session->asyncIOThreads());
445 addRow(ASYNC_IO_THREADS, (tr("Asynchronous I/O threads") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#aio_threads", "(?)"))
446 , &m_spinBoxAsyncIOThreads);
448 #ifdef QBT_USES_LIBTORRENT2
449 // Hashing threads
450 m_spinBoxHashingThreads.setMinimum(1);
451 m_spinBoxHashingThreads.setMaximum(1024);
452 m_spinBoxHashingThreads.setValue(session->hashingThreads());
453 addRow(HASHING_THREADS, (tr("Hashing threads") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#hashing_threads", "(?)"))
454 , &m_spinBoxHashingThreads);
455 #endif
457 // File pool size
458 m_spinBoxFilePoolSize.setMinimum(1);
459 m_spinBoxFilePoolSize.setMaximum(std::numeric_limits<int>::max());
460 m_spinBoxFilePoolSize.setValue(session->filePoolSize());
461 addRow(FILE_POOL_SIZE, (tr("File pool size") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#file_pool_size", "(?)"))
462 , &m_spinBoxFilePoolSize);
464 // Checking Memory Usage
465 m_spinBoxCheckingMemUsage.setMinimum(1);
466 // When build as 32bit binary, set the maximum value lower to prevent crashes.
467 #ifdef QBT_APP_64BIT
468 m_spinBoxCheckingMemUsage.setMaximum(1024);
469 #else
470 // Allocate at most 128MiB out of the remaining 512MiB (see the cache part below)
471 m_spinBoxCheckingMemUsage.setMaximum(128);
472 #endif
473 m_spinBoxCheckingMemUsage.setValue(session->checkingMemUsage());
474 m_spinBoxCheckingMemUsage.setSuffix(tr(" MiB"));
475 addRow(CHECKING_MEM_USAGE, (tr("Outstanding memory when checking torrents") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#checking_mem_usage", "(?)"))
476 , &m_spinBoxCheckingMemUsage);
477 #ifndef QBT_USES_LIBTORRENT2
478 // Disk write cache
479 m_spinBoxCache.setMinimum(-1);
480 // When build as 32bit binary, set the maximum at less than 2GB to prevent crashes.
481 #ifdef QBT_APP_64BIT
482 m_spinBoxCache.setMaximum(33554431); // 32768GiB
483 #else
484 // allocate 1536MiB and leave 512MiB to the rest of program data in RAM
485 m_spinBoxCache.setMaximum(1536);
486 #endif
487 m_spinBoxCache.setValue(session->diskCacheSize());
488 updateCacheSpinSuffix(m_spinBoxCache.value());
489 connect(&m_spinBoxCache, qOverload<int>(&QSpinBox::valueChanged)
490 , this, &AdvancedSettings::updateCacheSpinSuffix);
491 addRow(DISK_CACHE, (tr("Disk cache") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#cache_size", "(?)"))
492 , &m_spinBoxCache);
493 // Disk cache expiry
494 m_spinBoxCacheTTL.setMinimum(1);
495 m_spinBoxCacheTTL.setMaximum(std::numeric_limits<int>::max());
496 m_spinBoxCacheTTL.setValue(session->diskCacheTTL());
497 m_spinBoxCacheTTL.setSuffix(tr(" s", " seconds"));
498 addRow(DISK_CACHE_TTL, (tr("Disk cache expiry interval") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#cache_expiry", "(?)"))
499 , &m_spinBoxCacheTTL);
500 #endif
501 // Enable OS cache
502 m_checkBoxOsCache.setChecked(session->useOSCache());
503 addRow(OS_CACHE, (tr("Enable OS cache") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#disk_io_write_mode", "(?)"))
504 , &m_checkBoxOsCache);
505 #ifndef QBT_USES_LIBTORRENT2
506 // Coalesce reads & writes
507 m_checkBoxCoalesceRW.setChecked(session->isCoalesceReadWriteEnabled());
508 addRow(COALESCE_RW, (tr("Coalesce reads & writes") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#coalesce_reads", "(?)"))
509 , &m_checkBoxCoalesceRW);
510 #endif
511 // Piece extent affinity
512 m_checkBoxPieceExtentAffinity.setChecked(session->usePieceExtentAffinity());
513 addRow(PIECE_EXTENT_AFFINITY, (tr("Use piece extent affinity") + ' ' + makeLink("https://libtorrent.org/single-page-ref.html#piece_extent_affinity", "(?)")), &m_checkBoxPieceExtentAffinity);
514 // Suggest mode
515 m_checkBoxSuggestMode.setChecked(session->isSuggestModeEnabled());
516 addRow(SUGGEST_MODE, (tr("Send upload piece suggestions") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#suggest_mode", "(?)"))
517 , &m_checkBoxSuggestMode);
518 // Send buffer watermark
519 m_spinBoxSendBufferWatermark.setMinimum(1);
520 m_spinBoxSendBufferWatermark.setMaximum(std::numeric_limits<int>::max());
521 m_spinBoxSendBufferWatermark.setSuffix(tr(" KiB"));
522 m_spinBoxSendBufferWatermark.setValue(session->sendBufferWatermark());
523 addRow(SEND_BUF_WATERMARK, (tr("Send buffer watermark") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#send_buffer_watermark", "(?)"))
524 , &m_spinBoxSendBufferWatermark);
525 m_spinBoxSendBufferLowWatermark.setMinimum(1);
526 m_spinBoxSendBufferLowWatermark.setMaximum(std::numeric_limits<int>::max());
527 m_spinBoxSendBufferLowWatermark.setSuffix(tr(" KiB"));
528 m_spinBoxSendBufferLowWatermark.setValue(session->sendBufferLowWatermark());
529 addRow(SEND_BUF_LOW_WATERMARK, (tr("Send buffer low watermark") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#send_buffer_low_watermark", "(?)"))
530 , &m_spinBoxSendBufferLowWatermark);
531 m_spinBoxSendBufferWatermarkFactor.setMinimum(1);
532 m_spinBoxSendBufferWatermarkFactor.setMaximum(std::numeric_limits<int>::max());
533 m_spinBoxSendBufferWatermarkFactor.setSuffix(" %");
534 m_spinBoxSendBufferWatermarkFactor.setValue(session->sendBufferWatermarkFactor());
535 addRow(SEND_BUF_WATERMARK_FACTOR, (tr("Send buffer watermark factor") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#send_buffer_watermark_factor", "(?)"))
536 , &m_spinBoxSendBufferWatermarkFactor);
537 // Outgoing connections per second
538 m_spinBoxConnectionSpeed.setMinimum(0);
539 m_spinBoxConnectionSpeed.setMaximum(std::numeric_limits<int>::max());
540 m_spinBoxConnectionSpeed.setValue(session->connectionSpeed());
541 addRow(CONNECTION_SPEED, (tr("Outgoing connections per second") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#connection_speed", "(?)"))
542 , &m_spinBoxConnectionSpeed);
543 // Socket listen backlog size
544 m_spinBoxSocketBacklogSize.setMinimum(1);
545 m_spinBoxSocketBacklogSize.setMaximum(std::numeric_limits<int>::max());
546 m_spinBoxSocketBacklogSize.setValue(session->socketBacklogSize());
547 addRow(SOCKET_BACKLOG_SIZE, (tr("Socket backlog size") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#listen_queue_size", "(?)"))
548 , &m_spinBoxSocketBacklogSize);
549 // Save resume data interval
550 m_spinBoxSaveResumeDataInterval.setMinimum(0);
551 m_spinBoxSaveResumeDataInterval.setMaximum(std::numeric_limits<int>::max());
552 m_spinBoxSaveResumeDataInterval.setValue(session->saveResumeDataInterval());
553 connect(&m_spinBoxSaveResumeDataInterval, qOverload<int>(&QSpinBox::valueChanged)
554 , this, &AdvancedSettings::updateSaveResumeDataIntervalSuffix);
555 updateSaveResumeDataIntervalSuffix(m_spinBoxSaveResumeDataInterval.value());
556 addRow(SAVE_RESUME_DATA_INTERVAL, tr("Save resume data interval", "How often the fastresume file is saved."), &m_spinBoxSaveResumeDataInterval);
557 // Outgoing port Min
558 m_spinBoxOutgoingPortsMin.setMinimum(0);
559 m_spinBoxOutgoingPortsMin.setMaximum(65535);
560 m_spinBoxOutgoingPortsMin.setValue(session->outgoingPortsMin());
561 addRow(OUTGOING_PORT_MIN, (tr("Outgoing ports (Min) [0: Disabled]")
562 + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#outgoing_port", "(?)"))
563 , &m_spinBoxOutgoingPortsMin);
564 // Outgoing port Min
565 m_spinBoxOutgoingPortsMax.setMinimum(0);
566 m_spinBoxOutgoingPortsMax.setMaximum(65535);
567 m_spinBoxOutgoingPortsMax.setValue(session->outgoingPortsMax());
568 addRow(OUTGOING_PORT_MAX, (tr("Outgoing ports (Max) [0: Disabled]")
569 + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#outgoing_port", "(?)"))
570 , &m_spinBoxOutgoingPortsMax);
571 // UPnP lease duration
572 m_spinBoxUPnPLeaseDuration.setMinimum(0);
573 m_spinBoxUPnPLeaseDuration.setMaximum(std::numeric_limits<int>::max());
574 m_spinBoxUPnPLeaseDuration.setValue(session->UPnPLeaseDuration());
575 m_spinBoxUPnPLeaseDuration.setSuffix(tr(" s", " seconds"));
576 addRow(UPNP_LEASE_DURATION, (tr("UPnP lease duration [0: Permanent lease]") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#upnp_lease_duration", "(?)"))
577 , &m_spinBoxUPnPLeaseDuration);
578 // Type of service
579 m_spinBoxPeerToS.setMinimum(0);
580 m_spinBoxPeerToS.setMaximum(255);
581 m_spinBoxPeerToS.setValue(session->peerToS());
582 addRow(PEER_TOS, (tr("Type of service (ToS) for connections to peers") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#peer_tos", "(?)"))
583 , &m_spinBoxPeerToS);
584 // uTP-TCP mixed mode
585 m_comboBoxUtpMixedMode.addItems({tr("Prefer TCP"), tr("Peer proportional (throttles TCP)")});
586 m_comboBoxUtpMixedMode.setCurrentIndex(static_cast<int>(session->utpMixedMode()));
587 addRow(UTP_MIX_MODE, (tr("%1-TCP mixed mode algorithm", "uTP-TCP mixed mode algorithm").arg(C_UTP)
588 + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#mixed_mode_algorithm", "(?)"))
589 , &m_comboBoxUtpMixedMode);
590 // Support internationalized domain name (IDN)
591 m_checkBoxIDNSupport.setChecked(session->isIDNSupportEnabled());
592 addRow(IDN_SUPPORT, (tr("Support internationalized domain name (IDN)")
593 + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#allow_idna", "(?)"))
594 , &m_checkBoxIDNSupport);
595 // multiple connections per IP
596 m_checkBoxMultiConnectionsPerIp.setChecked(session->multiConnectionsPerIpEnabled());
597 addRow(MULTI_CONNECTIONS_PER_IP, (tr("Allow multiple connections from the same IP address")
598 + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#allow_multiple_connections_per_ip", "(?)"))
599 , &m_checkBoxMultiConnectionsPerIp);
600 // Validate HTTPS tracker certificate
601 m_checkBoxValidateHTTPSTrackerCertificate.setChecked(session->validateHTTPSTrackerCertificate());
602 addRow(VALIDATE_HTTPS_TRACKER_CERTIFICATE, (tr("Validate HTTPS tracker certificates")
603 + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#validate_https_trackers", "(?)"))
604 , &m_checkBoxValidateHTTPSTrackerCertificate);
605 // SSRF mitigation
606 m_checkBoxSSRFMitigation.setChecked(session->isSSRFMitigationEnabled());
607 addRow(SSRF_MITIGATION, (tr("Server-side request forgery (SSRF) mitigation")
608 + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#ssrf_mitigation", "(?)"))
609 , &m_checkBoxSSRFMitigation);
610 // Disallow connection to peers on privileged ports
611 m_checkBoxBlockPeersOnPrivilegedPorts.setChecked(session->blockPeersOnPrivilegedPorts());
612 addRow(BLOCK_PEERS_ON_PRIVILEGED_PORTS, (tr("Disallow connection to peers on privileged ports") + ' ' + makeLink("https://libtorrent.org/single-page-ref.html#no_connect_privileged_ports", "(?)")), &m_checkBoxBlockPeersOnPrivilegedPorts);
613 // Recheck completed torrents
614 m_checkBoxRecheckCompleted.setChecked(pref->recheckTorrentsOnCompletion());
615 addRow(RECHECK_COMPLETED, tr("Recheck torrents on completion"), &m_checkBoxRecheckCompleted);
616 // Transfer list refresh interval
617 m_spinBoxListRefresh.setMinimum(30);
618 m_spinBoxListRefresh.setMaximum(99999);
619 m_spinBoxListRefresh.setValue(session->refreshInterval());
620 m_spinBoxListRefresh.setSuffix(tr(" ms", " milliseconds"));
621 addRow(LIST_REFRESH, tr("Transfer list refresh interval"), &m_spinBoxListRefresh);
622 // Resolve Peer countries
623 m_checkBoxResolveCountries.setChecked(pref->resolvePeerCountries());
624 addRow(RESOLVE_COUNTRIES, tr("Resolve peer countries"), &m_checkBoxResolveCountries);
625 // Resolve peer hosts
626 m_checkBoxResolveHosts.setChecked(pref->resolvePeerHostNames());
627 addRow(RESOLVE_HOSTS, tr("Resolve peer host names"), &m_checkBoxResolveHosts);
628 // Network interface
629 m_comboBoxInterface.addItem(tr("Any interface", "i.e. Any network interface"));
630 const QString currentInterface = session->networkInterface();
631 bool interfaceExists = currentInterface.isEmpty();
632 int i = 1;
633 for (const QNetworkInterface &iface : asConst(QNetworkInterface::allInterfaces()))
635 m_comboBoxInterface.addItem(iface.humanReadableName(), iface.name());
636 if (!currentInterface.isEmpty() && (iface.name() == currentInterface))
638 m_comboBoxInterface.setCurrentIndex(i);
639 interfaceExists = true;
641 ++i;
643 // Saved interface does not exist, show it anyway
644 if (!interfaceExists)
646 m_comboBoxInterface.addItem(session->networkInterfaceName(), currentInterface);
647 m_comboBoxInterface.setCurrentIndex(i);
649 connect(&m_comboBoxInterface, qOverload<int>(&QComboBox::currentIndexChanged)
650 , this, &AdvancedSettings::updateInterfaceAddressCombo);
651 addRow(NETWORK_IFACE, tr("Network interface"), &m_comboBoxInterface);
652 // Network interface address
653 updateInterfaceAddressCombo();
654 addRow(NETWORK_IFACE_ADDRESS, tr("Optional IP address to bind to"), &m_comboBoxInterfaceAddress);
655 // Announce IP
656 m_lineEditAnnounceIP.setText(session->announceIP());
657 addRow(ANNOUNCE_IP, (tr("IP Address to report to trackers (requires restart)")
658 + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#announce_ip", "(?)"))
659 , &m_lineEditAnnounceIP);
660 // Max concurrent HTTP announces
661 m_spinBoxMaxConcurrentHTTPAnnounces.setMaximum(std::numeric_limits<int>::max());
662 m_spinBoxMaxConcurrentHTTPAnnounces.setValue(session->maxConcurrentHTTPAnnounces());
663 addRow(MAX_CONCURRENT_HTTP_ANNOUNCES, (tr("Max concurrent HTTP announces") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#max_concurrent_http_announces", "(?)"))
664 , &m_spinBoxMaxConcurrentHTTPAnnounces);
665 // Stop tracker timeout
666 m_spinBoxStopTrackerTimeout.setValue(session->stopTrackerTimeout());
667 m_spinBoxStopTrackerTimeout.setSuffix(tr(" s", " seconds"));
668 addRow(STOP_TRACKER_TIMEOUT, (tr("Stop tracker timeout") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#stop_tracker_timeout", "(?)"))
669 , &m_spinBoxStopTrackerTimeout);
671 // Program notifications
672 const MainWindow *const mainWindow = static_cast<Application*>(QCoreApplication::instance())->mainWindow();
673 m_checkBoxProgramNotifications.setChecked(mainWindow->isNotificationsEnabled());
674 addRow(PROGRAM_NOTIFICATIONS, tr("Display notifications"), &m_checkBoxProgramNotifications);
675 // Torrent added notifications
676 m_checkBoxTorrentAddedNotifications.setChecked(mainWindow->isTorrentAddedNotificationsEnabled());
677 addRow(TORRENT_ADDED_NOTIFICATIONS, tr("Display notifications for added torrents"), &m_checkBoxTorrentAddedNotifications);
678 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
679 // Notification timeout
680 m_spinBoxNotificationTimeout.setMinimum(-1);
681 m_spinBoxNotificationTimeout.setMaximum(std::numeric_limits<int>::max());
682 m_spinBoxNotificationTimeout.setValue(mainWindow->getNotificationTimeout());
683 m_spinBoxNotificationTimeout.setSpecialValueText(tr("System default"));
684 m_spinBoxNotificationTimeout.setSuffix(tr(" ms", " milliseconds"));
685 addRow(NOTIFICATION_TIMEOUT, tr("Notification timeout [0: infinite]"), &m_spinBoxNotificationTimeout);
686 #endif
687 // Reannounce to all trackers when ip/port changed
688 m_checkBoxReannounceWhenAddressChanged.setChecked(session->isReannounceWhenAddressChangedEnabled());
689 addRow(REANNOUNCE_WHEN_ADDRESS_CHANGED, tr("Reannounce to all trackers when IP or port changed"), &m_checkBoxReannounceWhenAddressChanged);
690 // Download tracker's favicon
691 m_checkBoxTrackerFavicon.setChecked(mainWindow->isDownloadTrackerFavicon());
692 addRow(DOWNLOAD_TRACKER_FAVICON, tr("Download tracker's favicon"), &m_checkBoxTrackerFavicon);
693 // Save path history length
694 m_spinBoxSavePathHistoryLength.setRange(AddNewTorrentDialog::minPathHistoryLength, AddNewTorrentDialog::maxPathHistoryLength);
695 m_spinBoxSavePathHistoryLength.setValue(AddNewTorrentDialog::savePathHistoryLength());
696 addRow(SAVE_PATH_HISTORY_LENGTH, tr("Save path history length"), &m_spinBoxSavePathHistoryLength);
697 // Enable speed graphs
698 m_checkBoxSpeedWidgetEnabled.setChecked(pref->isSpeedWidgetEnabled());
699 addRow(ENABLE_SPEED_WIDGET, tr("Enable speed graphs"), &m_checkBoxSpeedWidgetEnabled);
700 #ifndef Q_OS_MACOS
701 // Enable icons in menus
702 m_checkBoxIconsInMenusEnabled.setChecked(pref->iconsInMenusEnabled());
703 addRow(ENABLE_ICONS_IN_MENUS, tr("Enable icons in menus"), &m_checkBoxIconsInMenusEnabled);
704 #endif
705 // Tracker State
706 m_checkBoxTrackerStatus.setChecked(session->isTrackerEnabled());
707 addRow(TRACKER_STATUS, tr("Enable embedded tracker"), &m_checkBoxTrackerStatus);
708 // Tracker port
709 m_spinBoxTrackerPort.setMinimum(1);
710 m_spinBoxTrackerPort.setMaximum(65535);
711 m_spinBoxTrackerPort.setValue(pref->getTrackerPort());
712 addRow(TRACKER_PORT, tr("Embedded tracker port"), &m_spinBoxTrackerPort);
713 // Choking algorithm
714 m_comboBoxChokingAlgorithm.addItems({tr("Fixed slots"), tr("Upload rate based")});
715 m_comboBoxChokingAlgorithm.setCurrentIndex(static_cast<int>(session->chokingAlgorithm()));
716 addRow(CHOKING_ALGORITHM, (tr("Upload slots behavior") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#choking_algorithm", "(?)"))
717 , &m_comboBoxChokingAlgorithm);
718 // Seed choking algorithm
719 m_comboBoxSeedChokingAlgorithm.addItems({tr("Round-robin"), tr("Fastest upload"), tr("Anti-leech")});
720 m_comboBoxSeedChokingAlgorithm.setCurrentIndex(static_cast<int>(session->seedChokingAlgorithm()));
721 addRow(SEED_CHOKING_ALGORITHM, (tr("Upload choking algorithm") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#seed_choking_algorithm", "(?)"))
722 , &m_comboBoxSeedChokingAlgorithm);
724 // Torrent recheck confirmation
725 m_checkBoxConfirmTorrentRecheck.setChecked(pref->confirmTorrentRecheck());
726 addRow(CONFIRM_RECHECK_TORRENT, tr("Confirm torrent recheck"), &m_checkBoxConfirmTorrentRecheck);
728 // Remove all tags confirmation
729 m_checkBoxConfirmRemoveAllTags.setChecked(pref->confirmRemoveAllTags());
730 addRow(CONFIRM_REMOVE_ALL_TAGS, tr("Confirm removal of all tags"), &m_checkBoxConfirmRemoveAllTags);
732 // Announce to all trackers in a tier
733 m_checkBoxAnnounceAllTrackers.setChecked(session->announceToAllTrackers());
734 addRow(ANNOUNCE_ALL_TRACKERS, (tr("Always announce to all trackers in a tier")
735 + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#announce_to_all_trackers", "(?)"))
736 , &m_checkBoxAnnounceAllTrackers);
738 // Announce to all tiers
739 m_checkBoxAnnounceAllTiers.setChecked(session->announceToAllTiers());
740 addRow(ANNOUNCE_ALL_TIERS, (tr("Always announce to all tiers")
741 + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#announce_to_all_tiers", "(?)"))
742 , &m_checkBoxAnnounceAllTiers);
744 m_spinBoxPeerTurnover.setMinimum(0);
745 m_spinBoxPeerTurnover.setMaximum(100);
746 m_spinBoxPeerTurnover.setValue(session->peerTurnover());
747 m_spinBoxPeerTurnover.setSuffix(" %");
748 addRow(PEER_TURNOVER, (tr("Peer turnover disconnect percentage") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#peer_turnover", "(?)"))
749 , &m_spinBoxPeerTurnover);
750 m_spinBoxPeerTurnoverCutoff.setMinimum(0);
751 m_spinBoxPeerTurnoverCutoff.setMaximum(100);
752 m_spinBoxPeerTurnoverCutoff.setSuffix(" %");
753 m_spinBoxPeerTurnoverCutoff.setValue(session->peerTurnoverCutoff());
754 addRow(PEER_TURNOVER_CUTOFF, (tr("Peer turnover threshold percentage") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#peer_turnover", "(?)"))
755 , &m_spinBoxPeerTurnoverCutoff);
756 m_spinBoxPeerTurnoverInterval.setMinimum(30);
757 m_spinBoxPeerTurnoverInterval.setMaximum(3600);
758 m_spinBoxPeerTurnoverInterval.setSuffix(tr(" s", " seconds"));
759 m_spinBoxPeerTurnoverInterval.setValue(session->peerTurnoverInterval());
760 addRow(PEER_TURNOVER_INTERVAL, (tr("Peer turnover disconnect interval") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#peer_turnover", "(?)"))
761 , &m_spinBoxPeerTurnoverInterval);
764 template <typename T>
765 void AdvancedSettings::addRow(const int row, const QString &text, T *widget)
767 auto label = new QLabel(text);
768 label->setOpenExternalLinks(true);
770 setCellWidget(row, PROPERTY, label);
771 setCellWidget(row, VALUE, widget);
773 if constexpr (std::is_same_v<T, QCheckBox>)
774 connect(widget, &QCheckBox::stateChanged, this, &AdvancedSettings::settingsChanged);
775 else if constexpr (std::is_same_v<T, QSpinBox>)
776 connect(widget, qOverload<int>(&QSpinBox::valueChanged), this, &AdvancedSettings::settingsChanged);
777 else if constexpr (std::is_same_v<T, QComboBox>)
778 connect(widget, qOverload<int>(&QComboBox::currentIndexChanged), this, &AdvancedSettings::settingsChanged);
779 else if constexpr (std::is_same_v<T, QLineEdit>)
780 connect(widget, &QLineEdit::textChanged, this, &AdvancedSettings::settingsChanged);