Improved color handling, prepared for zypper.conf.
authorJán Kupec <jkupec@suse.cz>
Fri, 13 Mar 2009 13:20:53 +0000 (14:20 +0100)
committerJán Kupec <jkupec@suse.cz>
Fri, 13 Mar 2009 13:20:53 +0000 (14:20 +0100)
src/Config.cc
src/Config.h
src/output/OutNormal.cc
src/utils/colors.cc
src/utils/colors.h
src/utils/prompt.cc

index eb725b5..9dec290 100644 (file)
                              |__/|_|  |_|
 \*---------------------------------------------------------------------------*/
 
-#include "utils/colors.h"
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Measure.h"
 
 #include "Config.h"
 
-#include <iostream>
+using namespace std;
+using namespace zypp;
 
 Config::Config()
   : do_colors(false)
   , color_useColors("never")
-  , color_background(false)        // dark background
+  , color_background(false)          // dark background
+  , color_colorResult     ("white")  // default colors for dark background
+  , color_colorMsgStatus  ("grey")   // if background is actually light, these
+  , color_colorMsgError   ("red")    // colors will be overwritten in read()
+  , color_colorMsgWarning ("yellow")
+  , color_colorPositive   ("green")
+  , color_colorNegative   ("red")
+  , color_colorPromptOption("grey")
+  , color_colorPromptShorthand("yellow")
 {}
 
 void Config::read()
 {
-  // color_useColors = auges.getOption("colors/useColors");
-  // ...
+  debug::Measure m("ReadConfig");
+
+  // get augeas
+
+  m.elapsed();
+
+  // ---------------[ main ]--------------------------------------------------
+
+  // TODO
+
+  // ---------------[ colors ]------------------------------------------------
+
+  // color_useColors = augeas.getOption("colors/useColors");
   do_colors =
-    color_useColors == "autodetect" && has_colors()
+    (color_useColors == "autodetect" && has_colors())
     || color_useColors == "always";
+
+  ////// colors/background //////
+
+  string s;
+  // s = augeas.getOption("colors/background");
+  if (s == "light")
+    color_background = true;
+  else if (!s.empty() && s != "dark")
+    ERR << "invalid colors/background value: " << s << endl;
+
+  Color c("none");
+
+  ////// colors/colorResult //////
+
+  // c =  augeas.getOption("colors/colorResult");
+  if (c.value().empty())
+  {
+    // set a default for light background
+    if (color_background)
+      color_colorResult = Color("black");
+  }
+  else
+    color_colorResult = c;
+
+  ////// colors/colorMsgStatus //////
+
+  // c =  augeas.getOption("colors/colorMsgStatus");
+  if (c.value().empty())
+  {
+    // set a default for light background
+    if (color_background)
+      color_colorMsgStatus = Color("default");
+  }
+  else
+    color_colorMsgStatus = c;
+
+  ////// colors/colorMsgError //////
+
+  // c =  augeas.getOption("colors/colorMsgError");
+  if (!c.value().empty())
+    color_colorMsgError = c;
+
+  ////// colors/colorMsgWarning //////
+
+  // c =  augeas.getOption("colors/colorMsgWarning");
+  if (c.value().empty())
+  {
+    // set a default for light background
+    if (color_background)
+      color_colorMsgWarning = Color("brown");
+  }
+  else
+    color_colorMsgWarning = c;
+
+  ////// colors/colorPositive //////
+
+  // c =  augeas.getOption("colors/colorPositive");
+  if (!c.value().empty())
+    color_colorPositive = c;
+
+  ////// colors/colorNegative //////
+
+  // c =  augeas.getOption("colors/colorNegative");
+  if (!c.value().empty())
+    color_colorNegative = c;
+
+  ////// colors/colorPromptOption //////
+
+  // c =  augeas.getOption("colors/colorPromptOption");
+  if (c.value().empty())
+  {
+    // set a default for light background
+    if (color_background)
+      color_colorPromptOption = Color("darkgrey");
+  }
+  else
+    color_colorPromptOption = c;
+
+  ////// colors/colorPromptShorthand //////
+
+  // c =  augeas.getOption("colors/colorPromptShorthand");
+  if (c.value().empty())
+  {
+    // set a default for light background
+    if (color_background)
+      color_colorPromptShorthand = Color("cyan");
+  }
+  else
+    color_colorPromptShorthand = c;
+
+  m.stop();
 }
index d5b197e..f22b5be 100644 (file)
@@ -10,6 +10,8 @@
 
 #include <string>
 
