From 0b45062eb35350e356addfc49aa99e71e0cd168a Mon Sep 17 00:00:00 2001 From: rakshasa Date: Sun, 6 Aug 2006 18:20:56 +0000 Subject: [PATCH] * Use varargs in Canvas::print. * Added min/max size to display::Window. Added flags for windows that should be on located at the left/bottom side of a Frame. * Reversed print order in WindowLog*. * Added display::TextElement and subclasses for flexible printing to a char buffer with associated attributes. Added display::Attributes and display::Canvas::print_attributes. * Added display::WindowText for use with display::TextElement's. * Added ui::ElementMenu for generic menus, and started work adding this to ui::Download. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@751 e378c898-3ddf-0310-93e7-cc216c733640 --- rak/functional.h | 33 ++- rak/ranges.h | 8 +- src/core/log.cc | 2 +- src/core/view_manager.cc | 2 +- src/display/Makefile.am | 8 + src/display/attributes.h | 82 ++++++ src/display/canvas.cc | 23 ++ src/display/canvas.h | 68 ++--- src/display/frame.cc | 278 ++++++++++++++----- src/display/frame.h | 27 +- src/display/manager.cc | 1 + src/display/text_element.h | 58 ++++ src/display/text_element_list.cc | 62 +++++ src/display/text_element_list.h | 73 +++++ src/display/text_element_string.cc | 76 +++++ src/display/text_element_string.h | 66 +++++ src/display/window.cc | 8 +- src/display/window.h | 31 ++- src/display/window_download_chunks_seen.cc | 4 +- src/display/window_download_list.cc | 4 +- src/display/window_download_list.h | 2 +- src/display/window_download_statusbar.cc | 2 +- src/display/window_download_transfer_list.cc | 4 +- src/display/window_file_list.cc | 2 +- src/display/window_http_queue.cc | 4 +- src/display/window_input.h | 2 +- src/display/window_log.cc | 17 +- src/display/window_log_complete.cc | 24 +- src/display/window_peer_info.cc | 2 +- src/display/window_peer_list.cc | 2 +- src/display/window_statusbar.h | 2 +- src/display/window_string_list.cc | 2 +- src/display/window_text.cc | 86 ++++++ src/display/window_text.h | 77 +++++ src/display/window_title.cc | 2 +- src/display/window_title.h | 2 +- src/display/window_tracker_list.cc | 2 +- src/input/manager.cc | 6 +- src/ui/Makefile.am | 2 + src/ui/download.cc | 102 ++++--- src/ui/download.h | 13 +- src/ui/download_list.cc | 3 +- src/ui/download_list.h | 2 +- src/ui/element_base.h | 2 +- src/ui/element_chunks_seen.cc | 5 +- src/ui/element_chunks_seen.h | 2 +- src/ui/element_download_list.cc | 2 +- src/ui/element_download_list.h | 2 +- src/ui/element_file_list.cc | 5 +- src/ui/element_file_list.h | 2 +- src/ui/element_log_complete.cc | 2 +- src/ui/element_log_complete.h | 2 +- src/ui/element_menu.cc | 161 +++++++++++ src/ui/element_menu.h | 101 +++++++ src/ui/element_peer_info.cc | 5 +- src/ui/element_peer_info.h | 2 +- src/ui/element_peer_list.cc | 10 +- src/ui/element_peer_list.h | 6 +- src/ui/element_string_list.cc | 2 +- src/ui/element_string_list.h | 2 +- src/ui/element_tracker_list.cc | 5 +- src/ui/element_tracker_list.h | 2 +- src/ui/element_transfer_list.cc | 5 +- src/ui/element_transfer_list.h | 2 +- src/ui/root.cc | 1 + src/utils/variable_map.cc | 2 +- 66 files changed, 1350 insertions(+), 256 deletions(-) create mode 100644 src/display/attributes.h create mode 100644 src/display/text_element.h create mode 100644 src/display/text_element_list.cc create mode 100644 src/display/text_element_list.h create mode 100644 src/display/text_element_string.cc create mode 100644 src/display/text_element_string.h create mode 100644 src/display/window_text.cc create mode 100644 src/display/window_text.h create mode 100644 src/ui/element_menu.cc create mode 100644 src/ui/element_menu.h diff --git a/rak/functional.h b/rak/functional.h index 15da5c9e..3e45ed05 100644 --- a/rak/functional.h +++ b/rak/functional.h @@ -317,24 +317,37 @@ mem_ptr(Member Class::*m) { } template -struct mem_ptr_ref_t : public std::unary_function { - mem_ptr_ref_t(Member Class::*m) : m_member(m) {} +struct mem_ref_t : public std::unary_function { + mem_ref_t(Member Class::*m) : m_member(m) {} Member& operator () (Class& c) { return c.*m_member; } - const Member& operator () (const Class& c) { - return c.*m_member; - } - Member Class::*m_member; }; template -inline mem_ptr_ref_t -mem_ptr_ref(Member Class::*m) { - return mem_ptr_ref_t(m); +struct const_mem_ref_t : public std::unary_function { + const_mem_ref_t(const Member Class::*m) : m_member(m) {} + + const Member& operator () (const Class& c) { + return c.*m_member; + } + + const Member Class::*m_member; +}; + +template +inline mem_ref_t +mem_ref(Member Class::*m) { + return mem_ref_t(m); +} + +template +inline const_mem_ref_t +const_mem_ref(const Member Class::*m) { + return const_mem_ref_t(m); } template @@ -415,7 +428,7 @@ protected: Operation m_op; value_type m_value; -}; +}; template inline bind2nd_t diff --git a/rak/ranges.h b/rak/ranges.h index cd563962..f5581e37 100644 --- a/rak/ranges.h +++ b/rak/ranges.h @@ -83,7 +83,7 @@ ranges::insert(value_type r) { if (r.first >= r.second) return; - iterator first = std::find_if(begin(), end(), rak::less_equal(r.first, rak::mem_ptr_ref(&value_type::second))); + iterator first = std::find_if(begin(), end(), rak::less_equal(r.first, rak::const_mem_ref(&value_type::second))); if (first == end() || r.second < first->first) { // The new range is before the first, after the last or between @@ -107,13 +107,13 @@ ranges::insert(value_type r) { template inline typename ranges::iterator ranges::find(Type index) { - return std::find_if(begin(), end(), rak::less(index, rak::mem_ptr_ref(&value_type::second))); + return std::find_if(begin(), end(), rak::less(index, rak::const_mem_ref(&value_type::second))); } template inline typename ranges::const_iterator ranges::find(Type index) const { - return std::find_if(begin(), end(), rak::less(index, rak::mem_ptr_ref(&value_type::second))); + return std::find_if(begin(), end(), rak::less(index, rak::const_mem_ref(&value_type::second))); } // Use find with no closest match. @@ -128,7 +128,7 @@ ranges::has(Type index) const { template inline void ranges::unify(iterator first) { - iterator last = std::find_if((first + 1), end(), rak::less(first->second, rak::mem_ptr_ref(&value_type::first))); + iterator last = std::find_if((first + 1), end(), rak::less(first->second, rak::const_mem_ref(&value_type::first))); first->second = std::max(first->second, (last - 1)->second); Base::erase((first + 1), last); diff --git a/src/core/log.cc b/src/core/log.cc index 9f49e170..7ebeacc2 100644 --- a/src/core/log.cc +++ b/src/core/log.cc @@ -59,7 +59,7 @@ Log::push_front(const std::string& msg) { Log::iterator Log::find_older(rak::timer t) { - return std::find_if(begin(), end(), rak::on(rak::mem_ptr_ref(&Type::first), std::bind2nd(std::less_equal(), t))); + return std::find_if(begin(), end(), rak::on(rak::mem_ref(&Type::first), std::bind2nd(std::less_equal(), t))); } } diff --git a/src/core/view_manager.cc b/src/core/view_manager.cc index 1665baef..eede4b7a 100644 --- a/src/core/view_manager.cc +++ b/src/core/view_manager.cc @@ -153,7 +153,7 @@ ViewManager::ViewManager(DownloadList* dl) : void ViewManager::clear() { std::for_each(begin(), end(), rak::call_delete()); - std::for_each(m_sort.begin(), m_sort.end(), rak::on(rak::mem_ptr_ref(&sort_map::value_type::second), rak::call_delete())); + std::for_each(m_sort.begin(), m_sort.end(), rak::on(rak::mem_ref(&sort_map::value_type::second), rak::call_delete())); base_type::clear(); } diff --git a/src/display/Makefile.am b/src/display/Makefile.am index 70f23f2b..497ce116 100644 --- a/src/display/Makefile.am +++ b/src/display/Makefile.am @@ -1,6 +1,7 @@ noinst_LIBRARIES = libsub_display.a libsub_display_a_SOURCES = \ + attributes.h \ canvas.cc \ canvas.h \ client_info.cc \ @@ -11,6 +12,11 @@ libsub_display_a_SOURCES = \ manager.h \ utils.cc \ utils.h \ + text_element.h \ + text_element_list.cc \ + text_element_list.h \ + text_element_string.cc \ + text_element_string.h \ window.cc \ window.h \ window_download_chunks_seen.cc \ @@ -39,6 +45,8 @@ libsub_display_a_SOURCES = \ window_statusbar.h \ window_string_list.cc \ window_string_list.h \ + window_text.cc \ + window_text.h \ window_title.cc \ window_title.h \ window_tracker_list.cc \ diff --git a/src/display/attributes.h b/src/display/attributes.h new file mode 100644 index 00000000..964ff7f4 --- /dev/null +++ b/src/display/attributes.h @@ -0,0 +1,82 @@ +// 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_DISPLAY_ATTRIBUTES_H +#define RTORRENT_DISPLAY_ATTRIBUTES_H + +#include +#include +#include + +// Let us hail the creators of curses for being idiots. The only +// clever move they made was in the naming. +#undef timeout +#undef move + +namespace display { + +class Attributes { +public: + static const int a_invalid = ~int(); + static const int a_normal = A_NORMAL; + static const int a_bold = A_BOLD; + static const int a_reverse = A_REVERSE; + + static const int color_invalid = ~int(); + static const int color_default = 0; + + Attributes() {} + Attributes(const char* pos, int attr, int col) : + m_position(pos), m_attributes(attr), m_colors(col) {} + + const char* position() const { return m_position; } + void set_position(const char* pos) { m_position = pos; } + + int attributes() const { return m_attributes; } + void set_attributes(int attr) { m_attributes = attr; } + + int colors() const { return m_colors; } + void set_colors(int col) { m_colors = col; } + +private: + const char* m_position; + int m_attributes; + int m_colors; +}; + +} + +#endif diff --git a/src/display/canvas.cc b/src/display/canvas.cc index e26f9cc7..7a7f440a 100644 --- a/src/display/canvas.cc +++ b/src/display/canvas.cc @@ -39,6 +39,7 @@ #include #include #include +#include #include "canvas.h" @@ -52,6 +53,28 @@ Canvas::resize(int x, int y, int w, int h) { mvwin(m_window, y, x); } +void +Canvas::print_attributes(unsigned int x, unsigned int y, const char* first, const char* last, const attributes_list* attributes) { + move(x, y); + + int attr = A_NORMAL; + attributes_list::const_iterator attrItr = attributes->begin(); + + while (first != last) { + if (attrItr != attributes->end() && attrItr->position() == first) { + attr = attrItr->attributes(); + // Set colors or something. + + attrItr++; + continue; + } + + waddch(m_window, *first++ | attr); + } + + // Reset the color. +} + void Canvas::initialize() { if (m_isInitialized) diff --git a/src/display/canvas.h b/src/display/canvas.h index f71642af..7d77c665 100644 --- a/src/display/canvas.h +++ b/src/display/canvas.h @@ -38,12 +38,16 @@ #define RTORRENT_DISPLAY_CANVAS_H #include -#include +#include + +#include "attributes.h" namespace display { class Canvas { public: + typedef std::vector attributes_list; + Canvas(int x = 0, int y = 0, int width = 0, int height = 0) : m_window(newwin(height, width, y, x)) {} ~Canvas() { delwin(m_window); } @@ -59,10 +63,13 @@ public: static void resize_term(int x, int y) { resizeterm(y, x); } static void resize_term(std::pair dim) { resizeterm(dim.second, dim.first); } - int get_x() { int x, y; getyx(m_window, y, x); return x; } - int get_y() { int x, y; getyx(m_window, y, x); return y; } - int width() { int x, y; getmaxyx(m_window, y, x); return x; } - int height() { int x, y; getmaxyx(m_window, y, x); return y; } + unsigned int get_x() { int x, y; getyx(m_window, y, x); return x; } + unsigned int get_y() { int x, y; getyx(m_window, y, x); return y; } + + unsigned int width() { int x, y; getmaxyx(m_window, y, x); return x; } + unsigned int height() { int x, y; getmaxyx(m_window, y, x); return y; } + + void move(unsigned int x, unsigned int y) { wmove(m_window, y, x); } chtype get_background() { return getbkgd(m_window); } void set_background(chtype c) { return wbkgdset(m_window, c); } @@ -79,43 +86,14 @@ public: // since the string shall always be a C string choosen at // compiletime. Might cause extra copying of the string? - void print(int x, int y, char* str) { mvwprintw(m_window, y, x, str); } + void print(unsigned int x, unsigned int y, const char* str, ...); - template - void print(int x, int y, char* str, A1 a1) { mvwprintw(m_window, y, x, str, a1); } + void print_attributes(unsigned int x, unsigned int y, const char* first, const char* last, const attributes_list* attributes); - template - void print(int x, int y, char* str, A1 a1, A2 a2) { mvwprintw(m_window, y, x, str, a1, a2); } + void print_char(const chtype ch) { waddch(m_window, ch); } + void print_char(unsigned int x, unsigned int y, const chtype ch) { mvwaddch(m_window, y, x, ch); } - template - void print(int x, int y, char* str, - A1 a1, A2 a2, A3 a3) { mvwprintw(m_window, y, x, str, a1, a2, a3); } - - template - void print(int x, int y, char* str, - A1 a1, A2 a2, A3 a3, A4 a4) { mvwprintw(m_window, y, x, str, a1, a2, a3, a4); } - - template - void print(int x, int y, char* str, - A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { mvwprintw(m_window, y, x, str, a1, a2, a3, a4, a5); } - - template - void print(int x, int y, char* str, - A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { mvwprintw(m_window, y, x, str, a1, a2, a3, a4, a5, a6); } - - template - void print(int x, int y, char* str, - A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { mvwprintw(m_window, y, x, str, a1, a2, a3, a4, a5, a6, a7); } - - template - void print(int x, int y, char* str, - A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { mvwprintw(m_window, y, x, str, a1, a2, a3, a4, a5, a6, a7, a8); } - - void print_char(const chtype ch) { waddch(m_window, ch); } - - void print_char(int x, int y, const chtype ch) { mvwaddch(m_window, y, x, ch); } - - void set_attr(int x, int y, int n, int attr, int color) { mvwchgat(m_window, y, x, n, attr, color, NULL); } + void set_attr(unsigned int x, unsigned int y, unsigned int n, int attr, int color) { mvwchgat(m_window, y, x, n, attr, color, NULL); } // Initialize stdscr. static void initialize(); @@ -137,9 +115,15 @@ private: WINDOW* m_window; }; -// Undefines 'timeout' that ncurses defines which screws up the global -// namespace. Idiots; Especially you, ESR. -#undef timeout +inline void +Canvas::print(unsigned int x, unsigned int y, const char* str, ...) { + va_list arglist; + + va_start(arglist, str); + wmove(m_window, y, x); + vw_printw(m_window, str, arglist); + va_end(arglist); +} } diff --git a/src/display/frame.cc b/src/display/frame.cc index 7f98cb83..cbe58369 100644 --- a/src/display/frame.cc +++ b/src/display/frame.cc @@ -58,11 +58,8 @@ Frame::Frame() : bool Frame::is_width_dynamic() const { switch (m_type) { - case TYPE_NONE: - return false; - - case TYPE_WINDOW: - return m_window->is_active() && m_window->is_width_dynamic(); + case TYPE_NONE: return false; + case TYPE_WINDOW: return m_window->is_active() && m_window->is_width_dynamic(); case TYPE_ROW: case TYPE_COLUMN: @@ -79,11 +76,8 @@ Frame::is_width_dynamic() const { bool Frame::is_height_dynamic() const { switch (m_type) { - case TYPE_NONE: - return false; - - case TYPE_WINDOW: - return m_window->is_active() && m_window->is_height_dynamic(); + case TYPE_NONE: return false; + case TYPE_WINDOW: return m_window->is_active() && m_window->is_height_dynamic(); case TYPE_ROW: case TYPE_COLUMN: @@ -97,35 +91,82 @@ Frame::is_height_dynamic() const { return false; } -Frame::pair_type +bool +Frame::has_left_frame() const { + switch (m_type) { + case TYPE_NONE: + case TYPE_ROW: return false; + case TYPE_WINDOW: return m_window->is_active() && m_window->is_left(); + + case TYPE_COLUMN: + for (size_type i = 0; i < m_containerSize; ++i) + if (m_container[i]->has_left_frame()) + return true; + + return false; + } + + return false; +} + +bool +Frame::has_bottom_frame() const { + switch (m_type) { + case TYPE_NONE: + case TYPE_COLUMN: return false; + case TYPE_WINDOW: return m_window->is_active() && m_window->is_bottom(); + + case TYPE_ROW: + for (size_type i = 0; i < m_containerSize; ++i) + if (m_container[i]->has_bottom_frame()) + return true; + + return false; + } + + return false; +} + +Frame::bounds_type Frame::preferred_size() const { switch (m_type) { case TYPE_NONE: - return pair_type(0, 0); + return bounds_type(0, 0, 0, 0); case TYPE_WINDOW: if (m_window->is_active()) - return pair_type(std::max(m_window->min_width(), 1), std::max(m_window->min_height(), 1)); + return bounds_type(m_window->min_width(), m_window->min_height(), + m_window->max_width(), m_window->max_height()); else - return pair_type(0, 0); + return bounds_type(0, 0, 0, 0); case TYPE_ROW: case TYPE_COLUMN: { - pair_type accum(0, 0); + bounds_type accum(0, 0, 0, 0); for (size_type i = 0; i < m_containerSize; ++i) { - pair_type p = m_container[i]->preferred_size(); + bounds_type p = m_container[i]->preferred_size(); - accum.first += p.first; - accum.second += p.second; + accum.minWidth += p.minWidth; + accum.minHeight += p.minHeight; + + if (p.maxWidth == Window::extent_full || accum.maxWidth == Window::extent_full) + accum.maxWidth = Window::extent_full; + else + accum.maxWidth += p.maxWidth; + + if (p.maxHeight == Window::extent_full || accum.maxHeight == Window::extent_full) + accum.maxHeight = Window::extent_full; + else + accum.maxHeight += p.maxHeight; } return accum; } } - return pair_type(0, 0); + return bounds_type(0, 0, 0, 0); } void @@ -185,6 +226,12 @@ Frame::initialize_column(size_type size) { void Frame::clear() { switch (m_type) { + case TYPE_WINDOW: + if (m_window != NULL) + m_window->set_offscreen(true); + + break; + case TYPE_ROW: case TYPE_COLUMN: for (size_type i = 0; i < m_containerSize; ++i) { @@ -249,41 +296,64 @@ Frame::balance(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { m_width = width; m_height = height; - if (m_type == TYPE_NONE) - return; + switch (m_type) { + case TYPE_NONE: break; + case TYPE_WINDOW: balance_window(x, y, width, height); break; + case TYPE_ROW: balance_row(x, y, width, height); break; + case TYPE_COLUMN: balance_column(x, y, width, height); break; + } +} - if (m_type == TYPE_WINDOW) { - // Ensure that we don't draw windows that are offscreen or have - // zero extent. - if (width == 0 || height == 0 || !m_window->is_active()) { - m_window->set_offscreen(true); - return; - } - - m_window->set_offscreen(false); - m_window->resize(x, y, width, height); - m_window->mark_dirty(); +inline void +Frame::balance_window(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { + // Ensure that we don't draw windows that are offscreen or have + // zero extent. + if (width == 0 || height == 0 || !m_window->is_active()) { + m_window->set_offscreen(true); return; } + if (width > m_window->max_width()) { + if (m_window->is_left()) + x += width - m_window->max_width(); + + width = m_window->max_width(); + } + + if (height > m_window->max_height()) { + if (m_window->is_bottom()) + y += height - m_window->max_height(); + + height = m_window->max_height(); + } + + m_window->set_offscreen(false); + m_window->resize(x, y, width, height); + m_window->mark_dirty(); +} + +inline void +Frame::balance_row(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { // Find the size of the static frames. The dynamic frames are added // to a temporary list for the second pass. Each frame uses the // m_width and m_height as temporary storage for width and height in // this algorithm. - size_type dynamicSize = 0; - Frame* dynamicFrames[max_size]; + size_type dynamicSize = 0; + dynamic_type dynamicFrames[max_size]; - int remaining = m_type == TYPE_ROW ? height : width; + int remaining = height; for (Frame **itr = m_container, **last = m_container + m_containerSize; itr != last; ++itr) { - pair_type p = (*itr)->preferred_size(); - (*itr)->m_width = p.first; - (*itr)->m_height = p.second; + bounds_type bounds = (*itr)->preferred_size(); + + if ((*itr)->is_height_dynamic()) { + (*itr)->m_height = 0; + dynamicFrames[dynamicSize++] = std::make_pair(*itr, bounds); - if ((m_type == TYPE_ROW && (*itr)->is_height_dynamic()) || (m_type == TYPE_COLUMN && (*itr)->is_width_dynamic())) - dynamicFrames[dynamicSize++] = *itr; - else - remaining -= m_type == TYPE_ROW ? p.second : p.first; + } else { + (*itr)->m_height = bounds.minHeight; + remaining -= bounds.minHeight; + } } // Sort the dynamic frames by the min size in the direction we are @@ -292,42 +362,122 @@ Frame::balance(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { // following frames. // // Else if we're short, only give each what they require. + std::sort(dynamicFrames, dynamicFrames + dynamicSize, + rak::greater2(rak::on(rak::const_mem_ref(&dynamic_type::second), rak::const_mem_ref(&Frame::bounds_type::minHeight)), + rak::on(rak::const_mem_ref(&dynamic_type::second), rak::const_mem_ref(&Frame::bounds_type::minHeight)))); - if (m_type == TYPE_ROW) - std::sort(dynamicFrames, dynamicFrames + dynamicSize, rak::greater2(rak::mem_ptr(&Frame::m_height), rak::mem_ptr(&Frame::m_height))); - else - std::sort(dynamicFrames, dynamicFrames + dynamicSize, rak::greater2(rak::mem_ptr(&Frame::m_width), rak::mem_ptr(&Frame::m_width))); + bool retry; - for (Frame **itr = dynamicFrames, **last = dynamicFrames + dynamicSize; itr != last; ++itr, --dynamicSize) { - uint32_t s = std::max((std::max(remaining, 0) + dynamicSize - 1) / dynamicSize, - m_type == TYPE_ROW ? (*itr)->m_height : (*itr)->m_width); + do { + retry = false; + + for (dynamic_type *itr = dynamicFrames, *last = dynamicFrames + dynamicSize; itr != last; ++itr) { + uint32_t adjust = (std::max(remaining, 0) + std::distance(itr, last) - 1) / std::distance(itr, last); - remaining -= s; - - if (m_type == TYPE_ROW) - (*itr)->m_height = s; - else - (*itr)->m_width = s; - } + adjust += itr->first->m_height; + adjust = std::max(adjust, itr->second.minHeight); + adjust = std::min(adjust, itr->second.maxHeight); + + remaining -= adjust - itr->first->m_height; + + retry = retry || itr->first->m_height != adjust; + + itr->first->m_height = adjust; + } + + } while (retry && remaining > 0); // Use the pre-calculated frame sizes to balance the sub-frames. If // the frame is too small, it will set the remaining windows to zero // extent which will flag them as offscreen. for (Frame **itr = m_container, **last = m_container + m_containerSize; itr != last; ++itr) { - if (m_type == TYPE_ROW) { - (*itr)->balance(x, y, m_width, std::min((*itr)->m_height, height)); + // If there is any remaining space, check if we want to shift + // the subsequent frames to the other side of this frame. + if (remaining > 0 && (*itr)->has_bottom_frame()) { + (*itr)->m_height += remaining; + remaining = 0; + } - y += (*itr)->m_height; - height -= (*itr)->m_height; + (*itr)->balance(x, y, m_width, std::min((*itr)->m_height, height)); + + y += (*itr)->m_height; + height -= (*itr)->m_height; + } +} + +inline void +Frame::balance_column(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { + // Find the size of the static frames. The dynamic frames are added + // to a temporary list for the second pass. Each frame uses the + // m_width and m_height as temporary storage for width and height in + // this algorithm. + size_type dynamicSize = 0; + dynamic_type dynamicFrames[max_size]; + + int remaining = width; + + for (Frame **itr = m_container, **last = m_container + m_containerSize; itr != last; ++itr) { + bounds_type bounds = (*itr)->preferred_size(); + + if ((*itr)->is_width_dynamic()) { + (*itr)->m_width = 0; + dynamicFrames[dynamicSize++] = std::make_pair(*itr, bounds); } else { - (*itr)->balance(x, y, std::min((*itr)->m_width, width), m_height); - - x += (*itr)->m_width; - width -= (*itr)->m_width; + (*itr)->m_width = bounds.minWidth; + remaining -= bounds.minWidth; } } + + // Sort the dynamic frames by the min size in the direction we are + // interested in. Then try to satisfy the largest first, and if we + // have any remaining space we can use that to extend it and any + // following frames. + // + // Else if we're short, only give each what they require. + std::sort(dynamicFrames, dynamicFrames + dynamicSize, + rak::greater2(rak::on(rak::const_mem_ref(&dynamic_type::second), rak::const_mem_ref(&Frame::bounds_type::minWidth)), + rak::on(rak::const_mem_ref(&dynamic_type::second), rak::const_mem_ref(&Frame::bounds_type::minWidth)))); + + bool retry; + + do { + retry = false; + + for (dynamic_type *itr = dynamicFrames, *last = dynamicFrames + dynamicSize; itr != last; ++itr) { + uint32_t adjust = (std::max(remaining, 0) + std::distance(itr, last) - 1) / std::distance(itr, last); + + adjust += itr->first->m_width; + adjust = std::max(adjust, itr->second.minWidth); + adjust = std::min(adjust, itr->second.maxWidth); + + remaining -= adjust - itr->first->m_width; + + retry = retry || itr->first->m_width != adjust; + + itr->first->m_width = adjust; + } + + } while (retry && remaining > 0); + + // Use the pre-calculated frame sizes to balance the sub-frames. If + // the frame is too small, it will set the remaining windows to zero + // extent which will flag them as offscreen. + + for (Frame **itr = m_container, **last = m_container + m_containerSize; itr != last; ++itr) { + // If there is any remaining space, check if we want to shift + // the subsequent frames to the other side of this frame. + if (remaining > 0 && (*itr)->has_left_frame()) { + (*itr)->m_width += remaining; + remaining = 0; + } + + (*itr)->balance(x, y, std::min((*itr)->m_width, width), m_height); + + y += (*itr)->m_width; + width -= (*itr)->m_width; + } } } diff --git a/src/display/frame.h b/src/display/frame.h index 784b3836..462337dc 100644 --- a/src/display/frame.h +++ b/src/display/frame.h @@ -45,6 +45,9 @@ class Window; class Frame { public: + typedef uint32_t extent_type; + typedef uint32_t size_type; + enum Type { TYPE_NONE, TYPE_WINDOW, @@ -52,8 +55,19 @@ public: TYPE_COLUMN }; - typedef uint32_t size_type; - typedef std::pair pair_type; + struct bounds_type { + bounds_type() {} + bounds_type(extent_type minW, extent_type minH, extent_type maxW, extent_type maxH) : + minWidth(minW), minHeight(minH), maxWidth(maxW), maxHeight(maxH) {} + + extent_type minWidth; + extent_type minHeight; + + extent_type maxWidth; + extent_type maxHeight; + }; + + typedef std::pair dynamic_type; static const size_type max_size = 5; @@ -62,7 +76,10 @@ public: bool is_width_dynamic() const; bool is_height_dynamic() const; - pair_type preferred_size() const; + bool has_left_frame() const; + bool has_bottom_frame() const; + + bounds_type preferred_size() const; uint32_t position_x() const { return m_positionX; } uint32_t position_y() const { return m_positionY; } @@ -92,6 +109,10 @@ public: void balance(uint32_t x, uint32_t y, uint32_t width, uint32_t height); private: + inline void balance_window(uint32_t x, uint32_t y, uint32_t width, uint32_t height); + inline void balance_row(uint32_t x, uint32_t y, uint32_t width, uint32_t height); + inline void balance_column(uint32_t x, uint32_t y, uint32_t width, uint32_t height); + Type m_type; uint32_t m_positionX; diff --git a/src/display/manager.cc b/src/display/manager.cc index d686f232..1bb5773e 100644 --- a/src/display/manager.cc +++ b/src/display/manager.cc @@ -77,6 +77,7 @@ Manager::unschedule(Window* w) { void Manager::adjust_layout() { + Canvas::redraw_std(); m_rootFrame.balance(0, 0, Canvas::get_screen_width(), Canvas::get_screen_height()); } diff --git a/src/display/text_element.h b/src/display/text_element.h new file mode 100644 index 00000000..5ec47fcb --- /dev/null +++ b/src/display/text_element.h @@ -0,0 +1,58 @@ +// 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_DISPLAY_TEXT_ELEMENT_H +#define RTORRENT_DISPLAY_TEXT_ELEMENT_H + +#include + +#include "display/canvas.h" + +namespace display { + +class TextElement { +public: + virtual ~TextElement() {} + + // The last element must point to a valid memory location into which + // the caller must write a '\0' to terminate the c string. The + // attributes must contain at least one attribute. + virtual char* print(char* first, const char* last, Canvas::attributes_list* attributes, void* object) = 0; +}; + +} + +#endif diff --git a/src/display/text_element_list.cc b/src/display/text_element_list.cc new file mode 100644 index 00000000..cad8adf1 --- /dev/null +++ b/src/display/text_element_list.cc @@ -0,0 +1,62 @@ +// 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 "text_element_list.h" + +namespace display { + +void +TextElementList::clear() { + std::for_each(begin(), end(), rak::call_delete()); + base_type::clear(); +} + +char* +TextElementList::print(char* first, const char* last, Canvas::attributes_list* attributes, void* object) { + // Call print for each element even if first == last so that any + // attributes gets added to the list. + for (iterator itr = begin(); itr != end(); ++itr) + first = (*itr)->print(first, last, attributes, object); + + return first; +} + +} diff --git a/src/display/text_element_list.h b/src/display/text_element_list.h new file mode 100644 index 00000000..7f3a64e2 --- /dev/null +++ b/src/display/text_element_list.h @@ -0,0 +1,73 @@ +// 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_DISPLAY_TEXT_ELEMENT_LIST_H +#define RTORRENT_DISPLAY_TEXT_ELEMENT_LIST_H + +#include "text_element.h" + +namespace display { + +class TextElementList : public TextElement, public std::vector { +public: + typedef std::vector base_type; + + typedef base_type::value_type value_type; + typedef base_type::reference reference; + typedef base_type::iterator iterator; + typedef base_type::const_iterator const_iterator; + typedef base_type::reverse_iterator reverse_iterator; + + using base_type::empty; + using base_type::size; + + using base_type::begin; + using base_type::end; + using base_type::rbegin; + using base_type::rend; + + using base_type::push_back; + + virtual ~TextElementList() { clear(); } + + void clear(); + + virtual char* print(char* first, const char* last, Canvas::attributes_list* attributes, void* object); +}; + +} + +#endif diff --git a/src/display/text_element_string.cc b/src/display/text_element_string.cc new file mode 100644 index 00000000..4cf1569e --- /dev/null +++ b/src/display/text_element_string.cc @@ -0,0 +1,76 @@ +// 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 "text_element_string.h" + +namespace display { + +char* +TextElementString::print(char* first, const char* last, Canvas::attributes_list* attributes, void* object) { + size_t length = std::min(last - first, m_string.size()); + + if (length == 0) + return first; + + std::memcpy(first, m_string.c_str(), length); + + // Move this stuff into a function in TextElement. + Attributes base = attributes->back(); + Attributes current(NULL, m_attributes, Attributes::color_invalid); + + if (current.attributes() == Attributes::a_invalid) + current.set_attributes(base.attributes()); + else if (current.attributes() != base.attributes()) + current.set_position(first); + + if (current.colors() == Attributes::color_invalid) + current.set_colors(base.colors()); + else if (current.colors() != base.colors()) + current.set_position(first); + + if (current.position() != NULL) { + attributes->push_back(current); + attributes->push_back(Attributes(first + length, base.attributes(), base.colors())); + } + + return first + length; +} + +} diff --git a/src/display/text_element_string.h b/src/display/text_element_string.h new file mode 100644 index 00000000..20c5be1f --- /dev/null +++ b/src/display/text_element_string.h @@ -0,0 +1,66 @@ +// 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_DISPLAY_TEXT_ELEMENT_LIST_H +#define RTORRENT_DISPLAY_TEXT_ELEMENT_LIST_H + +#include + +#include "text_element.h" + +namespace display { + +class TextElementString : public TextElement { +public: + TextElementString() {} + TextElementString(const std::string& s, int attributes = Attributes::a_invalid) : m_string(s), m_attributes(attributes) {} + + const std::string& str() const { return m_string; } + void set_str(const std::string& s) { m_string = s; } + + int attributes() const { return m_attributes; } + void set_attributes(int a) { m_attributes = a; } + + virtual char* print(char* first, const char* last, Canvas::attributes_list* attributes, void* object); + +private: + std::string m_string; + int m_attributes; +}; + +} + +#endif diff --git a/src/display/window.cc b/src/display/window.cc index dd718a6d..9944b63c 100644 --- a/src/display/window.cc +++ b/src/display/window.cc @@ -49,11 +49,15 @@ Window::Slot Window::m_slotAdjust; // When constructing the window we set flag_offscreen so that redraw // doesn't get triggered until after a successful Frame::balance call. -Window::Window(Canvas* canvas, int flags, extent_type minWidth, extent_type minHeight) : +Window::Window(Canvas* canvas, int flags, extent_type minWidth, extent_type minHeight, extent_type maxWidth, extent_type maxHeight) : m_canvas(canvas), m_flags(flags | flag_offscreen), + m_minWidth(minWidth), - m_minHeight(minHeight) { + m_minHeight(minHeight), + + m_maxWidth(maxWidth), + m_maxHeight(maxHeight) { m_taskUpdate.set_slot(rak::mem_fn(this, &Window::redraw)); } diff --git a/src/display/window.h b/src/display/window.h index 22ef7b41..522cbff8 100644 --- a/src/display/window.h +++ b/src/display/window.h @@ -56,12 +56,17 @@ public: typedef rak::mem_fun1 SlotWindow; typedef rak::mem_fun2 SlotTimer; - static const int flag_active = (1 << 0); - static const int flag_offscreen = (1 << 1); - static const int flag_width_dynamic = (1 << 2); - static const int flag_height_dynamic = (1 << 3); + static const int flag_active = (1 << 0); + static const int flag_offscreen = (1 << 1); + static const int flag_left = (1 << 2); + static const int flag_bottom = (1 << 3); - Window(Canvas* canvas, int flags, extent_type minWidth, extent_type minHeight); + static const extent_type extent_static = extent_type(); + static const extent_type extent_full = ~extent_type(); + + Window(Canvas* canvas, int flags, + extent_type minWidth, extent_type minHeight, + extent_type maxWidth, extent_type maxHeight); virtual ~Window(); @@ -71,8 +76,14 @@ public: bool is_offscreen() const { return m_flags & flag_offscreen; } void set_offscreen(bool state) { if (state) m_flags |= flag_offscreen; else m_flags &= ~flag_offscreen; } - bool is_width_dynamic() const { return m_flags & flag_width_dynamic; } - bool is_height_dynamic() const { return m_flags & flag_height_dynamic; } + bool is_left() const { return m_flags & flag_left; } + void set_left(bool state) { if (state) m_flags |= flag_left; else m_flags &= ~flag_left; } + + bool is_bottom() const { return m_flags & flag_bottom; } + void set_bottom(bool state) { if (state) m_flags |= flag_bottom; else m_flags &= ~flag_bottom; } + + bool is_width_dynamic() const { return m_maxWidth > m_minWidth; } + bool is_height_dynamic() const { return m_maxHeight > m_minHeight; } bool is_dirty() { return m_taskUpdate.is_queued(); } void mark_dirty() { m_slotSchedule(this, cachedTime + 1); } @@ -80,6 +91,9 @@ public: extent_type min_width() const { return m_minWidth; } extent_type min_height() const { return m_minHeight; } + extent_type max_width() const { return std::max(m_maxWidth, m_minWidth); } + extent_type max_height() const { return std::max(m_maxHeight, m_minHeight); } + extent_type width() const { return m_canvas->width(); } extent_type height() const { return m_canvas->height(); } @@ -110,6 +124,9 @@ protected: extent_type m_minWidth; extent_type m_minHeight; + extent_type m_maxWidth; + extent_type m_maxHeight; + rak::priority_item m_taskUpdate; }; diff --git a/src/display/window_download_chunks_seen.cc b/src/display/window_download_chunks_seen.cc index a10817a5..da650156 100644 --- a/src/display/window_download_chunks_seen.cc +++ b/src/display/window_download_chunks_seen.cc @@ -52,7 +52,7 @@ namespace display { WindowDownloadChunksSeen::WindowDownloadChunksSeen(core::Download* d, unsigned int *focus) : - Window(new Canvas, flag_width_dynamic | flag_height_dynamic, 0, 0), + Window(new Canvas, 0, 0, 0, extent_full, extent_full), m_download(d), m_focus(focus) { } @@ -102,7 +102,7 @@ WindowDownloadChunksSeen::redraw() { while (itrTransfer != transferChunks.end() && (uint32_t)(chunk - seen) > (*itrTransfer)->index()) itrTransfer++; - for (int y = 1; y < m_canvas->height() && chunk < last; ++y) { + for (unsigned int y = 1; y < m_canvas->height() && chunk < last; ++y) { m_canvas->print(0, y, "%5u ", (int)(chunk - seen)); while (chunk < last) { diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc index da14d333..6630db90 100644 --- a/src/display/window_download_list.cc +++ b/src/display/window_download_list.cc @@ -92,9 +92,9 @@ WindowDownloadList::redraw() { int pos = 1; while (range.first != range.second) { - char buffer[m_canvas->width()]; + char buffer[m_canvas->width() + 1]; char* position; - char* last = buffer + m_canvas->width() - 2; + char* last = buffer + m_canvas->width() - 2 + 1; position = print_download_title(buffer, last, *range.first); m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); diff --git a/src/display/window_download_list.h b/src/display/window_download_list.h index f121dfe5..c03761e4 100644 --- a/src/display/window_download_list.h +++ b/src/display/window_download_list.h @@ -52,7 +52,7 @@ namespace display { class WindowDownloadList : public Window { public: WindowDownloadList() : - Window(new Canvas, flag_width_dynamic | flag_height_dynamic, 120, 1), + Window(new Canvas, 0, 120, 1, extent_full, extent_full), m_view(NULL) {} ~WindowDownloadList(); diff --git a/src/display/window_download_statusbar.cc b/src/display/window_download_statusbar.cc index c52e761a..c2aaeded 100644 --- a/src/display/window_download_statusbar.cc +++ b/src/display/window_download_statusbar.cc @@ -49,7 +49,7 @@ namespace display { WindowDownloadStatusbar::WindowDownloadStatusbar(core::Download* d) : - Window(new Canvas, flag_width_dynamic, 0, 3), + Window(new Canvas, 0, 0, 3, extent_full, extent_static), m_download(d) { } diff --git a/src/display/window_download_transfer_list.cc b/src/display/window_download_transfer_list.cc index 0a23a053..5a535094 100644 --- a/src/display/window_download_transfer_list.cc +++ b/src/display/window_download_transfer_list.cc @@ -49,7 +49,7 @@ namespace display { WindowDownloadTransferList::WindowDownloadTransferList(core::Download* d, unsigned int *focus) : - Window(new Canvas, flag_width_dynamic | flag_height_dynamic, 0, 0), + Window(new Canvas, 0, 0, 0, extent_full, extent_full), m_download(d), m_focus(focus) { } @@ -73,7 +73,7 @@ WindowDownloadTransferList::redraw() { // is just something i threw in there, someone really should // prettify this. (This is a very subtle hint) - for (int y = 1; y < m_canvas->height() && itr != transfers->end(); ++y, ++itr) { + for (unsigned int y = 1; y < m_canvas->height() && itr != transfers->end(); ++y, ++itr) { m_canvas->print(0, y, "%5u [P: %u F: %u]", (*itr)->index(), (*itr)->priority(), (*itr)->failed()); // Handle window size. diff --git a/src/display/window_file_list.cc b/src/display/window_file_list.cc index c75a35fe..b4f1d2f2 100644 --- a/src/display/window_file_list.cc +++ b/src/display/window_file_list.cc @@ -49,7 +49,7 @@ namespace display { WindowFileList::WindowFileList(core::Download* d, unsigned int* focus) : - Window(new Canvas, flag_width_dynamic | flag_height_dynamic, 0, 0), + Window(new Canvas, 0, 0, 0, extent_full, extent_full), m_download(d), m_focus(focus) { } diff --git a/src/display/window_http_queue.cc b/src/display/window_http_queue.cc index a65aefc0..13cf7349 100644 --- a/src/display/window_http_queue.cc +++ b/src/display/window_http_queue.cc @@ -48,7 +48,7 @@ namespace display { WindowHttpQueue::WindowHttpQueue(core::HttpQueue* q) : - Window(new Canvas, flag_width_dynamic, 0, 1), + Window(new Canvas, 0, 0, 1, extent_full, 1), m_queue(q) { set_active(false); @@ -72,7 +72,7 @@ WindowHttpQueue::redraw() { m_canvas->erase(); m_canvas->print(0, 0, "Http [%i]", m_queue->size()); - int pos = 10; + unsigned int pos = 10; Container::iterator itr = m_container.begin(); while (itr != m_container.end() && pos + 20 < m_canvas->width()) { diff --git a/src/display/window_input.h b/src/display/window_input.h index e2a373f3..1c68f142 100644 --- a/src/display/window_input.h +++ b/src/display/window_input.h @@ -50,7 +50,7 @@ namespace display { class WindowInput : public Window { public: WindowInput() : - Window(new Canvas, flag_width_dynamic, 0, 1), + Window(new Canvas, 0, 0, 1, extent_full, 1), m_input(NULL), m_focus(false) {} diff --git a/src/display/window_log.cc b/src/display/window_log.cc index 023fbccd..f7cdc111 100644 --- a/src/display/window_log.cc +++ b/src/display/window_log.cc @@ -45,7 +45,7 @@ namespace display { WindowLog::WindowLog(core::Log* l) : - Window(new Canvas, flag_width_dynamic, 0, 0), + Window(new Canvas, 0, 0, 0, extent_full, extent_static), m_log(l) { m_taskUpdate.set_slot(rak::mem_fn(this, &WindowLog::receive_update)), @@ -68,13 +68,13 @@ void WindowLog::redraw() { m_canvas->erase(); - int pos = 0; + int pos = m_canvas->height(); - for (core::Log::iterator itr = m_log->begin(), end = find_older(); itr != end && pos < m_canvas->height(); ++itr) { + for (core::Log::iterator itr = m_log->begin(), last = find_older(); itr != last && pos > 0; ++itr, --pos) { char buffer[16]; print_hhmmss_local(buffer, buffer + 16, static_cast(itr->first.seconds())); - m_canvas->print(0, pos++, "(%s) %s", buffer, itr->second.c_str()); + m_canvas->print(0, pos - 1, "(%s) %s", buffer, itr->second.c_str()); } } @@ -86,10 +86,11 @@ WindowLog::receive_update() { return; iterator itr = find_older(); - extent_type h = std::min(std::distance(m_log->begin(), itr), (std::iterator_traits::difference_type)10); + extent_type height = std::min(std::distance(m_log->begin(), itr), (std::iterator_traits::difference_type)10); - if (h != m_minHeight) { - m_minHeight = h; + if (height != m_maxHeight) { + m_minHeight = height != 0 ? 1 : 0; + m_maxHeight = height; m_slotAdjust(); } else { @@ -98,7 +99,7 @@ WindowLog::receive_update() { priority_queue_erase(&taskScheduler, &m_taskUpdate); - if (h != 0) + if (height != 0) priority_queue_insert(&taskScheduler, &m_taskUpdate, (cachedTime + rak::timer::from_seconds(30)).round_seconds()); } diff --git a/src/display/window_log_complete.cc b/src/display/window_log_complete.cc index 51f3c38a..3ca177c1 100644 --- a/src/display/window_log_complete.cc +++ b/src/display/window_log_complete.cc @@ -45,7 +45,7 @@ namespace display { WindowLogComplete::WindowLogComplete(core::Log* l) : - Window(new Canvas, flag_width_dynamic | flag_height_dynamic, 30, 1), + Window(new Canvas, 0, 30, 1, extent_full, extent_full), m_log(l) { // We're trying out scheduled tasks instead. @@ -65,15 +65,27 @@ void WindowLogComplete::redraw() { m_canvas->erase(); - int pos = 0; + if (m_canvas->width() < 16) + return; -// m_canvas->print(std::max(0, (int)m_canvas->width() / 2 - 5), pos++, "*** Log ***"); + int pos = m_canvas->height(); - for (core::Log::iterator itr = m_log->begin(), e = m_log->end(); itr != e && pos < m_canvas->height(); ++itr) { + for (core::Log::iterator itr = m_log->begin(), last = m_log->end(); itr != last && pos > 0; ++itr) { char buffer[16]; - print_hhmmss_local(buffer, buffer + 16, static_cast(itr->first.seconds())); - m_canvas->print(0, pos++, "(%s) %s", buffer, itr->second.c_str()); + // Use an arbitrary min width of 60 for allowing multiple + // lines. This should ensure we don't mess up the display when the + // screen is shrunk too much. + unsigned int timeWidth = 3 + print_hhmmss_local(buffer, buffer + 16, static_cast(itr->first.seconds())) - buffer; + + unsigned int logWidth = m_canvas->width() > 60 ? (m_canvas->width() - timeWidth) : (60 - timeWidth); + unsigned int logHeight = (itr->second.size() + logWidth - 1) / logWidth; + + for (unsigned int j = logHeight; j > 0 && pos > 0; --j, --pos) + if (j == 1) + m_canvas->print(0, pos - 1, "(%s) %s", buffer, itr->second.substr(0, m_canvas->width() - timeWidth).c_str()); + else + m_canvas->print(timeWidth, pos - 1, "%s", itr->second.substr(logWidth * (j - 1), m_canvas->width() - timeWidth).c_str()); } } diff --git a/src/display/window_peer_info.cc b/src/display/window_peer_info.cc index 0e00f646..fcd7cf27 100644 --- a/src/display/window_peer_info.cc +++ b/src/display/window_peer_info.cc @@ -54,7 +54,7 @@ namespace display { WindowPeerInfo::WindowPeerInfo(core::Download* d, PList* l, PList::iterator* f) : - Window(new Canvas, flag_width_dynamic | flag_height_dynamic, 0, 0), + Window(new Canvas, 0, 0, 0, extent_full, extent_full), m_download(d), m_list(l), m_focus(f) { diff --git a/src/display/window_peer_list.cc b/src/display/window_peer_list.cc index 4535efbb..0d0d45e0 100644 --- a/src/display/window_peer_list.cc +++ b/src/display/window_peer_list.cc @@ -52,7 +52,7 @@ namespace display { WindowPeerList::WindowPeerList(core::Download* d, PList* l, PList::iterator* f) : - Window(new Canvas, flag_width_dynamic | flag_height_dynamic, 0, 0), + Window(new Canvas, 0, 0, 0, extent_full, extent_full), m_download(d), m_list(l), m_focus(f) { diff --git a/src/display/window_statusbar.h b/src/display/window_statusbar.h index 47837fa9..0063f8d9 100644 --- a/src/display/window_statusbar.h +++ b/src/display/window_statusbar.h @@ -46,7 +46,7 @@ namespace display { class WindowStatusbar : public Window { public: WindowStatusbar() : - Window(new Canvas, flag_width_dynamic, 0, 1), + Window(new Canvas, 0, 0, 1, extent_full, extent_static), m_lastTick(0) {} virtual void redraw(); diff --git a/src/display/window_string_list.cc b/src/display/window_string_list.cc index 48adc9fd..a180b7ab 100644 --- a/src/display/window_string_list.cc +++ b/src/display/window_string_list.cc @@ -43,7 +43,7 @@ namespace display { WindowStringList::WindowStringList() : - Window(new Canvas, flag_width_dynamic | flag_height_dynamic, 0, 0) { + Window(new Canvas, 0, 0, 0, extent_full, extent_full) { } WindowStringList::~WindowStringList() { diff --git a/src/display/window_text.cc b/src/display/window_text.cc new file mode 100644 index 00000000..e1a4c1ac --- /dev/null +++ b/src/display/window_text.cc @@ -0,0 +1,86 @@ +// 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 "canvas.h" +#include "utils.h" +#include "window_text.h" + +namespace display { + +void +WindowText::clear() { + std::for_each(begin(), end(), rak::call_delete()); + base_type::clear(); +} + +void +WindowText::push_back(TextElement* element) { + base_type::push_back(element); + +// m_minHeight = size(); + m_maxHeight = size(); + + // Check if active, if so do the update thingie. Or be lazy? +} + +void +WindowText::redraw() { + m_canvas->erase(); + + unsigned int position = 0; + + for (iterator itr = begin(); itr != end() && position < m_canvas->height(); ++itr, ++position) { + if (*itr == NULL) + continue; + + char buffer[m_canvas->width() + 1]; + + // Add a print function that sets up attributes etc? + Canvas::attributes_list attributes; + attributes.push_back(Attributes(buffer, Attributes::a_normal, Attributes::color_default)); + + char* last = (*itr)->print(buffer, buffer + m_canvas->width(), &attributes, NULL); + + m_canvas->print_attributes(0, position, buffer, last, &attributes); + } +} + +} diff --git a/src/display/window_text.h b/src/display/window_text.h new file mode 100644 index 00000000..5578b50d --- /dev/null +++ b/src/display/window_text.h @@ -0,0 +1,77 @@ +// 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_DISPLAY_WINDOW_TEXT_H +#define RTORRENT_DISPLAY_WINDOW_TEXT_H + +#include + +#include "text_element.h" +#include "window.h" + +namespace display { + +class WindowText : public Window, public std::vector { +public: + typedef std::vector base_type; + + typedef base_type::value_type value_type; + typedef base_type::reference reference; + typedef base_type::iterator iterator; + typedef base_type::const_iterator const_iterator; + typedef base_type::reverse_iterator reverse_iterator; + + using base_type::empty; + using base_type::size; + + using base_type::begin; + using base_type::end; + using base_type::rbegin; + using base_type::rend; + + WindowText() : Window(new Canvas, 0, 0, 0, extent_full, extent_static) {} + ~WindowText() { clear(); } + + void clear(); + + void push_back(TextElement* element); + + virtual void redraw(); +}; + +} + +#endif diff --git a/src/display/window_title.cc b/src/display/window_title.cc index c23b4405..f18a9a26 100644 --- a/src/display/window_title.cc +++ b/src/display/window_title.cc @@ -46,7 +46,7 @@ WindowTitle::redraw() { m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); m_canvas->erase(); - m_canvas->print(std::max(0, (m_canvas->width() - (int)m_title.size()) / 2 - 4), 0, + m_canvas->print(std::max(0, ((int)m_canvas->width() - (int)m_title.size()) / 2 - 4), 0, "*** %s ***", m_title.c_str()); } diff --git a/src/display/window_title.h b/src/display/window_title.h index 261f2494..ff564ec7 100644 --- a/src/display/window_title.h +++ b/src/display/window_title.h @@ -44,7 +44,7 @@ namespace display { class WindowTitle : public Window { public: - WindowTitle() : Window(new Canvas, flag_width_dynamic, 0, 1) {} + WindowTitle() : Window(new Canvas, 0, 0, 1, extent_full, extent_static) {} const std::string& title() const { return m_title; } void set_title(const std::string& title) { m_title = title; mark_dirty(); } diff --git a/src/display/window_tracker_list.cc b/src/display/window_tracker_list.cc index 02f1a35d..d3d6b98a 100644 --- a/src/display/window_tracker_list.cc +++ b/src/display/window_tracker_list.cc @@ -49,7 +49,7 @@ namespace display { WindowTrackerList::WindowTrackerList(core::Download* d, unsigned int* focus) : - Window(new Canvas, flag_width_dynamic | flag_height_dynamic, 0, 0), + Window(new Canvas, 0, 0, 0, extent_full, extent_full), m_download(d), m_focus(focus) { } diff --git a/src/input/manager.cc b/src/input/manager.cc index dbb8f7b2..ab6cd3dc 100644 --- a/src/input/manager.cc +++ b/src/input/manager.cc @@ -38,7 +38,7 @@ #include #include -#include +#include #include "manager.h" #include "bindings.h" @@ -51,12 +51,12 @@ Manager::erase(Bindings* b) { iterator itr = std::find(begin(), end(), b); if (itr == end()) - throw std::logic_error("Manager::erase(...) could not find binding"); + return; Base::erase(itr); if (std::find(begin(), end(), b) != end()) - throw std::logic_error("Manager::erase(...) found duplicate bindings"); + throw torrent::client_error("Manager::erase(...) found duplicate bindings."); } void diff --git a/src/ui/Makefile.am b/src/ui/Makefile.am index e8e4ebf2..01e74e5f 100644 --- a/src/ui/Makefile.am +++ b/src/ui/Makefile.am @@ -14,6 +14,8 @@ libsub_ui_a_SOURCES = \ element_file_list.h \ element_log_complete.cc \ element_log_complete.h \ + element_menu.cc \ + element_menu.h \ element_peer_info.cc \ element_peer_info.h \ element_peer_list.cc \ diff --git a/src/ui/download.cc b/src/ui/download.cc index a816108f..8365bb44 100644 --- a/src/ui/download.cc +++ b/src/ui/download.cc @@ -51,6 +51,7 @@ #include "download.h" #include "root.h" #include "element_file_list.h" +#include "element_menu.h" #include "element_peer_info.h" #include "element_peer_list.h" #include "element_tracker_list.h" @@ -62,11 +63,25 @@ namespace ui { Download::Download(DPtr d) : m_download(d), m_state(DISPLAY_MAX_SIZE), - - m_windowDownloadStatus(new WDownloadStatus(d)) { + m_focusDisplay(false) { m_focus = m_peers.end(); + m_windowDownloadStatus = new WDownloadStatus(d); + m_windowDownloadStatus->set_bottom(true); + + ElementMenu* elementMenu = new ElementMenu; + + elementMenu->push_back("Peer List", sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_PEER_LIST)); +// elementMenu->push_back("Peer Info", sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_PEER_INFO)); + elementMenu->push_back("File List", sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_FILE_LIST)); + elementMenu->push_back("Tracker List", sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_TRACKER_LIST)); + elementMenu->push_back("Chunks Seen", sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_CHUNKS_SEEN)); + elementMenu->push_back("Transfer List", sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_TRANSFER_LIST)); + + elementMenu->focus_next(); + + m_uiArray[DISPLAY_MENU] = elementMenu; m_uiArray[DISPLAY_PEER_LIST] = new ElementPeerList(d, &m_peers, &m_focus); m_uiArray[DISPLAY_PEER_INFO] = new ElementPeerInfo(d, &m_peers, &m_focus); m_uiArray[DISPLAY_FILE_LIST] = new ElementFileList(d); @@ -95,14 +110,19 @@ Download::~Download() { } void -Download::activate(display::Frame* frame) { +Download::activate(display::Frame* frame, bool focus) { if (is_active()) throw torrent::client_error("ui::Download::activate() called on an already activated object."); control->input()->push_back(&m_bindings); m_frame = frame; - activate_display(DISPLAY_PEER_LIST); + m_frame->initialize_row(2); + + m_frame->frame(1)->initialize_window(m_windowDownloadStatus); + m_windowDownloadStatus->set_active(true); + + activate_display_menu(DISPLAY_PEER_LIST); } void @@ -112,35 +132,42 @@ Download::disable() { control->input()->erase(&m_bindings); - activate_display(DISPLAY_MAX_SIZE); + activate_display_focus(DISPLAY_MAX_SIZE); + + m_windowDownloadStatus->set_active(false); + m_frame->clear(); + m_frame = NULL; } void -Download::activate_display(Display displayType) { +Download::activate_display(Display displayType, bool focusDisplay) { if (!is_active()) throw torrent::client_error("ui::Download::activate_display(...) !is_active()."); if (displayType > DISPLAY_MAX_SIZE) throw torrent::client_error("ui::Download::activate_display(...) out of bounds"); - if (displayType == m_state) + if (focusDisplay == m_focusDisplay && displayType == m_state) return; + display::Frame* frame = m_frame->frame(0); + // Cleanup previous state. switch (m_state) { + case DISPLAY_MENU: + break; + case DISPLAY_PEER_LIST: case DISPLAY_PEER_INFO: case DISPLAY_FILE_LIST: case DISPLAY_TRACKER_LIST: case DISPLAY_CHUNKS_SEEN: case DISPLAY_TRANSFER_LIST: + m_uiArray[DISPLAY_MENU]->disable(); m_uiArray[m_state]->disable(); - m_windowDownloadStatus->set_active(false); - m_frame->frame(1)->clear(); - - m_frame->clear(); + frame->clear(); break; case DISPLAY_MAX_SIZE: @@ -148,21 +175,23 @@ Download::activate_display(Display displayType) { } m_state = displayType; + m_focusDisplay = focusDisplay; // Initialize new state. switch (displayType) { + case DISPLAY_MENU: + break; + case DISPLAY_PEER_LIST: case DISPLAY_PEER_INFO: case DISPLAY_FILE_LIST: case DISPLAY_TRACKER_LIST: case DISPLAY_CHUNKS_SEEN: case DISPLAY_TRANSFER_LIST: - m_frame->initialize_row(2); + frame->initialize_column(2); - m_uiArray[displayType]->activate(m_frame->frame(0)); - - m_frame->frame(1)->initialize_window(m_windowDownloadStatus); - m_windowDownloadStatus->set_active(true); + m_uiArray[DISPLAY_MENU]->activate(frame->frame(0), !focusDisplay); + m_uiArray[displayType]->activate(frame->frame(1), focusDisplay); break; case DISPLAY_MAX_SIZE: @@ -185,7 +214,7 @@ Download::receive_next() { else m_focus = m_peers.begin(); - mark_dirty(); +// mark_dirty(); } void @@ -195,7 +224,7 @@ Download::receive_prev() { else m_focus = m_peers.end(); - mark_dirty(); +// mark_dirty(); } void @@ -205,7 +234,7 @@ Download::receive_disconnect_peer() { m_download->download()->disconnect_peer(*m_focus); - mark_dirty(); +// mark_dirty(); } void @@ -247,14 +276,6 @@ Download::receive_max_peers(int t) { m_download->download()->set_peers_max(std::max(m_download->download()->peers_max() + t, (uint32_t)5)); } -void -Download::receive_change(Display d) { - if (d == m_state) - return; - - activate_display(d); -} - void Download::receive_snub_peer() { if (m_focus == m_peers.end()) @@ -262,7 +283,7 @@ Download::receive_snub_peer() { m_focus->set_snubbed(!m_focus->is_snubbed()); - mark_dirty(); +// mark_dirty(); } void @@ -291,30 +312,25 @@ Download::bind_keys() { m_bindings['t'] = sigc::bind(sigc::mem_fun(m_download->tracker_list(), &torrent::TrackerList::manual_request), false); m_bindings['T'] = sigc::bind(sigc::mem_fun(m_download->tracker_list(), &torrent::TrackerList::manual_request), true); - m_bindings['p'] = sigc::bind(sigc::mem_fun(this, &Download::receive_change), DISPLAY_PEER_INFO); - m_bindings['o'] = sigc::bind(sigc::mem_fun(this, &Download::receive_change), DISPLAY_TRACKER_LIST); - m_bindings['i'] = sigc::bind(sigc::mem_fun(this, &Download::receive_change), DISPLAY_CHUNKS_SEEN); - m_bindings['u'] = sigc::bind(sigc::mem_fun(this, &Download::receive_change), DISPLAY_TRANSFER_LIST); + m_bindings['p'] = sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_PEER_INFO); + m_bindings['o'] = sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_TRACKER_LIST); + m_bindings['i'] = sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_CHUNKS_SEEN); + m_bindings['u'] = sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_TRANSFER_LIST); m_bindings[KEY_UP] = sigc::mem_fun(this, &Download::receive_prev); m_bindings[KEY_DOWN] = sigc::mem_fun(this, &Download::receive_next); // Key bindings for sub-ui's. - m_uiArray[DISPLAY_PEER_LIST]->bindings()[KEY_RIGHT] = sigc::bind(sigc::mem_fun(this, &Download::receive_change), DISPLAY_FILE_LIST); - m_uiArray[DISPLAY_PEER_INFO]->bindings()[KEY_LEFT] = sigc::bind(sigc::mem_fun(this, &Download::receive_change), DISPLAY_PEER_LIST); - m_uiArray[DISPLAY_FILE_LIST]->bindings()[KEY_LEFT] = sigc::bind(sigc::mem_fun(this, &Download::receive_change), DISPLAY_PEER_LIST); - m_uiArray[DISPLAY_TRACKER_LIST]->bindings()[KEY_LEFT] = sigc::bind(sigc::mem_fun(this, &Download::receive_change), DISPLAY_PEER_LIST); - m_uiArray[DISPLAY_CHUNKS_SEEN]->bindings()[KEY_LEFT] = sigc::bind(sigc::mem_fun(this, &Download::receive_change), DISPLAY_PEER_LIST); - m_uiArray[DISPLAY_TRANSFER_LIST]->bindings()[KEY_LEFT]= sigc::bind(sigc::mem_fun(this, &Download::receive_change), DISPLAY_PEER_LIST); + m_uiArray[DISPLAY_PEER_LIST]->bindings()[KEY_LEFT] = sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_MENU); + m_uiArray[DISPLAY_PEER_INFO]->bindings()[KEY_LEFT] = sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_MENU); + m_uiArray[DISPLAY_FILE_LIST]->bindings()[KEY_LEFT] = sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_MENU); + m_uiArray[DISPLAY_TRACKER_LIST]->bindings()[KEY_LEFT] = sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_MENU); + m_uiArray[DISPLAY_CHUNKS_SEEN]->bindings()[KEY_LEFT] = sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_MENU); + m_uiArray[DISPLAY_TRANSFER_LIST]->bindings()[KEY_LEFT]= sigc::bind(sigc::mem_fun(this, &Download::activate_display_focus), DISPLAY_MENU); // Doesn't belong here. m_uiArray[DISPLAY_PEER_LIST]->bindings()['*'] = sigc::mem_fun(this, &Download::receive_snub_peer); m_uiArray[DISPLAY_PEER_INFO]->bindings()['*'] = sigc::mem_fun(this, &Download::receive_snub_peer); } -void -Download::mark_dirty() { -// (*m_window)->mark_dirty(); -} - } diff --git a/src/ui/download.h b/src/ui/download.h index 55341585..2fe8e684 100644 --- a/src/ui/download.h +++ b/src/ui/download.h @@ -64,6 +64,7 @@ public: typedef std::list PList; typedef enum { + DISPLAY_MENU, DISPLAY_PEER_LIST, DISPLAY_PEER_INFO, DISPLAY_FILE_LIST, @@ -76,10 +77,13 @@ public: Download(DPtr d); ~Download(); - void activate(display::Frame* frame); + void activate(display::Frame* frame, bool focus = true); void disable(); - void activate_display(Display d); + void activate_display(Display d, bool focusDisplay); + + void activate_display_focus(Display d) { activate_display(d, true); } + void activate_display_menu(Display d) { activate_display(d, false); } void receive_next_priority(); void receive_prev_priority(); @@ -101,14 +105,11 @@ private: void receive_max_uploads(int t); void receive_min_peers(int t); void receive_max_peers(int t); - void receive_change(Display d); void receive_snub_peer(); void bind_keys(); - void mark_dirty(); - DPtr m_download; PList m_peers; PList::iterator m_focus; @@ -116,6 +117,8 @@ private: Display m_state; ElementBase* m_uiArray[DISPLAY_MAX_SIZE]; + bool m_focusDisplay; + WDownloadStatus* m_windowDownloadStatus; sigc::connection m_connPeerConnected; diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index d45a63ff..2461e711 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -55,7 +55,6 @@ #include "display/window_log.h" #include "display/window_title.h" -#include "display/window_statusbar.h" #include "control.h" #include "download.h" @@ -90,7 +89,7 @@ DownloadList::~DownloadList() { } void -DownloadList::activate(display::Frame* frame) { +DownloadList::activate(display::Frame* frame, bool focus) { if (is_active()) throw torrent::client_error("ui::DownloadList::activate() called on an already activated object"); diff --git a/src/ui/download_list.h b/src/ui/download_list.h index df4a94b8..b79808ed 100644 --- a/src/ui/download_list.h +++ b/src/ui/download_list.h @@ -95,7 +95,7 @@ public: DownloadList(); ~DownloadList(); - void activate(display::Frame* frame); + void activate(display::Frame* frame, bool focus = true); void disable(); void activate_display(Display d); diff --git a/src/ui/element_base.h b/src/ui/element_base.h index 97193194..f0c6b2f7 100644 --- a/src/ui/element_base.h +++ b/src/ui/element_base.h @@ -55,7 +55,7 @@ public: input::Bindings& bindings() { return m_bindings; } - virtual void activate(display::Frame* frame) = 0; + virtual void activate(display::Frame* frame, bool focus = true) = 0; virtual void disable() = 0; protected: diff --git a/src/ui/element_chunks_seen.cc b/src/ui/element_chunks_seen.cc index b5d50c94..0a72faaa 100644 --- a/src/ui/element_chunks_seen.cc +++ b/src/ui/element_chunks_seen.cc @@ -61,11 +61,12 @@ ElementChunksSeen::ElementChunksSeen(core::Download* d) : } void -ElementChunksSeen::activate(display::Frame* frame) { +ElementChunksSeen::activate(display::Frame* frame, bool focus) { if (is_active()) throw torrent::client_error("ui::ElementChunksSeen::activate(...) is_active()."); - control->input()->push_back(&m_bindings); + if (focus) + control->input()->push_back(&m_bindings); m_window = new WChunksSeen(m_download, &m_focus); m_window->set_active(true); diff --git a/src/ui/element_chunks_seen.h b/src/ui/element_chunks_seen.h index 019c43cd..4c95c4d6 100644 --- a/src/ui/element_chunks_seen.h +++ b/src/ui/element_chunks_seen.h @@ -55,7 +55,7 @@ public: ElementChunksSeen(core::Download* d); - void activate(display::Frame* frame); + void activate(display::Frame* frame, bool focus = true); void disable(); display::Window* window(); diff --git a/src/ui/element_download_list.cc b/src/ui/element_download_list.cc index 11b9e0b4..ba00035d 100644 --- a/src/ui/element_download_list.cc +++ b/src/ui/element_download_list.cc @@ -83,7 +83,7 @@ ElementDownloadList::ElementDownloadList() : } void -ElementDownloadList::activate(display::Frame* frame) { +ElementDownloadList::activate(display::Frame* frame, bool focus) { if (is_active()) throw torrent::client_error("ui::ElementDownloadList::activate(...) is_active()."); diff --git a/src/ui/element_download_list.h b/src/ui/element_download_list.h index f8fbe9e5..ce538663 100644 --- a/src/ui/element_download_list.h +++ b/src/ui/element_download_list.h @@ -56,7 +56,7 @@ public: ElementDownloadList(); - void activate(display::Frame* frame); + void activate(display::Frame* frame, bool focus = true); void disable(); core::View* view() { return m_view; } diff --git a/src/ui/element_file_list.cc b/src/ui/element_file_list.cc index 8e57bb15..2876a842 100644 --- a/src/ui/element_file_list.cc +++ b/src/ui/element_file_list.cc @@ -62,11 +62,12 @@ ElementFileList::ElementFileList(core::Download* d) : } void -ElementFileList::activate(display::Frame* frame) { +ElementFileList::activate(display::Frame* frame, bool focus) { if (m_window != NULL) throw torrent::client_error("ui::ElementFileList::activate(...) is_active()."); - control->input()->push_back(&m_bindings); + if (focus) + control->input()->push_back(&m_bindings); m_window = new WFileList(m_download, &m_focus); m_window->set_active(true); diff --git a/src/ui/element_file_list.h b/src/ui/element_file_list.h index 974dcc15..a046144e 100644 --- a/src/ui/element_file_list.h +++ b/src/ui/element_file_list.h @@ -58,7 +58,7 @@ public: ElementFileList(core::Download* d); - void activate(display::Frame* frame); + void activate(display::Frame* frame, bool focus = true); void disable(); display::Window* window(); diff --git a/src/ui/element_log_complete.cc b/src/ui/element_log_complete.cc index bf2075cc..c26fc567 100644 --- a/src/ui/element_log_complete.cc +++ b/src/ui/element_log_complete.cc @@ -54,7 +54,7 @@ ElementLogComplete::ElementLogComplete(core::Log* l) : } void -ElementLogComplete::activate(display::Frame* frame) { +ElementLogComplete::activate(display::Frame* frame, bool focus) { if (is_active()) throw torrent::client_error("ui::ElementLogComplete::activate(...) is_active()."); diff --git a/src/ui/element_log_complete.h b/src/ui/element_log_complete.h index 1ec78c11..f2d9e49f 100644 --- a/src/ui/element_log_complete.h +++ b/src/ui/element_log_complete.h @@ -55,7 +55,7 @@ public: ElementLogComplete(core::Log* l); - void activate(display::Frame* frame); + void activate(display::Frame* frame, bool focus = true); void disable(); display::Window* window(); diff --git a/src/ui/element_menu.cc b/src/ui/element_menu.cc new file mode 100644 index 00000000..bed4df4b --- /dev/null +++ b/src/ui/element_menu.cc @@ -0,0 +1,161 @@ +// 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 "display/frame.h" +#include "display/window_text.h" +#include "display/text_element_string.h" +#include "input/manager.h" + +#include "control.h" +#include "element_menu.h" + +namespace ui { + +struct ElementMenuEntry { + display::TextElementString* m_element; + + ElementMenu::slot_type m_slotFocus; + ElementMenu::slot_type m_slotSelect; +}; + +ElementMenu::ElementMenu() : + m_window(new WindowText), + m_focus(focus_invalid) { + + // Move bindings. + m_bindings[KEY_UP] = sigc::mem_fun(this, &ElementMenu::focus_prev); + m_bindings[KEY_DOWN] = sigc::mem_fun(this, &ElementMenu::focus_next); + m_bindings[KEY_RIGHT] = sigc::mem_fun(this, &ElementMenu::focus_select); +} + +ElementMenu::~ElementMenu() { + delete m_window; +} + +void +ElementMenu::activate(display::Frame* frame, bool focus) { + if (is_active()) + throw torrent::client_error("ui::ElementMenu::activate(...) is_active()."); + + if (focus) + control->input()->push_back(&m_bindings); + + m_window->set_active(true); + + m_frame = frame; + m_frame->initialize_window(m_window); +} + +void +ElementMenu::disable() { + if (!is_active()) + throw torrent::client_error("ui::ElementMenu::disable(...) !is_active()."); + + control->input()->erase(&m_bindings); + + m_frame->clear(); + m_frame = NULL; + + m_window->set_active(false); +} + +void +ElementMenu::push_back(const std::string& name, const slot_type& slotSelect, const slot_type& slotFocus) { + entry_type* entry = new entry_type; + + entry->m_element = new display::TextElementString(name); + entry->m_slotSelect = slotSelect; + entry->m_slotFocus = slotFocus; + + m_window->push_back(NULL); + m_window->push_back(entry->m_element); + + base_type::push_back(entry); + + // For the moment, don't bother doing anything if the window is + // already active. + m_window->mark_dirty(); +} + +void +ElementMenu::focus_next() { + if (empty() || (size() == 1 && m_focus == 0)) + return; + + if (m_focus < size()) + base_type::operator[](m_focus)->m_element->set_attributes(display::Attributes::a_normal); + + if (++m_focus >= size()) + m_focus = 0; + + base_type::operator[](m_focus)->m_slotFocus(); + base_type::operator[](m_focus)->m_element->set_attributes(display::Attributes::a_reverse); + + m_window->mark_dirty(); +} + +void +ElementMenu::focus_prev() { + if (empty() || (size() == 1 && m_focus == 0)) + return; + + if (m_focus < size()) + base_type::operator[](m_focus)->m_element->set_attributes(display::Attributes::a_normal); + + if (--m_focus >= size()) + m_focus = size() - 1; + + base_type::operator[](m_focus)->m_slotFocus(); + base_type::operator[](m_focus)->m_element->set_attributes(display::Attributes::a_reverse); + + m_window->mark_dirty(); +} + +void +ElementMenu::focus_select() { + if (m_focus >= size()) + return; + + base_type::operator[](m_focus)->m_slotSelect(); + + m_window->mark_dirty(); +} + +} diff --git a/src/ui/element_menu.h b/src/ui/element_menu.h new file mode 100644 index 00000000..427f4d1d --- /dev/null +++ b/src/ui/element_menu.h @@ -0,0 +1,101 @@ +// 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_UI_ELEMENT_MENU_H +#define RTORRENT_UI_ELEMENT_MENU_H + +#include + +#include "core/download.h" + +#include "element_base.h" + +namespace display { + class WindowText; +} + +namespace ui { + +struct ElementMenuEntry; + +class ElementMenu : public ElementBase, public std::vector { +public: + typedef ElementMenuEntry entry_type; + typedef std::vector base_type; + typedef sigc::slot0 slot_type; + + typedef display::WindowText WindowText; + + typedef uint32_t size_type; + + typedef base_type::value_type value_type; + typedef base_type::reference reference; + typedef base_type::iterator iterator; + typedef base_type::const_iterator const_iterator; + typedef base_type::reverse_iterator reverse_iterator; + + using base_type::empty; + using base_type::size; + + static const size_type focus_invalid = ~size_type(); + + ElementMenu(); + ~ElementMenu(); + + void activate(display::Frame* frame, bool focus = false); + void disable(); + + // Consider returning a pointer that can be used to manipulate + // entries, f.ex disabling them. + + void push_back(const std::string& name, + const slot_type& slotSelect = slot_type(), + const slot_type& slotFocus = slot_type()); + + void focus_next(); + void focus_prev(); + + void focus_select(); + +private: + WindowText* m_window; + + size_type m_focus; +}; + +} + +#endif diff --git a/src/ui/element_peer_info.cc b/src/ui/element_peer_info.cc index d4961c0a..49cee32a 100644 --- a/src/ui/element_peer_info.cc +++ b/src/ui/element_peer_info.cc @@ -56,11 +56,12 @@ ElementPeerInfo::ElementPeerInfo(core::Download* d, PList* l, PList::iterator* f } void -ElementPeerInfo::activate(display::Frame* frame) { +ElementPeerInfo::activate(display::Frame* frame, bool focus) { if (is_active()) throw torrent::client_error("ui::ElementPeerInfo::activate(...) is_active()."); - control->input()->push_back(&m_bindings); + if (focus) + control->input()->push_back(&m_bindings); m_window = new WPeerInfo(m_download, m_list, m_focus); m_window->set_active(true); diff --git a/src/ui/element_peer_info.h b/src/ui/element_peer_info.h index 216a1714..02a8c609 100644 --- a/src/ui/element_peer_info.h +++ b/src/ui/element_peer_info.h @@ -56,7 +56,7 @@ public: ElementPeerInfo(core::Download* d, PList* l, PList::iterator* f); - void activate(display::Frame* frame); + void activate(display::Frame* frame, bool focus = true); void disable(); display::Window* window(); diff --git a/src/ui/element_peer_list.cc b/src/ui/element_peer_list.cc index a51085fc..33cc0f65 100644 --- a/src/ui/element_peer_list.cc +++ b/src/ui/element_peer_list.cc @@ -55,11 +55,12 @@ ElementPeerList::ElementPeerList(core::Download* d, PList* l, PList::iterator* f } void -ElementPeerList::activate(display::Frame* frame) { +ElementPeerList::activate(display::Frame* frame, bool focus) { if (is_active()) throw torrent::client_error("ui::ElementPeerList::activate(...) is_active()."); - control->input()->push_back(&m_bindings); + if (focus) + control->input()->push_back(&m_bindings); m_window = new WPeerList(m_download, m_list, m_focus); m_window->set_active(true); @@ -82,9 +83,4 @@ ElementPeerList::disable() { m_window = NULL; } -display::Window* -ElementPeerList::window() { - return m_window; -} - } diff --git a/src/ui/element_peer_list.h b/src/ui/element_peer_list.h index a77788c5..ce090b36 100644 --- a/src/ui/element_peer_list.h +++ b/src/ui/element_peer_list.h @@ -41,8 +41,6 @@ #include "element_base.h" -class Control; - namespace display { class WindowPeerList; } @@ -56,11 +54,9 @@ public: ElementPeerList(core::Download* d, PList* l, PList::iterator* f); - void activate(display::Frame* frame); + void activate(display::Frame* frame, bool focus = true); void disable(); - display::Window* window(); - private: core::Download* m_download; WPeerList* m_window; diff --git a/src/ui/element_string_list.cc b/src/ui/element_string_list.cc index 7d4e3899..59b0be5a 100644 --- a/src/ui/element_string_list.cc +++ b/src/ui/element_string_list.cc @@ -51,7 +51,7 @@ ElementStringList::ElementStringList() : } void -ElementStringList::activate(display::Frame* frame) { +ElementStringList::activate(display::Frame* frame, bool focus) { if (is_active()) throw torrent::client_error("ui::ElementStringList::activate(...) is_active()."); diff --git a/src/ui/element_string_list.h b/src/ui/element_string_list.h index e01781ec..654f0131 100644 --- a/src/ui/element_string_list.h +++ b/src/ui/element_string_list.h @@ -57,7 +57,7 @@ public: ElementStringList(); - void activate(display::Frame* frame); + void activate(display::Frame* frame, bool focus = true); void disable(); display::Window* window() { return m_window; } diff --git a/src/ui/element_tracker_list.cc b/src/ui/element_tracker_list.cc index a708ddf4..81b74327 100644 --- a/src/ui/element_tracker_list.cc +++ b/src/ui/element_tracker_list.cc @@ -61,11 +61,12 @@ ElementTrackerList::ElementTrackerList(core::Download* d) : } void -ElementTrackerList::activate(display::Frame* frame) { +ElementTrackerList::activate(display::Frame* frame, bool focus) { if (m_window != NULL) throw torrent::client_error("ui::ElementTrackerList::activate(...) is_active()."); - control->input()->push_back(&m_bindings); + if (focus) + control->input()->push_back(&m_bindings); m_window = new WTrackerList(m_download, &m_focus); m_window->set_active(true); diff --git a/src/ui/element_tracker_list.h b/src/ui/element_tracker_list.h index 840f750b..a09f0a32 100644 --- a/src/ui/element_tracker_list.h +++ b/src/ui/element_tracker_list.h @@ -55,7 +55,7 @@ public: ElementTrackerList(core::Download* d); - void activate(display::Frame* frame); + void activate(display::Frame* frame, bool focus = true); void disable(); display::Window* window(); diff --git a/src/ui/element_transfer_list.cc b/src/ui/element_transfer_list.cc index ff20d6e1..8ceda14c 100644 --- a/src/ui/element_transfer_list.cc +++ b/src/ui/element_transfer_list.cc @@ -61,11 +61,12 @@ ElementTransferList::ElementTransferList(core::Download* d) : } void -ElementTransferList::activate(display::Frame* frame) { +ElementTransferList::activate(display::Frame* frame, bool focus) { if (is_active()) throw torrent::client_error("ui::ElementTransferList::activate(...) is_active()."); - control->input()->push_back(&m_bindings); + if (focus) + control->input()->push_back(&m_bindings); m_window = new WTransferList(m_download, &m_focus); m_window->set_active(true); diff --git a/src/ui/element_transfer_list.h b/src/ui/element_transfer_list.h index 3a5d66ea..51a6774f 100644 --- a/src/ui/element_transfer_list.h +++ b/src/ui/element_transfer_list.h @@ -55,7 +55,7 @@ public: ElementTransferList(core::Download* d); - void activate(display::Frame* frame); + void activate(display::Frame* frame, bool focus = true); void disable(); display::Window* window(); diff --git a/src/ui/root.cc b/src/ui/root.cc index 2ce12197..489d36f1 100644 --- a/src/ui/root.cc +++ b/src/ui/root.cc @@ -91,6 +91,7 @@ Root::init(Control* c) { m_windowTitle->set_active(true); m_windowStatusbar->set_active(true); + m_windowStatusbar->set_bottom(true); setup_keys(); diff --git a/src/utils/variable_map.cc b/src/utils/variable_map.cc index d4b9ba15..28cbda23 100644 --- a/src/utils/variable_map.cc +++ b/src/utils/variable_map.cc @@ -51,7 +51,7 @@ namespace utils { VariableMap::~VariableMap() { - std::for_each(base_type::begin(), base_type::end(), rak::on(rak::mem_ptr_ref(&value_type::second), rak::call_delete())); + std::for_each(base_type::begin(), base_type::end(), rak::on(rak::mem_ref(&value_type::second), rak::call_delete())); } void