From 6d07bf3540cfc1013af5890b5a722e1949a17399 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Mon, 8 May 2006 23:42:03 +0000 Subject: [PATCH] * Added 'view_filter_on' option that triggers filtering of a download after an event. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@685 e378c898-3ddf-0310-93e7-cc216c733640 --- doc/rtorrent.1.xml | 2 +- rak/functional.h | 2 +- rak/functional_fun.h | 46 +++++++---- src/core/download_list.cc | 51 ++++++++---- src/core/download_list.h | 82 ++++++++++++------ src/core/hash_queue.cc | 7 ++ src/core/hash_queue.h | 2 + src/core/manager.cc | 4 +- src/core/manager.h | 1 - src/core/scheduler.cc | 6 +- src/core/view.cc | 124 +++++++++++++++++++--------- src/core/view.h | 31 ++++--- src/core/view_manager.cc | 41 +++++++++ src/core/view_manager.h | 1 + src/display/window_download_list.cc | 8 +- src/main.cc | 18 +++- src/option_handler_rules.cc | 22 +++++ src/ui/download_list.cc | 19 +++-- 18 files changed, 332 insertions(+), 135 deletions(-) diff --git a/doc/rtorrent.1.xml b/doc/rtorrent.1.xml index b8547f3f..901e2c60 100644 --- a/doc/rtorrent.1.xml +++ b/doc/rtorrent.1.xml @@ -120,7 +120,7 @@ - 1 - 6 + 1 - 7 Change view. diff --git a/rak/functional.h b/rak/functional.h index 82f93d47..43888ed8 100644 --- a/rak/functional.h +++ b/rak/functional.h @@ -350,7 +350,7 @@ public: m_op(op), m_value(v) {} typename Operation::result_type - operator () (const argument_type arg) { + operator () (argument_type arg) { return m_op(arg, m_value); } diff --git a/rak/functional_fun.h b/rak/functional_fun.h index 2c1c7baa..7c48cdb8 100644 --- a/rak/functional_fun.h +++ b/rak/functional_fun.h @@ -343,32 +343,42 @@ value_fn(const Result& val) { return new value_fn0_t(val); } +template +struct equal_types_t { + typedef A first_type; + typedef B second_type; + + const static int result = 0; +}; + +template +struct equal_types_t { + typedef A first_type; + typedef A second_type; + + const static int result = 1; +}; + template inline function_base0* convert_fn(function_base0* src) { - return new convert_fn0_t(src); -} - -// This overload ensures that if we try to convert to the same type, -// it will optimize away the unneeded layer. -template -inline function_base0* -convert_fn(function_base0* src) { - return src; + if (equal_types_t, function_base0 >::result) + // The pointer cast never gets done if the types are different, + // but needs to be here to pleasant the compiler. + return reinterpret_cast, function_base0 >::first_type*>(src); + else + return new convert_fn0_t(src); } template inline function_base1* convert_fn(function_base1* src) { - return new convert_fn1_t(src); -} - -// This overload ensures that if we try to convert to the same type, -// it will optimize away the unneeded layer. -template -inline function_base1* -convert_fn(function_base1* src) { - return src; + if (equal_types_t, function_base1 >::result) + // The pointer cast never gets done if the types are different, + // but needs to be here to pleasant the compiler. + return reinterpret_cast, function_base1 >::first_type*>(src); + else + return new convert_fn1_t(src); } } diff --git a/src/core/download_list.cc b/src/core/download_list.cc index 25cb63e1..c92a872e 100644 --- a/src/core/download_list.cc +++ b/src/core/download_list.cc @@ -74,7 +74,8 @@ struct download_list_call { Download* m_download; }; -DownloadList::~DownloadList() { +void +DownloadList::clear() { std::for_each(begin(), end(), std::bind1st(std::mem_fun(&DownloadList::close), this)); std::for_each(begin(), end(), rak::call_delete()); @@ -120,7 +121,7 @@ DownloadList::insert(Download* download) { try { (*itr)->download()->signal_download_done(sigc::bind(sigc::mem_fun(*this, &DownloadList::received_finished), download)); - std::for_each(m_slotMapInsert.begin(), m_slotMapInsert.end(), download_list_call(*itr)); + std::for_each(slot_map_insert().begin(), slot_map_insert().end(), download_list_call(*itr)); } catch (torrent::local_error& e) { // Should perhaps relax this, just print an error and remove the @@ -147,7 +148,7 @@ DownloadList::erase(iterator itr) { control->core()->download_store()->remove(*itr); - std::for_each(m_slotMapErase.begin(), m_slotMapErase.end(), download_list_call(*itr)); + std::for_each(slot_map_erase().begin(), slot_map_erase().end(), download_list_call(*itr)); torrent::download_remove(*(*itr)->download()); delete *itr; @@ -180,7 +181,7 @@ DownloadList::open_throw(Download* download) { download->download()->open(); - std::for_each(m_slotMapOpen.begin(), m_slotMapOpen.end(), download_list_call(download)); + std::for_each(slot_map_open().begin(), slot_map_open().end(), download_list_call(download)); } void @@ -212,11 +213,20 @@ DownloadList::close_throw(Download* download) { // // Reconsider this save. Should be done explicitly when shutting down. //control->core()->download_store()->save(download); - control->core()->hash_queue()->remove(download); - download->download()->close(); + if (control->core()->hash_queue()->is_queued(download)) { + control->core()->hash_queue()->remove(download); + download->download()->close(); - std::for_each(m_slotMapClose.begin(), m_slotMapClose.end(), download_list_call(download)); + // Hash removed slot must be called after close as we can't atm + // stop already started hash checks except through close. + std::for_each(slot_map_hash_removed().begin(), slot_map_hash_removed().end(), download_list_call(download)); + + } else { + download->download()->close(); + } + + std::for_each(slot_map_close().begin(), slot_map_close().end(), download_list_call(download)); } void @@ -249,7 +259,7 @@ DownloadList::resume(Download* download) { // Properly escape when resume get's called during hashing. The // 'state' is changed by the call to DownloadList::start so it // will automagically start afterwards. - if (control->core()->hash_queue()->find(download) != control->core()->hash_queue()->end()) + if (control->core()->hash_queue()->is_queued(download)) return; download->variable()->set("state_changed", cachedTime.seconds()); @@ -269,6 +279,8 @@ DownloadList::resume(Download* download) { download->variable()->set("hashing", Download::variable_hashing_started); control->core()->hash_queue()->insert(download); + std::for_each(slot_map_hash_queued().begin(), slot_map_hash_queued().end(), download_list_call(download)); + return; } @@ -289,7 +301,7 @@ DownloadList::resume(Download* download) { download->set_priority(download->priority()); download->download()->start(); - std::for_each(m_slotMapStart.begin(), m_slotMapStart.end(), download_list_call(download)); + std::for_each(slot_map_start().begin(), slot_map_start().end(), download_list_call(download)); } catch (torrent::local_error& e) { control->core()->push_log(e.what()); @@ -302,9 +314,13 @@ DownloadList::pause(Download* download) { try { - // Make sure we don't start hash checking a download that we won't - // start. - control->core()->hash_queue()->remove(download); + if (control->core()->hash_queue()->is_queued(download)) { + control->core()->hash_queue()->remove(download); + + // Hash removed slot must be called after close as we can't atm + // stop already started hash checks except through close. + std::for_each(slot_map_hash_removed().begin(), slot_map_hash_removed().end(), download_list_call(download)); + } if (!download->download()->is_active()) return; @@ -312,7 +328,7 @@ DownloadList::pause(Download* download) { download->download()->stop(); download->download()->hash_resume_save(); - std::for_each(m_slotMapStop.begin(), m_slotMapStop.end(), download_list_call(download)); + std::for_each(slot_map_stop().begin(), slot_map_stop().end(), download_list_call(download)); download->variable()->set("state_changed", cachedTime.seconds()); @@ -351,6 +367,7 @@ DownloadList::check_hash_throw(Download* download) { // If any more stuff is added here, make sure resume etc are still // correct. control->core()->hash_queue()->insert(download); + std::for_each(slot_map_hash_queued().begin(), slot_map_hash_queued().end(), download_list_call(download)); } void @@ -388,7 +405,7 @@ DownloadList::hash_done(Download* download) { if (download->variable()->get_value("state") == 1) resume(download); - return; + break; case Download::variable_hashing_last: @@ -403,7 +420,7 @@ DownloadList::hash_done(Download* download) { download->set_message("Hash check on download completion found bad chunks."); } - return; + break; case Download::variable_hashing_stopped: default: @@ -411,6 +428,8 @@ DownloadList::hash_done(Download* download) { download->set_message("Hash check completed but the \"hashing\" variable is in an invalid state."); return; } + + std::for_each(slot_map_hash_done().begin(), slot_map_hash_done().end(), download_list_call(download)); } void @@ -449,7 +468,7 @@ DownloadList::confirm_finished(Download* download) { control->core()->download_store()->save(download); } - std::for_each(m_slotMapFinished.begin(), m_slotMapFinished.end(), download_list_call(download)); + std::for_each(slot_map_finished().begin(), slot_map_finished().end(), download_list_call(download)); } } diff --git a/src/core/download_list.h b/src/core/download_list.h index 04571f7b..2e9b5842 100644 --- a/src/core/download_list.h +++ b/src/core/download_list.h @@ -72,7 +72,7 @@ public: using base_type::empty; using base_type::size; - ~DownloadList(); + void clear(); void session_save(); @@ -104,12 +104,41 @@ public: void hash_done(Download* d); - slot_map& slot_map_insert() { return m_slotMapInsert; } - slot_map& slot_map_erase() { return m_slotMapErase; } - slot_map& slot_map_open() { return m_slotMapOpen; } - slot_map& slot_map_close() { return m_slotMapClose; } - slot_map& slot_map_start() { return m_slotMapStart; } - slot_map& slot_map_stop() { return m_slotMapStop; } + enum { + SLOTS_INSERT, + SLOTS_ERASE, + SLOTS_OPEN, + SLOTS_CLOSE, + SLOTS_START, + SLOTS_STOP, + SLOTS_HASH_QUEUED, + SLOTS_HASH_REMOVED, + SLOTS_HASH_DONE, + SLOTS_FINISHED, + + SLOTS_MAX_SIZE + }; + + slot_map& slots(int m) { return m_slotMaps[m]; } + const slot_map& slots(int m) const { return m_slotMaps[m]; } + + slot_map* slot_map_begin() { return m_slotMaps; } + const slot_map* slot_map_begin() const { return m_slotMaps; } + slot_map* slot_map_end() { return m_slotMaps + SLOTS_MAX_SIZE; } + const slot_map* slot_map_end() const { return m_slotMaps + SLOTS_MAX_SIZE; } + + slot_map& slot_map_insert() { return m_slotMaps[SLOTS_INSERT]; } + const slot_map& slot_map_insert() const { return m_slotMaps[SLOTS_INSERT]; } + slot_map& slot_map_erase() { return m_slotMaps[SLOTS_ERASE]; } + const slot_map& slot_map_erase() const { return m_slotMaps[SLOTS_ERASE]; } + slot_map& slot_map_open() { return m_slotMaps[SLOTS_OPEN]; } + const slot_map& slot_map_open() const { return m_slotMaps[SLOTS_OPEN]; } + slot_map& slot_map_close() { return m_slotMaps[SLOTS_CLOSE]; } + const slot_map& slot_map_close() const { return m_slotMaps[SLOTS_CLOSE]; } + slot_map& slot_map_start() { return m_slotMaps[SLOTS_START]; } + const slot_map& slot_map_start() const { return m_slotMaps[SLOTS_START]; } + slot_map& slot_map_stop() { return m_slotMaps[SLOTS_STOP]; } + const slot_map& slot_map_stop() const { return m_slotMaps[SLOTS_STOP]; } // The finished slots will be called when an active download with // "finished" == 0 performs a hash check which returns a done @@ -119,18 +148,27 @@ public: // Also we need to handle cases when a hashing torrent starts up // after a shutdown. - slot_map& slot_map_hash_done() { return m_slotMapHashDone; } - slot_map& slot_map_finished() { return m_slotMapFinished; } + slot_map& slot_map_hash_queued() { return m_slotMaps[SLOTS_HASH_QUEUED]; } + const slot_map& slot_map_hash_queued() const { return m_slotMaps[SLOTS_HASH_QUEUED]; } + slot_map& slot_map_hash_removed() { return m_slotMaps[SLOTS_HASH_REMOVED]; } + const slot_map& slot_map_hash_removed() const { return m_slotMaps[SLOTS_HASH_REMOVED]; } + slot_map& slot_map_hash_done() { return m_slotMaps[SLOTS_HASH_DONE]; } + const slot_map& slot_map_hash_done() const { return m_slotMaps[SLOTS_HASH_DONE]; } + slot_map& slot_map_finished() { return m_slotMaps[SLOTS_FINISHED]; } + const slot_map& slot_map_finished() const { return m_slotMaps[SLOTS_FINISHED]; } - bool has_slot_insert(const std::string& key) const { return m_slotMapInsert.find(key) != m_slotMapInsert.end(); } - bool has_slot_erase(const std::string& key) const { return m_slotMapErase.find(key) != m_slotMapErase.end(); } - bool has_slot_open(const std::string& key) const { return m_slotMapOpen.find(key) != m_slotMapOpen.end(); } - bool has_slot_close(const std::string& key) const { return m_slotMapClose.find(key) != m_slotMapClose.end(); } - bool has_slot_start(const std::string& key) const { return m_slotMapStart.find(key) != m_slotMapStart.end(); } - bool has_slot_stop(const std::string& key) const { return m_slotMapStop.find(key) != m_slotMapStop.end(); } + bool has_slot_insert(const std::string& key) const { return slot_map_insert().find(key) != slot_map_insert().end(); } + bool has_slot_erase(const std::string& key) const { return slot_map_erase().find(key) != slot_map_erase().end(); } + bool has_slot_open(const std::string& key) const { return slot_map_open().find(key) != slot_map_open().end(); } + bool has_slot_close(const std::string& key) const { return slot_map_close().find(key) != slot_map_close().end(); } + bool has_slot_start(const std::string& key) const { return slot_map_start().find(key) != slot_map_start().end(); } + bool has_slot_stop(const std::string& key) const { return slot_map_stop().find(key) != slot_map_stop().end(); } - bool has_slot_hash_done(const std::string& key) const { return m_slotMapFinished.find(key) != m_slotMapFinished.end(); } - bool has_slot_finished(const std::string& key) const { return m_slotMapFinished.find(key) != m_slotMapFinished.end(); } + bool has_slot_hash_queued(const std::string& key) const{ return slot_map_hash_queued().find(key) != slot_map_hash_queued().end(); } + bool has_slot_hash_done(const std::string& key) const { return slot_map_hash_done().find(key) != slot_map_hash_done().end(); } + bool has_slot_finished(const std::string& key) const { return slot_map_finished().find(key) != slot_map_finished().end(); } + + static void erase_key(slot_map& sm, const std::string& key) { sm.erase(key); } private: inline void check_contains(Download* d); @@ -138,15 +176,7 @@ private: void received_finished(Download* d); void confirm_finished(Download* d); - slot_map m_slotMapInsert; - slot_map m_slotMapErase; - slot_map m_slotMapOpen; - slot_map m_slotMapClose; - slot_map m_slotMapStart; - slot_map m_slotMapStop; - - slot_map m_slotMapHashDone; - slot_map m_slotMapFinished; + slot_map m_slotMaps[SLOTS_MAX_SIZE]; }; } diff --git a/src/core/hash_queue.cc b/src/core/hash_queue.cc index e153e061..ca187d42 100644 --- a/src/core/hash_queue.cc +++ b/src/core/hash_queue.cc @@ -47,6 +47,13 @@ namespace core { +bool +HashQueue::is_queued(Download* download) const { + return + download->download()->is_hash_checking() || + std::find_if(begin(), end(), rak::equal(download, std::mem_fun(&HashQueueNode::download))) != end(); +} + void HashQueue::insert(Download* download) { if (download->download()->is_hash_checking() || find(download) != end()) diff --git a/src/core/hash_queue.h b/src/core/hash_queue.h index c4ddf920..89a90e75 100644 --- a/src/core/hash_queue.h +++ b/src/core/hash_queue.h @@ -71,6 +71,8 @@ public: // Replace 'dl' with a slot. HashQueue(DownloadList* dl) : m_downloadList(dl) {} + bool is_queued(Download* download) const; + void insert(Download* d); // It's safe to try to remove downloads not in the queue. The hash diff --git a/src/core/manager.cc b/src/core/manager.cc index bf7f0e6b..68b03872 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -160,7 +160,7 @@ Manager::initialize_second() { m_downloadList->slot_map_insert()["1_connect_storage_log"] = sigc::bind(sigc::ptr_fun(&connect_signal_storage_log), sigc::mem_fun(m_logComplete, &Log::push_front)); m_downloadList->slot_map_insert()["1_connect_tracker_dump"] = sigc::bind(sigc::ptr_fun(&connect_signal_tracker_dump), sigc::ptr_fun(&receive_tracker_dump)); - m_downloadList->slot_map_erase()["1_delete_tied"] = sigc::ptr_fun(&delete_tied); + m_downloadList->slot_map_erase()["1_delete_tied"] = sigc::ptr_fun(&delete_tied); } void @@ -168,6 +168,8 @@ Manager::cleanup() { // Need to disconnect log signals? Not really since we won't receive // any more. + m_downloadList->clear(); + torrent::cleanup(); CurlStack::global_cleanup(); diff --git a/src/core/manager.h b/src/core/manager.h index b5aff40e..8f7ad891 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -62,7 +62,6 @@ public: Manager(); ~Manager(); - DownloadList* download_list() { return m_downloadList; } DownloadStore* download_store() { return m_downloadStore; } diff --git a/src/core/scheduler.cc b/src/core/scheduler.cc index 19c1cb9c..086b4482 100644 --- a/src/core/scheduler.cc +++ b/src/core/scheduler.cc @@ -65,7 +65,7 @@ Scheduler::set_view(View* view) { Scheduler::size_type Scheduler::active() const { - return std::count_if(m_view->begin(), m_view->end(), std::mem_fun(&Download::is_active)); + return std::count_if(m_view->begin_visible(), m_view->end_visible(), std::mem_fun(&Download::is_active)); } void @@ -80,7 +80,7 @@ Scheduler::update() { // inactive we can switch with. size_type target = m_maxActive - std::min(m_cycle, m_maxActive); - for (View::iterator itr = m_view->begin(), last = m_view->end(); curActive > target; ++itr) { + for (View::iterator itr = m_view->begin_visible(), last = m_view->end_visible(); curActive > target; ++itr) { if (itr == last) throw torrent::internal_error("Scheduler::update() loop bork."); @@ -92,7 +92,7 @@ Scheduler::update() { m_view->sort(); - for (View::iterator itr = m_view->begin(), last = m_view->end(); curActive < m_maxActive; ++itr) { + for (View::iterator itr = m_view->begin_visible(), last = m_view->end_visible(); curActive < m_maxActive; ++itr) { if (itr == last) throw torrent::internal_error("Scheduler::update() loop bork."); diff --git a/src/core/view.cc b/src/core/view.cc index a0b42713..9038ce4d 100644 --- a/src/core/view.cc +++ b/src/core/view.cc @@ -38,6 +38,8 @@ #include #include +#include +#include #include #include "download.h" @@ -51,14 +53,11 @@ View::~View() { if (m_name.empty()) return; - std::string key = "0_view_" + m_name; - - m_list->slot_map_insert().erase(key); - m_list->slot_map_erase().erase(key); + std::for_each(m_list->slot_map_begin(), m_list->slot_map_end(), rak::bind2nd(std::ptr_fun(&DownloadList::erase_key), "0_view_" + m_name)); } void -View::initialize(const std::string& name, core::DownloadList* list) { +View::initialize(const std::string& name, core::DownloadList* dlist) { if (!m_name.empty()) throw torrent::internal_error("View::initialize(...) called on an already initialized view."); @@ -67,21 +66,22 @@ View::initialize(const std::string& name, core::DownloadList* list) { std::string key = "0_view_" + name; - if (list->has_slot_insert(key) || list->has_slot_erase(key)) + if (dlist->has_slot_insert(key) || dlist->has_slot_erase(key)) throw torrent::internal_error("View::initialize(...) duplicate key name found in DownloadList."); m_name = name; + m_list = dlist; - m_list = list; - m_size = 0; + // Urgh, wrong. No filtering being done. + std::for_each(m_list->begin(), m_list->end(), rak::bind1st(std::mem_fun(&View::push_back), this)); + + m_size = base_type::size(); m_focus = 0; + m_list->slot_map_insert()[key] = sigc::bind(sigc::mem_fun(this, &View::received), (int)DownloadList::SLOTS_INSERT); + m_list->slot_map_erase()[key] = sigc::bind(sigc::mem_fun(this, &View::received), (int)DownloadList::SLOTS_ERASE); + set_last_changed(rak::timer()); - - std::for_each(m_list->begin(), m_list->end(), std::bind1st(std::mem_fun(&View::received_insert), this)); - - m_list->slot_map_insert()[key] = sigc::mem_fun(this, &View::received_insert); - m_list->slot_map_erase()[key] = sigc::mem_fun(this, &View::received_erase); } void @@ -144,12 +144,12 @@ struct view_downloads_filter : std::unary_function { void View::sort() { - Download* curFocus = focus() != end() ? *focus() : NULL; + Download* curFocus = focus() != end_visible() ? *focus() : NULL; // Don't go randomly switching around equivalent elements. - std::stable_sort(begin(), end(), view_downloads_compare(m_sortCurrent)); + std::stable_sort(begin(), end_visible(), view_downloads_compare(m_sortCurrent)); - m_focus = position(std::find(begin(), end(), curFocus)); + m_focus = position(std::find(begin(), end_visible(), curFocus)); m_signalChanged.emit(); } @@ -164,38 +164,84 @@ View::filter() { } void -View::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)); +View::set_filter_on(int event) { + if (event == DownloadList::SLOTS_INSERT || event == DownloadList::SLOTS_ERASE || event >= DownloadList::SLOTS_MAX_SIZE) + throw torrent::internal_error("View::filter_on(...) invalid event."); - m_size++; - m_focus += (m_focus >= position(itr)); - - } else { - itr = end_filtered(); - } - - if (m_focus > m_size) - throw torrent::internal_error("View::received_insert(...) m_focus > m_size."); - - base_type::insert(itr, d); - m_signalChanged.emit(); + m_list->slots(event)["0_view_" + m_name] = sigc::bind(sigc::mem_fun(this, &View::received), event); } -void -View::received_erase(core::Download* d) { - iterator itr = std::find(begin(), end_filtered(), d); +inline void +View::insert_visible(Download* d) { + iterator itr = std::find_if(begin_visible(), end_visible(), std::bind1st(view_downloads_compare(m_sortNew), d)); + m_size++; + m_focus += (m_focus >= position(itr)); + + base_type::insert(itr, d); +} + +inline void +View::erase(iterator itr) { if (itr == end_filtered()) - return; + throw torrent::internal_error("View::erase_visible(...) iterator out of range."); - m_size -= (itr < end()); + m_size -= (itr < end_visible()); m_focus -= (m_focus > position(itr)); base_type::erase(itr); +} + +void +View::received(core::Download* download, int event) { + iterator itr = std::find(base_type::begin(), base_type::end(), download); + + switch (event) { + case DownloadList::SLOTS_INSERT: + + if (itr != base_type::end()) + throw torrent::internal_error("View::received(..., SLOTS_INSERT) already inserted."); + + if (view_downloads_filter(m_filter)(download)) + insert_visible(download); + else + base_type::insert(end_filtered(), download); + + if (m_focus > m_size) + throw torrent::internal_error("View::received(...) m_focus > m_size."); + + break; + + case DownloadList::SLOTS_ERASE: + erase(itr); + break; + + default: + if (itr == end_filtered()) + throw torrent::internal_error("View::received(..., SLOTS_*) could not find download."); + + if (view_downloads_filter(m_filter)(download)) { + + if (itr < end_visible()) + return; + + // Use base_type::erase as we don't need to modify m_size nor + // m_focus. + base_type::erase(itr); + insert_visible(download); + + } else { + + if (itr >= begin_filtered()) + return; + + erase(itr); + base_type::push_back(download); + } + + break; + } + m_signalChanged.emit(); } diff --git a/src/core/view.h b/src/core/view.h index 3dd0e39c..e40dfe46 100644 --- a/src/core/view.h +++ b/src/core/view.h @@ -64,9 +64,9 @@ class DownloadList; class ViewSort; class ViewFilter; -class View : public std::vector { +class View : private std::vector { public: - typedef std::vector base_type; + typedef std::vector base_type; typedef sigc::signal0 signal_type; typedef std::vector sort_list; typedef std::vector filter_list; @@ -78,13 +78,10 @@ public: using base_type::size_type; - using base_type::begin; - using base_type::rbegin; - View() {} ~View(); - void initialize(const std::string& name, core::DownloadList* list); + void initialize(const std::string& name, DownloadList* dlist); const std::string& name() const { return m_name; } @@ -92,10 +89,14 @@ public: 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; } + iterator begin_visible() { return begin(); } + const_iterator begin_visible() const { return begin(); } -// using base_type::rend; + iterator end_visible() { return begin() + m_size; } + const_iterator end_visible() const { return begin() + m_size; } + + iterator begin_filtered() { return begin() + m_size; } + const_iterator begin_filtered() const { return begin() + m_size; } iterator end_filtered() { return base_type::end(); } const_iterator end_filtered() const { return base_type::end(); } @@ -114,7 +115,9 @@ public: // Need to explicity trigger filtering. void filter(); + void set_filter(const filter_list& s) { m_filter = s; } + void set_filter_on(int event); // 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 @@ -133,8 +136,12 @@ private: View(const View&); void operator = (const View&); - void received_insert(core::Download* d); - void received_erase(core::Download* d); + void push_back(Download* d) { base_type::push_back(d); } + + inline void insert_visible(Download* d); + inline void erase(iterator itr); + + void received(Download* d, int event); size_type position(const_iterator itr) const { return itr - begin(); } @@ -142,7 +149,7 @@ private: std::string m_name; - core::DownloadList* m_list; + DownloadList* m_list; size_type m_size; size_type m_focus; diff --git a/src/core/view_manager.cc b/src/core/view_manager.cc index 2ec0fe00..2e399fdf 100644 --- a/src/core/view_manager.cc +++ b/src/core/view_manager.cc @@ -43,6 +43,9 @@ #include "globals.h" #include "download.h" +#include "download_list.h" +#include "hash_queue.h" +#include "manager.h" #include "view.h" #include "view_manager.h" @@ -118,6 +121,13 @@ private: torrent::Object::value_type m_value; }; +class ViewFilterHashing : public ViewFilter { +public: + virtual bool operator () (Download* d1) const { + return control->core()->hash_queue()->is_queued(d1); + } +}; + // Really need to implement a factory and allow options in the sort // statements. ViewManager::ViewManager(DownloadList* dl) : @@ -140,6 +150,7 @@ ViewManager::ViewManager(DownloadList* dl) : m_filter["stopped"] = new ViewFilterVariableValue("state", 0); m_filter["complete"] = new ViewFilterVariableValue("complete", 1); m_filter["incomplete"] = new ViewFilterVariableValue("complete", 0); + m_filter["hashing"] = new ViewFilterHashing(); } void @@ -244,4 +255,34 @@ ViewManager::set_filter(const std::string& name, const filter_args& args) { (*viewItr)->set_filter(build_filter_list(args)); } +void +ViewManager::set_filter_on(const std::string& name, const filter_args& args) { + iterator viewItr = find_throw(name); + + for (filter_args::const_iterator itr = args.begin(); itr != args.end(); ++itr) { + + if (*itr == "start") + (*viewItr)->set_filter_on(DownloadList::SLOTS_START); + + else if (*itr == "stop") + (*viewItr)->set_filter_on(DownloadList::SLOTS_STOP); + + else if (*itr == "hash_queued") + (*viewItr)->set_filter_on(DownloadList::SLOTS_HASH_QUEUED); + + else if (*itr == "hash_removed") + (*viewItr)->set_filter_on(DownloadList::SLOTS_HASH_REMOVED); + + else if (*itr == "hash_done") + (*viewItr)->set_filter_on(DownloadList::SLOTS_HASH_DONE); + + else if (*itr == "finished") + (*viewItr)->set_filter_on(DownloadList::SLOTS_FINISHED); + + else + throw torrent::input_error("Invalid filter on identifier."); + + } +} + } diff --git a/src/core/view_manager.h b/src/core/view_manager.h index da4bddf8..375172df 100644 --- a/src/core/view_manager.h +++ b/src/core/view_manager.h @@ -101,6 +101,7 @@ public: void set_sort_current(const std::string& name, const sort_args& sort); void set_filter(const std::string& name, const filter_args& args); + void set_filter_on(const std::string& name, const filter_args& args); private: inline sort_list build_sort_list(const sort_args& args); diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc index 266339e8..cdad86bc 100644 --- a/src/display/window_download_list.cc +++ b/src/display/window_download_list.cc @@ -78,15 +78,15 @@ WindowDownloadList::redraw() { typedef std::pair Range; - Range range = rak::advance_bidirectional(m_view->begin(), - m_view->focus() != m_view->end() ? m_view->focus() : m_view->begin(), - m_view->end(), + Range range = rak::advance_bidirectional(m_view->begin_visible(), + m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(), + m_view->end_visible(), m_canvas->get_height() / 3); // Make sure we properly fill out the last lines so it looks like // there are more torrents, yet don't hide it if we got the last one // in focus. - if (range.second != m_view->end()) + if (range.second != m_view->end_visible()) ++range.second; int pos = 1; diff --git a/src/main.cc b/src/main.cc index c88aa76c..731fd7d7 100644 --- a/src/main.cc +++ b/src/main.cc @@ -173,30 +173,40 @@ main(int argc, char** argv) { control->variable()->process_command("view_add = started"); control->variable()->process_command("view_filter = started,started"); + control->variable()->process_command("view_filter_on = started,start,stop"); control->variable()->process_command("view_sort_new = started,name"); control->variable()->process_command("view_sort_current = started,name"); control->variable()->process_command("view_add = stopped"); control->variable()->process_command("view_filter = stopped,stopped"); + control->variable()->process_command("view_filter_on = stopped,start,stop"); control->variable()->process_command("view_sort_new = stopped,name"); control->variable()->process_command("view_sort_current = stopped,name"); control->variable()->process_command("view_add = complete"); control->variable()->process_command("view_filter = complete,complete"); + control->variable()->process_command("view_filter_on = complete,hash_done,finished"); control->variable()->process_command("view_sort_new = complete,state_changed"); control->variable()->process_command("view_sort_current = complete,state_changed_reverse"); control->variable()->process_command("view_add = incomplete"); control->variable()->process_command("view_filter = incomplete,incomplete"); + control->variable()->process_command("view_filter_on = incomplete,hash_done,finished"); control->variable()->process_command("view_sort_new = incomplete,state_changed"); control->variable()->process_command("view_sort_current = incomplete,state_changed_reverse"); + control->variable()->process_command("view_add = hashing"); + control->variable()->process_command("view_filter = hashing,hashing"); + control->variable()->process_command("view_filter_on = hashing,hash_queued,hash_removed,hash_done"); + control->variable()->process_command("view_sort_new = hashing,state_changed"); + control->variable()->process_command("view_sort_current = hashing,state_changed"); + control->variable()->process_command("schedule = view_main,10,10,view_sort=main,20"); control->variable()->process_command("schedule = view_name,10,10,view_sort=name,20"); - control->variable()->process_command("schedule = view_started,10,10,view_sort=started,5"); - control->variable()->process_command("schedule = view_stopped,10,10,view_sort=stopped,5"); - control->variable()->process_command("schedule = view_complete,10,10,view_sort=complete,5"); - control->variable()->process_command("schedule = view_incomplete,10,10,view_sort=incomplete,5"); +// control->variable()->process_command("schedule = view_started,10,10,view_sort=started,5"); +// control->variable()->process_command("schedule = view_stopped,10,10,view_sort=stopped,5"); +// control->variable()->process_command("schedule = view_complete,10,10,view_sort=complete,5"); +// control->variable()->process_command("schedule = view_incomplete,10,10,view_sort=incomplete,5"); //control->variable()->process_command("schedule = scheduler,10,10,download_scheduler="); control->variable()->process_command("schedule = session_save,1800,1800,session_save="); diff --git a/src/option_handler_rules.cc b/src/option_handler_rules.cc index f8b2264b..8256f79f 100644 --- a/src/option_handler_rules.cc +++ b/src/option_handler_rules.cc @@ -205,6 +205,27 @@ apply_view_filter(Control* control, const std::string& arg) { control->view_manager()->set_filter(name, filterArgs); } +void +apply_view_filter_on(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_on(name, filterArgs); +} + void apply_view_sort(Control* control, const std::string& arg) { rak::split_iterator_t itr = rak::split_iterator(arg, ','); @@ -335,6 +356,7 @@ initialize_option_handler(Control* c) { variables->insert("view_add", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::mem_fn(c->view_manager(), &core::ViewManager::insert_throw))); variables->insert("view_filter", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_view_filter, c))); + variables->insert("view_filter_on", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_view_filter_on, c))); variables->insert("view_sort", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_view_sort, c))); variables->insert("view_sort_new", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_view_sort_new, c))); diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index 79dcc6a3..5dd2b0b4 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -197,7 +197,7 @@ DownloadList::receive_prev() { void DownloadList::receive_start_download() { - if (m_view->focus() == m_view->end()) + if (m_view->focus() == m_view->end_visible()) return; m_control->core()->download_list()->start(*m_view->focus()); @@ -206,7 +206,7 @@ DownloadList::receive_start_download() { void DownloadList::receive_stop_download() { - if (m_view->focus() == m_view->end()) + if (m_view->focus() == m_view->end_visible()) return; if ((*m_view->focus())->download()->is_active()) @@ -219,7 +219,7 @@ DownloadList::receive_stop_download() { void DownloadList::receive_close_download() { - if (m_view->focus() == m_view->end()) + if (m_view->focus() == m_view->end_visible()) return; m_control->core()->download_list()->close(*m_view->focus()); @@ -228,7 +228,7 @@ DownloadList::receive_close_download() { void DownloadList::receive_view_download() { - if (m_view->focus() == m_view->end()) + if (m_view->focus() == m_view->end_visible()) return; if (m_uiDownload != NULL) @@ -259,7 +259,7 @@ DownloadList::receive_exit_download() { void DownloadList::receive_next_priority() { - if (m_view->focus() == m_view->end()) + if (m_view->focus() == m_view->end_visible()) return; (*m_view->focus())->set_priority(((*m_view->focus())->priority() + 1) % 4); @@ -267,7 +267,7 @@ DownloadList::receive_next_priority() { void DownloadList::receive_prev_priority() { - if (m_view->focus() == m_view->end()) + if (m_view->focus() == m_view->end_visible()) return; (*m_view->focus())->set_priority(((*m_view->focus())->priority() - 1) % 4); @@ -275,7 +275,7 @@ DownloadList::receive_prev_priority() { void DownloadList::receive_check_hash() { - if (m_view->focus() == m_view->end()) + if (m_view->focus() == m_view->end_visible()) return; // Catch here? @@ -326,7 +326,7 @@ DownloadList::receive_exit_input(Input type) { break; case INPUT_CHANGE_DIRECTORY: - if (m_view->focus() == m_view->end()) + if (m_view->focus() == m_view->end_visible()) throw torrent::input_error("No download in focus to change root directory."); (*m_view->focus())->variable()->set("directory", rak::trim(m_windowTextInput->get_input()->str())); @@ -367,7 +367,7 @@ DownloadList::receive_change(Display d) { void DownloadList::receive_download_erased(core::Download* d) { - if (m_view->focus() == m_view->end() || *m_view->focus() != d) + if (m_view->focus() == m_view->end_visible() || *m_view->focus() != d) return; if (m_uiDownload != NULL) @@ -430,6 +430,7 @@ DownloadList::setup_keys() { (*m_bindings)['4'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change_view), "stopped"); (*m_bindings)['5'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change_view), "complete"); (*m_bindings)['6'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change_view), "incomplete"); + (*m_bindings)['7'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change_view), "hashing"); m_uiArray[DISPLAY_LOG]->get_bindings()[' '] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change), DISPLAY_DOWNLOAD_LIST); }