From 199bd1985c97c654783dc22eaaccde30851ed5f3 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Thu, 20 Mar 2008 11:50:57 +0000 Subject: [PATCH] * Added view_event_{added,removed} commands that sets a command to be triggered when a download is added/removed from the specified view. * Minor compiler errors fixed. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@1045 e378c898-3ddf-0310-93e7-cc216c733640 --- doc/rtorrent.1.xml | 24 ++++++++------- src/command_ui.cc | 3 ++ src/core/view.cc | 63 +++++++++++++++++++++++++++++++--------- src/core/view.h | 6 ++++ src/core/view_manager.cc | 14 --------- src/core/view_manager.h | 8 +++-- src/rpc/parse_commands.h | 9 ++++++ src/rpc/xmlrpc.cc | 4 ++- 8 files changed, 90 insertions(+), 41 deletions(-) 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