+#include "utils/colors.h"
+
 struct Config
 {
   /** Initializes the config options to defaults. */
@@ -21,16 +23,28 @@ struct Config
 
 
   /**
-   * Whether to print colors to stdout. This is evaluated according to
+   * Whether to colorize the output. This is evaluated according to
    * color_useColors and has_colors()
    */
   bool do_colors;
 
-  /** zypper.conf: color.useColors option */
+  /** zypper.conf: color.useColors */
   std::string color_useColors;
 
-  /** dark (false) or light (true) */
+  /**
+   * zypper.conf: color.background
+   * dark (false) or light (true)
+   */
   bool color_background;
+
+  Color color_colorResult;
+  Color color_colorMsgStatus;
+  Color color_colorMsgError;
+  Color color_colorMsgWarning;
+  Color color_colorPositive;
+  Color color_colorNegative;
+  Color color_colorPromptOption;
+  Color color_colorPromptShorthand;
 };
 
 #endif /* ZYPPER_CONFIG_H_ */
index 4414398..84b1fb2 100644 (file)
@@ -58,10 +58,12 @@ void OutNormal::info(const std::string & msg, Verbosity verbosity, Type mask)
   if (infoWarningFilter(verbosity, mask))
     return;
 
-  if (_use_colors && verbosity > Out::QUIET)
-    cout << COLOR_WHITE << msg << COLOR_RESET << endl;
+  if (verbosity == Out::QUIET)
+    print_color(msg, COLOR_CONTEXT_RESULT);
   else
-    cout << msg << endl;
+    cout << msg;
+
+  cout << endl;
 }
 
 void OutNormal::warning(const std::string & msg, Verbosity verbosity, Type mask)
@@ -69,18 +71,13 @@ void OutNormal::warning(const std::string & msg, Verbosity verbosity, Type mask)
   if (infoWarningFilter(verbosity, mask))
     return;
 
