From 090b3889ce3c8523e304c9e550d31d1ef9459595 Mon Sep 17 00:00:00 2001 From: kannibalox Date: Thu, 19 Dec 2024 20:20:15 -0500 Subject: [PATCH 01/11] Add color support for the TUI Closes #398 --- src/command_ui.cc | 56 ++---- src/display/canvas.cc | 163 +++++++++++---- src/display/canvas.h | 241 +++++++++++++++-------- src/display/color_map.h | 48 +++++ src/display/window_download_list.cc | 106 +++++----- src/display/window_download_list.h | 38 +--- src/display/window_download_statusbar.cc | 39 +--- src/display/window_statusbar.cc | 38 +--- src/display/window_title.cc | 44 +---- 9 files changed, 413 insertions(+), 360 deletions(-) create mode 100644 src/display/color_map.h diff --git a/src/command_ui.cc b/src/command_ui.cc index 7e3b7aae..99040d08 100644 --- a/src/command_ui.cc +++ b/src/command_ui.cc @@ -1,39 +1,3 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, 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 @@ -47,8 +11,10 @@ #include "core/manager.h" #include "core/view_manager.h" +#include "display/canvas.h" #include "ui/root.h" #include "ui/download_list.h" +#include "display/color_map.h" #include "rpc/parse.h" #include "globals.h" @@ -796,6 +762,13 @@ cmd_status_throttle_names(bool up, const torrent::Object::list_type& args) { return torrent::Object(); } +torrent::Object +apply_set_color(int color_id, const torrent::Object::string_type& color_str) { + control->object_storage()->set_str_string(display::color_vars[color_id], color_str); + display::Canvas::build_colors(); + return torrent::Object(); +} + void initialize_command_ui() { CMD2_VAR_STRING("keys.layout", "qwerty"); @@ -893,4 +866,15 @@ initialize_command_ui() { CMD2_ANY_LIST ("elapsed.less", std::bind(&apply_elapsed_less, std::placeholders::_2)); CMD2_ANY_LIST ("elapsed.greater", std::bind(&apply_elapsed_greater, std::placeholders::_2)); + + // Build set/get methods for all color definitions + for (int color_id = 1; color_id < display::RCOLOR_MAX; color_id++) { + control->object_storage()->insert_str(display::color_vars[color_id], "", rpc::object_storage::flag_string_type); + CMD2_ANY_STRING(std::string(display::color_vars[color_id]) + ".set", [color_id](const auto&, const auto& arg) { + return apply_set_color(color_id, arg); + }); + CMD2_ANY(display::color_vars[color_id], [color_id](const auto&, const auto&) { + return control->object_storage()->get_str(display::color_vars[color_id]); + }); + } } diff --git a/src/display/canvas.cc b/src/display/canvas.cc index fc24bbc3..51cc592d 100644 --- a/src/display/canvas.cc +++ b/src/display/canvas.cc @@ -1,45 +1,9 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, Jari Sundell -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations -// including the two. -// -// You must obey the GNU General Public License in all respects for -// all of the code used other than OpenSSL. If you modify file(s) -// with this exception, you may extend this exception to your version -// of the file(s), but you are not obligated to do so. If you do not -// wish to do so, delete this exception statement from your version. -// If you delete this exception statement from all source files in the -// program, then also delete it here. -// -// Contact: Jari Sundell -// -// Skomakerveien 33 -// 3185 Skoppum, NORWAY - #include "config.h" -#include #include #include #include +#include #include "rpc/parse_commands.h" @@ -48,14 +12,16 @@ namespace display { bool Canvas::m_isInitialized = false; -bool Canvas::m_isDaemon = false; +bool Canvas::m_isDaemon = false; +// Maps ncurses color IDs to a ncurses attribute int +std::unordered_map Canvas::m_attr_map = {}; Canvas::Canvas(int x, int y, int width, int height) { if (!m_isDaemon) { - m_window = newwin(height, width, y, x); + m_window = newwin(height, width, y, x); - if (m_window == NULL) - throw torrent::internal_error("Could not allocate ncurses canvas."); + if (m_window == NULL) + throw torrent::internal_error("Could not allocate ncurses canvas."); } } @@ -73,7 +39,7 @@ Canvas::print_attributes(unsigned int x, unsigned int y, const char* first, cons move(x, y); attr_t org_attr; - short org_pair; + short org_pair; wattr_get(m_window, &org_attr, &org_pair, NULL); attributes_list::const_iterator attrItr = attributes->begin(); @@ -111,6 +77,9 @@ Canvas::initialize() { if (!m_isDaemon) { initscr(); + start_color(); + use_default_colors(); + Canvas::build_colors(); raw(); noecho(); nodelay(stdscr, TRUE); @@ -119,6 +88,114 @@ Canvas::initialize() { } } +// Function wrapper for what possibly is a macro +int +get_colors() { + return COLORS; +} + +// Turns the string color definitions from the "ui.color.*" RPC +// commands into valid ncurses color pairs +void +Canvas::build_colors() { + + // This may get called early in the start process by the config + // file, so we need to delay building until initscr() has a chance + // to run + if (!m_isInitialized || m_isDaemon) + return; + + // basic color names, index maps to ncurses COLOR_* + static const char* color_names[] = { + "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"}; + + // Those hold the background colors of "odd" and "even" + int bg_odd = -1; + int bg_even = -1; + + for (int k = 1; k < RCOLOR_MAX; k++) { + init_pair(k, -1, -1); + std::string color_def = rpc::call_command_string(color_vars[k]); + if (color_def.empty()) + continue; // Use terminal default if definition is empty + + short color[2] = {-1, -1}; // fg, bg + short color_idx = 0; // 0 = fg; 1 = bg + short bright = 0; + unsigned long attr = A_NORMAL; + + // Process string as space-separated words + size_t start = 0, end = 0; + while (true) { + end = color_def.find(' ', start); + std::string word = color_def.substr(start, end - start); + + if (word == "bold") + attr |= A_BOLD; + else if (word == "standout") + attr |= A_STANDOUT; + else if (word == "underline") + attr |= A_UNDERLINE; + else if (word == "reverse") + attr |= A_REVERSE; + else if (word == "blink") + attr |= A_BLINK; + else if (word == "dim") + attr |= A_DIM; + else if (word == "on") { + color_idx = 1; + bright = 0; + } // Switch to background color + else if (word == "gray" || word == "grey") + color[color_idx] = bright ? 7 : 8; // Bright gray is white + else if (word == "bright") + bright = 8; + else if (word.find_first_not_of("0123456789") == std::string::npos) { + // Handle numeric index + short c = -1; + sscanf(word.c_str(), "%hd", &c); + color[color_idx] = c; + } else + for (short c = 0; c < 8; c++) { // Check for basic color names + if (word == color_names[c]) { + color[color_idx] = bright + c; + break; + } + } + if (end == std::string::npos) + break; + start = end + 1; + } + + // Check that fg & bg color index is valid + if ((color[0] != -1 && color[0] >= get_colors()) || (color[1] != -1 && color[1] >= get_colors())) { + char buf[33]; + sprintf(buf, "%d", get_colors()); + Canvas::cleanup(); + throw torrent::input_error(color_def + ": your terminal only supports " + buf + " colors."); + } + + m_attr_map[k] = attr; // overwrite or insert the value + init_pair(k, color[0], color[1]); + if (k == RCOLOR_EVEN) + bg_even = color[1]; + if (k == RCOLOR_ODD) + bg_odd = color[1]; + } + + // Now make copies of the basic colors with the "odd" and "even" definitions mixed in + for (int k = 1; k < RCOLOR_MAX; k++) { + short fg, bg; + pair_content(k, &fg, &bg); + + // Replace the background color, and mix in the attributes + m_attr_map[k + 1 * RCOLOR_MAX] = m_attr_map[k] | m_attr_map[RCOLOR_EVEN]; + m_attr_map[k + 2 * RCOLOR_MAX] = m_attr_map[k] | m_attr_map[RCOLOR_ODD]; + init_pair(k + 1 * RCOLOR_MAX, fg, bg == -1 ? bg_even : bg); + init_pair(k + 2 * RCOLOR_MAX, fg, bg == -1 ? bg_odd : bg); + } +} + void Canvas::cleanup() { if (!m_isInitialized) @@ -143,4 +220,4 @@ Canvas::term_size() { return std::pair(80, 24); } -} +} // namespace display diff --git a/src/display/canvas.h b/src/display/canvas.h index b324d81a..51c73f3f 100644 --- a/src/display/canvas.h +++ b/src/display/canvas.h @@ -1,123 +1,206 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, 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_CANVAS_H #define RTORRENT_DISPLAY_CANVAS_H #include +#include #include #include "attributes.h" +#include "color_map.h" namespace display { class Canvas { public: - typedef std::vector attributes_list; + typedef std::vector attributes_list; + typedef std::unordered_map attributes_map; Canvas(int x = 0, int y = 0, int width = 0, int height = 0); - ~Canvas() { if (!m_isDaemon) { delwin(m_window); } } + ~Canvas() { + if (!m_isDaemon) { + delwin(m_window); + } + } - void refresh() { if (!m_isDaemon) { wnoutrefresh(m_window); } } - static void refresh_std() { if (!m_isDaemon) { wnoutrefresh(stdscr); } } - void redraw() { if (!m_isDaemon) { redrawwin(m_window); } } - static void redraw_std() { if (!m_isDaemon) { redrawwin(stdscr); } } + void refresh() { + if (!m_isDaemon) { + wnoutrefresh(m_window); + } + } + static void refresh_std() { + if (!m_isDaemon) { + wnoutrefresh(stdscr); + } + } + void redraw() { + if (!m_isDaemon) { + redrawwin(m_window); + } + } + static void redraw_std() { + if (!m_isDaemon) { + redrawwin(stdscr); + } + } - void resize(int w, int h) { if (!m_isDaemon) { wresize(m_window, h, w); } } - void resize(int x, int y, int w, int h); + void resize(int w, int h) { + if (!m_isDaemon) { + wresize(m_window, h, w); + } + } + void resize(int x, int y, int w, int h); - static void resize_term(int x, int y) { if (!m_isDaemon) { resizeterm(y, x); } } - static void resize_term(std::pair dim) { if (!m_isDaemon) { resizeterm(dim.second, dim.first); } } + static void resize_term(int x, int y) { + if (!m_isDaemon) { + resizeterm(y, x); + } + } + static void resize_term(std::pair dim) { + if (!m_isDaemon) { + resizeterm(dim.second, dim.first); + } + } - unsigned int get_x() { int x, __UNUSED y; if (!m_isDaemon) { getyx(m_window, y, x); } else { x=1; } return x; } - unsigned int get_y() { int x, y; if (!m_isDaemon) { getyx(m_window, y, x); } else { y=1; } return y; } + unsigned int get_x() { + int x, __UNUSED y; + if (!m_isDaemon) { + getyx(m_window, y, x); + } else { + x = 1; + } + return x; + } + unsigned int get_y() { + int x, y; + if (!m_isDaemon) { + getyx(m_window, y, x); + } else { + y = 1; + } + return y; + } - unsigned int width() { int x, __UNUSED y; if (!m_isDaemon) { getmaxyx(m_window, y, x); } else { x=80; } return x; } - unsigned int height() { int x, y; if (!m_isDaemon) { getmaxyx(m_window, y, x); } else { y=24; } return y; } + unsigned int width() { + int x, __UNUSED y; + if (!m_isDaemon) { + getmaxyx(m_window, y, x); + } else { + x = 80; + } + return x; + } + unsigned int height() { + int x, y; + if (!m_isDaemon) { + getmaxyx(m_window, y, x); + } else { + y = 24; + } + return y; + } - void move(unsigned int x, unsigned int y) { if (!m_isDaemon) { wmove(m_window, y, x); } } + void move(unsigned int x, unsigned int y) { + if (!m_isDaemon) { + wmove(m_window, y, x); + } + } - chtype get_background() { chtype bg=0; if (!m_isDaemon) { bg=getbkgd(m_window); } return bg; } - void set_background(chtype c) { if (!m_isDaemon) { return wbkgdset(m_window, c); } } - - void erase() { if (!m_isDaemon) { werase(m_window); } } - static void erase_std() { if (!m_isDaemon) { werase(stdscr); } } - - void print_border(chtype ls, chtype rs, - chtype ts, chtype bs, - chtype tl, chtype tr, - chtype bl, chtype br) { if (!m_isDaemon) { wborder(m_window, ls, rs, ts, bs, tl, tr, bl, br); } } + void erase() { + if (!m_isDaemon) { + werase(m_window); + } + } + static void erase_std() { + if (!m_isDaemon) { + werase(stdscr); + } + } // The format string is non-const, but that will not be a problem // since the string shall always be a C string choosen at // compiletime. Might cause extra copying of the string? - void print(const char* str, ...); - void print(unsigned int x, unsigned int y, const char* str, ...); + void print(const char* str, ...); + void print(unsigned int x, unsigned int y, const char* str, ...); - void print_attributes(unsigned int x, unsigned int y, const char* first, const char* last, const attributes_list* attributes); + void print_attributes(unsigned int x, unsigned int y, const char* first, const char* last, const attributes_list* attributes); - void print_char(const chtype ch) { if (!m_isDaemon) { waddch(m_window, ch); } } - void print_char(unsigned int x, unsigned int y, const chtype ch) { if (!m_isDaemon) { mvwaddch(m_window, y, x, ch); } } + void print_char(const chtype ch) { + if (!m_isDaemon) { + waddch(m_window, ch); + } + } + void print_char(unsigned int x, unsigned int y, const chtype ch) { + if (!m_isDaemon) { + mvwaddch(m_window, y, x, ch); + } + } - void set_attr(unsigned int x, unsigned int y, unsigned int n, int attr, int color) { if (!m_isDaemon) { 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) { + if (!m_isDaemon) { + mvwchgat(m_window, y, x, n, attr, color, NULL); + } + } - void set_default_attributes(int attr) { if (!m_isDaemon) { (void)wattrset(m_window, attr); } } + void set_attr(unsigned int x, unsigned int y, unsigned int n, ColorKind k) { + if (!m_isDaemon) { + mvwchgat(m_window, y, x, n, m_attr_map[k], k, NULL); + } + } + + void set_default_attributes(int attr) { + if (!m_isDaemon) { + (void)wattrset(m_window, attr); + } + } // Initialize stdscr. - static void initialize(); - static void cleanup(); + static void initialize(); + static void build_colors(); + static void cleanup(); - static int get_screen_width() { int x, __UNUSED y; if (!m_isDaemon) { getmaxyx(stdscr, y, x); } else { x=80; } return x; } - static int get_screen_height() { int x, y; if (!m_isDaemon) { getmaxyx(stdscr, y, x); } else { y=24;} return y; } + static int get_screen_width() { + int x, __UNUSED y; + if (!m_isDaemon) { + getmaxyx(stdscr, y, x); + } else { + x = 80; + } + return x; + } + static int get_screen_height() { + int x, y; + if (!m_isDaemon) { + getmaxyx(stdscr, y, x); + } else { + y = 24; + } + return y; + } static std::pair term_size(); - static void do_update() { if (!m_isDaemon) { doupdate(); } } + static void do_update() { + if (!m_isDaemon) { + doupdate(); + } + } - static bool daemon() { return m_isDaemon; } + static bool daemon() { return m_isDaemon; } + + static const attributes_map& attr_map() { return m_attr_map; } private: Canvas(const Canvas&); - void operator = (const Canvas&); + void operator=(const Canvas&); - static bool m_isInitialized; - static bool m_isDaemon; + static bool m_isInitialized; + static bool m_isDaemon; - WINDOW* m_window; + // Maps ncurses color IDs to a ncurses attribute int + static std::unordered_map m_attr_map; + + WINDOW* m_window; }; inline void @@ -143,6 +226,6 @@ Canvas::print(unsigned int x, unsigned int y, const char* str, ...) { } } -} +} // namespace display #endif diff --git a/src/display/color_map.h b/src/display/color_map.h new file mode 100644 index 00000000..3a9fb1e0 --- /dev/null +++ b/src/display/color_map.h @@ -0,0 +1,48 @@ +#ifndef RTORRENT_DISPLAY_COLOR_MAP_H +#define RTORRENT_DISPLAY_COLOR_MAP_H + +#include + +#include + +namespace display { + +enum ColorKind { + RCOLOR_NCURSES_DEFAULT, // Color 0 is reserved by ncurses and cannot be changed + RCOLOR_TITLE, + RCOLOR_FOOTER, + RCOLOR_FOCUS, + RCOLOR_LABEL, + RCOLOR_INFO, + RCOLOR_ALARM, + RCOLOR_COMPLETE, + RCOLOR_SEEDING, + RCOLOR_STOPPED, + RCOLOR_QUEUED, + RCOLOR_INCOMPLETE, + RCOLOR_LEECHING, + RCOLOR_ODD, + RCOLOR_EVEN, + RCOLOR_MAX, +}; + +static const char* color_vars[RCOLOR_MAX] = { + 0, + "ui.color.title", + "ui.color.footer", + "ui.color.focus", + "ui.color.label", + "ui.color.info", + "ui.color.alarm", + "ui.color.complete", + "ui.color.seeding", + "ui.color.stopped", + "ui.color.queued", + "ui.color.incomplete", + "ui.color.leeching", + "ui.color.odd", + "ui.color.even", +}; + +} // namespace display +#endif diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc index 6b4936d2..428b786d 100644 --- a/src/display/window_download_list.cc +++ b/src/display/window_download_list.cc @@ -1,41 +1,6 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, 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 "display/color_map.h" #include #include "core/download.h" @@ -50,14 +15,14 @@ namespace display { WindowDownloadList::WindowDownloadList() : - Window(new Canvas, 0, 120, 1, extent_full, extent_full), - m_view(NULL) { + Window(new Canvas, 0, 120, 1, extent_full, extent_full), + m_view(NULL) { } WindowDownloadList::~WindowDownloadList() { if (m_view != NULL) m_view->signal_changed().erase(m_changed_itr); - + m_view = NULL; } @@ -72,8 +37,26 @@ WindowDownloadList::set_view(core::View* l) { m_changed_itr = m_view->signal_changed().insert(m_view->signal_changed().begin(), std::bind(&Window::mark_dirty, this)); } +// Return a pair of ints, representing a) the ncurses attributes and b) the ncurses color pair ID to use +std::pair +WindowDownloadList::get_attr_color(core::View::iterator selected) { + core::Download* item = *selected; + unsigned long focus_attr = selected == m_view->focus() ? m_canvas->attr_map().at(RCOLOR_FOCUS) : 0; + int offset = (((selected - m_view->begin_visible()) & 1) + 1) * RCOLOR_MAX; // Determine the even/odd offset for the color pair + bool active = item->is_open() && item->is_active(); + int title_color; + if (item->is_done()) + title_color = (active ? item->info()->up_rate()->rate() ? RCOLOR_SEEDING : RCOLOR_COMPLETE : RCOLOR_STOPPED) + offset; + else + title_color = (active ? item->info()->down_rate()->rate() ? RCOLOR_LEECHING : RCOLOR_INCOMPLETE : RCOLOR_QUEUED) + offset; + return std::make_pair(m_canvas->attr_map().at(title_color) | focus_attr, title_color); +} + void WindowDownloadList::redraw() { + if (m_canvas->daemon()) + return; + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); m_canvas->erase(); @@ -81,7 +64,7 @@ WindowDownloadList::redraw() { if (m_view == NULL) return; - m_canvas->print(0, 0, "%s", ("[View: " + m_view->name() + (m_view->get_filter_temp().is_empty() ? "" : " (filtered)") + "]").c_str()); + m_canvas->print("%s", ("[View: " + m_view->name() + (m_view->get_filter_temp().is_empty() ? "" : " (filtered)") + "]").c_str()); if (m_view->empty_visible() || m_canvas->width() < 5 || m_canvas->height() < 2) return; @@ -95,7 +78,9 @@ WindowDownloadList::redraw() { m_canvas->print(m_canvas->width() - 16, 0, "[%5d of %-5d]", item_idx + 1, m_view->size()); } - int layout_height; + m_canvas->set_attr(0, 0, -1, RCOLOR_TITLE); + + int layout_height; const std::string layout_name = rpc::call_command_string("ui.torrent_list.layout"); if (layout_name == "full") { @@ -107,12 +92,10 @@ WindowDownloadList::redraw() { return; } - typedef std::pair Range; - - Range range = rak::advance_bidirectional(m_view->begin_visible(), - m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(), - m_view->end_visible(), - m_canvas->height() / layout_height); + ViewRange range = rak::advance_bidirectional(m_view->begin_visible(), + m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(), + m_view->end_visible(), + m_canvas->height() / layout_height); // Make sure we properly fill out the last lines so it looks like // there are more torrents, yet don't hide it if we got the last one @@ -120,8 +103,8 @@ WindowDownloadList::redraw() { if (range.second != m_view->end_visible()) ++range.second; - int pos = 1; - char buffer[m_canvas->width() + 1]; + int pos = 1; + char buffer[m_canvas->width() + 1]; char* last = buffer + m_canvas->width() - 2 + 1; // Add a proper 'column info' method. @@ -134,25 +117,38 @@ WindowDownloadList::redraw() { if (layout_name == "full") { while (range.first != range.second) { + bool is_focused = range.first == m_view->focus(); + char focus_char = is_focused ? '*' : ' '; + ColorKind focus_color = is_focused ? RCOLOR_FOCUS : RCOLOR_LABEL; + auto attr_color = get_attr_color(range.first); + print_download_title(buffer, last, *range.first); - m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); + m_canvas->print(0, pos, "%c %s", focus_char, buffer); + m_canvas->set_attr(2, pos++, -1, attr_color.first, attr_color.second); + print_download_info_full(buffer, last, *range.first); - m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); + m_canvas->print(0, pos, "%c %s", focus_char, buffer); + m_canvas->set_attr(2, pos++, -1, focus_color); + print_download_status(buffer, last, *range.first); - m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); + m_canvas->print(0, pos, "%c %s", focus_char, buffer); + m_canvas->set_attr(2, pos++, -1, focus_color); range.first++; } } else { while (range.first != range.second) { + char focus_char = range.first == m_view->focus() ? '*' : ' '; + auto attr_color = get_attr_color(range.first); + print_download_info_compact(buffer, last, *range.first); - m_canvas->set_default_attributes(range.first == m_view->focus() ? A_REVERSE : A_NORMAL); - m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); + m_canvas->print(0, pos, "%c %s", focus_char, buffer); + m_canvas->set_attr(2, pos++, -1, attr_color.first, attr_color.second); range.first++; } } } -} +} // namespace display diff --git a/src/display/window_download_list.h b/src/display/window_download_list.h index 8e15cac9..897d7c25 100644 --- a/src/display/window_download_list.h +++ b/src/display/window_download_list.h @@ -1,39 +1,3 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, 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_DOWNLOAD_LIST_H #define RTORRENT_DISPLAY_WINDOW_DOWNLOAD_LIST_H @@ -47,6 +11,7 @@ namespace display { class WindowDownloadList : public Window { public: typedef core::View::signal_void::iterator signal_void_itr; + typedef std::pair ViewRange; WindowDownloadList(); ~WindowDownloadList(); @@ -58,6 +23,7 @@ public: private: core::View* m_view; + std::pair get_attr_color(core::View::iterator selected); signal_void_itr m_changed_itr; }; diff --git a/src/display/window_download_statusbar.cc b/src/display/window_download_statusbar.cc index 284b8b54..dfc7347d 100644 --- a/src/display/window_download_statusbar.cc +++ b/src/display/window_download_statusbar.cc @@ -1,39 +1,3 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, 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 @@ -59,6 +23,9 @@ WindowDownloadStatusbar::WindowDownloadStatusbar(core::Download* d) : void WindowDownloadStatusbar::redraw() { + if (m_canvas->daemon()) + return; + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); m_canvas->erase(); diff --git a/src/display/window_statusbar.cc b/src/display/window_statusbar.cc index 9592a338..4447d5b6 100644 --- a/src/display/window_statusbar.cc +++ b/src/display/window_statusbar.cc @@ -1,41 +1,6 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, 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 "display/color_map.h" #include #include @@ -66,6 +31,7 @@ WindowStatusbar::redraw() { position = print_status_extra(buffer, last); m_canvas->print(m_canvas->width() - (position - buffer), 0, "%s", buffer); } + m_canvas->set_attr(0, 0, -1, RCOLOR_FOOTER); m_lastTick = control->tick(); } diff --git a/src/display/window_title.cc b/src/display/window_title.cc index 1df79adf..a0f70f38 100644 --- a/src/display/window_title.cc +++ b/src/display/window_title.cc @@ -1,39 +1,3 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, 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 "canvas.h" @@ -43,11 +7,13 @@ namespace display { void WindowTitle::redraw() { + if (m_canvas->daemon()) + return; + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); m_canvas->erase(); - m_canvas->print(std::max(0, ((int)m_canvas->width() - (int)m_title.size()) / 2 - 4), 0, - "*** %s ***", m_title.c_str()); + m_canvas->print(std::max(0, ((int)m_canvas->width() - (int)m_title.size()) / 2 - 4), 0, "*** %s ***", m_title.c_str()); } -} +} // namespace display From 842edaa2010095f43b9090c8a00f11ca44ea39fe Mon Sep 17 00:00:00 2001 From: rakshasa Date: Wed, 1 Jan 2025 12:15:54 +0000 Subject: [PATCH 02/11] Added missing base64.h header. --- .gitignore | 1 + src/Makefile.am | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index cde4aa20..901b7963 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ .libs Makefile aclocal.m4 +ar-lib autom4te.cache compile config.h diff --git a/src/Makefile.am b/src/Makefile.am index b3769aad..457b05f2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -155,6 +155,7 @@ libsub_root_a_SOURCES = \ ui/root.cc \ ui/root.h \ \ + utils/base64.h \ utils/directory.cc \ utils/directory.h \ utils/file_status_cache.cc \ From 8f77d879946d81ae15c38587b392b149c49fc1d3 Mon Sep 17 00:00:00 2001 From: stickz Date: Mon, 30 Dec 2024 13:16:40 -0500 Subject: [PATCH 03/11] tinyxml2: Change from i4 to i8 We need to follow the same specification as xmlrpc-c until we deprecate it. It is breaking various software such as sonarr. We can't have xmlrpc using i8 and tinyxml2 using i4, while we allow both to be used. --- src/rpc/xmlrpc_tinyxml2.cc | 10 +++----- test/rpc/xmlrpc_test.cc | 2 +- test/rpc/xmlrpc_test_data.txt | 46 +++++++++++++++++------------------ 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/rpc/xmlrpc_tinyxml2.cc b/src/rpc/xmlrpc_tinyxml2.cc index 42ee5589..1c9f669d 100644 --- a/src/rpc/xmlrpc_tinyxml2.cc +++ b/src/rpc/xmlrpc_tinyxml2.cc @@ -142,11 +142,7 @@ print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) { printer->CloseElement(true); break; case torrent::Object::TYPE_VALUE: - if (obj.as_value() > ((torrent::Object::value_type)2 << 30) || obj.as_value() < -((torrent::Object::value_type)2 << 30)) { - printer->OpenElement("i8", true); - } else { - printer->OpenElement("i4", true); - } + printer->OpenElement("i8", true); printer->PushText(std::to_string(obj.as_value()).c_str()); printer->CloseElement(true); break; @@ -194,7 +190,7 @@ print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) { printer->CloseElement(true); break; default: - printer->OpenElement("i4", true); + printer->OpenElement("i8", true); printer->PushText(0); printer->CloseElement(true); } @@ -320,7 +316,7 @@ print_xmlrpc_fault(int faultCode, std::string faultString, tinyxml2::XMLPrinter* printer->PushText("faultCode"); printer->CloseElement(true); printer->OpenElement("value", true); - printer->OpenElement("i4", true); + printer->OpenElement("i8", true); printer->PushText(faultCode); printer->CloseElement(true); printer->CloseElement(true); diff --git a/test/rpc/xmlrpc_test.cc b/test/rpc/xmlrpc_test.cc index a5c2932d..51120a11 100644 --- a/test/rpc/xmlrpc_test.cc +++ b/test/rpc/xmlrpc_test.cc @@ -78,7 +78,7 @@ XmlrpcTest::test_invalid_utf8() { void XmlrpcTest::test_size_limit() { std::string input = "xmlrpc_reflect\xc3\x28"; - std::string expected = "faultCode-509faultStringContent size exceeds maximum XML-RPC limit"; + std::string expected = "faultCode-509faultStringContent size exceeds maximum XML-RPC limit"; std::string output; m_xmlrpc.set_size_limit(1); m_xmlrpc.process(input.c_str(), input.size(), [&output](const char* c, uint32_t l){ output.append(c, l); return true;}); diff --git a/test/rpc/xmlrpc_test_data.txt b/test/rpc/xmlrpc_test_data.txt index b1e0fc2e..c7adb105 100644 --- a/test/rpc/xmlrpc_test_data.txt +++ b/test/rpc/xmlrpc_test_data.txt @@ -18,9 +18,9 @@ xmlrpc_reflectZm9vYmFy foobar -# i4 ints -xmlrpc_reflect41 -41 +# i8 ints +xmlrpc_reflect41 +41 # i8 ints xmlrpc_reflect2247483647 @@ -43,37 +43,37 @@ # Simple struct -xmlrpc_reflectlowerBound18upperBound139 -lowerBound18upperBound139 +xmlrpc_reflectlowerBound18upperBound139 +lowerBound18upperBound139 # Invalid - missing method -no_such_method41 -faultCode-506faultStringmethod 'no_such_method' not defined +no_such_method41 +faultCode-506faultStringmethod 'no_such_method' not defined -# Invalid - i4 target -xmlrpc_reflect41 -faultCode-500faultStringinvalid parameters: target must be a string +# Invalid - i8 target +xmlrpc_reflect41 +faultCode-500faultStringinvalid parameters: target must be a string # Invalid - empty int tag -xmlrpc_reflect -faultCode-501faultStringunable to parse empty integer +xmlrpc_reflect +faultCode-501faultStringunable to parse empty integer # Invalid - empty int text -xmlrpc_reflect -faultCode-501faultStringunable to parse empty integer +xmlrpc_reflect +faultCode-501faultStringunable to parse empty integer # Invalid - broken XML -thodCall>test_a41faultCode-503faultStringError=XML_ERROR_PARSING_ELEMENT ErrorID=6 (0x6) Line number=1: XMLElement name=method +thodCall>test_a41faultCode-503faultStringError=XML_ERROR_PARSING_ELEMENT ErrorID=6 (0x6) Line number=1: XMLElement name=method -# Invalid - non-integer i4 -xmlrpc_reflectstring value -faultCode-501faultStringunable to parse integer value +# Invalid - non-integer i8 +xmlrpc_reflectstring value +faultCode-501faultStringunable to parse integer value -# Invalid - float i4 -xmlrpc_reflect3.14 -faultCode-501faultStringunable to parse integer value +# Invalid - float i8 +xmlrpc_reflect3.14 +faultCode-501faultStringunable to parse integer value # Invalid - non-boolean boolean xmlrpc_reflectstring value -faultCode-501faultStringunknown boolean value: string value +faultCode-501faultStringunknown boolean value: string value From c9a8a1e35ca5d93a212ebbe4dda82f4d4aec5414 Mon Sep 17 00:00:00 2001 From: simonc56 Date: Sat, 28 Dec 2024 14:08:19 +0100 Subject: [PATCH 04/11] tinyxml2: in xmlrpc an array must include values in a data element --- src/rpc/xmlrpc_tinyxml2.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rpc/xmlrpc_tinyxml2.cc b/src/rpc/xmlrpc_tinyxml2.cc index 1c9f669d..700cc93b 100644 --- a/src/rpc/xmlrpc_tinyxml2.cc +++ b/src/rpc/xmlrpc_tinyxml2.cc @@ -148,12 +148,14 @@ print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) { break; case torrent::Object::TYPE_LIST: printer->OpenElement("array", true); + printer->OpenElement("data", true); for (const auto& itr : obj.as_list()) { printer->OpenElement("value", true); print_object_xml(itr, printer); printer->CloseElement(true); } printer->CloseElement(true); + printer->CloseElement(true); break; case torrent::Object::TYPE_MAP: printer->OpenElement("struct", true); From 452397b8afab7f6218c8d649a612925cf7737eec Mon Sep 17 00:00:00 2001 From: simonc56 Date: Sat, 28 Dec 2024 15:11:43 +0100 Subject: [PATCH 05/11] tinyxml2: array with data for dict type --- src/rpc/xmlrpc_tinyxml2.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rpc/xmlrpc_tinyxml2.cc b/src/rpc/xmlrpc_tinyxml2.cc index 700cc93b..6aee9504 100644 --- a/src/rpc/xmlrpc_tinyxml2.cc +++ b/src/rpc/xmlrpc_tinyxml2.cc @@ -173,7 +173,7 @@ print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) { break; case torrent::Object::TYPE_DICT_KEY: printer->OpenElement("array", true); - + printer->OpenElement("data", true); printer->OpenElement("value", true); print_object_xml(obj.as_dict_key(), printer); printer->CloseElement(true); @@ -190,6 +190,7 @@ print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) { printer->CloseElement(true); } printer->CloseElement(true); + printer->CloseElement(true); break; default: printer->OpenElement("i8", true); From 4cc79590bed8e6a01aaab7e8259e1ec43d13fdf1 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Wed, 1 Jan 2025 12:59:06 +0000 Subject: [PATCH 06/11] Release 0.15.1. --- configure.ac | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 7df42666..329c0ad1 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ m4_pattern_allow([PKG_CHECK_EXISTS]) -AC_INIT([rtorrent],[0.15.0],[sundell.software@gmail.com]) +AC_INIT([rtorrent],[0.15.1],[sundell.software@gmail.com]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIRS([scripts]) @@ -9,7 +9,6 @@ AM_PROG_AR AC_DEFINE([API_VERSION], [10], [api version]) -AC_PROG_RANLIB AC_PROG_CXX AC_SYS_LARGEFILE LT_INIT @@ -51,7 +50,7 @@ fi PKG_CHECK_MODULES([LIBCURL], [libcurl],, [LIBCURL_CHECK_CONFIG]) PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"]) -PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.15.0]) +PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.15.1]) AC_LANG_PUSH(C++) TORRENT_WITH_XMLRPC_C From 06741a75789cb33139bb2c6744a7987f51d0fb25 Mon Sep 17 00:00:00 2001 From: kannibalox Date: Sat, 28 Dec 2024 11:24:04 -0500 Subject: [PATCH 07/11] Update XMLRPC unit tests to include for arrays --- test/rpc/xmlrpc_test.cc | 2 +- test/rpc/xmlrpc_test_data.txt | 31 ++++++++++++++++--------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/test/rpc/xmlrpc_test.cc b/test/rpc/xmlrpc_test.cc index 51120a11..7ca22657 100644 --- a/test/rpc/xmlrpc_test.cc +++ b/test/rpc/xmlrpc_test.cc @@ -69,7 +69,7 @@ XmlrpcTest::test_invalid_utf8() { // valid UTF-8, but doesn't check strings, and Object strings are // just a series of bytes so it reflects just fine. std::string input = "xmlrpc_reflect\xc3\x28"; - std::string expected = "\xc3\x28"; + std::string expected = "\xc3\x28"; std::string output; m_xmlrpc.process(input.c_str(), input.size(), [&output](const char* c, uint32_t l){ output.append(c, l); return true;}); CPPUNIT_ASSERT_EQUAL(expected, output); diff --git a/test/rpc/xmlrpc_test_data.txt b/test/rpc/xmlrpc_test_data.txt index c7adb105..bab15b23 100644 --- a/test/rpc/xmlrpc_test_data.txt +++ b/test/rpc/xmlrpc_test_data.txt @@ -1,50 +1,51 @@ # Basic call xmlrpc_reflect - + # Basic call without params xmlrpc_reflect - + # UTF-8 string xmlrpc_reflectчао -чао +чао # emoji string xmlrpc_reflect😊 -😊 +😊 # base64 data (which gets returned as a string) xmlrpc_reflectZm9vYmFy -foobar +foobar # i8 ints xmlrpc_reflect41 -41 +41 # i8 ints xmlrpc_reflect2247483647 -2247483647 +2247483647 # negative i8 ints xmlrpc_reflect-2347483647 --2347483647 - -# Empty array -xmlrpc_reflect2247483647 -2247483647 +-2347483647 # Simple array +xmlrpc_reflect2247483647 +2247483647 + +# Empty array xmlrpc_reflect - + # Empty struct xmlrpc_reflect - + # Simple struct + xmlrpc_reflectlowerBound18upperBound139 -lowerBound18upperBound139 +lowerBound18upperBound139 # Invalid - missing method no_such_method41 From 525b038dbb1cfd2a47476ae847628625427087fa Mon Sep 17 00:00:00 2001 From: rakshasa Date: Thu, 2 Jan 2025 20:58:48 +0000 Subject: [PATCH 08/11] Inline test data instead of using separate text file. --- test/rpc/xmlrpc_test.cc | 115 +++++++++++++++++++++++++--------- test/rpc/xmlrpc_test_data.txt | 80 ----------------------- 2 files changed, 86 insertions(+), 109 deletions(-) delete mode 100644 test/rpc/xmlrpc_test_data.txt diff --git a/test/rpc/xmlrpc_test.cc b/test/rpc/xmlrpc_test.cc index 7ca22657..be0dee19 100644 --- a/test/rpc/xmlrpc_test.cc +++ b/test/rpc/xmlrpc_test.cc @@ -15,6 +15,89 @@ torrent::Object xmlrpc_cmd_test_reflect(rpc::target_type t, const torrent::Objec void initialize_command_dynamic(); #if defined(HAVE_XMLRPC_TINYXML2) && !defined(HAVE_XMLRPC_C) + +std::vector> basic_requests = { + std::make_tuple("Basic call", + "xmlrpc_reflect", + ""), + + std::make_tuple("Basic call without params", + "xmlrpc_reflect", + ""), + + std::make_tuple("UTF-8 string", + "xmlrpc_reflectчао", + "чао"), + + std::make_tuple("emoji string", + "xmlrpc_reflect😊", + "😊"), + + std::make_tuple("base64 data (which gets returned as a string)", + "xmlrpc_reflectZm9vYmFy", + "foobar"), + + std::make_tuple("i8 ints", + "xmlrpc_reflect41", + "41"), + + std::make_tuple("i8 ints", + "xmlrpc_reflect2247483647", + "2247483647"), + + std::make_tuple("negative i8 ints", + "xmlrpc_reflect-2347483647", + "-2347483647"), + + std::make_tuple("Simple array", + "xmlrpc_reflect2247483647", + "2247483647"), + + std::make_tuple("Empty array", + "xmlrpc_reflect", + ""), + + std::make_tuple("Empty struct", + "xmlrpc_reflect", + ""), + + std::make_tuple("Simple struct", + "xmlrpc_reflectlowerBound18upperBound139", + "lowerBound18upperBound139"), + + std::make_tuple("Invalid - missing method", + "no_such_method41", + "faultCode-506faultStringmethod 'no_such_method' not defined"), + + std::make_tuple("Invalid - i8 target", + "xmlrpc_reflect41", + "faultCode-500faultStringinvalid parameters: target must be a string"), + + std::make_tuple("Invalid - empty int tag", + "xmlrpc_reflect", + "faultCode-501faultStringunable to parse empty integer"), + + std::make_tuple("Invalid - empty int text", + "xmlrpc_reflect", + "faultCode-501faultStringunable to parse empty integer"), + + std::make_tuple("Invalid - broken XML", + "thodCall>test_a41faultCode-503faultStringError=XML_ERROR_PARSING_ELEMENT ErrorID=6 (0x6) Line number=1: XMLElement name=method"), + + std::make_tuple("Invalid - non-integer i8", + "xmlrpc_reflectstring value", + "faultCode-501faultStringunable to parse integer value"), + + std::make_tuple("Invalid - float i8", + "xmlrpc_reflect3.14", + "faultCode-501faultStringunable to parse integer value"), + + std::make_tuple("Invalid - non-boolean boolean", + "xmlrpc_reflectstring value", + "faultCode-501faultStringunknown boolean value: string value") +}; + void XmlrpcTest::setUp() { m_commandItr = m_commands; @@ -30,36 +113,10 @@ XmlrpcTest::setUp() { void XmlrpcTest::test_basics() { - std::ifstream file; file.open("rpc/xmlrpc_test_data.txt"); - CPPUNIT_ASSERT(file.good()); - std::vector titles; - std::vector inputs; - std::vector outputs; - std::string line; - int index = 0; - // Read file into inputs/outputs - while (std::getline(file, line)) { - if (line.size() == 0) { - continue; - } - if (line[0] == '#') { - titles.push_back(line); - continue; - } - if (index % 2) { - outputs.push_back(line); - } else { - inputs.push_back(line); - } - index++; - } - - // Sanity check the above parser - CPPUNIT_ASSERT_MESSAGE("Could not parse test data", inputs.size() > 0 && inputs.size() == outputs.size() && inputs.size() == titles.size()); - for (int i = 0; i < inputs.size(); i++) { + for (auto& test : basic_requests) { std::string output; - m_xmlrpc.process(inputs[i].c_str(), inputs[i].size(), [&output](const char* c, uint32_t l){ output.append(c, l); return true;}); - CPPUNIT_ASSERT_EQUAL_MESSAGE(titles[i], std::string(outputs[i]), output); + m_xmlrpc.process(std::get<1>(test).c_str(), std::get<1>(test).size(), [&output](const char* c, uint32_t l){ output.append(c, l); return true;}); + CPPUNIT_ASSERT_EQUAL_MESSAGE(std::get<0>(test), std::get<2>(test), output); } } diff --git a/test/rpc/xmlrpc_test_data.txt b/test/rpc/xmlrpc_test_data.txt deleted file mode 100644 index bab15b23..00000000 --- a/test/rpc/xmlrpc_test_data.txt +++ /dev/null @@ -1,80 +0,0 @@ -# Basic call -xmlrpc_reflect - - -# Basic call without params -xmlrpc_reflect - - -# UTF-8 string -xmlrpc_reflectчао -чао - -# emoji string -xmlrpc_reflect😊 -😊 - -# base64 data (which gets returned as a string) -xmlrpc_reflectZm9vYmFy -foobar - -# i8 ints -xmlrpc_reflect41 -41 - -# i8 ints -xmlrpc_reflect2247483647 -2247483647 - -# negative i8 ints -xmlrpc_reflect-2347483647 --2347483647 - -# Simple array -xmlrpc_reflect2247483647 -2247483647 - -# Empty array -xmlrpc_reflect - - -# Empty struct -xmlrpc_reflect - - -# Simple struct - -xmlrpc_reflectlowerBound18upperBound139 -lowerBound18upperBound139 - -# Invalid - missing method -no_such_method41 -faultCode-506faultStringmethod 'no_such_method' not defined - -# Invalid - i8 target -xmlrpc_reflect41 -faultCode-500faultStringinvalid parameters: target must be a string - -# Invalid - empty int tag -xmlrpc_reflect -faultCode-501faultStringunable to parse empty integer - -# Invalid - empty int text -xmlrpc_reflect -faultCode-501faultStringunable to parse empty integer - -# Invalid - broken XML -thodCall>test_a41faultCode-503faultStringError=XML_ERROR_PARSING_ELEMENT ErrorID=6 (0x6) Line number=1: XMLElement name=method - -# Invalid - non-integer i8 -xmlrpc_reflectstring value -faultCode-501faultStringunable to parse integer value - -# Invalid - float i8 -xmlrpc_reflect3.14 -faultCode-501faultStringunable to parse integer value - -# Invalid - non-boolean boolean -xmlrpc_reflectstring value -faultCode-501faultStringunknown boolean value: string value From 7534b028150208e8228f1ac202c2e6db18fb25e3 Mon Sep 17 00:00:00 2001 From: kannibalox Date: Fri, 13 Dec 2024 16:40:00 -0500 Subject: [PATCH 09/11] Add page up/down and home/end bindings to download list --- src/command_ui.cc | 2 + src/core/view.cc | 69 ++++++-------- src/core/view.h | 43 +-------- src/display/window_download_list.cc | 46 +++++---- src/display/window_download_list.h | 3 + src/ui/element_download_list.cc | 139 ++++++++++++++-------------- src/ui/element_download_list.h | 80 +++++----------- 7 files changed, 163 insertions(+), 219 deletions(-) diff --git a/src/command_ui.cc b/src/command_ui.cc index 99040d08..c93a3366 100644 --- a/src/command_ui.cc +++ b/src/command_ui.cc @@ -816,6 +816,8 @@ initialize_command_ui() { CMD2_VAR_VALUE ("ui.throttle.global.step.medium", 50); CMD2_VAR_VALUE ("ui.throttle.global.step.large", 500); + CMD2_VAR_VALUE ("ui.focus.page_size", 0); + CMD2_ANY_LIST ("ui.status.throttle.up.set", std::bind(&cmd_status_throttle_names, true, std::placeholders::_2)); CMD2_ANY_LIST ("ui.status.throttle.down.set", std::bind(&cmd_status_throttle_names, false, std::placeholders::_2)); diff --git a/src/core/view.cc b/src/core/view.cc index e975ed85..7d28af65 100644 --- a/src/core/view.cc +++ b/src/core/view.cc @@ -1,39 +1,3 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, 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 @@ -236,20 +200,45 @@ View::set_not_visible(Download* download) { } void -View::next_focus() { +View::next_focus(unsigned int i) { if (empty()) return; - m_focus = (m_focus + 1) % (size() + 1); + // If at the boundary, roll over + if (m_focus == size() - 1) { + m_focus = size(); + emit_changed(); + return; + } + + // Move forward, stop at the boundary + if (m_focus == size()) // Needs special handling to ensure it's not off by one + m_focus = i - 1; + else + m_focus += i; + if (m_focus > size() - 1) + m_focus = size() - 1; + emit_changed(); } void -View::prev_focus() { +View::prev_focus(unsigned int i) { if (empty()) return; - m_focus = (m_focus - 1 + size() + 1) % (size() + 1); + // If at the boundary, roll over + if (m_focus == size()) { + m_focus = size() - 1; + emit_changed(); + return; + } + + // Move backward, stop at the boundary + m_focus -= i; + if (m_focus < 0 || m_focus > size()) + m_focus = size(); + emit_changed(); } diff --git a/src/core/view.h b/src/core/view.h index 837acb6b..1fe6f2be 100644 --- a/src/core/view.h +++ b/src/core/view.h @@ -1,39 +1,3 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, Jari Sundell -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations -// including the two. -// -// You must obey the GNU General Public License in all respects for -// all of the code used other than OpenSSL. If you modify file(s) -// with this exception, you may extend this exception to your version -// of the file(s), but you are not obligated to do so. If you do not -// wish to do so, delete this exception statement from your version. -// If you delete this exception statement from all source files in the -// program, then also delete it here. -// -// Contact: Jari Sundell -// -// Skomakerveien 33 -// 3185 Skoppum, NORWAY - // Provides a filtered and sorted list of downloads that can be // updated auto-magically. // @@ -114,8 +78,11 @@ public: void set_visible(Download* download); void set_not_visible(Download* download); - void next_focus(); - void prev_focus(); + void next_focus(unsigned int i); + void prev_focus(unsigned int i); + + void next_focus() { next_focus(1); } + void prev_focus() { prev_focus(1); } void sort(); diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc index 428b786d..981829a6 100644 --- a/src/display/window_download_list.cc +++ b/src/display/window_download_list.cc @@ -15,14 +15,14 @@ namespace display { WindowDownloadList::WindowDownloadList() : - Window(new Canvas, 0, 120, 1, extent_full, extent_full), - m_view(NULL) { + Window(new Canvas, 0, 120, 1, extent_full, extent_full), + m_view(NULL) { } WindowDownloadList::~WindowDownloadList() { if (m_view != NULL) m_view->signal_changed().erase(m_changed_itr); - + m_view = NULL; } @@ -52,6 +52,27 @@ WindowDownloadList::get_attr_color(core::View::iterator selected) { return std::make_pair(m_canvas->attr_map().at(title_color) | focus_attr, title_color); } +int +WindowDownloadList::page_size(const std::string layout_name) { + // Calculate the page size for torrents. This is a public method + // because it's also used to determine the default size for page + // up/down actions. + int layout_height; + if (layout_name == "full") { + layout_height = 3; + } else if (layout_name == "compact") { + layout_height = 1; + } else { + return 0; + } + return m_canvas->height() / layout_height; +} + +int +WindowDownloadList::page_size() { + return page_size(rpc::call_command_string("ui.torrent_list.layout")); +} + void WindowDownloadList::redraw() { if (m_canvas->daemon()) @@ -64,7 +85,7 @@ WindowDownloadList::redraw() { if (m_view == NULL) return; - m_canvas->print("%s", ("[View: " + m_view->name() + (m_view->get_filter_temp().is_empty() ? "" : " (filtered)") + "]").c_str()); + m_canvas->print(0, 0, "%s", ("[View: " + m_view->name() + (m_view->get_filter_temp().is_empty() ? "" : " (filtered)") + "]").c_str()); if (m_view->empty_visible() || m_canvas->width() < 5 || m_canvas->height() < 2) return; @@ -83,19 +104,12 @@ WindowDownloadList::redraw() { int layout_height; const std::string layout_name = rpc::call_command_string("ui.torrent_list.layout"); - if (layout_name == "full") { - layout_height = 3; - } else if (layout_name == "compact") { - layout_height = 1; - } else { - m_canvas->print(0, 0, "INVALID ui.torrent_list.layout '%s'", layout_name.c_str()); - return; - } + typedef std::pair Range; - ViewRange range = rak::advance_bidirectional(m_view->begin_visible(), - m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(), - m_view->end_visible(), - m_canvas->height() / layout_height); + Range range = rak::advance_bidirectional(m_view->begin_visible(), + m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(), + m_view->end_visible(), + page_size(layout_name)); // Make sure we properly fill out the last lines so it looks like // there are more torrents, yet don't hide it if we got the last one diff --git a/src/display/window_download_list.h b/src/display/window_download_list.h index 897d7c25..8133c4a0 100644 --- a/src/display/window_download_list.h +++ b/src/display/window_download_list.h @@ -20,6 +20,9 @@ public: void set_view(core::View* l); + int page_size(const std::string layout_name); + int page_size(); + private: core::View* m_view; diff --git a/src/ui/element_download_list.cc b/src/ui/element_download_list.cc index 90a769ab..5c92984c 100644 --- a/src/ui/element_download_list.cc +++ b/src/ui/element_download_list.cc @@ -1,39 +1,3 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, 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 @@ -55,8 +19,8 @@ namespace ui { ElementDownloadList::ElementDownloadList() : - m_window(NULL), - m_view(NULL) { + m_window(NULL), + m_view(NULL) { receive_change_view("main"); @@ -67,40 +31,42 @@ ElementDownloadList::ElementDownloadList() : m_bindings['\x04'] = std::bind(&ElementDownloadList::receive_command, this, "branch=d.state=,d.stop=,d.erase="); m_bindings['\x0B'] = std::bind(&ElementDownloadList::receive_command, this, "d.ignore_commands.set=1; d.stop=; d.close="); m_bindings['\x12'] = std::bind(&ElementDownloadList::receive_command, this, "d.complete.set=0; d.check_hash="); - m_bindings['\x05'] = std::bind(&ElementDownloadList::receive_command, this, - "f.multicall=,f.set_create_queued=0,f.set_resize_queued=0; print=\"Queued create/resize of files in torrent.\""); + m_bindings['\x05'] = std::bind(&ElementDownloadList::receive_command, this, "f.multicall=,f.set_create_queued=0,f.set_resize_queued=0; print=\"Queued create/resize of files in torrent.\""); - m_bindings['+'] = std::bind(&ElementDownloadList::receive_next_priority, this); - m_bindings['-'] = std::bind(&ElementDownloadList::receive_prev_priority, this); - m_bindings['T'-'@']= std::bind(&ElementDownloadList::receive_cycle_throttle, this); - m_bindings['I'] = std::bind(&ElementDownloadList::receive_command, this, - "branch=d.ignore_commands=," - "{d.ignore_commands.set=0, print=\"Torrent set to heed commands.\"}," - "{d.ignore_commands.set=1, print=\"Torrent set to ignore commands.\"}"); - m_bindings['B'-'@']= std::bind(&ElementDownloadList::receive_command, this, - "branch=d.is_active=," - "{print=\"Cannot enable initial seeding on an active download.\"}," - "{d.connection_seed.set=initial_seed, print=\"Enabled initial seeding for the selected download.\"}"); + m_bindings['+'] = std::bind(&ElementDownloadList::receive_next_priority, this); + m_bindings['-'] = std::bind(&ElementDownloadList::receive_prev_priority, this); + m_bindings['T' - '@'] = std::bind(&ElementDownloadList::receive_cycle_throttle, this); + m_bindings['I'] = std::bind(&ElementDownloadList::receive_command, this, "branch=d.ignore_commands=," + "{d.ignore_commands.set=0, print=\"Torrent set to heed commands.\"}," + "{d.ignore_commands.set=1, print=\"Torrent set to ignore commands.\"}"); + m_bindings['B' - '@'] = std::bind(&ElementDownloadList::receive_command, this, "branch=d.is_active=," + "{print=\"Cannot enable initial seeding on an active download.\"}," + "{d.connection_seed.set=initial_seed, print=\"Enabled initial seeding for the selected download.\"}"); - m_bindings['U'] = std::bind(&ElementDownloadList::receive_command, this, - "d.delete_tied=; print=\"Cleared tied to file association for the selected download.\""); + m_bindings['U'] = std::bind(&ElementDownloadList::receive_command, this, "d.delete_tied=; print=\"Cleared tied to file association for the selected download.\""); // These should also be commands. - m_bindings['1'] = std::bind(&ElementDownloadList::receive_change_view, this, "main"); - m_bindings['2'] = std::bind(&ElementDownloadList::receive_change_view, this, "name"); - m_bindings['3'] = std::bind(&ElementDownloadList::receive_change_view, this, "started"); - m_bindings['4'] = std::bind(&ElementDownloadList::receive_change_view, this, "stopped"); - m_bindings['5'] = std::bind(&ElementDownloadList::receive_change_view, this, "complete"); - m_bindings['6'] = std::bind(&ElementDownloadList::receive_change_view, this, "incomplete"); - m_bindings['7'] = std::bind(&ElementDownloadList::receive_change_view, this, "hashing"); - m_bindings['8'] = std::bind(&ElementDownloadList::receive_change_view, this, "seeding"); - m_bindings['9'] = std::bind(&ElementDownloadList::receive_change_view, this, "leeching"); - m_bindings['0'] = std::bind(&ElementDownloadList::receive_change_view, this, "active"); + m_bindings['1'] = std::bind(&ElementDownloadList::receive_change_view, this, "main"); + m_bindings['2'] = std::bind(&ElementDownloadList::receive_change_view, this, "name"); + m_bindings['3'] = std::bind(&ElementDownloadList::receive_change_view, this, "started"); + m_bindings['4'] = std::bind(&ElementDownloadList::receive_change_view, this, "stopped"); + m_bindings['5'] = std::bind(&ElementDownloadList::receive_change_view, this, "complete"); + m_bindings['6'] = std::bind(&ElementDownloadList::receive_change_view, this, "incomplete"); + m_bindings['7'] = std::bind(&ElementDownloadList::receive_change_view, this, "hashing"); + m_bindings['8'] = std::bind(&ElementDownloadList::receive_change_view, this, "seeding"); + m_bindings['9'] = std::bind(&ElementDownloadList::receive_change_view, this, "leeching"); + m_bindings['0'] = std::bind(&ElementDownloadList::receive_change_view, this, "active"); - m_bindings[KEY_UP] = m_bindings['P' - '@'] = std::bind(&ElementDownloadList::receive_prev, this); + m_bindings[KEY_UP] = m_bindings['P' - '@'] = std::bind(&ElementDownloadList::receive_prev, this); m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = std::bind(&ElementDownloadList::receive_next, this); - m_bindings['L'] = std::bind(&ElementDownloadList::toggle_layout, this); + m_bindings[KEY_PPAGE] = m_bindings['U' - '@'] = [this] { receive_pageprev(); }; + m_bindings[KEY_NPAGE] = m_bindings['H' - '@'] = [this] { receive_pagenext(); }; + + m_bindings[KEY_HOME] = m_bindings['A' - '@'] = [this] { receive_home(); }; + m_bindings[KEY_END] = m_bindings['E' - '@'] = [this] { receive_end(); }; + + m_bindings['L'] = std::bind(&ElementDownloadList::toggle_layout, this); } void @@ -172,6 +138,41 @@ ElementDownloadList::receive_prev() { m_view->set_last_changed(); } +int +ElementDownloadList::page_size() { + int rpc_page_size = rpc::call_command_value("ui.focus.page_size"); + if (rpc_page_size > 0) + return rpc_page_size; + int auto_page_size = m_window->page_size() - 1; + if (auto_page_size > 0) + return auto_page_size; + return 50; +} + +void +ElementDownloadList::receive_pagenext() { + m_view->next_focus(page_size()); + m_view->set_last_changed(); +} + +void +ElementDownloadList::receive_pageprev() { + m_view->prev_focus(page_size()); + m_view->set_last_changed(); +} + +void +ElementDownloadList::receive_home() { + m_view->set_focus(m_view->begin_visible()); + m_view->set_last_changed(); +} + +void +ElementDownloadList::receive_end() { + m_view->set_focus(m_view->end_visible() - 1); + m_view->set_last_changed(); +} + void ElementDownloadList::receive_next_priority() { if (m_view->focus() == m_view->end_visible()) @@ -222,12 +223,10 @@ ElementDownloadList::receive_change_view(const std::string& name) { std::string old_name = view() ? view()->name() : ""; if (!old_name.empty()) - rpc::commands.call_catch("event.view.hide", rpc::make_target(), name, - "View hide event action failed: "); + rpc::commands.call_catch("event.view.hide", rpc::make_target(), name, "View hide event action failed: "); set_view(*itr); if (!old_name.empty()) - rpc::commands.call_catch("event.view.show", rpc::make_target(), old_name, - "View show event action failed: "); + rpc::commands.call_catch("event.view.show", rpc::make_target(), old_name, "View show event action failed: "); } void @@ -240,4 +239,4 @@ ElementDownloadList::toggle_layout() { rpc::call_command("ui.torrent_list.layout.set", "full"); } } -} +} // namespace ui diff --git a/src/ui/element_download_list.h b/src/ui/element_download_list.h index 5bf4f08f..67585dbc 100644 --- a/src/ui/element_download_list.h +++ b/src/ui/element_download_list.h @@ -1,43 +1,6 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, 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_DOWNLOAD_LIST_H #define RTORRENT_UI_ELEMENT_DOWNLOAD_LIST_H -#include "core/download_list.h" #include "display/window_download_list.h" #include "element_base.h" @@ -45,7 +8,7 @@ class Control; namespace core { - class View; +class View; } namespace ui { @@ -56,34 +19,41 @@ public: ElementDownloadList(); - void activate(display::Frame* frame, bool focus = true); - void disable(); + void activate(display::Frame* frame, bool focus = true); + void disable(); - core::View* view() { return m_view; } - void set_view(core::View* l); + core::View* view() { return m_view; } + void set_view(core::View* l); - void receive_command(const char* cmd); + void receive_command(const char* cmd); - void receive_next(); - void receive_prev(); + void receive_next(); + void receive_prev(); - void receive_stop_download(); - void receive_close_download(); + int page_size(); + void receive_pagenext(); + void receive_pageprev(); - void receive_next_priority(); - void receive_prev_priority(); + void receive_home(); + void receive_end(); - void receive_cycle_throttle(); + void receive_stop_download(); + void receive_close_download(); - void receive_change_view(const std::string& name); + void receive_next_priority(); + void receive_prev_priority(); - void toggle_layout(); + void receive_cycle_throttle(); + + void receive_change_view(const std::string& name); + + void toggle_layout(); private: - WDownloadList* m_window; - core::View* m_view; + WDownloadList* m_window; + core::View* m_view; }; -} +} // namespace ui #endif From 6c5a6a3bb3664af211284b5ab2ec18ece6de7f7d Mon Sep 17 00:00:00 2001 From: kannibalox Date: Thu, 26 Dec 2024 17:44:01 -0500 Subject: [PATCH 10/11] Manually include config.h for clang-tidy Specifically this fixes errors related to undefined macros in headers, since those aren't recorded in compile_commands.json --- .github/workflows/PR.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/PR.yml b/.github/workflows/PR.yml index 67382f29..118ff4d6 100644 --- a/.github/workflows/PR.yml +++ b/.github/workflows/PR.yml @@ -54,7 +54,7 @@ jobs: mkdir clang-tidy-result - name: Analyze run: | - git diff -U0 "$(git merge-base HEAD "upstream/${{ github.event.pull_request.base.ref }}")" | clang-tidy-diff -p1 -path build -export-fixes clang-tidy-result/fixes.yml + git diff -U0 "$(git merge-base HEAD "upstream/${{ github.event.pull_request.base.ref }}")" | clang-tidy-diff -p1 -path build -export-fixes clang-tidy-result/fixes.yml "-extra-arg=-include/${PWD}/config.h" - name: Save PR metadata run: | echo "${{ github.event.number }}" > clang-tidy-result/pr-id.txt From b193fedc6571e222da94bd0d90140cd6ff3e0346 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Thu, 9 Jan 2025 15:51:04 +0000 Subject: [PATCH 11/11] Moved inlined functions out of class declaration. --- src/display/canvas.h | 356 +++++++++++++++++++++++++------------------ 1 file changed, 208 insertions(+), 148 deletions(-) diff --git a/src/display/canvas.h b/src/display/canvas.h index 51c73f3f..b5d7656a 100644 --- a/src/display/canvas.h +++ b/src/display/canvas.h @@ -16,105 +16,26 @@ public: typedef std::unordered_map attributes_map; Canvas(int x = 0, int y = 0, int width = 0, int height = 0); - ~Canvas() { - if (!m_isDaemon) { - delwin(m_window); - } - } + ~Canvas(); - void refresh() { - if (!m_isDaemon) { - wnoutrefresh(m_window); - } - } - static void refresh_std() { - if (!m_isDaemon) { - wnoutrefresh(stdscr); - } - } - void redraw() { - if (!m_isDaemon) { - redrawwin(m_window); - } - } - static void redraw_std() { - if (!m_isDaemon) { - redrawwin(stdscr); - } - } + void refresh(); + void redraw(); + void resize(int w, int h); + void resize(int x, int y, int w, int h); - void resize(int w, int h) { - if (!m_isDaemon) { - wresize(m_window, h, w); - } - } - void resize(int x, int y, int w, int h); + static void refresh_std(); + static void redraw_std(); + static void resize_term(int x, int y); + static void resize_term(std::pair dim); - static void resize_term(int x, int y) { - if (!m_isDaemon) { - resizeterm(y, x); - } - } - static void resize_term(std::pair dim) { - if (!m_isDaemon) { - resizeterm(dim.second, dim.first); - } - } + unsigned int get_x(); + unsigned int get_y(); + unsigned int width(); + unsigned int height(); - unsigned int get_x() { - int x, __UNUSED y; - if (!m_isDaemon) { - getyx(m_window, y, x); - } else { - x = 1; - } - return x; - } - unsigned int get_y() { - int x, y; - if (!m_isDaemon) { - getyx(m_window, y, x); - } else { - y = 1; - } - return y; - } - - unsigned int width() { - int x, __UNUSED y; - if (!m_isDaemon) { - getmaxyx(m_window, y, x); - } else { - x = 80; - } - return x; - } - unsigned int height() { - int x, y; - if (!m_isDaemon) { - getmaxyx(m_window, y, x); - } else { - y = 24; - } - return y; - } - - void move(unsigned int x, unsigned int y) { - if (!m_isDaemon) { - wmove(m_window, y, x); - } - } - - void erase() { - if (!m_isDaemon) { - werase(m_window); - } - } - static void erase_std() { - if (!m_isDaemon) { - werase(stdscr); - } - } + void move(unsigned int x, unsigned int y); + void erase(); + static void erase_std(); // The format string is non-const, but that will not be a problem // since the string shall always be a C string choosen at @@ -122,72 +43,27 @@ public: void print(const char* str, ...); void print(unsigned int x, unsigned int y, const char* str, ...); - void print_attributes(unsigned int x, unsigned int y, const char* first, const char* last, const attributes_list* attributes); + void print_char(const chtype ch); + void print_char(unsigned int x, unsigned int y, const chtype ch); - void print_char(const chtype ch) { - if (!m_isDaemon) { - waddch(m_window, ch); - } - } - void print_char(unsigned int x, unsigned int y, const chtype ch) { - if (!m_isDaemon) { - mvwaddch(m_window, y, x, ch); - } - } - - void set_attr(unsigned int x, unsigned int y, unsigned int n, int attr, int color) { - if (!m_isDaemon) { - mvwchgat(m_window, y, x, n, attr, color, NULL); - } - } - - void set_attr(unsigned int x, unsigned int y, unsigned int n, ColorKind k) { - if (!m_isDaemon) { - mvwchgat(m_window, y, x, n, m_attr_map[k], k, NULL); - } - } - - void set_default_attributes(int attr) { - if (!m_isDaemon) { - (void)wattrset(m_window, attr); - } - } + void set_attr(unsigned int x, unsigned int y, unsigned int n, int attr, int color); + void set_attr(unsigned int x, unsigned int y, unsigned int n, ColorKind k); + void set_default_attributes(int attr); // Initialize stdscr. static void initialize(); static void build_colors(); static void cleanup(); - static int get_screen_width() { - int x, __UNUSED y; - if (!m_isDaemon) { - getmaxyx(stdscr, y, x); - } else { - x = 80; - } - return x; - } - static int get_screen_height() { - int x, y; - if (!m_isDaemon) { - getmaxyx(stdscr, y, x); - } else { - y = 24; - } - return y; - } + static int get_screen_width(); + static int get_screen_height(); static std::pair term_size(); - static void do_update() { - if (!m_isDaemon) { - doupdate(); - } - } + static void do_update(); static bool daemon() { return m_isDaemon; } - static const attributes_map& attr_map() { return m_attr_map; } private: @@ -203,6 +79,126 @@ private: WINDOW* m_window; }; +inline Canvas::~Canvas() { + if (!m_isDaemon) { + delwin(m_window); + } +} + +inline void +Canvas::refresh() { + if (!m_isDaemon) { + wnoutrefresh(m_window); + } +} + +inline void +Canvas::refresh_std() { + if (!m_isDaemon) { + wnoutrefresh(stdscr); + } +} + +inline void +Canvas::redraw() { + if (!m_isDaemon) { + redrawwin(m_window); + } +} + +inline void +Canvas::redraw_std() { + if (!m_isDaemon) { + redrawwin(stdscr); + } +} + +inline void +Canvas::resize(int w, int h) { + if (!m_isDaemon) { + wresize(m_window, h, w); + } +} + +inline void +Canvas::resize_term(int x, int y) { + if (!m_isDaemon) { + resizeterm(y, x); + } +} + +inline void +Canvas::resize_term(std::pair dim) { + if (!m_isDaemon) { + resizeterm(dim.second, dim.first); + } +} + +inline unsigned int +Canvas::get_x() { + int x, __UNUSED y; + if (!m_isDaemon) { + getyx(m_window, y, x); + } else { + x = 1; + } + return x; +} + +inline unsigned int +Canvas::get_y() { + int x, y; + if (!m_isDaemon) { + getyx(m_window, y, x); + } else { + y = 1; + } + return y; +} + +inline unsigned int +Canvas::width() { + int x, __UNUSED y; + if (!m_isDaemon) { + getmaxyx(m_window, y, x); + } else { + x = 80; + } + return x; +} + +inline unsigned int +Canvas::height() { + int x, y; + if (!m_isDaemon) { + getmaxyx(m_window, y, x); + } else { + y = 24; + } + return y; +} + +inline void +Canvas::move(unsigned int x, unsigned int y) { + if (!m_isDaemon) { + wmove(m_window, y, x); + } +} + +inline void +Canvas::erase() { + if (!m_isDaemon) { + werase(m_window); + } +} + +inline void +Canvas::erase_std() { + if (!m_isDaemon) { + werase(stdscr); + } +} + inline void Canvas::print(const char* str, ...) { va_list arglist; @@ -226,6 +222,70 @@ Canvas::print(unsigned int x, unsigned int y, const char* str, ...) { } } +inline void +Canvas::print_char(const chtype ch) { + if (!m_isDaemon) { + waddch(m_window, ch); + } +} + +inline void +Canvas::print_char(unsigned int x, unsigned int y, const chtype ch) { + if (!m_isDaemon) { + mvwaddch(m_window, y, x, ch); + } +} + +inline void +Canvas::set_attr(unsigned int x, unsigned int y, unsigned int n, int attr, int color) { + if (!m_isDaemon) { + mvwchgat(m_window, y, x, n, attr, color, NULL); + } +} + +inline void +Canvas::set_attr(unsigned int x, unsigned int y, unsigned int n, ColorKind k) { + if (!m_isDaemon) { + mvwchgat(m_window, y, x, n, m_attr_map[k], k, NULL); + } +} + +inline void +Canvas::set_default_attributes(int attr) { + if (!m_isDaemon) { + (void)wattrset(m_window, attr); + } +} + +inline int +Canvas::get_screen_width() { + int x, __UNUSED y; + if (!m_isDaemon) { + getmaxyx(stdscr, y, x); + } else { + x = 80; + } + return x; +} + +inline int +Canvas::get_screen_height() { + int x, y; + if (!m_isDaemon) { + getmaxyx(stdscr, y, x); + } else { + y = 24; + } + return y; +} + +inline void +Canvas::do_update() { + if (!m_isDaemon) { + doupdate(); + } +} + } // namespace display #endif