From 02a05104393cabf4dd5f8c68e6fe168af83d2b2d Mon Sep 17 00:00:00 2001 From: rakshasa Date: Thu, 27 Apr 2006 21:38:02 +0000 Subject: [PATCH] * Changed ViewSort::less(...) to operator(). * Added a "last changed" paramter to ViewManager::sort(...) which is checked, if the ViewDownloads's last_changed timer was poked withing that timeframe it won't update. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@676 e378c898-3ddf-0310-93e7-cc216c733640 --- doc/rtorrent.1.xml | 1 + doc/rtorrent.rc | 6 ++- src/core/view_downloads.cc | 6 ++- src/core/view_downloads.h | 26 ++++++---- src/core/view_manager.cc | 18 ++++--- src/core/view_manager.h | 6 ++- src/display/window_download_list.cc | 26 ++++++---- src/display/window_download_list.h | 4 +- src/main.cc | 25 +++++++++- src/option_handler_rules.cc | 73 +++++++++++++++++++---------- src/ui/download_list.cc | 37 +++++++++++++-- src/ui/download_list.h | 2 + src/ui/element_download_list.cc | 22 ++++++--- src/ui/element_download_list.h | 9 ++-- 14 files changed, 190 insertions(+), 71 deletions(-) diff --git a/doc/rtorrent.1.xml b/doc/rtorrent.1.xml index 611a263b..b1b2297f 100644 --- a/doc/rtorrent.1.xml +++ b/doc/rtorrent.1.xml @@ -427,6 +427,7 @@ interface address. ip = a.b.c.d + ip = hostname Set the address reported to the tracker. diff --git a/doc/rtorrent.rc b/doc/rtorrent.rc index b7c7a573..df0c0acd 100644 --- a/doc/rtorrent.rc +++ b/doc/rtorrent.rc @@ -27,11 +27,13 @@ #schedule = untied_directory,5,5,stop_untied= # The ip address reported to the tracker. -#ip = rakshasa +#ip = 127.0.0.1 +#ip = rakshasa.no # The ip address the listening socket and outgoing connections is # bound to. -#bind = rakshasa +#bind = 127.0.0.1 +#bind = rakshasa.no # Port range to use for listening. #port_range = 6890-6999 diff --git a/src/core/view_downloads.cc b/src/core/view_downloads.cc index 3ab7dd71..60019789 100644 --- a/src/core/view_downloads.cc +++ b/src/core/view_downloads.cc @@ -76,6 +76,8 @@ ViewDownloads::initialize(const std::string& name, core::DownloadList* list) { m_size = 0; m_focus = 0; + set_last_changed(rak::timer()); + std::for_each(m_list->begin(), m_list->end(), std::bind1st(std::mem_fun(&ViewDownloads::received_insert), this)); m_list->slot_map_insert()[key] = sigc::mem_fun(this, &ViewDownloads::received_insert); @@ -111,9 +113,9 @@ struct view_downloads_compare : std::binary_function bool operator () (Download* d1, Download* d2) const { for (ViewDownloads::sort_list::const_iterator itr = m_sort.begin(), last = m_sort.end(); itr != last; ++itr) - if ((*itr)->less(d1, d2)) + if ((**itr)(d1, d2)) return true; - else if ((*itr)->less(d2, d1)) + else if ((**itr)(d2, d1)) return false; // Since we're testing equivalence, return false if we're diff --git a/src/core/view_downloads.h b/src/core/view_downloads.h index acbdfaa7..0108606f 100644 --- a/src/core/view_downloads.h +++ b/src/core/view_downloads.h @@ -37,11 +37,9 @@ // Provides a filtered and sorted list of downloads that can be // updated auto-magically. // -// We don't worry about std::vector's insert/erase performance as it -// get's called so often, better with cache locality. -// -// Do we want to be able to modify the underlying DownloadList from -// here? +// We don't worry about std::vector's insert/erase performance as the +// elements get accessed often but not modified, better with cache +// locality. // // ViewDownloads::m_size indicates the number of Download's that // remain visible, e.g. has not been filtered out. The Download's that @@ -54,9 +52,11 @@ #include #include #include -//#include +#include #include +#include "globals.h" + namespace core { class Download; @@ -116,6 +116,15 @@ public: void filter(); void set_filter(const filter_list& s) { m_filter = s; } + // 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. + // + // Currently initialized to rak::timer(), though perhaps we should + // use cachedTimer. + rak::timer last_changed() const { return m_lastChanged; } + void set_last_changed(const rak::timer& t = ::cachedTime) { m_lastChanged = t; } + // 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; } @@ -143,8 +152,7 @@ private: filter_list m_filter; - // Timer, last changed. - + rak::timer m_lastChanged; signal_type m_signalChanged; }; @@ -152,7 +160,7 @@ class ViewSort { public: virtual ~ViewSort() {} - virtual bool less(Download* d1, Download* d2) const = 0; + virtual bool operator () (Download* d1, Download* d2) const = 0; }; class ViewFilter { diff --git a/src/core/view_manager.cc b/src/core/view_manager.cc index 6c890a01..32f85560 100644 --- a/src/core/view_manager.cc +++ b/src/core/view_manager.cc @@ -40,6 +40,8 @@ #include #include +#include "globals.h" + #include "download.h" #include "view_downloads.h" #include "view_manager.h" @@ -48,7 +50,7 @@ namespace core { class ViewSortName : public ViewSort { public: - virtual bool less(Download* d1, Download* d2) const { + virtual bool operator () (Download* d1, Download* d2) const { return d1->download()->name() < d2->download()->name(); } }; @@ -58,7 +60,7 @@ public: ViewSortVariable(const std::string& name, const std::string& value) : m_name(name), m_value(value) {} - virtual bool less(Download* d1, Download* d2) const { + virtual bool operator () (Download* d1, Download* d2) const { return d1->variable()->get_string(m_name) == m_value && d2->variable()->get_string(m_name) != m_value; @@ -74,7 +76,7 @@ public: ViewSortVariableValue(const std::string& name) : m_name(name) {} - virtual bool less(Download* d1, Download* d2) const { + virtual bool operator () (Download* d1, Download* d2) const { return d1->variable()->get_value(m_name) < d2->variable()->get_value(m_name); } @@ -87,8 +89,8 @@ public: ViewSortReverse(ViewSort* s) : m_sort(s) {} ~ViewSortReverse() { delete m_sort; } - virtual bool less(Download* d1, Download* d2) const { - return m_sort->less(d2, d1); + virtual bool operator () (Download* d1, Download* d2) const { + return (*m_sort)(d2, d1); } private: @@ -126,6 +128,7 @@ ViewManager::ViewManager(DownloadList* dl) : m_sort["state_changed_reverse"] = new ViewSortReverse(new ViewSortVariableValue("state_changed")); m_filter["started"] = new ViewFilterVariableValue("state", 1); + m_filter["stopped"] = new ViewFilterVariableValue("state", 0); } void @@ -180,9 +183,12 @@ ViewManager::build_sort_list(const sort_args& args) { } void -ViewManager::sort(const std::string& name) { +ViewManager::sort(const std::string& name, uint32_t timeout) { iterator viewItr = find_throw(name); + if ((*viewItr)->last_changed() + rak::timer::from_seconds(timeout) > cachedTime) + return; + // Should we rename sort, or add a seperate function? (*viewItr)->filter(); diff --git a/src/core/view_manager.h b/src/core/view_manager.h index cfa5e42c..f841778d 100644 --- a/src/core/view_manager.h +++ b/src/core/view_manager.h @@ -92,7 +92,11 @@ public: iterator find(const std::string& name); iterator find_throw(const std::string& name); - void sort(const std::string& name); + // If ViewDownloads::last_changed() is less than 'timeout' seconds + // ago, don't sort. + // + // 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 sort_args& sort); void set_sort_current(const std::string& name, const sort_args& sort); diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc index 70e22fb5..78d36844 100644 --- a/src/display/window_download_list.cc +++ b/src/display/window_download_list.cc @@ -48,24 +48,32 @@ namespace display { -WindowDownloadList::WindowDownloadList(core::ViewDownloads* l) : - Window(new Canvas, true), - m_view(l) { - - m_connChanged = m_view->signal_changed().connect(sigc::mem_fun(*this, &Window::mark_dirty)); -} - WindowDownloadList::~WindowDownloadList() { m_connChanged.disconnect(); } +void +WindowDownloadList::set_view(core::ViewDownloads* l) { + m_view = l; + + m_connChanged.disconnect(); + + if (m_view != NULL) + m_connChanged = m_view->signal_changed().connect(sigc::mem_fun(*this, &Window::mark_dirty)); +} + void WindowDownloadList::redraw() { m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); m_canvas->erase(); - if (m_view->empty() || m_canvas->get_width() < 5) + if (m_view == NULL) + return; + + m_canvas->print(0, 0, "%s", ("[View: " + m_view->name() + "]").c_str()); + + if (m_view->empty() || m_canvas->get_width() < 5 || m_canvas->get_height() < 2) return; typedef std::pair Range; @@ -81,7 +89,7 @@ WindowDownloadList::redraw() { if (range.second != m_view->end()) ++range.second; - int pos = 0; + int pos = 1; while (range.first != range.second) { char buffer[m_canvas->get_width()]; diff --git a/src/display/window_download_list.h b/src/display/window_download_list.h index 64438f09..a6167984 100644 --- a/src/display/window_download_list.h +++ b/src/display/window_download_list.h @@ -51,11 +51,13 @@ namespace display { class WindowDownloadList : public Window { public: - WindowDownloadList(core::ViewDownloads* l); + WindowDownloadList() : Window(new Canvas, true), m_view(NULL) {} ~WindowDownloadList(); virtual void redraw(); + void set_view(core::ViewDownloads* l); + private: core::ViewDownloads* m_view; diff --git a/src/main.cc b/src/main.cc index 1ec7427a..3e30f0e9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -163,11 +163,32 @@ 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"); - // Changing these will bork the scheduler. + control->variable()->process_command("view_add = started"); + control->variable()->process_command("view_filter = started,started"); + 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_sort_new = stopped,name"); + control->variable()->process_command("view_sort_current = stopped,name"); + + control->variable()->process_command("view_add = finished"); + //control->variable()->process_command("view_filter = finished,stopped"); + control->variable()->process_command("view_sort_new = finished,state_changed"); + control->variable()->process_command("view_sort_current = finished,state_changed_reverse"); + + control->variable()->process_command("schedule = view_main,10,10,view_sort=main,30"); + control->variable()->process_command("schedule = view_started,10,10,view_sort=started"); + control->variable()->process_command("schedule = view_stopped,10,10,view_sort=stopped"); + control->variable()->process_command("schedule = view_finished,10,10,view_sort=finished,30"); + + //control->variable()->process_command("schedule = scheduler,10,10,download_scheduler="); + + // Changing these will bork the (non-existant) scheduler. 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"); diff --git a/src/option_handler_rules.cc b/src/option_handler_rules.cc index 666ac0f2..34ff254a 100644 --- a/src/option_handler_rules.cc +++ b/src/option_handler_rules.cc @@ -183,6 +183,52 @@ apply_tos(const std::string& arg) { throw torrent::input_error("Invalid TOS identifier."); } +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_view_sort(Control* control, const std::string& arg) { + rak::split_iterator_t itr = rak::split_iterator(arg, ','); + + std::string name = rak::trim(*itr); + ++itr; + + if (name.empty()) + throw torrent::input_error("First argument must be a string."); + + // Need some generic tools for this, rather than hacking up + // something every time... + std::string arg1; + int32_t value = 0; + + if (itr != rak::split_iterator(arg) && !(arg1 = *itr).empty()) { + char* endPtr; + + if ((value = strtol(arg1.c_str(), &endPtr, 0)) < 0 || *endPtr != '\0') + throw torrent::input_error("Second argument must be a value."); + } + + control->view_manager()->sort(name, value); +} + void apply_view_sort_current(Control* control, const std::string& arg) { rak::split_iterator_t itr = rak::split_iterator(arg, ','); @@ -225,27 +271,6 @@ 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(); @@ -302,12 +327,12 @@ initialize_option_handler(Control* c) { rak::mem_fn(control->variable(), &utils::VariableMap::process_file_nothrow))); 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_sort", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::mem_fn(c->view_manager(), &core::ViewManager::sort))); + variables->insert("view_filter", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_view_filter, 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))); 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/download_list.cc b/src/ui/download_list.cc index e961303d..67d41110 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -83,12 +83,15 @@ DownloadList::DownloadList(Control* c) : m_control(c), m_bindings(new input::Bindings) { - m_view = *c->view_manager()->find_throw("main"); - - m_uiArray[DISPLAY_DOWNLOAD_LIST] = new ElementDownloadList(m_view); + m_uiArray[DISPLAY_DOWNLOAD_LIST] = new ElementDownloadList(); m_uiArray[DISPLAY_LOG] = new ElementLogComplete(&m_control->core()->get_log_complete()); m_windowLog = new WLog(&m_control->core()->get_log_important()); + receive_change_view("main"); + + if (m_view == NULL) + throw torrent::internal_error("View \"main\" must be present to initialize the main display."); + m_taskUpdate.set_slot(rak::mem_fn(this, &DownloadList::task_update)), setup_keys(); @@ -183,11 +186,13 @@ DownloadList::disable_display() { void DownloadList::receive_next() { m_view->next_focus(); + m_view->set_last_changed(); } void DownloadList::receive_prev() { m_view->prev_focus(); + m_view->set_last_changed(); } void @@ -365,6 +370,26 @@ DownloadList::receive_download_erased(core::Download* d) { receive_next(); } +void +DownloadList::receive_change_view(const std::string& name) { + core::ViewManager::iterator itr = m_control->view_manager()->find(name); + + if (itr == m_control->view_manager()->end()) { + m_control->core()->push_log("Could not find view \"" + name + "\"."); + return; + } + + m_view = *itr; + m_view->sort(); + + ElementDownloadList* ui = dynamic_cast(m_uiArray[DISPLAY_DOWNLOAD_LIST]); + + if (ui == NULL) + throw torrent::internal_error("DownloadList::receive_change_view(...) could not cast ui."); + + ui->set_view(m_view); +} + void DownloadList::task_update() { m_windowLog->receive_update(); @@ -393,6 +418,12 @@ DownloadList::setup_keys() { (*m_bindings)[KEY_RIGHT] = sigc::mem_fun(*this, &DownloadList::receive_view_download); (*m_bindings)['l'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change), DISPLAY_LOG); + (*m_bindings)['1'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change_view), "main"); + (*m_bindings)['2'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change_view), "started"); + (*m_bindings)['3'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change_view), "stopped"); + (*m_bindings)['4'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change_view), "finished"); + (*m_bindings)['5'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change_view), "incomplete"); + m_uiArray[DISPLAY_LOG]->get_bindings()[' '] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_change), DISPLAY_DOWNLOAD_LIST); } diff --git a/src/ui/download_list.h b/src/ui/download_list.h index 676bda50..ea8139f1 100644 --- a/src/ui/download_list.h +++ b/src/ui/download_list.h @@ -137,6 +137,8 @@ private: void receive_download_erased(core::Download* d); + void receive_change_view(const std::string& name); + void task_update(); void setup_keys(); diff --git a/src/ui/element_download_list.cc b/src/ui/element_download_list.cc index dc4f84c7..f5659ee8 100644 --- a/src/ui/element_download_list.cc +++ b/src/ui/element_download_list.cc @@ -38,7 +38,6 @@ #include -#include "display/window_download_list.h" #include "input/manager.h" #include "control.h" @@ -46,11 +45,6 @@ namespace ui { -ElementDownloadList::ElementDownloadList(core::ViewDownloads* l) : - m_window(NULL), - m_view(l) { -} - void ElementDownloadList::activate(Control* c, MItr mItr) { if (m_window != NULL) @@ -58,7 +52,10 @@ ElementDownloadList::activate(Control* c, MItr mItr) { c->input()->push_front(&m_bindings); - *mItr = m_window = new WDownloadList(m_view); + m_window = new WDownloadList(); + m_window->set_view(m_view); + + *mItr = m_window; } void @@ -72,4 +69,15 @@ ElementDownloadList::disable(Control* c) { m_window = NULL; } +void +ElementDownloadList::set_view(core::ViewDownloads* l) { + m_view = l; + + if (m_window == NULL) + return; + + m_window->set_view(l); + m_window->mark_dirty(); +} + } diff --git a/src/ui/element_download_list.h b/src/ui/element_download_list.h index f574591d..f9672511 100644 --- a/src/ui/element_download_list.h +++ b/src/ui/element_download_list.h @@ -38,6 +38,7 @@ #define RTORRENT_UI_ELEMENT_DOWNLOAD_LIST_H #include "core/download_list.h" +#include "display/window_download_list.h" #include "element_base.h" @@ -47,21 +48,19 @@ namespace core { class ViewDownloads; } -namespace display { - class WindowDownloadList; -} - namespace ui { class ElementDownloadList : public ElementBase { public: typedef display::WindowDownloadList WDownloadList; - ElementDownloadList(core::ViewDownloads* l); + ElementDownloadList() : m_window(NULL), m_view(NULL) {} void activate(Control* c, MItr mItr); void disable(Control* c); + void set_view(core::ViewDownloads* l); + private: WDownloadList* m_window; core::ViewDownloads* m_view;