diff --git a/rak/algorithm.h b/rak/algorithm.h index a2f74fd9..0af2f903 100644 --- a/rak/algorithm.h +++ b/rak/algorithm.h @@ -75,6 +75,48 @@ advance_bidirectional(_InputIter __first, _InputIter __middle1, _InputIter __las return std::make_pair(__middle1, __middle2); } +struct +string_starts_with : public std::binary_function { + bool operator () (const std::string& complete, const std::string& base) const { + return !complete.compare(0, base.size(), base); + } +}; + +// Count the number of elements from the start of the containers to +// the first inequal element. +template +typename std::iterator_traits<_InputIter>::difference_type +count_base(_InputIter __first1, _InputIter __last1, + _InputIter __first2, _InputIter __last2) { + + typename std::iterator_traits<_InputIter>::difference_type __n = 0; + + for ( ;__first1 != __last1 && __first2 != __last2; ++__first1, ++__first2, ++__n) + if (*__first1 != *__first2) + return __n; + + return __n; +} + +template +typename std::iterator_traits<_InputIter>::value_type +make_base(_InputIter __first, _InputIter __last) { + if (__first == __last) + return ""; + + typename std::iterator_traits<_InputIter>::value_type __base = *__first++; + + for ( ;__first != __last; ++__first) { + typename std::iterator_traits<_InputIter>::difference_type __pos = count_base(__base.begin(), __base.end(), + __first->begin(), __first->end()); + + if (__pos < __base.size()) + __base.resize(__pos); + } + + return __base; +} + } #endif diff --git a/src/core/download_store.cc b/src/core/download_store.cc index 72e0a8d6..37303d73 100644 --- a/src/core/download_store.cc +++ b/src/core/download_store.cc @@ -81,7 +81,9 @@ DownloadStore::get_formated_entries() { return utils::Directory(); utils::Directory d(m_path); - d.update(); + + if (!d.update()) + throw std::runtime_error("core::DownloadStore::update() could not open directory"); d.erase(std::remove_if(d.begin(), d.end(), std::not1(std::ptr_fun(&DownloadStore::is_correct_format))), d.end()); diff --git a/src/display/utils.cc b/src/display/utils.cc index 2d2adb41..b5cb7569 100644 --- a/src/display/utils.cc +++ b/src/display/utils.cc @@ -24,6 +24,7 @@ #include #include +#include #include "core/download.h" #include "utils/timer.h" @@ -63,10 +64,9 @@ print_hhmmss(utils::Timer t) { return "inv_time"; std::stringstream str; - str.width(2); str.fill('0'); - str << u->tm_hour << ':' << u->tm_min << ':' << u->tm_sec; + str << std::setw(2) << u->tm_hour << ':' << std::setw(2) << u->tm_min << ':' << std::setw(2) << u->tm_sec; return str.str(); } diff --git a/src/input/Makefile.am b/src/input/Makefile.am index 76fc66b7..31285ff1 100644 --- a/src/input/Makefile.am +++ b/src/input/Makefile.am @@ -5,6 +5,8 @@ libsub_input_a_SOURCES = \ bindings.h \ manager.cc \ manager.h \ + path_input.cc \ + path_input.h \ text_input.cc \ text_input.h diff --git a/src/input/path_input.cc b/src/input/path_input.cc new file mode 100644 index 00000000..32ac1864 --- /dev/null +++ b/src/input/path_input.cc @@ -0,0 +1,137 @@ +// rTorrent - BitTorrent client +// Copyright (C) 2005, 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 +// +// Contact: Jari Sundell +// +// Skomakerveien 33 +// 3185 Skoppum, NORWAY + +#include "config.h" + +#include + +#include "path_input.h" + +namespace input { + +struct +string_starts_with : public std::binary_function { + bool operator () (const std::string& complete, const std::string& base) const { + return !complete.compare(0, base.size(), base); + } +}; + +template +typename std::iterator_traits<_InputIter>::difference_type +count_base(_InputIter __first1, _InputIter __last1, + _InputIter __first2, _InputIter __last2) { + + typename std::iterator_traits<_InputIter>::difference_type __n = 0; + + for ( ;__first1 != __last1 && __first2 != __last2; ++__first1, ++__first2, ++__n) + if (*__first1 != *__first2) + return __n; + + return __n; +} + +template +std::string +make_base(_InputIter __first, _InputIter __last) { + if (__first == __last) + return ""; + + std::string __base = *__first++; + + for ( ;__first != __last; ++__first) { + std::string::size_type __pos = count_base(__base.begin(), __base.end(), + __first->begin(), __first->end()); + + if (__pos < __base.size()) + __base.resize(__pos); + } + + return __base; +} + +PathInput::PathInput() { +} + +bool +PathInput::pressed(int key) { + if (key == '\t') + receive_do_complete(); + else + return TextInput::pressed(key); + + return true; +} + +void +PathInput::receive_do_complete() { + size_type dirEnd = find_last_delim(); + + utils::Directory dir(dirEnd != 0 ? str().substr(0, dirEnd) : "./"); + + if (!dir.update() || dir.empty()) { + str() += "!"; + mark_dirty(); + + return; + } + + Range r = find_incomplete(dir, str().substr(dirEnd, get_pos())); + + if (r.first == r.second) + return; // Show some nice colors here. + + std::string base = make_base(r.first, r.second); + + // Clear the path after the cursor to make this code cleaner. It's + // not really nessesary to add the complexity just because someone + // might start tab-completeing in the middle of a path. + str().resize(dirEnd); + str().insert(dirEnd, base); + + set_pos(dirEnd + base.size()); + + mark_dirty(); +} + +PathInput::size_type +PathInput::find_last_delim() { + size_type r = str().rfind('/', get_pos()); + + if (r == npos) + return 0; + else if (r == size()) + return r; + else + return r + 1; +} + +PathInput::Range +PathInput::find_incomplete(utils::Directory& d, const std::string& f) { + Range r; + + r.first = std::find_if(d.begin(), d.end(), std::bind2nd(string_starts_with(), f)); + r.second = std::find_if(r.first, d.end(), std::not1(std::bind2nd(string_starts_with(), f))); + + return r; +} + +} diff --git a/src/input/path_input.h b/src/input/path_input.h new file mode 100644 index 00000000..8075a213 --- /dev/null +++ b/src/input/path_input.h @@ -0,0 +1,50 @@ +// rTorrent - BitTorrent client +// Copyright (C) 2005, 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 +// +// Contact: Jari Sundell +// +// Skomakerveien 33 +// 3185 Skoppum, NORWAY + +#ifndef RTORRENT_INPUT_PATH_INPUT_H +#define RTORRENT_INPUT_PATH_INPUT_H + +#include "utils/directory.h" + +#include "text_input.h" + +namespace input { + +class PathInput : public TextInput { +public: + typedef std::pair Range; + + PathInput(); + virtual ~PathInput() {} + + virtual bool pressed(int key); + +private: + void receive_do_complete(); + + size_type find_last_delim(); + Range find_incomplete(utils::Directory& d, const std::string& f); +}; + +} + +#endif diff --git a/src/input/text_input.h b/src/input/text_input.h index 038cbbf1..2ee77ea2 100644 --- a/src/input/text_input.h +++ b/src/input/text_input.h @@ -36,24 +36,29 @@ public: using Base::c_str; using Base::empty; using Base::size; + using Base::size_type; + using Base::npos; TextInput() : m_pos(0), m_alt(false) {} + virtual ~TextInput() {} - size_type get_pos() { return m_pos; } + size_type get_pos() { return m_pos; } + void set_pos(size_type pos) { m_pos = pos; } - bool pressed(int key); + virtual bool pressed(int key); - void clear() { m_pos = 0; m_alt = false; Base::clear(); } + void clear() { m_pos = 0; m_alt = false; Base::clear(); } - void slot_dirty(SlotDirty s) { m_slotDirty = s; } + void slot_dirty(SlotDirty s) { m_slotDirty = s; } + void mark_dirty() { m_slotDirty(); } - const std::string& str() { return *this; } + std::string& str() { return *this; } private: - size_type m_pos; + size_type m_pos; - bool m_alt; - SlotDirty m_slotDirty; + bool m_alt; + SlotDirty m_slotDirty; }; } diff --git a/src/main.cc b/src/main.cc index 79c8e613..1f6841d9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -241,6 +241,7 @@ print_help() { std::cout << " 1,2 Adjust max uploads" << std::endl; std::cout << " 3,4,5,6 Adjust min/max connected peers" << std::endl; std::cout << " t Query tracker for more peers" << std::endl; + std::cout << " * Snub peer" << std::endl; std::cout << " right View files" << std::endl; std::cout << " p View peer information" << std::endl; std::cout << " o View trackers" << std::endl; diff --git a/src/ui/download.cc b/src/ui/download.cc index 2683f1e5..10130774 100644 --- a/src/ui/download.cc +++ b/src/ui/download.cc @@ -80,6 +80,8 @@ Download::~Download() { delete m_windowDownloadStatus; delete m_windowMainStatus; + delete m_bindings; + m_connPeerConnected.disconnect(); m_connPeerDisconnected.disconnect(); } @@ -216,6 +218,16 @@ Download::receive_change(Display d) { activate_display(d); } +void +Download::receive_snub_peer() { + if (m_focus == m_peers.end()) + return; + + m_focus->set_snubbed(!m_focus->get_snubbed()); + + mark_dirty(); +} + void Download::bind_keys() { (*m_bindings)['a'] = sigc::bind(sigc::mem_fun(*this, &Download::receive_throttle), 1); @@ -245,6 +257,9 @@ Download::bind_keys() { m_uiArray[DISPLAY_PEER_INFO]->get_bindings()[' '] = sigc::bind(sigc::mem_fun(*this, &Download::receive_change), DISPLAY_PEER_LIST); m_uiArray[DISPLAY_FILE_LIST]->get_bindings()[KEY_LEFT] = sigc::bind(sigc::mem_fun(*this, &Download::receive_change), DISPLAY_PEER_LIST); m_uiArray[DISPLAY_TRACKER_LIST]->get_bindings()[' '] = sigc::bind(sigc::mem_fun(*this, &Download::receive_change), DISPLAY_PEER_LIST); + + m_uiArray[DISPLAY_PEER_LIST]->get_bindings()['*'] = sigc::mem_fun(*this, &Download::receive_snub_peer); + m_uiArray[DISPLAY_PEER_INFO]->get_bindings()['*'] = sigc::mem_fun(*this, &Download::receive_snub_peer); } void diff --git a/src/ui/download.h b/src/ui/download.h index 8f6151e0..82c490f7 100644 --- a/src/ui/download.h +++ b/src/ui/download.h @@ -88,6 +88,8 @@ private: void receive_max_peers(int t); void receive_change(Display d); + void receive_snub_peer(); + void bind_keys(); void mark_dirty(); diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index eb8f3c87..73d26f7c 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -29,7 +29,7 @@ #include "core/download.h" #include "input/bindings.h" -#include "input/text_input.h" +#include "input/path_input.h" #include "display/window_download_list.h" #include "display/window_http_queue.h" @@ -48,7 +48,7 @@ namespace ui { DownloadList::DownloadList(Control* c) : m_windowTitle(new WTitle("rtorrent " VERSION " - " + torrent::get(torrent::LIBRARY_NAME))), m_windowStatus(new WStatus(&c->get_core())), - m_windowTextInput(new WInput(new input::TextInput)), + m_windowTextInput(new WInput(new input::PathInput)), m_windowHttpQueue(new WHttp(&c->get_core().get_http_queue())), m_taskUpdate(sigc::mem_fun(*this, &DownloadList::task_update)), diff --git a/src/utils/directory.cc b/src/utils/directory.cc index 9cdb0d09..0d18ba0d 100644 --- a/src/utils/directory.cc +++ b/src/utils/directory.cc @@ -31,7 +31,7 @@ namespace utils { -void +bool Directory::update() { if (m_path.empty()) throw std::logic_error("Directory::update() tried to open an empty path"); @@ -39,19 +39,21 @@ Directory::update() { DIR* d = opendir(m_path.c_str()); if (d == NULL) - throw std::runtime_error("Directory::update() could not open directory"); + return false; struct dirent* ent; while ((ent = readdir(d)) != NULL) { std::string de(ent->d_name); - if (de != "." && de != "..") + if (!de.empty() && de[0] != '.') Base::push_back(ent->d_name); } closedir(d); Base::sort(std::less()); + + return true; } Directory::Base diff --git a/src/utils/directory.h b/src/utils/directory.h index 887bf5c7..1a09fde5 100644 --- a/src/utils/directory.h +++ b/src/utils/directory.h @@ -50,7 +50,7 @@ public: Directory() {} Directory(const std::string& path) : m_path(path) {} - void update(); + bool update(); const std::string& get_path() { return m_path; }