refactor: add libtransmission-app (#7978)

* refactor: add libtransmission-app

* refactor: add libtransmission-app/display-modes.h

* refactor: use app::SortMode, app:ShowMode in Qt client

* feat: add to_variant(), to_value() in serializer

* refactor: use app::SortMode in GTK client

* refactor: use app::ShowMode in GTK client

* refactor: make naming consistent with libtransmission-app
This commit is contained in:
Charles Kerr
2025-12-22 20:05:57 -06:00
committed by GitHub
parent 888f90ca1e
commit d5314e2c44
43 changed files with 674 additions and 413 deletions

View File

@@ -827,6 +827,7 @@ function(tr_install_web DST_DIR)
endfunction()
add_subdirectory(libtransmission)
add_subdirectory(libtransmission-app)
set(MAC_PROJECT_DIR macosx)

View File

@@ -3,11 +3,13 @@ BasedOnStyle: InheritParentConfig
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<libtransmission/transmission.h>'
- Regex: '^<libtransmission-app/'
Priority: 3
- Regex: '^<libtransmission/transmission.h>'
Priority: 4
SortPriority: 3
- Regex: '^<libtransmission/'
Priority: 3
Priority: 4
SortPriority: 4
- Regex: '^<(cairo|gdk|gio|glib|gtk|pango)mm([-./]|config)'
Priority: 5

View File

@@ -197,6 +197,7 @@ target_include_directories(${TR_NAME}-gtk SYSTEM
target_link_libraries(${TR_NAME}-gtk
PRIVATE
${TR_NAME}-app
${TR_NAME}
transmission::gtk_impl
fmt::fmt-header-only

View File

@@ -3,7 +3,7 @@
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <libtransmission/favicon-cache.h>
#include <libtransmission-app/favicon-cache.h>
#include <gdkmm/pixbuf.h>
#include <giomm/memoryinputstream.h>

View File

@@ -13,6 +13,8 @@
#include "TorrentFilter.h"
#include "Utils.h"
#include <libtransmission-app/display-modes.h>
#include <libtransmission/tr-macros.h>
#include <gdkmm/pixbuf.h>
@@ -45,12 +47,13 @@
#include <string>
#include <unordered_map>
using namespace transmission::app;
namespace
{
using ActivityType = TorrentFilter::Activity;
using TrackerType = TorrentFilter::Tracker;
constexpr auto ActivitySeparator = static_cast<ActivityType>(-1);
constexpr auto ShowModeSeparator = static_cast<ShowMode>(-1);
constexpr auto TrackerSeparator = static_cast<TrackerType>(-1);
} // namespace
@@ -72,18 +75,20 @@ private:
template<typename T>
T* get_template_child(char const* name) const;
void activity_combo_box_init(Gtk::ComboBox& combo);
static void render_activity_pixbuf_func(Gtk::CellRendererPixbuf& cell_renderer, Gtk::TreeModel::const_iterator const& iter);
void show_mode_combo_box_init(Gtk::ComboBox& combo);
static void render_show_mode_pixbuf_func(
Gtk::CellRendererPixbuf& cell_renderer,
Gtk::TreeModel::const_iterator const& iter);
void tracker_combo_box_init(Gtk::ComboBox& combo);
static void render_pixbuf_func(Gtk::CellRendererPixbuf& cell_renderer, Gtk::TreeModel::const_iterator const& iter);
static void render_number_func(Gtk::CellRendererText& cell_renderer, Gtk::TreeModel::const_iterator const& iter);
void update_filter_activity();
void update_filter_show_mode();
void update_filter_tracker();
void update_filter_text();
bool activity_filter_model_update();
bool show_mode_filter_model_update();
bool tracker_filter_model_update();
void favicon_ready_cb(Glib::RefPtr<Gdk::Pixbuf> const* pixbuf, Gtk::TreeModel::Path const& path);
@@ -94,9 +99,9 @@ private:
void update_count_label_idle();
bool update_count_label();
static Glib::RefPtr<Gtk::ListStore> activity_filter_model_new();
static Glib::RefPtr<Gtk::ListStore> show_mode_filter_model_new();
static void status_model_update_count(Gtk::TreeModel::iterator const& iter, int n);
static bool activity_is_it_a_separator(Gtk::TreeModel::const_iterator const& iter);
static bool show_mode_is_it_a_separator(Gtk::TreeModel::const_iterator const& iter);
static Glib::RefPtr<Gtk::TreeStore> tracker_filter_model_new();
static void tracker_model_update_count(Gtk::TreeModel::iterator const& iter, int n);
@@ -110,10 +115,10 @@ private:
FilterBar& widget_;
Glib::RefPtr<Session> const core_;
Glib::RefPtr<Gtk::ListStore> const activity_model_;
Glib::RefPtr<Gtk::ListStore> const show_mode_model_;
Glib::RefPtr<Gtk::TreeStore> const tracker_model_;
Gtk::ComboBox* activity_ = nullptr;
Gtk::ComboBox* show_mode_ = nullptr;
Gtk::ComboBox* tracker_ = nullptr;
Gtk::Entry* entry_ = nullptr;
Gtk::Label* show_lb_ = nullptr;
@@ -390,54 +395,50 @@ void FilterBar::Impl::tracker_combo_box_init(Gtk::ComboBox& combo)
namespace
{
/***
****
**** ACTIVITY
****
***/
// --- Show Mode
class ActivityFilterModelColumns : public Gtk::TreeModelColumnRecord
class ShowModeFilterModelColumns : public Gtk::TreeModelColumnRecord
{
public:
ActivityFilterModelColumns() noexcept
ShowModeFilterModelColumns() noexcept
{
add(name);
add(count);
add(type);
add(show_mode);
add(icon_name);
}
Gtk::TreeModelColumn<Glib::ustring> name;
Gtk::TreeModelColumn<int> count;
Gtk::TreeModelColumn<ActivityType> type;
Gtk::TreeModelColumn<ShowMode> show_mode;
Gtk::TreeModelColumn<Glib::ustring> icon_name;
};
ActivityFilterModelColumns const activity_filter_cols;
ShowModeFilterModelColumns const show_mode_filter_cols;
} // namespace
bool FilterBar::Impl::activity_is_it_a_separator(Gtk::TreeModel::const_iterator const& iter)
bool FilterBar::Impl::show_mode_is_it_a_separator(Gtk::TreeModel::const_iterator const& iter)
{
return iter->get_value(activity_filter_cols.type) == ActivitySeparator;
return iter->get_value(show_mode_filter_cols.show_mode) == ShowModeSeparator;
}
void FilterBar::Impl::status_model_update_count(Gtk::TreeModel::iterator const& iter, int n)
{
if (n != iter->get_value(activity_filter_cols.count))
if (n != iter->get_value(show_mode_filter_cols.count))
{
iter->set_value(activity_filter_cols.count, n);
iter->set_value(show_mode_filter_cols.count, n);
}
}
bool FilterBar::Impl::activity_filter_model_update()
bool FilterBar::Impl::show_mode_filter_model_update()
{
auto const torrents_model = core_->get_model();
for (auto& row : activity_model_->children())
for (auto& row : show_mode_model_->children())
{
auto const type = row.get_value(activity_filter_cols.type);
if (type == ActivitySeparator)
auto const type = row.get_value(show_mode_filter_cols.show_mode);
if (type == ShowModeSeparator)
{
continue;
}
@@ -447,7 +448,7 @@ bool FilterBar::Impl::activity_filter_model_update()
for (auto i = 0U, count = torrents_model->get_n_items(); i < count; ++i)
{
auto const torrent = gtr_ptr_dynamic_cast<Torrent>(torrents_model->get_object(i));
if (torrent != nullptr && TorrentFilter::match_activity(*torrent, static_cast<ActivityType>(type)))
if (torrent != nullptr && TorrentFilter::match_mode(*torrent, static_cast<ShowMode>(type)))
{
++hits;
}
@@ -459,29 +460,29 @@ bool FilterBar::Impl::activity_filter_model_update()
return false;
}
Glib::RefPtr<Gtk::ListStore> FilterBar::Impl::activity_filter_model_new()
Glib::RefPtr<Gtk::ListStore> FilterBar::Impl::show_mode_filter_model_new()
{
struct FilterTypeInfo
{
ActivityType type;
ShowMode show_mode;
char const* context;
char const* name;
char const* icon_name;
};
static auto constexpr types = std::array<FilterTypeInfo, 9>({ {
{ ActivityType::ALL, nullptr, N_("All"), nullptr },
{ ActivityType{ -1 }, nullptr, nullptr, nullptr },
{ ActivityType::ACTIVE, nullptr, N_("Active"), "system-run" },
{ ActivityType::DOWNLOADING, "Verb", NC_("Verb", "Downloading"), "network-receive" },
{ ActivityType::SEEDING, "Verb", NC_("Verb", "Seeding"), "network-transmit" },
{ ActivityType::PAUSED, nullptr, N_("Paused"), "media-playback-pause" },
{ ActivityType::FINISHED, nullptr, N_("Finished"), "media-playback-stop" },
{ ActivityType::VERIFYING, "Verb", NC_("Verb", "Verifying"), "view-refresh" },
{ ActivityType::ERROR, nullptr, N_("Error"), "dialog-error" },
{ ShowMode::ShowAll, nullptr, N_("All"), nullptr },
{ ShowMode{ -1 }, nullptr, nullptr, nullptr },
{ ShowMode::ShowActive, nullptr, N_("Active"), "system-run" },
{ ShowMode::ShowDownloading, "Verb", NC_("Verb", "Downloading"), "network-receive" },
{ ShowMode::ShowSeeding, "Verb", NC_("Verb", "Seeding"), "network-transmit" },
{ ShowMode::ShowPaused, nullptr, N_("Paused"), "media-playback-pause" },
{ ShowMode::ShowFinished, nullptr, N_("Finished"), "media-playback-stop" },
{ ShowMode::ShowVerifying, "Verb", NC_("Verb", "Verifying"), "view-refresh" },
{ ShowMode::ShowError, nullptr, N_("Error"), "dialog-error" },
} });
auto store = Gtk::ListStore::create(activity_filter_cols);
auto store = Gtk::ListStore::create(show_mode_filter_cols);
for (auto const& type : types)
{
@@ -489,40 +490,40 @@ Glib::RefPtr<Gtk::ListStore> FilterBar::Impl::activity_filter_model_new()
Glib::ustring(type.context != nullptr ? g_dpgettext2(nullptr, type.context, type.name) : _(type.name)) :
Glib::ustring();
auto const iter = store->append();
iter->set_value(activity_filter_cols.name, name);
iter->set_value(activity_filter_cols.type, type.type);
iter->set_value(activity_filter_cols.icon_name, Glib::ustring(type.icon_name != nullptr ? type.icon_name : ""));
iter->set_value(show_mode_filter_cols.name, name);
iter->set_value(show_mode_filter_cols.show_mode, type.show_mode);
iter->set_value(show_mode_filter_cols.icon_name, Glib::ustring(type.icon_name != nullptr ? type.icon_name : ""));
}
return store;
}
void FilterBar::Impl::render_activity_pixbuf_func(
void FilterBar::Impl::render_show_mode_pixbuf_func(
Gtk::CellRendererPixbuf& cell_renderer,
Gtk::TreeModel::const_iterator const& iter)
{
auto const type = ActivityType{ iter->get_value(activity_filter_cols.type) };
cell_renderer.property_width() = type == ActivityType::ALL ? 0 : 20;
cell_renderer.property_ypad() = type == ActivityType::ALL ? 0 : 2;
auto const type = ShowMode{ iter->get_value(show_mode_filter_cols.show_mode) };
cell_renderer.property_width() = type == ShowMode::ShowAll ? 0 : 20;
cell_renderer.property_ypad() = type == ShowMode::ShowAll ? 0 : 2;
}
void FilterBar::Impl::activity_combo_box_init(Gtk::ComboBox& combo)
void FilterBar::Impl::show_mode_combo_box_init(Gtk::ComboBox& combo)
{
combo.set_model(activity_model_);
combo.set_row_separator_func(sigc::hide<0>(&Impl::activity_is_it_a_separator));
combo.set_model(show_mode_model_);
combo.set_row_separator_func(sigc::hide<0>(&Impl::show_mode_is_it_a_separator));
combo.set_active(0);
{
auto* r = Gtk::make_managed<Gtk::CellRendererPixbuf>();
combo.pack_start(*r, false);
combo.add_attribute(r->property_icon_name(), activity_filter_cols.icon_name);
combo.set_cell_data_func(*r, [r](auto const& iter) { render_activity_pixbuf_func(*r, iter); });
combo.add_attribute(r->property_icon_name(), show_mode_filter_cols.icon_name);
combo.set_cell_data_func(*r, [r](auto const& iter) { render_show_mode_pixbuf_func(*r, iter); });
}
{
auto* r = Gtk::make_managed<Gtk::CellRendererText>();
combo.pack_start(*r, true);
combo.add_attribute(r->property_text(), activity_filter_cols.name);
combo.add_attribute(r->property_text(), show_mode_filter_cols.name);
}
{
@@ -537,16 +538,16 @@ void FilterBar::Impl::update_filter_text()
filter_->set_text(entry_->get_text());
}
void FilterBar::Impl::update_filter_activity()
void FilterBar::Impl::update_filter_show_mode()
{
/* set active_activity_type_ from the activity combobox */
if (auto const iter = activity_->get_active(); iter)
/* set active_show_mode_type_ from the show_mode combobox */
if (auto const iter = show_mode_->get_active(); iter)
{
filter_->set_activity(ActivityType{ iter->get_value(activity_filter_cols.type) });
filter_->set_mode(ShowMode{ iter->get_value(show_mode_filter_cols.show_mode) });
}
else
{
filter_->set_activity(ActivityType::ALL);
filter_->set_mode(ShowMode::ShowAll);
}
}
@@ -577,15 +578,15 @@ bool FilterBar::Impl::update_count_label()
trackerCount = iter->get_value(tracker_filter_cols.count);
}
/* get the activity count */
int activityCount = 0;
if (auto const iter = activity_->get_active(); iter)
/* get the mode count */
int modeCount = 0;
if (auto const iter = show_mode_->get_active(); iter)
{
activityCount = iter->get_value(activity_filter_cols.count);
modeCount = iter->get_value(show_mode_filter_cols.count);
}
/* set the text */
if (auto const new_markup = visibleCount == std::min(activityCount, trackerCount) ?
if (auto const new_markup = visibleCount == std::min(modeCount, trackerCount) ?
_("_Show:") :
fmt::format(fmt::runtime(_("_Show {count:L} of:")), fmt::arg("count", visibleCount));
new_markup != show_lb_->get_label().raw())
@@ -606,14 +607,14 @@ void FilterBar::Impl::update_count_label_idle()
void FilterBar::Impl::update_filter_models(Torrent::ChangeFlags changes)
{
static auto TR_CONSTEXPR23 activity_flags = Torrent::ChangeFlag::ACTIVE_PEERS_DOWN | Torrent::ChangeFlag::ACTIVE_PEERS_UP |
static auto TR_CONSTEXPR23 show_mode_flags = Torrent::ChangeFlag::ACTIVE_PEERS_DOWN | Torrent::ChangeFlag::ACTIVE_PEERS_UP |
Torrent::ChangeFlag::ACTIVE | Torrent::ChangeFlag::ACTIVITY | Torrent::ChangeFlag::ERROR_CODE |
Torrent::ChangeFlag::FINISHED;
static auto constexpr tracker_flags = Torrent::ChangeFlag::TRACKERS;
if (changes.test(activity_flags))
if (changes.test(show_mode_flags))
{
activity_filter_model_update();
show_mode_filter_model_update();
}
if (changes.test(tracker_flags))
@@ -623,7 +624,7 @@ void FilterBar::Impl::update_filter_models(Torrent::ChangeFlags changes)
filter_->update(changes);
if (changes.test(activity_flags | tracker_flags))
if (changes.test(show_mode_flags | tracker_flags))
{
update_count_label_idle();
}
@@ -657,7 +658,7 @@ void FilterBarExtraInit::class_init(void* klass, void* /*user_data*/)
gtk_widget_class_set_template_from_resource(widget_klass, gtr_get_full_resource_path("FilterBar.ui").c_str());
gtk_widget_class_bind_template_child_full(widget_klass, "activity_combo", FALSE, 0);
gtk_widget_class_bind_template_child_full(widget_klass, "show_mode_combo", FALSE, 0);
gtk_widget_class_bind_template_child_full(widget_klass, "tracker_combo", FALSE, 0);
gtk_widget_class_bind_template_child_full(widget_klass, "text_entry", FALSE, 0);
gtk_widget_class_bind_template_child_full(widget_klass, "show_label", FALSE, 0);
@@ -692,9 +693,9 @@ FilterBar::~FilterBar() = default;
FilterBar::Impl::Impl(FilterBar& widget, Glib::RefPtr<Session> const& core)
: widget_(widget)
, core_(core)
, activity_model_(activity_filter_model_new())
, show_mode_model_(show_mode_filter_model_new())
, tracker_model_(tracker_filter_model_new())
, activity_(get_template_child<Gtk::ComboBox>("activity_combo"))
, show_mode_(get_template_child<Gtk::ComboBox>("show_mode_combo"))
, tracker_(get_template_child<Gtk::ComboBox>("tracker_combo"))
, entry_(get_template_child<Gtk::Entry>("text_entry"))
, show_lb_(get_template_child<Gtk::Label>("show_label"))
@@ -704,10 +705,10 @@ FilterBar::Impl::Impl(FilterBar& widget, Glib::RefPtr<Session> const& core)
update_filter_models_on_change_tag_ = core_->signal_torrents_changed().connect(
sigc::hide<0>(sigc::mem_fun(*this, &Impl::update_filter_models_idle)));
activity_filter_model_update();
show_mode_filter_model_update();
tracker_filter_model_update();
activity_combo_box_init(*activity_);
show_mode_combo_box_init(*show_mode_);
tracker_combo_box_init(*tracker_);
filter_->signal_changed().connect([this](auto /*changes*/) { update_count_label_idle(); });
@@ -715,7 +716,7 @@ FilterBar::Impl::Impl(FilterBar& widget, Glib::RefPtr<Session> const& core)
filter_model_ = FilterListModel<Torrent>::create(core_->get_sorted_model(), filter_);
tracker_->signal_changed().connect(sigc::mem_fun(*this, &Impl::update_filter_tracker));
activity_->signal_changed().connect(sigc::mem_fun(*this, &Impl::update_filter_activity));
show_mode_->signal_changed().connect(sigc::mem_fun(*this, &Impl::update_filter_show_mode));
#if GTKMM_CHECK_VERSION(4, 0, 0)
entry_->signal_icon_release().connect([this](auto /*icon_position*/) { entry_->set_text({}); });

View File

@@ -7,7 +7,10 @@
#include "GtkCompat.h"
#include "PrefsDialog.h"
#include <libtransmission-app/display-modes.h>
#include <libtransmission/transmission.h>
#include <libtransmission/serializer.h>
#include <libtransmission/utils.h>
#include <libtransmission/variant.h>
@@ -17,6 +20,8 @@
#include <string_view>
using namespace std::literals;
using namespace transmission::app;
using libtransmission::serializer::to_variant;
std::string gl_confdir;
@@ -71,7 +76,7 @@ namespace
map.try_emplace(TR_KEY_show_statusbar, true);
map.try_emplace(TR_KEY_show_toolbar, true);
map.try_emplace(TR_KEY_show_tracker_scrapes, false);
map.try_emplace(TR_KEY_sort_mode, "sort-by-name"sv);
map.try_emplace(TR_KEY_sort_mode, to_variant(DefaultSortMode));
map.try_emplace(TR_KEY_sort_reversed, false);
map.try_emplace(TR_KEY_statusbar_stats, "total-ratio"sv);
map.try_emplace(TR_KEY_torrent_added_notification_enabled, true);
@@ -121,6 +126,11 @@ tr_variant& gtr_pref_get_all()
return getPrefs();
}
tr_variant::Map& gtr_pref_get_map()
{
return *getPrefs().get_if<tr_variant::Map>();
}
int64_t gtr_pref_int_get(tr_quark const key)
{
int64_t i = 0;

View File

@@ -6,12 +6,25 @@
#include <libtransmission/transmission.h> /* tr_variant, tr_session */
#include <libtransmission/quark.h>
#include <libtransmission/serializer.h>
#include <libtransmission/variant.h>
#include <cstdint> // int64_t
#include <string>
#include <string_view>
#include <vector>
[[nodiscard]] tr_variant::Map& gtr_pref_get_map();
template<typename T>
[[nodiscard]] std::optional<T> gtr_pref_get(tr_quark const key)
{
using namespace libtransmission::serializer;
auto const& map = gtr_pref_get_map();
auto const iter = map.find(key);
return iter != std::end(map) ? to_value<T>(iter->second) : std::nullopt;
}
void gtr_pref_init(std::string_view config_dir);
int64_t gtr_pref_int_get(tr_quark key);

View File

@@ -478,7 +478,10 @@ void Session::Impl::on_pref_changed(tr_quark const key)
switch (key)
{
case TR_KEY_sort_mode:
sorter_->set_mode(gtr_pref_string_get(TR_KEY_sort_mode));
if (auto const sort_mode = gtr_pref_get<SortMode>(TR_KEY_sort_mode))
{
sorter_->set_mode(*sort_mode);
}
break;
case TR_KEY_sort_reversed:

View File

@@ -7,8 +7,9 @@
#include "GtkCompat.h"
#include "Torrent.h"
#include <libtransmission-app/favicon-cache.h>
#include <libtransmission/transmission.h>
#include <libtransmission/favicon-cache.h>
#include <libtransmission/variant.h>
#include <gdkmm/pixbuf.h>

View File

@@ -19,24 +19,25 @@ TorrentFilter::TorrentFilter()
{
}
void TorrentFilter::set_activity(Activity type)
void TorrentFilter::set_mode(ShowMode const mode)
{
if (activity_type_ == type)
if (show_mode_ == mode)
{
return;
}
auto change = Change::DIFFERENT;
if (activity_type_ == Activity::ALL)
if (show_mode_ == ShowMode::ShowAll)
{
change = Change::MORE_STRICT;
}
else if (type == Activity::ALL)
else if (mode == ShowMode::ShowAll)
{
change = Change::LESS_STRICT;
}
activity_type_ = type;
show_mode_ = mode;
changed(change);
}
@@ -98,9 +99,9 @@ void TorrentFilter::set_text(Glib::ustring const& text)
changed(change);
}
bool TorrentFilter::match_activity(Torrent const& torrent) const
bool TorrentFilter::match_mode(Torrent const& torrent) const
{
return match_activity(torrent, activity_type_);
return match_mode(torrent, show_mode_);
}
bool TorrentFilter::match_tracker(Torrent const& torrent) const
@@ -115,12 +116,12 @@ bool TorrentFilter::match_text(Torrent const& torrent) const
bool TorrentFilter::match(Torrent const& torrent) const
{
return match_activity(torrent) && match_tracker(torrent) && match_text(torrent);
return match_mode(torrent) && match_tracker(torrent) && match_text(torrent);
}
bool TorrentFilter::matches_all() const
{
return activity_type_ == Activity::ALL && tracker_type_ == Tracker::ALL && text_.empty();
return show_mode_ == ShowMode::ShowAll && tracker_type_ == Tracker::ALL && text_.empty();
}
void TorrentFilter::update(Torrent::ChangeFlags changes)
@@ -129,23 +130,23 @@ void TorrentFilter::update(Torrent::ChangeFlags changes)
bool refilter_needed = false;
if (activity_type_ != Activity::ALL)
if (show_mode_ != ShowMode::ShowAll)
{
static auto TR_CONSTEXPR23 ActivityFlags = std::array<std::pair<Activity, Torrent::ChangeFlags>, 7U>{ {
{ Activity::DOWNLOADING, Flag::ACTIVITY },
{ Activity::SEEDING, Flag::ACTIVITY },
{ Activity::ACTIVE, Flag::ACTIVE_PEER_COUNT | Flag::ACTIVITY },
{ Activity::PAUSED, Flag::ACTIVITY },
{ Activity::FINISHED, Flag::FINISHED },
{ Activity::VERIFYING, Flag::ACTIVITY },
{ Activity::ERROR, Flag::ERROR_CODE },
static auto TR_CONSTEXPR23 ShowModeFlags = std::array<std::pair<ShowMode, Torrent::ChangeFlags>, 7U>{ {
{ ShowMode::ShowActive, Flag::ACTIVE_PEER_COUNT | Flag::ACTIVITY },
{ ShowMode::ShowDownloading, Flag::ACTIVITY },
{ ShowMode::ShowError, Flag::ERROR_CODE },
{ ShowMode::ShowFinished, Flag::FINISHED },
{ ShowMode::ShowPaused, Flag::ACTIVITY },
{ ShowMode::ShowSeeding, Flag::ACTIVITY },
{ ShowMode::ShowVerifying, Flag::ACTIVITY },
} };
auto const iter = std::find_if(
std::begin(ActivityFlags),
std::end(ActivityFlags),
[key = activity_type_](auto const& row) { return row.first == key; });
refilter_needed = iter != std::end(ActivityFlags) && changes.test(iter->second);
std::begin(ShowModeFlags),
std::end(ShowModeFlags),
[key = show_mode_](auto const& row) { return row.first == key; });
refilter_needed = iter != std::end(ShowModeFlags) && changes.test(iter->second);
}
if (!refilter_needed)
@@ -170,43 +171,42 @@ Glib::RefPtr<TorrentFilter> TorrentFilter::create()
return Glib::make_refptr_for_instance(new TorrentFilter());
}
bool TorrentFilter::match_activity(Torrent const& torrent, Activity type)
bool TorrentFilter::match_mode(Torrent const& torrent, ShowMode const type)
{
auto activity = tr_torrent_activity();
switch (type)
{
case Activity::ALL:
case ShowMode::ShowAll:
return true;
case Activity::DOWNLOADING:
case ShowMode::ShowDownloading:
activity = torrent.get_activity();
return activity == TR_STATUS_DOWNLOAD || activity == TR_STATUS_DOWNLOAD_WAIT;
case Activity::SEEDING:
case ShowMode::ShowSeeding:
activity = torrent.get_activity();
return activity == TR_STATUS_SEED || activity == TR_STATUS_SEED_WAIT;
case Activity::ACTIVE:
case ShowMode::ShowActive:
return torrent.get_active_peer_count() > 0 || torrent.get_activity() == TR_STATUS_CHECK;
case Activity::PAUSED:
case ShowMode::ShowPaused:
return torrent.get_activity() == TR_STATUS_STOPPED;
case Activity::FINISHED:
case ShowMode::ShowFinished:
return torrent.get_finished();
case Activity::VERIFYING:
case ShowMode::ShowVerifying:
activity = torrent.get_activity();
return activity == TR_STATUS_CHECK || activity == TR_STATUS_CHECK_WAIT;
case Activity::ERROR:
case ShowMode::ShowError:
return torrent.get_error_code() != 0;
default:
g_assert_not_reached();
return true;
}
g_assert_not_reached();
return true;
}
bool TorrentFilter::match_tracker(Torrent const& torrent, Tracker type, Glib::ustring const& host)

View File

@@ -8,6 +8,8 @@
#include "FilterBase.h"
#include "Torrent.h"
#include <libtransmission-app/display-modes.h>
#include <glibmm/refptr.h>
#include <glibmm/ustring.h>
@@ -16,17 +18,7 @@
class TorrentFilter : public FilterBase<Torrent>
{
public:
enum class Activity : int8_t
{
ALL,
DOWNLOADING,
SEEDING,
ACTIVE,
PAUSED,
FINISHED,
VERIFYING,
ERROR,
};
using ShowMode = transmission::app::ShowMode;
enum class Tracker : int8_t
{
@@ -35,13 +27,13 @@ public:
};
public:
void set_activity(Activity type);
void set_mode(ShowMode mode);
void set_tracker(Tracker type, Glib::ustring const& host);
void set_text(Glib::ustring const& text);
bool match_activity(Torrent const& torrent) const;
bool match_tracker(Torrent const& torrent) const;
bool match_mode(Torrent const& torrent) const;
bool match_text(Torrent const& torrent) const;
bool match_tracker(Torrent const& torrent) const;
// FilterBase<Torrent>
bool match(Torrent const& torrent) const override;
@@ -51,7 +43,7 @@ public:
static Glib::RefPtr<TorrentFilter> create();
static bool match_activity(Torrent const& torrent, Activity type);
static bool match_mode(Torrent const& torrent, ShowMode mode);
static bool match_tracker(Torrent const& torrent, Tracker type, Glib::ustring const& host);
static bool match_text(Torrent const& torrent, Glib::ustring const& text);
@@ -59,7 +51,7 @@ private:
TorrentFilter();
private:
Activity activity_type_ = Activity::ALL;
ShowMode show_mode_ = transmission::app::DefaultShowMode;
Tracker tracker_type_ = Tracker::ALL;
Glib::ustring tracker_host_;
Glib::ustring text_;

View File

@@ -9,18 +9,24 @@
#include "SorterBase.hh"
#include "Utils.h"
#include <libtransmission-app/display-modes.h>
#include <libtransmission/transmission.h>
#include <libtransmission/tr-macros.h>
#include <libtransmission/utils.h>
#include <small/map.hpp>
#include <algorithm>
#include <array>
#include <utility>
using namespace std::string_view_literals;
using namespace transmission::app;
namespace
{
using CompareFunc = int (*)(Torrent const&, Torrent const&);
constexpr bool is_valid_eta(time_t value)
{
@@ -150,6 +156,12 @@ int compare_by_progress(Torrent const& lhs, Torrent const& rhs)
return compare_by_ratio(lhs, rhs);
}
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
int compare_by_id(Torrent const& lhs, Torrent const& rhs)
{
return -tr_compare_3way(lhs.get_id(), rhs.get_id());
}
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
int compare_by_eta(Torrent const& lhs, Torrent const& rhs)
{
@@ -171,7 +183,6 @@ int compare_by_state(Torrent const& lhs, Torrent const& rhs)
return compare_by_queue(lhs, rhs);
}
} // namespace
TorrentSorter::TorrentSorter()
@@ -179,25 +190,23 @@ TorrentSorter::TorrentSorter()
{
}
void TorrentSorter::set_mode(std::string_view mode)
void TorrentSorter::set_mode(SortMode const mode)
{
static auto constexpr DefaultCompareFunc = &compare_by_name;
static auto constexpr CompareFuncs = std::array<std::pair<std::string_view, CompareFunc>, 9U>{ {
{ "sort-by-activity"sv, &compare_by_activity },
{ "sort-by-age"sv, &compare_by_age },
{ "sort-by-name"sv, &compare_by_name },
{ "sort-by-progress"sv, &compare_by_progress },
{ "sort-by-queue"sv, &compare_by_queue },
{ "sort-by-ratio"sv, &compare_by_ratio },
{ "sort-by-size"sv, &compare_by_size },
{ "sort-by-state"sv, &compare_by_state },
{ "sort-by-time-left"sv, &compare_by_eta },
static auto const CompareFuncs = small::max_size_map<SortMode, CompareFunc, SortModeCount>{ {
{ SortMode::SortByActivity, &compare_by_activity },
{ SortMode::SortByAge, &compare_by_age },
{ SortMode::SortByEta, &compare_by_eta },
{ SortMode::SortById, &compare_by_id },
{ SortMode::SortByName, &compare_by_name },
{ SortMode::SortByProgress, &compare_by_progress },
{ SortMode::SortByQueue, &compare_by_queue },
{ SortMode::SortByRatio, &compare_by_ratio },
{ SortMode::SortBySize, &compare_by_size },
{ SortMode::SortByState, &compare_by_state },
} };
auto const iter = std::find_if(
std::begin(CompareFuncs),
std::end(CompareFuncs),
[key = mode](auto const& row) { return row.first == key; });
auto const iter = CompareFuncs.find(mode);
auto const compare_func = iter != std::end(CompareFuncs) ? iter->second : DefaultCompareFunc;
if (compare_func_ == compare_func)
{

View File

@@ -8,14 +8,18 @@
#include "SorterBase.h"
#include "Torrent.h"
#include <libtransmission-app/display-modes.h>
#include <glibmm/refptr.h>
using SortMode = transmission::app::SortMode;
class TorrentSorter : public SorterBase<Torrent>
{
using CompareFunc = int (*)(Torrent const&, Torrent const&);
public:
void set_mode(std::string_view mode);
void set_mode(SortMode mode);
void set_reversed(bool is_reversed);
// SorterBase<Torrent>

View File

@@ -8,6 +8,8 @@
#include "Prefs.h"
#include "Utils.h"
#include <libtransmission-app/app.h>
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h>
#include <libtransmission/version.h>
@@ -48,11 +50,9 @@ Glib::OptionEntry create_option_entry(Glib::ustring const& long_name, gchar shor
int main(int argc, char** argv)
{
/* init libtransmission */
tr_lib_init();
transmission::app::init();
/* init i18n */
tr_locale_set_global("");
bindtextdomain(AppTranslationDomainName, TRANSMISSIONLOCALEDIR);
bind_textdomain_codeset(AppTranslationDomainName, "UTF-8");
textdomain(AppTranslationDomainName);

View File

@@ -201,47 +201,47 @@
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by _Activity</attribute>
<attribute name="target">sort-by-activity</attribute>
<attribute name="target">sort_by_activity</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by A_ge</attribute>
<attribute name="target">sort-by-age</attribute>
<attribute name="target">sort_by_age</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by _Name</attribute>
<attribute name="target">sort-by-name</attribute>
<attribute name="target">sort_by_name</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by _Progress</attribute>
<attribute name="target">sort-by-progress</attribute>
<attribute name="target">sort_by_progress</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by _Queue</attribute>
<attribute name="target">sort-by-queue</attribute>
<attribute name="target">sort_by_queue</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by Rati_o</attribute>
<attribute name="target">sort-by-ratio</attribute>
<attribute name="target">sort_by_ratio</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by Si_ze</attribute>
<attribute name="target">sort-by-size</attribute>
<attribute name="target">sort_by_size</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by Stat_e</attribute>
<attribute name="target">sort-by-state</attribute>
<attribute name="target">sort_by_state</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by Time _Left</attribute>
<attribute name="target">sort-by-time-left</attribute>
<attribute name="target">sort_by_time_left</attribute>
</item>
</section>
<section>
@@ -308,47 +308,47 @@
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by _Activity</attribute>
<attribute name="target">sort-by-activity</attribute>
<attribute name="target">sort_by_activity</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by A_ge</attribute>
<attribute name="target">sort-by-age</attribute>
<attribute name="target">sort_by_age</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by _Name</attribute>
<attribute name="target">sort-by-name</attribute>
<attribute name="target">sort_by_name</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by _Progress</attribute>
<attribute name="target">sort-by-progress</attribute>
<attribute name="target">sort_by_progress</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by _Queue</attribute>
<attribute name="target">sort-by-queue</attribute>
<attribute name="target">sort_by_queue</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by Rati_o</attribute>
<attribute name="target">sort-by-ratio</attribute>
<attribute name="target">sort_by_ratio</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by Si_ze</attribute>
<attribute name="target">sort-by-size</attribute>
<attribute name="target">sort_by_size</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by Stat_e</attribute>
<attribute name="target">sort-by-state</attribute>
<attribute name="target">sort_by_state</attribute>
</item>
<item>
<attribute name="action">win.sort-torrents</attribute>
<attribute name="label" translatable="yes">Sort by Time _Left</attribute>
<attribute name="target">sort-by-time-left</attribute>
<attribute name="target">sort_by_time_left</attribute>
</item>
</section>
<section>

View File

@@ -12,7 +12,7 @@
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Show:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">activity_combo</property>
<property name="mnemonic-widget">show_mode_combo</property>
</object>
<packing>
<property name="expand">False</property>
@@ -21,7 +21,7 @@
</packing>
</child>
<child>
<object class="GtkComboBox" id="activity_combo">
<object class="GtkComboBox" id="show_mode_combo">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-end">6</property>

View File

@@ -7,11 +7,11 @@
<object class="GtkLabel" id="show_label">
<property name="label" translatable="1">_Show:</property>
<property name="use-underline">1</property>
<property name="mnemonic-widget">activity_combo</property>
<property name="mnemonic-widget">show_mode_combo</property>
</object>
</child>
<child>
<object class="GtkComboBox" id="activity_combo">
<object class="GtkComboBox" id="show_mode_combo">
<property name="hexpand">1</property>
<property name="margin-end">6</property>
</object>

View File

@@ -0,0 +1,55 @@
---
HeaderFilterRegex: .*/libtransmission/.*
# TODO: Enable `portability-template-virtual-member-function` after https://github.com/llvm/llvm-project/issues/139031 is fixed
# PRs welcome to fix & re-enable any of these explicitly-disabled checks
Checks: >
bugprone-*,
-bugprone-branch-clone,
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-narrowing-conversions,
cert-*,
-cert-err58-cpp,
-cert-int09-c,
clang-analyzer-*,
-clang-analyzer-optin.core.EnumCastOutOfRange,
cppcoreguidelines-avoid-do-while,
cppcoreguidelines-avoid-goto,
cppcoreguidelines-avoid-reference-coroutine-parameters,
cppcoreguidelines-init-variables,
cppcoreguidelines-interfaces-global-init,
cppcoreguidelines-no-malloc,
cppcoreguidelines-prefer-member-initializer,
cppcoreguidelines-pro-type-cstyle-cast,
cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-slicing,
cppcoreguidelines-special-member-functions,
cppcoreguidelines-virtual-class-destructor,
google-explicit-constructor,
misc-*,
-misc-include-cleaner,
-misc-no-recursion,
-misc-non-private-member-variables-in-classes,
modernize-*,
-modernize-use-trailing-return-type,
performance-*,
-performance-move-const-arg,
portability-*,
-portability-template-virtual-member-function,
readability-*,
-readability-enum-initial-value,
-readability-function-cognitive-complexity,
-readability-identifier-length,
-readability-magic-numbers,
-readability-qualified-auto,
CheckOptions:
- { key: cppcoreguidelines-avoid-do-while.IgnoreMacros, value: true }
- { key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor, value: true }
- { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase }
- { key: readability-identifier-naming.ParameterCase, value: lower_case }
- { key: readability-identifier-naming.PrivateMemberSuffix, value: _ }
- { key: readability-identifier-naming.ProtectedMemberSuffix, value: _ }
- { key: readability-identifier-naming.VariableCase, value: lower_case }
- { key: readability-implicit-bool-conversion.UseUpperCaseLiteralSuffix, value: true }

View File

@@ -0,0 +1,25 @@
include(CheckLibraryExists)
include(CheckSymbolExists)
set(LIBNAME ${TR_NAME}-app)
add_library(${LIBNAME} STATIC)
add_library(transmission::app ALIAS ${LIBNAME})
target_sources(${LIBNAME}
PRIVATE
app.cc
app.h
converters.cc
converters.h
display-modes.h
favicon-cache.h)
target_include_directories(${LIBNAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_BINARY_DIR}/..)
target_link_libraries(${LIBNAME}
PUBLIC
fmt::fmt-header-only)

View File

@@ -0,0 +1,19 @@
// 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 <libtransmission/utils.h> // tr_lib_init()
#include "libtransmission-app/app.h"
#include "libtransmission-app/converters.h"
namespace transmission::app
{
void init()
{
tr_lib_init();
tr_locale_set_global("");
detail::register_app_converters();
}
} // namespace transmission::app

14
libtransmission-app/app.h Normal file
View File

@@ -0,0 +1,14 @@
// 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.
#pragma once
namespace transmission::app
{
// should be called once when starting the app
void init();
} // namespace transmission::app

View File

@@ -0,0 +1,140 @@
// 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 <array>
#include <mutex>
#include <string_view>
#include <utility>
#include "libtransmission/serializer.h"
#include "libtransmission/variant.h"
#include "libtransmission-app/display-modes.h"
#include "libtransmission-app/converters.h"
namespace transmission::app::detail
{
namespace
{
template<typename T, size_t N>
using Lookup = std::array<std::pair<std::string_view, T>, N>;
// ---
auto constexpr ShowKeys = std::array<std::pair<tr_quark, ShowMode>, ShowModeCount>{ {
{ TR_KEY_show_active, ShowMode::ShowActive },
{ TR_KEY_show_all, ShowMode::ShowAll },
{ TR_KEY_show_downloading, ShowMode::ShowDownloading },
{ TR_KEY_show_error, ShowMode::ShowError },
{ TR_KEY_show_finished, ShowMode::ShowFinished },
{ TR_KEY_show_paused, ShowMode::ShowPaused },
{ TR_KEY_show_seeding, ShowMode::ShowSeeding },
{ TR_KEY_show_verifying, ShowMode::ShowVerifying },
} };
bool to_show_mode(tr_variant const& src, ShowMode* tgt)
{
static constexpr auto& Keys = ShowKeys;
if (auto const str = src.value_if<std::string_view>())
{
if (auto const needle = tr_quark_lookup(*str))
{
for (auto const& [key, val] : Keys)
{
if (needle == key)
{
*tgt = val;
return true;
}
}
}
}
return false;
}
tr_variant from_show_mode(ShowMode const& src)
{
static constexpr auto& Keys = ShowKeys;
for (auto const& [key, val] : Keys)
{
if (src == val)
{
return tr_variant::unmanaged_string(key);
}
}
return tr_variant::unmanaged_string(TR_KEY_show_all);
}
// ---
auto constexpr SortKeys = std::array<std::pair<tr_quark, SortMode>, SortModeCount>{ {
{ TR_KEY_sort_by_activity, SortMode::SortByActivity },
{ TR_KEY_sort_by_age, SortMode::SortByAge },
{ TR_KEY_sort_by_eta, SortMode::SortByEta },
{ TR_KEY_sort_by_id, SortMode::SortById },
{ TR_KEY_sort_by_name, SortMode::SortByName },
{ TR_KEY_sort_by_progress, SortMode::SortByProgress },
{ TR_KEY_sort_by_queue, SortMode::SortByQueue },
{ TR_KEY_sort_by_ratio, SortMode::SortByRatio },
{ TR_KEY_sort_by_size, SortMode::SortBySize },
{ TR_KEY_sort_by_state, SortMode::SortByState },
} };
bool to_sort_mode(tr_variant const& src, SortMode* tgt)
{
static constexpr auto& Keys = SortKeys;
if (auto const str = src.value_if<std::string_view>())
{
if (auto const needle = tr_quark_lookup(*str))
{
for (auto const& [key, val] : Keys)
{
if (needle == key)
{
*tgt = val;
return true;
}
}
}
}
return false;
}
tr_variant from_sort_mode(SortMode const& src)
{
static constexpr auto& Keys = SortKeys;
for (auto const& [key, val] : Keys)
{
if (src == val)
{
return tr_variant::unmanaged_string(key);
}
}
return tr_variant::unmanaged_string(TR_KEY_sort_by_name);
}
} // unnamed namespace
void register_app_converters()
{
static auto once = std::once_flag{};
std::call_once(
once,
[]
{
using Converters = libtransmission::serializer::Converters;
Converters::add(to_show_mode, from_show_mode);
Converters::add(to_sort_mode, from_sort_mode);
});
}
} // namespace transmission::app::detail

View File

@@ -0,0 +1,13 @@
// 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.
#pragma once
namespace transmission::app::detail
{
void register_app_converters();
} // namespace transmission::app::detail

View File

@@ -0,0 +1,40 @@
// 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.
#pragma once
namespace transmission::app
{
enum class ShowMode
{
ShowAll,
ShowActive,
ShowDownloading,
ShowSeeding,
ShowPaused,
ShowFinished,
ShowVerifying,
ShowError,
};
inline auto constexpr ShowModeCount = 8U;
inline auto constexpr DefaultShowMode = ShowMode::ShowAll;
enum class SortMode
{
SortByActivity,
SortByAge,
SortByEta,
SortByName,
SortByProgress,
SortByQueue,
SortByRatio,
SortBySize,
SortByState,
SortById,
};
inline auto constexpr SortModeCount = 10U;
inline auto constexpr DefaultSortMode = SortMode::SortByName;
} // namespace transmission::app

View File

@@ -55,7 +55,6 @@ target_sources(${TR_NAME}
error-types.h
error.cc
error.h
favicon-cache.h
file-capacity.cc
file-piece-map.cc
file-piece-map.h
@@ -307,7 +306,6 @@ if(INSTALL_LIB)
FILES
error-types.h
error.h
favicon-cache.h
file.h
log.h
makemeta.h

View File

@@ -8,6 +8,7 @@
#include <array>
#include <cstddef>
#include <iterator>
#include <optional>
#include <tuple>
#include <type_traits>
#include <typeinfo>
@@ -213,6 +214,23 @@ private:
static inline ConverterStorage<T> converter_storage;
};
template<typename T>
[[nodiscard]] std::optional<T> to_value(tr_variant const& var)
{
if (auto ret = T{}; Converters::deserialize<T>(var, &ret))
{
return ret;
}
return {};
}
template<typename T>
[[nodiscard]] tr_variant to_variant(T const& val)
{
return Converters::serialize<T>(val);
}
// ---
/**

View File

@@ -17,7 +17,7 @@
#include <QTranslator>
#include <QWeakPointer>
#include <libtransmission/favicon-cache.h>
#include <libtransmission-app/favicon-cache.h>
#include "AddData.h"
#include "Typedefs.h"

View File

@@ -211,6 +211,7 @@ target_include_directories(${TR_NAME}-qt
target_link_libraries(${TR_NAME}-qt
PRIVATE
${TR_NAME}-app
${TR_NAME}
transmission::qt_impl)

View File

@@ -15,7 +15,7 @@ public:
TrackerStatsList = QMetaType::User,
PeerList,
FileList,
FilterModeType,
ShowModeType,
SortModeType
};
};

View File

@@ -4,7 +4,7 @@
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <libtransmission/favicon-cache.h>
#include <libtransmission-app/favicon-cache.h>
#include <QApplication>
#include <QPixmap>

View File

@@ -45,25 +45,25 @@ FilterBarComboBox* FilterBar::createActivityCombo()
auto* model = new QStandardItemModel{ this };
auto* row = new QStandardItem{ tr("All") };
row->setData(FilterMode::SHOW_ALL, ACTIVITY_ROLE);
row->setData(QVariant::fromValue(ShowMode::ShowAll), ACTIVITY_ROLE);
model->appendRow(row);
model->appendRow(new QStandardItem{}); // separator
FilterBarComboBoxDelegate::setSeparator(model, model->index(1, 0));
auto add_row = [model](auto const filter_mode, QString label, std::optional<icons::Type> const type)
auto add_row = [model](auto const show_mode, QString label, std::optional<icons::Type> const type)
{
auto* row = type ? new QStandardItem{ icons::icon(*type), label } : new QStandardItem{ label };
row->setData(filter_mode, ACTIVITY_ROLE);
model->appendRow(row);
auto* new_row = type ? new QStandardItem{ icons::icon(*type), label } : new QStandardItem{ label };
new_row->setData(QVariant::fromValue(show_mode), ACTIVITY_ROLE);
model->appendRow(new_row);
};
add_row(FilterMode::SHOW_ACTIVE, tr("Active"), icons::Type::TorrentStateActive);
add_row(FilterMode::SHOW_SEEDING, tr("Seeding"), icons::Type::TorrentStateSeeding);
add_row(FilterMode::SHOW_DOWNLOADING, tr("Downloading"), icons::Type::TorrentStateDownloading);
add_row(FilterMode::SHOW_PAUSED, tr("Paused"), icons::Type::TorrentStatePaused);
add_row(FilterMode::SHOW_FINISHED, tr("Finished"), {});
add_row(FilterMode::SHOW_VERIFYING, tr("Verifying"), icons::Type::TorrentStateVerifying);
add_row(FilterMode::SHOW_ERROR, tr("Error"), icons::Type::TorrentStateError);
add_row(ShowMode::ShowActive, tr("Active"), icons::Type::TorrentStateActive);
add_row(ShowMode::ShowSeeding, tr("Seeding"), icons::Type::TorrentStateSeeding);
add_row(ShowMode::ShowDownloading, tr("Downloading"), icons::Type::TorrentStateDownloading);
add_row(ShowMode::ShowPaused, tr("Paused"), icons::Type::TorrentStatePaused);
add_row(ShowMode::ShowFinished, tr("Finished"), {});
add_row(ShowMode::ShowVerifying, tr("Verifying"), icons::Type::TorrentStateVerifying);
add_row(ShowMode::ShowError, tr("Error"), icons::Type::TorrentStateError);
c->setModel(model);
return c;
@@ -97,8 +97,6 @@ Torrent::fields_t constexpr TrackerFields = {
return name;
}
auto constexpr ActivityFields = FilterMode::TorrentFields;
} // namespace
void FilterBar::refreshTrackers()
@@ -266,9 +264,9 @@ void FilterBar::refreshPref(int key)
{
case Prefs::FILTER_MODE:
{
auto const m = prefs_.get<FilterMode>(key);
auto const show_mode = prefs_.get<ShowMode>(key);
QAbstractItemModel const* const model = activity_combo_->model();
QModelIndexList indices = model->match(model->index(0, 0), ACTIVITY_ROLE, m.mode());
QModelIndexList indices = model->match(model->index(0, 0), ACTIVITY_ROLE, QVariant::fromValue(show_mode));
activity_combo_->setCurrentIndex(indices.isEmpty() ? 0 : indices.first().row());
break;
}
@@ -308,7 +306,7 @@ void FilterBar::onTorrentsChanged(torrent_ids_t const& ids, Torrent::fields_t co
recountTrackersSoon();
}
if ((changed_fields & ActivityFields).any())
if ((changed_fields & ShowModeFields).any())
{
recountActivitySoon();
}
@@ -335,8 +333,8 @@ void FilterBar::onActivityIndexChanged(int i)
{
if (!is_bootstrapping_)
{
auto const mode = FilterMode(activity_combo_->itemData(i, ACTIVITY_ROLE).toInt());
prefs_.set(Prefs::FILTER_MODE, mode);
auto const show_mode = activity_combo_->itemData(i, ACTIVITY_ROLE).value<ShowMode>();
prefs_.set(Prefs::FILTER_MODE, show_mode);
}
}
@@ -369,10 +367,10 @@ void FilterBar::recount()
for (int row = 0, n = model->rowCount(); row < n; ++row)
{
auto const index = model->index(row, 0);
auto const mode = index.data(ACTIVITY_ROLE).toInt();
auto const count = torrents_per_mode[mode];
auto const show_mode = index.data(ACTIVITY_ROLE).value<ShowMode>();
auto const count = torrents_per_mode[static_cast<int>(show_mode)];
model->setData(index, count, FilterBarComboBox::CountRole);
model->setData(index, getCountString(static_cast<size_t>(count)), FilterBarComboBox::CountStringRole);
model->setData(index, getCountString(count), FilterBarComboBox::CountStringRole);
}
}

View File

@@ -5,33 +5,46 @@
#include "Filters.h"
// NB: if you change this function, update TorrentFields too
bool FilterMode::test(Torrent const& tor, int mode)
// NOTE: `ShowModeFields` is the set of all Torrent properties
// needed to correctly run these tests. If you change these tests,
// then update `ShowModeFields` accordingly.
bool should_show_torrent(Torrent const& tor, ShowMode const mode)
{
switch (mode)
{
case SHOW_ACTIVE:
case ShowMode::ShowActive:
return tor.peersWeAreUploadingTo() > 0 || tor.peersWeAreDownloadingFrom() > 0 || tor.isVerifying();
case SHOW_DOWNLOADING:
case ShowMode::ShowDownloading:
return tor.isDownloading() || tor.isWaitingToDownload();
case SHOW_ERROR:
case ShowMode::ShowError:
return tor.hasError();
case SHOW_FINISHED:
case ShowMode::ShowFinished:
return tor.isFinished();
case SHOW_PAUSED:
case ShowMode::ShowPaused:
return tor.isPaused();
case SHOW_SEEDING:
case ShowMode::ShowSeeding:
return tor.isSeeding() || tor.isWaitingToSeed();
case SHOW_VERIFYING:
case ShowMode::ShowVerifying:
return tor.isVerifying() || tor.isWaitingToVerify();
default: // SHOW_ALL
return true;
}
}
// The Torrent properties that can affect ShowMode filtering.
// When one of these changes, it's time to refilter.
// Update this as needed when ShowModeTests changes.
Torrent::fields_t const ShowModeFields{
(uint64_t{ 1 } << Torrent::TORRENT_ERROR) | //
(uint64_t{ 1 } << Torrent::IS_FINISHED) | //
(uint64_t{ 1 } << Torrent::PEERS_GETTING_FROM_US) | //
(uint64_t{ 1 } << Torrent::PEERS_SENDING_TO_US) | //
(uint64_t{ 1 } << Torrent::STATUS) //
};

View File

@@ -5,89 +5,20 @@
#pragma once
#include <cstdint> // uint64_t
#include <libtransmission-app/display-modes.h>
#include "Torrent.h"
class FilterMode
{
public:
enum
{
SHOW_ALL,
SHOW_ACTIVE,
SHOW_DOWNLOADING,
SHOW_SEEDING,
SHOW_PAUSED,
SHOW_FINISHED,
SHOW_VERIFYING,
SHOW_ERROR,
NUM_MODES
};
// The Torrent properties that can affect ShowMode filtering.
// When one of these changes, it's time to refilter.
extern Torrent::fields_t const ShowModeFields;
explicit FilterMode(int mode = SHOW_ALL)
: mode_{ mode }
{
}
[[nodiscard]] int mode() const
{
return mode_;
}
/* The Torrent properties that can affect this filter.
When one of these changes, it's time to refilter. */
static constexpr auto TorrentFields = Torrent::fields_t{
(uint64_t{ 1 } << Torrent::TORRENT_ERROR) | //
(uint64_t{ 1 } << Torrent::IS_FINISHED) | //
(uint64_t{ 1 } << Torrent::PEERS_GETTING_FROM_US) | //
(uint64_t{ 1 } << Torrent::PEERS_SENDING_TO_US) | //
(uint64_t{ 1 } << Torrent::STATUS) //
};
static bool test(Torrent const& tor, int mode);
[[nodiscard]] bool test(Torrent const& tor) const
{
return test(tor, mode());
}
private:
int mode_;
};
Q_DECLARE_METATYPE(FilterMode)
class SortMode
{
public:
enum
{
SORT_BY_ACTIVITY,
SORT_BY_AGE,
SORT_BY_ETA,
SORT_BY_NAME,
SORT_BY_PROGRESS,
SORT_BY_QUEUE,
SORT_BY_RATIO,
SORT_BY_SIZE,
SORT_BY_STATE,
SORT_BY_ID,
NUM_MODES
};
explicit SortMode(int mode = SORT_BY_ID) noexcept
: mode_{ mode }
{
}
[[nodiscard]] constexpr auto mode() const noexcept
{
return mode_;
}
private:
int mode_ = SORT_BY_ID;
};
using ShowMode = transmission::app::ShowMode;
Q_DECLARE_METATYPE(ShowMode)
inline auto constexpr DefaultShowMode = transmission::app::DefaultShowMode;
inline auto constexpr ShowModeCount = transmission::app::ShowModeCount;
bool should_show_torrent(Torrent const& torrent, ShowMode);
using SortMode = transmission::app::SortMode;
Q_DECLARE_METATYPE(SortMode)
inline auto constexpr DefaultSortMode = transmission::app::DefaultSortMode;

View File

@@ -156,16 +156,16 @@ MainWindow::MainWindow(Session& session, Prefs& prefs, TorrentModel& model, bool
ui_.listView->setModel(&filter_model_);
connect(ui_.listView->selectionModel(), &QItemSelectionModel::selectionChanged, refresh_action_sensitivity_soon);
std::array<std::pair<QAction*, int>, 9> const sort_modes = { {
{ ui_.action_SortByActivity, SortMode::SORT_BY_ACTIVITY },
{ ui_.action_SortByAge, SortMode::SORT_BY_AGE },
{ ui_.action_SortByETA, SortMode::SORT_BY_ETA },
{ ui_.action_SortByName, SortMode::SORT_BY_NAME },
{ ui_.action_SortByProgress, SortMode::SORT_BY_PROGRESS },
{ ui_.action_SortByQueue, SortMode::SORT_BY_QUEUE },
{ ui_.action_SortByRatio, SortMode::SORT_BY_RATIO },
{ ui_.action_SortBySize, SortMode::SORT_BY_SIZE },
{ ui_.action_SortByState, SortMode::SORT_BY_STATE },
auto const sort_modes = std::array<std::pair<QAction*, SortMode>, 9U>{ {
{ ui_.action_SortByActivity, SortMode::SortByActivity },
{ ui_.action_SortByAge, SortMode::SortByAge },
{ ui_.action_SortByETA, SortMode::SortByEta },
{ ui_.action_SortByName, SortMode::SortByName },
{ ui_.action_SortByProgress, SortMode::SortByProgress },
{ ui_.action_SortByQueue, SortMode::SortByQueue },
{ ui_.action_SortByRatio, SortMode::SortByRatio },
{ ui_.action_SortBySize, SortMode::SortBySize },
{ ui_.action_SortByState, SortMode::SortByState },
} };
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
@@ -173,7 +173,7 @@ MainWindow::MainWindow(Session& session, Prefs& prefs, TorrentModel& model, bool
for (auto const& [action, mode] : sort_modes)
{
action->setProperty(SortModeKey, mode);
action->setProperty(SortModeKey, QVariant::fromValue(mode));
action_group->addAction(action);
}
@@ -437,7 +437,7 @@ QMenu* MainWindow::createStatsModeMenu()
void MainWindow::onSortModeChanged(QAction const* action)
{
prefs_.set(Prefs::SORT_MODE, SortMode(action->property(SortModeKey).toInt()));
prefs_.set(Prefs::SORT_MODE, action->property(SortModeKey));
}
void MainWindow::setSortAscendingPref(bool b)
@@ -1109,7 +1109,6 @@ void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason)
void MainWindow::refreshPref(int key)
{
auto b = bool{};
auto i = int{};
auto str = QString{};
switch (key)
@@ -1130,11 +1129,12 @@ void MainWindow::refreshPref(int key)
break;
case Prefs::SORT_MODE:
i = prefs_.get<SortMode>(key).mode();
for (auto* action : ui_.action_SortByActivity->actionGroup()->actions())
{
action->setChecked(i == action->property(SortModeKey).toInt());
auto const sort_mode = prefs_.get<SortMode>(key);
for (auto* action : ui_.action_SortByActivity->actionGroup()->actions())
{
action->setChecked(sort_mode == action->property(SortModeKey).value<SortMode>());
}
}
break;

View File

@@ -21,6 +21,7 @@
#include <libtransmission/transmission.h>
#include <libtransmission/api-compat.h>
#include <libtransmission/serializer.h>
#include <libtransmission/variant.h>
#include "CustomVariantType.h"
@@ -29,6 +30,8 @@
#include "VariantHelpers.h"
namespace api_compat = libtransmission::api_compat;
using libtransmission::serializer::to_value;
using libtransmission::serializer::to_variant;
using ::trqt::variant_helpers::dictAdd;
using ::trqt::variant_helpers::getValue;
using namespace std::string_view_literals;
@@ -90,7 +93,7 @@ std::array<Prefs::PrefItem, Prefs::PREFS_COUNT> const Prefs::Items{
{ MAIN_WINDOW_WIDTH, TR_KEY_main_window_width, QMetaType::Int },
{ MAIN_WINDOW_X, TR_KEY_main_window_x, QMetaType::Int },
{ MAIN_WINDOW_Y, TR_KEY_main_window_y, QMetaType::Int },
{ FILTER_MODE, TR_KEY_filter_mode, CustomVariantType::FilterModeType },
{ FILTER_MODE, TR_KEY_filter_mode, CustomVariantType::ShowModeType },
{ FILTER_TRACKERS, TR_KEY_filter_trackers, QMetaType::QString },
{ FILTER_TEXT, TR_KEY_filter_text, QMetaType::QString },
{ SESSION_IS_REMOTE, TR_KEY_remote_session_enabled, QMetaType::Bool },
@@ -164,31 +167,6 @@ std::array<Prefs::PrefItem, Prefs::PREFS_COUNT> const Prefs::Items{
namespace
{
auto constexpr FilterModes = std::array<std::pair<int, std::string_view>, FilterMode::NUM_MODES>{ {
{ FilterMode::SHOW_ALL, "show-all" },
{ FilterMode::SHOW_ACTIVE, "show-active" },
{ FilterMode::SHOW_DOWNLOADING, "show-downloading" },
{ FilterMode::SHOW_SEEDING, "show-seeding" },
{ FilterMode::SHOW_PAUSED, "show-paused" },
{ FilterMode::SHOW_FINISHED, "show-finished" },
{ FilterMode::SHOW_VERIFYING, "show-verifying" },
{ FilterMode::SHOW_ERROR, "show-error" },
} };
auto constexpr SortModes = std::array<std::pair<int, std::string_view>, SortMode::NUM_MODES>{ {
{ SortMode::SORT_BY_NAME, "sort-by-name" },
{ SortMode::SORT_BY_ACTIVITY, "sort-by-activity" },
{ SortMode::SORT_BY_AGE, "sort-by-age" },
{ SortMode::SORT_BY_ETA, "sort-by-eta" },
{ SortMode::SORT_BY_PROGRESS, "sort-by-progress" },
{ SortMode::SORT_BY_QUEUE, "sort-by-queue" },
{ SortMode::SORT_BY_RATIO, "sort-by-ratio" },
{ SortMode::SORT_BY_SIZE, "sort-by-size" },
{ SortMode::SORT_BY_STATE, "sort-by-state" },
{ SortMode::SORT_BY_ID, "sort-by-id" },
} };
bool isValidUtf8(QByteArray const& byteArray)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
@@ -257,30 +235,16 @@ Prefs::Prefs(QString config_dir)
break;
case CustomVariantType::SortModeType:
if (auto const value = getValue<std::string_view>(b); value)
if (auto const val = to_value<SortMode>(*b))
{
auto const test = [&value](auto const& item)
{
return item.second == *value;
};
// NOLINTNEXTLINE(readability-qualified-auto)
auto const it = std::find_if(std::cbegin(SortModes), std::cend(SortModes), test);
auto const& [mode, mode_str] = it == std::end(SortModes) ? SortModes.front() : *it;
values_[i] = QVariant::fromValue(SortMode{ mode });
values_[i] = QVariant::fromValue(*val);
}
break;
case CustomVariantType::FilterModeType:
if (auto const value = getValue<std::string_view>(b); value)
case CustomVariantType::ShowModeType:
if (auto const val = to_value<ShowMode>(*b))
{
auto const test = [&value](auto const& item)
{
return item.second == *value;
};
// NOLINTNEXTLINE(readability-qualified-auto)
auto const it = std::find_if(std::cbegin(FilterModes), std::cend(FilterModes), test);
auto const& [mode, mode_str] = it == std::end(FilterModes) ? FilterModes.front() : *it;
values_[i] = QVariant::fromValue(FilterMode{ mode });
values_[i] = QVariant::fromValue(*val);
}
break;
@@ -349,32 +313,12 @@ Prefs::~Prefs()
break;
case CustomVariantType::SortModeType:
{
auto const mode = val.value<SortMode>().mode();
auto const test = [&mode](auto const& item)
{
return item.first == mode;
};
// NOLINTNEXTLINE(readability-qualified-auto)
auto const it = std::find_if(std::cbegin(SortModes), std::cend(SortModes), test);
auto const& [mode_val, mode_str] = it == std::end(SortModes) ? SortModes.front() : *it;
dictAdd(&current_settings, key, mode_str);
break;
}
*tr_variantDictAdd(&current_settings, key) = to_variant(val.value<SortMode>());
break;
case CustomVariantType::FilterModeType:
{
auto const mode = val.value<FilterMode>().mode();
auto const test = [&mode](auto const& item)
{
return item.first == mode;
};
// NOLINTNEXTLINE(readability-qualified-auto)
auto const it = std::find_if(std::cbegin(FilterModes), std::cend(FilterModes), test);
auto const& [mode_val, mode_str] = it == std::end(FilterModes) ? FilterModes.front() : *it;
dictAdd(&current_settings, key, mode_str);
break;
}
case CustomVariantType::ShowModeType:
*tr_variantDictAdd(&current_settings, key) = to_variant(val.value<ShowMode>());
break;
case QMetaType::QString:
dictAdd(&current_settings, key, val.toString());
@@ -430,11 +374,11 @@ tr_variant Prefs::get_default_app_settings()
settings.try_emplace(TR_KEY_blocklist_updates_enabled, true);
settings.try_emplace(TR_KEY_compact_view, false);
settings.try_emplace(TR_KEY_download_dir, download_dir);
settings.try_emplace(TR_KEY_filter_mode, tr_variant::unmanaged_string("all"sv));
settings.try_emplace(TR_KEY_filter_mode, to_variant(DefaultShowMode));
settings.try_emplace(TR_KEY_inhibit_desktop_hibernation, false);
settings.try_emplace(TR_KEY_main_window_height, 500);
settings.try_emplace(TR_KEY_main_window_layout_order, tr_variant::unmanaged_string("menu,toolbar,filter,list,statusbar"sv));
settings.try_emplace(TR_KEY_main_window_width, 300);
settings.try_emplace(TR_KEY_main_window_width, 600);
settings.try_emplace(TR_KEY_main_window_x, 50);
settings.try_emplace(TR_KEY_main_window_y, 50);
settings.try_emplace(TR_KEY_open_dialog_dir, QDir::home().absolutePath().toStdString());
@@ -443,11 +387,11 @@ tr_variant Prefs::get_default_app_settings()
settings.try_emplace(TR_KEY_remote_session_enabled, false);
settings.try_emplace(TR_KEY_remote_session_host, tr_variant::unmanaged_string("localhost"sv));
settings.try_emplace(TR_KEY_remote_session_https, false);
settings.try_emplace(TR_KEY_remote_session_password, tr_variant::unmanaged_string(""));
settings.try_emplace(TR_KEY_remote_session_password, tr_variant::unmanaged_string(""sv));
settings.try_emplace(TR_KEY_remote_session_port, TrDefaultRpcPort);
settings.try_emplace(TR_KEY_remote_session_requires_authentication, false);
settings.try_emplace(TR_KEY_remote_session_rpc_url_path, TR_DEFAULT_RPC_URL_STR "rpc");
settings.try_emplace(TR_KEY_remote_session_username, tr_variant::unmanaged_string(""));
settings.try_emplace(TR_KEY_remote_session_rpc_url_path, tr_variant::unmanaged_string(TR_DEFAULT_RPC_URL_STR "rpc"));
settings.try_emplace(TR_KEY_remote_session_username, tr_variant::unmanaged_string(""sv));
settings.try_emplace(TR_KEY_show_backup_trackers, false);
settings.try_emplace(TR_KEY_show_filterbar, true);
settings.try_emplace(TR_KEY_show_notification_area_icon, false);
@@ -455,7 +399,7 @@ tr_variant Prefs::get_default_app_settings()
settings.try_emplace(TR_KEY_show_statusbar, true);
settings.try_emplace(TR_KEY_show_toolbar, true);
settings.try_emplace(TR_KEY_show_tracker_scrapes, false);
settings.try_emplace(TR_KEY_sort_mode, tr_variant::unmanaged_string("sort-by-name"sv));
settings.try_emplace(TR_KEY_sort_mode, to_variant(DefaultSortMode));
settings.try_emplace(TR_KEY_sort_reversed, false);
settings.try_emplace(TR_KEY_start_minimized, false);
settings.try_emplace(TR_KEY_statusbar_stats, tr_variant::unmanaged_string("total-ratio"));

View File

@@ -13,6 +13,8 @@
#include <libtransmission/quark.h>
#include <libtransmission-app/display-modes.h>
class QDateTime;
extern "C"
@@ -168,7 +170,7 @@ public:
QDateTime getDateTime(int key) const;
template<typename T>
T get(int key) const
[[nodiscard]] T get(int const key) const
{
return values_[key].value<T>();
}

View File

@@ -27,6 +27,7 @@
#include <libtransmission/transmission.h>
#include <libtransmission/quark.h>
#include <libtransmission/serializer.h>
#include <libtransmission/session-id.h>
#include <libtransmission/utils.h>
#include <libtransmission/variant.h>
@@ -35,6 +36,7 @@
#include "AddData.h"
#include "CustomVariantType.h"
#include "Filters.h"
#include "Prefs.h"
#include "RpcQueue.h"
#include "SessionDialog.h"
@@ -43,10 +45,11 @@
using namespace std::literals;
using ::libtransmission::serializer::to_value;
using ::libtransmission::serializer::to_variant;
using ::trqt::variant_helpers::dictAdd;
using ::trqt::variant_helpers::dictFind;
using ::trqt::variant_helpers::getValue;
using ::trqt::variant_helpers::to_variant;
/***
****
@@ -675,6 +678,8 @@ using TorrentProperties = Session::TorrentProperties;
};
}
// clang-format on
return {};
}
} // namespace
@@ -952,8 +957,22 @@ void Session::updateInfo(tr_variant* args_dict)
break;
case CustomVariantType::FilterModeType:
case CustomVariantType::ShowModeType:
if (auto const val = to_value<ShowMode>(*b))
{
prefs_.set(i, *val);
}
break;
case CustomVariantType::SortModeType:
if (auto const val = to_value<SortMode>(*b))
{
prefs_.set(i, *val);
}
break;
case QMetaType::QString:
if (auto const value = getValue<QString>(b); value)
{

View File

@@ -81,9 +81,9 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
auto const* a = sourceModel()->data(left, TorrentModel::TorrentRole).value<Torrent const*>();
auto const* b = sourceModel()->data(right, TorrentModel::TorrentRole).value<Torrent const*>();
switch (prefs_.get<SortMode>(Prefs::SORT_MODE).mode())
switch (prefs_.get<SortMode>(Prefs::SORT_MODE))
{
case SortMode::SORT_BY_QUEUE:
case SortMode::SortByQueue:
if (val == 0)
{
val = -tr_compare_3way(a->queuePosition(), b->queuePosition());
@@ -91,7 +91,7 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
break;
case SortMode::SORT_BY_SIZE:
case SortMode::SortBySize:
if (val == 0)
{
val = tr_compare_3way(a->sizeWhenDone(), b->sizeWhenDone());
@@ -99,7 +99,7 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
break;
case SortMode::SORT_BY_AGE:
case SortMode::SortByAge:
if (val == 0)
{
val = tr_compare_3way(a->dateAdded(), b->dateAdded());
@@ -107,7 +107,7 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
break;
case SortMode::SORT_BY_ID:
case SortMode::SortById:
if (val == 0)
{
val = tr_compare_3way(a->id(), b->id());
@@ -115,7 +115,7 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
break;
case SortMode::SORT_BY_ACTIVITY:
case SortMode::SortByActivity:
if (val == 0)
{
val = tr_compare_3way(a->downloadSpeed() + a->uploadSpeed(), b->downloadSpeed() + b->uploadSpeed());
@@ -130,7 +130,7 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
[[fallthrough]];
case SortMode::SORT_BY_STATE:
case SortMode::SortByState:
if (val == 0)
{
val = -tr_compare_3way(a->isPaused(), b->isPaused());
@@ -153,7 +153,7 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
[[fallthrough]];
case SortMode::SORT_BY_PROGRESS:
case SortMode::SortByProgress:
if (val == 0)
{
val = tr_compare_3way(a->metadataPercentDone(), b->metadataPercentDone());
@@ -176,7 +176,7 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
[[fallthrough]];
case SortMode::SORT_BY_RATIO:
case SortMode::SortByRatio:
if (val == 0)
{
val = a->compareRatio(*b);
@@ -184,7 +184,7 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
break;
case SortMode::SORT_BY_ETA:
case SortMode::SortByEta:
if (val == 0)
{
val = a->compareETA(*b);
@@ -221,8 +221,8 @@ bool TorrentFilter::filterAcceptsRow(int source_row, QModelIndex const& source_p
if (accepts)
{
auto const m = prefs_.get<FilterMode>(Prefs::FILTER_MODE);
accepts = m.test(tor);
auto const show_mode = prefs_.get<ShowMode>(Prefs::FILTER_MODE);
accepts = should_show_torrent(tor, show_mode);
}
if (accepts)
@@ -241,7 +241,7 @@ bool TorrentFilter::filterAcceptsRow(int source_row, QModelIndex const& source_p
return accepts;
}
std::array<int, FilterMode::NUM_MODES> TorrentFilter::countTorrentsPerMode() const
std::array<int, ShowModeCount> TorrentFilter::countTorrentsPerMode() const
{
auto* const torrent_model = dynamic_cast<TorrentModel*>(sourceModel());
if (torrent_model == nullptr)
@@ -249,13 +249,13 @@ std::array<int, FilterMode::NUM_MODES> TorrentFilter::countTorrentsPerMode() con
return {};
}
auto torrent_counts = std::array<int, FilterMode::NUM_MODES>{};
auto torrent_counts = std::array<int, ShowModeCount>{};
for (auto const& tor : torrent_model->torrents())
{
for (int mode = 0; mode < FilterMode::NUM_MODES; ++mode)
for (unsigned int mode = 0; mode < ShowModeCount; ++mode)
{
if (FilterMode::test(*tor, mode))
if (should_show_torrent(*tor, static_cast<ShowMode>(mode)))
{
++torrent_counts[mode];
}

View File

@@ -14,7 +14,6 @@
class QString;
class FilterMode;
class Prefs;
class Torrent;
@@ -36,7 +35,7 @@ public:
TorrentFilter& operator=(TorrentFilter&&) = delete;
TorrentFilter& operator=(TorrentFilter const&) = delete;
[[nodiscard]] std::array<int, FilterMode::NUM_MODES> countTorrentsPerMode() const;
[[nodiscard]] std::array<int, ShowModeCount> countTorrentsPerMode() const;
protected:
// QSortFilterProxyModel

View File

@@ -10,7 +10,8 @@
#include <QTextDocument>
#include <libtransmission/web-utils.h>
#include <libtransmission/favicon-cache.h>
#include <libtransmission-app/favicon-cache.h>
#include "Formatter.h"
#include "Torrent.h"

View File

@@ -14,7 +14,6 @@
#include <QString>
#include <libtransmission/serializer.h>
#include <libtransmission/variant.h>
class QByteArray;
@@ -29,12 +28,6 @@ namespace trqt::variant_helpers
{
void register_qt_converters();
template<typename T>
[[nodiscard]] tr_variant to_variant(T const& val)
{
return libtransmission::serializer::Converters::serialize(val);
}
template<typename T, typename std::enable_if_t<std::is_same_v<T, bool>>* = nullptr>
auto getValue(tr_variant const* variant)
{

View File

@@ -15,6 +15,8 @@
#include <libtransmission/utils.h>
#include <libtransmission/version.h>
#include <libtransmission-app/app.h>
#include "Application.h"
#include "InteropHelper.h"
#include "Prefs.h"
@@ -99,8 +101,7 @@ bool tryDelegate(QStringList const& filenames)
int tr_main(int argc, char** argv)
{
tr_lib_init();
tr_locale_set_global("");
transmission::app::init();
trqt::variant_helpers::register_qt_converters();
// parse the command-line arguments