Files
nethack/src/coloratt.c
PatR 2e9d0753c8 enhance the windowcolors option
When processing
|OPTIONS=windowcolors:window-type foreground-color/background-color
parse the color values and use their names rather than the player's
raw options text.  Affects the feedback from 'm O' and is essential
for the next feature.

Accept either "gray" or "grey" where colortable[] always uses "gray"
(half a dozen or so instances), and accept dash or underscore where
colortable[] always uses dash (many instances).

Also, complain about 'window-type' if it isn't recognized as one of
menu, message, status, or text.  [For curses, the complaint gets
written to stdout and is then immediately erased as it goes into full
screen mode.  That's a general problem, not specific to this option.]
2024-03-18 13:24:21 -07:00

786 lines
31 KiB
C

/* NetHack 3.7 coloratt.c $NHDT-Date: 1710792438 2024/03/18 20:07:18 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.2 $ */
/* Copyright (c) Pasi Kallinen, 2024 */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
#include <ctype.h>
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 },
};
/* { colortyp, tableindex, rgbindx, name, hexval, r, g, b }, */
static struct nethack_color colortable[] = {
{ nh_color, 0, 0, "black", "", 0, 0, 0 },
{ nh_color, 1, 0, "red", "", 255, 0, 0 },
{ nh_color, 2, 0, "green", "", 34, 139, 34 },
{ nh_color, 3, 0, "brown", "", 165, 42, 42 },
{ nh_color, 4, 0, "blue", "", 0, 0, 255 },
{ nh_color, 5, 0, "magenta", "", 255, 0, 255 },
{ nh_color, 6, 0, "cyan", "", 0, 255, 255 },
{ nh_color, 7, 0, "gray", "", 128, 128, 128 },
{ no_color, 8, 0, "nocolor", "", 0, 0, 0 },
{ nh_color, 9, 0, "orange", "", 255, 165, 0 },
{ nh_color, 10, 0, "bright-green", "", 0, 128, 0 },
{ nh_color, 11, 0, "yellow", "", 255, 255, 0 },
{ nh_color, 12, 0, "bright-blue", "", 173, 216, 230 },
{ nh_color, 13, 0, "bright-magenta", "", 147, 112, 219 },
{ nh_color, 14, 0, "light-cyan", "", 224, 255, 255 },
{ nh_color, 15, 0, "white", "", 255, 255, 255 },
{ rgb_color, 16, 0, "maroon", "#800000", 128, 0, 0 },
{ rgb_color, 17, 1, "dark-red", "#8B0000", 139, 0, 0 },
{ rgb_color, 18, 2, "brown", "#A52A2A", 165, 42, 42 },
{ rgb_color, 19, 3, "firebrick", "#B22222", 178, 34, 34 },
{ rgb_color, 20, 4, "crimson", "#DC143C", 220, 20, 60 },
{ rgb_color, 21, 5, "red", "#FF0000", 255, 0, 0 },
{ rgb_color, 22, 6, "tomato", "#FF6347", 255, 99, 71 },
{ rgb_color, 23, 7, "coral", "#FF7F50", 255, 127, 80 },
{ rgb_color, 24, 8, "indian-red", "#CD5C5C", 205, 92, 92 },
{ rgb_color, 25, 9, "light-coral", "#F08080", 240, 128, 128 },
{ rgb_color, 26, 10, "dark-salmon", "#E9967A", 233, 150, 122 },
{ rgb_color, 27, 11, "salmon", "#FA8072", 250, 128, 114 },
{ rgb_color, 28, 12, "light-salmon", "#FFA07A", 255, 160, 122 },
{ rgb_color, 29, 13, "orange-red", "#FF4500", 255, 69, 0 },
{ rgb_color, 30, 14, "dark-orange", "#FF8C00", 255, 140, 0 },
{ rgb_color, 31, 15, "orange", "#FFA500", 255, 165, 0 },
{ rgb_color, 32, 16, "gold", "#FFD700", 255, 215, 0 },
{ rgb_color, 33, 17, "dark-golden-rod", "#B8860B", 184, 134, 11 },
{ rgb_color, 34, 18, "golden-rod", "#DAA520", 218, 165, 32 },
{ rgb_color, 35, 19, "pale-golden-rod", "#EEE8AA", 238, 232, 170 },
{ rgb_color, 36, 20, "dark-khaki", "#BDB76B", 189, 183, 107 },
{ rgb_color, 37, 21, "khaki", "#F0E68C", 240, 230, 140 },
{ rgb_color, 38, 22, "olive", "#808000", 128, 128, 0 },
{ rgb_color, 39, 23, "yellow", "#FFFF00", 255, 255, 0 },
{ rgb_color, 40, 24, "yellow-green", "#9ACD32", 154, 205, 50 },
{ rgb_color, 41, 25, "dark-olive-green", "#556B2F", 85, 107, 47 },
{ rgb_color, 42, 26, "olive-drab", "#6B8E23", 107, 142, 35 },
{ rgb_color, 43, 27, "lawn-green", "#7CFC00", 124, 252, 0 },
{ rgb_color, 44, 28, "chart-reuse", "#7FFF00", 127, 255, 0 },
{ rgb_color, 45, 29, "green-yellow", "#ADFF2F", 173, 255, 47 },
{ rgb_color, 46, 30, "dark-green", "#006400", 0, 100, 0 },
{ rgb_color, 47, 31, "green", "#008000", 0, 128, 0 },
{ rgb_color, 48, 32, "forest-green", "#228B22", 34, 139, 34 },
{ rgb_color, 49, 33, "lime", "#00FF00", 0, 255, 0 },
{ rgb_color, 50, 34, "lime-green", "#32CD32", 50, 205, 50 },
{ rgb_color, 51, 35, "light-green", "#90EE90", 144, 238, 144 },
{ rgb_color, 52, 36, "pale-green", "#98FB98", 152, 251, 152 },
{ rgb_color, 53, 37, "dark-sea-green", "#8FBC8F", 143, 188, 143 },
{ rgb_color, 54, 38, "medium-spring-green", "#00FA9A", 0, 250, 154 },
{ rgb_color, 55, 39, "spring-green", "#00FF7F", 0, 255, 127 },
{ rgb_color, 56, 40, "sea-green", "#2E8B57", 46, 139, 87 },
{ rgb_color, 57, 41, "medium-aqua-marine", "#66CDAA", 102, 205, 170 },
{ rgb_color, 58, 42, "medium-sea-green", "#3CB371", 60, 179, 113 },
{ rgb_color, 59, 43, "light-sea-green", "#20B2AA", 32, 178, 170 },
{ rgb_color, 60, 44, "dark-slate-gray", "#2F4F4F", 47, 79, 79 },
{ rgb_color, 61, 45, "teal", "#008080", 0, 128, 128 },
{ rgb_color, 62, 46, "dark-cyan", "#008B8B", 0, 139, 139 },
{ rgb_color, 63, 47, "aqua", "#00FFFF", 0, 255, 255 },
{ rgb_color, 64, 48, "cyan", "#00FFFF", 0, 255, 255 },
{ rgb_color, 65, 49, "light-cyan", "#E0FFFF", 224, 255, 255 },
{ rgb_color, 66, 50, "dark-turquoise", "#00CED1", 0, 206, 209 },
{ rgb_color, 67, 51, "turquoise", "#40E0D0", 64, 224, 208 },
{ rgb_color, 68, 52, "medium-turquoise", "#48D1CC", 72, 209, 204 },
{ rgb_color, 69, 53, "pale-turquoise", "#AFEEEE", 175, 238, 238 },
{ rgb_color, 70, 54, "aqua-marine", "#7FFFD4", 127, 255, 212 },
{ rgb_color, 71, 55, "powder-blue", "#B0E0E6", 176, 224, 230 },
{ rgb_color, 72, 56, "cadet-blue", "#5F9EA0", 95, 158, 160 },
{ rgb_color, 73, 57, "steel-blue", "#4682B4", 70, 130, 180 },
{ rgb_color, 74, 58, "corn-flower-blue", "#6495ED", 100, 149, 237 },
{ rgb_color, 75, 59, "deep-sky-blue", "#00BFFF", 0, 191, 255 },
{ rgb_color, 76, 60, "dodger-blue", "#1E90FF", 30, 144, 255 },
{ rgb_color, 77, 61, "light-blue", "#ADD8E6", 173, 216, 230 },
{ rgb_color, 78, 62, "sky-blue", "#87CEEB", 135, 206, 235 },
{ rgb_color, 79, 63, "light-sky-blue", "#87CEFA", 135, 206, 250 },
{ rgb_color, 80, 64, "midnight-blue", "#191970", 25, 25, 112 },
{ rgb_color, 81, 65, "navy", "#000080", 0, 0, 128 },
{ rgb_color, 82, 66, "dark-blue", "#00008B", 0, 0, 139 },
{ rgb_color, 83, 67, "medium-blue", "#0000CD", 0, 0, 205 },
{ rgb_color, 84, 68, "blue", "#0000FF", 0, 0, 255 },
{ rgb_color, 85, 69, "royal-blue", "#4169E1", 65, 105, 225 },
{ rgb_color, 86, 70, "blue-violet", "#8A2BE2", 138, 43, 226 },
{ rgb_color, 87, 71, "indigo", "#4B0082", 75, 0, 130 },
{ rgb_color, 88, 72, "dark-slate-blue", "#483D8B", 72, 61, 139 },
{ rgb_color, 89, 73, "slate-blue", "#6A5ACD", 106, 90, 205 },
{ rgb_color, 90, 74, "medium-slate-blue", "#7B68EE", 123, 104, 238 },
{ rgb_color, 91, 75, "medium-purple", "#9370DB", 147, 112, 219 },
{ rgb_color, 92, 76, "dark-magenta", "#8B008B", 139, 0, 139 },
{ rgb_color, 93, 77, "dark-violet", "#9400D3", 148, 0, 211 },
{ rgb_color, 94, 78, "dark-orchid", "#9932CC", 153, 50, 204 },
{ rgb_color, 95, 79, "medium-orchid", "#BA55D3", 186, 85, 211 },
{ rgb_color, 96, 80, "purple", "#800080", 128, 0, 128 },
{ rgb_color, 97, 81, "thistle", "#D8BFD8", 216, 191, 216 },
{ rgb_color, 98, 82, "plum", "#DDA0DD", 221, 160, 221 },
{ rgb_color, 99, 83, "violet", "#EE82EE", 238, 130, 238 },
{ rgb_color, 100, 84, "magenta", "#FF00FF", 255, 0, 255 },
{ rgb_color, 101, 85, "orchid", "#DA70D6", 218, 112, 214 },
{ rgb_color, 102, 86, "medium-violet-red", "#C71585", 199, 21, 133 },
{ rgb_color, 103, 87, "pale-violet-red", "#DB7093", 219, 112, 147 },
{ rgb_color, 104, 88, "deep-pink", "#FF1493", 255, 20, 147 },
{ rgb_color, 105, 89, "hot-pink", "#FF69B4", 255, 105, 180 },
{ rgb_color, 106, 90, "light-pink", "#FFB6C1", 255, 182, 193 },
{ rgb_color, 107, 91, "pink", "#FFC0CB", 255, 192, 203 },
{ rgb_color, 108, 92, "antique-white", "#FAEBD7", 250, 235, 215 },
{ rgb_color, 109, 93, "beige", "#F5F5DC", 245, 245, 220 },
{ rgb_color, 110, 94, "bisque", "#FFE4C4", 255, 228, 196 },
{ rgb_color, 111, 95, "blanched-almond", "#FFEBCD", 255, 235, 205 },
{ rgb_color, 112, 96, "wheat", "#F5DEB3", 245, 222, 179 },
{ rgb_color, 113, 97, "corn-silk", "#FFF8DC", 255, 248, 220 },
{ rgb_color, 114, 98, "lemon-chiffon", "#FFFACD", 255, 250, 205 },
{ rgb_color, 115, 99, "light-golden-rod-yellow", "#FAFAD2", 250, 250, 210 },
{ rgb_color, 116, 100, "light-yellow", "#FFFFE0", 255, 255, 224 },
{ rgb_color, 117, 101, "saddle-brown", "#8B4513", 139, 69, 19 },
{ rgb_color, 118, 102, "sienna", "#A0522D", 160, 82, 45 },
{ rgb_color, 119, 103, "chocolate", "#D2691E", 210, 105, 30 },
{ rgb_color, 120, 104, "peru", "#CD853F", 205, 133, 63 },
{ rgb_color, 121, 105, "sandy-brown", "#F4A460", 244, 164, 96 },
{ rgb_color, 122, 106, "burly-wood", "#DEB887", 222, 184, 135 },
{ rgb_color, 123, 107, "tan", "#D2B48C", 210, 180, 140 },
{ rgb_color, 124, 108, "rosy-brown", "#BC8F8F", 188, 143, 143 },
{ rgb_color, 125, 109, "moccasin", "#FFE4B5", 255, 228, 181 },
{ rgb_color, 126, 110, "navajo-white", "#FFDEAD", 255, 222, 173 },
{ rgb_color, 127, 111, "peach-puff", "#FFDAB9", 255, 218, 185 },
{ rgb_color, 128, 112, "misty-rose", "#FFE4E1", 255, 228, 225 },
{ rgb_color, 129, 113, "lavender-blush", "#FFF0F5", 255, 240, 245 },
{ rgb_color, 130, 114, "linen", "#FAF0E6", 250, 240, 230 },
{ rgb_color, 131, 115, "old-lace", "#FDF5E6", 253, 245, 230 },
{ rgb_color, 132, 116, "papaya-whip", "#FFEFD5", 255, 239, 213 },
{ rgb_color, 133, 117, "sea-shell", "#FFF5EE", 255, 245, 238 },
{ rgb_color, 134, 118, "mint-cream", "#F5FFFA", 245, 255, 250 },
{ rgb_color, 135, 119, "slate-gray", "#708090", 112, 128, 144 },
{ rgb_color, 136, 120, "light-slate-gray", "#778899", 119, 136, 153 },
{ rgb_color, 137, 121, "light-steel-blue", "#B0C4DE", 176, 196, 222 },
{ rgb_color, 138, 122, "lavender", "#E6E6FA", 230, 230, 250 },
{ rgb_color, 139, 123, "floral-white", "#FFFAF0", 255, 250, 240 },
{ rgb_color, 140, 124, "alice-blue", "#F0F8FF", 240, 248, 255 },
{ rgb_color, 141, 125, "ghost-white", "#F8F8FF", 248, 248, 255 },
{ rgb_color, 142, 126, "honeydew", "#F0FFF0", 240, 255, 240 },
{ rgb_color, 143, 127, "ivory", "#FFFFF0", 255, 255, 240 },
{ rgb_color, 144, 128, "azure", "#F0FFFF", 240, 255, 255 },
{ rgb_color, 145, 129, "snow", "#FFFAFA", 255, 250, 250 },
{ rgb_color, 146, 130, "black", "#000000", 0, 0, 0 },
{ rgb_color, 147, 131, "dim-gray", "#696969", 105, 105, 105 },
{ rgb_color, 148, 132, "gray", "#808080", 128, 128, 128 },
{ rgb_color, 149, 133, "dark-gray", "#A9A9A9", 169, 169, 169 },
{ rgb_color, 150, 134, "silver", "#C0C0C0", 192, 192, 192 },
{ rgb_color, 151, 135, "light-gray", "#D3D3D3", 211, 211, 211 },
{ rgb_color, 152, 136, "gainsboro", "#DCDCDC", 220, 220, 220 },
{ rgb_color, 153, 137, "white-smoke", "#F5F5F5", 245, 245, 245 },
{ rgb_color, 154, 138, "white", "#FFFFFF", 255, 255, 255 },
};
int32
colortable_to_int32(struct nethack_color *cte)
{
int32 clr = NO_COLOR | NH_BASIC_COLOR;
if (cte->colortyp == rgb_color)
clr = (cte->r << 16) | (cte->g << 8) | cte->b;
else if (cte->colortyp == nh_color)
clr = cte->tableindex | NH_BASIC_COLOR;
return clr;
}
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 */
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, FALSE);
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, FALSE);
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, FALSE);
if (tmp >= CLR_MAX)
return FALSE;
c = tmp;
} else {
a = tmp;
}
}
ca->attr = a;
ca->color = c;
return TRUE;
}
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;
}
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;
}
/*
* Color support functions and data for "color"
*
* Used by: optfn_()
*
*/
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, boolean suppress_msg)
{
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) {
if (!suppress_msg)
config_error_add("Unknown color '%.60s'", str);
c = CLR_MAX; /* "none of the above" */
}
return c;
}
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;
}
/* 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;
}
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;
}
DISABLE_WARNING_FORMAT_NONLITERAL
extern const char regex_id[]; /* from sys/share/<various>regex.{c,cpp} */
/* set up a menu for picking a color, one that shows each name in its color;
overrides player's MENUCOLORS with a set of "blue"=blue, "red"=red, and
so forth; suppresses color for black and white because one of those will
likely be invisible due to matching the background; the alternate set of
MENUCOLORS is kept around for potential re-use */
void
basic_menu_colors(
boolean load_colors) /* True: temporarily replace menu color entries with
* a fake set of menu colors which match their names;
* False: restore user-specified colorings */
{
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 (so that the
shorter basic names won't get false matches as substrings
of the longer ones) */
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
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, FALSE);
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 */
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;
}
}
int
count_menucolors(void)
{
struct menucoloring *tmp;
int count = 0;
for (tmp = gm.menu_colorings; tmp; tmp = tmp->next)
count++;
return count;
}
/* returns -1 on no-match.
* buf is NONNULLARG1
*/
int32
check_enhanced_colors(char *buf)
{
char xtra = '\0'; /* used to catch trailing junk after "#rrggbb" */
unsigned r, g, b;
int32 retcolor = -1, color;
if ((color = match_str2clr(buf, TRUE)) != CLR_MAX) {
retcolor = color | NH_BASIC_COLOR;
} else if (sscanf(buf, "#%02x%02x%02x%c", &r, &g, &b, &xtra) >= 3) {
retcolor = !xtra ? (int32) ((r << 16) | (g << 8) | b) : -1;
} else {
/* altbuf: allow user's "grey" to match colortable[]'s "gray";
* fuzzymatch(): ignore spaces, hyphens, and underscores so that
* space or underscore in user-supplied name will match hyphen
* [note: caller splits text at spaces so we won't see any here]
*/
char *altbuf = NULL, *grey = strstri(buf, "grey");
ptrdiff_t greyoffset = grey ? (grey - buf) : -1;
if (greyoffset >= 0) {
altbuf = dupstr(buf);
/* use direct copy because strsubst() is case-sensitive */
/*(void) strncpy(&altbuf[greyoffset], "gray", 4);*/
(void) memcpy(altbuf + greyoffset, "gray", 4);
}
for (color = 0; color < SIZE(colortable); ++color) {
if (fuzzymatch(buf, colortable[color].name, " -_", TRUE)
|| (altbuf && fuzzymatch(altbuf, colortable[color].name,
" -_", TRUE))) {
retcolor = colortable_to_int32(&colortable[color]);
break;
}
}
if (altbuf)
free(altbuf);
}
return retcolor;
}
/* return the canonical name of a particular color */
const char *
wc_color_name(int32 colorindx)
{
static char hexcolor[sizeof "#rrggbb"]; /* includes room for '\0' */
const char *result = "no-color";
if (colorindx >= 0) {
int32 basicindx = colorindx & ~NH_BASIC_COLOR;
/* if colorindx has NH_BASIC_COLOR bit set, basicindx won't,
so differing implies a basic color */
if (basicindx != colorindx) {
assert(basicindx < 16);
result = colortable[basicindx].name;
} else {
int indx;
long r = (colorindx >> 16) & 0x0000ff, /* shift rrXXXX to rr */
g = (colorindx >> 8) & 0x0000ff, /* shift XXggXX to gg */
b = colorindx & 0x0000ff; /* mask XXXXbb to bb */
Snprintf(hexcolor, sizeof hexcolor, "#%02x%02x%02x",
(uint8) r, (uint8) g, (uint8) b);
result = hexcolor;
/* override hex value if this is a named color */
for (indx = 16; indx < SIZE(colortable); ++indx)
if (colortable[indx].r == r
&& colortable[indx].g == g
&& colortable[indx].b == b) {
result = colortable[indx].name;
break;
}
}
}
return result;
}
/*coloratt.c*/