diff --git a/src/control.cc b/src/control.cc index c47115ea..b543eba1 100644 --- a/src/control.cc +++ b/src/control.cc @@ -41,6 +41,8 @@ #include #include "core/manager.h" +#include "core/view_manager.h" + #include "display/canvas.h" #include "display/client_info.h" #include "display/window.h" @@ -59,7 +61,6 @@ Control::Control() : m_shutdownQuick(false), m_ui(new ui::Root()), - m_core(new core::Manager()), m_display(new display::Manager()), m_input(new input::Manager()), m_inputStdin(new input::InputEvent(STDIN_FILENO)), @@ -71,6 +72,9 @@ Control::Control() : m_tick(0) { + m_core = new core::Manager(); + m_viewManager = new core::ViewManager(&m_core->download_list()); + m_inputStdin->slot_pressed(sigc::mem_fun(m_input, &input::Manager::pressed)); m_taskShutdown.set_slot(rak::mem_fn(this, &Control::handle_shutdown)); @@ -104,7 +108,7 @@ Control::initialize() { m_core->initialize_second(); m_core->listen_open(); - m_core->download_store().enable(m_variables->get_string("session_lock") == "yes"); + m_core->download_store().enable(m_variables->get_value("session_lock")); m_ui->init(this); @@ -151,3 +155,9 @@ Control::set_umask(mode_t m) { m_umask = m; } + +void +Control::set_working_directory(const std::string& dir) { + if (::chdir(dir.c_str()) != 0) + throw torrent::input_error("Could not change working directory."); +} diff --git a/src/control.h b/src/control.h index 66a26136..1140f2b6 100644 --- a/src/control.h +++ b/src/control.h @@ -49,6 +49,7 @@ namespace ui { namespace core { class Manager; + class ViewManager; } namespace display { @@ -83,8 +84,10 @@ public: void receive_normal_shutdown() { m_shutdownReceived = true; } void receive_quick_shutdown() { m_shutdownReceived = true; m_shutdownQuick = true; } - ui::Root* ui() { return m_ui; } core::Manager* core() { return m_core; } + core::ViewManager* view_manager() { return m_viewManager; } + + ui::Root* ui() { return m_ui; } display::Manager* display() { return m_display; } input::Manager* input() { return m_input; } input::InputEvent* input_stdin() { return m_inputStdin; } @@ -100,6 +103,9 @@ public: mode_t umask() const { return m_umask; } void set_umask(mode_t m); + const std::string& working_directory() const { return m_workingDirectory; } + void set_working_directory(const std::string& dir); + private: Control(const Control&); void operator = (const Control&); @@ -107,8 +113,10 @@ private: bool m_shutdownReceived; bool m_shutdownQuick; - ui::Root* m_ui; core::Manager* m_core; + core::ViewManager* m_viewManager; + + ui::Root* m_ui; display::Manager* m_display; input::Manager* m_input; input::InputEvent* m_inputStdin; @@ -119,7 +127,9 @@ private: display::ClientInfo* m_clientInfo; uint64_t m_tick; + mode_t m_umask; + std::string m_workingDirectory; rak::priority_item m_taskShutdown; }; diff --git a/src/core/Makefile.am b/src/core/Makefile.am index fd764514..b99f85bf 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -27,6 +27,10 @@ libsub_core_a_SOURCES = \ poll_manager_epoll.cc \ poll_manager_epoll.h \ poll_manager_select.cc \ - poll_manager_select.h + poll_manager_select.h \ + view_downloads.cc \ + view_downloads.h \ + view_manager.cc \ + view_manager.h INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) diff --git a/src/core/download_factory.cc b/src/core/download_factory.cc index 63cd4b1e..89ca38b2 100644 --- a/src/core/download_factory.cc +++ b/src/core/download_factory.cc @@ -192,7 +192,7 @@ DownloadFactory::receive_success() { rtorrent->insert_key("key", (*itr)->tracker_list()->key()); } - if (control->variable()->get_string("use_udp_trackers") == "no") + if (!control->variable()->get_value("use_udp_trackers")) (*itr)->enable_udp_trackers(false); if (rtorrent->has_key_value("total_uploaded")) diff --git a/src/core/download_list.cc b/src/core/download_list.cc index 2d0e1524..2b1bd5a2 100644 --- a/src/core/download_list.cc +++ b/src/core/download_list.cc @@ -95,10 +95,17 @@ DownloadList::insert(std::istream* str, bool printLog) { } } +void +DownloadList::erase(Download* d) { + erase(std::find(begin(), end(), d)); +} + DownloadList::iterator DownloadList::erase(iterator itr) { - // Make safe to erase active downloads. + if (itr == end()) + throw torrent::internal_error("DownloadList::erase(...) could not find download."); + // Make safe to erase active downloads. if ((*itr)->download()->is_active()) throw std::logic_error("DownloadList::erase(...) called on an active download."); diff --git a/src/core/download_list.h b/src/core/download_list.h index 6059757a..3abf4e8d 100644 --- a/src/core/download_list.h +++ b/src/core/download_list.h @@ -74,6 +74,8 @@ public: ~DownloadList() { clear(); } iterator insert(std::istream* str, bool printLog); + + void erase(Download* d); iterator erase(iterator itr); void open(Download* d); @@ -95,6 +97,15 @@ public: SlotMap& slot_map_finished() { return m_slotMapFinished; } + 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_finished(const std::string& key) const { return m_slotMapFinished.find(key) != m_slotMapFinished.end(); } + private: void clear(); diff --git a/src/core/manager.cc b/src/core/manager.cc index 8bbeb3ae..2ff63fd7 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -203,7 +203,7 @@ Manager::check_hash(Download* d) { void Manager::receive_download_done(Download* d) { - if (control->variable()->get("check_hash").as_string() == "yes") { + if (control->variable()->get_value("check_hash")) { // Start the hash checking, send completed to tracker after // finishing. prepare_hash_check(d); @@ -218,13 +218,13 @@ Manager::receive_download_done(Download* d) { void Manager::listen_open() { - if (control->variable()->get_string("port_open") != "yes") + if (!control->variable()->get_value("port_open")) return; if (m_portFirst > m_portLast) throw torrent::input_error("Invalid port range for listening"); - if (control->variable()->get("port_random").as_string() == "yes") { + if (control->variable()->get_value("port_random")) { int boundary = m_portFirst + random() % (m_portLast - m_portFirst + 1); if (torrent::connection_manager()->listen_open(boundary, m_portLast) || @@ -323,7 +323,7 @@ void Manager::receive_download_done_hash_checked(Download* d) { m_downloadList.resume(d); - if (control->variable()->get_string("session_on_completion") == "yes") + if (control->variable()->get_value("session_on_completion")) m_downloadStore.save(d); // Don't send if we did a hash check and found incompelete chunks. diff --git a/src/core/view_downloads.cc b/src/core/view_downloads.cc new file mode 100644 index 00000000..a5fc984a --- /dev/null +++ b/src/core/view_downloads.cc @@ -0,0 +1,141 @@ +// rTorrent - BitTorrent client +// Copyright (C) 2005-2006, Jari Sundell +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// In addition, as a special exception, the copyright holders give +// permission to link the code of portions of this program with the +// OpenSSL library under certain conditions as described in each +// individual source file, and distribute linked combinations +// including the two. +// +// You must obey the GNU General Public License in all respects for +// all of the code used other than OpenSSL. If you modify file(s) +// with this exception, you may extend this exception to your version +// of the file(s), but you are not obligated to do so. If you do not +// wish to do so, delete this exception statement from your version. +// If you delete this exception statement from all source files in the +// program, then also delete it here. +// +// Contact: Jari Sundell +// +// Skomakerveien 33 +// 3185 Skoppum, NORWAY + +#include "config.h" + +#include +#include +#include + +#include "download.h" +#include "download_list.h" + +#include "view_downloads.h" + +namespace core { + +ViewDownloads::~ViewDownloads() { + 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); +} + +void +ViewDownloads::initialize(const std::string& name, core::DownloadList* list) { + if (!m_name.empty()) + throw torrent::internal_error("ViewDownloads::initialize(...) called on an already initialized view."); + + if (name.empty()) + throw torrent::internal_error("ViewDownloads::initialize(...) called with an empty name."); + + std::string key = "0_view_" + name; + + if (list->has_slot_insert(key) || list->has_slot_erase(key)) + throw torrent::internal_error("ViewDownloads::initialize(...) duplicate key name found in DownloadList."); + + m_name = name; + + m_list = list; + m_focus = 0; + + m_list->slot_map_insert()[key] = sigc::mem_fun(this, &ViewDownloads::received_insert); + m_list->slot_map_erase()[key] = sigc::mem_fun(this, &ViewDownloads::received_erase); + + // Add from download, do various stuff to them. +} + +void +ViewDownloads::next_focus() { + if (empty()) + return; + + m_focus = (m_focus + 1) % (size() + 1); + + m_signalChanged.emit(); +} + +void +ViewDownloads::prev_focus() { + if (empty()) + return; + + m_focus = (m_focus - 1 + size() + 1) % (size() + 1); + + m_signalChanged.emit(); +} + +void +ViewDownloads::sort(sort_slot s) { + Download* curFocus = focus() != end() ? *focus() : NULL; + + std::sort(begin(), end(), std::ptr_fun(s)); + + m_focus = position(std::find(begin(), end(), curFocus)); + m_signalChanged.emit(); +} + +void +ViewDownloads::received_insert(core::Download* d) { + iterator itr = m_sortNew != NULL ? + std::find_if(begin(), end(), std::bind1st(std::ptr_fun(m_sortNew), d)) : + end(); + + if (m_focus >= position(itr)) + m_focus++; + + base_type::insert(itr, d); + m_signalChanged.emit(); +} + +void +ViewDownloads::received_erase(core::Download* d) { + iterator itr = std::find(begin(), end(), d); + + if (itr == end()) + return; + + if (m_focus > position(itr)) + m_focus--; + + base_type::erase(itr); + m_signalChanged.emit(); +} + +} diff --git a/src/core/view_downloads.h b/src/core/view_downloads.h new file mode 100644 index 00000000..b5654e06 --- /dev/null +++ b/src/core/view_downloads.h @@ -0,0 +1,129 @@ +// rTorrent - BitTorrent client +// Copyright (C) 2005-2006, Jari Sundell +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// In addition, as a special exception, the copyright holders give +// permission to link the code of portions of this program with the +// OpenSSL library under certain conditions as described in each +// individual source file, and distribute linked combinations +// including the two. +// +// You must obey the GNU General Public License in all respects for +// all of the code used other than OpenSSL. If you modify file(s) +// with this exception, you may extend this exception to your version +// of the file(s), but you are not obligated to do so. If you do not +// wish to do so, delete this exception statement from your version. +// If you delete this exception statement from all source files in the +// program, then also delete it here. +// +// Contact: Jari Sundell +// +// Skomakerveien 33 +// 3185 Skoppum, NORWAY + +// 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? + +#ifndef RTORRENT_CORE_VIEW_DOWNLOADS_H +#define RTORRENT_CORE_VIEW_DOWNLOADS_H + +#include +#include +#include + +namespace core { + +class Download; +class DownloadList; + +class ViewDownloads : public std::vector { +public: + typedef std::vector base_type; + typedef sigc::signal0 signal_type; + + // Switch with a base class that defines some function. We might + // want to include some parameters etc. + // + // Also, it should be possible to check the focus index etc. + typedef bool (*sort_slot)(core::Download* d1, core::Download* d2); + + using base_type::iterator; + using base_type::const_iterator; + using base_type::reverse_iterator; + using base_type::const_reverse_iterator; + + 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() : m_sortNew(NULL) {} + ~ViewDownloads(); + + void initialize(const std::string& name, core::DownloadList* list); + + const std::string& name() const { return m_name; } + + 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(); } + + void next_focus(); + void prev_focus(); + + void sort(sort_slot s); + + void set_sort_new(sort_slot s) { m_sortNew = s; } + + signal_type& signal_changed() { return m_signalChanged; } + +private: + ViewDownloads(const ViewDownloads&); + void operator = (const ViewDownloads&); + + void received_insert(core::Download* d); + void received_erase(core::Download* d); + + size_type position(const_iterator itr) const { return (size_type)(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_focus; + + sort_slot m_sortNew; + + // Timer, last changed. + + signal_type m_signalChanged; +}; + +} + +#endif diff --git a/src/core/view_manager.cc b/src/core/view_manager.cc new file mode 100644 index 00000000..b842ba87 --- /dev/null +++ b/src/core/view_manager.cc @@ -0,0 +1,133 @@ +// rTorrent - BitTorrent client +// Copyright (C) 2005-2006, Jari Sundell +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// In addition, as a special exception, the copyright holders give +// permission to link the code of portions of this program with the +// OpenSSL library under certain conditions as described in each +// individual source file, and distribute linked combinations +// including the two. +// +// You must obey the GNU General Public License in all respects for +// all of the code used other than OpenSSL. If you modify file(s) +// with this exception, you may extend this exception to your version +// of the file(s), but you are not obligated to do so. If you do not +// wish to do so, delete this exception statement from your version. +// If you delete this exception statement from all source files in the +// program, then also delete it here. +// +// Contact: Jari Sundell +// +// Skomakerveien 33 +// 3185 Skoppum, NORWAY + +#include "config.h" + +#include +#include +#include + +#include "download.h" +#include "view_downloads.h" +#include "view_manager.h" + +namespace core { + +ViewManager::ViewManager(DownloadList* dl) : + m_list(dl) { +} + +void +ViewManager::clear() { + std::for_each(begin(), end(), rak::call_delete()); + + base_type::clear(); +} + +ViewManager::iterator +ViewManager::insert(const std::string& name) { + if (find(name) != end()) + throw torrent::internal_error("ViewManager::insert(...) name already inserted."); + + ViewDownloads* view = new ViewDownloads(); + view->initialize(name, m_list); + + return base_type::insert(end(), view); +} + +ViewManager::iterator +ViewManager::find(const std::string& name) { + return std::find_if(begin(), end(), rak::equal(name, std::mem_fun(&ViewDownloads::name))); +} + +ViewManager::iterator +ViewManager::find_throw(const std::string& name) { + iterator itr = std::find_if(begin(), end(), rak::equal(name, std::mem_fun(&ViewDownloads::name))); + + if (itr == end()) + throw torrent::input_error("Could not find view: " + name); + + return itr; +} + +// Put this somewhere else. +bool +view_sort_name(core::Download* d1, core::Download* d2) { + return d1->download()->name() < d2->download()->name(); +} + +bool +view_sort_name_reverse(core::Download* d1, core::Download* d2) { + return d1->download()->name() > d2->download()->name(); +} + +void +ViewManager::sort(const std::string& name, const std::string& sort) { + iterator itr = find_throw(name); + + // Quick and dirty hack. + if (sort == "none") + (*itr)->sort(NULL); + + else if (sort == "name") + (*itr)->sort(&view_sort_name); + + else if (sort == "name_reverse") + (*itr)->sort(&view_sort_name_reverse); + + else + throw torrent::input_error("Invalid sorting identifier."); +} + +void +ViewManager::set_sort_new(const std::string& name, const std::string& sort) { + iterator itr = find_throw(name); + + // Quick and dirty hack. + if (sort == "none") + (*itr)->set_sort_new(NULL); + + else if (sort == "name") + (*itr)->set_sort_new(&view_sort_name); + + else if (sort == "name_reverse") + (*itr)->set_sort_new(&view_sort_name_reverse); + + else + throw torrent::input_error("Invalid sorting identifier."); +} + +} diff --git a/src/core/view_manager.h b/src/core/view_manager.h new file mode 100644 index 00000000..6de14f6b --- /dev/null +++ b/src/core/view_manager.h @@ -0,0 +1,93 @@ +// rTorrent - BitTorrent client +// Copyright (C) 2005-2006, Jari Sundell +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// In addition, as a special exception, the copyright holders give +// permission to link the code of portions of this program with the +// OpenSSL library under certain conditions as described in each +// individual source file, and distribute linked combinations +// including the two. +// +// You must obey the GNU General Public License in all respects for +// all of the code used other than OpenSSL. If you modify file(s) +// with this exception, you may extend this exception to your version +// of the file(s), but you are not obligated to do so. If you do not +// wish to do so, delete this exception statement from your version. +// If you delete this exception statement from all source files in the +// program, then also delete it here. +// +// Contact: Jari Sundell +// +// Skomakerveien 33 +// 3185 Skoppum, NORWAY + +#ifndef RTORRENT_CORE_VIEW_MANAGER_H +#define RTORRENT_CORE_VIEW_MANAGER_H + +#include + +namespace core { + +class ViewDownloads; + +class ViewManager : public rak::unordered_vector { +public: + typedef rak::unordered_vector base_type; + + using base_type::iterator; + using base_type::const_iterator; + using base_type::reverse_iterator; + using base_type::const_reverse_iterator; + + 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; + + ViewManager(DownloadList* dl); + ~ViewManager() { clear(); } + + // Ffff... Just throwing together an interface, need to think some + // more on this. + + void clear(); + + iterator insert(const std::string& name); + + // When erasing, just 'disable' the view so that the users won't + // suddenly find their pointer dangling? + + iterator find(const std::string& name); + iterator find_throw(const std::string& name); + + void sort(const std::string& name, const std::string& sort); + + // For now, just take the sort type as a string. Later might move + // this lookup outside. + void set_sort_new(const std::string& name, const std::string& sort); + +private: + DownloadList* m_list; +}; + +} + +#endif diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc index afcf0a5d..70e22fb5 100644 --- a/src/display/window_download_list.cc +++ b/src/display/window_download_list.cc @@ -39,6 +39,7 @@ #include #include "core/download.h" +#include "core/view_downloads.h" #include "canvas.h" #include "globals.h" @@ -47,11 +48,11 @@ namespace display { -WindowDownloadList::WindowDownloadList(DList* l) : +WindowDownloadList::WindowDownloadList(core::ViewDownloads* l) : Window(new Canvas, true), - m_list(l) { + m_view(l) { - m_connChanged = m_list->signal_changed().connect(sigc::mem_fun(*this, &Window::mark_dirty)); + m_connChanged = m_view->signal_changed().connect(sigc::mem_fun(*this, &Window::mark_dirty)); } WindowDownloadList::~WindowDownloadList() { @@ -64,20 +65,20 @@ WindowDownloadList::redraw() { m_canvas->erase(); - if (m_list->base().empty() || m_canvas->get_width() < 5) + if (m_view->empty() || m_canvas->get_width() < 5) return; - typedef std::pair Range; + typedef std::pair Range; - Range range = rak::advance_bidirectional(m_list->begin(), - m_list->get_focus() != m_list->end() ? m_list->get_focus() : m_list->begin(), - m_list->end(), + Range range = rak::advance_bidirectional(m_view->begin(), + m_view->focus() != m_view->end() ? m_view->focus() : m_view->begin(), + m_view->end(), 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_list->end()) + if (range.second != m_view->end()) ++range.second; int pos = 0; @@ -88,13 +89,13 @@ WindowDownloadList::redraw() { char* last = buffer + m_canvas->get_width() - 2; position = print_download_title(buffer, last, *range.first); - m_canvas->print(0, pos++, "%c %s", range.first == m_list->get_focus() ? '*' : ' ', buffer); + m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); position = print_download_info(buffer, last, *range.first); - m_canvas->print(0, pos++, "%c %s", range.first == m_list->get_focus() ? '*' : ' ', buffer); + m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); position = print_download_status(buffer, last, *range.first); - m_canvas->print(0, pos++, "%c %s", range.first == m_list->get_focus() ? '*' : ' ', buffer); + m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); ++range.first; } diff --git a/src/display/window_download_list.h b/src/display/window_download_list.h index e3c23437..64438f09 100644 --- a/src/display/window_download_list.h +++ b/src/display/window_download_list.h @@ -42,23 +42,24 @@ #include "window.h" #include "core/download_list.h" -#include "utils/list_focus.h" + +namespace core { + class ViewDownloads; +} namespace display { class WindowDownloadList : public Window { public: - typedef utils::ListFocus DList; - - WindowDownloadList(DList* l); + WindowDownloadList(core::ViewDownloads* l); ~WindowDownloadList(); virtual void redraw(); private: - DList* m_list; + core::ViewDownloads* m_view; - sigc::connection m_connChanged; + sigc::connection m_connChanged; }; } diff --git a/src/main.cc b/src/main.cc index 08b3118f..7b9fe60c 100644 --- a/src/main.cc +++ b/src/main.cc @@ -162,6 +162,9 @@ main(int argc, char** argv) { // torrent::ConnectionManager* is valid etc. initialize_option_handler(control); + control->variable()->process_command("view_add = main"); + control->variable()->process_command("view_sort_new = main,name"); + // Move env and go through "try_import". if (!control->variable()->process_file("~/.rtorrent.rc")) control->core()->get_log_important().push_front("Could not load \"~/.rtorrent.rc\"."); diff --git a/src/option_handler_rules.cc b/src/option_handler_rules.cc index 4d9ccd55..830974ae 100644 --- a/src/option_handler_rules.cc +++ b/src/option_handler_rules.cc @@ -53,6 +53,7 @@ #include "core/download.h" #include "core/manager.h" +#include "core/view_manager.h" #include "ui/root.h" #include "utils/directory.h" #include "utils/variable_generic.h" @@ -63,12 +64,6 @@ #include "option_handler_rules.h" #include "command_scheduler.h" -void -apply_working_directory(const std::string& path) { - if (chdir(path.c_str()) != 0) - throw torrent::input_error("Could not change working directory."); -} - void apply_hash_read_ahead(__UNUSED Control* m, int arg) { torrent::set_hash_read_ahead(arg << 20); @@ -90,11 +85,6 @@ apply_port_range(Control* m, const std::string& arg) { m->core()->set_port_range(a, b); } -void -apply_http_proxy(Control* m, const std::string& arg) { - m->core()->get_poll_manager()->get_http_stack()->set_http_proxy(arg); -} - void apply_load(Control* m, const std::string& arg) { m->core()->try_create_download_expand(arg, false, false, true); @@ -164,7 +154,7 @@ apply_enable_trackers(Control* m, __UNUSED const std::string& arg) { else tl.get(i).disable(); - if (state && control->variable()->get_string("use_udp_trackers") == "no") + if (state && !control->variable()->get_value("use_udp_trackers")) (*itr)->enable_udp_trackers(false); } } @@ -192,29 +182,67 @@ apply_tos(const std::string& arg) { throw torrent::input_error("Invalid TOS identifier."); } +void +apply_view_add(Control* control, const std::string& arg) { + // Restrict the range of characters. + + control->view_manager()->insert(rak::trim(arg)); +} + +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; + + std::string sort = rak::trim(*itr); + ++itr; + + if (name.empty() || sort.empty() || itr != rak::split_iterator(arg)) + throw torrent::input_error("Invalid number of arguments."); + + control->view_manager()->sort(name, sort); +} + +void +apply_view_sort_new(Control* control, const std::string& arg) { + rak::split_iterator_t itr = rak::split_iterator(arg, ','); + + std::string name = rak::trim(*itr); + ++itr; + + std::string sort = rak::trim(*itr); + ++itr; + + if (name.empty() || sort.empty() || itr != rak::split_iterator(arg)) + throw torrent::input_error("Invalid number of arguments."); + + control->view_manager()->set_sort_new(name, sort); +} + void initialize_option_handler(Control* c) { utils::VariableMap* variables = control->variable(); // Cleaned up. - variables->insert("check_hash", new utils::VariableAny("yes")); - variables->insert("use_udp_trackers", new utils::VariableAny("yes")); - variables->insert("port_open", new utils::VariableAny("yes")); - variables->insert("port_random", new utils::VariableAny("yes")); + variables->insert("check_hash", new utils::VariableBool(true)); + variables->insert("use_udp_trackers", new utils::VariableBool(true)); + variables->insert("port_open", new utils::VariableBool(true)); + variables->insert("port_random", new utils::VariableBool(true)); variables->insert("tracker_dump", new utils::VariableAny(std::string())); variables->insert("session", new utils::VariableStringSlot(rak::mem_fn(&control->core()->download_store(), &core::DownloadStore::path), rak::mem_fn(&control->core()->download_store(), &core::DownloadStore::set_path))); - variables->insert("session_lock", new utils::VariableAny("yes")); - variables->insert("session_on_completion", new utils::VariableAny("yes")); + variables->insert("session_lock", new utils::VariableBool(true)); + variables->insert("session_on_completion", new utils::VariableBool(true)); variables->insert("connection_leech", new utils::VariableAny("leech")); variables->insert("connection_seed", new utils::VariableAny("seed")); variables->insert("directory", new utils::VariableAny("./")); - variables->insert("working_directory", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::ptr_fn(&apply_working_directory))); variables->insert("tos", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::ptr_fn(&apply_tos))); variables->insert("bind", new utils::VariableStringSlot(rak::mem_fn(control->core(), &core::Manager::bind_address), @@ -222,6 +250,9 @@ initialize_option_handler(Control* c) { variables->insert("ip", new utils::VariableStringSlot(rak::mem_fn(control->core(), &core::Manager::local_address), rak::mem_fn(control->core(), &core::Manager::set_local_address))); + variables->insert("http_proxy", new utils::VariableStringSlot(rak::mem_fn(c->core()->get_poll_manager()->get_http_stack(), &core::CurlStack::http_proxy), + rak::mem_fn(c->core()->get_poll_manager()->get_http_stack(), &core::CurlStack::set_http_proxy))); + variables->insert("min_peers", new utils::VariableValue(40)); variables->insert("max_peers", new utils::VariableValue(100)); variables->insert("max_uploads", new utils::VariableValue(15)); @@ -242,6 +273,10 @@ initialize_option_handler(Control* c) { variables->insert("try_import", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::mem_fn(control->variable(), &utils::VariableMap::process_file_nothrow))); + variables->insert("view_add", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_view_add, 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("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()), @@ -258,7 +293,10 @@ initialize_option_handler(Control* c) { variables->insert("hash_read_ahead", new utils::VariableValueSlot(rak::ptr_fn(torrent::hash_read_ahead), rak::bind_ptr_fn(&apply_hash_read_ahead, c))); variables->insert("hash_interval", new utils::VariableValueSlot(rak::ptr_fn(torrent::hash_interval), rak::bind_ptr_fn(&apply_hash_interval, c))); - variables->insert("umask", new utils::VariableValueSlot(rak::mem_fn(control, &Control::umask), rak::mem_fn(control, &Control::set_umask), 8)); + variables->insert("umask", new utils::VariableValueSlot(rak::mem_fn(control, &Control::umask), + rak::mem_fn(control, &Control::set_umask), 8)); + variables->insert("working_directory", new utils::VariableStringSlot(rak::mem_fn(control, &Control::working_directory), + rak::mem_fn(control, &Control::set_working_directory))); variables->insert("load", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_load, c))); variables->insert("load_start", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_load_start, c))); @@ -267,5 +305,4 @@ initialize_option_handler(Control* c) { variables->insert("enable_trackers", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_enable_trackers, c))); variables->insert("encoding_list", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_encoding_list, c))); - variables->insert("http_proxy", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_http_proxy, c))); } diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index 6170e544..e961303d 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -45,7 +45,10 @@ #include #include "core/download.h" +#include "core/download_list.h" #include "core/manager.h" +#include "core/view_downloads.h" +#include "core/view_manager.h" #include "input/bindings.h" #include "input/manager.h" @@ -77,12 +80,12 @@ DownloadList::DownloadList(Control* c) : m_uiDownload(NULL), - m_downloadList(&c->core()->download_list()), - m_control(c), m_bindings(new input::Bindings) { - m_uiArray[DISPLAY_DOWNLOAD_LIST] = new ElementDownloadList(&m_downloadList); + m_view = *c->view_manager()->find_throw("main"); + + m_uiArray[DISPLAY_DOWNLOAD_LIST] = new ElementDownloadList(m_view); m_uiArray[DISPLAY_LOG] = new ElementLogComplete(&m_control->core()->get_log_complete()); m_windowLog = new WLog(&m_control->core()->get_log_important()); @@ -179,44 +182,44 @@ DownloadList::disable_display() { void DownloadList::receive_next() { - m_downloadList.inc_focus(); + m_view->next_focus(); } void DownloadList::receive_prev() { - m_downloadList.dec_focus(); + m_view->prev_focus(); } void DownloadList::receive_start_download() { - if (m_downloadList.get_focus() == m_downloadList.end()) + if (m_view->focus() == m_view->end()) return; - m_control->core()->download_list().start(*m_downloadList.get_focus()); + m_control->core()->download_list().start(*m_view->focus()); } void DownloadList::receive_stop_download() { - if (m_downloadList.get_focus() == m_downloadList.end()) + if (m_view->focus() == m_view->end()) return; - if ((*m_downloadList.get_focus())->download()->is_active()) - m_control->core()->download_list().stop(*m_downloadList.get_focus()); + if ((*m_view->focus())->download()->is_active()) + m_control->core()->download_list().stop(*m_view->focus()); else - m_downloadList.set_focus(m_control->core()->download_list().erase(m_downloadList.get_focus())); + m_control->core()->download_list().erase(*m_view->focus()); } void DownloadList::receive_close_download() { - if (m_downloadList.get_focus() == m_downloadList.end()) + if (m_view->focus() == m_view->end()) return; - m_control->core()->download_list().close(*m_downloadList.get_focus()); + m_control->core()->download_list().close(*m_view->focus()); } void DownloadList::receive_view_download() { - if (m_downloadList.get_focus() == m_downloadList.end()) + if (m_view->focus() == m_view->end()) return; if (m_uiDownload != NULL) @@ -224,7 +227,7 @@ DownloadList::receive_view_download() { disable(); - m_uiDownload = new Download(*m_downloadList.get_focus(), m_control); + m_uiDownload = new Download(*m_view->focus(), m_control); m_uiDownload->activate(); m_uiDownload->get_bindings()[KEY_LEFT] = sigc::mem_fun(*this, &DownloadList::receive_exit_download); @@ -246,26 +249,26 @@ DownloadList::receive_exit_download() { void DownloadList::receive_next_priority() { - if (m_downloadList.get_focus() == m_downloadList.end()) + if (m_view->focus() == m_view->end()) return; - (*m_downloadList.get_focus())->set_priority(((*m_downloadList.get_focus())->priority() + 1) % 4); + (*m_view->focus())->set_priority(((*m_view->focus())->priority() + 1) % 4); } void DownloadList::receive_prev_priority() { - if (m_downloadList.get_focus() == m_downloadList.end()) + if (m_view->focus() == m_view->end()) return; - (*m_downloadList.get_focus())->set_priority(((*m_downloadList.get_focus())->priority() - 1) % 4); + (*m_view->focus())->set_priority(((*m_view->focus())->priority() - 1) % 4); } void DownloadList::receive_check_hash() { - if (m_downloadList.get_focus() == m_downloadList.end()) + if (m_view->focus() == m_view->end()) return; - m_control->core()->check_hash(*m_downloadList.get_focus()); + m_control->core()->check_hash(*m_view->focus()); } void @@ -312,11 +315,11 @@ DownloadList::receive_exit_input(Input type) { break; case INPUT_CHANGE_DIRECTORY: - if (m_downloadList.get_focus() == m_downloadList.end()) + if (m_view->focus() == m_view->end()) throw torrent::input_error("No download in focus to change root directory."); - (*m_downloadList.get_focus())->variable()->set("directory", rak::trim(m_windowTextInput->get_input()->str())); - m_control->core()->push_log("New root dir \"" + (*m_downloadList.get_focus())->variable()->get_string("directory") + "\" for torrent."); + (*m_view->focus())->variable()->set("directory", rak::trim(m_windowTextInput->get_input()->str())); + m_control->core()->push_log("New root dir \"" + (*m_view->focus())->variable()->get_string("directory") + "\" for torrent."); break; case INPUT_COMMAND: @@ -353,8 +356,7 @@ DownloadList::receive_change(Display d) { void DownloadList::receive_download_erased(core::Download* d) { - if (m_downloadList.get_focus() == m_downloadList.end() || - *m_downloadList.get_focus() != d) + if (m_view->focus() == m_view->end() || *m_view->focus() != d) return; if (m_uiDownload != NULL) diff --git a/src/ui/download_list.h b/src/ui/download_list.h index 56cb5a97..676bda50 100644 --- a/src/ui/download_list.h +++ b/src/ui/download_list.h @@ -39,14 +39,17 @@ #include -#include "core/download_list.h" #include "display/manager.h" -#include "utils/list_focus.h" #include "globals.h" class Control; +namespace core { + class Download; + class ViewDownloads; +} + namespace input { class Bindings; } @@ -74,8 +77,6 @@ public: typedef display::WindowLogComplete WLogComplete; typedef display::WindowTitle WTitle; - typedef utils::ListFocus DList; - typedef sigc::slot1 SlotOpenUri; typedef display::Manager::iterator MItr; @@ -156,7 +157,7 @@ private: Download* m_uiDownload; - DList m_downloadList; + core::ViewDownloads* m_view; Control* m_control; input::Bindings* m_bindings; diff --git a/src/ui/element_download_list.cc b/src/ui/element_download_list.cc index 06d820e1..dc4f84c7 100644 --- a/src/ui/element_download_list.cc +++ b/src/ui/element_download_list.cc @@ -46,9 +46,9 @@ namespace ui { -ElementDownloadList::ElementDownloadList(DList* l) : +ElementDownloadList::ElementDownloadList(core::ViewDownloads* l) : m_window(NULL), - m_list(l) { + m_view(l) { } void @@ -58,7 +58,7 @@ ElementDownloadList::activate(Control* c, MItr mItr) { c->input()->push_front(&m_bindings); - *mItr = m_window = new WDownloadList(m_list); + *mItr = m_window = new WDownloadList(m_view); } void diff --git a/src/ui/element_download_list.h b/src/ui/element_download_list.h index deb0d873..f574591d 100644 --- a/src/ui/element_download_list.h +++ b/src/ui/element_download_list.h @@ -38,12 +38,15 @@ #define RTORRENT_UI_ELEMENT_DOWNLOAD_LIST_H #include "core/download_list.h" -#include "utils/list_focus.h" #include "element_base.h" class Control; +namespace core { + class ViewDownloads; +} + namespace display { class WindowDownloadList; } @@ -52,18 +55,16 @@ namespace ui { class ElementDownloadList : public ElementBase { public: - typedef display::WindowDownloadList WDownloadList; - typedef utils::ListFocus DList; + typedef display::WindowDownloadList WDownloadList; - ElementDownloadList(DList* l); + ElementDownloadList(core::ViewDownloads* l); void activate(Control* c, MItr mItr); void disable(Control* c); private: - WDownloadList* m_window; - - DList* m_list; + WDownloadList* m_window; + core::ViewDownloads* m_view; }; } diff --git a/src/utils/variable_generic.cc b/src/utils/variable_generic.cc index 91c1b325..acc068c8 100644 --- a/src/utils/variable_generic.cc +++ b/src/utils/variable_generic.cc @@ -42,9 +42,6 @@ namespace utils { -VariableAny::~VariableAny() { -} - const torrent::Object& VariableAny::get() { return m_variable; @@ -55,14 +52,6 @@ VariableAny::set(const torrent::Object& arg) { m_variable = arg; } -VariableValue::~VariableValue() { -} - -const torrent::Object& -VariableValue::get() { - return m_variable; -} - void VariableValue::set(const torrent::Object& arg) { uint64_t value; @@ -93,40 +82,31 @@ VariableValue::set(const torrent::Object& arg) { } } -VariableBool::~VariableBool() { -} - -const torrent::Object& -VariableBool::get() { - return m_variable; -} - void VariableBool::set(const torrent::Object& arg) { - if (arg.is_value()) { + switch (arg.type()) { + case torrent::Object::TYPE_VALUE: m_variable = arg.as_value() ? (int64_t)1 : (int64_t)0; + break; - } else if (arg.is_string()) { - - if (arg.as_string() == "yes" || - arg.as_string() == "true") + case torrent::Object::TYPE_STRING: + // Move the checks into some is_true, is_false think in Variable. + if (arg.as_string() == "yes" || arg.as_string() == "true") m_variable = (int64_t)1; - else if (arg.as_string() == "no" || - arg.as_string() == "false") + else if (arg.as_string() == "no" || arg.as_string() == "false") m_variable = (int64_t)0; else throw torrent::input_error("String does not parse as a boolean."); - } else { + break; + + default: throw torrent::input_error("Input is not a boolean."); } } -VariableObject::~VariableObject() { -} - const torrent::Object& VariableObject::get() { if (m_root.empty()) diff --git a/src/utils/variable_generic.h b/src/utils/variable_generic.h index 40c66f43..60919da1 100644 --- a/src/utils/variable_generic.h +++ b/src/utils/variable_generic.h @@ -56,38 +56,26 @@ class VariableAny : public Variable { public: VariableAny(const torrent::Object& v = torrent::Object()) : m_variable(v) {} - virtual ~VariableAny(); virtual const torrent::Object& get(); virtual void set(const torrent::Object& arg); -private: +protected: torrent::Object m_variable; }; -class VariableValue : public Variable { +class VariableValue : public VariableAny { public: - VariableValue(int64_t v) : m_variable(v) {} - virtual ~VariableValue(); + VariableValue(int64_t v) { m_variable = v; } - virtual const torrent::Object& get(); - virtual void set(const torrent::Object& arg); - -private: - torrent::Object m_variable; -}; - -class VariableBool : public Variable { -public: - VariableBool(bool state) : m_variable(state ? (int64_t)1 : (int64_t)0) {} - VariableBool(const torrent::Object& v = torrent::Object((int64_t)0)) { set(v); } - virtual ~VariableBool(); - - virtual const torrent::Object& get(); virtual void set(const torrent::Object& arg); +}; -private: - torrent::Object m_variable; +class VariableBool : public VariableAny { +public: + VariableBool(bool state = false) { m_variable = state ? (int64_t)1 : (int64_t)0; } + + virtual void set(const torrent::Object& arg); }; class VariableObject : public Variable { @@ -99,13 +87,12 @@ public: const std::string& key, Type t = torrent::Object::TYPE_NONE) : m_bencode(b), m_root(root), m_key(key), m_type(t) {} - virtual ~VariableObject(); virtual const torrent::Object& get(); virtual void set(const torrent::Object& arg); private: - torrent::Object* m_bencode; + torrent::Object* m_bencode; std::string m_root; std::string m_key; Type m_type; diff --git a/src/utils/variable_map.cc b/src/utils/variable_map.cc index 2cc4467e..2b5fe2a8 100644 --- a/src/utils/variable_map.cc +++ b/src/utils/variable_map.cc @@ -65,8 +65,8 @@ VariableMap::insert(const std::string& key, Variable* v) { } const VariableMap::mapped_type& -VariableMap::get(const std::string& key) { - iterator itr = base_type::find(key); +VariableMap::get(const std::string& key) const { + const_iterator itr = base_type::find(key); if (itr == base_type::end()) throw torrent::input_error("Variable \"" + key + "\" does not exist."); diff --git a/src/utils/variable_map.h b/src/utils/variable_map.h index 3795b83e..b088a58c 100644 --- a/src/utils/variable_map.h +++ b/src/utils/variable_map.h @@ -50,6 +50,7 @@ class VariableMap : public std::map { public: typedef std::map base_type; typedef torrent::Object mapped_type; + typedef mapped_type::value_type mapped_value_type; static const int max_size_key = 128; static const int max_size_opt = 1024; @@ -65,8 +66,9 @@ public: // Consider taking char* start and finish instead of std::string to // avoid copying. Or make a view class. - const mapped_type& get(const std::string& key); - const std::string& get_string(const std::string& key) { return get(key).as_string(); } + const mapped_type& get(const std::string& key) const; + const std::string& get_string(const std::string& key) const { return get(key).as_string(); } + mapped_value_type get_value(const std::string& key) const { return get(key).as_value(); } void set(const std::string& key, const mapped_type& arg); void set_string(const std::string& key, const std::string& arg) { set(key, mapped_type(arg)); }