Add menucolors

-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
This commit is contained in:
Pasi Kallinen
2015-03-17 22:50:11 +02:00
parent 9f66b234ed
commit c8e781c418
15 changed files with 253 additions and 1 deletions

View 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

View 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
@@ -51,4 +55,18 @@
#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 */

View 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 */

View 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 ### */

View 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 */

View 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 */

View 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");

View 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);

View 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 */

View 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

View 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",

View 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 {

View 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;

View 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);

View 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);
}