mirror of
https://github.com/transmission/transmission.git
synced 2025-12-20 10:28:32 +00:00
* refactor: change `leftUntilDone` to `left_until_done` * refactor: change `magnetLink` to `magnet_link` * refactor: change `manualAnnounceTime` to `manual_announce_time` * refactor: change `maxConnectedPeers` to `max_connected_peers` * refactor: change `metadataPercentComplete` to `metadata_percent_complete` * refactor: change `peersConnected` to `peers_connected` * refactor: change `peersFrom` to `peers_from` * refactor: change `peersGettingFromUs` to `peers_getting_from_us` * refactor: change `peersSendingToUs` to `peers_sending_to_us` * refactor: change `percentComplete` to `percent_complete` * refactor: change `percentDone` to `percent_done` * refactor: change `pieceCount` to `piece_count` * refactor: use quark when possible * refactor: change `pieceSize` to `piece_size` * refactor: change `primary-mime-type` to `primary_mime_type` * refactor: change `rateDownload` to `rate_download` * refactor: change `rateUpload` to `rate_upload` * refactor: change `recheckProgress` to `recheck_progress` * refactor: change `secondsDownloading` to `seconds_downloading` * refactor: change `secondsSeeding` to `seconds_seeding` * refactor: change `sizeWhenDone` to `size_when_done` * refactor: change `startDate` to `start_date` * refactor: change `trackerStats` to `tracker_stats` * refactor: change `totalSize` to `total_size` * refactor: change `torrentFile` to `torrent_file` * refactor: change `uploadedEver` to `uploaded_ever` * refactor: change `uploadRatio` to `upload_ratio` * refactor: change `webseedsSendingToUs` to `webseeds_sending_to_us` * refactor: change `bytesCompleted` to `bytes_completed` * refactor: change `clientName` to `client_name` * refactor: change `clientIsChoked` to `client_is_choked` * refactor: change `clientIsInterested` to `client_is_interested` * refactor: change `flagStr` to `flag_str` * refactor: change `isDownloadingFrom` to `is_downloading_from` * refactor: change `isEncrypted` to `is_encrypted` * refactor: change `isIncoming` to `is_incoming` * refactor: change `isUploadingTo` to `is_uploading_to` * refactor: change `isUTP` to `is_utp` * refactor: change `peerIsChoked` to `peer_is_choked` * refactor: change `peerIsInterested` to `peer_is_interested` * refactor: change `rateToClient` to `rate_to_client` * refactor: change `rateToPeer` to `rate_to_peer` * refactor: change `fromCache` to `from_cache` * refactor: change `fromDht` to `from_dht` * refactor: change `fromIncoming` to `from_incoming` * refactor: change `fromLpd` to `from_lpd` * refactor: change `fromLtep` to `from_ltep` * refactor: change `fromPex` to `from_pex` * refactor: change `fromTracker` to `from_tracker` * refactor: change `announceState` to `announce_state` * refactor: change `downloadCount` to `download_count` * refactor: change `hasAnnounced` to `has_announced` * refactor: change `hasScraped` to `has_scraped` * refactor: change `isBackup` to `is_backup` * refactor: change `lastAnnouncePeerCount` to `last_announce_peer_count` * refactor: change `lastAnnounceResult` to `last_announce_result` * refactor: change `lastAnnounceStartTime` to `last_announce_start_time` * refactor: change `lastAnnounceSucceeded` to `last_announce_succeeded` * refactor: change `lastAnnounceTime` to `last_announce_time` * refactor: change `lastAnnounceTimedOut` to `last_announce_timed_out` * refactor: change `lastScrapeResult` to `last_scrape_result` * refactor: change `lastScrapeStartTime` to `last_scrape_start_time` * refactor: change `lastScrapeSucceeded` to `last_scrape_succeeded` * refactor: change `lastScrapeTime` to `last_scrape_time` * refactor: change `lastScrapeTimedOut` to `last_scrape_timed_out` * refactor: change `leecherCount` to `leecher_count` * refactor: change `nextAnnounceTime` to `next_announce_time` * refactor: change `nextScrapeTime` to `next_scrape_time` * refactor: change `scrapeState` to `scrape_state` * refactor: change `seederCount` to `seeder_count` * refactor: change `torrent-added` to `torrent_added` * refactor: change `torrent-duplicate` to `torrent_duplicate` * refactor: change `torrent-remove` to `torrent_remove` * refactor: change `delete-local-data` to `delete_local_data` * refactor: change `torrent-rename-path` to `torrent_rename_path` * refactor: change `alt-speed-down` to `alt_speed_down` * refactor: convert `pref_toggle_entries` to quark array * refactor: change `alt-speed-enabled` to `alt_speed_enabled` * refactor: change `compact-view` to `compact_view` * refactor: change `sort-reversed` to `sort_reversed` * refactor: change `show-filterbar` to `show_filterbar` * refactor: change `show-statusbar` to `show_statusbar` * refactor: change `show-toolbar` to `show_toolbar` * refactor: change `alt-speed-time-begin` to `alt_speed_time_begin` * refactor: change `alt-speed-time-day` to `alt_speed_time_day` * refactor: change `alt-speed-time-end` to `alt_speed_time_end` * refactor: change `alt-speed-up` to `alt_speed_up` * refactor: change `alt-speed-time-enabled` to `alt_speed_time_enabled` * refactor: change `blocklist-enabled` to `blocklist_enabled` * refactor: change `blocklist-size` to `blocklist_size` * refactor: change `blocklist-url` to `blocklist_url` * refactor: change `cache-size-mb` to `cache_size_mb` * refactor: change `config-dir` to `config_dir` * refactor: change `default-trackers` to `default_trackers` * refactor: change `dht-enabled` to `dht_enabled` * refactor: change `download-dir-free-space` to `download_dir_free_space` * refactor: change `download-queue-enabled` to `download_queue_enabled` * refactor: change `download-queue-size` to `download_queue_size` * refactor: change `idle-seeding-limit-enabled` to `idle_seeding_limit_enabled` * refactor: change `idle-seeding-limit` to `idle_seeding_limit` * refactor: change `incomplete-dir-enabled` to `incomplete_dir_enabled` * refactor: change `incomplete-dir` to `incomplete_dir` * refactor: change `lpd-enabled` to `lpd_enabled` * refactor: change `peer-limit-global` to `peer_limit_global` * refactor: change `peer-limit-per-torrent` to `peer_limit_per_torrent` * refactor: change `peer-port-random-on-start` to `peer_port_random_on_start` * refactor: change `peer-port` to `peer_port` * refactor: change `pex-enabled` to `pex_enabled` * refactor: change `port-forwarding-enabled` to `port_forwarding_enabled` * refactor: change `queue-stalled-enabled` to `queue_stalled_enabled` * refactor: change `queue-stalled-minutes` to `queue_stalled_minutes` * refactor: change `rename-partial-files` to `rename_partial_files` * refactor: change `rpc-version-minimum` to `rpc_version_minimum` * refactor: change `rpc-version-semver` to `rpc_version_semver` * refactor: change `rpc-version` to `rpc_version` * refactor: change `script-torrent-added-enabled` to `script_torrent_added_enabled` * refactor: change `script-torrent-added-filename` to `script_torrent_added_filename` * refactor: change `script-torrent-done-enabled` to `script_torrent_done_enabled` * refactor: change `script-torrent-done-filename` to `script_torrent_done_filename` * refactor: change `script-torrent-done-seeding-enabled` to `script_torrent_done_seeding_enabled` * refactor: change `script-torrent-done-seeding-filename` to `script_torrent_done_seeding_filename` * refactor: change `seed-queue-enabled` to `seed_queue_enabled` * refactor: change `seed-queue-size` to `seed_queue_size` * refactor: change `seedRatioLimited` to `seed_ratio_limited` * refactor: change `session-id` to `session_id` * refactor: change `speed-limit-down-enabled` to `speed_limit_down_enabled` * refactor: change `speed-limit-down` to `speed_limit_down` * refactor: change `speed-limit-up-enabled` to `speed_limit_up_enabled` * refactor: change `speed-limit-up` to `speed_limit_up` * refactor: change `start-added-torrents` to `start_added_torrents` * refactor: change `trash-original-torrent-files` to `trash_original_torrent_files` * refactor: change `utp-enabled` to `utp_enabled` * refactor: change `tcp-enabled` to `tcp_enabled` * docs: add missing docs for RPC `tcp_enabled` * refactor: change `speed-units` to `speed_units` * refactor: change `speed-bytes` to `speed_bytes` * refactor: change `size-units` to `size_units` * refactor: change `size-bytes` to `size_bytes` * refactor: change `memory-units` to `memory_units` * refactor: change `memory-bytes` to `memory_bytes` * refactor: change `session-set` to `session_set` * refactor: change `session-get` to `session_get` * refactor: change `session-stats` to `session_stats` * refactor: change `activeTorrentCount` to `active_torrent_count` * refactor: change `downloadSpeed` to `download_speed` * refactor: change `pausedTorrentCount` to `paused_torrent_count` * refactor: change `torrentCount` to `torrent_count` * refactor: change `uploadSpeed` to `upload_speed` * refactor: change `cumulative-stats` to `cumulative_stats` * refactor: change `current-stats` to `current_stats` * refactor: change `uploadedBytes` and `uploaded-bytes` to `uploaded_bytes` * refactor: change `downloadedBytes` and `downloaded-bytes` to `downloaded_bytes` * refactor: change `filesAdded` and `files-added` to `files_added` * refactor: change `sessionCount` and `session-count` to `session_count` * refactor: change `secondsActive` and `seconds-active` to `seconds_active` * refactor: change `blocklist-update` to `blocklist_update` * refactor: change `port-test` to `port_test` * refactor: change `session-close` to `session_close` * refactor: change `queue-move-top` to `queue_move_top` * refactor: change `queue-move-up` to `queue_move_up` * refactor: change `queue-move-down` to `queue_move_down` * refactor: change `queue-move-bottom` to `queue_move_bottom` * refactor: change `free-space` to `free_space` * refactor: change `group-set` to `group_set` * refactor: change `group-get` to `group_get` * refactor: change `announce-ip` to `announce_ip` * refactor: change `announce-ip-enabled` to `announce_ip_enabled` * refactor: change `upload-slots-per-torrent` to `upload_slots_per_torrent` * refactor: change `trash-can-enabled` to `trash_can_enabled` * refactor: change `watch-dir-enabled` to `watch_dir_enabled` * refactor: change `watch-dir-force-generic` to `watch_dir_force_generic` * refactor: change `watch-dir` to `watch_dir` * refactor: change `message-level` to `message_level` * refactor: change `scrape-paused-torrents-enabled` to `scrape_paused_torrents_enabled` * refactor: change `torrent-added-verify-mode` to `torrent_added_verify_mode` * refactor: change `sleep-per-seconds-during-verify` to `sleep_per_seconds_during_verify` * refactor: change `bind-address-ipv4` to `bind_address_ipv4` * refactor: change `bind-address-ipv6` to `bind_address_ipv6` * refactor: change `peer-congestion-algorithm` to `peer_congestion_algorithm` * refactor: change `peer-socket-tos` to `peer_socket_tos` * refactor: change `peer-port-random-high` to `peer_port_random_high` * refactor: change `peer-port-random-low` to `peer_port_random_low` * refactor: change `anti-brute-force-enabled` to `anti_brute_force_enabled` * refactor: change `rpc-authentication-required` to `rpc_authentication_required` * refactor: change `rpc-bind-address` to `rpc_bind_address` * refactor: change `rpc-enabled` to `rpc_enabled` * refactor: change `rpc-host-whitelist` to `rpc_host_whitelist` * refactor: change `rpc-host-whitelist-enabled` to `rpc_host_whitelist_enabled` * refactor: change `rpc-password` to `rpc_password` * refactor: change `rpc-port` to `rpc_port` * refactor: change `rpc-socket-mode` to `rpc_socket_mode` * refactor: change `rpc-url` to `rpc_url` * refactor: change `rpc-username` to `rpc_username` * refactor: change `rpc-whitelist` to `rpc_whitelist` * refactor: change `rpc-whitelist-enabled` to `rpc_whitelist_enabled` * refactor: change `ratio-limit-enabled` to `ratio_limit_enabled` * refactor: change `ratio-limit` to `ratio_limit` * refactor: change `show-options-window` to `show_options_window` * refactor: change `open-dialog-dir` to `open_dialog_dir` * refactor: change `inhibit-desktop-hibernation` to `inhibit_desktop_hibernation` * refactor: change `show-notification-area-icon` to `show_notification_area_icon` * refactor: change `start-minimized` to `start_minimized` * refactor: change `torrent-added-notification-enabled` to `torrent_added_notification_enabled` * refactor: change `anti-brute-force-threshold` to `anti_brute_force_threshold` * refactor: change `torrent-complete-notification-enabled` to `torrent_complete_notification_enabled` * refactor: change `prompt-before-exit` to `prompt_before_exit` * refactor: change `sort-mode` to `sort_mode` * refactor: change `statusbar-stats` to `statusbar_stats` * refactor: change `show-extra-peer-details` to `show_extra_peer_details` * refactor: change `show-backup-trackers` to `show_backup_trackers` * refactor: change `blocklist-date` to `blocklist_date` * refactor: change `blocklist-updates-enabled` to `blocklist_updates_enabled` * refactor: change `main-window-layout-order` to `main_window_layout_order` * refactor: change `main-window-height` to `main_window_height` * refactor: change `main-window-width` to `main_window_width` * refactor: change `main-window-x` to `main_window_x` * refactor: change `main-window-y` to `main_window_y` * refactor: change `filter-mode` to `filter_mode` * refactor: change `filter-trackers` to `filter_trackers` * refactor: change `filter-text` to `filter_text` * refactor: change `remote-session-enabled` to `remote_session_enabled` * refactor: change `remote-session-host` to `remote_session_host` * refactor: change `remote-session-https` to `remote_session_https` * refactor: change `remote-session-password` to `remote_session_password` * refactor: change `remote-session-port` to `remote_session_port` * refactor: change `remote-session-requres-authentication` to `remote_session_requires_authentication` * refactor: change `remote-session-username` to `remote_session_username` * refactor: change `torrent-complete-sound-command` to `torrent_complete_sound_command` * refactor: change `torrent-complete-sound-enabled` to `torrent_complete_sound_enabled` * refactor: change `user-has-given-informed-consent` to `user_has_given_informed_consent` * refactor: change `read-clipboard` to `read_clipboard` * refactor: change `details-window-height` to `details_window_height` * refactor: change `details-window-width` to `details_window_width` * refactor: change `main-window-is-maximized` to `main_window_is_maximized` * refactor: change `port-is-open` to `port_is_open` * refactor: change `show-tracker-scrapes` to `show_tracker_scrapes` * refactor: change `max-peers` to `max_peers` * refactor: change `peers2-6` to `peers2_6` * refactor: change `seeding-time-seconds` to `seeding_time_seconds` * refactor: change `downloading-time-seconds` to `downloading_time_seconds` * refactor: change `ratio-mode` to `ratio_mode` * refactor: change `idle-limit` to `idle_limit` * refactor: change `idle-mode` to `idle_mode` * refactor: change `speed-Bps` to `speed_Bps` * refactor: change `use-global-speed-limit` to `use_global_speed_limit` * refactor: change `use-speed-limit` to `use_speed_limit` * chore: remove TODO comment * docs: add upgrade instructions to `5.0.0` * chore: bump rpc semver major version * chore: housekeeping
877 lines
27 KiB
C++
877 lines
27 KiB
C++
// This file Copyright © Mnemosyne LLC.
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
// License text can be found in the licenses/ folder.
|
|
|
|
#include "PrefsDialog.h"
|
|
|
|
#include <cassert>
|
|
#include <optional>
|
|
|
|
#include <QCheckBox>
|
|
#include <QComboBox>
|
|
#include <QCoreApplication>
|
|
#include <QDialogButtonBox>
|
|
#include <QDoubleSpinBox>
|
|
#include <QFileIconProvider>
|
|
#include <QFileInfo>
|
|
#include <QHBoxLayout>
|
|
#include <QIcon>
|
|
#include <QLabel>
|
|
#include <QLineEdit>
|
|
#include <QMessageBox>
|
|
#include <QPlainTextEdit>
|
|
#include <QPushButton>
|
|
#include <QSpinBox>
|
|
#include <QStyle>
|
|
#include <QTabWidget>
|
|
#include <QTime>
|
|
#include <QTimeEdit>
|
|
#include <QTimer>
|
|
#include <QVBoxLayout>
|
|
|
|
#include "ColumnResizer.h"
|
|
#include "Formatter.h"
|
|
#include "FreeSpaceLabel.h"
|
|
#include "Prefs.h"
|
|
#include "Session.h"
|
|
#include "Utils.h"
|
|
|
|
// ---
|
|
|
|
namespace
|
|
{
|
|
|
|
class PreferenceWidget
|
|
{
|
|
static char const* const PrefKey;
|
|
|
|
public:
|
|
explicit PreferenceWidget(QObject* object)
|
|
: object_{ object }
|
|
{
|
|
}
|
|
|
|
template<typename T>
|
|
[[nodiscard]] bool is() const
|
|
{
|
|
return qobject_cast<T*>(object_) != nullptr;
|
|
}
|
|
|
|
template<typename T>
|
|
[[nodiscard]] T const* as() const
|
|
{
|
|
assert(is<T>());
|
|
return static_cast<T const*>(object_);
|
|
}
|
|
|
|
template<typename T>
|
|
[[nodiscard]] T* as()
|
|
{
|
|
assert(is<T>());
|
|
return static_cast<T*>(object_);
|
|
}
|
|
|
|
void setPrefKey(int key)
|
|
{
|
|
object_->setProperty(PrefKey, key);
|
|
}
|
|
|
|
[[nodiscard]] int getPrefKey() const
|
|
{
|
|
return object_->property(PrefKey).toInt();
|
|
}
|
|
|
|
private:
|
|
QObject* object_;
|
|
};
|
|
|
|
char const* const PreferenceWidget::PrefKey = "pref-key";
|
|
|
|
int qtDayToTrDay(int day)
|
|
{
|
|
switch (day)
|
|
{
|
|
case Qt::Monday:
|
|
return TR_SCHED_MON;
|
|
|
|
case Qt::Tuesday:
|
|
return TR_SCHED_TUES;
|
|
|
|
case Qt::Wednesday:
|
|
return TR_SCHED_WED;
|
|
|
|
case Qt::Thursday:
|
|
return TR_SCHED_THURS;
|
|
|
|
case Qt::Friday:
|
|
return TR_SCHED_FRI;
|
|
|
|
case Qt::Saturday:
|
|
return TR_SCHED_SAT;
|
|
|
|
case Qt::Sunday:
|
|
return TR_SCHED_SUN;
|
|
|
|
default:
|
|
assert(false && "Invalid day of week");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
QString qtDayName(int day)
|
|
{
|
|
switch (day)
|
|
{
|
|
case Qt::Monday:
|
|
return PrefsDialog::tr("Monday");
|
|
|
|
case Qt::Tuesday:
|
|
return PrefsDialog::tr("Tuesday");
|
|
|
|
case Qt::Wednesday:
|
|
return PrefsDialog::tr("Wednesday");
|
|
|
|
case Qt::Thursday:
|
|
return PrefsDialog::tr("Thursday");
|
|
|
|
case Qt::Friday:
|
|
return PrefsDialog::tr("Friday");
|
|
|
|
case Qt::Saturday:
|
|
return PrefsDialog::tr("Saturday");
|
|
|
|
case Qt::Sunday:
|
|
return PrefsDialog::tr("Sunday");
|
|
|
|
default:
|
|
assert(false && "Invalid day of week");
|
|
return {};
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] bool isDescendantOf(QObject const* descendant, QObject const* ancestor)
|
|
{
|
|
if (ancestor == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
while (descendant != nullptr)
|
|
{
|
|
if (descendant == ancestor)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
descendant = descendant->parent();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
} // namespace
|
|
|
|
bool PrefsDialog::updateWidgetValue(QWidget* widget, int pref_key) const
|
|
{
|
|
auto pref_widget = PreferenceWidget{ widget };
|
|
|
|
if (pref_widget.is<QCheckBox>())
|
|
{
|
|
pref_widget.as<QCheckBox>()->setChecked(prefs_.getBool(pref_key));
|
|
}
|
|
else if (pref_widget.is<QSpinBox>())
|
|
{
|
|
pref_widget.as<QSpinBox>()->setValue(prefs_.getInt(pref_key));
|
|
}
|
|
else if (pref_widget.is<QDoubleSpinBox>())
|
|
{
|
|
pref_widget.as<QDoubleSpinBox>()->setValue(prefs_.getDouble(pref_key));
|
|
}
|
|
else if (pref_widget.is<QTimeEdit>())
|
|
{
|
|
pref_widget.as<QTimeEdit>()->setTime(QTime{ 0, 0 }.addSecs(prefs_.getInt(pref_key) * 60));
|
|
}
|
|
else if (pref_widget.is<QLineEdit>())
|
|
{
|
|
pref_widget.as<QLineEdit>()->setText(prefs_.getString(pref_key));
|
|
}
|
|
else if (pref_widget.is<PathButton>())
|
|
{
|
|
pref_widget.as<PathButton>()->setPath(prefs_.getString(pref_key));
|
|
}
|
|
else if (pref_widget.is<FreeSpaceLabel>())
|
|
{
|
|
pref_widget.as<FreeSpaceLabel>()->setPath(prefs_.getString(pref_key));
|
|
}
|
|
else if (pref_widget.is<QPlainTextEdit>())
|
|
{
|
|
pref_widget.as<QPlainTextEdit>()->setPlainText(prefs_.getString(pref_key));
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PrefsDialog::linkWidgetToPref(QWidget* widget, int pref_key)
|
|
{
|
|
auto pref_widget = PreferenceWidget{ widget };
|
|
|
|
pref_widget.setPrefKey(pref_key);
|
|
updateWidgetValue(widget, pref_key);
|
|
widgets_.try_emplace(pref_key, widget);
|
|
|
|
if (auto const* check_box = qobject_cast<QCheckBox*>(widget); check_box != nullptr)
|
|
{
|
|
connect(check_box, &QAbstractButton::toggled, this, &PrefsDialog::checkBoxToggled);
|
|
return;
|
|
}
|
|
|
|
if (auto const* time_edit = qobject_cast<QTimeEdit*>(widget); time_edit != nullptr)
|
|
{
|
|
connect(time_edit, &QAbstractSpinBox::editingFinished, this, &PrefsDialog::timeEditingFinished);
|
|
return;
|
|
}
|
|
|
|
if (auto const* line_edit = qobject_cast<QLineEdit*>(widget); line_edit != nullptr)
|
|
{
|
|
connect(line_edit, &QLineEdit::editingFinished, this, &PrefsDialog::lineEditingFinished);
|
|
return;
|
|
}
|
|
|
|
if (auto const* path_button = qobject_cast<PathButton*>(widget); path_button != nullptr)
|
|
{
|
|
connect(path_button, &PathButton::pathChanged, this, &PrefsDialog::pathChanged);
|
|
return;
|
|
}
|
|
|
|
if (auto const* spin_box = qobject_cast<QAbstractSpinBox*>(widget); spin_box != nullptr)
|
|
{
|
|
connect(spin_box, &QAbstractSpinBox::editingFinished, this, &PrefsDialog::spinBoxEditingFinished);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void PrefsDialog::focusChanged(QWidget* old, QWidget* cur)
|
|
{
|
|
// We don't want to change the preference every time there's a keystroke
|
|
// in a QPlainTextEdit, so instead of connecting to the textChanged signal,
|
|
// only update the pref when the text changed AND focus was lost.
|
|
char constexpr const* const StartValue = "StartValue";
|
|
|
|
if (auto* const edit = qobject_cast<QPlainTextEdit*>(cur); isDescendantOf(edit, this))
|
|
{
|
|
edit->setProperty(StartValue, edit->toPlainText());
|
|
}
|
|
|
|
if (auto const* const edit = qobject_cast<QPlainTextEdit*>(old); isDescendantOf(edit, this))
|
|
{
|
|
if (auto const val = edit->toPlainText(); val != edit->property(StartValue).toString())
|
|
{
|
|
setPref(PreferenceWidget{ old }.getPrefKey(), val);
|
|
}
|
|
}
|
|
|
|
// (TODO: we probably want to do this for single-line text entries too?)
|
|
}
|
|
|
|
void PrefsDialog::checkBoxToggled(bool checked)
|
|
{
|
|
auto const pref_widget = PreferenceWidget{ sender() };
|
|
|
|
if (pref_widget.is<QCheckBox>())
|
|
{
|
|
setPref(pref_widget.getPrefKey(), checked);
|
|
}
|
|
}
|
|
|
|
void PrefsDialog::spinBoxEditingFinished()
|
|
{
|
|
auto const pref_widget = PreferenceWidget{ sender() };
|
|
|
|
if (pref_widget.is<QDoubleSpinBox>())
|
|
{
|
|
setPref(pref_widget.getPrefKey(), pref_widget.as<QDoubleSpinBox>()->value());
|
|
}
|
|
else if (pref_widget.is<QSpinBox>())
|
|
{
|
|
setPref(pref_widget.getPrefKey(), pref_widget.as<QSpinBox>()->value());
|
|
}
|
|
}
|
|
|
|
void PrefsDialog::timeEditingFinished()
|
|
{
|
|
auto const pref_widget = PreferenceWidget{ sender() };
|
|
|
|
if (pref_widget.is<QTimeEdit>())
|
|
{
|
|
setPref(pref_widget.getPrefKey(), QTime{ 0, 0 }.secsTo(pref_widget.as<QTimeEdit>()->time()) / 60);
|
|
}
|
|
}
|
|
|
|
void PrefsDialog::lineEditingFinished()
|
|
{
|
|
auto const pref_widget = PreferenceWidget{ sender() };
|
|
|
|
if (pref_widget.is<QLineEdit>())
|
|
{
|
|
auto const* const line_edit = pref_widget.as<QLineEdit>();
|
|
|
|
if (line_edit->isModified())
|
|
{
|
|
setPref(pref_widget.getPrefKey(), line_edit->text());
|
|
}
|
|
}
|
|
}
|
|
|
|
void PrefsDialog::pathChanged(QString const& path)
|
|
{
|
|
auto const pref_widget = PreferenceWidget{ sender() };
|
|
|
|
if (pref_widget.is<PathButton>())
|
|
{
|
|
setPref(pref_widget.getPrefKey(), path);
|
|
}
|
|
}
|
|
|
|
// ---
|
|
|
|
void PrefsDialog::initRemoteTab()
|
|
{
|
|
linkWidgetToPref(ui_.enableRpcCheck, Prefs::RPC_ENABLED);
|
|
linkWidgetToPref(ui_.rpcPortSpin, Prefs::RPC_PORT);
|
|
linkWidgetToPref(ui_.requireRpcAuthCheck, Prefs::RPC_AUTH_REQUIRED);
|
|
linkWidgetToPref(ui_.rpcUsernameEdit, Prefs::RPC_USERNAME);
|
|
linkWidgetToPref(ui_.rpcPasswordEdit, Prefs::RPC_PASSWORD);
|
|
linkWidgetToPref(ui_.enableRpcWhitelistCheck, Prefs::RPC_WHITELIST_ENABLED);
|
|
linkWidgetToPref(ui_.rpcWhitelistEdit, Prefs::RPC_WHITELIST);
|
|
|
|
web_widgets_ << ui_.rpcPortLabel << ui_.rpcPortSpin << ui_.requireRpcAuthCheck << ui_.enableRpcWhitelistCheck;
|
|
web_auth_widgets_ << ui_.rpcUsernameLabel << ui_.rpcUsernameEdit << ui_.rpcPasswordLabel << ui_.rpcPasswordEdit;
|
|
web_whitelist_widgets_ << ui_.rpcWhitelistLabel << ui_.rpcWhitelistEdit;
|
|
unsupported_when_remote_ << ui_.enableRpcCheck << web_widgets_ << web_auth_widgets_ << web_whitelist_widgets_;
|
|
|
|
connect(ui_.openWebClientButton, &QAbstractButton::clicked, &session_, &Session::launchWebInterface);
|
|
}
|
|
|
|
// ---
|
|
|
|
void PrefsDialog::altSpeedDaysEdited(int i)
|
|
{
|
|
int const value = qobject_cast<QComboBox*>(sender())->itemData(i).toInt();
|
|
setPref(Prefs::ALT_SPEED_LIMIT_TIME_DAY, value);
|
|
}
|
|
|
|
void PrefsDialog::initSpeedTab()
|
|
{
|
|
auto const suffix = QStringLiteral(" %1").arg(Speed::display_name(Speed::Units::KByps));
|
|
|
|
auto const locale = QLocale{};
|
|
|
|
ui_.uploadSpeedLimitSpin->setSuffix(suffix);
|
|
ui_.downloadSpeedLimitSpin->setSuffix(suffix);
|
|
ui_.altUploadSpeedLimitSpin->setSuffix(suffix);
|
|
ui_.altDownloadSpeedLimitSpin->setSuffix(suffix);
|
|
|
|
ui_.altSpeedLimitDaysCombo->addItem(tr("Every Day"), QVariant{ TR_SCHED_ALL });
|
|
ui_.altSpeedLimitDaysCombo->addItem(tr("Weekdays"), QVariant{ TR_SCHED_WEEKDAY });
|
|
ui_.altSpeedLimitDaysCombo->addItem(tr("Weekends"), QVariant{ TR_SCHED_WEEKEND });
|
|
ui_.altSpeedLimitDaysCombo->insertSeparator(ui_.altSpeedLimitDaysCombo->count());
|
|
|
|
for (int i = locale.firstDayOfWeek(); i <= Qt::Sunday; ++i)
|
|
{
|
|
ui_.altSpeedLimitDaysCombo->addItem(qtDayName(i), qtDayToTrDay(i));
|
|
}
|
|
|
|
for (int i = Qt::Monday; i < locale.firstDayOfWeek(); ++i)
|
|
{
|
|
ui_.altSpeedLimitDaysCombo->addItem(qtDayName(i), qtDayToTrDay(i));
|
|
}
|
|
|
|
ui_.altSpeedLimitDaysCombo->setCurrentIndex(
|
|
ui_.altSpeedLimitDaysCombo->findData(prefs_.getInt(Prefs::ALT_SPEED_LIMIT_TIME_DAY)));
|
|
|
|
linkWidgetToPref(ui_.uploadSpeedLimitCheck, Prefs::USPEED_ENABLED);
|
|
linkWidgetToPref(ui_.uploadSpeedLimitSpin, Prefs::USPEED);
|
|
linkWidgetToPref(ui_.downloadSpeedLimitCheck, Prefs::DSPEED_ENABLED);
|
|
linkWidgetToPref(ui_.downloadSpeedLimitSpin, Prefs::DSPEED);
|
|
linkWidgetToPref(ui_.altUploadSpeedLimitSpin, Prefs::ALT_SPEED_LIMIT_UP);
|
|
linkWidgetToPref(ui_.altDownloadSpeedLimitSpin, Prefs::ALT_SPEED_LIMIT_DOWN);
|
|
linkWidgetToPref(ui_.altSpeedLimitScheduleCheck, Prefs::ALT_SPEED_LIMIT_TIME_ENABLED);
|
|
linkWidgetToPref(ui_.altSpeedLimitStartTimeEdit, Prefs::ALT_SPEED_LIMIT_TIME_BEGIN);
|
|
linkWidgetToPref(ui_.altSpeedLimitEndTimeEdit, Prefs::ALT_SPEED_LIMIT_TIME_END);
|
|
|
|
sched_widgets_ << ui_.altSpeedLimitStartTimeEdit << ui_.altSpeedLimitToLabel << ui_.altSpeedLimitEndTimeEdit
|
|
<< ui_.altSpeedLimitDaysLabel << ui_.altSpeedLimitDaysCombo;
|
|
|
|
auto* cr = new ColumnResizer{ this };
|
|
cr->addLayout(ui_.speedLimitsSectionLayout);
|
|
cr->addLayout(ui_.altSpeedLimitsSectionLayout);
|
|
cr->update();
|
|
|
|
connect(ui_.altSpeedLimitDaysCombo, qOverload<int>(&QComboBox::activated), this, &PrefsDialog::altSpeedDaysEdited);
|
|
}
|
|
|
|
// ---
|
|
|
|
void PrefsDialog::initDesktopTab()
|
|
{
|
|
linkWidgetToPref(ui_.showTrayIconCheck, Prefs::SHOW_TRAY_ICON);
|
|
linkWidgetToPref(ui_.startMinimizedCheck, Prefs::START_MINIMIZED);
|
|
linkWidgetToPref(ui_.notifyOnTorrentAddedCheck, Prefs::SHOW_NOTIFICATION_ON_ADD);
|
|
linkWidgetToPref(ui_.notifyOnTorrentCompletedCheck, Prefs::SHOW_NOTIFICATION_ON_COMPLETE);
|
|
linkWidgetToPref(ui_.playSoundOnTorrentCompletedCheck, Prefs::COMPLETE_SOUND_ENABLED);
|
|
}
|
|
|
|
// ---
|
|
|
|
QString PrefsDialog::getPortStatusText(PrefsDialog::PortTestStatus status) noexcept
|
|
{
|
|
switch (status)
|
|
{
|
|
case PORT_TEST_UNKNOWN:
|
|
return tr("unknown");
|
|
case PORT_TEST_CHECKING:
|
|
return tr("checking…");
|
|
case PORT_TEST_OPEN:
|
|
return tr("open");
|
|
case PORT_TEST_CLOSED:
|
|
return tr("closed");
|
|
case PORT_TEST_ERROR:
|
|
return tr("error");
|
|
default:
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void PrefsDialog::updatePortStatusLabel()
|
|
{
|
|
auto const status_ipv4 = getPortStatusText(port_test_status_[Session::PORT_TEST_IPV4]);
|
|
auto const status_ipv6 = getPortStatusText(port_test_status_[Session::PORT_TEST_IPV6]);
|
|
|
|
ui_.peerPortStatusLabel->setText(
|
|
port_test_status_[Session::PORT_TEST_IPV4] == port_test_status_[Session::PORT_TEST_IPV6] ?
|
|
tr("Status: <b>%1</b>").arg(status_ipv4) :
|
|
tr("Status: <b>%1</b> (IPv4), <b>%2</b> (IPv6)").arg(status_ipv4).arg(status_ipv6));
|
|
}
|
|
|
|
void PrefsDialog::portTestSetEnabled()
|
|
{
|
|
// Depend on the RPC call status instead of the UI status, so that the widgets
|
|
// won't be enabled even if the port peer port changed while we have port_test
|
|
// RPC call(s) in-flight.
|
|
auto const sensitive = !session_.portTestPending(Session::PORT_TEST_IPV4) &&
|
|
!session_.portTestPending(Session::PORT_TEST_IPV6);
|
|
ui_.testPeerPortButton->setEnabled(sensitive);
|
|
widgets_[Prefs::PEER_PORT]->setEnabled(sensitive);
|
|
}
|
|
|
|
void PrefsDialog::onPortTested(std::optional<bool> result, Session::PortTestIpProtocol ip_protocol)
|
|
{
|
|
constexpr auto StatusFromResult = [](std::optional<bool> const res)
|
|
{
|
|
if (!res)
|
|
{
|
|
return PORT_TEST_ERROR;
|
|
}
|
|
if (!*res)
|
|
{
|
|
return PORT_TEST_CLOSED;
|
|
}
|
|
return PORT_TEST_OPEN;
|
|
};
|
|
|
|
// Only update the UI if the current status is "checking", so that
|
|
// we won't show the port test results for the old peer port if it
|
|
// changed while we have port_test RPC call(s) in-flight.
|
|
if (port_test_status_[ip_protocol] == PORT_TEST_CHECKING)
|
|
{
|
|
port_test_status_[ip_protocol] = StatusFromResult(result);
|
|
updatePortStatusLabel();
|
|
}
|
|
portTestSetEnabled();
|
|
}
|
|
|
|
void PrefsDialog::onPortTest()
|
|
{
|
|
port_test_status_[Session::PORT_TEST_IPV4] = PORT_TEST_CHECKING;
|
|
port_test_status_[Session::PORT_TEST_IPV6] = PORT_TEST_CHECKING;
|
|
updatePortStatusLabel();
|
|
|
|
session_.portTest(Session::PORT_TEST_IPV4);
|
|
session_.portTest(Session::PORT_TEST_IPV6);
|
|
|
|
portTestSetEnabled();
|
|
}
|
|
|
|
void PrefsDialog::initNetworkTab()
|
|
{
|
|
ui_.torrentPeerLimitSpin->setRange(1, INT_MAX);
|
|
ui_.globalPeerLimitSpin->setRange(1, INT_MAX);
|
|
|
|
linkWidgetToPref(ui_.peerPortSpin, Prefs::PEER_PORT);
|
|
linkWidgetToPref(ui_.randomPeerPortCheck, Prefs::PEER_PORT_RANDOM_ON_START);
|
|
linkWidgetToPref(ui_.enablePortForwardingCheck, Prefs::PORT_FORWARDING);
|
|
linkWidgetToPref(ui_.torrentPeerLimitSpin, Prefs::PEER_LIMIT_TORRENT);
|
|
linkWidgetToPref(ui_.globalPeerLimitSpin, Prefs::PEER_LIMIT_GLOBAL);
|
|
linkWidgetToPref(ui_.enableUtpCheck, Prefs::UTP_ENABLED);
|
|
linkWidgetToPref(ui_.enablePexCheck, Prefs::PEX_ENABLED);
|
|
linkWidgetToPref(ui_.enableDhtCheck, Prefs::DHT_ENABLED);
|
|
linkWidgetToPref(ui_.enableLpdCheck, Prefs::LPD_ENABLED);
|
|
linkWidgetToPref(ui_.defaultTrackersPlainTextEdit, Prefs::DEFAULT_TRACKERS);
|
|
|
|
auto* cr = new ColumnResizer{ this };
|
|
cr->addLayout(ui_.incomingPeersSectionLayout);
|
|
cr->addLayout(ui_.peerLimitsSectionLayout);
|
|
cr->update();
|
|
|
|
connect(ui_.testPeerPortButton, &QAbstractButton::clicked, this, &PrefsDialog::onPortTest);
|
|
connect(&session_, &Session::portTested, this, &PrefsDialog::onPortTested);
|
|
|
|
updatePortStatusLabel();
|
|
}
|
|
|
|
// ---
|
|
|
|
void PrefsDialog::onBlocklistDialogDestroyed(QObject* o)
|
|
{
|
|
Q_UNUSED(o)
|
|
|
|
blocklist_dialog_ = nullptr;
|
|
}
|
|
|
|
void PrefsDialog::onUpdateBlocklistCancelled()
|
|
{
|
|
disconnect(&session_, &Session::blocklistUpdated, this, &PrefsDialog::onBlocklistUpdated);
|
|
blocklist_dialog_->deleteLater();
|
|
}
|
|
|
|
void PrefsDialog::onBlocklistUpdated(int n)
|
|
{
|
|
blocklist_dialog_->setText(tr("<b>Update succeeded!</b><p>Blocklist now has %Ln rule(s).</p>", nullptr, n));
|
|
blocklist_dialog_->setTextFormat(Qt::RichText);
|
|
}
|
|
|
|
void PrefsDialog::onUpdateBlocklistClicked()
|
|
{
|
|
blocklist_dialog_ = new QMessageBox{ QMessageBox::Information,
|
|
QString{},
|
|
tr("<b>Update Blocklist</b><p>Getting new blocklist…</p>"),
|
|
QMessageBox::Close,
|
|
this };
|
|
connect(blocklist_dialog_, &QDialog::rejected, this, &PrefsDialog::onUpdateBlocklistCancelled);
|
|
connect(&session_, &Session::blocklistUpdated, this, &PrefsDialog::onBlocklistUpdated);
|
|
blocklist_dialog_->show();
|
|
session_.updateBlocklist();
|
|
}
|
|
|
|
void PrefsDialog::encryptionEdited(int i)
|
|
{
|
|
int const value(qobject_cast<QComboBox*>(sender())->itemData(i).toInt());
|
|
setPref(Prefs::ENCRYPTION, value);
|
|
}
|
|
|
|
void PrefsDialog::initPrivacyTab()
|
|
{
|
|
ui_.encryptionModeCombo->addItem(tr("Allow encryption"), 0);
|
|
ui_.encryptionModeCombo->addItem(tr("Prefer encryption"), 1);
|
|
ui_.encryptionModeCombo->addItem(tr("Require encryption"), 2);
|
|
|
|
linkWidgetToPref(ui_.encryptionModeCombo, Prefs::ENCRYPTION);
|
|
linkWidgetToPref(ui_.blocklistCheck, Prefs::BLOCKLIST_ENABLED);
|
|
linkWidgetToPref(ui_.blocklistEdit, Prefs::BLOCKLIST_URL);
|
|
linkWidgetToPref(ui_.autoUpdateBlocklistCheck, Prefs::BLOCKLIST_UPDATES_ENABLED);
|
|
|
|
block_widgets_ << ui_.blocklistEdit << ui_.blocklistStatusLabel << ui_.updateBlocklistButton
|
|
<< ui_.autoUpdateBlocklistCheck;
|
|
|
|
auto* cr = new ColumnResizer{ this };
|
|
cr->addLayout(ui_.encryptionSectionLayout);
|
|
cr->addLayout(ui_.blocklistSectionLayout);
|
|
cr->update();
|
|
|
|
connect(ui_.updateBlocklistButton, &QAbstractButton::clicked, this, &PrefsDialog::onUpdateBlocklistClicked);
|
|
connect(ui_.encryptionModeCombo, qOverload<int>(&QComboBox::activated), this, &PrefsDialog::encryptionEdited);
|
|
|
|
updateBlocklistLabel();
|
|
}
|
|
|
|
// ---
|
|
|
|
void PrefsDialog::onIdleLimitChanged()
|
|
{
|
|
//: Spin box format, "Stop seeding if idle for: [ 5 minutes ]"
|
|
auto const* const units_format = QT_TRANSLATE_N_NOOP("PrefsDialog", "%1 minute(s)");
|
|
auto const placeholder = QStringLiteral("%1");
|
|
Utils::updateSpinBoxFormat(ui_.idleLimitSpin, "PrefsDialog", units_format, placeholder);
|
|
}
|
|
|
|
void PrefsDialog::initSeedingTab()
|
|
{
|
|
ui_.doneSeedingScriptButton->setTitle(tr("Select \"Torrent Done Seeding\" Script"));
|
|
|
|
linkWidgetToPref(ui_.ratioLimitCheck, Prefs::RATIO_ENABLED);
|
|
linkWidgetToPref(ui_.ratioLimitSpin, Prefs::RATIO);
|
|
linkWidgetToPref(ui_.idleLimitCheck, Prefs::IDLE_LIMIT_ENABLED);
|
|
linkWidgetToPref(ui_.idleLimitSpin, Prefs::IDLE_LIMIT);
|
|
linkWidgetToPref(ui_.doneSeedingScriptCheck, Prefs::SCRIPT_TORRENT_DONE_SEEDING_ENABLED);
|
|
linkWidgetToPref(ui_.doneSeedingScriptButton, Prefs::SCRIPT_TORRENT_DONE_SEEDING_FILENAME);
|
|
linkWidgetToPref(ui_.doneSeedingScriptEdit, Prefs::SCRIPT_TORRENT_DONE_SEEDING_FILENAME);
|
|
|
|
connect(ui_.idleLimitSpin, qOverload<int>(&QSpinBox::valueChanged), this, &PrefsDialog::onIdleLimitChanged);
|
|
|
|
updateSeedingWidgetsLocality();
|
|
onIdleLimitChanged();
|
|
}
|
|
|
|
void PrefsDialog::onQueueStalledMinutesChanged()
|
|
{
|
|
//: Spin box format, "Download is inactive if data sharing stopped: [ 5 minutes ago ]"
|
|
auto const* const units_format = QT_TRANSLATE_N_NOOP("PrefsDialog", "%1 minute(s) ago");
|
|
auto const placeholder = QStringLiteral("%1");
|
|
Utils::updateSpinBoxFormat(ui_.queueStalledMinutesSpin, "PrefsDialog", units_format, placeholder);
|
|
}
|
|
|
|
void PrefsDialog::initDownloadingTab()
|
|
{
|
|
ui_.watchDirButton->setMode(PathButton::DirectoryMode);
|
|
ui_.downloadDirButton->setMode(PathButton::DirectoryMode);
|
|
ui_.incompleteDirButton->setMode(PathButton::DirectoryMode);
|
|
ui_.doneDownloadingScriptButton->setMode(PathButton::FileMode);
|
|
ui_.doneSeedingScriptButton->setMode(PathButton::FileMode);
|
|
|
|
ui_.watchDirButton->setTitle(tr("Select Watch Directory"));
|
|
ui_.downloadDirButton->setTitle(tr("Select Destination"));
|
|
ui_.incompleteDirButton->setTitle(tr("Select Incomplete Directory"));
|
|
ui_.doneDownloadingScriptButton->setTitle(tr("Select \"Torrent Done Downloading\" Script"));
|
|
|
|
ui_.watchDirStack->setMinimumWidth(200);
|
|
|
|
ui_.downloadDirFreeSpaceLabel->setSession(session_);
|
|
ui_.downloadDirFreeSpaceLabel->setPath(prefs_.getString(Prefs::DOWNLOAD_DIR));
|
|
|
|
linkWidgetToPref(ui_.watchDirCheck, Prefs::DIR_WATCH_ENABLED);
|
|
linkWidgetToPref(ui_.watchDirButton, Prefs::DIR_WATCH);
|
|
linkWidgetToPref(ui_.watchDirEdit, Prefs::DIR_WATCH);
|
|
linkWidgetToPref(ui_.showTorrentOptionsDialogCheck, Prefs::OPTIONS_PROMPT);
|
|
linkWidgetToPref(ui_.startAddedTorrentsCheck, Prefs::START);
|
|
linkWidgetToPref(ui_.detectTorrentFromClipboard, Prefs::READ_CLIPBOARD);
|
|
linkWidgetToPref(ui_.trashTorrentFileCheck, Prefs::TRASH_ORIGINAL);
|
|
linkWidgetToPref(ui_.downloadDirButton, Prefs::DOWNLOAD_DIR);
|
|
linkWidgetToPref(ui_.downloadDirEdit, Prefs::DOWNLOAD_DIR);
|
|
linkWidgetToPref(ui_.downloadDirFreeSpaceLabel, Prefs::DOWNLOAD_DIR);
|
|
linkWidgetToPref(ui_.downloadQueueSizeSpin, Prefs::DOWNLOAD_QUEUE_SIZE);
|
|
linkWidgetToPref(ui_.queueStalledMinutesSpin, Prefs::QUEUE_STALLED_MINUTES);
|
|
linkWidgetToPref(ui_.renamePartialFilesCheck, Prefs::RENAME_PARTIAL_FILES);
|
|
linkWidgetToPref(ui_.incompleteDirCheck, Prefs::INCOMPLETE_DIR_ENABLED);
|
|
linkWidgetToPref(ui_.incompleteDirButton, Prefs::INCOMPLETE_DIR);
|
|
linkWidgetToPref(ui_.incompleteDirEdit, Prefs::INCOMPLETE_DIR);
|
|
linkWidgetToPref(ui_.doneDownloadingScriptCheck, Prefs::SCRIPT_TORRENT_DONE_ENABLED);
|
|
linkWidgetToPref(ui_.doneDownloadingScriptButton, Prefs::SCRIPT_TORRENT_DONE_FILENAME);
|
|
linkWidgetToPref(ui_.doneDownloadingScriptEdit, Prefs::SCRIPT_TORRENT_DONE_FILENAME);
|
|
|
|
auto* cr = new ColumnResizer{ this };
|
|
cr->addLayout(ui_.addingSectionLayout);
|
|
cr->addLayout(ui_.downloadQueueSectionLayout);
|
|
cr->addLayout(ui_.incompleteSectionLayout);
|
|
cr->update();
|
|
|
|
connect(
|
|
ui_.queueStalledMinutesSpin,
|
|
qOverload<int>(&QSpinBox::valueChanged),
|
|
this,
|
|
&PrefsDialog::onQueueStalledMinutesChanged);
|
|
|
|
updateDownloadingWidgetsLocality();
|
|
onQueueStalledMinutesChanged();
|
|
}
|
|
|
|
void PrefsDialog::updateDownloadingWidgetsLocality()
|
|
{
|
|
ui_.watchDirStack->setCurrentWidget(is_local_ ? static_cast<QWidget*>(ui_.watchDirButton) : ui_.watchDirEdit);
|
|
ui_.downloadDirStack->setCurrentWidget(is_local_ ? static_cast<QWidget*>(ui_.downloadDirButton) : ui_.downloadDirEdit);
|
|
ui_.incompleteDirStack->setCurrentWidget(
|
|
is_local_ ? static_cast<QWidget*>(ui_.incompleteDirButton) : ui_.incompleteDirEdit);
|
|
ui_.doneDownloadingScriptStack->setCurrentWidget(
|
|
is_local_ ? static_cast<QWidget*>(ui_.doneDownloadingScriptButton) : ui_.doneDownloadingScriptEdit);
|
|
|
|
ui_.watchDirStack->setFixedHeight(ui_.watchDirStack->currentWidget()->sizeHint().height());
|
|
ui_.downloadDirStack->setFixedHeight(ui_.downloadDirStack->currentWidget()->sizeHint().height());
|
|
ui_.incompleteDirStack->setFixedHeight(ui_.incompleteDirStack->currentWidget()->sizeHint().height());
|
|
ui_.doneDownloadingScriptStack->setFixedHeight(ui_.doneDownloadingScriptStack->currentWidget()->sizeHint().height());
|
|
|
|
ui_.downloadDirLabel->setBuddy(ui_.downloadDirStack->currentWidget());
|
|
}
|
|
|
|
void PrefsDialog::updateSeedingWidgetsLocality()
|
|
{
|
|
ui_.doneSeedingScriptStack->setCurrentWidget(
|
|
is_local_ ? static_cast<QWidget*>(ui_.doneSeedingScriptButton) : ui_.doneSeedingScriptEdit);
|
|
ui_.doneSeedingScriptStack->setFixedHeight(ui_.doneSeedingScriptStack->currentWidget()->sizeHint().height());
|
|
}
|
|
|
|
// ---
|
|
|
|
PrefsDialog::PrefsDialog(Session& session, Prefs& prefs, QWidget* parent)
|
|
: BaseDialog{ parent }
|
|
, session_{ session }
|
|
, prefs_{ prefs }
|
|
, is_server_{ session.isServer() }
|
|
, is_local_{ session_.isLocal() }
|
|
{
|
|
ui_.setupUi(this);
|
|
|
|
initSpeedTab();
|
|
initDownloadingTab();
|
|
initSeedingTab();
|
|
initPrivacyTab();
|
|
initNetworkTab();
|
|
initDesktopTab();
|
|
initRemoteTab();
|
|
|
|
connect(&session_, &Session::sessionUpdated, this, &PrefsDialog::sessionUpdated);
|
|
|
|
static std::array<int, 10> constexpr InitKeys = {
|
|
Prefs::ALT_SPEED_LIMIT_ENABLED,
|
|
Prefs::ALT_SPEED_LIMIT_TIME_ENABLED,
|
|
Prefs::BLOCKLIST_ENABLED,
|
|
Prefs::DIR_WATCH,
|
|
Prefs::DOWNLOAD_DIR,
|
|
Prefs::ENCRYPTION,
|
|
Prefs::INCOMPLETE_DIR,
|
|
Prefs::INCOMPLETE_DIR_ENABLED,
|
|
Prefs::RPC_ENABLED,
|
|
Prefs::SCRIPT_TORRENT_DONE_FILENAME,
|
|
};
|
|
|
|
for (auto const key : InitKeys)
|
|
{
|
|
refreshPref(key);
|
|
}
|
|
|
|
// if it's a remote session, disable the preferences
|
|
// that don't work in remote sessions
|
|
if (!is_server_)
|
|
{
|
|
for (QWidget* const w : unsupported_when_remote_)
|
|
{
|
|
w->setToolTip(tr("Not supported by remote sessions"));
|
|
w->setEnabled(false);
|
|
}
|
|
}
|
|
|
|
adjustSize();
|
|
|
|
connect(qApp, &QApplication::focusChanged, this, &PrefsDialog::focusChanged);
|
|
}
|
|
|
|
void PrefsDialog::setPref(int key, QVariant const& v)
|
|
{
|
|
prefs_.set(key, v);
|
|
refreshPref(key);
|
|
}
|
|
|
|
// ---
|
|
|
|
void PrefsDialog::sessionUpdated()
|
|
{
|
|
if (bool const is_local = session_.isLocal(); is_local_ != is_local)
|
|
{
|
|
is_local_ = is_local;
|
|
updateDownloadingWidgetsLocality();
|
|
updateSeedingWidgetsLocality();
|
|
}
|
|
|
|
updateBlocklistLabel();
|
|
}
|
|
|
|
void PrefsDialog::updateBlocklistLabel()
|
|
{
|
|
int const n = session_.blocklistSize();
|
|
ui_.blocklistStatusLabel->setText(tr("<i>Blocklist contains %Ln rule(s)</i>", nullptr, n));
|
|
}
|
|
|
|
void PrefsDialog::refreshPref(int key)
|
|
{
|
|
switch (key)
|
|
{
|
|
case Prefs::RPC_ENABLED:
|
|
case Prefs::RPC_WHITELIST_ENABLED:
|
|
case Prefs::RPC_AUTH_REQUIRED:
|
|
{
|
|
bool const enabled(prefs_.getBool(Prefs::RPC_ENABLED));
|
|
bool const whitelist(prefs_.getBool(Prefs::RPC_WHITELIST_ENABLED));
|
|
bool const auth(prefs_.getBool(Prefs::RPC_AUTH_REQUIRED));
|
|
|
|
for (QWidget* const w : web_whitelist_widgets_)
|
|
{
|
|
w->setEnabled(enabled && whitelist);
|
|
}
|
|
|
|
for (QWidget* const w : web_auth_widgets_)
|
|
{
|
|
w->setEnabled(enabled && auth);
|
|
}
|
|
|
|
for (QWidget* const w : web_widgets_)
|
|
{
|
|
w->setEnabled(enabled);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Prefs::ALT_SPEED_LIMIT_TIME_ENABLED:
|
|
{
|
|
bool const enabled = prefs_.getBool(key);
|
|
|
|
for (QWidget* const w : sched_widgets_)
|
|
{
|
|
w->setEnabled(enabled);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Prefs::BLOCKLIST_ENABLED:
|
|
{
|
|
bool const enabled = prefs_.getBool(key);
|
|
|
|
for (QWidget* const w : block_widgets_)
|
|
{
|
|
w->setEnabled(enabled);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Prefs::PEER_PORT:
|
|
port_test_status_[Session::PORT_TEST_IPV4] = PORT_TEST_UNKNOWN;
|
|
port_test_status_[Session::PORT_TEST_IPV6] = PORT_TEST_UNKNOWN;
|
|
updatePortStatusLabel();
|
|
portTestSetEnabled();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (auto iter = widgets_.find(key); iter != std::end(widgets_))
|
|
{
|
|
QWidget* const w = iter->second;
|
|
|
|
w->blockSignals(true);
|
|
|
|
if (!updateWidgetValue(w, key) && (key == Prefs::ENCRYPTION))
|
|
{
|
|
auto* combo_box = qobject_cast<QComboBox*>(w);
|
|
int const index = combo_box->findData(prefs_.getInt(key));
|
|
combo_box->setCurrentIndex(index);
|
|
}
|
|
|
|
w->blockSignals(false);
|
|
}
|
|
}
|