Merge branch 'main' into docs/absorb-the-wiki
This commit is contained in:
commit
aa657bb0ca
|
@ -94,7 +94,7 @@ static struct event_base* ev_base = nullptr;
|
|||
**** Config File
|
||||
***/
|
||||
|
||||
static auto constexpr Options = std::array<tr_option, 43>{
|
||||
static auto constexpr Options = std::array<tr_option, 44>{
|
||||
{ { 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", true, "<list>" },
|
||||
{ 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
|
||||
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
|
||||
|
@ -103,6 +103,7 @@ static auto constexpr Options = std::array<tr_option, 43>{
|
|||
{ 941, "incomplete-dir", "Where to store new torrents until they're complete", nullptr, true, "<directory>" },
|
||||
{ 942, "no-incomplete-dir", "Don't store incomplete torrents in a different location", nullptr, false, nullptr },
|
||||
{ 'd', "dump-settings", "Dump the settings and exit", "d", false, nullptr },
|
||||
{ 943, "default-trackers", "Trackers for public torrents to use automatically", nullptr, true, "<list>" },
|
||||
{ 'e', "logfile", "Dump the log messages to this filename", "e", true, "<filename>" },
|
||||
{ 'f', "foreground", "Run in the foreground instead of daemonizing", "f", false, nullptr },
|
||||
{ 'g', "config-dir", "Where to look for configuration files", "g", true, "<path>" },
|
||||
|
@ -416,6 +417,10 @@ static bool parse_args(
|
|||
tr_variantDictAddBool(settings, TR_KEY_incomplete_dir_enabled, false);
|
||||
break;
|
||||
|
||||
case 943:
|
||||
tr_variantDictAddStr(settings, TR_KEY_default_trackers, optstr);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
*dump_settings = true;
|
||||
break;
|
||||
|
|
|
@ -531,6 +531,7 @@ Response arguments: `path`, `name`, and `id`, holding the torrent ID integer
|
|||
| `blocklist-url` | string | location of the blocklist to use for `blocklist-update`
|
||||
| `cache-size-mb` | number | maximum size of the disk cache (MB)
|
||||
| `config-dir` | string | location of transmission's configuration directory
|
||||
| `default-trackers` | list of default trackers to use on public torrents
|
||||
| `dht-enabled` | boolean | true means allow dht in public torrents
|
||||
| `download-dir` | string | default path to download torrents
|
||||
| `download-dir-free-space` | number | **DEPRECATED** Use the `free-space` method instead.
|
||||
|
@ -940,6 +941,7 @@ Transmission 4.0.0 (`rpc-version-semver` 5.3.0, `rpc-version`: 17)
|
|||
| `/upload` | :warning: undocumented `/upload` endpoint removed
|
||||
| `session-get` | **DEPRECATED** `download-dir-free-space`. Use `free-space` instead.
|
||||
| `free-space` | new return arg `total-capacity`
|
||||
| `session-get` | new arg `default-trackers`
|
||||
| `session-get` | new arg `rpc-version-semver`
|
||||
| `session-get` | new arg `script-torrent-added-enabled`
|
||||
| `session-get` | new arg `script-torrent-added-filename`
|
||||
|
|
|
@ -1022,6 +1022,10 @@ void Application::Impl::on_prefs_changed(tr_quark const key)
|
|||
tr_sessionSetEncryption(tr, static_cast<tr_encryption_mode>(gtr_pref_int_get(key)));
|
||||
break;
|
||||
|
||||
case TR_KEY_default_trackers:
|
||||
tr_sessionSetDefaultTrackers(tr, gtr_pref_string_get(key).c_str());
|
||||
break;
|
||||
|
||||
case TR_KEY_download_dir:
|
||||
tr_sessionSetDownloadDir(tr, gtr_pref_string_get(key).c_str());
|
||||
break;
|
||||
|
|
|
@ -2241,28 +2241,9 @@ namespace
|
|||
|
||||
std::string get_editable_tracker_list(tr_torrent const* tor)
|
||||
{
|
||||
std::ostringstream gstr;
|
||||
int tier = 0;
|
||||
|
||||
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
|
||||
{
|
||||
auto const tracker = tr_torrentTracker(tor, i);
|
||||
|
||||
if (tier != tracker.tier)
|
||||
{
|
||||
tier = tracker.tier;
|
||||
gstr << '\n';
|
||||
}
|
||||
|
||||
gstr << tracker.announce << '\n';
|
||||
}
|
||||
|
||||
auto str = gstr.str();
|
||||
if (!str.empty())
|
||||
{
|
||||
str.resize(str.size() - 1);
|
||||
}
|
||||
|
||||
char* cstr = tr_torrentGetTrackerList(tor);
|
||||
auto str = std::string{ cstr != nullptr ? cstr : "" };
|
||||
tr_free(cstr);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -2291,8 +2272,8 @@ void DetailsDialog::Impl::on_edit_trackers()
|
|||
|
||||
auto* l = Gtk::make_managed<Gtk::Label>();
|
||||
l->set_markup(
|
||||
_("To add a backup URL, add it on the line after the primary URL.\n"
|
||||
"To add another primary URL, add it after a blank line."));
|
||||
_("To add a backup URL, add it on the next line after a primary URL.\n"
|
||||
"To add a new primary URL, add it after a blank line."));
|
||||
l->set_justify(Gtk::JUSTIFY_LEFT);
|
||||
l->set_halign(Gtk::ALIGN_START);
|
||||
l->set_valign(Gtk::ALIGN_CENTER);
|
||||
|
@ -2309,6 +2290,13 @@ void DetailsDialog::Impl::on_edit_trackers()
|
|||
fr->set_size_request(500U, 166U);
|
||||
t->add_wide_tall_control(row, *fr);
|
||||
|
||||
l = Gtk::make_managed<Gtk::Label>();
|
||||
l->set_markup(_("Also see Default Public Trackers in Edit > Preferences > Network"));
|
||||
l->set_justify(Gtk::JUSTIFY_LEFT);
|
||||
l->set_halign(Gtk::ALIGN_START);
|
||||
l->set_valign(Gtk::ALIGN_CENTER);
|
||||
t->add_wide_control(row, *l);
|
||||
|
||||
gtr_dialog_set_content(*d, *t);
|
||||
|
||||
d->set_data(TORRENT_ID_KEY, GINT_TO_POINTER(torrent_id));
|
||||
|
|
|
@ -176,6 +176,32 @@ Gtk::Entry* new_entry(tr_quark const key, Glib::RefPtr<Session> const& core)
|
|||
return w;
|
||||
}
|
||||
|
||||
Gtk::Widget* new_text_view(tr_quark const key, Glib::RefPtr<Session> const& core)
|
||||
{
|
||||
auto* w = Gtk::make_managed<Gtk::TextView>();
|
||||
auto buffer = w->get_buffer();
|
||||
|
||||
buffer->set_text(gtr_pref_string_get(key));
|
||||
|
||||
/* set up the scrolled window and put the text view in it */
|
||||
auto* scroll = Gtk::make_managed<Gtk::ScrolledWindow>();
|
||||
scroll->set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC);
|
||||
scroll->set_shadow_type(Gtk::ShadowType::SHADOW_IN);
|
||||
scroll->add(*w);
|
||||
scroll->set_size_request(-1, 166);
|
||||
|
||||
/* signal */
|
||||
w->add_events(Gdk::FOCUS_CHANGE_MASK);
|
||||
w->signal_focus_out_event().connect(
|
||||
[buffer, key, core](GdkEventFocus*)
|
||||
{
|
||||
core->set_pref(key, buffer->get_text());
|
||||
return false;
|
||||
});
|
||||
|
||||
return scroll;
|
||||
}
|
||||
|
||||
void chosen_cb(Gtk::FileChooser* w, tr_quark const key, Glib::RefPtr<Session> const& core)
|
||||
{
|
||||
core->set_pref(key, w->get_filename());
|
||||
|
@ -1051,6 +1077,16 @@ Gtk::Widget* PrefsDialog::Impl::networkPage()
|
|||
w->set_tooltip_text(_("LPD is a tool for finding peers on your local network."));
|
||||
t->add_wide_control(row, *w);
|
||||
|
||||
t->add_section_divider(row);
|
||||
t->add_section_title(row, _("Default Public Trackers"));
|
||||
|
||||
auto tv = new_text_view(TR_KEY_default_trackers, core_);
|
||||
tv->set_tooltip_text(
|
||||
_("Trackers to use on all public torrents.\n\n"
|
||||
"To add a backup URL, add it on the next line after a primary URL.\n"
|
||||
"To add a new primary URL, add it after a blank line."));
|
||||
t->add_wide_control(row, *tv);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,29 @@ bool tr_announce_list::add(std::string_view announce_url_sv, tr_tracker_tier_t t
|
|||
return true;
|
||||
}
|
||||
|
||||
void tr_announce_list::add(tr_announce_list const& src)
|
||||
{
|
||||
if (std::empty(src))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto src_tier = src.at(0).tier;
|
||||
auto& tgt = *this;
|
||||
auto tgt_tier = tgt.nextTier();
|
||||
|
||||
for (auto const& tracker : src)
|
||||
{
|
||||
if (src_tier != tracker.tier)
|
||||
{
|
||||
src_tier = tracker.tier;
|
||||
++tgt_tier;
|
||||
}
|
||||
|
||||
tgt.add(tracker.announce.full, tgt_tier);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> tr_announce_list::announceToScrape(std::string_view announce)
|
||||
{
|
||||
// To derive the scrape URL use the following steps:
|
||||
|
|
|
@ -92,12 +92,23 @@ public:
|
|||
|
||||
[[nodiscard]] tr_tracker_tier_t nextTier() const;
|
||||
|
||||
[[nodiscard]] bool operator==(tr_announce_list const& that) const
|
||||
{
|
||||
return trackers_ == that.trackers_;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(tr_announce_list const& that) const
|
||||
{
|
||||
return trackers_ != that.trackers_;
|
||||
}
|
||||
|
||||
bool add(std::string_view announce_url_sv)
|
||||
{
|
||||
return add(announce_url_sv, this->nextTier());
|
||||
}
|
||||
|
||||
bool add(std::string_view announce_url_sv, tr_tracker_tier_t tier);
|
||||
void add(tr_announce_list const& that);
|
||||
bool remove(std::string_view announce_url);
|
||||
bool remove(tr_tracker_id_t id);
|
||||
bool replace(tr_tracker_id_t id, std::string_view announce_url_sv);
|
||||
|
|
|
@ -505,7 +505,7 @@ struct tr_torrent_announcer
|
|||
{
|
||||
// build the trackers
|
||||
auto tier_to_infos = std::map<tr_tracker_tier_t, std::vector<tr_announce_list::tracker_info const*>>{};
|
||||
auto const& announce_list = tor->announceList();
|
||||
auto const announce_list = getAnnounceList(tor);
|
||||
for (auto const& info : announce_list)
|
||||
{
|
||||
tier_to_infos[info.tier].emplace_back(&info);
|
||||
|
@ -575,6 +575,20 @@ struct tr_torrent_announcer
|
|||
|
||||
tr_tracker_callback callback = nullptr;
|
||||
void* callback_data = nullptr;
|
||||
|
||||
private:
|
||||
[[nodiscard]] static tr_announce_list getAnnounceList(tr_torrent const* tor)
|
||||
{
|
||||
auto announce_list = tor->announceList();
|
||||
|
||||
// if it's a public torrent, add the default trackers
|
||||
if (tor->isPublic())
|
||||
{
|
||||
announce_list.add(tor->session->defaultTrackers());
|
||||
}
|
||||
|
||||
return announce_list;
|
||||
}
|
||||
};
|
||||
|
||||
static tr_tier* getTier(tr_announcer* announcer, tr_sha1_digest_t const& info_hash, int tier_id)
|
||||
|
|
|
@ -18,7 +18,7 @@ using namespace std::literals;
|
|||
namespace
|
||||
{
|
||||
|
||||
auto constexpr my_static = std::array<std::string_view, 388>{ ""sv,
|
||||
auto constexpr my_static = std::array<std::string_view, 389>{ ""sv,
|
||||
"activeTorrentCount"sv,
|
||||
"activity-date"sv,
|
||||
"activityDate"sv,
|
||||
|
@ -74,6 +74,7 @@ auto constexpr my_static = std::array<std::string_view, 388>{ ""sv,
|
|||
"current-stats"sv,
|
||||
"date"sv,
|
||||
"dateCreated"sv,
|
||||
"default-trackers"sv,
|
||||
"delete-local-data"sv,
|
||||
"desiredAvailable"sv,
|
||||
"destination"sv,
|
||||
|
|
|
@ -77,6 +77,7 @@ enum
|
|||
TR_KEY_current_stats,
|
||||
TR_KEY_date,
|
||||
TR_KEY_dateCreated,
|
||||
TR_KEY_default_trackers,
|
||||
TR_KEY_delete_local_data,
|
||||
TR_KEY_desiredAvailable,
|
||||
TR_KEY_destination,
|
||||
|
|
|
@ -1789,6 +1789,11 @@ static char const* sessionSet(
|
|||
tr_sessionSetQueueStalledEnabled(session, boolVal);
|
||||
}
|
||||
|
||||
if (tr_variantDictFindStrView(args_in, TR_KEY_default_trackers, &sv))
|
||||
{
|
||||
session->setDefaultTrackers(sv);
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(args_in, TR_KEY_download_queue_size, &i))
|
||||
{
|
||||
tr_sessionSetQueueSize(session, TR_DOWN, (int)i);
|
||||
|
@ -2071,6 +2076,10 @@ static void addSessionField(tr_session* s, tr_variant* d, tr_quark key)
|
|||
tr_variantDictAddStr(d, key, tr_sessionGetConfigDir(s));
|
||||
break;
|
||||
|
||||
case TR_KEY_default_trackers:
|
||||
tr_variantDictAddStr(d, key, s->defaultTrackersStr());
|
||||
break;
|
||||
|
||||
case TR_KEY_download_dir:
|
||||
tr_variantDictAddStr(d, key, s->downloadDir());
|
||||
break;
|
||||
|
|
|
@ -333,6 +333,7 @@ void tr_sessionGetDefaultSettings(tr_variant* d)
|
|||
tr_variantDictAddBool(d, TR_KEY_utp_enabled, true);
|
||||
tr_variantDictAddBool(d, TR_KEY_lpd_enabled, false);
|
||||
tr_variantDictAddStr(d, TR_KEY_download_dir, tr_getDefaultDownloadDir());
|
||||
tr_variantDictAddStr(d, TR_KEY_default_trackers, "");
|
||||
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, 100);
|
||||
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, false);
|
||||
tr_variantDictAddInt(d, TR_KEY_encryption, TR_DEFAULT_ENCRYPTION);
|
||||
|
@ -411,6 +412,7 @@ void tr_sessionGetSettings(tr_session const* s, tr_variant* d)
|
|||
tr_variantDictAddBool(d, TR_KEY_utp_enabled, s->isUTPEnabled);
|
||||
tr_variantDictAddBool(d, TR_KEY_lpd_enabled, s->isLPDEnabled);
|
||||
tr_variantDictAddStr(d, TR_KEY_download_dir, tr_sessionGetDownloadDir(s));
|
||||
tr_variantDictAddStr(d, TR_KEY_default_trackers, s->defaultTrackersStr());
|
||||
tr_variantDictAddInt(d, TR_KEY_download_queue_size, tr_sessionGetQueueSize(s, TR_DOWN));
|
||||
tr_variantDictAddBool(d, TR_KEY_download_queue_enabled, tr_sessionGetQueueEnabled(s, TR_DOWN));
|
||||
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, tr_sessionGetSpeedLimit_KBps(s, TR_DOWN));
|
||||
|
@ -811,6 +813,11 @@ static void sessionSetImpl(void* vdata)
|
|||
tr_sessionSetCacheLimit_MB(session, i);
|
||||
}
|
||||
|
||||
if (tr_variantDictFindStrView(settings, TR_KEY_default_trackers, &sv))
|
||||
{
|
||||
session->setDefaultTrackers(sv);
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(settings, TR_KEY_peer_limit_per_torrent, &i))
|
||||
{
|
||||
tr_sessionSetPeerLimitPerTorrent(session, i);
|
||||
|
@ -2244,6 +2251,37 @@ int tr_sessionGetCacheLimit_MB(tr_session const* session)
|
|||
****
|
||||
***/
|
||||
|
||||
void tr_session::setDefaultTrackers(std::string_view trackers)
|
||||
{
|
||||
auto const oldval = default_trackers_;
|
||||
|
||||
default_trackers_str_ = trackers;
|
||||
default_trackers_.parse(trackers);
|
||||
|
||||
// if the list changed, update all the public torrents
|
||||
if (default_trackers_ != oldval)
|
||||
{
|
||||
for (auto* tor : torrents)
|
||||
{
|
||||
if (tor->isPublic())
|
||||
{
|
||||
tr_announcerResetTorrent(announcer, tor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tr_sessionSetDefaultTrackers(tr_session* session, char const* trackers)
|
||||
{
|
||||
TR_ASSERT(tr_isSession(session));
|
||||
|
||||
session->setDefaultTrackers(trackers != nullptr ? trackers : "");
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
struct port_forwarding_data
|
||||
{
|
||||
bool enabled;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "announce-list.h"
|
||||
#include "net.h" // tr_socket_t
|
||||
#include "quark.h"
|
||||
#include "web.h"
|
||||
|
@ -150,6 +151,21 @@ public:
|
|||
download_dir_ = dir;
|
||||
}
|
||||
|
||||
// default trackers
|
||||
// (trackers to apply automatically to public torrents)
|
||||
|
||||
auto const& defaultTrackersStr() const
|
||||
{
|
||||
return default_trackers_str_;
|
||||
}
|
||||
|
||||
auto const& defaultTrackers() const
|
||||
{
|
||||
return default_trackers_;
|
||||
}
|
||||
|
||||
void setDefaultTrackers(std::string_view trackers);
|
||||
|
||||
// incomplete dir
|
||||
|
||||
std::string const& incompleteDir() const
|
||||
|
@ -387,6 +403,8 @@ public:
|
|||
|
||||
std::unique_ptr<tr_rpc_server> rpc_server_;
|
||||
|
||||
tr_announce_list default_trackers_;
|
||||
|
||||
// One of <netinet/ip.h>'s IPTOS_ values.
|
||||
// See tr_netTos*() in libtransmission/net.h for more info
|
||||
// Only session.cc should use this.
|
||||
|
@ -398,6 +416,7 @@ private:
|
|||
std::array<std::string, TR_SCRIPT_N_TYPES> scripts_;
|
||||
std::string blocklist_url_;
|
||||
std::string download_dir_;
|
||||
std::string default_trackers_str_;
|
||||
std::string incomplete_dir_;
|
||||
std::string peer_congestion_algorithm_;
|
||||
|
||||
|
|
|
@ -392,6 +392,8 @@ bool tr_sessionIsRPCPasswordEnabled(tr_session const* session);
|
|||
|
||||
char const* tr_sessionGetRPCBindAddress(tr_session const* session);
|
||||
|
||||
void tr_sessionSetDefaultTrackers(tr_session* session, char const* trackers);
|
||||
|
||||
enum tr_rpc_callback_type
|
||||
{
|
||||
TR_RPC_TORRENT_ADDED,
|
||||
|
@ -1187,7 +1189,13 @@ char* tr_torrentGetMagnetLink(tr_torrent const* tor);
|
|||
|
||||
/**
|
||||
* Returns a newly-allocated string listing its tracker's announce URLs.
|
||||
* One URL per line, with a blank line between tiers
|
||||
* One URL per line, with a blank line between tiers.
|
||||
*
|
||||
* NOTE: this only includes the trackers included in the torrent and,
|
||||
* along with tr_torrentSetTrackerList(), is intended for import/export
|
||||
* and user editing. It does *not* include the "default trackers" that
|
||||
* are applied to all public torrents. If you want a full display of all
|
||||
* trackers, use tr_torrentTracker() and tr_torrentTrackerCount()
|
||||
*/
|
||||
char* tr_torrentGetTrackerList(tr_torrent const* tor);
|
||||
|
||||
|
@ -1413,6 +1421,13 @@ struct tr_tracker_view
|
|||
|
||||
struct tr_tracker_view tr_torrentTracker(tr_torrent const* torrent, size_t i);
|
||||
|
||||
/**
|
||||
* Count all the trackers (both active and backup) this torrent is using.
|
||||
*
|
||||
* NOTE: this is for a status display only and may include trackers from
|
||||
* the default tracker list if this is a public torrent. If you want a
|
||||
* list of trackers the user can edit, see tr_torrentGetTrackerList().
|
||||
*/
|
||||
size_t tr_torrentTrackerCount(tr_torrent const* torrent);
|
||||
|
||||
/*
|
||||
|
|
|
@ -109,6 +109,7 @@ std::array<Prefs::PrefItem, Prefs::PREFS_COUNT> const Prefs::Items{
|
|||
{ ALT_SPEED_LIMIT_TIME_DAY, TR_KEY_alt_speed_time_day, QVariant::Int },
|
||||
{ BLOCKLIST_ENABLED, TR_KEY_blocklist_enabled, QVariant::Bool },
|
||||
{ BLOCKLIST_URL, TR_KEY_blocklist_url, QVariant::String },
|
||||
{ DEFAULT_TRACKERS, TR_KEY_default_trackers, QVariant::String },
|
||||
{ DSPEED, TR_KEY_speed_limit_down, QVariant::Int },
|
||||
{ DSPEED_ENABLED, TR_KEY_speed_limit_down_enabled, QVariant::Bool },
|
||||
{ DOWNLOAD_DIR, TR_KEY_download_dir, QVariant::String },
|
||||
|
|
|
@ -83,6 +83,7 @@ public:
|
|||
ALT_SPEED_LIMIT_TIME_DAY,
|
||||
BLOCKLIST_ENABLED,
|
||||
BLOCKLIST_URL,
|
||||
DEFAULT_TRACKERS,
|
||||
DSPEED,
|
||||
DSPEED_ENABLED,
|
||||
DOWNLOAD_DIR,
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <QStyle>
|
||||
|
@ -190,6 +191,10 @@ bool PrefsDialog::updateWidgetValue(QWidget* widget, int pref_key) const
|
|||
{
|
||||
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;
|
||||
|
@ -233,9 +238,51 @@ void PrefsDialog::linkWidgetToPref(QWidget* widget, int pref_key)
|
|||
if (auto const* spin_box = qobject_cast<QAbstractSpinBox*>(widget); spin_box != nullptr)
|
||||
{
|
||||
connect(spin_box, &QAbstractSpinBox::editingFinished, this, &PrefsDialog::spinBoxEditingFinished);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
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 const constexpr* 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)
|
||||
{
|
||||
PreferenceWidget const pref_widget(sender());
|
||||
|
@ -423,6 +470,7 @@ void PrefsDialog::initNetworkTab()
|
|||
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);
|
||||
|
@ -676,6 +724,8 @@ PrefsDialog::PrefsDialog(Session& session, Prefs& prefs, QWidget* parent)
|
|||
}
|
||||
|
||||
adjustSize();
|
||||
|
||||
connect(qApp, &QApplication::focusChanged, this, &PrefsDialog::focusChanged);
|
||||
}
|
||||
|
||||
void PrefsDialog::setPref(int key, QVariant const& v)
|
||||
|
@ -775,11 +825,15 @@ void PrefsDialog::refreshPref(int key)
|
|||
{
|
||||
QWidget* w(it.value());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ public:
|
|||
PrefsDialog(Session&, Prefs&, QWidget* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void focusChanged(QWidget* old, QWidget* now);
|
||||
void checkBoxToggled(bool checked);
|
||||
void spinBoxEditingFinished();
|
||||
void timeEditingFinished();
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>601</width>
|
||||
<height>597</height>
|
||||
<height>657</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -17,7 +17,7 @@
|
|||
<item>
|
||||
<widget class="QTabWidget" name="tabs">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="elideMode">
|
||||
<enum>Qt::ElideNone</enum>
|
||||
|
@ -985,6 +985,58 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="defaultTrackersSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>1</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="defaultTrackersLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Default Public Trackers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="defaultTrackersLayout">
|
||||
<property name="leftMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="defaultTrackersPlainTextEdit">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Trackers to use on all public torrents.</p><p><br/></p><p>To add a backup URL, add it on the next line after a primary URL.</p><p>To add a new primary URL, add it after a blank line.</p><p><br/></p></body></html></string>
|
||||
</property>
|
||||
<property name="tabChangesFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="lineWrapMode">
|
||||
<enum>QPlainTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="networkTabBottomSpacer">
|
||||
<property name="orientation">
|
||||
|
|
|
@ -155,6 +155,7 @@ void Session::updatePref(int key)
|
|||
case Prefs::BLOCKLIST_DATE:
|
||||
case Prefs::BLOCKLIST_ENABLED:
|
||||
case Prefs::BLOCKLIST_URL:
|
||||
case Prefs::DEFAULT_TRACKERS:
|
||||
case Prefs::DHT_ENABLED:
|
||||
case Prefs::DOWNLOAD_QUEUE_ENABLED:
|
||||
case Prefs::DOWNLOAD_QUEUE_SIZE:
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<width>515</width>
|
||||
<height>450</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -33,19 +33,30 @@
|
|||
<number>18</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="primaryLabel">
|
||||
<property name="text">
|
||||
<string>To add another primary URL, add it after a blank line.</string>
|
||||
<string>To add a new primary URL, add it after a blank line.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QPlainTextEdit" name="trackerList"/>
|
||||
<widget class="QPlainTextEdit" name="trackerList">
|
||||
<property name="tabChangesFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="destinationLabel">
|
||||
<widget class="QLabel" name="backupLabel">
|
||||
<property name="text">
|
||||
<string>To add a backup URL, add it on the line after the primary URL.</string>
|
||||
<string><html><head/><body><p>To add a backup URL, add it on the next line after a primary URL.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="defaultsLabel">
|
||||
<property name="text">
|
||||
<string>Also see Default Public Trackers in Edit > Preferences > Network</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -611,3 +611,83 @@ TEST_F(AnnounceListTest, parseDuplicateUrl)
|
|||
|
||||
EXPECT_FALSE(announce_list.parse(Text));
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, addAnnounceListWithSingleTracker)
|
||||
{
|
||||
auto constexpr Trackers =
|
||||
"https://www.foo.com/announce\n"
|
||||
"\n"
|
||||
"https://www.bar.com/announce\n"sv;
|
||||
auto announce_list = tr_announce_list{};
|
||||
announce_list.parse(Trackers);
|
||||
|
||||
auto constexpr AddStr = "https://www.baz.com/announce"sv;
|
||||
auto tmp = tr_announce_list{};
|
||||
tmp.parse(AddStr);
|
||||
|
||||
announce_list.add(tmp);
|
||||
|
||||
auto constexpr Expected =
|
||||
"https://www.foo.com/announce\n"
|
||||
"\n"
|
||||
"https://www.bar.com/announce\n"
|
||||
"\n"
|
||||
"https://www.baz.com/announce\n"sv;
|
||||
EXPECT_EQ(Expected, announce_list.toString());
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, addAnnounceWithSingleTier)
|
||||
{
|
||||
auto constexpr Trackers =
|
||||
"https://www.foo.com/announce\n"
|
||||
"\n"
|
||||
"https://www.bar.com/announce\n"sv;
|
||||
auto announce_list = tr_announce_list{};
|
||||
announce_list.parse(Trackers);
|
||||
|
||||
auto constexpr AddStr =
|
||||
"https://www.baz.com/announce\n"
|
||||
"https://www.qux.com/announce\n"sv;
|
||||
auto tmp = tr_announce_list{};
|
||||
tmp.parse(AddStr);
|
||||
|
||||
announce_list.add(tmp);
|
||||
|
||||
auto constexpr Expected =
|
||||
"https://www.foo.com/announce\n"
|
||||
"\n"
|
||||
"https://www.bar.com/announce\n"
|
||||
"\n"
|
||||
"https://www.baz.com/announce\n"
|
||||
"https://www.qux.com/announce\n"sv;
|
||||
EXPECT_EQ(Expected, announce_list.toString());
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, addAnnounceListWithMultiTier)
|
||||
{
|
||||
auto constexpr Trackers =
|
||||
"https://www.foo.com/announce\n"
|
||||
"\n"
|
||||
"https://www.bar.com/announce\n"sv;
|
||||
auto announce_list = tr_announce_list{};
|
||||
announce_list.parse(Trackers);
|
||||
|
||||
auto constexpr AddStr =
|
||||
"https://www.baz.com/announce\n"
|
||||
"\n"
|
||||
"https://www.qux.com/announce\n"sv;
|
||||
auto tmp = tr_announce_list{};
|
||||
tmp.parse(AddStr);
|
||||
|
||||
announce_list.add(tmp);
|
||||
|
||||
auto constexpr Expected =
|
||||
"https://www.foo.com/announce\n"
|
||||
"\n"
|
||||
"https://www.bar.com/announce\n"
|
||||
"\n"
|
||||
"https://www.baz.com/announce\n"
|
||||
"\n"
|
||||
"https://www.qux.com/announce\n"sv;
|
||||
EXPECT_EQ(Expected, announce_list.toString());
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ TEST_F(RpcTest, sessionGet)
|
|||
EXPECT_TRUE(tr_variantDictFindDict(&response, TR_KEY_arguments, &args));
|
||||
|
||||
// what we expected
|
||||
auto const expected_keys = std::array<tr_quark, 57>{
|
||||
auto const expected_keys = std::array<tr_quark, 58>{
|
||||
TR_KEY_alt_speed_down,
|
||||
TR_KEY_alt_speed_enabled,
|
||||
TR_KEY_alt_speed_time_begin,
|
||||
|
@ -109,6 +109,7 @@ TEST_F(RpcTest, sessionGet)
|
|||
TR_KEY_blocklist_url,
|
||||
TR_KEY_cache_size_mb,
|
||||
TR_KEY_config_dir,
|
||||
TR_KEY_default_trackers,
|
||||
TR_KEY_dht_enabled,
|
||||
TR_KEY_download_dir,
|
||||
TR_KEY_download_dir_free_space,
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -63,6 +63,10 @@ export class PrefsDialog extends EventTarget {
|
|||
}
|
||||
|
||||
static _getValue(e) {
|
||||
if (e.tagName === 'TEXTAREA') {
|
||||
return e.value;
|
||||
}
|
||||
|
||||
switch (e.type) {
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
|
@ -128,6 +132,7 @@ export class PrefsDialog extends EventTarget {
|
|||
}
|
||||
break;
|
||||
case 'text':
|
||||
case 'textarea':
|
||||
case 'url':
|
||||
case 'email':
|
||||
case 'number':
|
||||
|
@ -150,6 +155,7 @@ export class PrefsDialog extends EventTarget {
|
|||
}
|
||||
break;
|
||||
default:
|
||||
console.log(element.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -646,7 +652,32 @@ export class PrefsDialog extends EventTarget {
|
|||
root.append(cal.root);
|
||||
const utp_check = cal.check;
|
||||
|
||||
label = document.createElement('div');
|
||||
label.textContent = 'Default Public Trackers';
|
||||
label.classList.add('section-label');
|
||||
root.append(label);
|
||||
|
||||
const tracker_labels = [
|
||||
'Trackers to use on all public torrents.',
|
||||
'To add a backup URL, add it on the next line after a primary URL.',
|
||||
'To add a new primary URL, add it after a blank line.',
|
||||
];
|
||||
for (const text of tracker_labels) {
|
||||
label = document.createElement('label');
|
||||
label.classList.add('default-trackers-label');
|
||||
label.textContent = text;
|
||||
label.setAttribute('for', 'default-trackers');
|
||||
root.append(label);
|
||||
}
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.dataset.key = 'default-trackers';
|
||||
textarea.id = 'default-trackers';
|
||||
root.append(textarea);
|
||||
const default_trackers_textarea = textarea;
|
||||
|
||||
return {
|
||||
default_trackers_textarea,
|
||||
port_forwarding_check,
|
||||
port_input,
|
||||
port_status_label,
|
||||
|
@ -714,6 +745,8 @@ export class PrefsDialog extends EventTarget {
|
|||
console.trace(`unhandled input: ${element.type}`);
|
||||
break;
|
||||
}
|
||||
} else if (element.tagName === 'TEXTAREA') {
|
||||
element.addEventListener('change', on_change);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -762,6 +762,10 @@ $popup-top: 61px; // TODO: ugly that this is hardcoded
|
|||
grid-column: span 2;
|
||||
}
|
||||
|
||||
#default-trackers {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.blocklist-size-label,
|
||||
.blocklist-update-button,
|
||||
.port-status {
|
||||
|
@ -787,6 +791,11 @@ $popup-top: 61px; // TODO: ugly that this is hardcoded
|
|||
}
|
||||
}
|
||||
|
||||
#default-trackers,
|
||||
.default-trackers-label {
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
|
||||
.alt-speed-label {
|
||||
font-size: smaller;
|
||||
font-style: lighter;
|
||||
|
|
Loading…
Reference in New Issue