-  if (_use_colors)
-    cout << COLOR_YELLOW_BOLD << _("Warning: ") << COLOR_RESET << msg << endl;
-  else
-    cout << msg << endl;
+  print_color(_("Warning: "), COLOR_CONTEXT_MSG_WARNING);
+  cout << msg << endl;
 }
 
 void OutNormal::error(const std::string & problem_desc, const std::string & hint)
 {
-  if (_use_colors)
-    cerr << COLOR_RED_BOLD << problem_desc << COLOR_RESET;
-  else
-    cerr << problem_desc;
+  fprint_color(cerr, problem_desc, COLOR_CONTEXT_MSG_ERROR);
   if (!hint.empty() && this->verbosity() > Out::QUIET)
     cerr << endl << hint;
   cerr << endl;
@@ -92,16 +89,12 @@ void OutNormal::error(const zypp::Exception & e,
                       const string & problem_desc,
                       const string & hint)
 {
-  if (_use_colors)
-    cerr << COLOR_RED_BOLD;
-
   // problem
-  cerr << problem_desc << endl;
+  fprint_color(cerr, problem_desc, COLOR_CONTEXT_MSG_ERROR);
+  cerr << endl;
   // cause
-  cerr << zyppExceptionReport(e) << endl;
-
-  if (_use_colors)
-    cerr << COLOR_RESET;
+  fprint_color(cerr, zyppExceptionReport(e), COLOR_CONTEXT_MSG_ERROR);
+  cerr << endl;
 
   // hint
   if (!hint.empty() && this->verbosity() > Out::QUIET)
@@ -153,9 +146,6 @@ void OutNormal::progressStart(const std::string & id,
   if (progressFilter())
     return;
 
-  if (_use_colors)
-    cerr << COLOR_WHITE;
-
   if (!_isatty)
     cout << label << " [";
 
@@ -163,9 +153,6 @@ void OutNormal::progressStart(const std::string & id,
     display_tick(id, label);
   else
     display_progress(id, label, 0);
-
-  if (_use_colors)
-    cerr << COLOR_RESET;
 }
 
 void OutNormal::progress(const std::string & id, const string & label, int value)
@@ -173,16 +160,10 @@ void OutNormal::progress(const std::string & id, const string & label, int value
   if (progressFilter())
     return;
 
-  if (_use_colors)
-    cerr << COLOR_WHITE;
-
   if (value)
     display_progress(id, label, value);
   else
     display_tick(id, label);
-
-  if (_use_colors)
-    cerr << COLOR_RESET;
 }
 
 void OutNormal::progressEnd(const std::string & id, const string & label, bool error)
@@ -190,21 +171,21 @@ void OutNormal::progressEnd(const std::string & id, const string & label, bool e
   if (progressFilter())
     return;
 
-  if (_use_colors)
-    cerr << COLOR_WHITE;
+  if (!error && _use_colors)
+    cout << get_color(COLOR_CONTEXT_MSG_STATUS);
 
   if (_isatty)
-  {
     cout << CLEARLN << label << " [";
-    if (error)
-      print_color(_("error"), COLOR_RED, COLOR_WHITE);
-    else
-      cout << _("done");
-  }
+
+  if (error)
+    print_color(_("error"), COLOR_CONTEXT_NEGATIVE);
+  else
+    cout << _("done");
+
   cout << "]";
 
-  if (_use_colors)
-    cerr << COLOR_RESET;
+  if (!error && _use_colors)
+    cout << COLOR_RESET;
 
   cout << endl << std::flush;
 }
@@ -215,24 +196,18 @@ void OutNormal::dwnldProgressStart(const zypp::Url & uri)
   if (verbosity() < NORMAL)
     return;
 
-  if (_use_colors)
-    cerr << COLOR_WHITE;
-
-  if (isatty(STDOUT_FILENO))
+  if (_isatty)
     cout << CLEARLN;
   cout << _("Retrieving:") << " ";
   if (verbosity() == DEBUG)
     cout << uri; //! \todo shorten to fit the width of the terminal
   else
     cout << zypp::Pathname(uri.getPathName()).basename();
-  if (isatty(STDOUT_FILENO))
+  if (_isatty)
     cout << " [" << _("starting") << "]"; //! \todo align to the right
   else
     cout << " [" ;
 
-  if (_use_colors)
-    cerr << COLOR_RESET;
-
   cout << std::flush;
 }
 
@@ -249,9 +224,6 @@ void OutNormal::dwnldProgress(const zypp::Url & uri,
     return;
   }
 
-  if (_use_colors)
-    cerr << COLOR_WHITE;
-
   cout << CLEARLN << _("Retrieving:") << " ";
   if (verbosity() == DEBUG)
     cout << uri; //! \todo shorten to fit the width of the terminal
@@ -268,9 +240,6 @@ void OutNormal::dwnldProgress(const zypp::Url & uri,
     cout << "]";
   }
 
-  if (_use_colors)
-    cerr << COLOR_RESET;
-
   cout << std::flush;
 }
 
@@ -279,8 +248,8 @@ void OutNormal::dwnldProgressEnd(const zypp::Url & uri, long rate, bool error)
   if (verbosity() < NORMAL)
     return;
 
-  if (_use_colors)
-    cerr << COLOR_WHITE;
+  if (!error && _use_colors)
+    cout << get_color(COLOR_CONTEXT_MSG_STATUS);
 
   if (_isatty)
   {
@@ -291,7 +260,7 @@ void OutNormal::dwnldProgressEnd(const zypp::Url & uri, long rate, bool error)
       cout << zypp::Pathname(uri.getPathName()).basename();
     cout << " [";
     if (error)
-      print_color(_("error"), COLOR_RED, COLOR_WHITE);
+      print_color(_("error"), COLOR_CONTEXT_NEGATIVE);
     else
       cout << _("done");
   }
@@ -302,8 +271,8 @@ void OutNormal::dwnldProgressEnd(const zypp::Url & uri, long rate, bool error)
     cout << " (" << zypp::ByteCount(rate) << "/s)";
   cout << "]";
 
-  if (_use_colors)
-    cerr << COLOR_RESET;
+  if (!error && _use_colors)
+    cout << COLOR_RESET;
 
   cout << endl << std::flush;
 }
index 8f34601..2b90c60 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include "zypp/base/Logger.h"
+
+#include "Zypper.h"
+
 #include "colors.h"
 
 using namespace std;
 
