]> granicus.if.org Git - nethack/commitdiff
Add menucolors
authorPasi Kallinen <paxed@alt.org>
Tue, 17 Mar 2015 20:50:11 +0000 (22:50 +0200)
committerPasi Kallinen <paxed@alt.org>
Thu, 2 Apr 2015 17:16:25 +0000 (20:16 +0300)
-Add a boolean option menucolors to toggle menu color
-Add MENUCOLOR -config file option

TODO:
-Better support for win32
-Support more windowports
-Update Guidebook
-Allow changing menucolor lines in-game

15 files changed:
doc/fixes35.0
include/color.h
include/config.h
include/extern.h
include/flag.h
src/decl.c
src/files.c
src/options.c
src/save.c
sys/share/NetHack.cnf
util/makedefs.c
win/tty/wintty.c
win/win32/mhmap.c
win/win32/mhmap.h
win/win32/mhmenu.c

index 46b87d58f3877bc6df3006a885e6fabfbb8a1760..5fa64755d3e0a6142d61aac54483a7007529507e 100644 (file)
@@ -1132,6 +1132,7 @@ smartphone: added "Type Cmd" command that allows to type arbitrary commands
 smartphone: added Q(quiver) command to "Attack" layout
 smartphone: fixed F command to prompt for direction
 unix,vms: altmeta option to handle terminals which send "ESC c" for Alt+c
+tty,win32gui,win32tty: add menucolors
 
 
 NetHack Community Patches (or Variation) Included
index ab6fe0462b9147cf59d55462637264723fe46763..f7bec4a08e6e01c808e7a593e9d0d46b35642188 100644 (file)
@@ -7,6 +7,10 @@
 #ifndef COLOR_H
 #define COLOR_H
 
+#ifdef MENU_COLOR_REGEX
+#include <regex.h>
+#endif
+
 /*
  * The color scheme used is tailored for an IBM PC.  It consists of the
  * standard 8 colors, folowed by their bright counterparts.  There are
 #define DRAGON_SILVER  CLR_BRIGHT_CYAN
 #define HI_ZAP         CLR_BRIGHT_BLUE
 
+struct menucoloring {
+# ifdef MENU_COLOR_REGEX
+#  ifdef MENU_COLOR_REGEX_POSIX
+    regex_t match;
+#  else
+    struct re_pattern_buffer match;
+#  endif
+# else
+    char *match;
+# endif
+    int color, attr;
+    struct menucoloring *next;
+};
+
 #endif /* COLOR_H */
index d8c96af14ef7064836f9898bda49df4b434ea620..c5e53abf6c3a275e4b359cbba778834f463c37c0 100644 (file)
@@ -437,6 +437,14 @@ typedef unsigned char      uchar;
  * bugs left here.
  */
 
+/* Menucolors */
+# define MENU_COLOR_REGEX  /* use GNU regex */
+/*# define MENU_COLOR_REGEX_POSIX*/ /* use POSIX regex */
+/* if neither is defined, uses pmatch()
+ * pmatch() provides basic globbing: '*' and '?' wildcards.
+ */
+
+
 #define STATUS_VIA_WINDOWPORT  /* re-work of the status line updating process */
 #define STATUS_HILITES         /* support hilites of status fields */
 /* #define WINCHAIN*/          /* stacked window systems */
index 4e2780c6b6bca65bf7520eafd671a13bb6122c36..28680c5fa2256608ff733618c452db79569448ae 100644 (file)
@@ -1583,6 +1583,9 @@ E void FDECL(parsesymbols, (char *));
 E struct symparse *FDECL(match_sym, (char *));
 E void NDECL(set_playmode);
 E int FDECL(sym_val, (char *));
+E boolean FDECL(add_menu_coloring, (char *));
+E boolean FDECL(get_menu_coloring, (char *, int *, int *));
+E void NDECL(free_menu_coloring);
 
 /* ### pager.c ### */
 
