diff --git a/doc/rtorrent.1.xml b/doc/rtorrent.1.xml
index f08fc7dd..a7b2910b 100644
--- a/doc/rtorrent.1.xml
+++ b/doc/rtorrent.1.xml
@@ -808,21 +808,23 @@ number of criteria, including zero, from the following:
-
- view_filter = name,...
-
+
+
+
-Set a list of filter to apply when new new downloads are added and
-when view_sort is called. All filters must
-match for the download to be included.
+
+
+
-
-stopped, started,
-complete, incomplete,
-
-
+
+
+
+
+
+
+
key_layout = qwerty|azerty|qwertz|dvorak
diff --git a/src/command_ui.cc b/src/command_ui.cc
index 805d37dc..b180d064 100644
--- a/src/command_ui.cc
+++ b/src/command_ui.cc
@@ -393,6 +393,9 @@ initialize_command_ui() {
ADD_COMMAND_LIST("view_sort_new", rak::bind_ptr_fn(&apply_view_cfilter, &core::ViewManager::set_sort_new));
ADD_COMMAND_LIST("view_sort_current", rak::bind_ptr_fn(&apply_view_cfilter, &core::ViewManager::set_sort_current));
+ ADD_COMMAND_LIST("view_event_added", rak::bind_ptr_fn(&apply_view_cfilter, &core::ViewManager::set_event_added));
+ ADD_COMMAND_LIST("view_event_removed", rak::bind_ptr_fn(&apply_view_cfilter, &core::ViewManager::set_event_removed));
+
// Move.
ADD_ANY_NONE("print", rak::ptr_fn(&apply_print));
diff --git a/src/core/view.cc b/src/core/view.cc
index 52fe1686..74fec0b0 100644
--- a/src/core/view.cc
+++ b/src/core/view.cc
@@ -171,12 +171,34 @@ View::sort() {
void
View::filter() {
- iterator split = std::stable_partition(base_type::begin(), base_type::end(), view_downloads_filter(m_filter));
+ // 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));
- m_size = position(split);
+ base_type changed(splitVisible, splitFiltered);
+ iterator splitChanged = changed.begin() + std::distance(splitVisible, end_visible());
+
+ m_size = std::distance(begin(), std::copy(splitChanged, changed.end(), splitVisible));
+ std::copy(changed.begin(), splitChanged, begin_filtered());
- // Fix focus
+ // Fix this...
m_focus = std::min(m_focus, m_size);
+
+ // The commands are allowed to remove itself from or change View
+ // sorting since the commands are being called on the 'changed'
+ // vector. But this will cause undefined behavior if elements are
+ // removed.
+ //
+ // Consider if View should lock itself (and throw) if erase events
+ // are triggered on a Download in the 'changed' list. This can be
+ // done by using a base_type* member variable, and making sure we
+ // set the elements to NULL as we trigger commands on them. Or
+ // perhaps always clear them, thus not throwing anything.
+ if (!m_eventRemoved.empty())
+ std::for_each(changed.begin(), splitChanged, rak::bind2nd(std::ptr_fun(&rpc::parse_command_multiple_d_nothrow), m_eventRemoved));
+
+ if (!m_eventAdded.empty())
+ std::for_each(splitChanged, changed.end(), rak::bind2nd(std::ptr_fun(&rpc::parse_command_multiple_d_nothrow), m_eventAdded));
}
void
@@ -225,10 +247,13 @@ View::received(core::Download* download, int event) {
if (itr != base_type::end())
throw torrent::internal_error("View::received(..., SLOTS_INSERT) already inserted.");
- if (view_downloads_filter(m_filter)(download))
+ if (view_downloads_filter(m_filter)(download)) {
insert_visible(download);
- else
+ rpc::parse_command_multiple_d_nothrow(download, m_eventAdded);
+
+ } else {
base_type::insert(end_filtered(), download);
+ }
if (m_focus > m_size)
throw torrent::internal_error("View::received(...) m_focus > m_size.");
@@ -237,6 +262,8 @@ View::received(core::Download* download, int event) {
case DownloadList::SLOTS_ERASE:
erase(itr);
+ rpc::parse_command_multiple_d_nothrow(download, m_eventRemoved);
+
break;
default:
@@ -245,20 +272,30 @@ View::received(core::Download* download, int event) {
if (view_downloads_filter(m_filter)(download)) {
- // Erase even if it is in visible so that the download is
- // re-sorted.
- //
- // Do we really want to do this?
- erase(itr);
- insert_visible(download);
+ if (itr >= end_visible()) {
+ // Erase even if it is in visible so that the download is
+ // re-sorted.
+ //
+ // This isn't the best solution...
+ erase(itr);
+ insert_visible(download);
+
+ rpc::parse_command_multiple_d_nothrow(download, m_eventAdded);
+
+ } else {
+ // Should we really sort it here, or just return?
+ erase(itr);
+ insert_visible(download);
+ }
} else {
-
- if (itr >= begin_filtered())
+ if (itr >= end_visible())
return;
erase(itr);
base_type::push_back(download);
+
+ rpc::parse_command_multiple_d_nothrow(download, m_eventRemoved);
}
break;
diff --git a/src/core/view.h b/src/core/view.h
index f36b3f26..dab8795e 100644
--- a/src/core/view.h
+++ b/src/core/view.h
@@ -118,6 +118,9 @@ public:
void clear_filter_on();
+ void set_event_added(const std::string& cmd) { m_eventAdded = cmd; }
+ void set_event_removed(const std::string& cmd) { m_eventRemoved = cmd; }
+
// The time of the last change to the view, semantics of this is
// user-dependent. Used by f.ex. ViewManager to decide if it should
// sort and/or filter a view.
@@ -159,6 +162,9 @@ private:
std::string m_filter;
+ std::string m_eventAdded;
+ std::string m_eventRemoved;
+
rak::timer m_lastChanged;
signal_type m_signalChanged;
};
diff --git a/src/core/view_manager.cc b/src/core/view_manager.cc
index 34eaf8e4..1755c6b9 100644
--- a/src/core/view_manager.cc
+++ b/src/core/view_manager.cc
@@ -104,20 +104,6 @@ ViewManager::sort(const std::string& name, uint32_t timeout) {
(*viewItr)->sort();
}
-void
-ViewManager::set_sort_new(const std::string& name, const std::string& cmd) {
- iterator viewItr = find_throw(name);
-
- (*viewItr)->set_sort_new(cmd);
-}
-
-void
-ViewManager::set_sort_current(const std::string& name, const std::string& cmd) {
- iterator viewItr = find_throw(name);
-
- (*viewItr)->set_sort_current(cmd);
-}
-
void
ViewManager::set_filter(const std::string& name, const std::string& cmd) {
iterator viewItr = find_throw(name);
diff --git a/src/core/view_manager.h b/src/core/view_manager.h
index cff38233..c0222318 100644
--- a/src/core/view_manager.h
+++ b/src/core/view_manager.h
@@ -87,12 +87,16 @@ public:
// Find a better name for 'timeout'.
void sort(const std::string& name, uint32_t timeout = 0);
- void set_sort_new(const std::string& name, const std::string& cmd);
- void set_sort_current(const std::string& name, const std::string& cmd);
+ // These could be moved to where the command is implemented.
+ void set_sort_new(const std::string& name, const std::string& cmd) { (*find_throw(name))->set_sort_new(cmd); }
+ void set_sort_current(const std::string& name, const std::string& cmd) { (*find_throw(name))->set_sort_current(cmd); }
void set_filter(const std::string& name, const std::string& cmd);
void set_filter_on(const std::string& name, const filter_args& args);
+ void set_event_added(const std::string& name, const std::string& cmd) { (*find_throw(name))->set_event_added(cmd); }
+ void set_event_removed(const std::string& name, const std::string& cmd) { (*find_throw(name))->set_event_removed(cmd); }
+
private:
DownloadList* m_list;
};
diff --git a/src/rpc/parse_commands.h b/src/rpc/parse_commands.h
index 6a6565e0..11e9140d 100644
--- a/src/rpc/parse_commands.h
+++ b/src/rpc/parse_commands.h
@@ -81,6 +81,15 @@ parse_command_single_std(const std::string& cmd) {
parse_command(make_target(), cmd.c_str(), cmd.c_str() + cmd.size());
}
+inline void
+parse_command_multiple_d_nothrow(core::Download* download, const std::string& cmd) {
+ try {
+ parse_command_multiple(make_target(download), cmd.c_str(), cmd.c_str() + cmd.size());
+ } catch (torrent::input_error& e) {
+ // Log?
+ }
+}
+
inline void
parse_command_d_single_std(core::Download* download, const std::string& cmd) {
parse_command(make_target(download), cmd.c_str(), cmd.c_str() + cmd.size());
diff --git a/src/rpc/xmlrpc.cc b/src/rpc/xmlrpc.cc
index be06b59f..a7014f70 100644
--- a/src/rpc/xmlrpc.cc
+++ b/src/rpc/xmlrpc.cc
@@ -137,6 +137,7 @@ xmlrpc_to_target(xmlrpc_env* env, xmlrpc_value* value) {
switch (xmlrpc_value_type(value)) {
case XMLRPC_TYPE_STRING:
+ {
const char* str;
xmlrpc_read_string(env, value, &str);
@@ -209,6 +210,7 @@ xmlrpc_to_target(xmlrpc_env* env, xmlrpc_value* value) {
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Invalid index.");
return target;
+ }
default:
return rpc::make_target();
@@ -537,7 +539,7 @@ void XmlRpc::set_dialect(__UNUSED int dialect) {}
bool XmlRpc::process(__UNUSED const char* inBuffer, __UNUSED uint32_t length, __UNUSED slot_write slotWrite) { return false; }
int64_t XmlRpc::size_limit() { return 0; }
-void XmlRpc::set_size_limit(int64_t size) {}
+void XmlRpc::set_size_limit(uint64_t size) {}
#endif