summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorxsmile2017-04-10 18:13:21 +0200
committerxsmile2017-04-10 18:13:21 +0200
commitf5c0bd9fd68574b4fde920898ca16f8ee4514b7a (patch)
treed3a00ba55210fc2a4858cc3a2c9afee43fdbabb9
parent1e361329df7798275282b491a58b38495d6e8679 (diff)
downloadaur-f5c0bd9fd68574b4fde920898ca16f8ee4514b7a.tar.gz
update
-rw-r--r--.SRCINFO10
-rw-r--r--PKGBUILD8
-rw-r--r--command_pyroscope.cc73
-rw-r--r--ps-event-view_all.patch21
-rw-r--r--ui_pyroscope.cc1269
5 files changed, 780 insertions, 601 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 52618110df17..6de11306fa3e 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,8 +1,8 @@
# Generated by mksrcinfo v8
-# Mon Apr 3 10:47:15 UTC 2017
+# Mon Apr 10 16:13:02 UTC 2017
pkgbase = rtorrent-ps
pkgdesc = Extended rTorrent distribution with UI enhancements, colorization, and some added features
- pkgver = 1.0.r40.ge768454
+ pkgver = 1.0.r55.g1f648ee
pkgrel = 1
url = https://github.com/pyroscope/rtorrent-ps
arch = any
@@ -23,6 +23,7 @@ pkgbase = rtorrent-ps
conflicts = rtorrent-vi-color
source = https://github.com/rakshasa/rtorrent/archive/0.9.6.tar.gz
source = command_pyroscope.cc
+ source = ps-event-view_all.patch
source = ps-fix-double-slash-319_all.patch
source = ps-fix-sort-started-stopped-views_all.patch
source = ps-handle-sighup-578_all.patch
@@ -37,7 +38,8 @@ pkgbase = rtorrent-ps
source = ui_pyroscope.h
source = ui_pyroscope.patch
md5sums = b8b4009f95f8543244ae1d23b1810d7c
- md5sums = 4fd2e4373b8dab7e37dd8684fe04a555
+ md5sums = 4ad3ae94e76106add9e228ac768c9881
+ md5sums = 56701bca42cc9b309637bf3f918ede12
md5sums = 22fae392c6e281dc438b39a5019e7e1b
md5sums = 3fd739c0d5a9442f0cdec9ed5a720eaa
md5sums = 2137e16f8b881170fb92fb7a6c276193
@@ -48,7 +50,7 @@ pkgbase = rtorrent-ps
md5sums = 7a88f8ab5d41242fdf1428de0e2ca182
md5sums = 26faff00b306b6ef276a7d9e6d964994
md5sums = bd04a0699b80c8042e1cf63a7e0e4222
- md5sums = cd39a495ee93d9c77039fa74a9e9dc94
+ md5sums = eaed2a7fea74c6ac46cc4dc93d868074
md5sums = 1258acfc82c50a8f452ace87fef0b416
md5sums = 0a2bbaf74c7160ba33876dcc2f050f14
diff --git a/PKGBUILD b/PKGBUILD
index 503227fa10c2..d40ec9b9cbf8 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -3,7 +3,7 @@
_pkgname=rtorrent
pkgname=rtorrent-ps
_pkgver=0.9.6
-pkgver=1.0.r40.ge768454
+pkgver=1.0.r55.g1f648ee
pkgrel=1
pkgdesc='Extended rTorrent distribution with UI enhancements, colorization, and some added features'
url='https://github.com/pyroscope/rtorrent-ps'
@@ -14,6 +14,7 @@ provides=('rtorrent')
conflicts=('rtorrent' 'rtorrent-cdl' 'rtorrent-color' 'rtorrent-git' 'rtorrent-ipv6' 'rtorrent-ps-git' 'rtorrent-pyro-git' 'rtorrent-vi-color')
source=("https://github.com/rakshasa/$_pkgname/archive/$_pkgver.tar.gz"
'command_pyroscope.cc'
+ 'ps-event-view_all.patch'
'ps-fix-double-slash-319_all.patch'
'ps-fix-sort-started-stopped-views_all.patch'
'ps-handle-sighup-578_all.patch'
@@ -28,7 +29,8 @@ source=("https://github.com/rakshasa/$_pkgname/archive/$_pkgver.tar.gz"
'ui_pyroscope.h'
'ui_pyroscope.patch')
md5sums=('b8b4009f95f8543244ae1d23b1810d7c'
- '4fd2e4373b8dab7e37dd8684fe04a555'
+ '4ad3ae94e76106add9e228ac768c9881'
+ '56701bca42cc9b309637bf3f918ede12'
'22fae392c6e281dc438b39a5019e7e1b'
'3fd739c0d5a9442f0cdec9ed5a720eaa'
'2137e16f8b881170fb92fb7a6c276193'
@@ -39,7 +41,7 @@ md5sums=('b8b4009f95f8543244ae1d23b1810d7c'
'7a88f8ab5d41242fdf1428de0e2ca182'
'26faff00b306b6ef276a7d9e6d964994'
'bd04a0699b80c8042e1cf63a7e0e4222'
- 'cd39a495ee93d9c77039fa74a9e9dc94'
+ 'eaed2a7fea74c6ac46cc4dc93d868074'
'1258acfc82c50a8f452ace87fef0b416'
'0a2bbaf74c7160ba33876dcc2f050f14')
diff --git a/command_pyroscope.cc b/command_pyroscope.cc
index 496017f21ec2..c1797a9b750a 100644
--- a/command_pyroscope.cc
+++ b/command_pyroscope.cc
@@ -487,7 +487,7 @@ d_multicall_filtered(const torrent::Object::list_type& args) {
viewItr = viewManager->find("default");
if (viewItr == viewManager->end())
- throw torrent::input_error("Could not find view.");
+ throw torrent::input_error("Could not find view '" + arg->as_string() + "'.");
// Make a filtered copy of the current item list
core::View::base_type dlist;
@@ -545,6 +545,70 @@ torrent::Object cmd_string_contains_i(rpc::target_type target, const torrent::Ob
}
+torrent::Object apply_string_mutate(int operation, const torrent::Object::list_type& args) {
+ if (args.size() < 1) {
+ throw torrent::input_error("string.* takes at least a string!");
+ }
+
+ torrent::Object::list_const_iterator itr = args.begin();
+ std::string result = itr->as_string();
+
+ for (++itr; itr != args.end(); ++itr) {
+ std::string needle = itr->as_list().begin()->as_string();
+ std::string subst = itr->as_list().rbegin()->as_string();
+
+ switch (operation) {
+ case 1:
+ if (result == needle)
+ result = subst;
+ break;
+ case 2:
+ for (size_t pos = 0; (pos = result.find(needle, pos)) != std::string::npos; pos += subst.length()) {
+ result.replace(pos, needle.length(), subst);
+ }
+ break;
+ }
+ }
+
+ return result;
+}
+
+torrent::Object cmd_string_map(rpc::target_type target, const torrent::Object::list_type& args) {
+ return apply_string_mutate(1, args);
+}
+
+torrent::Object cmd_string_replace(rpc::target_type target, const torrent::Object::list_type& args) {
+ return apply_string_mutate(2, args);
+}
+
+
+torrent::Object cmd_value(rpc::target_type target, const torrent::Object::list_type& args) {
+ if (args.size() < 1) {
+ throw torrent::input_error("'value' takes at least a number argument!");
+ }
+ if (args.size() > 2) {
+ throw torrent::input_error("'value' takes at most two arguments!");
+ }
+
+ torrent::Object::value_type val = 0;
+ if (args.front().is_value()) {
+ val = args.front().as_value();
+ } else {
+ int base = args.size() > 1 ? args.back().is_value() ?
+ args.back().as_value() : strtol(args.back().as_string().c_str(), NULL, 10) : 10;
+ char* endptr = 0;
+
+ val = strtoll(args.front().as_string().c_str(), &endptr, base);
+ while (*endptr == ' ' || *endptr == '\n') ++endptr;
+ if (*endptr) {
+ throw torrent::input_error("Junk at end of number: " + args.front().as_string());
+ }
+ }
+
+ return val;
+}
+
+
// Backports from 0.9.2
#if (API_VERSION < 3)
template <typename InputIterator, typename OutputIterator> OutputIterator
@@ -595,7 +659,9 @@ torrent::Object cmd_system_env(const torrent::Object::string_type& arg) {
// https://github.com/rakshasa/rtorrent/commit/30d8379391ad4cb3097d57aa56a488d061e68662
torrent::Object cmd_ui_current_view() {
- return control->ui()->download_list()->current_view()->name();
+ ui::DownloadList* dl = control->ui()->download_list();
+ core::View* view = dl ? dl->current_view() : 0;
+ return view ? view->name() : std::string();
}
#endif
@@ -622,6 +688,9 @@ void initialize_command_pyroscope() {
CMD2_ANY_LIST("string.contains", &cmd_string_contains);
CMD2_ANY_LIST("string.contains_i", &cmd_string_contains_i);
+ CMD2_ANY_LIST("string.map", &cmd_string_map);
+ CMD2_ANY_LIST("string.replace", &cmd_string_replace);
+ CMD2_ANY_LIST("value", &cmd_value);
CMD2_ANY_LIST("compare", &apply_compare);
CMD2_ANY("ui.bind_key", &apply_ui_bind_key);
CMD2_VAR_VALUE("ui.bind_key.verbose", 1);
diff --git a/ps-event-view_all.patch b/ps-event-view_all.patch
new file mode 100644
index 000000000000..6d38109c667d
--- /dev/null
+++ b/ps-event-view_all.patch
@@ -0,0 +1,21 @@
+--- orig-096/src/main.cc 2015-09-03 21:03:30.000000000 +0200
++++ rtorrent-0.9.6/src/main.cc 2017-04-09 14:47:56.000000000 +0200
+@@ -248,2 +249,5 @@
+ (rpc::make_target(),
++ "method.insert = event.view.hide,multi|rlookup|static\n"
++ "method.insert = event.view.show,multi|rlookup|static\n"
++
+ "method.insert = event.download.inserted,multi|rlookup|static\n"
+--- orig-096/src/ui/element_download_list.cc 2015-09-03 21:03:30.000000000 +0200
++++ rtorrent-0.9.6/src/ui/element_download_list.cc 2017-04-09 14:51:32.000000000 +0200
+@@ -219,5 +219,10 @@
+ }
+
++ std::string old_name = view() ? view()->name() : "";
++ rpc::commands.call_catch("event.view.hide", rpc::make_target(), name,
++ "View hide event action failed: ");
+ set_view(*itr);
++ rpc::commands.call_catch("event.view.show", rpc::make_target(), old_name,
++ "View show event action failed: ");
+ }
+
diff --git a/ui_pyroscope.cc b/ui_pyroscope.cc
index 6454dc6d69fc..ad02772efba7 100644
--- a/ui_pyroscope.cc
+++ b/ui_pyroscope.cc
@@ -18,6 +18,7 @@ python -c 'print u"\u22c5 \u22c5\u22c5 \u201d \u2019 \u266f \u2622 \u260d \u2318
#include "globals.h"
#include <cstdio>
+#include <cwchar>
#include <list>
#include <stdlib.h>
#include <unistd.h>
@@ -62,39 +63,39 @@ static unsigned long attr_map[3 * ps::COL_MAX] = {0};
// color indices for progress indication
int ratio_col[] = {
- ps::COL_PROGRESS0, ps::COL_PROGRESS20, ps::COL_PROGRESS40, ps::COL_PROGRESS60, ps::COL_PROGRESS80,
- ps::COL_PROGRESS100, ps::COL_PROGRESS120,
+ ps::COL_PROGRESS0, ps::COL_PROGRESS20, ps::COL_PROGRESS40, ps::COL_PROGRESS60, ps::COL_PROGRESS80,
+ ps::COL_PROGRESS100, ps::COL_PROGRESS120,
};
// basic color names
static const char* color_names[] = {
- "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
+ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
};
// list of color configuration variables, the order MUST correspond to the ColorKind enum
static const char* color_vars[ps::COL_MAX] = {
- 0,
- "ui.color.progress0",
- "ui.color.progress20",
- "ui.color.progress40",
- "ui.color.progress60",
- "ui.color.progress80",
- "ui.color.progress100",
- "ui.color.progress120",
- "ui.color.complete",
- "ui.color.seeding",
- "ui.color.stopped",
- "ui.color.queued",
- "ui.color.incomplete",
- "ui.color.leeching",
- "ui.color.alarm",
- "ui.color.title",
- "ui.color.footer",
- "ui.color.label",
- "ui.color.odd",
- "ui.color.even",
- "ui.color.info",
- "ui.color.focus",
+ 0,
+ "ui.color.progress0",
+ "ui.color.progress20",
+ "ui.color.progress40",
+ "ui.color.progress60",
+ "ui.color.progress80",
+ "ui.color.progress100",
+ "ui.color.progress120",
+ "ui.color.complete",
+ "ui.color.seeding",
+ "ui.color.stopped",
+ "ui.color.queued",
+ "ui.color.incomplete",
+ "ui.color.leeching",
+ "ui.color.alarm",
+ "ui.color.title",
+ "ui.color.footer",
+ "ui.color.label",
+ "ui.color.odd",
+ "ui.color.even",
+ "ui.color.info",
+ "ui.color.focus",
};
// collapsed state of views (default is false)
@@ -113,67 +114,82 @@ static std::string network_history_up_str;
static std::string network_history_down_str;
+// Chop off an UTF-8 string
+std::string u8_chop(const std::string& text, size_t glyphs) {
+ std::mbstate_t mbs = std::mbstate_t();
+ int bytes = 0, skip;
+ const char* pos = text.c_str();
+
+ while (*pos && glyphs-- > 0 && (skip = std::mbrlen(pos, text.length() - bytes, &mbs)) > 0) {
+ pos += skip;
+ bytes += skip;
+ }
+
+ return bytes < text.length() ? text.substr(0, bytes) : text;
+}
+
+
// get custom field contaioning a long (time_t)
unsigned long get_custom_long(core::Download* d, const char* name) {
- try {
- return atol(d->bencode()->get_key("rtorrent").get_key("custom").get_key_string(name).c_str());
- } catch (torrent::bencode_error& e) {
- return 0UL;
- }
+ try {
+ return atol(d->bencode()->get_key("rtorrent").get_key("custom").get_key_string(name).c_str());
+ } catch (torrent::bencode_error& e) {
+ return 0UL;
+ }
}
// get custom field contaioning a string
std::string get_custom_string(core::Download* d, const char* name) {
- try {
- return d->bencode()->get_key("rtorrent").get_key("custom").get_key_string(name);
- } catch (torrent::bencode_error& e) {
- return "";
- }
+ try {
+ return d->bencode()->get_key("rtorrent").get_key("custom").get_key_string(name);
+ } catch (torrent::bencode_error& e) {
+ return "";
+ }
}
// convert absolute timestamp to approximate human readable time diff (5 chars wide)
std::string elapsed_time(unsigned long dt) {
- if (dt == 0) return std::string("⋅ ⋅⋅ ");
-
- const char* unit[] = {"”", "’", "h", "d", "w", "m", "y"};
- unsigned long threshold[] = {1, 60, 3600, 86400, 7*86400, 30*86400, 365*86400, 0};
-
- int dim = 0;
- dt = time(NULL) - dt;
- while (threshold[dim] && dt >= threshold[dim]) ++dim;
- if (dim) --dim;
- float val = float(dt) / float(threshold[dim]);
-
- char buffer[15];
- if (val < 10.0 && dim) {
- snprintf(buffer, sizeof(buffer), "%1d%s%2d%s", int(val), unit[dim],
- int(dt % threshold[dim] / threshold[dim-1]), unit[dim-1]);
- } else {
- snprintf(buffer, sizeof(buffer), "%4d%s", int(val), unit[dim]);
- }
- return std::string(buffer);
+ if (dt == 0) return std::string("⋅ ⋅⋅ ");
+
+ const char* unit[] = {"”", "’", "h", "d", "w", "m", "y"};
+ unsigned long threshold[] = {1, 60, 3600, 86400, 7*86400, 30*86400, 365*86400, 0};
+
+ int dim = 0;
+ dt = time(NULL) - dt;
+ while (threshold[dim] && dt >= threshold[dim]) ++dim;
+ if (dim) --dim;
+ float val = float(dt) / float(threshold[dim]);
+
+ char buffer[15];
+ if (val < 10.0 && dim) {
+ snprintf(buffer, sizeof(buffer), "%1d%s%2d%s", int(val), unit[dim],
+ int(dt % threshold[dim] / threshold[dim-1]), unit[dim-1]);
+ } else {
+ snprintf(buffer, sizeof(buffer), "%4d%s", int(val), unit[dim]);
+ }
+ return std::string(buffer);
}
// return 2-digits number, or digit + dimension indicator
std::string num2(int64_t num) {
- if (num < 0 || 10*1000*1000 <= num) return std::string("♯♯");
- if (!num) return std::string(" ·");
-
- char buffer[10];
- if (num < 100) {
- snprintf(buffer, sizeof(buffer), "%2d", int(num));
- } else {
- // Roman numeral multipliers 10, 100, 1000, 10x1000, 100x1000, 1000x1000
- const char* roman = " xcmXCM";
- int dim = 0;
- while (num > 9) { ++dim; num /= 10; }
- snprintf(buffer, sizeof(buffer), "%1d%c", int(num), roman[dim]);
- }
-
- return std::string(buffer);
+ if (num < 0 || 10*1000*1000 <= num) return std::string("♯♯");
+ if (!num) return std::string(" ·");
+
+ char buffer[10];
+ if (num < 100) {
+ snprintf(buffer, sizeof(buffer), "%2d", int(num));
+ } else {
+ // Roman numeral multipliers 10, 100, 1000, 10x1000, 100x1000, 1000x1000
+ const char* roman = " xcmXCM";
+ int dim = 0;
+ while (num > 9) { ++dim; num /= 10; }
+ snprintf(buffer, sizeof(buffer), "%1d%c", int(num), roman[dim]);
+ }
+
+ return std::string(buffer);
}
@@ -182,7 +198,7 @@ namespace display {
// function wrapper for what possibly is a macro
static int get_colors() {
- return COLORS;
+ return COLORS;
}
@@ -191,37 +207,37 @@ static int get_colors() {
// into 4 chars by rounding for values >= 9.95.
// set bit 8 of format and 0 values will return a whitespace string of the correct length.
std::string human_size(int64_t bytes, unsigned int format=0) {
- if (format & 8 && bytes <= 0) return std::string((format & 7) ? 4 : 6, ' ');
- format &= 7;
+ if (format & 8 && bytes <= 0) return std::string((format & 7) ? 4 : 6, ' ');
+ format &= 7;
- int exp;
- char unit;
+ int exp;
+ char unit;
- if (bytes < (int64_t(1000) << 10)) { exp = 10; unit = 'K'; }
- else if (bytes < (int64_t(1000) << 20)) { exp = 20; unit = 'M'; }
- else if (bytes < (int64_t(1000) << 30)) { exp = 30; unit = 'G'; }
- else { exp = 40; unit = 'T'; }
+ if (bytes < (int64_t(1000) << 10)) { exp = 10; unit = 'K'; }
+ else if (bytes < (int64_t(1000) << 20)) { exp = 20; unit = 'M'; }
+ else if (bytes < (int64_t(1000) << 30)) { exp = 30; unit = 'G'; }
+ else { exp = 40; unit = 'T'; }
- char buffer[48];
- double value = double(bytes) / (int64_t(1) << exp);
- const char* formats[] = {"%5.1f%c", "%3.0f%c", "%3.1f%c"};
+ char buffer[48];
+ double value = double(bytes) / (int64_t(1) << exp);
+ const char* formats[] = {"%5.1f%c", "%3.0f%c", "%3.1f%c"};
- if (format > 2) format = 0;
- if (format == 2 and value >= 9.949999) format = 1;
- if (format == 1) value = int(value + 0.50002);
- snprintf(buffer, sizeof(buffer), formats[format], value, unit);
+ if (format > 2) format = 0;
+ if (format == 2 and value >= 9.949999) format = 1;
+ if (format == 1) value = int(value + 0.50002);
+ snprintf(buffer, sizeof(buffer), formats[format], value, unit);
- return std::string(buffer);
+ return std::string(buffer);
}
// split a given string into words separated by delim, and add them to the provided vector
void split(std::vector<std::string>& words, const char* str, char delim = ' ') {
- do {
- const char* begin = str;
- while (*str && *str != delim) str++;
- words.push_back(std::string(begin, str));
- } while (*str++);
+ do {
+ const char* begin = str;
+ while (*str && *str != delim) str++;
+ words.push_back(std::string(begin, str));
+ } while (*str++);
}
@@ -230,440 +246,482 @@ static bool color_init_recursion = false;
// create color map from configuration strings
void ui_pyroscope_colormap_init() {
- // if in early startup stage (configuration), then init the screen so we can query system constants
- if (!get_colors()) {
- if (color_init_recursion) {
- color_init_recursion = false;
- control->core()->push_log("Terminal color initialization failed, does your terminal have none?!");
- } else {
- color_init_recursion = true;
- initscr();
- ui_pyroscope_canvas_init(); // this calls us again!
- }
- return;
- }
- color_init_recursion = false;
-
- // Those hold the background colors of "odd" and "even"
- int bg_odd = -1;
- int bg_even = -1;
-
- // read the definition for basic colors from configuration
- for (int k = 1; k < ps::COL_MAX; k++) {
- init_pair(k, -1, -1);
- std::string col_def = rpc::call_command_string(color_vars[k]);
- if (col_def.empty()) continue; // use terminal default if definition is empty
-
- std::vector<std::string> words;
- split(words, col_def.c_str());
-
- short col[2] = {-1, -1}; // fg, bg
- short col_idx = 0; // 0 = fg; 1 = bg
- short bright = 0;
- unsigned long attr = A_NORMAL;
- for (int i = 0; i < words.size(); i++) { // look at all the words
- if (words[i] == "bold") attr |= A_BOLD;
- else if (words[i] == "standout") attr |= A_STANDOUT;
- else if (words[i] == "underline") attr |= A_UNDERLINE;
- else if (words[i] == "reverse") attr |= A_REVERSE;
- else if (words[i] == "blink") attr |= A_BLINK;
- else if (words[i] == "dim") attr |= A_DIM;
- else if (words[i] == "on") { col_idx = 1; bright = 0; } // switch to background color
- else if (words[i] == "gray") col[col_idx] = bright ? 7 : 8; // bright gray is white
- else if (words[i] == "bright") bright = 8;
- else if (words[i].find_first_not_of("0123456789") == std::string::npos) {
- // handle numeric index
- short c = -1;
- sscanf(words[i].c_str(), "%hd", &c);
- col[col_idx] = c;
- } else for (short c = 0; c < 8; c++) { // check for basic color names
- if (words[i] == color_names[c]) {
- col[col_idx] = bright + c;
- break;
- }
- }
- }
-
- // check that fg & bg color index is valid
- if (col[0] != -1 && col[0] >= get_colors() || col[1] != -1 && col[1] >= get_colors()) {
- char buf[33];
- sprintf(buf, "%d", get_colors());
- Canvas::cleanup();
- throw torrent::input_error(col_def + ": your terminal only supports " + buf + " colors.");
- }
-
- // store the parsed color definition
- attr_map[k] = attr;
- init_pair(k, col[0], col[1]);
- if (k == ps::COL_EVEN) bg_even = col[1];
- if (k == ps::COL_ODD) bg_odd = col[1];
- }
-
- // now make copies of the basic colors with the "odd" and "even" definitions mixed in
- for (int k = 1; k < ps::COL_MAX; k++) {
- short fg, bg;
- pair_content(k, &fg, &bg);
-
- // replace the background color, and mix in the attributes
- attr_map[k + 1 * ps::COL_MAX] = attr_map[k] | attr_map[ps::COL_EVEN];
- attr_map[k + 2 * ps::COL_MAX] = attr_map[k] | attr_map[ps::COL_ODD];
- init_pair(k + 1 * ps::COL_MAX, fg, bg == -1 ? bg_even : bg);
- init_pair(k + 2 * ps::COL_MAX, fg, bg == -1 ? bg_odd : bg);
- }
+ // if in early startup stage (configuration), then init the screen so we can query system constants
+ if (!get_colors()) {
+ if (color_init_recursion) {
+ color_init_recursion = false;
+ control->core()->push_log("Terminal color initialization failed, does your terminal have none?!");
+ } else {
+ color_init_recursion = true;
+ initscr();
+ ui_pyroscope_canvas_init(); // this calls us again!
+ }
+ return;
+ }
+ color_init_recursion = false;
+
+ // Those hold the background colors of "odd" and "even"
+ int bg_odd = -1;
+ int bg_even = -1;
+
+ // read the definition for basic colors from configuration
+ for (int k = 1; k < ps::COL_MAX; k++) {
+ init_pair(k, -1, -1);
+ std::string col_def = rpc::call_command_string(color_vars[k]);
+ if (col_def.empty()) continue; // use terminal default if definition is empty
+
+ std::vector<std::string> words;
+ split(words, col_def.c_str());
+
+ short col[2] = {-1, -1}; // fg, bg
+ short col_idx = 0; // 0 = fg; 1 = bg
+ short bright = 0;
+ unsigned long attr = A_NORMAL;
+ for (int i = 0; i < words.size(); i++) { // look at all the words
+ if (words[i] == "bold") attr |= A_BOLD;
+ else if (words[i] == "standout") attr |= A_STANDOUT;
+ else if (words[i] == "underline") attr |= A_UNDERLINE;
+ else if (words[i] == "reverse") attr |= A_REVERSE;
+ else if (words[i] == "blink") attr |= A_BLINK;
+ else if (words[i] == "dim") attr |= A_DIM;
+ else if (words[i] == "on") { col_idx = 1; bright = 0; } // switch to background color
+ else if (words[i] == "gray") col[col_idx] = bright ? 7 : 8; // bright gray is white
+ else if (words[i] == "bright") bright = 8;
+ else if (words[i].find_first_not_of("0123456789") == std::string::npos) {
+ // handle numeric index
+ short c = -1;
+ sscanf(words[i].c_str(), "%hd", &c);
+ col[col_idx] = c;
+ } else for (short c = 0; c < 8; c++) { // check for basic color names
+ if (words[i] == color_names[c]) {
+ col[col_idx] = bright + c;
+ break;
+ }
+ }
+ }
+
+ // check that fg & bg color index is valid
+ if (col[0] != -1 && col[0] >= get_colors() || col[1] != -1 && col[1] >= get_colors()) {
+ char buf[33];
+ sprintf(buf, "%d", get_colors());
+ Canvas::cleanup();
+ throw torrent::input_error(col_def + ": your terminal only supports " + buf + " colors.");
+ }
+
+ // store the parsed color definition
+ attr_map[k] = attr;
+ init_pair(k, col[0], col[1]);
+ if (k == ps::COL_EVEN) bg_even = col[1];
+ if (k == ps::COL_ODD) bg_odd = col[1];
+ }
+
+ // now make copies of the basic colors with the "odd" and "even" definitions mixed in
+ for (int k = 1; k < ps::COL_MAX; k++) {
+ short fg, bg;
+ pair_content(k, &fg, &bg);
+
+ // replace the background color, and mix in the attributes
+ attr_map[k + 1 * ps::COL_MAX] = attr_map[k] | attr_map[ps::COL_EVEN];
+ attr_map[k + 2 * ps::COL_MAX] = attr_map[k] | attr_map[ps::COL_ODD];
+ init_pair(k + 1 * ps::COL_MAX, fg, bg == -1 ? bg_even : bg);
+ init_pair(k + 2 * ps::COL_MAX, fg, bg == -1 ? bg_odd : bg);
+ }
}
// add color handling to canvas initialization
void ui_pyroscope_canvas_init() {
- start_color();
- use_default_colors();
- ui_pyroscope_colormap_init();
+ start_color();
+ use_default_colors();
+ ui_pyroscope_colormap_init();
}
// offset into the color index table, depending on whether this is an odd or even item
static int row_offset(core::View* view, Range& range) {
- return (((range.first - view->begin_visible()) & 1) + 1) * ps::COL_MAX;
+ return (((range.first - view->begin_visible()) & 1) + 1) * ps::COL_MAX;
}
static void decorate_download_title(Window* window, display::Canvas* canvas, core::View* view, int pos, Range& range) {
- int offset = row_offset(view, range);
- core::Download* item = *range.first;
- bool active = item->is_open() && item->is_active();
-
- // download title color
- int title_col;
- unsigned long focus_attr = range.first == view->focus() ? attr_map[ps::COL_FOCUS] : 0;
- if ((*range.first)->is_done())
- title_col = (active ? D_INFO(item)->up_rate()->rate() ? ps::COL_SEEDING : ps::COL_COMPLETE : ps::COL_STOPPED) + offset;
- else
- title_col = (active ? D_INFO(item)->down_rate()->rate() ? ps::COL_LEECHING : ps::COL_INCOMPLETE : ps::COL_QUEUED) + offset;
- canvas->set_attr(2, pos, -1, attr_map[title_col] | focus_attr, title_col);
-
- // show label for tracker in focus
- std::string url = get_active_tracker_domain((*range.first)->download());
- if (!url.empty()) {
- std::string alias = tracker_aliases[url];
- if (!alias.empty()) url = alias;
-
- // shorten label if too long
- int len = url.length();
- if (len > TRACKER_LABEL_WIDTH) {
- url = "…" + url.substr(len - TRACKER_LABEL_WIDTH);
- len = TRACKER_LABEL_WIDTH + 1;
- }
-
- // print it right-justified and in braces
- int td_col = ps::COL_INFO;
- //int td_col = active ? ps::COL_INFO : (*range.first)->is_done() ? ps::COL_STOPPED : ps::COL_QUEUED;
- int xpos = canvas->width() - len - 2;
- canvas->print(xpos, pos, "{%s}", url.c_str());
- canvas->set_attr(xpos + 1, pos, len, attr_map[td_col + offset] | focus_attr, td_col + offset);
- canvas->set_attr(xpos, pos, 1, (attr_map[td_col + offset] | focus_attr) ^ A_BOLD, td_col + offset);
- canvas->set_attr(canvas->width() - 1, pos, 1, (attr_map[td_col + offset] | focus_attr) ^ A_BOLD, td_col + offset);
- }
+ int offset = row_offset(view, range);
+ core::Download* item = *range.first;
+ bool active = item->is_open() && item->is_active();
+
+ // download title color
+ int title_col;
+ unsigned long focus_attr = range.first == view->focus() ? attr_map[ps::COL_FOCUS] : 0;
+ if ((*range.first)->is_done())
+ title_col = (active ? D_INFO(item)->up_rate()->rate() ?
+ ps::COL_SEEDING : ps::COL_COMPLETE : ps::COL_STOPPED) + offset;
+ else
+ title_col = (active ? D_INFO(item)->down_rate()->rate() ?
+ ps::COL_LEECHING : ps::COL_INCOMPLETE : ps::COL_QUEUED) + offset;
+ canvas->set_attr(2, pos, -1, attr_map[title_col] | focus_attr, title_col);
+
+ // show label for active tracker (a/k/a in focus tracker)
+ std::string url = get_active_tracker_domain((*range.first)->download());
+ if (!url.empty()) {
+ std::string alias = tracker_aliases[url];
+ if (!alias.empty()) url = alias;
+
+ // shorten label if too long
+ int len = url.length();
+ if (len > TRACKER_LABEL_WIDTH) {
+ url = "…" + url.substr(len - TRACKER_LABEL_WIDTH);
+ len = TRACKER_LABEL_WIDTH + 1;
+ }
+
+ // print it right-justified and in braces
+ int td_col = ps::COL_INFO;
+ //int td_col = active ? ps::COL_INFO : (*range.first)->is_done() ? ps::COL_STOPPED : ps::COL_QUEUED;
+ int xpos = canvas->width() - len - 2;
+ canvas->print(xpos, pos, "{%s}", url.c_str());
+ canvas->set_attr(xpos + 1, pos, len, attr_map[td_col + offset] | focus_attr, td_col + offset);
+ canvas->set_attr(xpos, pos, 1, (attr_map[td_col + offset] | focus_attr) ^ A_BOLD, td_col + offset);
+ canvas->set_attr(canvas->width() - 1, pos, 1, (attr_map[td_col + offset] | focus_attr) ^ A_BOLD, td_col + offset);
+ }
}
// show ratio progress by color (ratio is scaled x1000)
static int ratio_color(int ratio) {
- int rcol = sizeof(ratio_col) / sizeof(*ratio_col) - 1;
- return ratio_col[std::min(rcol, ratio * rcol / 1200)];
+ int rcol = sizeof(ratio_col) / sizeof(*ratio_col) - 1;
+ return ratio_col[std::min(rcol, ratio * rcol / 1200)];
}
// patch hook for download list canvas redraw of a single item; "pos" is placed AFTER the item
void ui_pyroscope_download_list_redraw_item(Window* window, display::Canvas* canvas, core::View* view, int pos, Range& range) {
- int offset = row_offset(view, range);
- torrent::Download* item = (*range.first)->download();
-
- pos -= 3;
-
- // is this the item in focus?
- if (range.first == view->focus()) {
- for (int i = 0; i < 3; i++ ) {
- canvas->set_attr(0, pos+i, 1, attr_map[ps::COL_FOCUS], ps::COL_FOCUS);
- }
- }
-
- decorate_download_title(window, canvas, view, pos, range);
-
- // better handling for trail of line 2 (ratio etc.)
- int status_pos = 91;
- int ratio = rpc::call_command_value("d.ratio", rpc::make_target(*range.first));
-
- if (status_pos < canvas->width()) {
- canvas->print(status_pos, pos+1, "R:%6.2f [%c%c] %-4.4s ",
- float(ratio) / 1000.0,
- rpc::call_command_string("d.tied_to_file", rpc::make_target(*range.first)).empty() ? ' ' : 'T',
- (rpc::call_command_value("d.ignore_commands", rpc::make_target(*range.first)) == 0) ? ' ' : 'I',
- (*range.first)->priority() == 2 ? "" :
- rpc::call_command_string("d.priority_str", rpc::make_target(*range.first)).c_str()
- );
- status_pos += 9 + 5 + 5;
- }
-
- // if space is left, show throttle name
- if (status_pos < canvas->width()) {
- std::string item_status;
-
- if (!(*range.first)->bencode()->get_key("rtorrent").get_key_string("throttle_name").empty()) {
- //item_status += "T=";
- item_status += rpc::call_command_string("d.throttle_name", rpc::make_target(*range.first)) + ' ';
- }
-
- // left-justifying this also overwrites any junk from the original display that we overwrite
- int chars_left = canvas->width() - status_pos - item_status.length();
- if (chars_left < 0) {
- item_status = item_status.substr(0, 1-chars_left) + "…";
- } else if (chars_left > 0) {
- item_status = std::string(chars_left, ' ') + item_status;
- }
- canvas->print(status_pos, pos+1, "%s", item_status.c_str());
- }
-
- //.........1.........2.........3.........4.........5.........6.........7.........8.........9.........0.........1
- //12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
- // [CLOSED] 0,0 / 15,9 MB Rate: 0,0 / 0,0 KB Uploaded: 0,0 MB [ 0%] --d --:-- R:nnnnnn [TI]
- // [CLOSED] 0.0K / 0.0K U/D: 0.0K / 0.0K Uploaded: 0.0K R: 0.00 [T ]
- int label_pos[] = {19, 1, 31, 5, 44, 1, 54, 9, 75, 1, 79, 1, 91, 2, 100, 1, 103, 1};
- const char* labels[sizeof(label_pos) / sizeof(int) / 2] = {0, " U/D:"};
- int col_active = ps::COL_INFO;
- //int col_active = item->is_open() && item->is_active() ? ps::COL_INFO : (*range.first)->is_done() ? ps::COL_STOPPED : ps::COL_QUEUED;
-
- // apply basic "info" style, and then revert static text to "label"
- canvas->set_attr(2, pos+1, canvas->width() - 1, attr_map[col_active + offset], col_active + offset);
- for (int label_idx = 0; label_idx < sizeof(label_pos) / sizeof(int); label_idx += 2) {
- if (labels[label_idx/2]) canvas->print(label_pos[label_idx], pos+1, labels[label_idx/2]);
- canvas->set_attr(label_pos[label_idx], pos+1, label_pos[label_idx+1], attr_map[ps::COL_LABEL + offset], ps::COL_LABEL + offset);
- }
-
- // apply progress color to completion indicator
- int pcol = ratio_color(item->file_list()->completed_chunks() * 1000 / item->file_list()->size_chunks());
- canvas->set_attr(76, pos+1, 3, attr_map[pcol + offset], pcol + offset);
-
- // show ratio progress by color
- int rcol = ratio_color(ratio);
- canvas->set_attr(93, pos+1, 6, attr_map[rcol + offset], rcol + offset);
-
- // mark active up / down ("focus", plus "seeding" or "leeching"), and dim inactive numbers (i.e. 0)
- canvas->set_attr(37, pos+1, 6, attr_map[ps::COL_SEEDING + offset] | (D_INFO(item)->up_rate()->rate() ? attr_map[ps::COL_FOCUS] : 0),
- (D_INFO(item)->up_rate()->rate() ? ps::COL_SEEDING : ps::COL_LABEL) + offset);
- canvas->set_attr(46, pos+1, 6, attr_map[ps::COL_LEECHING + offset] | (D_INFO(item)->down_rate()->rate() ? attr_map[ps::COL_FOCUS] : 0),
- (D_INFO(item)->down_rate()->rate() ? ps::COL_LEECHING : ps::COL_LABEL) + offset);
-
- // mark non-trivial messages
- if (!(*range.first)->message().empty() && (*range.first)->message().find("Tried all trackers") == std::string::npos) {
- canvas->set_attr(1, pos, 1, attr_map[ps::COL_ALARM + offset], ps::COL_ALARM + offset);
- canvas->set_attr(1, pos+1, 1, attr_map[ps::COL_ALARM + offset], ps::COL_ALARM + offset);
- canvas->set_attr(1, pos+2, -1, attr_map[ps::COL_ALARM + offset], ps::COL_ALARM + offset);
- }
+ int offset = row_offset(view, range);
+ torrent::Download* item = (*range.first)->download();
+
+ pos -= 3;
+
+ // is this the item in focus?
+ if (range.first == view->focus()) {
+ for (int i = 0; i < 3; i++ ) {
+ canvas->set_attr(0, pos+i, 1, attr_map[ps::COL_FOCUS], ps::COL_FOCUS);
+ }
+ }
+
+ decorate_download_title(window, canvas, view, pos, range);
+
+ // better handling for trail of line 2 (ratio etc.)
+ int status_pos = 91;
+ int ratio = rpc::call_command_value("d.ratio", rpc::make_target(*range.first));
+
+ if (status_pos < canvas->width()) {
+ canvas->print(status_pos, pos+1, "R:%6.2f [%c%c] %-4.4s ",
+ float(ratio) / 1000.0,
+ rpc::call_command_string("d.tied_to_file", rpc::make_target(*range.first)).empty() ? ' ' : 'T',
+ (rpc::call_command_value("d.ignore_commands", rpc::make_target(*range.first)) == 0) ? ' ' : 'I',
+ (*range.first)->priority() == 2 ? "" :
+ rpc::call_command_string("d.priority_str", rpc::make_target(*range.first)).c_str()
+ );
+ status_pos += 9 + 5 + 5;
+ }
+
+ // if space is left, show throttle name
+ if (status_pos < canvas->width()) {
+ std::string item_status;
+
+ if (!(*range.first)->bencode()->get_key("rtorrent").get_key_string("throttle_name").empty()) {
+ //item_status += "T=";
+ item_status += rpc::call_command_string("d.throttle_name", rpc::make_target(*range.first)) + ' ';
+ }
+
+ // left-justifying this also overwrites any junk from the original display that we overwrite
+ int chars_left = canvas->width() - status_pos - item_status.length();
+ if (chars_left < 0) {
+ item_status = item_status.substr(0, 1-chars_left) + "…";
+ } else if (chars_left > 0) {
+ item_status = std::string(chars_left, ' ') + item_status;
+ }
+ canvas->print(status_pos, pos+1, "%s", item_status.c_str());
+ }
+
+ //.........1.........2.........3.........4.........5.........6.........7.........8.........9.........0.........1
+ //12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ // [CLOSED] 0,0 / 15,9 MB Rate: 0,0 / 0,0 KB Uploaded: 0,0 MB [ 0%] --d --:-- R:nnnnnn [TI]
+ // [CLOSED] 0.0K / 0.0K U/D: 0.0K / 0.0K Uploaded: 0.0K R: 0.00 [T ]
+ int label_pos[] = {19, 1, 31, 5, 44, 1, 54, 9, 75, 1, 79, 1, 91, 2, 100, 1, 103, 1};
+ const char* labels[sizeof(label_pos) / sizeof(int) / 2] = {0, " U/D:"};
+ int col_active = ps::COL_INFO;
+ //int col_active = item->is_open() && item->is_active() ? ps::COL_INFO : (*range.first)->is_done() ? ps::COL_STOPPED : ps::COL_QUEUED;
+
+ // apply basic "info" style, and then revert static text to "label"
+ canvas->set_attr(2, pos+1, canvas->width() - 1, attr_map[col_active + offset], col_active + offset);
+ for (int label_idx = 0; label_idx < sizeof(label_pos) / sizeof(int); label_idx += 2) {
+ if (labels[label_idx/2]) canvas->print(label_pos[label_idx], pos+1, labels[label_idx/2]);
+ canvas->set_attr(label_pos[label_idx], pos+1, label_pos[label_idx+1], attr_map[ps::COL_LABEL + offset], ps::COL_LABEL + offset);
+ }
+
+ // apply progress color to completion indicator
+ int pcol = ratio_color(item->file_list()->completed_chunks() * 1000 / item->file_list()->size_chunks());
+ canvas->set_attr(76, pos+1, 3, attr_map[pcol + offset], pcol + offset);
+
+ // show ratio progress by color
+ int rcol = ratio_color(ratio);
+ canvas->set_attr(93, pos+1, 6, attr_map[rcol + offset], rcol + offset);
+
+ // mark active up / down ("focus", plus "seeding" or "leeching"), and dim inactive numbers (i.e. 0)
+ canvas->set_attr(37, pos+1, 6, attr_map[ps::COL_SEEDING + offset] | (D_INFO(item)->up_rate()->rate() ? attr_map[ps::COL_FOCUS] : 0),
+ (D_INFO(item)->up_rate()->rate() ? ps::COL_SEEDING : ps::COL_LABEL) + offset);
+ canvas->set_attr(46, pos+1, 6, attr_map[ps::COL_LEECHING + offset] | (D_INFO(item)->down_rate()->rate() ? attr_map[ps::COL_FOCUS] : 0),
+ (D_INFO(item)->down_rate()->rate() ? ps::COL_LEECHING : ps::COL_LABEL) + offset);
+
+ // mark non-trivial messages
+ if (!(*range.first)->message().empty() && (*range.first)->message().find("Tried all trackers") == std::string::npos) {
+ canvas->set_attr(1, pos, 1, attr_map[ps::COL_ALARM + offset], ps::COL_ALARM + offset);
+ canvas->set_attr(1, pos+1, 1, attr_map[ps::COL_ALARM + offset], ps::COL_ALARM + offset);
+ canvas->set_attr(1, pos+2, -1, attr_map[ps::COL_ALARM + offset], ps::COL_ALARM + offset);
+ }
+}
+
+
+// Render columns from `column_defs`, return total length
+int render_columns(bool headers, rpc::target_type target,
+ display::Canvas* canvas, int column, int pos,
+ const torrent::Object::map_type& column_defs) {
+ torrent::Object::map_const_iterator cols_itr, last_col = column_defs.end();
+ int total = 0;
+
+ for (cols_itr = column_defs.begin(); cols_itr != last_col; ++cols_itr) {
+ // Skip sort key (format is "sort:len:title")
+ size_t header_colon = cols_itr->first.find(':');
+ if (header_colon == std::string::npos) continue;
+
+ // Parse header length
+ const char* header_pos = cols_itr->first.c_str() + header_colon + 1;
+ char* header_text = 0;
+ int header_len = (int)strtol(header_pos, &header_text, 10);
+ if (*header_text++ != ':') continue;
+
+ // Render title text, or the result of the column command
+ if (headers) {
+ canvas->print(column, pos, " %s", header_text);
+ } else {
+ std::string text = rpc::call_object_nothrow(cols_itr->second, target).as_string();
+ //std::string text = rpc::call_command_string(cols_itr->second.as_string().c_str(), target);
+ canvas->print(column, pos, " %s", u8_chop(text, header_len).c_str());
+ }
+
+ // Advance posiiton
+ column += header_len + 1;
+ total += header_len + 1;
+ }
+ return total;
}
// patch hook for download list canvas redraw; if this returns true, the calling
// function is left immediately (i.e. true indicates we took over ALL redrawing)
bool ui_pyroscope_download_list_redraw(Window* window, display::Canvas* canvas, core::View* view) {
- // show "X of Y"
- if (canvas->width() > 16) {
- int item_idx = view->focus() - view->begin_visible();
- if (item_idx == view->size())
- canvas->print(canvas->width() - 16, 0, "[ none of %-5d]", view->size());
- else
- canvas->print(canvas->width() - 16, 0, "[%5d of %-5d]", item_idx + 1, view->size());
- }
- canvas->set_attr(0, 0, -1, attr_map[ps::COL_TITLE], ps::COL_TITLE);
-
- if (is_collapsed.find(view->name()) == is_collapsed.end() || !is_collapsed[view->name()])
- return false; // continue in calling function
-
- if (view->empty_visible() || canvas->width() < 5 || canvas->height() < 2)
- return true;
-
- // show column headers
- int pos = 1;
- canvas->print(2, pos, " ☢ ☍ ⌘ ✰ ⣿ ⚡ ☯ ⚑ ↺ ⤴ ⤵ ∆ ⌚ ≀∇ ✇ Name");
- if (canvas->width() > TRACKER_LABEL_WIDTH) {
- canvas->print(canvas->width() - 14, 1, "Tracker Domain");
- }
- canvas->set_attr(0, pos, -1, attr_map[ps::COL_LABEL], ps::COL_LABEL);
-
- // network traffic
- int network_history_lines = 0;
- if (network_history_depth) {
- network_history_lines = 2;
- pos = canvas->height() - 2;
-
- canvas->print(0, pos, "%s", network_history_up_str.c_str());
- canvas->set_attr(0, pos, -1, attr_map[ps::COL_SEEDING], ps::COL_SEEDING);
- canvas->print(0, pos+1, "%s", network_history_down_str.c_str());
- canvas->set_attr(0, pos+1, -1, attr_map[ps::COL_LEECHING], ps::COL_LEECHING);
- }
-
- // Styles
- #define PROGRESS_STEPS 9
- const char* progress[3][PROGRESS_STEPS] = {
- {},
- {"⠀ ", "⠁ ", "⠉ ", "⠋ ", "⠛ ", "⠟ ", "⠿ ", "⡿ ", "⣿ "},
- {"⠀ ", "▁ ", "▂ ", "▃ ", "▄ ", "▅ ", "▆ ", "▇ ", "█ "},
- };
- unsigned int progress_style = std::min<unsigned int>(rpc::call_command_value("ui.style.progress"), 2);
- #define YING_YANG_STEPS 11
- const char* ying_yang[4][YING_YANG_STEPS] = {
- {},
- {"☹ ", "➀ ", "➁ ", "➂ ", "➃ ", "➄ ", "➅ ", "➆ ", "➇ ", "➈ ", "➉ "},
- {"☹ ", "① ", "② ", "③ ", "④ ", "⑤ ", "⑥ ", "⑦ ", "⑧ ", "⑨ ", "⑩ "},
- {"☹ ", "➊ ", "➋ ", "➌ ", "➍ ", "➎ ", "➏ ", "➐ ", "➑ ", "➒ ", "➓ "},
- };
- unsigned int ying_yang_style = std::min<unsigned int>(rpc::call_command_value("ui.style.ratio"), 3);
-
- // define iterator range
- Range range = rak::advance_bidirectional(
- view->begin_visible(),
- view->focus() != view->end_visible() ? view->focus() : view->begin_visible(),
- view->end_visible(),
- canvas->height()-2-2-network_history_lines);
-
- pos = 2;
- while (range.first != range.second) {
- core::Download* d = *range.first;
- core::Download* item = d;
- torrent::Tracker* tracker = get_active_tracker((*range.first)->download());
- int ratio = rpc::call_command_value("d.ratio", rpc::make_target(d));
- bool has_msg = !d->message().empty();
- bool has_alert = has_msg && d->message().find("Tried all trackers") == std::string::npos;
- int offset = row_offset(view, range);
- int col_active = ps::COL_INFO;
- //int col_active = item->is_open() && item->is_active() ? ps::COL_INFO : d->is_done() ? ps::COL_STOPPED : ps::COL_QUEUED;
-
- const char* alert = "⚠ ";
- if (has_alert) {
- if (d->message().find("Timeout was reached") != std::string::npos
- || d->message().find("Timed out") != std::string::npos)
- alert = "◔ ";
- else if (d->message().find("Connecting to") != std::string::npos)
- alert = "⚡ ";
- else if (d->message().find("Could not parse bencoded data") != std::string::npos
- || d->message().find("Failed sending data") != std::string::npos
- || d->message().find("Server returned nothing") != std::string::npos
- || d->message().find("Couldn't connect to server") != std::string::npos)
- alert = "↯ ";
- else if (d->message().find("not registered") != std::string::npos
- || d->message().find("torrent cannot be found") != std::string::npos
- || d->message().find("unregistered") != std::string::npos)
- alert = "¿?";
- else if (d->message().find("not authorized") != std::string::npos
- || d->message().find("blocked from") != std::string::npos
- || d->message().find("denied") != std::string::npos
- || d->message().find("limit exceeded") != std::string::npos
- || d->message().find("active torrents are enough") != std::string::npos)
- alert = "⨂ ";
- }
-
- const char* prios[] = {"✖ ", "⇣ ", "  ", "⇡ "};
+ // show "X of Y"
+ if (canvas->width() > 16) {
+ int item_idx = view->focus() - view->begin_visible();
+ if (item_idx == view->size())
+ canvas->print(canvas->width() - 16, 0, "[ none of %-5d]", view->size());
+ else
+ canvas->print(canvas->width() - 16, 0, "[%5d of %-5d]", item_idx + 1, view->size());
+ }
+ canvas->set_attr(0, 0, -1, attr_map[ps::COL_TITLE], ps::COL_TITLE);
+
+ if (is_collapsed.find(view->name()) == is_collapsed.end() || !is_collapsed[view->name()])
+ return false; // continue in calling function
+
+ if (view->empty_visible() || canvas->width() < 5 || canvas->height() < 2)
+ return true;
+
+ // show column headers
+ const torrent::Object::map_type& column_defs = control->object_storage()->get_str("ui.column.render").as_map();
+ // x_base value depends on the static headers below!
+ int pos = 1, x_base = 31, column = x_base;
+
+ canvas->print(2, pos, " ⣿ ⚡ ☯ ⚑ ↺ ⤴ ⤵ ∆ ⌚ ≀∇ ");
+ column += render_columns(true, rpc::make_target(), canvas, column, pos, column_defs);
+ canvas->print(column, pos, " Name "); column += 6;
+ if (canvas->width() - column > TRACKER_LABEL_WIDTH) {
+ canvas->print(canvas->width() - 14, 1, "Tracker Domain");
+ }
+ canvas->set_attr(0, pos, -1, attr_map[ps::COL_LABEL], ps::COL_LABEL);
+
+ // network traffic
+ int network_history_lines = 0;
+ if (network_history_depth) {
+ network_history_lines = 2;
+ pos = canvas->height() - 2;
+
+ canvas->print(0, pos, "%s", network_history_up_str.c_str());
+ canvas->set_attr(0, pos, -1, attr_map[ps::COL_SEEDING], ps::COL_SEEDING);
+ canvas->print(0, pos+1, "%s", network_history_down_str.c_str());
+ canvas->set_attr(0, pos+1, -1, attr_map[ps::COL_LEECHING], ps::COL_LEECHING);
+ }
+
+ // Styles
+ #define PROGRESS_STEPS 9
+ const char* progress[3][PROGRESS_STEPS] = {
+ {},
+ {"⠀ ", "⠁ ", "⠉ ", "⠋ ", "⠛ ", "⠟ ", "⠿ ", "⡿ ", "⣿ "},
+ {"⠀ ", "▁ ", "▂ ", "▃ ", "▄ ", "▅ ", "▆ ", "▇ ", "█ "},
+ };
+ unsigned int progress_style = std::min<unsigned int>(rpc::call_command_value("ui.style.progress"), 2);
+ #define YING_YANG_STEPS 11
+ const char* ying_yang[4][YING_YANG_STEPS] = {
+ {},
+ {"☹ ", "➀ ", "➁ ", "➂ ", "➃ ", "➄ ", "➅ ", "➆ ", "➇ ", "➈ ", "➉ "},
+ {"☹ ", "① ", "② ", "③ ", "④ ", "⑤ ", "⑥ ", "⑦ ", "⑧ ", "⑨ ", "⑩ "},
+ {"☹ ", "➊ ", "➋ ", "➌ ", "➍ ", "➎ ", "➏ ", "➐ ", "➑ ", "➒ ", "➓ "},
+ };
+ unsigned int ying_yang_style = std::min<unsigned int>(rpc::call_command_value("ui.style.ratio"), 3);
+
+ // define iterator range
+ Range range = rak::advance_bidirectional(
+ view->begin_visible(),
+ view->focus() != view->end_visible() ? view->focus() : view->begin_visible(),
+ view->end_visible(),
+ canvas->height()-2-2-network_history_lines);
+
+ pos = 2;
+ while (range.first != range.second) {
+ core::Download* d = *range.first;
+ core::Download* item = d;
+ torrent::Tracker* tracker = get_active_tracker((*range.first)->download());
+ int ratio = rpc::call_command_value("d.ratio", rpc::make_target(d));
+ bool has_msg = !d->message().empty();
+ bool has_alert = has_msg && d->message().find("Tried all trackers") == std::string::npos;
+ int offset = row_offset(view, range);
+ int col_active = ps::COL_INFO;
+ //int col_active = item->is_open() && item->is_active() ? ps::COL_INFO : d->is_done() ? ps::COL_STOPPED : ps::COL_QUEUED;
+
+ const char* alert = "⚠ ";
+ if (has_alert) {
+ if (d->message().find("Timeout was reached") != std::string::npos
+ || d->message().find("Timed out") != std::string::npos)
+ alert = "◔ ";
+ else if (d->message().find("Connecting to") != std::string::npos)
+ alert = "⚡ ";
+ else if (d->message().find("Could not parse bencoded data") != std::string::npos
+ || d->message().find("Failed sending data") != std::string::npos
+ || d->message().find("Server returned nothing") != std::string::npos
+ || d->message().find("Couldn't connect to server") != std::string::npos)
+ alert = "↯ ";
+ else if (d->message().find("not registered") != std::string::npos
+ || d->message().find("torrent cannot be found") != std::string::npos
+ || d->message().find("unregistered") != std::string::npos)
+ alert = "¿?";
+ else if (d->message().find("not authorized") != std::string::npos
+ || d->message().find("blocked from") != std::string::npos
+ || d->message().find("denied") != std::string::npos
+ || d->message().find("limit exceeded") != std::string::npos
+ || d->message().find("active torrents are enough") != std::string::npos)
+ alert = "⨂ ";
+ }
std::string displayname = get_custom_string(d, "displayname");
- int is_tagged = rpc::commands.call_command_d("d.views.has", d, torrent::Object("tagged")).as_value() == 1;
- uint32_t down_rate = D_INFO(item)->down_rate()->rate();
- char buffer[canvas->width() + 1];
- char* last = buffer + canvas->width() - 2 + 1;
- print_download_title(buffer, last, d);
-
- char progress_str[6] = "##";
- char ying_yang_str[6] = "##";
- if (progress_style == 0) {
- sprintf(progress_str, item->file_list()->completed_chunks() ? "%2.2d" : "--",
- item->file_list()->completed_chunks() * 100 / item->file_list()->size_chunks());
- }
- if (ying_yang_style == 0 && ratio < 9949) {
- sprintf(ying_yang_str, ratio ? "%2.2d" : "--", ratio / 100);
- }
-
- canvas->print(0, pos, "%s %s%s%s%s%s%s%s%s %s %s %s %s %s%s %s%s%s",
- range.first == view->focus() ? "»" : " ",
- item->is_open() ? item->is_active() ? "▹ " : "╍ " : "▪ ",
- rpc::call_command_string("d.tied_to_file", rpc::make_target(d)).empty() ? "  " : "⚯ ",
- rpc::call_command_value("d.ignore_commands", rpc::make_target(d)) == 0 ? "⚒ " : "◌ ",
- prios[d->priority() % 4],
- d->is_done() ? "✔ " : progress_style == 0 ? progress_str : progress[progress_style][
- item->file_list()->completed_chunks() * PROGRESS_STEPS
- / item->file_list()->size_chunks()],
- D_INFO(item)->down_rate()->rate() ?
- (D_INFO(item)->up_rate()->rate() ? "⇅ " : "↡ ") :
- (D_INFO(item)->up_rate()->rate() ? "↟ " : "  "),
- ying_yang_style == 0 ? ying_yang_str :
- ratio >= YING_YANG_STEPS * 1000 ? "⊛ " : ying_yang[ying_yang_style][ratio / 1000],
- has_msg ? has_alert ? alert : "♺ " : is_tagged ? "⚑ " : "  ",
- tracker ? num2(tracker->scrape_downloaded()).c_str() : "  ",
- tracker ? num2(tracker->scrape_complete()).c_str() : "  ",
- tracker ? num2(tracker->scrape_incomplete()).c_str() : "  ",
- human_size(D_INFO(item)->up_rate()->rate(), 2 | 8).c_str(),
- d->is_done() || !down_rate ? "" : " ",
- d->is_done() ? elapsed_time(get_custom_long(d, "tm_completed")).c_str() :
- !down_rate ? elapsed_time(get_custom_long(d, "tm_loaded")).c_str() :
- human_size(down_rate, 2 | 8).c_str(),
- human_size(item->file_list()->size_bytes(), 2).c_str(),
- displayname.empty() ? "" : " ",
- displayname.empty() ? buffer : displayname.c_str()
- );
-
- int x_scrape = 3 + 8*2 + 1; // lead, 8 status columns, gap
- int x_rate = x_scrape + 3*3; // skip 3 scrape columns
- int x_name = x_rate + 3*5 + 1; // skip 3 rate/size columns
- decorate_download_title(window, canvas, view, pos, range);
- canvas->set_attr(2, pos, x_name-2, attr_map[col_active + offset], col_active + offset);
- if (has_alert) canvas->set_attr(x_scrape-3, pos, 2, attr_map[ps::COL_ALARM + offset], ps::COL_ALARM + offset);
-
- // apply progress color to completion indicator
- int pcol = ratio_color(item->file_list()->completed_chunks() * 1000 / item->file_list()->size_chunks());
- canvas->set_attr(x_scrape-9, pos, 2, attr_map[pcol + offset], pcol + offset);
-
- // show ratio progress by color
- int rcol = ratio_color(ratio);
- canvas->set_attr(x_scrape-5, pos, 2, attr_map[rcol + offset], rcol + offset);
-
- // color up/down rates
- canvas->set_attr(x_rate+0, pos, 4, attr_map[ps::COL_SEEDING + offset], ps::COL_SEEDING + offset);
- if (d->is_done() || !down_rate) {
- // time display
- int tm_color = (d->is_done() ? ps::COL_SEEDING : ps::COL_INCOMPLETE) + offset;
- canvas->set_attr(x_rate+5+1, pos, 1, attr_map[tm_color], tm_color);
- canvas->set_attr(x_rate+5+4, pos, 1, attr_map[tm_color], tm_color);
- } else {
- // down rate
- canvas->set_attr(x_rate+5, pos, 5, attr_map[ps::COL_LEECHING + offset], ps::COL_LEECHING + offset);
- }
-
- // is this the item in focus?
- if (range.first == view->focus()) {
- canvas->set_attr(0, pos, 1, attr_map[ps::COL_FOCUS], ps::COL_FOCUS);
- }
-
- ++pos;
- ++range.first;
- }
-
- if (view->focus() != view->end_visible()) {
- char buffer[canvas->width() + 1];
- char* last = buffer + canvas->width() + 1;
-
- pos = canvas->height() - 2 - network_history_lines;
- print_download_info(buffer, last, *view->focus());
- canvas->print(3, pos, "%s", buffer);
- canvas->set_attr(0, pos, -1, attr_map[ps::COL_LABEL], ps::COL_LABEL);
- print_download_status(buffer, last, *view->focus());
- canvas->print(3, pos+1, "%s", buffer);
- canvas->set_attr(0, pos+1, -1, attr_map[ps::COL_LABEL], ps::COL_LABEL);
- }
-
- return true;
+ int is_tagged = rpc::commands.call_command_d("d.views.has", d, torrent::Object("tagged")).as_value() == 1;
+ uint32_t down_rate = D_INFO(item)->down_rate()->rate();
+
+ char progress_str[6] = "##";
+ char ying_yang_str[6] = "##";
+ if (progress_style == 0) {
+ sprintf(progress_str, item->file_list()->completed_chunks() ? "%2.2d" : "--",
+ item->file_list()->completed_chunks() * 100 / item->file_list()->size_chunks());
+ }
+ if (ying_yang_style == 0 && ratio < 9949) {
+ sprintf(ying_yang_str, ratio ? "%2.2d" : "--", ratio / 100);
+ }
+
+ canvas->print(0, pos, "%s %s%s%s%s %s %s %s %s %s%s ",
+ range.first == view->focus() ? "»" : " ",
+ d->is_done() ? "✔ " : progress_style == 0 ? progress_str : progress[progress_style][
+ item->file_list()->completed_chunks() * PROGRESS_STEPS
+ / item->file_list()->size_chunks()],
+ D_INFO(item)->down_rate()->rate() ?
+ (D_INFO(item)->up_rate()->rate() ? "⇅ " : "↡ ") :
+ (D_INFO(item)->up_rate()->rate() ? "↟ " : "  "),
+ ying_yang_style == 0 ? ying_yang_str :
+ ratio >= YING_YANG_STEPS * 1000 ? "⊛ " : ying_yang[ying_yang_style][ratio / 1000],
+ has_msg ? has_alert ? alert : "♺ " : is_tagged ? "⚑ " : "  ",
+ tracker ? num2(tracker->scrape_downloaded()).c_str() : "  ",
+ tracker ? num2(tracker->scrape_complete()).c_str() : "  ",
+ tracker ? num2(tracker->scrape_incomplete()).c_str() : "  ",
+ human_size(D_INFO(item)->up_rate()->rate(), 2 | 8).c_str(),
+ d->is_done() || !down_rate ? "" : " ",
+ d->is_done() ? elapsed_time(get_custom_long(d, "tm_completed")).c_str() :
+ !down_rate ? elapsed_time(get_custom_long(d, "tm_loaded")).c_str() :
+ human_size(down_rate, 2 | 8).c_str()
+ );
+
+ // Render custom columns
+ column = x_base;
+ int custom_len = render_columns(false, rpc::make_target(d), canvas, column, pos, column_defs);
+ canvas->set_attr(column, pos, custom_len, attr_map[ps::COL_DEFAULT], ps::COL_DEFAULT);
+ column += custom_len;
+ int x_name = column + 1;
+
+ // Render name
+ canvas->print(column, pos, " %s", u8_chop(
+ displayname.empty() ? d->info()->name() : displayname.c_str(),
+ canvas->width() - x_name - 1).c_str());
+
+ int x_scrape = 3 + 4*2 + 1; // lead, 4 status columns, gap
+ int x_rate = x_scrape + 3*3; // skip 3 scrape columns
+ //int x_name = x_rate + 3*5 + 1; // skip 3 rate/size columns
+ decorate_download_title(window, canvas, view, pos, range);
+ canvas->set_attr(2, pos, x_name-2, attr_map[col_active + offset], col_active + offset);
+ if (has_alert) canvas->set_attr(x_scrape-3, pos, 2, attr_map[ps::COL_ALARM + offset], ps::COL_ALARM + offset);
+
+ // apply progress color to completion indicator
+ int pcol = ratio_color(item->file_list()->completed_chunks() * 1000 / item->file_list()->size_chunks());
+ canvas->set_attr(x_scrape-9, pos, 2, attr_map[pcol + offset], pcol + offset);
+
+ // show ratio progress by color
+ int rcol = ratio_color(ratio);
+ canvas->set_attr(x_scrape-5, pos, 2, attr_map[rcol + offset], rcol + offset);
+
+ // color up/down rates
+ canvas->set_attr(x_rate+0, pos, 4, attr_map[ps::COL_SEEDING + offset], ps::COL_SEEDING + offset);
+ if (d->is_done() || !down_rate) {
+ // time display
+ int tm_color = (d->is_done() ? ps::COL_SEEDING : ps::COL_INCOMPLETE) + offset;
+ canvas->set_attr(x_rate+5+1, pos, 1, attr_map[tm_color], tm_color);
+ canvas->set_attr(x_rate+5+4, pos, 1, attr_map[tm_color], tm_color);
+ } else {
+ // down rate
+ canvas->set_attr(x_rate+5, pos, 5, attr_map[ps::COL_LEECHING + offset], ps::COL_LEECHING + offset);
+ }
+
+ // is this the item in focus?
+ if (range.first == view->focus()) {
+ canvas->set_attr(0, pos, 1, attr_map[ps::COL_FOCUS], ps::COL_FOCUS);
+ }
+
+ ++pos;
+ ++range.first;
+ }
+
+ if (view->focus() != view->end_visible()) {
+ char buffer[canvas->width() + 1];
+ char* last = buffer + canvas->width() + 1;
+
+ pos = canvas->height() - 2 - network_history_lines;
+ print_download_info(buffer, last, *view->focus());
+ canvas->print(3, pos, "%s", buffer);
+ canvas->set_attr(0, pos, -1, attr_map[ps::COL_LABEL], ps::COL_LABEL);
+ print_download_status(buffer, last, *view->focus());
+ canvas->print(3, pos+1, "%s", buffer);
+ canvas->set_attr(0, pos+1, -1, attr_map[ps::COL_LABEL], ps::COL_LABEL);
+ }
+
+ return true;
}
// patch hook for window title canvas redraw
void ui_pyroscope_statusbar_redraw(Window* window, display::Canvas* canvas) {
- canvas->set_attr(0, 0, -1, attr_map[ps::COL_FOOTER], ps::COL_FOOTER);
+ canvas->set_attr(0, 0, -1, attr_map[ps::COL_FOOTER], ps::COL_FOOTER);
}
@@ -671,95 +729,95 @@ void ui_pyroscope_statusbar_redraw(Window* window, display::Canvas* canvas) {
torrent::Object cmd_view_collapsed_toggle(const torrent::Object::string_type& args) {
- std::string view_name = args;
+ std::string view_name = args;
- if (view_name.empty()) {
- view_name = control->ui()->download_list()->current_view()->name();
- }
+ if (view_name.empty()) {
+ view_name = control->ui()->download_list()->current_view()->name();
+ }
- is_collapsed[view_name] = is_collapsed.find(view_name) == is_collapsed.end() ? true : !is_collapsed[view_name];
+ is_collapsed[view_name] = is_collapsed.find(view_name) == is_collapsed.end() ? true : !is_collapsed[view_name];
- return is_collapsed[view_name];
+ return is_collapsed[view_name];
}
// implementation of method we patched into rpc::object_storage
const torrent::Object& rpc::object_storage::set_color_string(const torrent::raw_string& key, const std::string& object) {
- const torrent::Object& result = rpc::object_storage::set_string(key, object);
- display::ui_pyroscope_colormap_init();
- return result;
+ const torrent::Object& result = rpc::object_storage::set_string(key, object);
+ display::ui_pyroscope_colormap_init();
+ return result;
}
// Traffic history
int network_history_depth_get() {
- return network_history_depth;
+ return network_history_depth;
}
torrent::Object network_history_depth_set(int arg) {
- if (network_history_depth) {
- delete[] network_history_up;
- delete[] network_history_down;
- network_history_up = network_history_down = 0;
- }
+ if (network_history_depth) {
+ delete[] network_history_up;
+ delete[] network_history_down;
+ network_history_up = network_history_down = 0;
+ }
- network_history_depth = arg;
- network_history_count = 0;
+ network_history_depth = arg;
+ network_history_count = 0;
- if (network_history_depth) {
- network_history_up = new uint32_t[network_history_depth];
- network_history_down = new uint32_t[network_history_depth];
- }
+ if (network_history_depth) {
+ network_history_up = new uint32_t[network_history_depth];
+ network_history_down = new uint32_t[network_history_depth];
+ }
- return torrent::Object();
+ return torrent::Object();
}
void network_history_format(std::string& buf, char kind, uint32_t* data) {
- uint32_t samples = std::min(network_history_count, (uint32_t) network_history_depth);
- uint32_t min_rate = *std::min_element(data, data + samples);
- uint32_t max_rate = *std::max_element(data, data + samples);
- char buffer[80];
-
- snprintf(buffer, sizeof(buffer), "%c ⌈%s⌉⌊%s⌋%s", kind,
- display::human_size(max_rate, 0).c_str(), display::human_size(min_rate, 0).c_str(),
- rpc::call_command_value("network.history.auto_scale") ? "↨ " : " ");
- buf = buffer;
-
- if (max_rate > 102) {
- const char* meter[] = {"⠀", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"};
- uint32_t base = rpc::call_command_value("network.history.auto_scale") ? min_rate : 0;
- for (int i = 1; i <= samples; ++i) {
- uint32_t idx = (network_history_count - i) % network_history_depth;
- if (max_rate > base)
- buf += meter[std::min(8U, (data[idx] - base) * 9 / (max_rate - base))];
- else
- buf += " ";
- }
- }
- buf += " ";
+ uint32_t samples = std::min(network_history_count, (uint32_t) network_history_depth);
+ uint32_t min_rate = *std::min_element(data, data + samples);
+ uint32_t max_rate = *std::max_element(data, data + samples);
+ char buffer[80];
+
+ snprintf(buffer, sizeof(buffer), "%c ⌈%s⌉⌊%s⌋%s", kind,
+ display::human_size(max_rate, 0).c_str(), display::human_size(min_rate, 0).c_str(),
+ rpc::call_command_value("network.history.auto_scale") ? "↨ " : " ");
+ buf = buffer;
+
+ if (max_rate > 102) {
+ const char* meter[] = {"⠀", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"};
+ uint32_t base = rpc::call_command_value("network.history.auto_scale") ? min_rate : 0;
+ for (int i = 1; i <= samples; ++i) {
+ uint32_t idx = (network_history_count - i) % network_history_depth;
+ if (max_rate > base)
+ buf += meter[std::min(8U, (data[idx] - base) * 9 / (max_rate - base))];
+ else
+ buf += " ";
+ }
+ }
+ buf += " ";
}
// You MUST call this after changing the auto_scale flag, to see any changes immediately!
torrent::Object network_history_refresh() {
- if (network_history_depth) {
- network_history_format(network_history_up_str, 'U', network_history_up);
- network_history_format(network_history_down_str, 'D', network_history_down);
- }
+ if (network_history_depth) {
+ network_history_format(network_history_up_str, 'U', network_history_up);
+ network_history_format(network_history_down_str, 'D', network_history_down);
+ }
- return torrent::Object();
+ return torrent::Object();
}
torrent::Object network_history_sample() {
- if (network_history_depth) {
- network_history_up[network_history_count % network_history_depth] = torrent::up_rate()->rate();
- network_history_down[network_history_count % network_history_depth] = torrent::down_rate()->rate();
- ++network_history_count;
- }
+ if (network_history_depth) {
+ network_history_up[network_history_count % network_history_depth] = torrent::up_rate()->rate();
+ network_history_down[network_history_count % network_history_depth] = torrent::down_rate()->rate();
+ ++network_history_count;
+ }
- return network_history_refresh();
+ return network_history_refresh();
}
@@ -790,55 +848,82 @@ torrent::Object cmd_trackers_alias_items(rpc::target_type target) {
}
+torrent::Object apply_human_size(const torrent::Object::list_type& args) {
+ if (args.size() != 1 && args.size() != 2)
+ throw torrent::input_error("convert.human_size takes 1 or 2 arguments!");
+
+ torrent::Object::value_type bytes = args.front().as_value();
+ torrent::Object::value_type format = args.size() > 1 ? args.back().as_value() : 2;
+
+ return display::human_size(bytes, format);
+}
+
+
+torrent::Object apply_magnitude(const torrent::Object::list_type& args) {
+ if (args.size() != 1)
+ throw torrent::input_error("convert.magnitude takes 1 value argument!");
+
+ return num2(args.front().as_value());
+}
+
+
// register our commands
void initialize_command_ui_pyroscope() {
- #define PS_VARIABLE_COLOR(key, value) \
- control->object_storage()->insert_c_str(key, value, rpc::object_storage::flag_string_type); \
- CMD2_ANY(key, _cxxstd_::bind(&rpc::object_storage::get, control->object_storage(), \
- torrent::raw_string::from_c_str(key))); \
- CMD2_ANY_STRING(key ".set", _cxxstd_::bind(&rpc::object_storage::set_color_string, control->object_storage(), \
- torrent::raw_string::from_c_str(key), _cxxstd_::placeholders::_2));
-
- #define PS_CMD_ANY_FUN(key, func) \
- CMD2_ANY(key, _cxxstd_::bind(&func))
-
- CMD2_ANY ("network.history.depth", _cxxstd_::bind(&network_history_depth_get));
- CMD2_ANY_VALUE_V("network.history.depth.set", _cxxstd_::bind(&network_history_depth_set, _cxxstd_::placeholders::_2));
- CMD2_ANY ("network.history.refresh", _cxxstd_::bind(&network_history_refresh));
- CMD2_ANY ("network.history.sample", _cxxstd_::bind(&network_history_sample));
- CMD2_VAR_BOOL ("network.history.auto_scale", true);
-
- CMD2_ANY_STRING("view.collapsed.toggle", _cxxstd_::bind(&cmd_view_collapsed_toggle, _cxxstd_::placeholders::_2));
-
- CMD2_ANY_LIST("trackers.alias.set_key", &cmd_trackers_alias_set_key);
- CMD2_ANY("trackers.alias.items", _cxxstd_::bind(&cmd_trackers_alias_items, _cxxstd_::placeholders::_1));
-
- CMD2_VAR_VALUE("ui.style.progress", 1);
- CMD2_VAR_VALUE("ui.style.ratio", 1);
-
- PS_VARIABLE_COLOR("ui.color.progress0", "red");
- PS_VARIABLE_COLOR("ui.color.progress20", "bold bright red");
- PS_VARIABLE_COLOR("ui.color.progress40", "bold bright magenta");
- PS_VARIABLE_COLOR("ui.color.progress60", "yellow");
- PS_VARIABLE_COLOR("ui.color.progress80", "bold bright yellow");
- PS_VARIABLE_COLOR("ui.color.progress100", "green");
- PS_VARIABLE_COLOR("ui.color.progress120", "bold bright green");
- PS_VARIABLE_COLOR("ui.color.complete", "bright green");
- PS_VARIABLE_COLOR("ui.color.seeding", "bold bright green");
- PS_VARIABLE_COLOR("ui.color.stopped", "blue");
- PS_VARIABLE_COLOR("ui.color.queued", "magenta");
- PS_VARIABLE_COLOR("ui.color.incomplete", "yellow");
- PS_VARIABLE_COLOR("ui.color.leeching", "bold bright yellow");
- PS_VARIABLE_COLOR("ui.color.alarm", "bold white on red");
- PS_VARIABLE_COLOR("ui.color.title", "bold bright white on blue");
- PS_VARIABLE_COLOR("ui.color.footer", "bold bright cyan on blue");
- PS_VARIABLE_COLOR("ui.color.label", "gray");
- PS_VARIABLE_COLOR("ui.color.odd", "");
- PS_VARIABLE_COLOR("ui.color.even", "");
- PS_VARIABLE_COLOR("ui.color.info", "white");
- PS_VARIABLE_COLOR("ui.color.focus", "reverse");
-
- PS_CMD_ANY_FUN("system.colors.max", display::get_colors);
- PS_CMD_ANY_FUN("system.colors.enabled", has_colors);
- PS_CMD_ANY_FUN("system.colors.rgb", can_change_color);
+ #define PS_VARIABLE_COLOR(key, value) \
+ control->object_storage()->insert_c_str(key, value, rpc::object_storage::flag_string_type); \
+ CMD2_ANY(key, _cxxstd_::bind(&rpc::object_storage::get, control->object_storage(), \
+ torrent::raw_string::from_c_str(key))); \
+ CMD2_ANY_STRING(key ".set", _cxxstd_::bind(&rpc::object_storage::set_color_string, control->object_storage(), \
+ torrent::raw_string::from_c_str(key), _cxxstd_::placeholders::_2));
+
+ #define PS_CMD_ANY_FUN(key, func) \
+ CMD2_ANY(key, _cxxstd_::bind(&func))
+
+ CMD2_ANY ("network.history.depth", _cxxstd_::bind(&network_history_depth_get));
+ CMD2_ANY_VALUE_V("network.history.depth.set", _cxxstd_::bind(&network_history_depth_set, _cxxstd_::placeholders::_2));
+ CMD2_ANY ("network.history.refresh", _cxxstd_::bind(&network_history_refresh));
+ CMD2_ANY ("network.history.sample", _cxxstd_::bind(&network_history_sample));
+ CMD2_VAR_BOOL ("network.history.auto_scale", true);
+
+ CMD2_ANY_STRING("view.collapsed.toggle", _cxxstd_::bind(&cmd_view_collapsed_toggle, _cxxstd_::placeholders::_2));
+
+ CMD2_ANY_LIST("trackers.alias.set_key", &cmd_trackers_alias_set_key);
+ CMD2_ANY("trackers.alias.items", _cxxstd_::bind(&cmd_trackers_alias_items, _cxxstd_::placeholders::_1));
+
+ CMD2_VAR_VALUE("ui.style.progress", 1);
+ CMD2_VAR_VALUE("ui.style.ratio", 1);
+
+ PS_VARIABLE_COLOR("ui.color.progress0", "red");
+ PS_VARIABLE_COLOR("ui.color.progress20", "bold bright red");
+ PS_VARIABLE_COLOR("ui.color.progress40", "bold bright magenta");
+ PS_VARIABLE_COLOR("ui.color.progress60", "yellow");
+ PS_VARIABLE_COLOR("ui.color.progress80", "bold bright yellow");
+ PS_VARIABLE_COLOR("ui.color.progress100", "green");
+ PS_VARIABLE_COLOR("ui.color.progress120", "bold bright green");
+ PS_VARIABLE_COLOR("ui.color.complete", "bright green");
+ PS_VARIABLE_COLOR("ui.color.seeding", "bold bright green");
+ PS_VARIABLE_COLOR("ui.color.stopped", "blue");
+ PS_VARIABLE_COLOR("ui.color.queued", "magenta");
+ PS_VARIABLE_COLOR("ui.color.incomplete", "yellow");
+ PS_VARIABLE_COLOR("ui.color.leeching", "bold bright yellow");
+ PS_VARIABLE_COLOR("ui.color.alarm", "bold white on red");
+ PS_VARIABLE_COLOR("ui.color.title", "bold bright white on blue");
+ PS_VARIABLE_COLOR("ui.color.footer", "bold bright cyan on blue");
+ PS_VARIABLE_COLOR("ui.color.label", "gray");
+ PS_VARIABLE_COLOR("ui.color.odd", "");
+ PS_VARIABLE_COLOR("ui.color.even", "");
+ PS_VARIABLE_COLOR("ui.color.info", "white");
+ PS_VARIABLE_COLOR("ui.color.focus", "reverse");
+
+ PS_CMD_ANY_FUN("system.colors.max", display::get_colors);
+ PS_CMD_ANY_FUN("system.colors.enabled", has_colors);
+ PS_CMD_ANY_FUN("system.colors.rgb", can_change_color);
+
+ CMD2_ANY_LIST("convert.human_size", _cxxstd_::bind(&apply_human_size, _cxxstd_::placeholders::_2));
+ CMD2_ANY_LIST("convert.magnitude", _cxxstd_::bind(&apply_magnitude, _cxxstd_::placeholders::_2));
+
+ rpc::parse_command_multiple
+ (rpc::make_target(),
+ "method.insert = ui.column.render, multi|rlookup|static\n"
+ );
}