index 9eb7a68c6007447640399c432df63366681ada1b..072fb4a30c24eaa1a2fb580ad5b844c834b4f9b1 100644 (file)
@@ -201,6 +201,7 @@ struct instance_flags {
        boolean  rlecomp;       /* run-length comp of levels when writing savefile */
        uchar    num_pad_mode;
        boolean  echo;          /* 1 to echo characters */
+       boolean use_menu_color; /* use color in menus; only if wc_color */
 #if 0
        boolean  DECgraphics;   /* use DEC VT-xxx extended character set */
        boolean  IBMgraphics;   /* use IBM extended character set */
index 6529336b24283b3b0394a8007161acd2464d3547..d9429dbcfb8c757ddaef5aa5f2402e2d3dbfdc19 100644 (file)
@@ -219,6 +219,8 @@ NEARDATA struct c_color_names c_color_names = {
        "white"
 };
 
+struct menucoloring *menu_colorings = NULL;
+
 const char *c_obj_colors[] = {
        "black",                /* CLR_BLACK */
        "red",                  /* CLR_RED */
index 179cd34377ebde655c44f4d286326d8f43eae6d7..f3b9eb610c8df51f75f72daa12245e4dd8a5a82e 100644 (file)
@@ -2290,6 +2290,8 @@ int               src;
        } else if (match_varname(buf, "BOULDER", 3)) {
            (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE,
                              1, "BOULDER");
+       } else if (match_varname(buf, "MENUCOLOR", 9)) {
+           (void) add_menu_coloring(bufp);
        } else if (match_varname(buf, "WARNINGS", 5)) {
            (void) get_uchars(fp, buf, bufp, translate, FALSE,
                                        WARNCOUNT, "WARNINGS");
index 742958fc326ee92e749a82cc36c914464e45f27e..54e0471e8eaef393ace792a0e42525f011e28e70 100644 (file)
@@ -141,6 +141,7 @@ static struct Bool_Opt
        {"mail", (boolean *)0, TRUE, SET_IN_FILE},
 #endif
        {"mention_walls", &iflags.mention_walls, FALSE, SET_IN_GAME},
+       {"menucolors", &iflags.use_menu_color, FALSE,  SET_IN_GAME},
        /* for menu debugging only*/
        {"menu_tab_sep", &iflags.menu_tab_sep, FALSE, SET_IN_GAME},
        {"menu_objsyms", &iflags.menu_head_objsym, FALSE, SET_IN_GAME},
@@ -1090,6 +1091,174 @@ STATIC_VAR const struct paranoia_opts {
        { ~0, "all", 3, 0, 0, 0 },      /* ditto */
 };
 
+
+extern struct menucoloring *menu_colorings;
+
+static const struct {
+   const char *name;
+   const int color;
+} colornames[] = {
+   {"black", CLR_BLACK},
+   {"red", CLR_RED},
+   {"green", CLR_GREEN},
+   {"brown", CLR_BROWN},
+   {"blue", CLR_BLUE},
+   {"magenta", CLR_MAGENTA},
+   {"cyan", CLR_CYAN},
+   {"gray", CLR_GRAY},
+   {"grey", CLR_GRAY},
+   {"orange", CLR_ORANGE},
+   {"lightgreen", CLR_BRIGHT_GREEN},
+   {"yellow", CLR_YELLOW},
+   {"lightblue", CLR_BRIGHT_BLUE},
+   {"lightmagenta", CLR_BRIGHT_MAGENTA},
+   {"lightcyan", CLR_BRIGHT_CYAN},
+   {"white", CLR_WHITE}
+};
+
+static const struct {
+   const char *name;
+   const int attr;
+} attrnames[] = {
+     {"none", ATR_NONE},
+     {"bold", ATR_BOLD},
+     {"dim", ATR_DIM},
+     {"underline", ATR_ULINE},
+     {"blink", ATR_BLINK},
+     {"inverse", ATR_INVERSE}
+};
+
+/* parse '"regex_string"=color&attr' and add it to menucoloring */
+boolean
+add_menu_coloring(str)
+char *str;
+{
+    int i, c = NO_COLOR, a = ATR_NONE;
+    struct menucoloring *tmp;
+    char *tmps, *cs = strchr(str, '=');
+#ifdef MENU_COLOR_REGEX_POSIX
+    int errnum;
+    char errbuf[80];
+#endif
+    const char *err = (char *)0;
+
+    if (!cs || !str) return FALSE;
+
+    tmps = cs;
+    tmps++;
+    while (*tmps && isspace(*tmps)) tmps++;
+
+    for (i = 0; i < SIZE(colornames); i++)
+       if (strstri(tmps, colornames[i].name) == tmps) {
+           c = colornames[i].color;
+           break;
+       }
+    if ((i == SIZE(colornames)) && (*tmps >= '0' && *tmps <='9'))
+       c = atoi(tmps);
+
+    if (c > 15) return FALSE;
+
+    tmps = strchr(str, '&');
+    if (tmps) {
+       tmps++;
+       while (*tmps && isspace(*tmps)) tmps++;
+       for (i = 0; i < SIZE(attrnames); i++)
+           if (strstri(tmps, attrnames[i].name) == tmps) {
+               a = attrnames[i].attr;
+               break;
+           }
+       if ((i == SIZE(attrnames)) && (*tmps >= '0' && *tmps <='9'))
+           a = atoi(tmps);
+    }
+
+    *cs = '\0';
+    tmps = str;
+    if ((*tmps == '"') || (*tmps == '\'')) {
+       cs--;
+       while (isspace(*cs)) cs--;
+       if (*cs == *tmps) {
+           *cs = '\0';
+           tmps++;
+       }
+    }
+
+    tmp = (struct menucoloring *)alloc(sizeof(struct menucoloring));
+#ifdef MENU_COLOR_REGEX
+#ifdef MENU_COLOR_REGEX_POSIX
+    errnum = regcomp(&tmp->match, tmps, REG_EXTENDED | REG_NOSUB);
+    if (errnum != 0)
+    {
+       regerror(errnum, &tmp->match, errbuf, sizeof(errbuf));
+       err = errbuf;
+    }
+#else
+    tmp->match.translate = 0;
+    tmp->match.fastmap = 0;
+    tmp->match.buffer = 0;
+    tmp->match.allocated = 0;
+    tmp->match.regs_allocated = REGS_FIXED;
+    err = re_compile_pattern(tmps, strlen(tmps), &tmp->match);
+#endif
+#else
+    tmp->match = (char *)alloc(strlen(tmps)+1);
+    (void) memcpy((genericptr_t)tmp->match, (genericptr_t)tmps, strlen(tmps)+1);
+#endif
+    if (err) {
+       raw_printf("\nMenucolor regex error: %s\n", err);
+       wait_synch();
+       free(tmp);
+       return FALSE;
+    } else {
+       tmp->next = menu_colorings;
+       tmp->color = c;
+       tmp->attr = a;
+       menu_colorings = tmp;
+       return TRUE;
+    }
+}
+
+boolean
+get_menu_coloring(str, color, attr)
+char *str;
+int *color, *attr;
+{
+    struct menucoloring *tmpmc;
+    if (iflags.use_menu_color)
+       for (tmpmc = menu_colorings; tmpmc; tmpmc = tmpmc->next)
+#ifdef MENU_COLOR_REGEX
+# ifdef MENU_COLOR_REGEX_POSIX
+           if (regexec(&tmpmc->match, str, 0, NULL, 0) == 0) {
+# else
+           if (re_search(&tmpmc->match, str, strlen(str), 0, 9999, 0) >= 0) {
+# endif
+#else
+           if (pmatch(tmpmc->match, str)) {
+#endif
+               *color = tmpmc->color;
+               *attr = tmpmc->attr;
+               return TRUE;
+           }
+    return FALSE;
+}
+
+void
+free_menu_coloring()
+{
+    struct menucoloring *tmp = menu_colorings;
+
+    while (tmp) {
+       struct menucoloring *tmp2 = tmp->next;
+#ifdef MENU_COLOR_REGEX
+       (void) regfree(&tmp->match);
+#else
+       free(tmp->match);
+#endif
+       free(tmp);
+       tmp = tmp2;
+    }
+}
+
+
 void
 parseoptions(opts, tinitial, tfrom_file)
 register char *opts;
@@ -1431,6 +1600,16 @@ boolean tinitial, tfrom_file;
                return;
        }
 
+       /* menucolor:"regex_string"=color */
+       fullname = "menucolor";
+       if (match_optname(opts, fullname, 9, TRUE)) {
+           if (negated) bad_negation(fullname, FALSE);
+           else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
+               if (!add_menu_coloring(op))
+                   badoption(opts);
+           return;
+       }
+
        fullname = "msghistory";
        if (match_optname(opts, fullname, 3, TRUE)) {
                if (duplicate) complain_about_duplicate(opts,1);
index 4a4363e9ecb28f6466cc3a13bf423de2adfecd7d..094f5404217e22bfa3e45908d98a7e6a1ca365ba 100644 (file)
@@ -1296,6 +1296,7 @@ void
 freedynamicdata()
 {
        unload_qtlist();
+       free_menu_coloring();
        free_invbuf();  /* let_to_name (invent.c) */
        free_youbuf();  /* You_buf,&c (pline.c) */
        tmp_at(DISP_FREEMEM, 0);        /* temporary display effects */
index 29d8ce910777e29195e87ac63cba494c6cb6127b..2b16dfcc78c0e12ee47f17bb2fde9f736992d433 100644 (file)
@@ -121,3 +121,14 @@ OPTIONS=time,noshowexp,number_pad:2,lit_corridor
 # DEC Rainbows will hang if rawio is set, so they should instead use:
 #OPTIONS=BIOS,DECgraphics
 
+# Colored menus.
+#OPTIONS=menucolors
+# Syntax is: MENUCOLOR="string_to_match"=color&attribute
+#  Colors: black, red, green, brown, blue, magenta, cyan, gray, orange,
+#          lightgreen, yellow, lightblue, lightmagenta, lightcyan, white.
+#  Attributes: none, bold, dim, underline, blink, inverse.
+#MENUCOLOR=" blessed "=green
+#MENUCOLOR=" holy "=green
+#MENUCOLOR=" cursed "=red
+#MENUCOLOR=" unholy "=red
+#MENUCOLOR=" cursed .* (being worn)"=orange&underline
index 8555178e1f4a09927bf6cb98fc7af936870da54b..7bb689bef8232e006f8e8a1e9b1c30886b19bf67 100644 (file)
@@ -1294,6 +1294,11 @@ static const char *build_opts[] = {
 #ifdef NEWS
                "news file",
 #endif
+#ifdef MENU_COLOR_REGEX
+               "menu colors via regular expressions",
+#else
+               "menu colors via pmatch",
+#endif
 #ifdef OVERLAY
 # ifdef MOVERLAY
                "MOVE overlays",
index 8c8f5294c1c6a85807fef9bc3c6940f7147a6317..0aaf9e92dc285e0d1168a91374a61a561cf1877a 100644 (file)
@@ -1462,6 +1462,8 @@ struct WinDesc *cw;
                for (page_lines = 0, curr = page_start;
                        curr != page_end;
                        page_lines++, curr = curr->next) {
+                   int color = NO_COLOR, attr = ATR_NONE;
+                   boolean menucolr = FALSE;
                    if (curr->selector)
                        *rp++ = curr->selector;
 
@@ -1477,6 +1479,11 @@ struct WinDesc *cw;
                     * actually output the character.  We're faster doing
                     * this.
                     */
+                   if (iflags.use_menu_color &&
+                       (menucolr = get_menu_coloring(curr->str, &color,&attr))) {
+                       term_start_attr(attr);
+                       if (color != NO_COLOR) term_start_color(color);
+                   } else
                    term_start_attr(curr->attr);
                    for (n = 0, cp = curr->str;
 #ifndef WIN32CON
@@ -1494,6 +1501,10 @@ struct WinDesc *cw;
                                (void) putchar('#'); /* count selected */
                        } else
                            (void) putchar(*cp);
+                  if (iflags.use_menu_color && menucolr) {
+                      if (color != NO_COLOR) term_end_color();
+                      term_end_attr(attr);
+                  } else
                    term_end_attr(curr->attr);
                }
            } else {
index bd3c60a172c05750893dea842d5e712133d76603..d07af75c5c95810058237e4a4f53fd3b6b6f786b 100644 (file)
@@ -45,7 +45,6 @@ static void nhcoord2display(PNHMapWindow data, int x, int y, LPRECT lpOut);
 #if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2)
 static void nhglyph2charcolor(short glyph, uchar* ch, int* color);
 #endif
-static COLORREF nhcolor_to_RGB(int c);
 
 HWND mswin_init_map_window () {
        static int run_once = 0;
index 54a61473019a91a5c897463081eddf599928aa17..e33f64ec340e9f25e75a2994459128bc2c8778d7 100644 (file)
@@ -12,6 +12,7 @@
 #include "global.h"
 
 
+COLORREF nhcolor_to_RGB (int c);
 HWND mswin_init_map_window (void);
 void mswin_map_stretch(HWND hWnd, LPSIZE lpsz, BOOL redraw);
 int mswin_map_mode(HWND hWnd, int mode);
index e39470dc16704cd6f29147dc88ddc1053ce57ac9..6f32931e327d4a49482fe82667b95458272244f7 100644 (file)
@@ -917,6 +917,9 @@ BOOL onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
        int column;
        int spacing = 0;
 
+       int color = NO_COLOR, attr;
+       boolean menucolr = FALSE;
+
        lpdis = (LPDRAWITEMSTRUCT) lParam; 
 
     /* If there are no list box items, skip this message. */
@@ -967,6 +970,13 @@ BOOL onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
                        buf[0] = item->accelerator;
                        buf[1] = '\x0';
 
+                       if (iflags.use_menu_color &&
+                           (menucolr = get_menu_coloring(item->str, &color,&attr))) {
+                           /* TODO: use attr too */
+                           if (color != NO_COLOR)
+                               SetTextColor(lpdis->hDC, nhcolor_to_RGB(color));
+                       }
+
                        SetRect( &drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom );
                        DrawText(lpdis->hDC, NH_A2W(buf, wbuf, 2), 1, &drawRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
                }