Code

colors, utils: use g_list_free_full(g_free)
[ncmpc.git] / src / colors.c
index f7096939097d2e20b9a5e1f62fb4772c2246e88e..5a79fd9bbfff8b2c6a7967e499af18df19c98544 100644 (file)
@@ -1,5 +1,6 @@
-/* 
- * (c) 2004 by Kalle Wallin (kaw@linux.se)
+/* ncmpc (Ncurses MPD Client)
+ * (c) 2004-2017 The Music Player Daemon Project
+ * Project homepage: http://musicpd.org
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#include "colors.h"
+#include "i18n.h"
+#include "ncfix.h"
+
+#ifdef ENABLE_COLORS
+#include "options.h"
+#endif
+
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <ncurses.h>
 #include <glib.h>
 
-#include "config.h"
-#include "options.h"
-#include "support.h"
-#include "colors.h"
-
-#ifdef DEBUG
-#define D(x) x
-#else
-#define D(x)
-#endif
-
-#define COLOR_BRIGHT_MASK   (1<<7)
-
-#define COLOR_BRIGHT_BLACK    (COLOR_BLACK | COLOR_BRIGHT_MASK)
-#define COLOR_BRIGHT_RED      (COLOR_RED   | COLOR_BRIGHT_MASK)
-#define COLOR_BRIGHT_GREEN    (COLOR_GREEN | COLOR_BRIGHT_MASK)
-#define COLOR_BRIGHT_YELLOW   (COLOR_YELLOW | COLOR_BRIGHT_MASK)
-#define COLOR_BRIGHT_BLUE     (COLOR_BLUE | COLOR_BRIGHT_MASK)
-#define COLOR_BRIGHT_MAGENTA  (COLOR_MAGENTA | COLOR_BRIGHT_MASK)
-#define COLOR_BRIGHT_CYAN     (COLOR_CYAN | COLOR_BRIGHT_MASK)
-#define COLOR_BRIGHT_WHITE    (COLOR_WHITE | COLOR_BRIGHT_MASK)
-
-#define IS_BRIGHT(color) (color & COLOR_BRIGHT_MASK)
-
-/* name of the color fields */
-#define NAME_TITLE        "title"
-#define NAME_TITLE_BOLD   "title-bold"
-#define NAME_LINE         "line"
-#define NAME_LINE_BOLD    "line-flags"
-#define NAME_LIST         "list"
-#define NAME_LIST_BOLD    "list-bold"
-#define NAME_PROGRESS     "progressbar"
-#define NAME_STATUS       "status-song"
-#define NAME_STATUS_BOLD  "status-state"
-#define NAME_STATUS_TIME  "status-time"
-#define NAME_ALERT        "alert"
-#define NAME_BGCOLOR      "background"
+#define COLOR_NONE  G_MININT /* left most bit only */
+#define COLOR_ERROR -2
 
+#ifdef ENABLE_COLORS
 typedef struct {
-  short color;
-  short r,g,b;
+       short color;
+       short r,g,b;
 } color_definition_entry_t;
+#endif
 
 typedef struct {
-  int    id;
-  char   *name;
-  short  fg;
-  attr_t attrs;
+       const char *name;
+       int color;
+       int mono;
 } color_entry_t;
 
