diff --git a/src/clock.cpp b/src/clock.cpp index 13762a47..f06b873e 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -58,7 +58,7 @@ Clock::Clock() Width = Config.clock_display_seconds ? 60 : 40; m_pane = NC::Window(0, MainStartY, COLS, MainHeight, "", Config.main_color, NC::Border::None); - w = NC::Window((COLS-Width)/2, (MainHeight-Height)/2+MainStartY, Width, Height-1, "", Config.main_color, NC::Border(Config.main_color)); + w = NC::Window((COLS-Width)/2, (MainHeight-Height)/2+MainStartY, Width, Height-1, "", Config.main_color, NC::Border(Config.main_color.foreground())); } void Clock::resize() @@ -129,9 +129,9 @@ void Clock::update() char buf[64]; std::strftime(buf, 64, "%x", &time); - attron(COLOR_PAIR(int(Config.main_color))); + color_set(Config.main_color.pairNumber(), nullptr); mvprintw(w.getStarty()+w.getHeight(), w.getStartX()+(w.getWidth()-strlen(buf))/2, "%s", buf); - attroff(COLOR_PAIR(int(Config.main_color))); + standend(); refresh(); for (int k = 0; k < 6; ++k) diff --git a/src/display.cpp b/src/display.cpp index 25f1b8cc..ac31c720 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -124,7 +124,7 @@ void showSongs(NC::Menu &menu, const MPD::Song &s, const ProxySongList &pl, c else if (isdigit(*it)) // color { if (!discard_colors) - menu << NC::Color(*it-'0'); + menu << charToColor(*it); } else if (*it == 'R') // right align { diff --git a/src/helpers.h b/src/helpers.h index 10925c15..945facb4 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -27,6 +27,7 @@ #include "settings.h" #include "status.h" #include "utility/string.h" +#include "utility/type_conversions.h" #include "utility/wide_string.h" template @@ -344,7 +345,7 @@ void stringToBuffer(Iterator first, Iterator last, NC::BasicBuffer::Menu(size_t startx, const std::string &title, Color color, Border border) - : Window(startx, starty, width, height, title, color, border), + : Window(startx, starty, width, height, title, std::move(color), border), m_item_displayer(0), m_beginning(0), m_highlight(0), diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index b9dd3b24..862bf5f8 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -107,7 +107,7 @@ int main(int argc, char **argv) cerr_buffer = std::cerr.rdbuf(); std::cerr.rdbuf(errorlog.rdbuf()); - NC::initScreen("ncmpcpp ver. " VERSION, Config.colors_enabled); + NC::initScreen(Config.colors_enabled); Actions::OriginalStatusbarVisibility = Config.statusbar_visibility; diff --git a/src/screen.cpp b/src/screen.cpp index f370d9e8..73122b7f 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -30,9 +30,9 @@ using Global::myInactiveScreen; void drawSeparator(int x) { - attron(COLOR_PAIR(int(Config.main_color))); + color_set(Config.main_color.pairNumber(), nullptr); mvvline(Global::MainStartY, x, 0, Global::MainHeight); - attroff(COLOR_PAIR(int(Config.main_color))); + standend(); refresh(); } diff --git a/src/scrollpad.cpp b/src/scrollpad.cpp index 064b03fa..7ae099e6 100644 --- a/src/scrollpad.cpp +++ b/src/scrollpad.cpp @@ -130,7 +130,7 @@ void Scrollpad::clear() delwin(m_window); m_window = newpad(m_height, m_width); setTimeout(m_window_timeout); - setColor(m_color, m_bg_color); + setColor(m_color); keypad(m_window, 1); } @@ -276,7 +276,7 @@ void Scrollpad::reset() bool Scrollpad::setProperties(Color begin, const std::string &s, Color end, size_t id, boost::regex::flag_type flags) { - return regexSearch(m_buffer, begin, s, end, id, flags); + return regexSearch(m_buffer, std::move(begin), s, std::move(end), id, flags); } bool Scrollpad::setProperties(Format begin, const std::string &s, Format end, size_t id, boost::regex::flag_type flags) diff --git a/src/status.cpp b/src/status.cpp index befc26ea..17ce036c 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -671,19 +671,18 @@ void Status::Changes::flags() // this is done by raw ncurses because creating another // window only for handling this is quite silly - attrset(A_BOLD|COLOR_PAIR(int(Config.state_line_color))); + attrset(A_BOLD); + color_set(Config.state_line_color.pairNumber(), nullptr); mvhline(1, 0, 0, COLS); if (!switch_state.empty()) { mvprintw(1, COLS-switch_state.length()-3, "["); - attroff(COLOR_PAIR(int(Config.state_line_color))); - attron(COLOR_PAIR(int(Config.state_flags_color))); + color_set(Config.state_flags_color.pairNumber(), nullptr); mvprintw(1, COLS-switch_state.length()-2, "%s", switch_state.c_str()); - attroff(COLOR_PAIR(int(Config.state_flags_color))); - attron(COLOR_PAIR(int(Config.state_line_color))); + color_set(Config.state_line_color.pairNumber(), nullptr); mvprintw(1, COLS-2, "]"); } - attrset(0); + standend(); refresh(); break; case Design::Alternative: diff --git a/src/strbuffer.h b/src/strbuffer.h index b9d54891..2db181e1 100644 --- a/src/strbuffer.h +++ b/src/strbuffer.h @@ -36,7 +36,7 @@ template class BasicBuffer enum class Type { Color, Format }; Property(size_t position_, NC::Color color_, int id_) - : m_type(Type::Color), m_position(position_), m_color(color_), m_id(id_) { } + : m_type(Type::Color), m_position(position_), m_color(std::move(color_)), m_id(id_) { } Property(size_t position_, NC::Format format_, int id_) : m_type(Type::Format), m_position(position_), m_format(format_), m_id(id_) { } @@ -94,15 +94,15 @@ public: const Properties &properties() const { return m_properties; } template - void setProperty(size_t position, PropertyT property, size_t id = -1) + void setProperty(size_t position, PropertyT &&property, size_t id = -1) { - m_properties.insert(Property(position, property, id)); + m_properties.insert(Property(position, std::forward(property), id)); } template - bool removeProperty(size_t position, PropertyT property, size_t id = -1) + bool removeProperty(size_t position, PropertyT &&property, size_t id = -1) { - auto it = m_properties.find(Property(position, property, id)); + auto it = m_properties.find(Property(position, std::forward(property), id)); bool found = it != m_properties.end(); if (found) m_properties.erase(it); diff --git a/src/utility/type_conversions.cpp b/src/utility/type_conversions.cpp index c515afee..8e922bad 100644 --- a/src/utility/type_conversions.cpp +++ b/src/utility/type_conversions.cpp @@ -21,6 +21,35 @@ #include #include "utility/type_conversions.h" +NC::Color charToColor(char c) +{ + switch (c) + { + case '0': + return NC::Color::Default; + case '1': + return NC::Color::Black; + case '2': + return NC::Color::Red; + case '3': + return NC::Color::Green; + case '4': + return NC::Color::Yellow; + case '5': + return NC::Color::Blue; + case '6': + return NC::Color::Magenta; + case '7': + return NC::Color::Cyan; + case '8': + return NC::Color::White; + case '9': + return NC::Color::End; + default: + throw std::runtime_error("invalid character"); + } +} + NC::Color stringToColor(const std::string &color) { NC::Color result = NC::Color::Default; diff --git a/src/utility/type_conversions.h b/src/utility/type_conversions.h index 3a7e4909..7f072a5a 100644 --- a/src/utility/type_conversions.h +++ b/src/utility/type_conversions.h @@ -26,6 +26,8 @@ #include "window.h" #include "enums.h" +NC::Color charToColor(char c); + NC::Color stringToColor(const std::string &color); NC::Border stringToBorder(const std::string &border); diff --git a/src/visualizer.cpp b/src/visualizer.cpp index 1e6d6850..3904c89e 100644 --- a/src/visualizer.cpp +++ b/src/visualizer.cpp @@ -51,7 +51,7 @@ const int fps = 25; // toColor: a scaling function for coloring. For numbers 0 to max this function returns // a coloring from the lowest color to the highest, and colors will not loop from 0 to max. -NC::Color toColor(size_t number, size_t max, bool wrap = true) +const NC::Color &toColor(size_t number, size_t max, bool wrap = true) { const auto colors_size = Config.visualizer_colors.size(); const auto index = (number * colors_size) / max; diff --git a/src/window.cpp b/src/window.cpp index 6f730d94..5ddfd741 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -38,6 +38,7 @@ #include "window.h" namespace { + namespace rl { bool aborted; @@ -194,43 +195,43 @@ int add_base() } } -namespace NC {// +namespace NC { -std::ostream &operator<<(std::ostream &os, Color c) +Color Color::Default(0, -1, true, false); +Color Color::Black(COLOR_BLACK, -1); +Color Color::Red(COLOR_RED, -1); +Color Color::Green(COLOR_GREEN, -1); +Color Color::Yellow(COLOR_YELLOW, -1); +Color Color::Blue(COLOR_BLUE, -1); +Color Color::Magenta(COLOR_MAGENTA, -1); +Color Color::Cyan(COLOR_CYAN, -1); +Color Color::White(COLOR_WHITE, -1); +Color Color::End(0, 0, false, true); + +std::ostream &operator<<(std::ostream &os, const Color &c) { - switch (c) - { - case Color::Default: - os << "default"; - break; - case Color::Black: - os << "black"; - break; - case Color::Red: - os << "red"; - break; - case Color::Green: - os << "green"; - break; - case Color::Yellow: - os << "yellow"; - break; - case Color::Blue: - os << "blue"; - break; - case Color::Magenta: - os << "magenta"; - break; - case Color::Cyan: - os << "cyan"; - break; - case Color::White: - os << "white"; - break; - case Color::End: - os << "color_end"; - break; - } + if (c.isDefault()) + os << "default"; + else if (c == Color::Black) + os << "black"; + else if (c == Color::Red) + os << "red"; + else if (c == Color::Green) + os << "green"; + else if (c == Color::Yellow) + os << "yellow"; + else if (c == Color::Blue) + os << "blue"; + else if (c == Color::Magenta) + os << "magenta"; + else if (c == Color::Cyan) + os << "cyan"; + else if (c == Color::White) + os << "white"; + else if (c.isEnd()) + os << "color_end"; + else + os << "color_" << c.foreground() << "_" << c.background(); return os; } @@ -386,31 +387,19 @@ std::ostream &operator<<(std::ostream &os, Scroll s) return os; } -void initScreen(GNUC_UNUSED const char *window_title, bool enable_colors) +void initScreen(bool enable_colors) { - const int ColorsTable[] = - { - COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, - COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE - }; -# ifdef XCURSES - Xinitscr(1, const_cast(&window_title)); -# else initscr(); -# endif // XCURSES if (has_colors() && enable_colors) { start_color(); use_default_colors(); - int num = 1; -# ifdef USE_PDCURSES - int i = 0; -# else - int i = -1; -# endif // USE_PDCURSES - for (; i < 8; ++i) - for (int j = 0; j < 8; ++j) - init_pair(num++, ColorsTable[j], i < 0 ? i : ColorsTable[i]); + int npair = 1; + for (int bg = -1; bg < COLORS; ++bg) + { + for (int fg = 0; npair < COLOR_PAIRS && fg < COLORS; ++fg, ++npair) + init_pair(npair, fg, bg); + } } raw(); nonl(); @@ -450,6 +439,22 @@ void destroyScreen() endwin(); } +int Color::pairNumber() const +{ + int result; + if (isDefault()) + result = 0; + else if (isEnd()) + throw std::logic_error("'end' doesn't have a corresponding pair number"); + else + { + result = background(); + result *= COLORS; + result += foreground(); + } + return result; +} + Window::Window(size_t startx, size_t starty, size_t width, @@ -465,9 +470,7 @@ Window::Window(size_t startx, m_height(height), m_window_timeout(-1), m_color(color), - m_bg_color(Color::Default), m_base_color(color), - m_base_bg_color(Color::Default), m_border(border), m_prompt_hook(0), m_title(title), @@ -513,9 +516,7 @@ Window::Window(const Window &rhs) , m_height(rhs.m_height) , m_window_timeout(rhs.m_window_timeout) , m_color(rhs.m_color) -, m_bg_color(rhs.m_bg_color) , m_base_color(rhs.m_base_color) -, m_base_bg_color(rhs.m_base_bg_color) , m_border(rhs.m_border) , m_prompt_hook(rhs.m_prompt_hook) , m_title(rhs.m_title) @@ -538,9 +539,7 @@ Window::Window(Window &&rhs) , m_height(rhs.m_height) , m_window_timeout(rhs.m_window_timeout) , m_color(rhs.m_color) -, m_bg_color(rhs.m_bg_color) , m_base_color(rhs.m_base_color) -, m_base_bg_color(rhs.m_base_bg_color) , m_border(rhs.m_border) , m_prompt_hook(rhs.m_prompt_hook) , m_title(std::move(rhs.m_title)) @@ -566,9 +565,7 @@ Window &Window::operator=(Window rhs) std::swap(m_height, rhs.m_height); std::swap(m_window_timeout, rhs.m_window_timeout); std::swap(m_color, rhs.m_color); - std::swap(m_bg_color, rhs.m_bg_color); std::swap(m_base_color, rhs.m_base_color); - std::swap(m_base_bg_color, rhs.m_base_bg_color); std::swap(m_border, rhs.m_border); std::swap(m_prompt_hook, rhs.m_prompt_hook); std::swap(m_title, rhs.m_title); @@ -588,23 +585,20 @@ Window::~Window() delwin(m_border_window); } -void Window::setColor(Color fg, Color bg) +void Window::setColor(Color c) { - if (fg == Color::Default) - fg = m_base_color; - - if (fg != Color::Default) - wattron(m_window, COLOR_PAIR(int(bg)*8+int(fg))); + if (c.isDefault()) + c = m_base_color; + if (c != Color::Default) + wcolor_set(m_window, c.pairNumber(), nullptr); else - wattroff(m_window, COLOR_PAIR(int(m_color))); - m_color = fg; - m_bg_color = bg; + wcolor_set(m_window, m_base_color.pairNumber(), nullptr); + m_color = std::move(c); } -void Window::setBaseColor(Color fg, Color bg) +void Window::setBaseColor(Color c) { - m_base_color = fg; - m_base_bg_color = bg; + m_base_color = std::move(c); } void Window::setBorder(Border border) @@ -663,7 +657,7 @@ void Window::recreate(size_t width, size_t height) delwin(m_window); m_window = newpad(height, width); setTimeout(m_window_timeout); - setColor(m_color, m_bg_color); + setColor(m_color); keypad(m_window, 1); } @@ -712,7 +706,7 @@ void Window::refreshBorder() const if (m_border != Border::None) attron(COLOR_PAIR(int(m_border))); else - attron(COLOR_PAIR(int(m_base_color))); + wcolor_set(m_border_window, m_base_color.pairNumber(), nullptr); mvhline(m_start_y-1, m_start_x, 0, m_width); attron(A_BOLD); mvhline(m_start_y-2, m_start_x, 32, m_width); // clear title line @@ -961,7 +955,7 @@ const std::string &Window::getTitle() const return m_title; } -Color Window::getColor() const +const Color &Window::getColor() const { return m_color; } @@ -1002,43 +996,27 @@ void Window::scroll(Scroll where) } -Window &Window::operator<<(Colors colors) +Window &Window::operator<<(const Color &c) { - if (colors.fg == Color::End || colors.bg == Color::End) + if (c.isDefault()) { - *this << Color::End; - return *this; + while (!m_color_stack.empty()) + m_color_stack.pop(); + setColor(m_base_color); } - m_color_stack.push(colors); - setColor(colors.fg, colors.bg); - return *this; -} - -Window &Window::operator<<(Color color) -{ - switch (color) + else if (c.isEnd()) { - case Color::Default: - while (!m_color_stack.empty()) - m_color_stack.pop(); - setColor(m_base_color, m_base_bg_color); - break; - case Color::End: - if (!m_color_stack.empty()) - m_color_stack.pop(); - if (!m_color_stack.empty()) - setColor(m_color_stack.top().fg, m_color_stack.top().bg); - else - setColor(m_base_color, m_base_bg_color); - break; - default: - Color bg; - if (m_color_stack.empty()) - bg = m_bg_color; - else - bg = m_color_stack.top().bg; - m_color_stack.push(Colors(color, bg)); - setColor(m_color_stack.top().fg, m_color_stack.top().bg); + if (!m_color_stack.empty()) + m_color_stack.pop(); + if (!m_color_stack.empty()) + setColor(m_color_stack.top()); + else + setColor(m_base_color); + } + else + { + setColor(c); + m_color_stack.push(c); } return *this; } @@ -1090,7 +1068,7 @@ Window &Window::operator<<(int (*f)(WINDOW *)) return *this; } -Window &Window::operator<<(XY coords) +Window &Window::operator<<(const XY &coords) { goToXY(coords.x, coords.y); return *this; diff --git a/src/window.h b/src/window.h index 87b753b4..f32ce8ad 100644 --- a/src/window.h +++ b/src/window.h @@ -35,6 +35,7 @@ #include #include #include +#include #include // define some Ctrl-? keys @@ -125,11 +126,66 @@ private: std::string m_prompt; }; -/// Colors used by NCurses -enum class Color { Default, Black, Red, Green, Yellow, Blue, Magenta, Cyan, White, End }; +struct Color +{ + Color() : m_rep(0, -1, true, false) { } + Color(short foregound_value, short background_value = -1, + bool is_default = false, bool is_end = false) + : m_rep(foregound_value + 1, background_value + 1, is_default, is_end) + { + if (isDefault() && isEnd()) + throw std::logic_error("Color flag can't be marked as both 'default' and 'end'"); + } -std::ostream &operator<<(std::ostream &os, Color c); -std::istream &operator>>(std::istream &is, Color &c); + bool operator==(const Color &rhs) const + { + return m_rep == rhs.m_rep; + } + bool operator!=(const Color &rhs) const + { + return m_rep != rhs.m_rep; + } + bool operator<(const Color &rhs) const + { + return m_rep < rhs.m_rep; + } + + short foreground() const + { + return std::get<0>(m_rep); + } + short background() const + { + return std::get<1>(m_rep); + } + bool isDefault() const + { + return std::get<2>(m_rep); + } + bool isEnd() const + { + return std::get<3>(m_rep); + } + + int pairNumber() const; + + static Color Default; + static Color Black; + static Color Red; + static Color Green; + static Color Yellow; + static Color Blue; + static Color Magenta; + static Color Cyan; + static Color White; + static Color End; + +private: + std::tuple m_rep; +}; + +std::ostream &operator<<(std::ostream &os, const Color &c); +std::istream &operator>>(std::istream &is, Color &f); /// Format flags used by NCurses enum class Format { @@ -154,22 +210,12 @@ enum class Scroll { Up, Down, PageUp, PageDown, Home, End }; std::ostream &operator<<(std::ostream &os, Scroll s); /// Initializes curses screen and sets some additional attributes -/// @param window_title title of the window (has an effect only if pdcurses lib is used) /// @param enable_colors enables colors -void initScreen(const char *window_title, bool enable_colors); +void initScreen(bool enable_colors); /// Destroys the screen void destroyScreen(); -/// Struct used to set color of both foreground and background of window -/// @see Window::operator<<() -struct Colors -{ - Colors(Color one, Color two = Color::Default) : fg(one), bg(two) { } - Color fg; - Color bg; -}; - /// Struct used for going to given coordinates /// @see Window::operator<<() struct XY @@ -244,7 +290,7 @@ struct Window const std::string &getTitle() const; /// @return current window's color - Color getColor() const; + const Color &getColor() const; /// @return current window's border Border getBorder() const; @@ -301,7 +347,7 @@ struct Window /// Sets window's base color /// @param fg foregound base color /// @param bg background base color - void setBaseColor(Color fg, Color bg = Color::Default); + void setBaseColor(Color c); /// Sets window's border /// @param border new window's border @@ -365,89 +411,25 @@ struct Window virtual void scroll(Scroll where); /// Applies function of compatible prototype to internal WINDOW pointer - /// The mostly used function in this case seem to be wclrtoeol(), which - /// clears the window from current cursor position to the end of line. - /// Note that delwin() also matches that prototype, but I wouldn't - /// recommend anyone passing this pointer here ;) - /// @param f pointer to function to call with internal WINDOW pointer - /// @return reference to itself Window &operator<<(int (*f)(WINDOW *)); - - /// Applies foreground and background colors to window - /// @param colors struct that holds new colors information - /// @return reference to itself - Window &operator<<(Colors colors); - - /// Applies foregound color to window. Note that colors applied - /// that way are stacked, i.e if you applied Color::Red, then Color::Green - /// and Color::End, current color would be Color::Red. If you want to discard - /// all colors and fall back to base one, pass Color::Default. - /// @param color new color value - /// @return reference to itself - Window &operator<<(Color color); - - /// Applies format flag to window. Note that these attributes are - /// also stacked, so if you applied Format::Bold twice, to get rid of - /// it you have to pass Format::NoBold also twice. - /// @param format format flag - /// @return reference to itself + Window &operator<<(const Color &color); Window &operator<<(Format format); - - /// Moves current cursor position to given coordinates. - /// @param coords struct that holds information about new coordinations - /// @return reference to itself - Window &operator<<(XY coords); - - /// Prints string to window - /// @param s const pointer to char array to be printed - /// @return reference to itself + Window &operator<<(const XY &coords); Window &operator<<(const char *s); - - /// Prints single character to window - /// @param c character to be printed - /// @return reference to itself Window &operator<<(char c); - - /// Prints wide string to window - /// @param ws const pointer to wchar_t array to be printed - /// @return reference to itself Window &operator<<(const wchar_t *ws); - - /// Prints single wide character to window - /// @param wc wide character to be printed - /// @return reference to itself Window &operator<<(wchar_t wc); - - /// Prints int to window - /// @param i integer value to be printed - /// @return reference to itself Window &operator<<(int i); - - /// Prints double to window - /// @param d double value to be printed - /// @return reference to itself Window &operator<<(double d); - - /// Prints size_t to window - /// @param s size value to be printed - /// @return reference to itself Window &operator<<(size_t s); - - /// Prints std::string to window - /// @param s string to be printed - /// @return reference to itself Window &operator<<(const std::string &s); - - /// Prints std::wstring to window - /// @param ws wide string to be printed - /// @return reference to itself Window &operator<<(const std::wstring &ws); protected: /// Sets colors of window (interal use only) /// @param fg foregound color /// @param bg background color /// - void setColor(Color fg, Color bg = Color::Default); + void setColor(Color c); /// Refreshes window's border /// @@ -485,11 +467,9 @@ protected: /// current colors Color m_color; - Color m_bg_color; /// base colors Color m_base_color; - Color m_base_bg_color; /// current border Border m_border; @@ -524,7 +504,7 @@ private: std::string m_title; /// stack of colors - std::stack m_color_stack; + std::stack m_color_stack; /// input queue of a window. you can put characters there using /// PushChar and they will be immediately consumed and