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:
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 ### */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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");
|
||||
|
||||
179
src/options.c
179
src/options.c
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user