From e15270271de2e35b3d41133b934e9c2b93ad74ba Mon Sep 17 00:00:00 2001 From: Toff Date: Mon, 31 Oct 2016 12:19:41 +0100 Subject: [PATCH] Add a temporary name filter for the main view. By using 'F' in the main view you can enter a regex that will be used to filter the view based on the torrent name. To remove the filter set the input to empty. This is adding a 'view.temp_filter' property and a 'match{}' function. For example with Ctrl+X you could do: command> view.temp_filter=main,"match={d.name=,.*linux.*iso}" --- doc/old/rtorrent.1 | 3 +++ doc/old/rtorrent.1.xml | 7 ++++++ src/command_ui.cc | 51 ++++++++++++++++++++++++++++++++++++++++ src/core/view.cc | 23 +++++++++++------- src/core/view.h | 3 +++ src/core/view_manager.cc | 8 +++++++ src/core/view_manager.h | 1 + src/ui/download_list.cc | 24 +++++++++++++++++++ src/ui/download_list.h | 3 ++- 9 files changed, 113 insertions(+), 10 deletions(-) diff --git a/doc/old/rtorrent.1 b/doc/old/rtorrent.1 index fa5477f2..f1ef638a 100644 --- a/doc/old/rtorrent.1 +++ b/doc/old/rtorrent.1 @@ -99,6 +99,9 @@ Delete the file the torrent is tied to, and clear the association. .TP \fBI\fR Toggle whether torrent ignores ratio settings. +.TP +\fBF\fR +Add a temporary name based regex filter to the current view. .SS "DOWNLOAD VIEW KEYS" .TP \fB->\fR diff --git a/doc/old/rtorrent.1.xml b/doc/old/rtorrent.1.xml index e8093896..6968a94b 100644 --- a/doc/old/rtorrent.1.xml +++ b/doc/old/rtorrent.1.xml @@ -225,6 +225,13 @@ Toggle whether torrent ignores ratio settings. + + F + +Add a temporary name based regex filter to the current view. + + + diff --git a/src/command_ui.cc b/src/command_ui.cc index 69ee2f54..c6f7d311 100644 --- a/src/command_ui.cc +++ b/src/command_ui.cc @@ -39,6 +39,8 @@ #include #include +#include + #include #include @@ -282,6 +284,53 @@ torrent::Object apply_equal(rpc::target_type target, const torrent::Object::list return result.is_value() ? result.as_value() == 0 : (int64_t)false; } +// Regexp based 'match' function. +// arg1: the text to match. +// arg2: the regexp pattern. +// eg: match{d.name=,.*linux.*iso} +torrent::Object apply_match(rpc::target_type target, const torrent::Object::list_type& args) { + if (args.size() != 2) + throw torrent::input_error("Wrong argument count for 'match': 2 arguments needed."); + + // This really should be converted to using args flagged as + // commands, so that we can compare commands and statics values. + + torrent::Object result1; + torrent::Object result2; + + rpc::target_type target1 = rpc::is_target_pair(target) ? rpc::get_target_left(target) : target; + rpc::target_type target2 = rpc::is_target_pair(target) ? rpc::get_target_right(target) : target; + + if (args.front().is_dict_key()) + result1 = rpc::commands.call_command(args.front().as_dict_key().c_str(), args.front().as_dict_obj(), target1); + else + result1 = rpc::parse_command_single(target1, args.front().as_string()); + + if (args.back().is_dict_key()) + result2 = rpc::commands.call_command(args.back().as_dict_key().c_str(), args.back().as_dict_obj(), target2); + else + result2 = args.back().as_string(); + + if (result1.type() != result2.type()) + throw torrent::input_error("Type mismatch for 'match' arguments."); + + std::string text = result1.as_string(); + std::string pattern = result2.as_string(); + + std::transform(text.begin(), text.end(), text.begin(), ::tolower); + std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower); + + bool match = false; + try { + std::regex re(pattern); + match = std::regex_match(text, re); + } catch (const std::regex_error& exc) { + control->core()->push_log_std("regex_error: " + std::string(exc.what())); + } + + return match ? (int64_t)true : (int64_t)false; +} + torrent::Object apply_to_time(const torrent::Object& rawArgs, int flags) { std::tm *u; @@ -529,6 +578,7 @@ initialize_command_ui() { CMD2_ANY_LIST("view.set", std::bind(&apply_view_set, std::placeholders::_2)); CMD2_ANY_LIST("view.filter", std::bind(&apply_view_event, &core::ViewManager::set_filter, std::placeholders::_2)); + CMD2_ANY_LIST("view.temp_filter", std::bind(&apply_view_event, &core::ViewManager::set_temp_filter, std::placeholders::_2)); CMD2_ANY_LIST("view.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2)); CMD2_ANY_LIST("view.sort", std::bind(&apply_view_sort, std::placeholders::_2)); @@ -574,6 +624,7 @@ initialize_command_ui() { CMD2_ANY_LIST("less", &apply_less); CMD2_ANY_LIST("greater", &apply_greater); CMD2_ANY_LIST("equal", &apply_equal); + CMD2_ANY_LIST("match", &apply_match); CMD2_ANY_VALUE("convert.gm_time", std::bind(&apply_to_time, std::placeholders::_2, 0)); CMD2_ANY_VALUE("convert.gm_date", std::bind(&apply_to_time, std::placeholders::_2, 0x2)); diff --git a/src/core/view.cc b/src/core/view.cc index 842fdbbf..8859f452 100644 --- a/src/core/view.cc +++ b/src/core/view.cc @@ -89,17 +89,21 @@ struct view_downloads_compare : std::binary_function }; struct view_downloads_filter : std::unary_function { - view_downloads_filter(const torrent::Object& cmd) : m_command(cmd) {} + view_downloads_filter(const torrent::Object& cmd, const torrent::Object& cmd2) : m_command(cmd), m_command2(cmd2) {} bool operator () (Download* d1) const { - if (m_command.is_empty()) + return this->evalCmd(m_command, d1) && this->evalCmd(m_command2, d1); + } + + bool evalCmd(const torrent::Object& cmd, Download* d1) const { + if (cmd.is_empty()) return true; try { torrent::Object result; - if (m_command.is_dict_key()) { - // torrent::Object tmp_command = m_command; + if (cmd.is_dict_key()) { + // torrent::Object tmp_command = cmd; // uint32_t flags = tmp_command.flags() & torrent::Object::mask_function; // tmp_command.unset_flags(torrent::Object::mask_function); @@ -109,10 +113,10 @@ struct view_downloads_filter : std::unary_function { // result = rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(), // rpc::make_target(d1)); - result = rpc::commands.call_command(m_command.as_dict_key().c_str(), m_command.as_dict_obj(), rpc::make_target(d1)); + result = rpc::commands.call_command(cmd.as_dict_key().c_str(), cmd.as_dict_obj(), rpc::make_target(d1)); } else { - result = rpc::parse_command_single(rpc::make_target(d1), m_command.as_string()); + result = rpc::parse_command_single(rpc::make_target(d1), cmd.as_string()); } switch (result.type()) { @@ -136,6 +140,7 @@ struct view_downloads_filter : std::unary_function { } const torrent::Object& m_command; + const torrent::Object& m_command2; }; void @@ -262,8 +267,8 @@ View::filter() { return; // Parition the list in two steps so we know which elements changed. - iterator splitVisible = std::stable_partition(begin_visible(), end_visible(), view_downloads_filter(m_filter)); - iterator splitFiltered = std::stable_partition(begin_filtered(), end_filtered(), view_downloads_filter(m_filter)); + iterator splitVisible = std::stable_partition(begin_visible(), end_visible(), view_downloads_filter(m_filter, m_tempFilter)); + iterator splitFiltered = std::stable_partition(begin_filtered(), end_filtered(), view_downloads_filter(m_filter, m_tempFilter)); base_type changed(splitVisible, splitFiltered); iterator splitChanged = changed.begin() + std::distance(splitVisible, end_visible()); @@ -302,7 +307,7 @@ View::filter_download(core::Download* download) { if (itr == base_type::end()) throw torrent::internal_error("View::filter_download(...) could not find download."); - if (view_downloads_filter(m_filter)(download)) { + if (view_downloads_filter(m_filter, m_tempFilter)(download)) { if (itr >= end_visible()) { erase_internal(itr); diff --git a/src/core/view.h b/src/core/view.h index 1196de3e..4f93ab02 100644 --- a/src/core/view.h +++ b/src/core/view.h @@ -124,6 +124,8 @@ public: const torrent::Object& get_filter() const { return m_filter; } void set_filter(const torrent::Object& s) { m_filter = s; } + const torrent::Object& get_temp_filter() const { return m_tempFilter; } + void set_temp_filter(const torrent::Object& s) { m_tempFilter = s; } void set_filter_on_event(const std::string& event); void clear_filter_on(); @@ -172,6 +174,7 @@ private: torrent::Object m_sortCurrent; torrent::Object m_filter; + torrent::Object m_tempFilter; // Temporary view filter (eg: name based filter) torrent::Object m_event_added; torrent::Object m_event_removed; diff --git a/src/core/view_manager.cc b/src/core/view_manager.cc index 355d8809..49a82fd0 100644 --- a/src/core/view_manager.cc +++ b/src/core/view_manager.cc @@ -109,6 +109,14 @@ ViewManager::set_filter(const std::string& name, const torrent::Object& cmd) { (*viewItr)->filter(); } +void +ViewManager::set_temp_filter(const std::string& name, const torrent::Object& cmd) { + iterator viewItr = find_throw(name); + + (*viewItr)->set_temp_filter(cmd); + (*viewItr)->filter(); +} + void ViewManager::set_filter_on(const std::string& name, const filter_args& args) { iterator viewItr = find_throw(name); diff --git a/src/core/view_manager.h b/src/core/view_manager.h index 750d210b..8b44b266 100644 --- a/src/core/view_manager.h +++ b/src/core/view_manager.h @@ -93,6 +93,7 @@ public: void set_sort_current(const std::string& name, const torrent::Object& cmd) { (*find_throw(name))->set_sort_current(cmd); } void set_filter(const std::string& name, const torrent::Object& cmd); + void set_temp_filter(const std::string& name, const torrent::Object& cmd); void set_filter_on(const std::string& name, const filter_args& args); void set_event_added(const std::string& name, const torrent::Object& cmd) { (*find_throw(name))->set_event_added(cmd); } diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index 7a070f62..a8dd3210 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -262,6 +262,10 @@ DownloadList::receive_view_input(Input type) { title = "command"; break; + case INPUT_FILTER: + title = "filter"; + break; + default: throw torrent::internal_error("DownloadList::receive_view_input(...) Invalid input type."); } @@ -325,6 +329,25 @@ DownloadList::receive_exit_input(Input type) { input->str()); break; + case INPUT_FILTER: + if (input->str().empty()) { + control->core()->push_log_std("Clear temporary filter."); + current_view()->set_temp_filter(torrent::Object()); + current_view()->filter(); + } else { + std::string pattern = input->str(); + if (pattern.back() != '$') + pattern = pattern + ".*"; + if (pattern.front() != '^') + pattern = ".*" + pattern; + std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower); + std::string tempFilter = "match={d.name=," + pattern + "}"; + control->core()->push_log_std("Temporary filter: " + pattern); + current_view()->set_temp_filter(tempFilter); + current_view()->filter(); + } + break; + default: throw torrent::internal_error("DownloadList::receive_exit_input(...) Invalid input type."); } @@ -346,6 +369,7 @@ DownloadList::setup_keys() { m_bindings[KEY_ENTER] = std::bind(&DownloadList::receive_view_input, this, INPUT_LOAD_MODIFIED); m_bindings['\x0F'] = std::bind(&DownloadList::receive_view_input, this, INPUT_CHANGE_DIRECTORY); m_bindings['X' - '@'] = std::bind(&DownloadList::receive_view_input, this, INPUT_COMMAND); + m_bindings['F'] = std::bind(&DownloadList::receive_view_input, this, INPUT_FILTER); m_uiArray[DISPLAY_LOG]->bindings()[KEY_LEFT] = m_uiArray[DISPLAY_LOG]->bindings()['B' - '@'] = diff --git a/src/ui/download_list.h b/src/ui/download_list.h index 72ab5ae5..f766e63b 100644 --- a/src/ui/download_list.h +++ b/src/ui/download_list.h @@ -86,7 +86,8 @@ public: INPUT_LOAD_DEFAULT, INPUT_LOAD_MODIFIED, INPUT_CHANGE_DIRECTORY, - INPUT_COMMAND + INPUT_COMMAND, + INPUT_FILTER } Input; DownloadList();