+static map<string, string> str2esc;
+
+Color::Color(const string & color_str)
+  : _value(parse(color_str))
+{}
+
+string Color::parse(const string & value)
+{
+  if (str2esc.empty())
+  {
+    str2esc["green"]          = COLOR_GREEN;
+    str2esc["lightgreen"]     = COLOR_GREEN_LIGHT;
+    str2esc["red"]            = COLOR_RED;
+    str2esc["lightred"]       = COLOR_RED_LIGHT;
+    str2esc["grey"]           = COLOR_WHITE;
+    str2esc["white"]          = COLOR_WHITE_LIGHT;
+    str2esc["brown"]          = COLOR_YELLOW;
+    str2esc["yellow"]         = COLOR_YELLOW_LIGHT;
+    str2esc["purple"]         = COLOR_PURPLE;
+    str2esc["lightpurple"]    = COLOR_PURPLE_LIGHT;
+    str2esc["blue"]           = COLOR_BLUE;
+    str2esc["lightblue"]      = COLOR_BLUE_LIGHT;
+    str2esc["cyan"]           = COLOR_CYAN;
+    str2esc["lightcyan"]      = COLOR_CYAN_LIGHT;
+    str2esc["black"]          = COLOR_BLACK;
+    str2esc["darkgrey"]       = COLOR_GREY_DARK;
+
+    str2esc["reset"]          = COLOR_RESET;
+  }
+
+  map<string, string>::const_iterator it = str2esc.find(value);
+  if (it == str2esc.end())
+  {
+    ERR << "Unknown color '" << value << "'" << endl;
+    return string();
+  }
+  return it->second;
+}
+
 bool has_colors()
 {
   if (::isatty(STDOUT_FILENO))
@@ -25,16 +68,68 @@ bool has_colors()
   return false;
 }
 
+static const string get_color(const Config & conf, const ColorContext context)
+{
+  switch (context)
+  {
+  case COLOR_CONTEXT_RESULT:
+    return conf.color_colorResult.value();
+  case COLOR_CONTEXT_MSG_STATUS:
+    return conf.color_colorMsgStatus.value();
+  case COLOR_CONTEXT_MSG_WARNING:
+    return conf.color_colorMsgWarning.value();
+  case COLOR_CONTEXT_MSG_ERROR:
+    return conf.color_colorMsgError.value();
+  case COLOR_CONTEXT_POSTIVE:
+    return conf.color_colorPositive.value();
+  case COLOR_CONTEXT_NEGATIVE:
+    return conf.color_colorNegative.value();
+  case COLOR_CONTEXT_PROMPT_OPTION:
+    return conf.color_colorPromptOption.value();
+  case COLOR_CONTEXT_PROMPT_SHORTHAND:
+    return conf.color_colorPromptShorthand.value();
+  default:
+    return COLOR_RESET;
+  }
+}
+
+const string get_color(const ColorContext context)
+{
+  return get_color(Zypper::instance()->config(), context);
+}
+
 void print_color(const std::string & s,
     const char * ansi_color_seq, const char * prev_color)
 {
-  if (prev_color)
-    cout << COLOR_RESET;
+  fprint_color(cout, s, ansi_color_seq, prev_color);
+}
+
+void fprint_color(ostream & str, const std::string & s,
+    const char * ansi_color_seq, const char * prev_color)
+{
+  if (Zypper::instance()->config().do_colors)
+  {
+    if (prev_color)
+      str << COLOR_RESET;
 
-  cout << ansi_color_seq << s;
+    str << ansi_color_seq << s << COLOR_RESET;
 
-  if (prev_color)
-    cout << prev_color;
+    if (prev_color)
+      str << prev_color;
+  }
   else
-    cout << COLOR_RESET;
+    str << s;
+}
+
+void fprint_color(ostream & str, const std::string & s,
+    const ColorContext cc, const ColorContext prev_cc)
+{
+  const Config & conf = Zypper::instance()->config();
+  fprint_color(str, s, get_color(conf, cc).c_str(), get_color(conf, prev_cc).c_str());
+}
+
+void print_color(const std::string & s,
+    const ColorContext cc, const ColorContext prev_cc)
+{
+  fprint_color(cout, s, cc, prev_cc);
 }
index 0e50d0e..c2d84ab 100644 (file)
@@ -8,41 +8,83 @@
 #ifndef UTILS_COLORS_H_
 #define UTILS_COLORS_H_
 
+#include <string>
 #include <iosfwd>
 
+#define COLOR_BLACK             "\033[30m"
+#define COLOR_GREY_DARK         "\033[1;30m"
+#define COLOR_BLUE              "\033[34m"
+#define COLOR_BLUE_LIGHT        "\033[1;34m"
+#define COLOR_CYAN              "\033[36m"
+#define COLOR_CYAN_LIGHT        "\033[1;36m"
 #define COLOR_GREEN             "\033[32m"
-#define COLOR_GREEN_BOLD        "\033[1;32m"
+#define COLOR_GREEN_LIGHT       "\033[1;32m"
+#define COLOR_PURPLE            "\033[35m"
+#define COLOR_PURPLE_LIGHT      "\033[1;35m"
 #define COLOR_RED               "\033[31m"
-#define COLOR_RED_BOLD          "\033[1;31m"
+#define COLOR_RED_LIGHT         "\033[1;31m"
 #define COLOR_WHITE             "\033[37m"    // grey