-static color_entry_t colors[] = {
-
-  /* color pair,        field name,        color,            mono attribute */
-  /*-------------------------------------------------------------------------*/
-  { COLOR_TITLE,        NAME_TITLE,        COLOR_YELLOW,         A_NORMAL },
-  { COLOR_TITLE_BOLD,   NAME_TITLE_BOLD,   COLOR_BRIGHT_YELLOW,  A_BOLD },
-  { COLOR_LINE,         NAME_LINE,         COLOR_WHITE,          A_NORMAL },
-  { COLOR_LINE_BOLD,    NAME_LINE_BOLD,    COLOR_BRIGHT_WHITE,   A_BOLD },
-  { COLOR_LIST,         NAME_LIST,         COLOR_GREEN,          A_NORMAL },
-  { COLOR_LIST_BOLD,    NAME_LIST_BOLD,    COLOR_BRIGHT_GREEN,   A_BOLD },
-  { COLOR_PROGRESSBAR,  NAME_PROGRESS,     COLOR_WHITE,          A_NORMAL },
-  { COLOR_STATUS,       NAME_STATUS,       COLOR_YELLOW,         A_NORMAL },
-  { COLOR_STATUS_BOLD,  NAME_STATUS_BOLD,  COLOR_BRIGHT_YELLOW,  A_BOLD },
-  { COLOR_STATUS_TIME,  NAME_STATUS_TIME,  COLOR_RED,            A_NORMAL },
-  { COLOR_STATUS_ALERT, NAME_ALERT,        COLOR_BRIGHT_RED,     A_BOLD },
-  { 0,                  NULL,              0,                    0 }
+static color_entry_t colors[COLOR_END] = {
+       /* color pair = field name, color, mono */
+       [COLOR_TITLE]        = {"title",             COLOR_YELLOW,          A_NORMAL},
+       [COLOR_TITLE_BOLD]   = {"title-bold",        COLOR_YELLOW | A_BOLD, A_BOLD  },
+       [COLOR_LINE]         = {"line",              COLOR_WHITE,           A_NORMAL},
+       [COLOR_LINE_BOLD]    = {"line-bold",         COLOR_WHITE  | A_BOLD, A_BOLD  },
+       [COLOR_LINE_FLAGS]   = {"line-flags",        COLOR_YELLOW,          A_NORMAL},
+       [COLOR_LIST]         = {"list",              COLOR_GREEN,           A_NORMAL},
+       [COLOR_LIST_BOLD]    = {"list-bold",         COLOR_GREEN  | A_BOLD, A_BOLD  },
+       [COLOR_PROGRESSBAR]  = {"progressbar",       COLOR_WHITE,           A_NORMAL},
+       [COLOR_STATUS]       = {"status-song",       COLOR_YELLOW,          A_NORMAL},
+       [COLOR_STATUS_BOLD]  = {"status-state",      COLOR_YELLOW | A_BOLD, A_BOLD  },
+       [COLOR_STATUS_TIME]  = {"status-time",       COLOR_RED,             A_NORMAL},
+       [COLOR_STATUS_ALERT] = {"alert",             COLOR_RED    | A_BOLD, A_BOLD  },
+       [COLOR_DIRECTORY]    = {"browser-directory", COLOR_YELLOW,          A_NORMAL},
+       [COLOR_PLAYLIST]     = {"browser-playlist",  COLOR_RED,             A_NORMAL},
+       [COLOR_BACKGROUND]   = {"background",        COLOR_BLACK,           A_NORMAL},
 };
 
-/* background color */
-static short bg = COLOR_BLACK;
+#ifdef ENABLE_COLORS
 
 static GList *color_definition_list = NULL;
 
 static color_entry_t *
-colors_lookup(int id)
+colors_lookup_by_name(const char *name)
 {
-  int i;
-
-  i=0;
-  while( colors[i].name != NULL )
-    {
-      if( colors[i].id == id )
-       return &colors[i];
-      i++;
-    }
-  return NULL;
-}
+       for (enum color i = 1; i < COLOR_END; ++i)
+               if (!strcasecmp(colors[i].name, name))
+                       return &colors[i];
 
-static color_entry_t *
-colors_lookup_by_name(char *name)
-{
-  int i;
-
-  i=0;
-  while( colors[i].name != NULL )
-    {
-      if( !strcasecmp(colors[i].name, name) )
-       return &colors[i];
-      i++;
-    }
-  return NULL;
+       return NULL;
 }
 
-static int
-colors_update_pair(int id)
+static void
+colors_update_pair(enum color id)
 {
-  color_entry_t *entry = colors_lookup(id);
-  short fg = -1;
-
-  if( !entry )
-    return -1;
-
-  if( IS_BRIGHT(entry->fg) )
-    {
-      entry->attrs = A_BOLD;
-      fg = entry->fg & ~COLOR_BRIGHT_MASK;
-    }
-  else
-    {
-      entry->attrs = A_NORMAL;
-      fg = entry->fg;
-    }
-
-  init_pair(entry->id, fg, bg);
-  
-  return 0;
+       assert(id > 0 && id < COLOR_END);
+
+       int fg = colors[id].color;
+       int bg = colors[COLOR_BACKGROUND].color;
+
+       /* If color == COLOR_NONE (negative),
+        * pass -1 to avoid cast errors */
+       init_pair(id,
+               (fg < 0 ? -1 : fg),
+               (bg < 0 ? -1 : bg));
 }
 
-short
-colors_str2color(char *str)
+int
+colors_str2color(const char *str)
 {
-  if( !strcasecmp(str,"black") )
-    return COLOR_BLACK;
-  else if( !strcasecmp(str,"red") )
-    return COLOR_RED;
-  else if( !strcasecmp(str,"green") )
-    return COLOR_GREEN;
-  else if( !strcasecmp(str,"yellow") )
-    return COLOR_YELLOW;
-  else if( !strcasecmp(str,"blue") )
-    return COLOR_BLUE;
-  else if( !strcasecmp(str,"magenta") )
-    return COLOR_MAGENTA;
-  else if( !strcasecmp(str,"cyan") )
-    return COLOR_CYAN;
-  else if( !strcasecmp(str,"white") )
-    return COLOR_WHITE;
-  else if( !strcasecmp(str,"brightred") )
-    return COLOR_BRIGHT_RED;
-  else if( !strcasecmp(str,"brightgreen") )
-    return COLOR_BRIGHT_GREEN;
-  else if( !strcasecmp(str,"brightyellow") )
-    return COLOR_BRIGHT_YELLOW;
-  else if( !strcasecmp(str,"brightblue") )
-    return COLOR_BRIGHT_BLUE;
-  else if( !strcasecmp(str,"brightmagenta") )
-    return COLOR_BRIGHT_MAGENTA;
-  else if( !strcasecmp(str,"brightcyan") )
-    return COLOR_BRIGHT_CYAN;
-  else if( !strcasecmp(str,"brightwhite") )
-    return COLOR_BRIGHT_WHITE;
-  else if( !strcasecmp(str,"grey") || !strcasecmp(str,"gray") )
-    return COLOR_BRIGHT_BLACK;
-  else if( !strcasecmp(str,"none") )
-    return -1;
-  fprintf(stderr,"Warning: Unknown color - %s\n", str);
-  return -2;
+       int color = 0;
+       char **parts = g_strsplit(str, ",", 0);
+       for (int i = 0; parts[i]; i++) {
+               char *cur = parts[i];
+
+               /* Legacy colors (brightblue,etc) */
+               if (!strncasecmp(cur, "bright", 6)) {
+                       color |= A_BOLD;
+                       cur += 6;
+               }
+
+               /* Colors */
+               if (!strcasecmp(cur, "none"))
+                       color |= COLOR_NONE;
+               else if (!strcasecmp(cur, "black"))
+                       color |= COLOR_BLACK;
+               else if (!strcasecmp(cur, "red"))
+                       color |= COLOR_RED;
+               else if (!strcasecmp(cur, "green"))
+                       color |= COLOR_GREEN;
+               else if (!strcasecmp(cur, "yellow"))
+                       color |= COLOR_YELLOW;
+               else if (!strcasecmp(cur, "blue"))
+                       color |= COLOR_BLUE;
+               else if (!strcasecmp(cur, "magenta"))
+                       color |= COLOR_MAGENTA;
+               else if (!strcasecmp(cur, "cyan"))
+                       color |= COLOR_CYAN;
+               else if (!strcasecmp(cur, "white"))
+                       color |= COLOR_WHITE;
+               else if (!strcasecmp(cur, "grey") || !strcasecmp(cur, "gray"))
+                       color |= COLOR_BLACK | A_BOLD;
+
+               /* Attributes */
+               else if (!strcasecmp(cur, "standout"))
+                       color |= A_STANDOUT;
+               else if (!strcasecmp(cur, "underline"))
+                       color |= A_UNDERLINE;
+               else if (!strcasecmp(cur, "reverse"))
+                       color |= A_REVERSE;
+               else if (!strcasecmp(cur, "blink"))
+                       color |= A_BLINK;
+               else if (!strcasecmp(cur, "dim"))
+                       color |= A_DIM;
+               else if (!strcasecmp(cur, "bold"))
+                       color |= A_BOLD;
+               else {
+                       /* Numerical colors */
+                       char *endptr;
+                       int tmp = strtol(cur, &endptr, 10);
+                       if (cur != endptr && endptr[0] == '\0') {
+                               color |= tmp;
+                       } else {
+                               fprintf(stderr, "%s: %s\n",
+                                       _("Unknown color"), str);
+                               return COLOR_ERROR;
+                       }
+               }
+
+       }
+       g_strfreev(parts);
+       return color;
 }
 
 /* This function is called from conf.c before curses have been started,
- * it adds the definition to the color_definition_list and init_color() is 
+ * it adds the definition to the color_definition_list and init_color() is
  * done in colors_start() */
-int
-colors_define(char *name, short r, short g, short b)
+bool
+colors_define(const char *name, short r, short g, short b)
 {
-  color_definition_entry_t *entry;
-  short color = colors_str2color(name);
+       int color = colors_str2color(name);
 
-  if( color<0 )
-    return -1;
+       if (color < 0)
+               return false;
 
-  entry = g_malloc(sizeof(color_definition_entry_t));
-  entry->color = color;
-  entry->r = r;
-  entry->g = g;
-  entry->b = b;
+       color_definition_entry_t *entry =
+               g_malloc(sizeof(color_definition_entry_t));
+       entry->color = color;
+       entry->r = r;
+       entry->g = g;
+       entry->b = b;
 
-  color_definition_list = g_list_append(color_definition_list, entry);
+       color_definition_list = g_list_append(color_definition_list, entry);
 
-  return 0;
+       return true;
 }
 
-
-int
-colors_assign(char *name, char *value)
+bool
+colors_assign(const char *name, const char *value)
 {
-  color_entry_t *entry = colors_lookup_by_name(name);
-  short color;
-
-  if( !entry )
-    {
-      if( !strcasecmp(NAME_BGCOLOR, name) )
-       {
-         if( (color=colors_str2color(value)) < -1 )
-           return -1;
-         if( color > COLORS )
-           color = color & ~COLOR_BRIGHT_MASK;
-         bg = color;
-         return 0;
+       color_entry_t *entry = colors_lookup_by_name(name);
+
+       if (!entry) {
+               fprintf(stderr, "%s: %s",
+                       _("Unknown color field"), name);
+               return false;
        }
-      fprintf(stderr,"Warning: Unknown color field - %s\n", name);
-      return -1;
-    }
 
-  if( (color=colors_str2color(value)) < -1 )
-    return -1;
-  entry->fg = color;
+       const int color = colors_str2color(value);
+       if (color == COLOR_ERROR)
+               return false;
 
-  return 0;
+       entry->color = color;
+       return true;
 }
 
-
-int
+void
 colors_start(void)
 {
-  if( has_colors() )
-    {
-      /* initialize color support */
-      start_color();
-      use_default_colors();
-      /* define any custom colors defined in the configuration file */
-      if( color_definition_list && can_change_color() )
-       {
-         GList *list = color_definition_list;
-
-         while( list )
-           {
-             color_definition_entry_t *entry = list->data;
-
-             if( entry->color <= COLORS )
-               init_color(entry->color, entry->r, entry->g, entry->b);
-             list=list->next;
-           }
+       if (has_colors()) {
+               /* initialize color support */
+               start_color();
+               use_default_colors();
+               /* define any custom colors defined in the configuration file */
+               if (color_definition_list && can_change_color()) {
+                       GList *list = color_definition_list;
+
+                       while (list) {
+                               color_definition_entry_t *entry = list->data;
+
+                               if (entry->color <= COLORS)
+                                       init_color(entry->color, entry->r,
+                                                  entry->g, entry->b);
+                               list = list->next;
+                       }
+               } else if (color_definition_list && !can_change_color())
+                       fprintf(stderr, "%s\n",
+                               _("Terminal lacks support for changing colors"));
+
+               if (options.enable_colors) {
+                       for (enum color i = 1; i < COLOR_END; ++i)
+                               /* update the color pairs */
+                               colors_update_pair(i);
+               }
+       } else if (options.enable_colors) {
+               fprintf(stderr, "%s\n",
+                       _("Terminal lacks color capabilities"));
+               options.enable_colors = 0;
        }
-      else if( !can_change_color() )
-       fprintf(stderr, "Terminal lacks support for changing colors!\n");
 
-      if( options.enable_colors )
-       {
-         int i = 0;
-
-         while(colors[i].name)
-           {
-             /* update the color pairs */
-             colors_update_pair(colors[i].id);
-             i++;
-           }
-
-       }
-    }
-  else if( options.enable_colors )
-    {
-      fprintf(stderr, "Terminal lacks color capabilities!\n");
-      options.enable_colors = 0;
-    }
-
-  /* free the color_definition_list */
-  if( color_definition_list )
-    {
-      GList *list = color_definition_list;
-
-      while( list )
-       {
-         g_free(list->data);
-         list=list->next;
+       /* free the color_definition_list */
+       if (color_definition_list) {
+               g_list_free_full(color_definition_list, g_free);
+               color_definition_list = NULL;
        }
-      g_list_free(color_definition_list);
-      color_definition_list = NULL;
-    }
-
-  return 0;
 }
+#endif
 
-
-int
-colors_use(WINDOW *w, int id)
+void
+colors_use(WINDOW *w, enum color id)
 {
-  color_entry_t *entry = colors_lookup(id);
-  short pair;
-  attr_t attrs;
-
-  if( !entry )
-    return -1;
-
-  wattr_get(w, &attrs, &pair, NULL);
-
-  if( options.enable_colors )
-    {
-      /* color mode */
-      if( attrs != entry->attrs || id!=pair )
-       wattr_set(w, entry->attrs, id, NULL);
-    }
-  else
-    {
-      /* mono mode */
-      if( attrs != entry->attrs )
-       wattrset(w, entry->attrs);
-    }
-  
-  return 0;
-}
+       color_entry_t *entry = &colors[id];
+
+       assert(id > 0 && id < COLOR_END);
+
+       attr_t attrs;
+       short pair;
+       fix_wattr_get(w, &attrs, &pair, NULL);
 
+#ifdef ENABLE_COLORS
+       if (options.enable_colors) {
+               /* color mode */
+               if ((int)attrs != entry->color || (short)id != pair)
+                       wattr_set(w, entry->color, id, NULL);
+       } else {
+#endif
+               /* mono mode */
+               if ((int)attrs != entry->mono)
+                       (void)wattrset(w, entry->mono);
+#ifdef ENABLE_COLORS
+       }
+#endif
+}