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}"
This commit is contained in:
Toff
2016-10-31 12:19:41 +01:00
parent 226e670dec
commit e15270271d
9 changed files with 113 additions and 10 deletions
+3
View File
@@ -99,6 +99,9 @@ Delete the file the torrent is tied to, and clear the association.
.TP .TP
\fBI\fR \fBI\fR
Toggle whether torrent ignores ratio settings. Toggle whether torrent ignores ratio settings.
.TP
\fBF\fR
Add a temporary name based regex filter to the current view.
.SS "DOWNLOAD VIEW KEYS" .SS "DOWNLOAD VIEW KEYS"
.TP .TP
\fB->\fR \fB->\fR
+7
View File
@@ -225,6 +225,13 @@ Toggle whether torrent ignores ratio settings.
</para></listitem> </para></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term>F</term>
<listitem><para>
Add a temporary name based regex filter to the current view.
</para></listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect2> </refsect2>
+51
View File
@@ -39,6 +39,8 @@
#include <sys/types.h> #include <sys/types.h>
#include <ctime> #include <ctime>
#include <regex>
#include <rak/functional.h> #include <rak/functional.h>
#include <rak/functional_fun.h> #include <rak/functional_fun.h>
@@ -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; 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 torrent::Object
apply_to_time(const torrent::Object& rawArgs, int flags) { apply_to_time(const torrent::Object& rawArgs, int flags) {
std::tm *u; 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.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.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.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2));
CMD2_ANY_LIST("view.sort", std::bind(&apply_view_sort, 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("less", &apply_less);
CMD2_ANY_LIST("greater", &apply_greater); CMD2_ANY_LIST("greater", &apply_greater);
CMD2_ANY_LIST("equal", &apply_equal); 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_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)); CMD2_ANY_VALUE("convert.gm_date", std::bind(&apply_to_time, std::placeholders::_2, 0x2));
+14 -9
View File
@@ -89,17 +89,21 @@ struct view_downloads_compare : std::binary_function<Download*, Download*, bool>
}; };
struct view_downloads_filter : std::unary_function<Download*, bool> { struct view_downloads_filter : std::unary_function<Download*, bool> {
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 { 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; return true;
try { try {
torrent::Object result; torrent::Object result;
if (m_command.is_dict_key()) { if (cmd.is_dict_key()) {
// torrent::Object tmp_command = m_command; // torrent::Object tmp_command = cmd;
// uint32_t flags = tmp_command.flags() & torrent::Object::mask_function; // uint32_t flags = tmp_command.flags() & torrent::Object::mask_function;
// tmp_command.unset_flags(torrent::Object::mask_function); // tmp_command.unset_flags(torrent::Object::mask_function);
@@ -109,10 +113,10 @@ struct view_downloads_filter : std::unary_function<Download*, bool> {
// result = rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(), // result = rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(),
// rpc::make_target(d1)); // 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 { } 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()) { switch (result.type()) {
@@ -136,6 +140,7 @@ struct view_downloads_filter : std::unary_function<Download*, bool> {
} }
const torrent::Object& m_command; const torrent::Object& m_command;
const torrent::Object& m_command2;
}; };
void void
@@ -262,8 +267,8 @@ View::filter() {
return; return;
// Parition the list in two steps so we know which elements changed. // 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 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)); iterator splitFiltered = std::stable_partition(begin_filtered(), end_filtered(), view_downloads_filter(m_filter, m_tempFilter));
base_type changed(splitVisible, splitFiltered); base_type changed(splitVisible, splitFiltered);
iterator splitChanged = changed.begin() + std::distance(splitVisible, end_visible()); iterator splitChanged = changed.begin() + std::distance(splitVisible, end_visible());
@@ -302,7 +307,7 @@ View::filter_download(core::Download* download) {
if (itr == base_type::end()) if (itr == base_type::end())
throw torrent::internal_error("View::filter_download(...) could not find download."); 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()) { if (itr >= end_visible()) {
erase_internal(itr); erase_internal(itr);
+3
View File
@@ -124,6 +124,8 @@ public:
const torrent::Object& get_filter() const { return m_filter; } const torrent::Object& get_filter() const { return m_filter; }
void set_filter(const torrent::Object& s) { m_filter = s; } 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 set_filter_on_event(const std::string& event);
void clear_filter_on(); void clear_filter_on();
@@ -172,6 +174,7 @@ private:
torrent::Object m_sortCurrent; torrent::Object m_sortCurrent;
torrent::Object m_filter; torrent::Object m_filter;
torrent::Object m_tempFilter; // Temporary view filter (eg: name based filter)
torrent::Object m_event_added; torrent::Object m_event_added;
torrent::Object m_event_removed; torrent::Object m_event_removed;
+8
View File
@@ -109,6 +109,14 @@ ViewManager::set_filter(const std::string& name, const torrent::Object& cmd) {
(*viewItr)->filter(); (*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 void
ViewManager::set_filter_on(const std::string& name, const filter_args& args) { ViewManager::set_filter_on(const std::string& name, const filter_args& args) {
iterator viewItr = find_throw(name); iterator viewItr = find_throw(name);
+1
View File
@@ -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_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_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_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); } void set_event_added(const std::string& name, const torrent::Object& cmd) { (*find_throw(name))->set_event_added(cmd); }
+24
View File
@@ -262,6 +262,10 @@ DownloadList::receive_view_input(Input type) {
title = "command"; title = "command";
break; break;
case INPUT_FILTER:
title = "filter";
break;
default: default:
throw torrent::internal_error("DownloadList::receive_view_input(...) Invalid input type."); throw torrent::internal_error("DownloadList::receive_view_input(...) Invalid input type.");
} }
@@ -325,6 +329,25 @@ DownloadList::receive_exit_input(Input type) {
input->str()); input->str());
break; 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: default:
throw torrent::internal_error("DownloadList::receive_exit_input(...) Invalid input type."); 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[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['\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['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()[KEY_LEFT] =
m_uiArray[DISPLAY_LOG]->bindings()['B' - '@'] = m_uiArray[DISPLAY_LOG]->bindings()['B' - '@'] =
+2 -1
View File
@@ -86,7 +86,8 @@ public:
INPUT_LOAD_DEFAULT, INPUT_LOAD_DEFAULT,
INPUT_LOAD_MODIFIED, INPUT_LOAD_MODIFIED,
INPUT_CHANGE_DIRECTORY, INPUT_CHANGE_DIRECTORY,
INPUT_COMMAND INPUT_COMMAND,
INPUT_FILTER
} Input; } Input;
DownloadList(); DownloadList();