-#define COLOR_WHITE_BOLD        "\033[1;37m"
+#define COLOR_WHITE_LIGHT       "\033[1;37m"
 #define COLOR_YELLOW            "\033[33m"    // brown
-#define COLOR_YELLOW_BOLD       "\033[1;33m"
+#define COLOR_YELLOW_LIGHT      "\033[1;33m"
 
 #define COLOR_RESET             "\033[m"
 
-/*
-enum zypper_colors
+
+class Color
 {
-  ZYPPER_COLOR_MSG_NORMAL           = 1,
-  ZYPPER_COLOR_MSG_HIGHLIGHTED      = 2,
-  ZYPPER_COLOR_MSG_ERROR            = 3,
-  ZYPPER_COLOR_MSG_WARNING          = 4
+public:
+  explicit Color(const std::string & color_str);
+
+  std::string parse(const std::string & value);
+
+  std::string value() const
+  { return _value; }
+
+private:
+  std::string _value;
 };
-*/
+
+
+typedef enum zypper_color_contexts
+{
+  COLOR_CONTEXT_RESULT               = 1,
+  COLOR_CONTEXT_MSG_STATUS           = 2,
+  COLOR_CONTEXT_MSG_ERROR            = 3,
+  COLOR_CONTEXT_MSG_WARNING          = 4,
+  COLOR_CONTEXT_POSTIVE              = 5,
+  COLOR_CONTEXT_NEGATIVE             = 6,
+  COLOR_CONTEXT_PROMPT_OPTION        = 7,
+  COLOR_CONTEXT_PROMPT_SHORTHAND     = 8,
+
+  COLOR_CONTEXT_DEFAULT              = -1
+} ColorContext;
+
 
 /** Simple check whether stdout can handle colors. */
 bool has_colors();
 
+/** Get ISO terminal escape sequence for color in given \a context. */
+const std::string get_color(const ColorContext context);
+
 /**
- * Print string \a s in given color.
+ * Print string \a s in given color to stdout.
  *
  * \param s                string to print
- * \param ansi_color_seq   color to print with
+ * \param color_seq        color to print with (ISO terminal escape sequence)
  * \param prev_color       color to restore after printing. If NULL,
  *                         COLOR_RESET will be used
  */
 void print_color(const std::string & s,
-    const char * ansi_color_seq, const char * prev_color);
+    const char * color_seq, const char * prev_color = NULL);
+
+void fprint_color(std::ostream & str, const std::string & s,
+    const char * ansi_color_seq, const char * prev_color = NULL);
+
+void fprint_color(std::ostream & str, const std::string & s,
+    const ColorContext cc, const ColorContext prev_color = COLOR_CONTEXT_DEFAULT);
+
+void print_color(const std::string & s,
+    const ColorContext cc, const ColorContext prev_color = COLOR_CONTEXT_DEFAULT);
 
 #endif /* UTILS_COLORS_H_ */
index 1e9c4de..622e732 100644 (file)
@@ -96,25 +96,23 @@ const string PromptOptions::optionStringColored() const
   if ((it = options().begin()) != options().end() && shown_count > 0)
   {
     if (defaultOpt() == 0)
-      option_str << COLOR_YELLOW << *it;
+      fprint_color(option_str, *it, COLOR_CONTEXT_PROMPT_SHORTHAND);
     else
-      option_str << COLOR_WHITE << *it;
+      fprint_color(option_str, *it, COLOR_CONTEXT_PROMPT_OPTION);
     ++it;
   }
   for (unsigned int i = 1; it != options().end() && i < shown_count; ++it, ++i)
     if (isEnabled(i))
     {
-      option_str << COLOR_WHITE << "/";
+      fprint_color(option_str, "/", COLOR_CONTEXT_PROMPT_OPTION);
       if (defaultOpt() == i)
-        option_str << COLOR_YELLOW << *it;
+        fprint_color(option_str, *it, COLOR_CONTEXT_PROMPT_SHORTHAND);
       else
-        option_str << *it;
+        fprint_color(option_str, *it, COLOR_CONTEXT_PROMPT_OPTION);
     }
 
   if (!_opt_help.empty())
-    option_str << COLOR_WHITE << (shown_count > 0 ? "/" : "") << "?";
-
-  option_str << COLOR_RESET;
+    fprint_color(option_str, shown_count > 0 ? "/?" : "?", COLOR_CONTEXT_PROMPT_OPTION);
 
   return option_str.str();
 }