/** * Copyright (c) 2007-2012, Timothy Stack * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Timothy Stack nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @file statusview_curses.hh */ #ifndef __statusview_curses_hh #define __statusview_curses_hh #include #include #include "view_curses.hh" #include "ansi_scrubber.hh" /** * Container for individual status values. */ class status_field { public: /** * @param width The maximum width of the field in characters. * @param role The color role for this field, defaults to VCR_STATUS. */ status_field(int width = 1, view_colors::role_t role = view_colors::VCR_STATUS) : sf_width(width), sf_min_width(0), sf_right_justify(false), sf_cylon(false), sf_cylon_pos(0), sf_role(role), sf_share(0), sf_left_pad(0) { }; virtual ~status_field() { }; /** @param value The new value for this field. */ void set_value(std::string value) { string_attrs_t &sa = this->sf_value.get_attrs(); sa.clear(); scrub_ansi_string(value, sa); if (value.size() > this->get_width()) { if (value.size() <= 11) { value.resize(11); } else { static const std::string MIDSUB = " .. "; size_t half_width = this->get_width() / 2 - MIDSUB.size() / 2; std::string abbrev; abbrev.append(value, 0, half_width); abbrev.append(MIDSUB); abbrev.append(value, value.size() - half_width, std::string::npos); value = abbrev; } } if (this->sf_right_justify) { int padding = this->sf_width - value.size(); if (padding > 2) { value.insert(0, padding, ' '); } } this->sf_value.with_string(value); if (this->sf_cylon) { struct line_range lr(this->sf_cylon_pos, this->sf_width); sa.push_back(string_attr(lr, &view_curses::VC_STYLE, view_colors::ansi_color_pair(COLOR_WHITE, COLOR_GREEN) | A_BOLD)); this->sf_cylon_pos += 1; if (this->sf_cylon_pos > this->sf_width) { this->sf_cylon_pos = 0; } } }; /** * Set the new value for this field using a formatted string. * * @param fmt The format string. * @param ... Arguments for the format. */ void set_value(const char *fmt, ...) { char buffer[128]; va_list args; va_start(args, fmt); vsnprintf(buffer, sizeof(buffer), fmt, args); this->set_value(std::string(buffer)); va_end(args); }; void set_stitch_value(int color_pair) { string_attrs_t & sa = this->sf_value.get_attrs(); struct line_range lr(0, 1); this->sf_value.get_string() = "::"; sa.push_back(string_attr(lr, &view_curses::VC_STYLE, A_REVERSE | COLOR_PAIR(color_pair))); lr.lr_start = 1; lr.lr_end = 2; sa.push_back(string_attr(lr, &view_curses::VC_STYLE, COLOR_PAIR(color_pair))); }; void set_left_pad(size_t val) { this->sf_left_pad = val; }; size_t get_left_pad() const { return this->sf_left_pad; }; /** @return The string value for this field. */ attr_line_t &get_value() { return this->sf_value; }; void right_justify(bool yes) { this->sf_right_justify = yes; }; bool is_right_justified(void) const { return this->sf_right_justify; }; void set_cylon(bool yes) { this->sf_cylon = yes; }; bool is_cylon(void) const { return this->sf_cylon; }; /** @return True if this field's value is an empty string. */ bool empty() { return this->sf_value.get_string().empty(); }; void clear() { this->sf_value.clear(); }; /** @param role The color role for this field. */ void set_role(view_colors::role_t role) { this->sf_role = role; }; /** @return The color role for this field. */ view_colors::role_t get_role() const { return this->sf_role; }; /** @param width The maximum display width, in characters. */ void set_width(int width) { this->sf_width = width; }; /** @param width The maximum display width, in characters. */ size_t get_width() const { return this->sf_width; }; /** @param width The maximum display width, in characters. */ void set_min_width(int width) { this->sf_min_width = width; }; /** @param width The maximum display width, in characters. */ size_t get_min_width() const { return this->sf_min_width; }; void set_share(int share) { this->sf_share = share; }; int get_share() const { return this->sf_share; }; protected: size_t sf_width; /*< The maximum display width, in chars. */ size_t sf_min_width; /*< The minimum display width, in chars. */ bool sf_right_justify; bool sf_cylon; size_t sf_cylon_pos; attr_line_t sf_value; /*< The value to display for this field. */ view_colors::role_t sf_role; /*< The color role for this field. */ int sf_share; size_t sf_left_pad; }; /** * Data source for the fields to be displayed in a status view. */ class status_data_source { public: virtual ~status_data_source() { }; /** * @return The number of status_fields in this source. */ virtual size_t statusview_fields(void) = 0; /** * Callback used to get a particular field. * * @param field The index of the field to return. * @return A reference to the field at the given index. */ virtual status_field &statusview_value_for_field(int field) = 0; }; /** * A view that displays a collection of fields in a line on the display. */ class statusview_curses : public view_curses { public: statusview_curses() : sc_source(NULL), sc_window(NULL), sc_top(0), sc_last_width(0), sc_enabled(true) { }; virtual ~statusview_curses() { }; void set_data_source(status_data_source *src) { this->sc_source = src; }; status_data_source *get_data_source() { return this->sc_source; }; void set_top(int top) { this->sc_top = top; }; int get_top() const { return this->sc_top; }; void set_window(WINDOW *win) { this->sc_window = win; }; WINDOW *get_window() { return this->sc_window; }; void set_enabled(bool value) { this->sc_enabled = value; }; bool get_enabled() const { return this->sc_enabled; }; void window_change(void) { if (this->sc_source == NULL) { return; } int field_count = this->sc_source->statusview_fields(); int remaining, total_shares = 0; unsigned long width, height; getmaxyx(this->sc_window, height, width); // Silence the compiler. Remove this if height is used at a later stage. (void)height; remaining = width - 4; for (int field = 0; field < field_count; field++) { status_field &sf = this->sc_source->statusview_value_for_field( field); remaining -= sf.get_share() ? sf.get_min_width() : sf.get_width(); remaining -= 1; total_shares += sf.get_share(); } if (remaining < 2) { remaining = 0; } for (int field = 0; field < field_count; field++) { status_field &sf = this->sc_source->statusview_value_for_field( field); if (sf.get_share()) { int actual_width; actual_width = sf.get_min_width(); actual_width += remaining / (sf.get_share() / total_shares); sf.set_width(actual_width); } } this->sc_last_width = width; }; void do_update(void); private: status_data_source *sc_source; WINDOW * sc_window; int sc_top; unsigned long sc_last_width; bool sc_enabled; }; #endif