From 8ae5e51a0fb4791f4b72bfbb10dc7be73c743ac5 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Wed, 26 Apr 2006 21:23:37 +0000 Subject: [PATCH] * Change :rtorrent:state to an integer. * Split up DownloadList::insert to get proper ordering of slot map calls and variable initialization. * Added filtering to ViewDownloads. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@675 e378c898-3ddf-0310-93e7-cc216c733640 --- src/core/download.cc | 2 +- src/core/download_factory.cc | 50 ++++++++++++++++++------------- src/core/download_list.cc | 22 +++++++++----- src/core/download_list.h | 5 +++- src/core/view_downloads.cc | 57 +++++++++++++++++++++++++++++++----- src/core/view_downloads.h | 53 +++++++++++++++++++++++---------- src/core/view_manager.cc | 55 ++++++++++++++++++++++++++++++---- src/core/view_manager.h | 11 ++++++- src/main.cc | 5 ++-- src/option_handler_rules.cc | 23 +++++++++++++++ src/ui/root.cc | 5 ++-- 11 files changed, 224 insertions(+), 64 deletions(-) diff --git a/src/core/download.cc b/src/core/download.cc index 75a13986..966b6939 100644 --- a/src/core/download.cc +++ b/src/core/download.cc @@ -70,7 +70,7 @@ Download::Download(download_type d) : m_variables.insert("connection_leech", new utils::VariableAny(connection_type_to_string(download_type::CONNECTION_LEECH))); m_variables.insert("connection_seed", new utils::VariableAny(connection_type_to_string(download_type::CONNECTION_SEED))); - m_variables.insert("state", new utils::VariableObject(bencode(), "rtorrent", "state", torrent::Object::TYPE_STRING)); + m_variables.insert("state", new utils::VariableObject(bencode(), "rtorrent", "state", torrent::Object::TYPE_VALUE)); m_variables.insert("tied_to_file", new utils::VariableObject(bencode(), "rtorrent", "tied_to_file", torrent::Object::TYPE_STRING)); // The "state_changed" variable is required to be a valid unix time diff --git a/src/core/download_factory.cc b/src/core/download_factory.cc index 77489bdb..3b4ea7c2 100644 --- a/src/core/download_factory.cc +++ b/src/core/download_factory.cc @@ -141,16 +141,16 @@ DownloadFactory::receive_success() { if (m_stream == NULL) throw torrent::client_error("DownloadFactory::receive_success() called on an object with m_stream == NULL"); - DownloadList::iterator itr = m_manager->download_list().insert(m_stream, m_printLog); + Download* download = m_manager->download_list().create(m_stream, m_printLog); - if (itr == m_manager->download_list().end()) { + if (download == NULL) { // core::Manager should already have added the error message to // the log. m_slotFinished(); return; } - torrent::Object* root = (*itr)->bencode(); + torrent::Object* root = download->bencode(); if (!m_session) { // We only allow session torrents to keep their @@ -164,37 +164,46 @@ DownloadFactory::receive_success() { torrent::Object* rtorrent = &root->get_key("rtorrent"); - initialize_rtorrent(*itr, rtorrent); + initialize_rtorrent(download, rtorrent); + + if (m_manager->download_list().insert(download) == m_manager->download_list().end()) { + // ATM doesn't really ever get here. + delete download; + + m_slotFinished(); + return; + } // Move to 'rtorrent'. - (*itr)->variable()->set("connection_leech", m_variables.get("connection_leech")); - (*itr)->variable()->set("connection_seed", m_variables.get("connection_seed")); - (*itr)->variable()->set("min_peers", control->variable()->get("min_peers")); - (*itr)->variable()->set("max_peers", control->variable()->get("max_peers")); - (*itr)->variable()->set("max_uploads", control->variable()->get("max_uploads")); + download->variable()->set("connection_leech", m_variables.get("connection_leech")); + download->variable()->set("connection_seed", m_variables.get("connection_seed")); + download->variable()->set("min_peers", control->variable()->get("min_peers")); + download->variable()->set("max_peers", control->variable()->get("max_peers")); + download->variable()->set("max_uploads", control->variable()->get("max_uploads")); if (!control->variable()->get_value("use_udp_trackers")) - (*itr)->enable_udp_trackers(false); + download->enable_udp_trackers(false); if (m_session) { if (!rtorrent->has_key_string("directory")) - (*itr)->variable()->set("directory", m_variables.get("directory")); + download->variable()->set("directory", m_variables.get("directory")); else - (*itr)->variable()->set("directory", rtorrent->get_key("directory")); + download->variable()->set("directory", rtorrent->get_key("directory")); - if ((*itr)->variable()->get_string("state") == "started") - m_manager->download_list().resume(*itr); + if (download->variable()->get_value("state") == 1) + m_manager->download_list().resume(download); } else { - (*itr)->variable()->set("directory", m_variables.get("directory")); + download->variable()->set("directory", m_variables.get("directory")); if (m_variables.get("tied_to_file").as_value()) - (*itr)->variable()->set("tied_to_file", m_uri); + download->variable()->set("tied_to_file", m_uri); + // Use the state thingie here, move below. if (m_start) - m_manager->download_list().start(*itr); + m_manager->download_list().start(download); - m_manager->download_store().save(*itr); + m_manager->download_store().save(download); } m_slotFinished(); @@ -216,9 +225,8 @@ DownloadFactory::receive_failed(const std::string& msg) { void DownloadFactory::initialize_rtorrent(Download* download, torrent::Object* rtorrent) { - if (!rtorrent->has_key_string("state") || - (rtorrent->get_key("state").as_string() != "stopped" && rtorrent->get_key("state").as_string() != "started")) { - rtorrent->insert_key("state", "stopped"); + if (!rtorrent->has_key_value("state") || rtorrent->get_key("state").as_value() > 1) { + rtorrent->insert_key("state", (int64_t)m_start); rtorrent->insert_key("state_changed", cachedTime.seconds()); } else if (!rtorrent->has_key_value("state_changed") || diff --git a/src/core/download_list.cc b/src/core/download_list.cc index 51c192ec..1e0404a9 100644 --- a/src/core/download_list.cc +++ b/src/core/download_list.cc @@ -64,13 +64,12 @@ struct download_list_call { Download* m_download; }; -DownloadList::iterator -DownloadList::insert(std::istream* str, bool printLog) { +Download* +DownloadList::create(std::istream* str, bool printLog) { torrent::Object* object = new torrent::Object; torrent::Download download; try { - *str >> *object; // Catch, delete. @@ -85,16 +84,25 @@ DownloadList::insert(std::istream* str, bool printLog) { if (printLog) control->core()->push_log(e.what()); - return end(); + return NULL; } - iterator itr = Base::insert(end(), new Download(download)); + // There's no non-critical exceptions that should be throwable by + // the ctor, so don't catch. + return new Download(download); +} + +DownloadList::iterator +DownloadList::insert(Download* d) { + iterator itr = Base::insert(end(), d); try { (*itr)->download()->signal_download_done(sigc::bind(sigc::mem_fun(*this, &DownloadList::finished), *itr)); std::for_each(m_slotMapInsert.begin(), m_slotMapInsert.end(), download_list_call(*itr)); } catch (torrent::local_error& e) { + // Should perhaps relax this, just print an error and remove the + // downloads? throw torrent::internal_error("Caught during DownloadList::insert part 2: " + std::string(e.what())); } @@ -152,14 +160,14 @@ DownloadList::close(Download* d) { void DownloadList::start(Download* d) { - d->variable()->set("state", "started"); + d->variable()->set("state", (int64_t)1); resume(d); } void DownloadList::stop(Download* d) { - d->variable()->set("state", "stopped"); + d->variable()->set("state", (int64_t)0); pause(d); } diff --git a/src/core/download_list.h b/src/core/download_list.h index 3abf4e8d..577a2e6d 100644 --- a/src/core/download_list.h +++ b/src/core/download_list.h @@ -73,7 +73,10 @@ public: ~DownloadList() { clear(); } - iterator insert(std::istream* str, bool printLog); + // Might move this to DownloadFactory. + Download* create(std::istream* str, bool printLog); + + iterator insert(Download* d); void erase(Download* d); iterator erase(iterator itr); diff --git a/src/core/view_downloads.cc b/src/core/view_downloads.cc index eef8a581..3ab7dd71 100644 --- a/src/core/view_downloads.cc +++ b/src/core/view_downloads.cc @@ -73,6 +73,7 @@ ViewDownloads::initialize(const std::string& name, core::DownloadList* list) { m_name = name; m_list = list; + m_size = 0; m_focus = 0; std::for_each(m_list->begin(), m_list->end(), std::bind1st(std::mem_fun(&ViewDownloads::received_insert), this)); @@ -101,6 +102,9 @@ ViewDownloads::prev_focus() { m_signalChanged.emit(); } +// Need to use wrapper-functors so it will properly call the virtual +// functions. + // Also add focus thingie here? struct view_downloads_compare : std::binary_function { view_downloads_compare(const ViewDownloads::sort_list& s) : m_sort(s) {} @@ -120,6 +124,22 @@ struct view_downloads_compare : std::binary_function const ViewDownloads::sort_list& m_sort; }; +struct view_downloads_filter : std::unary_function { + view_downloads_filter(const ViewDownloads::filter_list& s) : m_filter(s) {} + + bool operator () (Download* d1) const { + for (ViewDownloads::filter_list::const_iterator itr = m_filter.begin(), last = m_filter.end(); itr != last; ++itr) + if (!(**itr)(d1)) + return false; + + // The default filter action is to return true, to not filter the + // download out. + return true; + } + + const ViewDownloads::filter_list& m_filter; +}; + void ViewDownloads::sort() { Download* curFocus = focus() != end() ? *focus() : NULL; @@ -132,11 +152,32 @@ ViewDownloads::sort() { } void -ViewDownloads::received_insert(core::Download* d) { - iterator itr = std::find_if(begin(), end(), std::bind1st(view_downloads_compare(m_sortNew), d)); +ViewDownloads::filter() { + iterator split = std::stable_partition(base_type::begin(), base_type::end(), view_downloads_filter(m_filter)); - if (m_focus >= position(itr)) - m_focus++; + m_size = position(split); + + // Fix focus + m_focus = std::min(m_focus, m_size); +} + +void +ViewDownloads::received_insert(core::Download* d) { + // Chagne according to filtered/not. + iterator itr; + + if (view_downloads_filter(m_filter)(d)) { + itr = std::find_if(begin(), end(), std::bind1st(view_downloads_compare(m_sortNew), d)); + + m_size++; + m_focus += (m_focus >= position(itr)); + + } else { + itr = end_filtered(); + } + + if (m_focus > m_size) + throw torrent::internal_error("ViewDownloads::received_insert(...) m_focus > m_size."); base_type::insert(itr, d); m_signalChanged.emit(); @@ -144,13 +185,13 @@ ViewDownloads::received_insert(core::Download* d) { void ViewDownloads::received_erase(core::Download* d) { - iterator itr = std::find(begin(), end(), d); + iterator itr = std::find(begin(), end_filtered(), d); - if (itr == end()) + if (itr == end_filtered()) return; - if (m_focus > position(itr)) - m_focus--; + m_size -= (itr < end()); + m_focus -= (m_focus > position(itr)); base_type::erase(itr); m_signalChanged.emit(); diff --git a/src/core/view_downloads.h b/src/core/view_downloads.h index 15ddbb84..acbdfaa7 100644 --- a/src/core/view_downloads.h +++ b/src/core/view_downloads.h @@ -42,6 +42,11 @@ // // Do we want to be able to modify the underlying DownloadList from // here? +// +// ViewDownloads::m_size indicates the number of Download's that +// remain visible, e.g. has not been filtered out. The Download's that +// were filtered are still in the underlying vector, but cannot be +// accessed through the normal stl container functions. #ifndef RTORRENT_CORE_VIEW_DOWNLOADS_H #define RTORRENT_CORE_VIEW_DOWNLOADS_H @@ -57,12 +62,14 @@ namespace core { class Download; class DownloadList; class ViewSort; +class ViewFilter; class ViewDownloads : public std::vector { public: - typedef std::vector base_type; - typedef sigc::signal0 signal_type; - typedef std::vector sort_list; + typedef std::vector base_type; + typedef sigc::signal0 signal_type; + typedef std::vector sort_list; + typedef std::vector filter_list; using base_type::iterator; using base_type::const_iterator; @@ -72,12 +79,7 @@ public: using base_type::size_type; using base_type::begin; - using base_type::end; using base_type::rbegin; - using base_type::rend; - - using base_type::empty; - using base_type::size; ViewDownloads() {} ~ViewDownloads(); @@ -86,6 +88,18 @@ public: const std::string& name() const { return m_name; } + bool empty() const { return m_size == 0; } + size_type size() const { return m_size; } + + // Perhaps this should be renamed? + iterator end() { return begin() + m_size; } + const_iterator end() const { return begin() + m_size; } + +// using base_type::rend; + + iterator end_filtered() { return base_type::end(); } + const_iterator end_filtered() const { return base_type::end(); } + iterator focus() { return begin() + m_focus; } const_iterator focus() const { return begin() + m_focus; } void set_focus(iterator itr) { m_focus = position(itr); m_signalChanged.emit(); } @@ -98,6 +112,10 @@ public: void set_sort_new(const sort_list& s) { m_sortNew = s; } void set_sort_current(const sort_list& s) { m_sortCurrent = s; } + // Need to explicity trigger filtering. + void filter(); + void set_filter(const filter_list& s) { m_filter = s; } + // Don't connect any slots until after initialize else it get's // triggered when adding the Download's in DownloadList. signal_type& signal_changed() { return m_signalChanged; } @@ -109,18 +127,22 @@ private: void received_insert(core::Download* d); void received_erase(core::Download* d); - size_type position(const_iterator itr) const { return (size_type)(itr - begin()); } + size_type position(const_iterator itr) const { return itr - begin(); } // An received thing for changed status so we can sort and filter. std::string m_name; core::DownloadList* m_list; + + size_type m_size; size_type m_focus; sort_list m_sortNew; sort_list m_sortCurrent; + filter_list m_filter; + // Timer, last changed. signal_type m_signalChanged; @@ -130,15 +152,16 @@ class ViewSort { public: virtual ~ViewSort() {} - // Add various extra stuff here, like bool flags for can sort new, - // normal etc. - - // How to take focus into account? - //virtual bool filter(Download* d1) const { return true; } - virtual bool less(Download* d1, Download* d2) const = 0; }; +class ViewFilter { +public: + virtual ~ViewFilter() {} + + virtual bool operator () (Download* d1) const = 0; +}; + } #endif diff --git a/src/core/view_manager.cc b/src/core/view_manager.cc index e9cbb6fc..6c890a01 100644 --- a/src/core/view_manager.cc +++ b/src/core/view_manager.cc @@ -95,6 +95,22 @@ private: ViewSort* m_sort; }; +class ViewFilterVariableValue : public ViewFilter { +public: + ViewFilterVariableValue(const std::string& name, torrent::Object::value_type v) : + m_name(name), m_value(v) {} + + virtual bool operator () (Download* d1) const { + return d1->variable()->get_value(m_name) == m_value; + } + +private: + std::string m_name; + torrent::Object::value_type m_value; +}; + +// Really need to implement a factory and allow options in the sort +// statements. ViewManager::ViewManager(DownloadList* dl) : m_list(dl) { @@ -103,11 +119,13 @@ ViewManager::ViewManager(DownloadList* dl) : m_sort["name"] = new ViewSortName(); m_sort["name_reverse"] = new ViewSortReverse(new ViewSortName()); - m_sort["started"] = new ViewSortVariable("state", "started"); - m_sort["stopped"] = new ViewSortVariable("state", "stopped"); + m_sort["stopped"] = new ViewSortVariableValue("state"); + m_sort["started"] = new ViewSortReverse(new ViewSortVariableValue("state")); m_sort["state_changed"] = new ViewSortVariableValue("state_changed"); m_sort["state_changed_reverse"] = new ViewSortReverse(new ViewSortVariableValue("state_changed")); + + m_filter["started"] = new ViewFilterVariableValue("state", 1); } void @@ -145,7 +163,7 @@ ViewManager::find_throw(const std::string& name) { } inline ViewManager::sort_list -ViewManager::build_list(const sort_args& args) { +ViewManager::build_sort_list(const sort_args& args) { ViewDownloads::sort_list sortList; sortList.reserve(args.size()); @@ -165,6 +183,9 @@ void ViewManager::sort(const std::string& name) { iterator viewItr = find_throw(name); + // Should we rename sort, or add a seperate function? + (*viewItr)->filter(); + (*viewItr)->sort(); } @@ -172,14 +193,38 @@ void ViewManager::set_sort_new(const std::string& name, const sort_args& sort) { iterator viewItr = find_throw(name); - (*viewItr)->set_sort_new(build_list(sort)); + (*viewItr)->set_sort_new(build_sort_list(sort)); } void ViewManager::set_sort_current(const std::string& name, const sort_args& sort) { iterator viewItr = find_throw(name); - (*viewItr)->set_sort_current(build_list(sort)); + (*viewItr)->set_sort_current(build_sort_list(sort)); +} + +inline ViewManager::filter_list +ViewManager::build_filter_list(const filter_args& args) { + ViewDownloads::filter_list filterList; + filterList.reserve(args.size()); + + for (filter_args::const_iterator itr = args.begin(), last = args.end(); itr != last; ++itr) { + filter_map::const_iterator filterItr = m_filter.find(*itr); + + if (filterItr == m_filter.end()) + throw torrent::input_error("Invalid filtering identifier."); + + filterList.push_back(filterItr->second); + } + + return filterList; +} + +void +ViewManager::set_filter(const std::string& name, const filter_args& args) { + iterator viewItr = find_throw(name); + + (*viewItr)->set_filter(build_filter_list(args)); } } diff --git a/src/core/view_manager.h b/src/core/view_manager.h index c7c04aab..cfa5e42c 100644 --- a/src/core/view_manager.h +++ b/src/core/view_manager.h @@ -51,10 +51,15 @@ class ViewSort; class ViewManager : public rak::unordered_vector { public: typedef rak::unordered_vector base_type; + typedef std::map sort_map; typedef ViewDownloads::sort_list sort_list; typedef std::list sort_args; + typedef std::map filter_map; + typedef ViewDownloads::filter_list filter_list; + typedef std::list filter_args; + using base_type::iterator; using base_type::const_iterator; using base_type::reverse_iterator; @@ -92,12 +97,16 @@ public: void set_sort_new(const std::string& name, const sort_args& sort); void set_sort_current(const std::string& name, const sort_args& sort); + void set_filter(const std::string& name, const filter_args& args); + private: - inline sort_list build_list(const sort_args& args); + inline sort_list build_sort_list(const sort_args& args); + inline filter_list build_filter_list(const sort_args& args); DownloadList* m_list; sort_map m_sort; + filter_map m_filter; }; } diff --git a/src/main.cc b/src/main.cc index 1b379692..1ec7427a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -163,6 +163,7 @@ main(int argc, char** argv) { initialize_option_handler(control); control->variable()->process_command("view_add = main"); + control->variable()->process_command("view_filter = main,started"); control->variable()->process_command("view_sort_new = main,name"); control->variable()->process_command("view_sort_current = main,name"); @@ -170,8 +171,8 @@ main(int argc, char** argv) { control->variable()->process_command("view_add = scheduler"); control->variable()->process_command("view_sort_new = scheduler,state_changed"); // add started? control->variable()->process_command("view_sort_current = scheduler,state_changed"); - -// control->variable()->process_command("schedule = scheduler,10,10,download_scheduler="); + + // control->variable()->process_command("schedule = scheduler,10,10,download_scheduler="); // Move env and go through "try_import". // if (!control->variable()->process_file("~/.rtorrent.rc")) diff --git a/src/option_handler_rules.cc b/src/option_handler_rules.cc index e3b8b6b0..666ac0f2 100644 --- a/src/option_handler_rules.cc +++ b/src/option_handler_rules.cc @@ -225,6 +225,27 @@ apply_view_sort_new(Control* control, const std::string& arg) { control->view_manager()->set_sort_new(name, sortArgs); } +void +apply_view_filter(Control* control, const std::string& arg) { + rak::split_iterator_t itr = rak::split_iterator(arg, ','); + + std::string name = rak::trim(*itr); + + if (name.empty()) + throw torrent::input_error("First argument must be a string."); + + core::ViewManager::filter_args filterArgs; + + while (++itr != rak::split_iterator(arg)) { + filterArgs.push_back(rak::trim(*itr)); + + if (filterArgs.back().empty()) + throw torrent::input_error("One of the arguments is empty."); + } + + control->view_manager()->set_filter(name, filterArgs); +} + void apply_download_scheduler(core::Scheduler* scheduler, __UNUSED const std::string& arg) { scheduler->update(); @@ -285,6 +306,8 @@ initialize_option_handler(Control* c) { variables->insert("view_sort_new", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_view_sort_new, c))); variables->insert("view_sort_current", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_view_sort_current, c))); + variables->insert("view_filter", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_view_filter, c))); + variables->insert("schedule", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::mem_fn(c->command_scheduler(), &CommandScheduler::parse))); variables->insert("schedule_remove", new utils::VariableStringSlot(rak::value_fn(std::string()), diff --git a/src/ui/root.cc b/src/ui/root.cc index 97f57703..da4efea1 100644 --- a/src/ui/root.cc +++ b/src/ui/root.cc @@ -108,9 +108,8 @@ Root::setup_keys() { m_bindings['D'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 50); m_bindings['C'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -50); - //m_bindings[KEY_RESIZE] = sigc::mem_fun(*m_control->display(), &display::Manager::adjust_layout); - m_bindings['\x0C'] = sigc::mem_fun(m_control->display(), &display::Manager::force_redraw); - m_bindings['\x11'] = sigc::mem_fun(m_control, &Control::receive_normal_shutdown); + m_bindings['\x0C'] = sigc::mem_fun(m_control->display(), &display::Manager::force_redraw); // ^L + m_bindings['\x11'] = sigc::mem_fun(m_control, &Control::receive_normal_shutdown); // ^Q } void