shorten options.c a little by moving some color stuff out of it
creates new coloratt.c file Also, this attempts to fulfill a wish-list item by paxed, to allow naming colors in symbols file by name as an alternative to using r-g-b values. The basic color names as well as html color names are supported.
This commit is contained in:
527
src/options.c
527
src/options.c
@@ -26,9 +26,6 @@ NEARDATA struct instance_flags iflags; /* provide linkage */
|
||||
#define PREV_MSGS 0
|
||||
#endif
|
||||
|
||||
static char *color_attr_to_str(color_attr *);
|
||||
static boolean color_attr_parse_str(color_attr *, char *);
|
||||
|
||||
/*
|
||||
* NOTE: If you add (or delete) an option, please review the following:
|
||||
* doc/options.txt
|
||||
@@ -340,17 +337,12 @@ static int petname_optfn(int, int, boolean, char *, char *);
|
||||
static int shared_menu_optfn(int, int, boolean, char *, char *);
|
||||
static int spcfn_misc_menu_cmd(int, int, boolean, char *, char *);
|
||||
|
||||
static const char *attr2attrname(int);
|
||||
static void basic_menu_colors(boolean);
|
||||
static const char * msgtype2name(int);
|
||||
static int query_msgtype(void);
|
||||
static boolean msgtype_add(int, char *);
|
||||
static void free_one_msgtype(int);
|
||||
static int msgtype_count(void);
|
||||
static boolean test_regex_pattern(const char *, const char *);
|
||||
static boolean add_menu_coloring_parsed(const char *, int, int);
|
||||
static void free_one_menu_coloring(int);
|
||||
static int count_menucolors(void);
|
||||
static boolean parse_role_opt(int, boolean, const char *, char *, char **);
|
||||
static char *get_cnf_role_opt(int);
|
||||
static unsigned int longest_option_name(int, int);
|
||||
@@ -2095,61 +2087,6 @@ optfn_menu_shift_right(int optidx, int req, boolean negated,
|
||||
|
||||
/* end of shared key assignments for menu commands */
|
||||
|
||||
static char *
|
||||
color_attr_to_str(color_attr *ca)
|
||||
{
|
||||
static char buf[BUFSZ];
|
||||
|
||||
Sprintf(buf, "%s&%s",
|
||||
clr2colorname(ca->color),
|
||||
attr2attrname(ca->attr));
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* parse string like "color&attr" into color_attr */
|
||||
static boolean
|
||||
color_attr_parse_str(color_attr *ca, char *str)
|
||||
{
|
||||
char buf[BUFSZ];
|
||||
char *amp = NULL;
|
||||
int tmp, c = NO_COLOR, a = ATR_NONE;
|
||||
|
||||
(void) strncpy(buf, str, sizeof buf - 1);
|
||||
buf[sizeof buf - 1] = '\0';
|
||||
|
||||
if ((amp = strchr(buf, '&')) != 0)
|
||||
*amp = '\0';
|
||||
|
||||
if (amp) {
|
||||
amp++;
|
||||
c = match_str2clr(buf);
|
||||
a = match_str2attr(amp, TRUE);
|
||||
/* FIXME: match_str2clr & match_str2attr give config_error_add(),
|
||||
so this is useless */
|
||||
if (c >= CLR_MAX && a == -1) {
|
||||
/* try other way around */
|
||||
c = match_str2clr(amp);
|
||||
a = match_str2attr(buf, TRUE);
|
||||
}
|
||||
if (c >= CLR_MAX || a == -1)
|
||||
return FALSE;
|
||||
} else {
|
||||
/* one param only */
|
||||
tmp = match_str2attr(buf, FALSE);
|
||||
if (tmp == -1) {
|
||||
tmp = match_str2clr(buf);
|
||||
if (tmp >= CLR_MAX)
|
||||
return FALSE;
|
||||
c = tmp;
|
||||
} else {
|
||||
a = tmp;
|
||||
}
|
||||
}
|
||||
ca->attr = a;
|
||||
ca->color = c;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
optfn_menu_headings(
|
||||
int optidx,
|
||||
@@ -7419,335 +7356,6 @@ parsebindings(char *bindings)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Color support functions and data for "color"
|
||||
*
|
||||
* Used by: optfn_()
|
||||
*
|
||||
*/
|
||||
|
||||
struct color_names {
|
||||
const char *name;
|
||||
int color;
|
||||
};
|
||||
|
||||
static const struct color_names 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 },
|
||||
{ "orange", CLR_ORANGE },
|
||||
{ "light green", CLR_BRIGHT_GREEN },
|
||||
{ "yellow", CLR_YELLOW },
|
||||
{ "light blue", CLR_BRIGHT_BLUE },
|
||||
{ "light magenta", CLR_BRIGHT_MAGENTA },
|
||||
{ "light cyan", CLR_BRIGHT_CYAN },
|
||||
{ "white", CLR_WHITE },
|
||||
{ "no color", NO_COLOR },
|
||||
{ (const char *) 0, CLR_BLACK }, /* everything after this is an alias */
|
||||
{ "transparent", NO_COLOR },
|
||||
{ "purple", CLR_MAGENTA },
|
||||
{ "light purple", CLR_BRIGHT_MAGENTA },
|
||||
{ "bright purple", CLR_BRIGHT_MAGENTA },
|
||||
{ "grey", CLR_GRAY },
|
||||
{ "bright red", CLR_ORANGE },
|
||||
{ "bright green", CLR_BRIGHT_GREEN },
|
||||
{ "bright blue", CLR_BRIGHT_BLUE },
|
||||
{ "bright magenta", CLR_BRIGHT_MAGENTA },
|
||||
{ "bright cyan", CLR_BRIGHT_CYAN }
|
||||
};
|
||||
|
||||
struct attr_names {
|
||||
const char *name;
|
||||
int attr;
|
||||
};
|
||||
|
||||
static const struct attr_names attrnames[] = {
|
||||
{ "none", ATR_NONE },
|
||||
{ "bold", ATR_BOLD },
|
||||
{ "dim", ATR_DIM },
|
||||
{ "italic", ATR_ITALIC },
|
||||
{ "underline", ATR_ULINE },
|
||||
{ "blink", ATR_BLINK },
|
||||
{ "inverse", ATR_INVERSE },
|
||||
{ (const char *) 0, ATR_NONE }, /* everything after this is an alias */
|
||||
{ "normal", ATR_NONE },
|
||||
{ "uline", ATR_ULINE },
|
||||
{ "reverse", ATR_INVERSE },
|
||||
};
|
||||
|
||||
const char *
|
||||
clr2colorname(int clr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SIZE(colornames); i++)
|
||||
if (colornames[i].name && colornames[i].color == clr)
|
||||
return colornames[i].name;
|
||||
return (char *) 0;
|
||||
}
|
||||
|
||||
int
|
||||
match_str2clr(char *str)
|
||||
{
|
||||
int i, c = CLR_MAX;
|
||||
|
||||
/* allow "lightblue", "light blue", and "light-blue" to match "light blue"
|
||||
(also junk like "_l i-gh_t---b l u e" but we won't worry about that);
|
||||
also copes with trailing space; caller has removed any leading space */
|
||||
for (i = 0; i < SIZE(colornames); i++)
|
||||
if (colornames[i].name
|
||||
&& fuzzymatch(str, colornames[i].name, " -_", TRUE)) {
|
||||
c = colornames[i].color;
|
||||
break;
|
||||
}
|
||||
if (i == SIZE(colornames) && digit(*str))
|
||||
c = atoi(str);
|
||||
|
||||
if (c < 0 || c >= CLR_MAX) {
|
||||
config_error_add("Unknown color '%.60s'", str);
|
||||
c = CLR_MAX; /* "none of the above" */
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static const char *
|
||||
attr2attrname(int attr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SIZE(attrnames); i++)
|
||||
if (attrnames[i].attr == attr)
|
||||
return attrnames[i].name;
|
||||
return (char *) 0;
|
||||
}
|
||||
|
||||
int
|
||||
match_str2attr(const char *str, boolean complain)
|
||||
{
|
||||
int i, a = -1;
|
||||
|
||||
for (i = 0; i < SIZE(attrnames); i++)
|
||||
if (attrnames[i].name
|
||||
&& fuzzymatch(str, attrnames[i].name, " -_", TRUE)) {
|
||||
a = attrnames[i].attr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (a == -1 && complain)
|
||||
config_error_add("Unknown text attribute '%.50s'", str);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
extern const char regex_id[]; /* from sys/share/<various>regex.{c,cpp} */
|
||||
|
||||
DISABLE_WARNING_FORMAT_NONLITERAL
|
||||
|
||||
/* True: temporarily replace menu color entries with a fake set of menu
|
||||
colors, { "light blue"=light_blue, "blue"=blue, "red"=red, &c }, that
|
||||
illustrates most colors for use when the pick-a-color menu is rendered;
|
||||
suppresses black and white because one of those will likely be invisible
|
||||
due to matching the background; False: restore user-specified colorings */
|
||||
static void
|
||||
basic_menu_colors(boolean load_colors)
|
||||
{
|
||||
if (load_colors) {
|
||||
/* replace normal menu colors with a set specifically for colors */
|
||||
gs.save_menucolors = iflags.use_menu_color;
|
||||
gs.save_colorings = gm.menu_colorings;
|
||||
|
||||
iflags.use_menu_color = TRUE;
|
||||
if (gc.color_colorings) {
|
||||
/* use the alternate colorings which were set up previously */
|
||||
gm.menu_colorings = gc.color_colorings;
|
||||
} else {
|
||||
/* create the alternate colorings once */
|
||||
char cnm[QBUFSZ];
|
||||
int i, c;
|
||||
boolean pmatchregex = !strcmpi(regex_id, "pmatchregex");
|
||||
const char *patternfmt = pmatchregex ? "*%s" : "%s";
|
||||
|
||||
/* menu_colorings pointer has been saved; clear it in order
|
||||
to add the alternate entries as if from scratch */
|
||||
gm.menu_colorings = (struct menucoloring *) 0;
|
||||
|
||||
/* this orders the patterns last-in/first-out; that means
|
||||
that the "light <foo>" variations come before the basic
|
||||
"<foo>" ones, which is exactly what we want */
|
||||
for (i = 0; i < SIZE(colornames); ++i) {
|
||||
if (!colornames[i].name) /* first alias entry has no name */
|
||||
break;
|
||||
c = colornames[i].color;
|
||||
if (c == CLR_BLACK || c == CLR_WHITE || c == NO_COLOR)
|
||||
continue; /* skip these */
|
||||
Sprintf(cnm, patternfmt, colornames[i].name);
|
||||
add_menu_coloring_parsed(cnm, c, ATR_NONE);
|
||||
}
|
||||
|
||||
/* right now, menu_colorings contains the alternate color list;
|
||||
remember that list for future pick-a-color instances and
|
||||
also keep it as is for this instance */
|
||||
gc.color_colorings = gm.menu_colorings;
|
||||
}
|
||||
} else {
|
||||
/* restore normal user-specified menu colors */
|
||||
iflags.use_menu_color = gs.save_menucolors;
|
||||
gm.menu_colorings = gs.save_colorings;
|
||||
}
|
||||
}
|
||||
|
||||
RESTORE_WARNING_FORMAT_NONLITERAL
|
||||
|
||||
int
|
||||
query_color(const char *prompt, int dflt_color)
|
||||
{
|
||||
winid tmpwin;
|
||||
anything any;
|
||||
int i, pick_cnt;
|
||||
menu_item *picks = (menu_item *) 0;
|
||||
|
||||
/* replace user patterns with color name ones and force 'menucolors' On */
|
||||
basic_menu_colors(TRUE);
|
||||
|
||||
tmpwin = create_nhwindow(NHW_MENU);
|
||||
start_menu(tmpwin, MENU_BEHAVE_STANDARD);
|
||||
any = cg.zeroany;
|
||||
for (i = 0; i < SIZE(colornames); i++) {
|
||||
if (!colornames[i].name)
|
||||
break;
|
||||
any.a_int = i + 1;
|
||||
add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
|
||||
ATR_NONE, NO_COLOR, colornames[i].name,
|
||||
(colornames[i].color == dflt_color) ? MENU_ITEMFLAGS_SELECTED
|
||||
: MENU_ITEMFLAGS_NONE);
|
||||
}
|
||||
end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick a color");
|
||||
pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
|
||||
destroy_nhwindow(tmpwin);
|
||||
|
||||
/* remove temporary color name patterns and restore user-specified ones;
|
||||
reset 'menucolors' option to its previous value */
|
||||
basic_menu_colors(FALSE);
|
||||
|
||||
if (pick_cnt > 0) {
|
||||
i = colornames[picks[0].item.a_int - 1].color;
|
||||
/* pick_cnt==2: explicitly picked something other than the
|
||||
preselected entry */
|
||||
if (pick_cnt == 2 && i == NO_COLOR)
|
||||
i = colornames[picks[1].item.a_int - 1].color;
|
||||
free((genericptr_t) picks);
|
||||
return i;
|
||||
} else if (pick_cnt == 0) {
|
||||
/* pick_cnt==0: explicitly picking preselected entry toggled it off */
|
||||
return dflt_color;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ask about highlighting attribute; for menu headers and menu
|
||||
coloring patterns, only one attribute at a time is allowed;
|
||||
for status highlighting, multiple attributes are allowed [overkill;
|
||||
life would be much simpler if that were restricted to one also...] */
|
||||
int
|
||||
query_attr(const char *prompt, int dflt_attr)
|
||||
{
|
||||
winid tmpwin;
|
||||
anything any;
|
||||
int i, pick_cnt;
|
||||
menu_item *picks = (menu_item *) 0;
|
||||
boolean allow_many = (prompt && !strncmpi(prompt, "Choose", 6));
|
||||
int clr = NO_COLOR;
|
||||
|
||||
tmpwin = create_nhwindow(NHW_MENU);
|
||||
start_menu(tmpwin, MENU_BEHAVE_STANDARD);
|
||||
any = cg.zeroany;
|
||||
for (i = 0; i < SIZE(attrnames); i++) {
|
||||
if (!attrnames[i].name)
|
||||
break;
|
||||
any.a_int = i + 1;
|
||||
add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
|
||||
attrnames[i].attr, clr, attrnames[i].name,
|
||||
(attrnames[i].attr == dflt_attr) ? MENU_ITEMFLAGS_SELECTED
|
||||
: MENU_ITEMFLAGS_NONE);
|
||||
}
|
||||
end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick an attribute");
|
||||
pick_cnt = select_menu(tmpwin, allow_many ? PICK_ANY : PICK_ONE, &picks);
|
||||
destroy_nhwindow(tmpwin);
|
||||
if (pick_cnt > 0) {
|
||||
int j, k = 0;
|
||||
|
||||
if (allow_many) {
|
||||
/* PICK_ANY, with one preselected entry (ATR_NONE) which
|
||||
should be excluded if any other choices were picked */
|
||||
for (i = 0; i < pick_cnt; ++i) {
|
||||
j = picks[i].item.a_int - 1;
|
||||
if (attrnames[j].attr != ATR_NONE || pick_cnt == 1) {
|
||||
switch (attrnames[j].attr) {
|
||||
case ATR_NONE:
|
||||
k = HL_NONE;
|
||||
break;
|
||||
case ATR_BOLD:
|
||||
k |= HL_BOLD;
|
||||
break;
|
||||
case ATR_DIM:
|
||||
k |= HL_DIM;
|
||||
break;
|
||||
case ATR_ITALIC:
|
||||
k |= HL_ITALIC;
|
||||
break;
|
||||
case ATR_ULINE:
|
||||
k |= HL_ULINE;
|
||||
break;
|
||||
case ATR_BLINK:
|
||||
k |= HL_BLINK;
|
||||
break;
|
||||
case ATR_INVERSE:
|
||||
k |= HL_INVERSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* PICK_ONE, but might get 0 or 2 due to preselected entry */
|
||||
j = picks[0].item.a_int - 1;
|
||||
/* pick_cnt==2: explicitly picked something other than the
|
||||
preselected entry */
|
||||
if (pick_cnt == 2 && attrnames[j].attr == dflt_attr)
|
||||
j = picks[1].item.a_int - 1;
|
||||
k = attrnames[j].attr;
|
||||
}
|
||||
free((genericptr_t) picks);
|
||||
return k;
|
||||
} else if (pick_cnt == 0 && !allow_many) {
|
||||
/* PICK_ONE, preselected entry explicitly chosen */
|
||||
return dflt_attr;
|
||||
}
|
||||
/* either ESC to explicitly cancel (pick_cnt==-1) or
|
||||
PICK_ANY with preselected entry toggled off and nothing chosen */
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean
|
||||
query_color_attr(color_attr *ca, const char *prompt)
|
||||
{
|
||||
int c, a;
|
||||
|
||||
c = query_color(prompt, ca->color);
|
||||
if (c == -1)
|
||||
return FALSE;
|
||||
a = query_attr(prompt, ca->attr);
|
||||
if (a == -1)
|
||||
return FALSE;
|
||||
ca->color = c;
|
||||
ca->attr = a;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
xint8 msgtyp;
|
||||
@@ -7974,141 +7582,6 @@ test_regex_pattern(const char *str, const char *errmsg)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static boolean
|
||||
add_menu_coloring_parsed(const char *str, int c, int a)
|
||||
{
|
||||
static const char re_error[] = "Menucolor regex error";
|
||||
struct menucoloring *tmp;
|
||||
|
||||
if (!str)
|
||||
return FALSE;
|
||||
tmp = (struct menucoloring *) alloc(sizeof *tmp);
|
||||
tmp->match = regex_init();
|
||||
/* test_regex_pattern() has already validated this regexp but parsing
|
||||
it again could conceivably run out of memory */
|
||||
if (!regex_compile(str, tmp->match)) {
|
||||
char errbuf[BUFSZ];
|
||||
char *re_error_desc = regex_error_desc(tmp->match, errbuf);
|
||||
|
||||
/* free first in case reason for regcomp failure was out-of-memory */
|
||||
regex_free(tmp->match);
|
||||
free((genericptr_t) tmp);
|
||||
config_error_add("%s: %s", re_error, re_error_desc);
|
||||
return FALSE;
|
||||
}
|
||||
tmp->next = gm.menu_colorings;
|
||||
tmp->origstr = dupstr(str);
|
||||
tmp->color = c;
|
||||
tmp->attr = a;
|
||||
gm.menu_colorings = tmp;
|
||||
iflags.use_menu_color = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* parse '"regex_string"=color&attr' and add it to menucoloring */
|
||||
boolean
|
||||
add_menu_coloring(char *tmpstr) /* never Null but could be empty */
|
||||
{
|
||||
int c = NO_COLOR, a = ATR_NONE;
|
||||
char *tmps, *cs, *amp;
|
||||
char str[BUFSZ];
|
||||
|
||||
(void) strncpy(str, tmpstr, sizeof str - 1);
|
||||
str[sizeof str - 1] = '\0';
|
||||
|
||||
if ((cs = strchr(str, '=')) == 0) {
|
||||
config_error_add("Malformed MENUCOLOR");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tmps = cs + 1; /* advance past '=' */
|
||||
mungspaces(tmps);
|
||||
if ((amp = strchr(tmps, '&')) != 0)
|
||||
*amp = '\0';
|
||||
|
||||
c = match_str2clr(tmps);
|
||||
if (c >= CLR_MAX)
|
||||
return FALSE;
|
||||
|
||||
if (amp) {
|
||||
tmps = amp + 1; /* advance past '&' */
|
||||
a = match_str2attr(tmps, TRUE);
|
||||
if (a == -1)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* the regexp portion here has not been condensed by mungspaces() */
|
||||
*cs = '\0';
|
||||
tmps = str;
|
||||
if (*tmps == '"' || *tmps == '\'') {
|
||||
cs--;
|
||||
while (isspace((uchar) *cs))
|
||||
cs--;
|
||||
if (*cs == *tmps) {
|
||||
*cs = '\0';
|
||||
tmps++;
|
||||
}
|
||||
}
|
||||
return add_menu_coloring_parsed(tmps, c, a);
|
||||
}
|
||||
|
||||
/* release all menu color patterns */
|
||||
void
|
||||
free_menu_coloring(void)
|
||||
{
|
||||
/* either menu_colorings or color_colorings or both might need to
|
||||
be freed or already be Null; do-loop will iterate at most twice */
|
||||
do {
|
||||
struct menucoloring *tmp, *tmp2;
|
||||
|
||||
for (tmp = gm.menu_colorings; tmp; tmp = tmp2) {
|
||||
tmp2 = tmp->next;
|
||||
regex_free(tmp->match);
|
||||
free((genericptr_t) tmp->origstr);
|
||||
free((genericptr_t) tmp);
|
||||
}
|
||||
gm.menu_colorings = gc.color_colorings;
|
||||
gc.color_colorings = (struct menucoloring *) 0;
|
||||
} while (gm.menu_colorings);
|
||||
}
|
||||
|
||||
/* release a specific menu color pattern; not used for color_colorings */
|
||||
static void
|
||||
free_one_menu_coloring(int idx) /* 0 .. */
|
||||
{
|
||||
struct menucoloring *tmp = gm.menu_colorings;
|
||||
struct menucoloring *prev = NULL;
|
||||
|
||||
while (tmp) {
|
||||
if (idx == 0) {
|
||||
struct menucoloring *next = tmp->next;
|
||||
|
||||
regex_free(tmp->match);
|
||||
free((genericptr_t) tmp->origstr);
|
||||
free((genericptr_t) tmp);
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
else
|
||||
gm.menu_colorings = next;
|
||||
return;
|
||||
}
|
||||
idx--;
|
||||
prev = tmp;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
count_menucolors(void)
|
||||
{
|
||||
struct menucoloring *tmp;
|
||||
int count = 0;
|
||||
|
||||
for (tmp = gm.menu_colorings; tmp; tmp = tmp->next)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
/* parse 'role' or 'race' or 'gender' or 'alignment' */
|
||||
static boolean
|
||||
parse_role_opt(
|
||||
|
||||
Reference in New Issue
Block a user