1 /* NetHack 3.7	winstat.c	$NHDT-Date: 1611697183 2021/01/26 21:39:43 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.30 $ */
2 /* Copyright (c) Dean Luick, 1992				  */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 /*
6  * Status window routines.  This file supports both the "traditional"
7  * tty status display and a "fancy" status display.  A tty status is
8  * made if a popup window is requested, otherwise a fancy status is
9  * made.  This code assumes that only one fancy status will ever be made.
10  * Currently, only one status window (of any type) is _ever_ made.
11  */
12 
13 #ifndef SYSV
14 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
15 #endif
16 
17 #include <X11/Intrinsic.h>
18 #include <X11/IntrinsicP.h> /* for XtResizeWidget() and XtConfigureWidget() */
19 #include <X11/StringDefs.h>
20 #include <X11/Shell.h>
21 #include <X11/Xaw/AsciiText.h>
22 #include <X11/Xaw/Cardinals.h> /* just for ONE, TWO */
23 #include <X11/Xaw/Form.h>
24 #include <X11/Xaw/Paned.h>
25 #include <X11/Xaw/Label.h>
26 #include <X11/Xaw/Viewport.h>
27 /*#include <X11/Xatom.h>*/
28 
29 #ifdef PRESERVE_NO_SYSV
30 #ifdef SYSV
31 #undef SYSV
32 #endif
33 #undef PRESERVE_NO_SYSV
34 #endif
35 
36 #include "hack.h"
37 #include "winX.h"
38 #include "xwindow.h"
39 
40 /*
41  * Fancy status form entry storage indices.
42  */
43 #define F_DUMMY     0
44 #define F_STR       1
45 #define F_DEX       2
46 #define F_CON       3
47 #define F_INT       4
48 #define F_WIS       5
49 #define F_CHA       6
50 
51 #define F_NAME      7 /* title: "Name the Rank" where rank is role-specific */
52 #define F_DLEVEL    8 /* location: dungeon branch and level */
53 #define F_GOLD      9
54 #define F_HP       10
55 #define F_MAXHP    11
56 #define F_POWER    12
57 #define F_MAXPOWER 13
58 #define F_AC       14
59 #define F_XP_LEVL  15
60 /*#define F_HD F_XP_LEVL*/
61 #define F_EXP_PTS  16
62 #define F_ALIGN    17
63 #define F_TIME     18
64 #define F_SCORE    19
65 
66 /* status conditions grouped by columns; tty orders these differently;
67    hunger/encumbrance/movement used to be in the middle with fatal
68    conditions on the left but those columns have been swapped and
69    renumbered to match new order (forcing shown_stats[] to be reordered);
70    some mutually exclusive conditions are overloaded during display--
71    they're separate within shown_stats[] but share the same widget */
72 #define F_HUNGER   20
73 #define F_ENCUMBER 21
74 #define F_TRAPPED  22
75 #define F_TETHERED 23 /* overloads trapped rather than having its own slot */
76 #define F_LEV      24
77 #define F_FLY      25
78 #define F_RIDE     26
79 
80 #define F_GRABBED  27
81 #define F_STONE    28
82 #define F_SLIME    29
83 #define F_STRNGL   30
84 #define F_FOODPOIS 31
85 #define F_TERMILL  32
86 #define F_IN_LAVA  33 /* could overload trapped but severity differs a lot */
87 
88 #define F_HELD     34 /* could overload grabbed but severity differs a lot */
89 #define F_HOLDING  35 /* overloads held */
90 #define F_BLIND    36
91 #define F_DEAF     37
92 #define F_STUN     38
93 #define F_CONF     39
94 #define F_HALLU    40
95 #define F_WITHER   41
96 
97 #define NUM_STATS  42
98 
99 static int condcolor(long, unsigned long *);
100 static int condattr(long, unsigned long *);
101 static void HiliteField(Widget, int, int, int, XFontStruct **);
102 static void PrepStatusField(int, Widget, const char *);
103 static void DisplayCond(int, unsigned long *);
104 static int render_conditions(int, int);
105 #ifdef STATUS_HILITES
106 static void tt_reset_color(int, int, unsigned long *);
107 #endif
108 static void tt_status_fixup(void);
109 static Widget create_tty_status_field(int, int, Widget, Widget);
110 static Widget create_tty_status(Widget, Widget);
111 static void update_fancy_status_field(int, int, int);
112 static void update_fancy_status(boolean);
113 static Widget create_fancy_status(Widget, Widget);
114 static void destroy_fancy_status(struct xwindow *);
115 static void create_status_window_fancy(struct xwindow *, boolean, Widget);
116 static void create_status_window_tty(struct xwindow *, boolean, Widget);
117 static void destroy_status_window_fancy(struct xwindow *);
118 static void destroy_status_window_tty(struct xwindow *);
119 static void adjust_status_fancy(struct xwindow *, const char *);
120 static void adjust_status_tty(struct xwindow *, const char *);
121 
122 extern const char *status_fieldfmt[MAXBLSTATS];
123 extern char *status_vals[MAXBLSTATS];
124 extern boolean status_activefields[MAXBLSTATS];
125 
126 static unsigned long X11_condition_bits, old_condition_bits;
127 static int X11_status_colors[MAXBLSTATS],
128            old_field_colors[MAXBLSTATS],
129            old_cond_colors[32];
130 static int hpbar_percent, hpbar_color;
131 /* Number of conditions displayed during this update and last update.
132    When the last update had more, the excess need to be erased. */
133 static int next_cond_indx = 0, prev_cond_indx = 0;
134 
135 /* TODO: support statuslines:3 in addition to 2 for the tty-style status */
136 #define X11_NUM_STATUS_LINES 2
137 #define X11_NUM_STATUS_FIELD 15
138 
139 static enum statusfields X11_fieldorder[][X11_NUM_STATUS_FIELD] = {
140     { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN,
141       BL_SCORE, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH,
142       BL_FLUSH },
143     { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
144       BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER,
145       BL_CAP, BL_CONDITION, BL_FLUSH }
146 };
147 
148 /* condition list for tty-style display, roughly in order of importance */
149 static struct tt_condinfo {
150     unsigned long mask;
151     const char *text;
152 } tt_condorder[] = {
153     { BL_MASK_GRAB, "Grabbed!" },
154     { BL_MASK_STONE, "Stone" },
155     { BL_MASK_SLIME, "Slime" },
156     { BL_MASK_STRNGL, "Strngl" },
157     { BL_MASK_FOODPOIS, "FoodPois" },
158     { BL_MASK_TERMILL, "TermIll" },
159     { BL_MASK_INLAVA, "InLava" },
160     { BL_MASK_WITHER, "Wither" },
161     { BL_MASK_HELD, "Held" },
162     { BL_MASK_HELD, "Holding" },
163     { BL_MASK_BLIND, "Blind" },
164     { BL_MASK_DEAF, "Deaf" },
165     { BL_MASK_STUN, "Stun" },
166     { BL_MASK_CONF, "Conf" },
167     { BL_MASK_HALLU, "Hallu" },
168     { BL_MASK_TRAPPED, "Trapped" },
169     { BL_MASK_TETHERED, "Tethered", },
170     { BL_MASK_LEV, "Lev" },
171     { BL_MASK_FLY, "Fly" },
172     { BL_MASK_RIDE, "Ride" },
173 };
174 
175 static const char *fancy_status_hilite_colors[] = {
176     "grey15",
177     "red3",
178     "dark green",
179     "saddle brown",
180     "blue",
181     "magenta3",
182     "dark cyan",
183     "web gray",
184     "",	/* NO_COLOR */
185     "orange",
186     "green3",
187     "goldenrod",
188     "royal blue",
189     "magenta",
190     "cyan",
191     "white",
192 };
193 
194 static Widget X11_status_widget;
195 static Widget X11_status_labels[MAXBLSTATS];
196 static Widget X11_cond_labels[32]; /* Ugh */
197 static XFontStruct *X11_status_font;
198 static Pixel X11_status_fg, X11_status_bg;
199 
200 struct xwindow *xw_status_win;
201 
202 static int
condcolor(long bm,unsigned long * bmarray)203 condcolor(long bm, unsigned long *bmarray)
204 {
205     int i;
206 
207     if (bm && bmarray)
208         for (i = 0; i < CLR_MAX; ++i) {
209             if (bmarray[i] && (bm & bmarray[i]))
210                 return i;
211         }
212     return NO_COLOR;
213 }
214 
215 static int
condattr(long bm,unsigned long * bmarray)216 condattr(long bm, unsigned long *bmarray)
217 {
218     int attr = 0;
219     int i;
220 
221     if (bm && bmarray) {
222         for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) {
223             if (bmarray[i] && (bm & bmarray[i])) {
224                 switch(i) {
225                 case HL_ATTCLR_DIM:
226                     attr |= HL_DIM;
227                     break;
228                 case HL_ATTCLR_BLINK:
229                     attr |= HL_BLINK;
230                     break;
231                 case HL_ATTCLR_ULINE:
232                     attr |= HL_ULINE;
233                     break;
234                 case HL_ATTCLR_INVERSE:
235                     attr |= HL_INVERSE;
236                     break;
237                 case HL_ATTCLR_BOLD:
238                     attr |= HL_BOLD;
239                     break;
240                 }
241             }
242         }
243     }
244     return attr;
245 }
246 
247 void
X11_status_init(void)248 X11_status_init(void)
249 {
250     int i;
251 
252     /* no color and no attributes */
253     for (i = 0; i < MAXBLSTATS; ++i)
254         X11_status_colors[i] = old_field_colors[i] = NO_COLOR;
255     for (i = 0; i < SIZE(old_cond_colors); ++i)
256         old_cond_colors[i] = NO_COLOR;
257     hpbar_percent = 0, hpbar_color = NO_COLOR;
258     X11_condition_bits = old_condition_bits = 0L;
259     /* let genl_status_init do most of the initialization */
260     genl_status_init();
261 }
262 
263 void
X11_status_finish(void)264 X11_status_finish(void)
265 {
266     /* nothing */
267     return;
268 }
269 
270 void
X11_status_enablefield(int fieldidx,const char * nm,const char * fmt,boolean enable)271 X11_status_enablefield(int fieldidx, const char *nm,
272                        const char *fmt, boolean enable)
273 {
274     genl_status_enablefield(fieldidx, nm, fmt, enable);
275 }
276 
277 #if 0
278 int
279 cond_bm2idx(unsigned long bm)
280 {
281     int i;
282 
283     for (i = 0; i < 32; i++)
284         if ((1 << i) == bm)
285             return i;
286     return -1;
287 }
288 #endif
289 
290 /* highlight a tty-style status field (or condition) */
291 static void
HiliteField(Widget label,int fld,int cond,int colrattr,XFontStruct ** font_p)292 HiliteField(Widget label,
293             int fld, int cond, int colrattr,
294             XFontStruct **font_p)
295 {
296 #ifdef STATUS_HILITES
297     static Pixel grayPxl, blackPxl, whitePxl;
298     Arg args[6];
299     Cardinal num_args;
300     XFontStruct *font = X11_status_font;
301     Pixel px, fg = X11_status_fg, bg = X11_status_bg;
302     struct xwindow *xw = xw_status_win;
303     int colr, attr;
304 
305 #ifdef TEXTCOLOR
306     if ((colrattr & 0x00ff) >= CLR_MAX)
307     /* for !TEXTCOLOR, the following line is unconditional */
308 #endif
309         colrattr = (colrattr & ~0x00ff) | NO_COLOR;
310     colr = colrattr & 0x00ff; /* guaranteed to be >= 0 and < CLR_MAX */
311     attr = (colrattr >> 8) & 0x00ff;
312 
313     /* potentially used even for !TEXTCOLOR configuration */
314     if (!grayPxl) {/* one-time init */
315         grayPxl = get_nhcolor(xw, CLR_GRAY).pixel;
316         blackPxl = get_nhcolor(xw, CLR_BLACK).pixel;
317         whitePxl = get_nhcolor(xw, CLR_WHITE).pixel;
318     }
319     /* [shouldn't be necessary; setting up gray will set up all colors] */
320     if (colr != NO_COLOR && !xw->nh_colors[colr].pixel)
321         (void) get_nhcolor(xw, colr);
322 
323     /* handle highlighting if caller has specified that; set foreground,
324        background, and font even if not specified this time in case they
325        used modified values last time (which would stick if not reset) */
326     (void) memset((genericptr_t) args, 0, sizeof args);
327     num_args = 0;
328     if (colr != NO_COLOR)
329         fg = xw->nh_colors[colr].pixel;
330     if ((attr & HL_INVERSE) != 0) {
331         px = fg;
332         fg = bg;
333         bg = px;
334     }
335     /* foreground and background might both default to black, so we
336        need to force one to be different if/when they're the same
337        (actually, tt_status_fixup() takes care of that nowadays);
338        using gray to implement 'dim' only works for black and white
339        (or color+'inverse' when former background was black or white) */
340     if (fg == bg
341         || ((attr & HL_DIM) != 0 && (fg == whitePxl || fg == blackPxl)))
342         fg = (fg != grayPxl) ? grayPxl
343              : (fg != blackPxl) ? blackPxl
344                : whitePxl;
345     XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++;
346     XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++;
347     if (attr & HL_BOLD) {
348         load_boldfont(xw_status_win, label);
349         if (xw_status_win->boldfs)
350             font = xw_status_win->boldfs;
351     }
352     XtSetArg(args[num_args], nhStr(XtNfont), font); num_args++;
353     XtSetValues(label, args, num_args);
354 
355     /* return possibly modified font to caller so that text width
356        measurement can use it */
357     if (font_p)
358         *font_p = font;
359 #else /*!STATUS_HILITES*/
360     nhUse(label);
361     nhUse(font_p);
362 #endif /*?STATUS_HILITES*/
363     if (fld != BL_CONDITION)
364         old_field_colors[fld] = colrattr;
365     else
366         old_cond_colors[cond] = colrattr;
367 }
368 
369 /* set up a specific field other than 'condition'; its general location
370    was specified during widget creation but it might need adjusting */
371 static void
PrepStatusField(int fld,Widget label,const char * text)372 PrepStatusField(int fld, Widget label, const char *text)
373 {
374     Arg args[6];
375     Cardinal num_args;
376     Dimension lbl_wid;
377     XFontStruct *font = X11_status_font;
378     int colrattr = X11_status_colors[fld];
379     struct status_info_t *si = xw_status_win->Win_info.Status_info;
380 
381     /* highlight if color and/or attribute(s) are different from last time */
382     if (colrattr != old_field_colors[fld])
383         HiliteField(label, fld, 0, colrattr, &font);
384 
385     num_args = 0;
386     (void) memset((genericptr_t) args, 0, sizeof args);
387     /* set up the current text to be displayed */
388     if (text && *text) {
389         lbl_wid = 2 * si->in_wd + XTextWidth(font, text, (int) strlen(text));
390     } else {
391         text = "";
392         lbl_wid = 1;
393     }
394     XtSetArg(args[num_args], nhStr(XtNlabel), text); num_args++;
395     /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
396     XtSetValues(label, args, num_args);
397     XtResizeWidget(label, lbl_wid, si->ht, si->brd);
398 }
399 
400 /* set up one status condition for tty-style status display */
401 static void
DisplayCond(int c_idx,unsigned long * colormasks)402 DisplayCond(int c_idx, /* index into tt_condorder[] */
403             unsigned long *colormasks)
404 {
405     Widget label;
406     Arg args[6];
407     Cardinal num_args;
408     Dimension lbl_wid;
409     XFontStruct *font = X11_status_font;
410     int coloridx, attrmask, colrattr, idx;
411     unsigned long bm = tt_condorder[c_idx].mask;
412     const char *text = tt_condorder[c_idx].text;
413     struct status_info_t *si = xw_status_win->Win_info.Status_info;
414 
415     if ((X11_condition_bits & bm) == 0)
416         return;
417 
418     /* widgets have been created for every condition; we allocate them
419        from left to right rather than keeping their original assignments */
420     idx = next_cond_indx;
421     label = X11_cond_labels[idx];
422 
423     /* handle highlighting if caller requests it */
424     coloridx = condcolor(bm, colormasks);
425     attrmask = condattr(bm, colormasks);
426     colrattr = (attrmask << 8) | coloridx;
427     if (colrattr != old_cond_colors[c_idx])
428         HiliteField(label, BL_CONDITION, c_idx, colrattr, &font);
429 
430     (void) memset((genericptr_t) args, 0, sizeof args);
431     num_args = 0;
432     /* set the condition text and its width; this widget might have
433        been displaying a different condition last time around */
434     XtSetArg(args[num_args], nhStr(XtNlabel), text); num_args++;
435     /* measure width after maybe changing font [HiliteField()] */
436     lbl_wid = 2 * si->in_wd + XTextWidth(font, text, (int) strlen(text));
437     /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
438 
439     /* make this condition widget be ready for display */
440     XtSetValues(label, args, num_args);
441     XtResizeWidget(label, lbl_wid, si->ht, si->brd);
442 
443     ++next_cond_indx;
444 }
445 
446 /* display the tty-style status conditions; the number shown varies and
447    we might be showing more, same, or fewer than during previous status */
448 static int
render_conditions(int row,int dx)449 render_conditions(int row, int dx)
450 {
451     Widget label;
452     Arg args[6];
453     Cardinal num_args;
454     Position lbl_x;
455     int i, gap = 5;
456     struct status_info_t *si = xw_status_win->Win_info.Status_info;
457     Dimension lbl_wid, brd_wid = si->brd;
458 
459     for (i = 0; i < next_cond_indx; i++) {
460         label = X11_cond_labels[i];
461 
462         /* width of this widget was set in DisplayCond(); fetch it */
463         (void) memset((genericptr_t) args, 0, sizeof args);
464         num_args = 0;
465         XtSetArg(args[num_args], nhStr(XtNwidth), &lbl_wid); num_args++;
466         XtGetValues(label, args, num_args);
467 
468         /* figure out where to draw this widget and place it there */
469         lbl_x = (Position) (dx + 1 + gap);
470 
471         XtConfigureWidget(label, lbl_x, si->y[row], lbl_wid, si->ht, brd_wid);
472 
473         /* keep track of where the end of our text appears */
474         dx = (int) lbl_x + (int) (lbl_wid + 2 * brd_wid) - 1;
475     }
476 
477     /* if we have fewer conditions shown now than last time, set the
478        excess ones to blank; unlike the set drawn above, these haven't
479        been prepared in advance by DisplayCond because they aren't
480        being displayed; they might have been highlighted last time so
481        we need to specify more than just an empty text string */
482     if (next_cond_indx < prev_cond_indx) {
483         XFontStruct *font = X11_status_font;
484         Pixel fg = X11_status_fg, bg = X11_status_bg;
485 
486         lbl_x = dx + 1;
487         lbl_wid = 1 + 2 * brd_wid;
488         for (i = next_cond_indx; i < prev_cond_indx; ++i) {
489             label = X11_cond_labels[i];
490 
491             (void) memset((genericptr_t) args, 0, sizeof args);
492             num_args = 0;
493             XtSetArg(args[num_args], nhStr(XtNlabel), ""); num_args++;
494             XtSetArg(args[num_args], nhStr(XtNfont), font); num_args++;
495             XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++;
496             XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++;
497             /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
498             /*XtSetArg(args[num_args], nhStr(XtNx), lbl_x); num_args++;*/
499             XtSetValues(label, args, num_args);
500             old_cond_colors[i] = NO_COLOR; /* fg, bg, font were just reset */
501             XtConfigureWidget(label, lbl_x, si->y[row], lbl_wid, si->ht, 0);
502             /* don't advance 'dx' here */
503         }
504     }
505 
506     return dx;
507 }
508 
509 #ifdef STATUS_HILITES
510 /* reset status_hilite for BL_RESET; if highlighting has been disabled or
511    this field is disabled, clear highlighting for this field or condition */
512 static void
tt_reset_color(int fld,int cond,unsigned long * colormasks)513 tt_reset_color(int fld, int cond, unsigned long *colormasks)
514 {
515     Widget label;
516     int colrattr = NO_COLOR;
517 
518     if (fld != BL_CONDITION) {
519         if (iflags.hilite_delta != 0L && status_activefields[fld])
520             colrattr = X11_status_colors[fld];
521         cond = 0;
522         label = X11_status_labels[fld];
523     } else {
524         unsigned long bm = tt_condorder[cond].mask;
525 
526         if (iflags.hilite_delta != 0L && (X11_condition_bits & bm) != 0) {
527             /* BL_RESET before first BL_CONDITION will have colormasks==Null
528                but condcolor() and condattr() can cope with that */
529 #ifdef TEXTCOLOR
530             colrattr = condcolor(bm, colormasks);
531 #endif
532             colrattr |= (condattr(bm, colormasks) << 8);
533         }
534         label = X11_cond_labels[cond];
535     }
536     HiliteField(label, fld, cond, colrattr, (XFontStruct **) 0);
537 }
538 #endif
539 
540 /* make sure foreground, background, and font have reasonable values,
541    then explicitly set them for all the status widgets;
542    also cache some geometry settings in (*xw_status_win).Status_info */
543 static void
tt_status_fixup(void)544 tt_status_fixup(void)
545 {
546     Arg args[6];
547     Cardinal num_args;
548     Widget w;
549     Position lbl_y;
550     int x, y, ci, fld;
551     XFontStruct *font = 0;
552     Pixel fg = 0, bg = 0;
553     struct status_info_t *si = xw_status_win->Win_info.Status_info;
554 
555     (void) memset((genericptr_t) args, 0, sizeof args);
556     num_args = 0;
557     XtSetArg(args[num_args], nhStr(XtNfont), &font); num_args++;
558     XtSetArg(args[num_args], nhStr(XtNforeground), &fg); num_args++;
559     XtSetArg(args[num_args], nhStr(XtNbackground), &bg); num_args++;
560     XtGetValues(X11_status_widget, args, num_args);
561     if (fg == bg) {
562         XColor black = get_nhcolor(xw_status_win, CLR_BLACK),
563                white = get_nhcolor(xw_status_win, CLR_WHITE);
564 
565         fg = (bg == white.pixel) ? black.pixel : white.pixel;
566     }
567     X11_status_fg = si->fg = fg, X11_status_bg = si->bg = bg;
568 
569     if (!font) {
570         w = X11_status_widget;
571         XtSetArg(args[0], nhStr(XtNfont), &font);
572         do {
573             XtGetValues(w, args, ONE);
574         } while (!font && (w = XtParent(w)) != 0);
575 
576         if (!font) {
577             /* trial and error time -- this is where we've actually
578                been obtaining the font even though we aren't setting
579                it for any of the field widgets (until for(y,x) below) */
580             XtGetValues(X11_status_labels[0], args, ONE);
581 
582             if (!font) { /* this bit is untested... */
583                 /* write some text and hope Xaw sets up font for us */
584                 XtSetArg(args[0], nhStr(XtNlabel), "NetHack");
585                 XtSetValues(X11_status_labels[0], args, ONE);
586                 (void) XFlush(XtDisplay(X11_status_labels[0]));
587 
588                 XtSetArg(args[0], nhStr(XtNfont), &font);
589                 XtGetValues(X11_status_labels[0], args, ONE);
590 
591                 /* if still Null, XTextWidth() would crash so bail out */
592                 if (!font)
593                     panic("X11 status can't determine font.");
594             }
595         }
596     }
597     X11_status_font = si->fs = font;
598 
599     /* amount of space to advance a widget's location by one space;
600        increase width a tiny bit beyond the actual space */
601     si->spacew = XTextWidth(X11_status_font, " ", 1) + (Dimension) 1;
602 
603     (void) memset((genericptr_t) args, 0, sizeof args);
604     num_args = 0;
605     XtSetArg(args[num_args], nhStr(XtNfont), font); num_args++;
606     XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++;
607     XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++;
608     XtSetValues(X11_status_widget, args, num_args);
609 
610     for (y = 0; y < X11_NUM_STATUS_LINES; y++) {
611         for (x = 0; x < X11_NUM_STATUS_FIELD; x++) {
612             fld = X11_fieldorder[y][x]; /* next field to handle */
613             if (fld <= BL_FLUSH)
614                 continue; /* skip fieldorder[][] padding */
615 
616             XtSetValues(X11_status_labels[fld], args, num_args);
617             old_field_colors[fld] = NO_COLOR;
618 
619             if (fld == BL_CONDITION) {
620                 for (ci = 0; ci < SIZE(X11_cond_labels); ++ci) { /* 0..31 */
621                     XtSetValues(X11_cond_labels[ci], args, num_args);
622                     old_cond_colors[ci] = NO_COLOR;
623                 }
624             }
625         }
626     }
627 
628     /* cache the y coordinate of each row for XtConfigureWidget() */
629     (void) memset((genericptr_t) args, 0, sizeof args);
630     num_args = 0;
631     XtSetArg(args[0], nhStr(XtNy), &lbl_y); num_args++;
632     for (y = 0; y < X11_NUM_STATUS_LINES; y++) {
633         fld = X11_fieldorder[y][0];
634         XtGetValues(X11_status_labels[fld], args, num_args);
635         si->y[y] = lbl_y;
636     }
637 
638     /* cache height, borderWidth, and internalWidth for XtResizeWidget() */
639     (void) memset((genericptr_t) args, 0, sizeof args);
640     num_args = 0;
641     XtSetArg(args[num_args], nhStr(XtNheight), &si->ht); num_args++;
642     XtSetArg(args[num_args], nhStr(XtNborderWidth), &si->brd); num_args++;
643     XtSetArg(args[num_args], nhStr(XtNinternalWidth), &si->in_wd); num_args++;
644     XtGetValues(X11_status_labels[0], args, num_args);
645 }
646 
647 DISABLE_WARNING_FORMAT_NONLITERAL
648 
649 /* core requests updating one status field (or is indicating that it's time
650    to flush all updated fields); tty-style handling */
651 static void
X11_status_update_tty(int fld,genericptr_t ptr,int chg UNUSED,int percent,int color,unsigned long * colormasks)652 X11_status_update_tty(int fld, genericptr_t ptr, int chg UNUSED, int percent,
653                       int color,
654                       unsigned long *colormasks) /* bitmask of highlights
655                                                     for conditions */
656 {
657     static int xtra_space[MAXBLSTATS];
658     static unsigned long *cond_colormasks = (unsigned long *) 0;
659 
660     Arg args[6];
661     Cardinal num_args;
662     Position lbl_x;
663     Dimension lbl_wid, brd_wid;
664     Widget label;
665 
666     struct status_info_t *si;
667     char goldbuf[40];
668     const char *fmt;
669     const char *text;
670     unsigned long *condptr;
671     int f, x, y, dx;
672 
673     if (X11_status_fg == X11_status_bg || !X11_status_font)
674         tt_status_fixup();
675 
676     if (fld == BL_RESET) {
677 #ifdef STATUS_HILITES
678         for (y = 0; y < X11_NUM_STATUS_LINES; y++) {
679             for (x = 0; x < X11_NUM_STATUS_FIELD; x++) {
680                 f = X11_fieldorder[y][x];
681                 if (f <= BL_FLUSH)
682                     continue; /* skip padding in the fieldorder[][] layout */
683                 if (f != BL_CONDITION) {
684                     tt_reset_color(f, 0, (unsigned long *) 0);
685                 } else {
686                     int c_i;
687 
688                     for (c_i = 0; c_i < SIZE(tt_condorder); ++c_i)
689                         tt_reset_color(f, c_i, cond_colormasks);
690                 }
691             }
692         }
693 #endif
694         fld = BL_FLUSH;
695     }
696 
697     if (fld == BL_CONDITION) {
698         condptr = (unsigned long *) ptr;
699         X11_condition_bits = *condptr;
700         cond_colormasks = colormasks; /* expected to be non-Null */
701 
702         prev_cond_indx = next_cond_indx;
703         next_cond_indx = 0;
704         /* if any conditions are active, set up their widgets */
705         if (X11_condition_bits)
706             for (f = 0; f < SIZE(tt_condorder); ++f)
707                 DisplayCond(f, cond_colormasks);
708         return;
709 
710     } else if (fld != BL_FLUSH) {
711         /* set up a specific field other than 'condition' */
712         text = (char *) ptr;
713         if (fld == BL_GOLD)
714             text = decode_mixed(goldbuf, text);
715         xtra_space[fld] = 0;
716         if (status_activefields[fld]) {
717             fmt = (fld == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30s"
718                   : status_fieldfmt[fld] ? status_fieldfmt[fld] : "%s";
719             if (*fmt == ' ') {
720                 ++xtra_space[fld];
721                 ++fmt;
722             }
723             Sprintf(status_vals[fld], fmt, text);
724         } else {
725             /* don't expect this since core won't call status_update()
726                for a field which isn't active */
727             *status_vals[fld] = '\0';
728         }
729 #ifndef TEXTCOLOR
730         /* even without color, attribute(s) bits still apply */
731         color = (color & ~0x00ff) | NO_COLOR;
732 #endif
733 #ifdef STATUS_HILITES
734         if (!iflags.hilite_delta)
735             color = NO_COLOR;
736 #endif
737         X11_status_colors[fld] = color;
738         if (iflags.wc2_hitpointbar && fld == BL_HP) {
739             hpbar_percent = percent;
740             hpbar_color = color;
741         }
742 
743         label = X11_status_labels[fld];
744         text = status_vals[fld];
745         PrepStatusField(fld, label, text);
746         return;
747     }
748 
749     /*
750      * BL_FLUSH:  draw all the status fields.
751      */
752     si = xw_status_win->Win_info.Status_info; /* for cached geometry */
753 
754     for (y = 0; y < X11_NUM_STATUS_LINES; y++) { /* row */
755         dx = 0; /* no pixels written to this row yet */
756 
757         for (x = 0; x < X11_NUM_STATUS_FIELD; x++) { /* 'column' */
758             f = X11_fieldorder[y][x];
759             if (f <= BL_FLUSH)
760                 continue; /* skip padding in the fieldorder[][] layout */
761 
762             if (f == BL_CONDITION) {
763                 if (next_cond_indx > 0 || prev_cond_indx > 0)
764                     dx = render_conditions(y, dx);
765                 continue;
766             }
767 
768             label = X11_status_labels[f];
769             text = status_vals[f];
770 
771             (void) memset((genericptr_t) args, 0, sizeof args);
772             num_args = 0;
773             XtSetArg(args[num_args], nhStr(XtNwidth), &lbl_wid); num_args++;
774             XtGetValues(label, args, num_args);
775             brd_wid = si->brd;
776 
777             /* for a field which shouldn't be shown, we can't just skip
778                it because we might need to remove its previous content
779                if it has just been toggled off */
780             if (!status_activefields[f]) {
781                 if (lbl_wid <= 1)
782                     continue;
783                 text = "";
784                 lbl_wid = (Dimension) 1;
785                 brd_wid = (Dimension) 0;
786             } else if (xtra_space[f]) {
787                 /* if this field was to be formatted with a leading space
788                    to separate it from the preceding field, we suppressed
789                    that space during formatting; insert separation between
790                    fields here; this prevents inverse video highlighting
791                    from inverting the separating space along with the text */
792                 dx += xtra_space[f] * si->spacew;
793             }
794 
795             /* where to display the current widget */
796             lbl_x = (Position) (dx + 1);
797 
798             (void) memset((genericptr_t) args, 0, sizeof args);
799             num_args = 0;
800             XtSetArg(args[num_args], nhStr(XtNlabel), text); num_args++;
801             /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
802             /*XtSetArg(args[num_args], nhStr(XtNx), lbl_x); num_args++;*/
803             XtSetValues(label, args, num_args);
804             XtConfigureWidget(label, lbl_x, si->y[y],
805                               lbl_wid, si->ht, brd_wid);
806 
807             /* track the right-most pixel written so far */
808             dx = (int) lbl_x + (int) (lbl_wid + 2 * brd_wid) - 1;
809         } /* [x] fields/columns in current row */
810     } /* [y] rows */
811 
812     /* this probably doesn't buy us anything but it isn't a sure thing
813        that nethack will immediately ask for input (triggering auto-flush) */
814     (void) XFlush(XtDisplay(X11_status_labels[0]));
815 }
816 
817 RESTORE_WARNING_FORMAT_NONLITERAL
818 
819 /*ARGSUSED*/
820 static void
X11_status_update_fancy(int fld,genericptr_t ptr,int chg UNUSED,int percent UNUSED,int colrattr,unsigned long * colormasks UNUSED)821 X11_status_update_fancy(int fld, genericptr_t ptr, int chg UNUSED,
822                         int percent UNUSED, int colrattr,
823                         unsigned long *colormasks UNUSED)
824 {
825     static const struct bl_to_ff {
826         int bl, ff;
827     } bl_to_fancyfield[] = {
828         { BL_TITLE, F_NAME },
829         { BL_STR, F_STR },
830         { BL_DX, F_DEX },
831         { BL_CO, F_CON },
832         { BL_IN, F_INT },
833         { BL_WI, F_WIS },
834         { BL_CH, F_CHA },
835         { BL_ALIGN, F_ALIGN },
836         { BL_SCORE, F_SCORE },
837         { BL_CAP, F_ENCUMBER },
838         { BL_GOLD, F_GOLD },
839         { BL_ENE, F_POWER },
840         { BL_ENEMAX, F_MAXPOWER },
841         { BL_XP, F_XP_LEVL }, /* shares with BL_HD, depending upon Upolyd */
842         { BL_AC, F_AC },
843         { BL_TIME, F_TIME },
844         { BL_HUNGER, F_HUNGER },
845         { BL_HP, F_HP },
846         { BL_HPMAX, F_MAXHP },
847         { BL_LEVELDESC, F_DLEVEL },
848         { BL_EXP, F_EXP_PTS }
849     };
850     static const struct mask_to_ff {
851         unsigned long mask;
852         int ff;
853     } mask_to_fancyfield[] = {
854         { BL_MASK_GRAB, F_GRABBED },
855         { BL_MASK_STONE, F_STONE },
856         { BL_MASK_SLIME, F_SLIME },
857         { BL_MASK_STRNGL, F_STRNGL },
858         { BL_MASK_FOODPOIS, F_FOODPOIS },
859         { BL_MASK_TERMILL, F_TERMILL },
860         { BL_MASK_WITHER, F_WITHER },
861         { BL_MASK_INLAVA, F_IN_LAVA },
862         { BL_MASK_HELD, F_HELD },
863         { BL_MASK_HOLDING, F_HOLDING },
864         { BL_MASK_BLIND, F_BLIND },
865         { BL_MASK_DEAF, F_DEAF },
866         { BL_MASK_STUN, F_STUN },
867         { BL_MASK_CONF, F_CONF },
868         { BL_MASK_HALLU, F_HALLU },
869         { BL_MASK_TRAPPED, F_TRAPPED },
870         { BL_MASK_TETHERED, F_TETHERED },
871         { BL_MASK_LEV, F_LEV },
872         { BL_MASK_FLY, F_FLY },
873         { BL_MASK_RIDE, F_RIDE }
874     };
875     int i;
876 
877     if (fld == BL_RESET || fld == BL_FLUSH) {
878         if (WIN_STATUS != WIN_ERR) {
879             update_fancy_status(FALSE);
880         }
881         return;
882     }
883 
884     if (fld == BL_CONDITION) {
885         unsigned long changed_bits, *condptr = (unsigned long *) ptr;
886 
887         X11_condition_bits = *condptr;
888         /* process the bits that are different from last time */
889         changed_bits = (X11_condition_bits ^ old_condition_bits);
890         if (changed_bits) {
891             for (i = 0; i < SIZE(mask_to_fancyfield); i++)
892                 if ((changed_bits & mask_to_fancyfield[i].mask) != 0L)
893                     update_fancy_status_field(mask_to_fancyfield[i].ff,
894                             condcolor(mask_to_fancyfield[i].mask, colormasks),
895                             condattr(mask_to_fancyfield[i].mask, colormasks));
896             old_condition_bits = X11_condition_bits; /* remember 'On' bits */
897         }
898     } else {
899         int colr, attr;
900 
901 #ifdef TEXTCOLOR
902         if ((colrattr & 0x00ff) >= CLR_MAX)
903             /* for !TEXTCOLOR, the following line is unconditional */
904 #endif
905             colrattr = (colrattr & ~0x00ff) | NO_COLOR;
906         colr = colrattr & 0x00ff; /* guaranteed to be >= 0 and < CLR_MAX */
907         attr = (colrattr >> 8) & 0x00ff;
908 
909         for (i = 0; i < SIZE(bl_to_fancyfield); i++)
910             if (bl_to_fancyfield[i].bl == fld) {
911                 update_fancy_status_field(bl_to_fancyfield[i].ff, colr, attr);
912                 break;
913             }
914     }
915 }
916 
917 void
X11_status_update(int fld,genericptr_t ptr,int chg,int percent,int color,unsigned long * colormasks)918 X11_status_update(int fld, genericptr_t ptr, int chg,
919                   int percent, int color,
920                   unsigned long *colormasks)
921 {
922     if (fld < BL_RESET || fld >= MAXBLSTATS)
923         panic("X11_status_update(%d) -- invalid field", fld);
924 
925     if (appResources.fancy_status)
926         X11_status_update_fancy(fld, ptr, chg, percent, color, colormasks);
927     else
928         X11_status_update_tty(fld, ptr, chg, percent, color, colormasks);
929 }
930 
931 /* create a widget for a particular status field or potential condition */
932 static Widget
create_tty_status_field(int fld,int condindx,Widget above,Widget left)933 create_tty_status_field(int fld, int condindx, Widget above, Widget left)
934 {
935     Arg args[16];
936     Cardinal num_args;
937     char labelname[40];
938     int gap = condindx ? 5 : 0;
939 
940     if (!condindx)
941         Sprintf(labelname, "label_%s", bl_idx_to_fldname(fld));
942     else
943         Sprintf(labelname, "cond_%02d", condindx);
944 
945     /* set up widget attributes which (mostly) aren't going to be changing */
946     (void) memset((genericptr_t) args, 0, sizeof args);
947     num_args = 0;
948     if (above)
949         XtSetArg(args[num_args], nhStr(XtNfromVert), above); num_args++;
950     if (left)
951         XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++;
952 
953     XtSetArg(args[num_args], nhStr(XtNhorizDistance), gap); num_args++;
954     XtSetArg(args[num_args], nhStr(XtNvertDistance), 0); num_args++;
955 
956     XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++;
957     XtSetArg(args[num_args], nhStr(XtNbottomMargin), 0); num_args++;
958     XtSetArg(args[num_args], nhStr(XtNleftMargin), 0); num_args++;
959     XtSetArg(args[num_args], nhStr(XtNrightMargin), 0); num_args++;
960     XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
961     /* internalWidth:  default is 4; cut that it half and adjust regular
962        width to have it on both left and right instead of just on the left */
963     XtSetArg(args[num_args], nhStr(XtNinternalWidth), 2); num_args++;
964     XtSetArg(args[num_args], nhStr(XtNborderWidth), 0); num_args++;
965     XtSetArg(args[num_args], nhStr(XtNlabel), ""); num_args++;
966     return XtCreateManagedWidget(labelname, labelWidgetClass,
967                                  X11_status_widget, args, num_args);
968 }
969 
970 /* create an overall status widget (X11_status_widget) and also
971    separate widgets for all status fields and potential conditions */
972 static Widget
create_tty_status(Widget parent,Widget top)973 create_tty_status(Widget parent, Widget top)
974 {
975     Widget form; /* viewport that holds the form that surrounds everything */
976     Widget w, over_w, prev_w;
977     Arg args[6];
978     Cardinal num_args;
979     int x, y, i, fld;
980 
981     (void) memset((genericptr_t) args, 0, sizeof args);
982     num_args = 0;
983     if (top)
984         XtSetArg(args[num_args], nhStr(XtNfromVert), top); num_args++;
985     XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++;
986     XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
987     XtSetArg(args[num_args], XtNwidth, 400); num_args++;
988     XtSetArg(args[num_args], XtNheight, 100); num_args++;
989     form = XtCreateManagedWidget("status_viewport", viewportWidgetClass,
990                                  parent, args, num_args);
991 
992     (void) memset((genericptr_t) args, 0, sizeof args);
993     num_args = 0;
994     XtSetArg(args[num_args], XtNwidth, 400); num_args++;
995     XtSetArg(args[num_args], XtNheight, 100); num_args++;
996     X11_status_widget = XtCreateManagedWidget("status_form", formWidgetClass,
997                                               form, args, num_args);
998 
999     for (y = 0; y < X11_NUM_STATUS_LINES; y++) {
1000         fld = (y > 0) ? X11_fieldorder[y - 1][0] : 0; /* temp, for over_w */
1001         /* for row(s) beyond the first, pick a widget in the previous
1002            row to put this one underneath (in 'y' terms; 'x' is fluid) */
1003         over_w = (y > 0) ? X11_status_labels[fld] : (Widget) 0;
1004         /* widget on current row to put the next one to the right of ('x') */
1005         prev_w = (Widget) 0;
1006         for (x = 0; x < X11_NUM_STATUS_FIELD; x++) {
1007             fld = X11_fieldorder[y][x]; /* next field to handle */
1008             if (fld <= BL_FLUSH)
1009                 continue; /* skip fieldorder[][] padding */
1010 
1011             w = create_tty_status_field(fld, 0, over_w, prev_w);
1012             X11_status_labels[fld] = prev_w = w;
1013 
1014             if (fld == BL_CONDITION) {
1015                 for (i = 1; i <= SIZE(X11_cond_labels); ++i) { /* 1..32 */
1016                     w = create_tty_status_field(fld, i, over_w, prev_w);
1017                     X11_cond_labels[i - 1] = prev_w = w;
1018                 }
1019             }
1020         }
1021     }
1022     return X11_status_widget;
1023 }
1024 
1025 /*ARGSUSED*/
1026 void
create_status_window_tty(struct xwindow * wp,boolean create_popup UNUSED,Widget parent)1027 create_status_window_tty(struct xwindow *wp, /* window pointer */
1028                          boolean create_popup UNUSED, Widget parent)
1029 {
1030     wp->type = NHW_STATUS;
1031     wp->w = create_tty_status(parent, (Widget) 0);
1032 }
1033 
1034 void
destroy_status_window_tty(struct xwindow * wp)1035 destroy_status_window_tty(struct xwindow *wp)
1036 {
1037     /* If status_information is defined, then it a "text" status window. */
1038     if (wp->status_information) {
1039         if (wp->popup) {
1040             nh_XtPopdown(wp->popup);
1041             if (!wp->keep_window)
1042                 XtDestroyWidget(wp->popup), wp->popup = (Widget) 0;
1043         }
1044         free((genericptr_t) wp->status_information);
1045         wp->status_information = 0;
1046     } else {
1047         ;
1048     }
1049     if (!wp->keep_window)
1050         wp->type = NHW_NONE;
1051 }
1052 
1053 /*ARGSUSED*/
1054 void
adjust_status_tty(struct xwindow * wp UNUSED,const char * str UNUSED)1055 adjust_status_tty(struct xwindow *wp UNUSED, const char *str UNUSED)
1056 {
1057     /* nothing */
1058     return;
1059 }
1060 
1061 void
create_status_window(struct xwindow * wp,boolean create_popup,Widget parent)1062 create_status_window(struct xwindow *wp, /* window pointer */
1063                      boolean create_popup, Widget parent)
1064 {
1065     struct status_info_t *si = (struct status_info_t *) alloc(sizeof *si);
1066 
1067     xw_status_win = wp;
1068     if (wp->Win_info.Status_info)
1069         free((genericptr_t) wp->Win_info.Status_info);
1070     wp->Win_info.Status_info = si;
1071     (void) memset((genericptr_t) si, 0, sizeof *si);
1072 
1073     if (!appResources.fancy_status)
1074         create_status_window_tty(wp, create_popup, parent);
1075     else
1076         create_status_window_fancy(wp, create_popup, parent);
1077 }
1078 
1079 void
destroy_status_window(struct xwindow * wp)1080 destroy_status_window(struct xwindow *wp)
1081 {
1082     if (appResources.fancy_status)
1083         destroy_status_window_fancy(wp);
1084     else
1085         destroy_status_window_tty(wp);
1086 }
1087 
1088 void
adjust_status(struct xwindow * wp,const char * str)1089 adjust_status(struct xwindow *wp, const char *str)
1090 {
1091     if (appResources.fancy_status)
1092         adjust_status_fancy(wp, str);
1093     else
1094         adjust_status_tty(wp, str);
1095 }
1096 
1097 void
create_status_window_fancy(struct xwindow * wp,boolean create_popup,Widget parent)1098 create_status_window_fancy(struct xwindow *wp, /* window pointer */
1099                            boolean create_popup, Widget parent)
1100 {
1101     XFontStruct *fs;
1102     Arg args[8];
1103     Cardinal num_args;
1104     Position top_margin, bottom_margin, left_margin, right_margin;
1105 
1106     wp->type = NHW_STATUS;
1107 
1108     if (!create_popup) {
1109         /*
1110          * If we are not creating a popup, then we must be the "main" status
1111          * window.
1112          */
1113         if (!parent)
1114             panic("create_status_window_fancy: no parent for fancy status");
1115         wp->status_information = 0;
1116         wp->w = create_fancy_status(parent, (Widget) 0);
1117         return;
1118     }
1119 
1120     wp->status_information =
1121         (struct status_info_t *) alloc(sizeof (struct status_info_t));
1122 
1123     init_text_buffer(&wp->status_information->text);
1124 
1125     num_args = 0;
1126     XtSetArg(args[num_args], XtNallowShellResize, False); num_args++;
1127     XtSetArg(args[num_args], XtNinput, False); num_args++;
1128 
1129     wp->popup = parent = XtCreatePopupShell("status_popup",
1130                                             topLevelShellWidgetClass,
1131                                             toplevel, args, num_args);
1132     /*
1133      * If we're here, then this is an auxiliary status window.  If we're
1134      * cancelled via a delete window message, we should just pop down.
1135      */
1136 
1137     num_args = 0;
1138     XtSetArg(args[num_args], nhStr(XtNdisplayCaret), False); num_args++;
1139     XtSetArg(args[num_args], nhStr(XtNscrollHorizontal),
1140              XawtextScrollWhenNeeded); num_args++;
1141     XtSetArg(args[num_args], nhStr(XtNscrollVertical),
1142              XawtextScrollWhenNeeded); num_args++;
1143 
1144     wp->w = XtCreateManagedWidget("status", /* name */
1145                                   asciiTextWidgetClass,
1146                                   parent,    /* parent widget */
1147                                   args,      /* set some values */
1148                                   num_args); /* number of values to set */
1149 
1150     /*
1151      * Adjust the height and width of the message window so that it
1152      * is two lines high and COLNO of the widest characters wide.
1153      */
1154 
1155     /* Get the font and margin information. */
1156     num_args = 0;
1157     XtSetArg(args[num_args], XtNfont, &fs); num_args++;
1158     XtSetArg(args[num_args], nhStr(XtNtopMargin), &top_margin); num_args++;
1159     XtSetArg(args[num_args], nhStr(XtNbottomMargin),
1160              &bottom_margin); num_args++;
1161     XtSetArg(args[num_args], nhStr(XtNleftMargin), &left_margin); num_args++;
1162     XtSetArg(args[num_args], nhStr(XtNrightMargin),
1163              &right_margin); num_args++;
1164     XtGetValues(wp->w, args, num_args);
1165 
1166     wp->pixel_height = 2 * nhFontHeight(wp->w) + top_margin + bottom_margin;
1167     wp->pixel_width = COLNO * fs->max_bounds.width
1168                     + left_margin + right_margin;
1169 
1170     /* Set the new width and height. */
1171     num_args = 0;
1172     XtSetArg(args[num_args], XtNwidth, wp->pixel_width); num_args++;
1173     XtSetArg(args[num_args], XtNheight, wp->pixel_height); num_args++;
1174     XtSetValues(wp->w, args, num_args);
1175 }
1176 
1177 void
destroy_status_window_fancy(struct xwindow * wp)1178 destroy_status_window_fancy(struct xwindow *wp)
1179 {
1180     /* If status_information is defined, then it a "text" status window. */
1181     if (wp->status_information) {
1182         if (wp->popup) {
1183             nh_XtPopdown(wp->popup);
1184             if (!wp->keep_window)
1185                 XtDestroyWidget(wp->popup), wp->popup = (Widget) 0;
1186         }
1187         free((genericptr_t) wp->status_information);
1188         wp->status_information = 0;
1189     } else {
1190         destroy_fancy_status(wp);
1191     }
1192     if (!wp->keep_window)
1193         wp->type = NHW_NONE;
1194 }
1195 
1196 /*
1197  * This assumes several things:
1198  *      + Status has only 2 lines
1199  *      + That both lines are updated in succession in line order.
1200  *      + We didn't set stringInPlace on the widget.
1201  */
1202 void
adjust_status_fancy(struct xwindow * wp,const char * str)1203 adjust_status_fancy(struct xwindow *wp, const char *str)
1204 {
1205     Arg args[2];
1206     Cardinal num_args;
1207 
1208     if (!wp->status_information) {
1209         update_fancy_status(TRUE);
1210         return;
1211     }
1212 
1213     if (wp->cursy == 0) {
1214         clear_text_buffer(&wp->status_information->text);
1215         append_text_buffer(&wp->status_information->text, str, FALSE);
1216         return;
1217     }
1218     append_text_buffer(&wp->status_information->text, str, FALSE);
1219 
1220     /* Set new buffer as text. */
1221     num_args = 0;
1222     XtSetArg(args[num_args], XtNstring,
1223              wp->status_information->text.text); num_args++;
1224     XtSetValues(wp->w, args, num_args);
1225 }
1226 
1227 /* Fancy ================================================================== */
1228 extern const char *hu_stat[];  /* from eat.c */
1229 extern const char *enc_stat[]; /* from botl.c */
1230 
1231 struct X_status_value {
1232     /* we have to cast away 'const' when assigning new names */
1233     const char *name;   /* text name */
1234     int type;           /* status type */
1235     Widget w;           /* widget of name/value pair */
1236     long last_value;    /* value displayed */
1237     int turn_count;     /* last time the value changed */
1238     boolean set;        /* if highlighted */
1239     boolean after_init; /* don't highlight on first change (init) */
1240     boolean inverted_hilite; /* if highlit due to hilite_status inverse rule */
1241     Pixel default_fg;   /* what FG color it initialized with */
1242     int colr, attr;     /* color and attribute */
1243 };
1244 
1245 /* valid type values */
1246 #define SV_VALUE 0 /* displays a label:value pair */
1247 #define SV_LABEL 1 /* displays a changable label */
1248 #define SV_NAME  2 /* displays an unchangeable name */
1249 
1250 /* for overloaded conditions */
1251 struct ovld_item {
1252     unsigned long ovl_mask;
1253     int ff;
1254 };
1255 #define NUM_OVLD 4 /* peak number of overloads for a single field */
1256 struct f_overload {
1257     unsigned long all_mask;
1258     struct ovld_item conds[NUM_OVLD];
1259 };
1260 
1261 static const struct f_overload *ff_ovld_from_mask(unsigned long);
1262 static const struct f_overload *ff_ovld_from_indx(int);
1263 static void hilight_label(Widget);
1264 static void update_val(struct X_status_value *, long);
1265 static void skip_cond_val(struct X_status_value *);
1266 static void update_color(struct X_status_value *, int);
1267 static boolean name_widget_has_label(struct X_status_value *);
1268 static void apply_hilite_attributes(struct X_status_value *, int);
1269 static const char *width_string(int);
1270 static void create_widget(Widget, struct X_status_value *, int);
1271 static void get_widths(struct X_status_value *, int *, int *);
1272 static void set_widths(struct X_status_value *, int, int);
1273 static Widget init_column(const char *, Widget, Widget, Widget, int *, int);
1274 static void fixup_cond_widths(void);
1275 static Widget init_info_form(Widget, Widget, Widget);
1276 
1277 /*
1278  * Notes:
1279  * + Alignment needs a different init value, because -1 is an alignment.
1280  * + Armor Class is an schar, so 256 is out of range.
1281  * + Blank value is 0 and should never change.
1282  *
1283  * - These must be in the same order as the F_foo numbers.
1284  */
1285 static struct X_status_value shown_stats[NUM_STATS] = {
1286     /* 0 */
1287     { "",             SV_NAME,  (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1288     /* 1 */
1289     { "Strength",     SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1290     { "Dexterity",    SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1291     { "Constitution", SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1292     { "Intelligence", SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1293     /* 5 */
1294     { "Wisdom",       SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1295     { "Charisma",     SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1296     /* F_NAME: 7 */
1297     { "",             SV_LABEL, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1298     /* F_DLEVEL: 8 */
1299     { "",             SV_LABEL, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1300     { "Gold",         SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1301     /* F_HP: 10 */
1302     { "Hit Points",   SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1303     { "Max HP",       SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1304     { "Power",        SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1305     { "Max Power",    SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1306     { "Armor Class",  SV_VALUE, (Widget) 0, 256L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1307     /* F_XP_LEVL: 15 */
1308     { "Xp Level",     SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1309     /* also 15 (overloaded field) */
1310     /*{ "Hit Dice",   SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },*/
1311     /* F_EXP_PTS: 16 (optionally displayed) */
1312     { "Exp Points",   SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1313     { "Alignment",    SV_VALUE, (Widget) 0,  -2L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1314     /* 18, optionally displayed */
1315     { "Time",         SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1316     /* 19, condtionally present, optionally displayed when present */
1317     { "Score",        SV_VALUE, (Widget) 0,  -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },
1318     /* F_HUNGER: 20 (blank if 'normal') */
1319     { "",             SV_NAME,  (Widget) 0,  -1L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1320     /* F_ENCUMBER: 21 (blank if unencumbered) */
1321     { "",             SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1322     { "Trapped",      SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1323     { "Tethered",     SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1324     { "Levitating",   SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1325     /* 25 */
1326     { "Flying",       SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1327     { "Riding",       SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1328     { "Grabbed!",     SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1329     /* F_STONE: 28 */
1330     { "Petrifying",   SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1331     { "Slimed",       SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1332     /* 30 */
1333     { "Strangled",    SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1334     { "Food Pois",    SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1335     { "Term Ill",     SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1336     /* F_IN_LAVA: 33 */
1337     { "Sinking",      SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1338     { "Held",         SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1339     /* 35 */
1340     { "Holding",      SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1341     { "Blind",        SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1342     { "Deaf",         SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1343     { "Stunned",      SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1344     { "Confused",     SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1345     /* F_HALLU: 40 (full spelling truncated due to space limitations) */
1346     { "Hallucinat",   SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1347     { "Wither",       SV_NAME,  (Widget) 0,   0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 },
1348 };
1349 /*
1350  * The following are supported by the core but not yet handled here:
1351  *  bareh      'bare handed' (no weapon and no gloves)
1352  *  busy       involved in some multi-turn activity, possibly involuntarily
1353  *  elf_iron   elf being harmed by contact with iron (not implemented)
1354  *  glowhands  'glowing hands' (inflict confuse monster for next N melee hits)
1355  *  icy        on or above ice terrain (temporary fumbling; might melt)
1356  *  parlyz     paralyzed (can't move)
1357  *  sleeping   asleep (can't move; might wake if attacked)
1358  *  slippery   'slippery hands' or gloves (will drop non-cursed weapons)
1359  *  submerged  underwater (severely restricted vision, hampered movement)
1360  *  unconsc    unconscious (can't move; includes fainted)
1361  *  woundedl   'wounded legs' (can't kick; temporary dex loss)
1362  */
1363 
1364 /* overloaded condition fields */
1365 static const struct f_overload cond_ovl[] = {
1366     { (BL_MASK_TRAPPED | BL_MASK_TETHERED),
1367       { { BL_MASK_TRAPPED, F_TRAPPED },
1368         { BL_MASK_TETHERED, F_TETHERED } },
1369     },
1370     { (BL_MASK_HELD | BL_MASK_HOLDING),
1371       { { BL_MASK_HELD, F_HELD },
1372         { BL_MASK_HOLDING, F_HOLDING } },
1373     },
1374 #if 0   /* not yet implemented */
1375     { (BL_MASK_BUSY | BL_MASK_PARALYZ | BL_MASK_SLEEPING | BL_MASK_UNCONSC),
1376       { { BL_MASK_BUSY, F_BUSY }, /* can't move but none of the below... */
1377         { BL_MASK_PARALYZ, F_PARALYZED }
1378         { BL_MASK_SLEEPING, F_SLEEPING }
1379         { BL_MASK_UNCONSC, F_UNCONSCIOUS } },
1380     },
1381 #endif
1382 };
1383 
1384 static const struct f_overload *
ff_ovld_from_mask(unsigned long mask)1385 ff_ovld_from_mask(unsigned long mask)
1386 {
1387     const struct f_overload *fo;
1388 
1389     for (fo = cond_ovl; fo < cond_ovl + SIZE(cond_ovl); ++fo) {
1390         if ((fo->all_mask & mask) != 0L)
1391             return fo;
1392     }
1393     return (struct f_overload *) 0;
1394 }
1395 
1396 static const struct f_overload *
ff_ovld_from_indx(int indx)1397 ff_ovld_from_indx(int indx) /* F_foo number, index into shown_stats[] */
1398 {
1399     const struct f_overload *fo;
1400     int i, ff;
1401 
1402     if (indx > 0) { /* skip 0 (F_DUMMY) */
1403         for (fo = cond_ovl; fo < cond_ovl + SIZE(cond_ovl); ++fo) {
1404             for (i = 0; i < NUM_OVLD && (ff = fo->conds[i].ff) > 0; ++i)
1405                 if (ff == indx)
1406                     return fo;
1407         }
1408     }
1409     return (struct f_overload *) 0;
1410 }
1411 
1412 /*
1413  * Set all widget values to a null string.  This is used after all spacings
1414  * have been calculated so that when the window is popped up we don't get all
1415  * kinds of funny values being displayed.
1416  */
1417 void
null_out_status(void)1418 null_out_status(void)
1419 {
1420     int i;
1421     struct X_status_value *sv;
1422     Arg args[1];
1423 
1424     for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++) {
1425         switch (sv->type) {
1426         case SV_VALUE:
1427             set_value(sv->w, "");
1428             break;
1429 
1430         case SV_LABEL:
1431         case SV_NAME:
1432             XtSetArg(args[0], XtNlabel, "");
1433             XtSetValues(sv->w, args, ONE);
1434             break;
1435 
1436         default:
1437             impossible("null_out_status: unknown type %d\n", sv->type);
1438             break;
1439         }
1440     }
1441 }
1442 
1443 /* this is almost an exact duplicate of hilight_value() */
1444 static void
hilight_label(Widget w)1445 hilight_label(Widget w) /* label widget */
1446 {
1447     /*
1448      * This predates STATUS_HILITES.
1449      * It is used to show any changed item in inverse and gets
1450      * reset on the next turn.
1451      */
1452     swap_fg_bg(w);
1453 }
1454 
1455 DISABLE_WARNING_FORMAT_NONLITERAL
1456 
1457 static void
update_val(struct X_status_value * attr_rec,long new_value)1458 update_val(struct X_status_value *attr_rec, long new_value)
1459 {
1460     static boolean Exp_shown = TRUE, time_shown = TRUE, score_shown = TRUE,
1461                    Xp_was_HD = FALSE;
1462     char buf[BUFSZ];
1463     Arg args[4];
1464 
1465     if (attr_rec->type == SV_LABEL) {
1466         if (attr_rec == &shown_stats[F_NAME]) {
1467             Strcpy(buf, g.plname);
1468             buf[0] = highc(buf[0]);
1469             Strcat(buf, " the ");
1470             if (Upolyd) {
1471                 char mnam[BUFSZ];
1472                 int k;
1473 
1474                 Strcpy(mnam, pmname(&mons[u.umonnum], Ugender));
1475                 for (k = 0; mnam[k] != '\0'; k++) {
1476                     if (k == 0 || mnam[k - 1] == ' ')
1477                         mnam[k] = highc(mnam[k]);
1478                 }
1479                 Strcat(buf, mnam);
1480             } else
1481                 Strcat(buf,
1482                        rank_of(u.ulevel, g.pl_character[0], flags.female));
1483 
1484         } else if (attr_rec == &shown_stats[F_DLEVEL]) {
1485             if (!describe_level(buf)) {
1486                 Strcpy(buf, g.dungeons[u.uz.dnum].dname);
1487                 Sprintf(eos(buf), ", level %d", depth(&u.uz));
1488             }
1489         } else {
1490             impossible("update_val: unknown label type \"%s\"",
1491                        attr_rec->name);
1492             return;
1493         }
1494 
1495         if (strcmp(buf, attr_rec->name) == 0)
1496             return; /* same */
1497 
1498         /* Set the label.  'name' field is const for most entries;
1499            we need to cast away that const for this assignment */
1500         Strcpy((char *) attr_rec->name, buf);
1501         XtSetArg(args[0], XtNlabel, buf);
1502         XtSetValues(attr_rec->w, args, ONE);
1503 
1504     } else if (attr_rec->type == SV_NAME) {
1505         if (attr_rec->last_value == new_value)
1506             return; /* no change */
1507 
1508         attr_rec->last_value = new_value;
1509 
1510         /* special cases: hunger and encumbrance */
1511         if (attr_rec == &shown_stats[F_HUNGER]) {
1512             Strcpy(buf, hu_stat[new_value]);
1513             (void) mungspaces(buf);
1514         } else if (attr_rec == &shown_stats[F_ENCUMBER]) {
1515             Strcpy(buf, enc_stat[new_value]);
1516         } else if (new_value) {
1517             Strcpy(buf, attr_rec->name); /* condition name On */
1518         } else {
1519             *buf = '\0'; /* condition name Off */
1520         }
1521 
1522         XtSetArg(args[0], XtNlabel, buf);
1523         XtSetValues(attr_rec->w, args, ONE);
1524 
1525     } else { /* a value pair */
1526         boolean force_update = FALSE;
1527 
1528         /* special case: time can be enabled & disabled */
1529         if (attr_rec == &shown_stats[F_TIME]) {
1530             if (flags.time && !time_shown) {
1531                 set_name(attr_rec->w, shown_stats[F_TIME].name);
1532                 force_update = TRUE;
1533                 time_shown = TRUE;
1534             } else if (!flags.time && time_shown) {
1535                 set_name(attr_rec->w, "");
1536                 set_value(attr_rec->w, "");
1537                 time_shown = FALSE;
1538             }
1539             if (!time_shown)
1540                 return;
1541 
1542         /* special case: experience points can be enabled & disabled */
1543         } else if (attr_rec == &shown_stats[F_EXP_PTS]) {
1544             boolean showexp = flags.showexp && !Upolyd;
1545 
1546             if (showexp && !Exp_shown) {
1547                 set_name(attr_rec->w, shown_stats[F_EXP_PTS].name);
1548                 force_update = TRUE;
1549                 Exp_shown = TRUE;
1550             } else if (!showexp && Exp_shown) {
1551                 set_name(attr_rec->w, "");
1552                 set_value(attr_rec->w, "");
1553                 Exp_shown = FALSE;
1554             }
1555             if (!Exp_shown)
1556                 return;
1557 
1558         /* special case: when available, score can be enabled & disabled */
1559         } else if (attr_rec == &shown_stats[F_SCORE]) {
1560 #ifdef SCORE_ON_BOTL
1561             if (flags.showscore && !score_shown) {
1562                 set_name(attr_rec->w, shown_stats[F_SCORE].name);
1563                 force_update = TRUE;
1564                 score_shown = TRUE;
1565             } else
1566 #endif
1567             if (!flags.showscore && score_shown) {
1568                 set_name(attr_rec->w, "");
1569                 set_value(attr_rec->w, "");
1570                 score_shown = FALSE;
1571             }
1572             if (!score_shown)
1573                 return;
1574 
1575         /* special case: when polymorphed, show "Hit Dice" and disable Exp */
1576         } else if (attr_rec == &shown_stats[F_XP_LEVL]) {
1577             if (Upolyd && !Xp_was_HD) {
1578                 force_update = TRUE;
1579                 set_name(attr_rec->w, "Hit Dice");
1580                 Xp_was_HD = TRUE;
1581             } else if (!Upolyd && Xp_was_HD) {
1582                 force_update = TRUE;
1583                 set_name(attr_rec->w, shown_stats[F_XP_LEVL].name);
1584                 Xp_was_HD = FALSE;
1585             }
1586             /* core won't call status_update() for Exp when it hasn't changed
1587                so do so ourselves (to get Exp_shown flag to match display) */
1588             if (force_update)
1589                 update_fancy_status_field(F_EXP_PTS, NO_COLOR, HL_UNDEF);
1590         }
1591 
1592         if (attr_rec->last_value == new_value && !force_update) /* same */
1593             return;
1594 
1595         attr_rec->last_value = new_value;
1596 
1597         /* Special cases: strength and other characteristics, alignment
1598            and "clear". */
1599         if (attr_rec >= &shown_stats[F_STR]
1600             && attr_rec <= &shown_stats[F_CHA]) {
1601             static const char fmt1[] = "%ld%s", fmt2[] = "%2ld%s";
1602             struct xwindow *wp;
1603             const char *fmt = fmt1, *padding = "";
1604 
1605             /* for full-fledged fancy status, force two digits for all
1606                six characteristics, followed by three spaces of padding
1607                to match "/xx" exceptional strength */
1608             wp = (WIN_STATUS != WIN_ERR) ? &window_list[WIN_STATUS] : 0;
1609             if (wp && !wp->status_information)
1610                 fmt = fmt2, padding = "   ";
1611 
1612             if (new_value > 18L && attr_rec == &shown_stats[F_STR]) {
1613                 if (new_value > 118L) /* 19..25 encoded as 119..125 */
1614                     Sprintf(buf, fmt, new_value - 100L, padding);
1615                 else if (new_value < 118L) /* 18/01..18/99 as 19..117*/
1616                     Sprintf(buf, "18/%02ld", new_value - 18L);
1617                 else
1618                     Strcpy(buf, "18/**"); /* 18/100 encoded as 118 */
1619             } else { /* non-strength or less than 18/01 strength (3..18) */
1620                 Sprintf(buf, fmt, new_value, padding); /* 3..25 */
1621             }
1622         } else if (attr_rec == &shown_stats[F_ALIGN]) {
1623             Strcpy(buf, (new_value == A_CHAOTIC) ? "Chaotic"
1624                         : (new_value == A_NEUTRAL) ? "Neutral" : "Lawful");
1625         } else {
1626             Sprintf(buf, "%ld", new_value);
1627         }
1628         set_value(attr_rec->w, buf);
1629     }
1630 
1631     /*
1632      * Now highlight the changed information.  Don't highlight Time because
1633      * it's continually changing.  For others, don't highlight if this is
1634      * the first update.  If already highlighted, don't change it unless
1635      * it's being set to blank (where that item should be reset now instead
1636      * of showing highlighted blank until the next expiration check).
1637      *
1638      * 3.7:  highlight non-labelled 'name' items (conditions plus hunger
1639      * and encumbrance) when they come On.  For all conditions going Off,
1640      * or changing to not-hungry or not-encumbered, there's nothing to
1641      * highlight because the field becomes blank.
1642      */
1643     if (attr_rec->after_init) {
1644         /* toggle if not highlighted and just set to nonblank or if
1645            already highlighted and just set to blank */
1646         if (attr_rec != &shown_stats[F_TIME] && !attr_rec->set ^ !*buf) {
1647             /* But don't hilite if inverted from status_hilite since
1648                it will already be hilited by apply_hilite_attributes(). */
1649             if (!attr_rec->inverted_hilite) {
1650                 if (attr_rec->type == SV_VALUE)
1651                     hilight_value(attr_rec->w);
1652                 else
1653                     hilight_label(attr_rec->w);
1654             }
1655             attr_rec->set = !attr_rec->set;
1656         }
1657         attr_rec->turn_count = 0;
1658     } else {
1659         XtSetArg(args[0], XtNforeground, &attr_rec->default_fg);
1660         XtGetValues(attr_rec->w, args, ONE);
1661         attr_rec->after_init = TRUE;
1662     }
1663 }
1664 
1665 RESTORE_WARNING_FORMAT_NONLITERAL
1666 
1667 /* overloaded condition is being cleared without going through update_val()
1668    so that an alternate can be shown; put this one back to default settings */
1669 static void
skip_cond_val(struct X_status_value * sv)1670 skip_cond_val(struct X_status_value *sv)
1671 {
1672     sv->last_value = 0L; /* Off */
1673     if (sv->set) {
1674         /* if condition was highlighted and the alternate value has
1675            also requested to be highlighted, it used its own copy of
1676            'set' but the same widget so the highlighing got toggled
1677            off; this will turn in back on in that exceptional case */
1678         hilight_label(sv->w);
1679         sv->set = FALSE;
1680     }
1681 }
1682 
1683 static void
update_color(struct X_status_value * sv,int color)1684 update_color(struct X_status_value *sv, int color)
1685 {
1686     Pixel pixel = 0;
1687     Arg args[1];
1688     XrmValue source;
1689     XrmValue dest;
1690     Widget w = (sv->type == SV_LABEL || sv->type == SV_NAME) ? sv->w
1691                : get_value_widget(sv->w);
1692 
1693     if (color == NO_COLOR) {
1694         if (sv->after_init)
1695             pixel = sv->default_fg;
1696         sv->colr = NO_COLOR;
1697     } else {
1698         source.addr = (XPointer) fancy_status_hilite_colors[color];
1699         source.size = (unsigned int) strlen((const char *) source.addr) + 1;
1700         dest.size = (unsigned int) sizeof (Pixel);
1701         dest.addr = (XPointer) &pixel;
1702         if (XtConvertAndStore(w, XtRString, &source, XtRPixel, &dest))
1703             sv->colr = color;
1704     }
1705     if (pixel != 0) {
1706         char *arg_name = (sv->set || sv->inverted_hilite) ? XtNbackground
1707                          : XtNforeground;
1708 
1709         XtSetArg(args[0], arg_name, pixel);
1710         XtSetValues(w, args, ONE);
1711     }
1712 }
1713 
1714 static boolean
name_widget_has_label(struct X_status_value * sv)1715 name_widget_has_label(struct X_status_value *sv)
1716 {
1717     Arg args[1];
1718     const char *label;
1719 
1720     XtSetArg(args[0], XtNlabel, &label);
1721     XtGetValues(sv->w, args, ONE);
1722     return strlen(label) > 0;
1723 }
1724 
1725 static void
apply_hilite_attributes(struct X_status_value * sv,int attributes)1726 apply_hilite_attributes(struct X_status_value *sv, int attributes)
1727 {
1728     boolean attr_inversion = ((HL_INVERSE & attributes)
1729                               && (sv->type != SV_NAME
1730                                   || name_widget_has_label(sv)));
1731 
1732     if (sv->inverted_hilite != attr_inversion) {
1733         sv->inverted_hilite = attr_inversion;
1734         if (!sv->set) {
1735             if (sv->type == SV_VALUE)
1736                 hilight_value(sv->w);
1737             else
1738                 hilight_label(sv->w);
1739         }
1740     }
1741     sv->attr = attributes;
1742     /* Could possibly add more attributes here: HL_ATTCLR_DIM,
1743        HL_ATTCLR_BLINK, HL_ATTCLR_ULINE, and HL_ATTCLR_BOLD. If so,
1744        extract the above into its own function apply_hilite_inverse()
1745        and each other attribute into its own to keep the code clean. */
1746 }
1747 
1748 /*
1749  * Update the displayed status.  The current code in botl.c updates
1750  * two lines of information.  Both lines are always updated one after
1751  * the other.  So only do our update when we update the second line.
1752  *
1753  * Information on the first line:
1754  *	name, characteristics, alignment, score
1755  *
1756  * Information on the second line:
1757  *	dlvl, gold, hp, power, ac, {level & exp or HD **}, time,
1758  *	status * (stone, slime, strngl, foodpois, termill,
1759  *                hunger, encumbrance, lev, fly, ride,
1760  *                blind, deaf, stun, conf, hallu)
1761  *
1762  *  [*] order of status fields is different on tty.
1763  * [**] HD is shown instead of level and exp if Upolyd.
1764  */
1765 static void
update_fancy_status_field(int i,int color,int attributes)1766 update_fancy_status_field(int i, int color, int attributes)
1767 {
1768     struct X_status_value *sv = &shown_stats[i];
1769     unsigned long condmask = 0L;
1770     long val = 0L;
1771 
1772     switch (i) {
1773         case F_DUMMY:
1774             val = 0L;
1775             break;
1776         case F_STR:
1777             val = (long) ACURR(A_STR);
1778             break;
1779         case F_DEX:
1780             val = (long) ACURR(A_DEX);
1781             break;
1782         case F_CON:
1783             val = (long) ACURR(A_CON);
1784             break;
1785         case F_INT:
1786             val = (long) ACURR(A_INT);
1787             break;
1788         case F_WIS:
1789             val = (long) ACURR(A_WIS);
1790             break;
1791         case F_CHA:
1792             val = (long) ACURR(A_CHA);
1793             break;
1794         /*
1795          * Label stats.  With the exceptions of hunger and encumbrance
1796          * these are either on or off.  Pleae leave the ternary operators
1797          * the way they are.  I want to specify 0 or 1, not a boolean.
1798          */
1799         case F_HUNGER:
1800             val = (long) u.uhs;
1801             break;
1802         case F_ENCUMBER:
1803             val = (long) near_capacity();
1804             break;
1805 
1806         case F_TRAPPED: /* belongs with non-fatal but fits with 'other' */
1807             condmask = BL_MASK_TRAPPED;
1808             break;
1809         case F_TETHERED: /* overloaded with 'trapped' */
1810             condmask = BL_MASK_TETHERED;
1811             break;
1812         /* 'other' status conditions */
1813         case F_LEV:
1814             condmask = BL_MASK_LEV;
1815             break;
1816         case F_FLY:
1817             condmask = BL_MASK_FLY;
1818             break;
1819         case F_RIDE:
1820             condmask = BL_MASK_RIDE;
1821             break;
1822         /* fatal status conditions */
1823         case F_GRABBED:
1824             condmask = BL_MASK_GRAB;
1825             break;
1826         case F_STONE:
1827             condmask = BL_MASK_STONE;
1828             break;
1829         case F_SLIME:
1830             condmask = BL_MASK_SLIME;
1831             break;
1832         case F_STRNGL:
1833             condmask = BL_MASK_STRNGL;
1834             break;
1835         case F_FOODPOIS:
1836             condmask = BL_MASK_FOODPOIS;
1837             break;
1838         case F_TERMILL:
1839             condmask = BL_MASK_TERMILL;
1840             break;
1841         case F_IN_LAVA: /* could overload with 'trapped' but is more severe */
1842             condmask = BL_MASK_INLAVA;
1843             break;
1844         case F_WITHER:
1845             condmask = BL_MASK_WITHER;
1846             break;
1847         /* non-fatal status conditions */
1848         case F_HELD:
1849             condmask = BL_MASK_HELD;
1850             break;
1851         case F_HOLDING: /* belongs with 'other' but overloads 'held' */
1852             condmask = BL_MASK_HOLDING;
1853             break;
1854         case F_BLIND:
1855             condmask = BL_MASK_BLIND;
1856             break;
1857         case F_DEAF:
1858             condmask = BL_MASK_DEAF;
1859             break;
1860         case F_STUN:
1861             condmask = BL_MASK_STUN;
1862             break;
1863         case F_CONF:
1864             condmask = BL_MASK_CONF;
1865             break;
1866         case F_HALLU:
1867             condmask = BL_MASK_HALLU;
1868             break;
1869 
1870         case F_NAME:
1871         case F_DLEVEL:
1872             val = (long) 0L;
1873             break; /* special */
1874 
1875         case F_GOLD:
1876             val = money_cnt(g.invent);
1877             if (val < 0L)
1878                 val = 0L; /* ought to issue impossible() and discard gold */
1879             break;
1880         case F_HP:
1881             val = (long) (Upolyd ? (u.mh > 0 ? u.mh : 0)
1882                                  : (u.uhp > 0 ? u.uhp : 0));
1883             break;
1884         case F_MAXHP:
1885             val = (long) (Upolyd ? u.mhmax : u.uhpmax);
1886             break;
1887         case F_POWER:
1888             val = (long) u.uen;
1889             break;
1890         case F_MAXPOWER:
1891             val = (long) u.uenmax;
1892             break;
1893         case F_AC:
1894             val = (long) u.uac;
1895             break;
1896         case F_XP_LEVL:
1897             val = (long) (Upolyd ? mons[u.umonnum].mlevel : u.ulevel);
1898             break;
1899         case F_EXP_PTS:
1900             val = flags.showexp ? u.uexp : 0L;
1901             break;
1902         case F_ALIGN:
1903             val = (long) u.ualign.type;
1904             break;
1905         case F_TIME:
1906             val = flags.time ? (long) g.moves : 0L;
1907             break;
1908         case F_SCORE:
1909 #ifdef SCORE_ON_BOTL
1910             val = flags.showscore ? botl_score() : 0L;
1911 #else
1912             val = 0L;
1913 #endif
1914             break;
1915         default: {
1916             /*
1917              * There is a possible infinite loop that occurs with:
1918              *
1919              * 	impossible->pline->flush_screen->bot->bot{1,2}->
1920              * 	putstr->adjust_status->update_other->impossible
1921              *
1922              * Break out with this.
1923              */
1924             static boolean active = FALSE;
1925 
1926             if (!active) {
1927                 active = TRUE;
1928                 impossible("update_other: unknown shown value");
1929                 active = FALSE;
1930             }
1931             val = 0L;
1932             break;
1933         } /* default */
1934     } /* switch */
1935 
1936     if (condmask) {
1937         const struct f_overload *fo = ff_ovld_from_mask(condmask);
1938 
1939         val = ((X11_condition_bits & condmask) != 0L);
1940         /* if we're turning an overloaded field Off, don't do it if any
1941            of the other alternatives are being set On because we would
1942            clobber that if the other one happens to be drawn first */
1943         if (!val && fo && (X11_condition_bits & fo->all_mask) != 0L) {
1944             skip_cond_val(sv);
1945             return;
1946         }
1947     }
1948     update_val(sv, val);
1949     if (color != sv->colr)
1950         update_color(sv, color);
1951     if (attributes != sv->attr)
1952         apply_hilite_attributes(sv, attributes);
1953 }
1954 
1955 /* fully update status after bl_flush or window resize */
1956 static void
update_fancy_status(boolean force_update)1957 update_fancy_status(boolean force_update)
1958 {
1959     static boolean old_showtime, old_showexp, old_showscore;
1960     static int old_upolyd = -1; /* -1: force first time update */
1961     int i;
1962 
1963     if (force_update
1964         || Upolyd != old_upolyd /* Xp vs HD */
1965         || flags.time != old_showtime
1966         || flags.showexp != old_showexp
1967         || flags.showscore != old_showscore) {
1968         /* update everything; usually only need this on the very first
1969            time, then later if the window gets resized or if poly/unpoly
1970            triggers Xp <-> HD switch or if an optional field gets
1971            toggled off since there won't be a status_update() call for
1972            the no longer displayed field; we're a bit more conservative
1973            than that and do this when toggling on as well as off */
1974         for (i = 0; i < NUM_STATS; i++)
1975             update_fancy_status_field(i, NO_COLOR, HL_UNDEF);
1976         old_condition_bits = X11_condition_bits;
1977 
1978         old_upolyd = Upolyd;
1979         old_showtime = flags.time;
1980         old_showexp = flags.showexp;
1981         old_showscore = flags.showscore;
1982     }
1983 }
1984 
1985 /*
1986  * Turn off hilighted status values after a certain amount of turns.
1987  */
1988 void
check_turn_events(void)1989 check_turn_events(void)
1990 {
1991     int i;
1992     struct X_status_value *sv;
1993 
1994     for (sv = shown_stats, i = 0; i < NUM_STATS; i++, sv++) {
1995         if (!sv->set)
1996             continue;
1997 
1998         if (sv->turn_count++ >= iflags.hilite_delta) {
1999             /* unhighlights by toggling a highlighted item back off again,
2000                unless forced inverted by a status_hilite rule */
2001             if (!sv->inverted_hilite) {
2002                 if (sv->type == SV_VALUE)
2003                     hilight_value(sv->w);
2004                 else
2005                     hilight_label(sv->w);
2006             }
2007             sv->set = FALSE;
2008         }
2009     }
2010 }
2011 
2012 /* Initialize alternate status ============================================ */
2013 
2014 /* Return a string for the initial width, so use longest possible value. */
2015 static const char *
width_string(int sv_index)2016 width_string(int sv_index)
2017 {
2018     switch (sv_index) {
2019     case F_DUMMY:
2020         return " ";
2021 
2022     case F_STR:
2023         return "018/**";
2024     case F_DEX:
2025     case F_CON:
2026     case F_INT:
2027     case F_WIS:
2028     case F_CHA:
2029         return "088"; /* all but str never get bigger */
2030 
2031     case F_HUNGER:
2032         return "Satiated";
2033     case F_ENCUMBER:
2034         return "Overloaded";
2035 
2036     case F_LEV:
2037     case F_FLY:
2038     case F_RIDE:
2039     case F_TRAPPED:
2040     case F_TETHERED:
2041     case F_GRABBED:
2042     case F_STONE:
2043     case F_SLIME:
2044     case F_STRNGL:
2045     case F_FOODPOIS:
2046     case F_TERMILL:
2047     case F_WITHER:
2048     case F_IN_LAVA:
2049     case F_HELD:
2050     case F_HOLDING:
2051     case F_BLIND:
2052     case F_DEAF:
2053     case F_STUN:
2054     case F_CONF:
2055     case F_HALLU:
2056         return shown_stats[sv_index].name;
2057 
2058     case F_NAME:
2059     case F_DLEVEL:
2060         return ""; /* longest possible value not needed for these */
2061 
2062     case F_HP:
2063     case F_MAXHP:
2064         return "9999";
2065     case F_POWER:
2066     case F_MAXPOWER:
2067         return "9999";
2068     case F_AC:
2069         return "-127";
2070     case F_XP_LEVL:
2071         return "99";
2072     case F_GOLD:
2073         /* strongest hero can pick up roughly 30% of this much */
2074         return "999999"; /* same limit as tty */
2075     case F_EXP_PTS:
2076     case F_TIME:
2077     case F_SCORE:
2078         return "123456789"; /* a tenth digit will still fit legibly */
2079     case F_ALIGN:
2080         return "Neutral";
2081     }
2082     impossible("width_string: unknown index %d\n", sv_index);
2083     return "";
2084 }
2085 
2086 static void
create_widget(Widget parent,struct X_status_value * sv,int sv_index)2087 create_widget(Widget parent, struct X_status_value *sv, int sv_index)
2088 {
2089     Arg args[4];
2090     Cardinal num_args;
2091 
2092     switch (sv->type) {
2093     case SV_VALUE:
2094         sv->w = create_value(parent, sv->name);
2095         set_value(sv->w, width_string(sv_index));
2096         break;
2097     case SV_LABEL:
2098         /* Labels get their own buffer. */
2099         sv->name = (char *) alloc(BUFSZ);
2100         /* we need to cast away 'const' when assigning a value */
2101         *(char *) (sv->name) = '\0';
2102 
2103         num_args = 0;
2104         XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
2105         XtSetArg(args[num_args], XtNinternalHeight, 0); num_args++;
2106         sv->w = XtCreateManagedWidget((sv_index == F_NAME)
2107                                          ? "name"
2108                                          : "dlevel",
2109                                       labelWidgetClass, parent,
2110                                       args, num_args);
2111         break;
2112     case SV_NAME: {
2113         char buf[BUFSZ];
2114         const char *txt;
2115         const struct f_overload *fo = ff_ovld_from_indx(sv_index);
2116         int baseindx = fo ? fo->conds[0].ff : sv_index;
2117 
2118         if (sv_index != baseindx) {
2119             /* this code isn't actually executed; only the base condition
2120                is in one of the fancy status columns and only the fields
2121                in those columns are passed to this routine; the real
2122                initialization--this same assignment--for overloaded
2123                conditions takes place at the end of create_fancy_status() */
2124             sv->w = shown_stats[baseindx].w;
2125             break;
2126         }
2127         txt = width_string(sv_index); /* for conditions, it's just sv->name */
2128         if (fo) {
2129             int i, ff, altln, ln = (int) strlen(txt);
2130 
2131             /* make the initial value have the width of the longest of
2132                these overloaded conditions; used for widget sizing, not for
2133                display, and ultimately only matters if one of the overloads
2134                happens to be the longest string in its whole column */
2135             for (i = 1; i < NUM_OVLD && (ff = fo->conds[i].ff) > 0; ++i)
2136                 if ((altln = (int) strlen(width_string(ff))) > ln)
2137                     ln = altln;
2138             Sprintf(buf, "%*s", ln, txt);
2139             txt = buf;
2140         }
2141         num_args = 0;
2142         XtSetArg(args[0], XtNlabel, txt); num_args++;
2143         XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
2144         XtSetArg(args[num_args], XtNinternalHeight, 0); num_args++;
2145         sv->w = XtCreateManagedWidget(sv->name, labelWidgetClass, parent,
2146                                       args, num_args);
2147         break;
2148     }
2149     default:
2150         panic("create_widget: unknown type %d", sv->type);
2151     }
2152 }
2153 
2154 /*
2155  * Get current width of value.  width2p is only valid for SV_VALUE types.
2156  */
2157 static void
get_widths(struct X_status_value * sv,int * width1p,int * width2p)2158 get_widths(struct X_status_value *sv, int *width1p, int *width2p)
2159 {
2160     Arg args[1];
2161     Dimension width;
2162 
2163     switch (sv->type) {
2164     case SV_VALUE:
2165         *width1p = get_name_width(sv->w);
2166         *width2p = get_value_width(sv->w);
2167         break;
2168     case SV_LABEL:
2169     case SV_NAME:
2170         XtSetArg(args[0], XtNwidth, &width);
2171         XtGetValues(sv->w, args, ONE);
2172         *width1p = width;
2173         *width2p = 0;
2174         break;
2175     default:
2176         panic("get_widths: unknown type %d", sv->type);
2177     }
2178 }
2179 
2180 static void
set_widths(struct X_status_value * sv,int width1,int width2)2181 set_widths(struct X_status_value *sv, int width1, int width2)
2182 {
2183     Arg args[1];
2184 
2185     switch (sv->type) {
2186     case SV_VALUE:
2187         set_name_width(sv->w, width1);
2188         set_value_width(sv->w, width2);
2189         break;
2190     case SV_LABEL:
2191     case SV_NAME:
2192         XtSetArg(args[0], XtNwidth, (width1 + width2));
2193         XtSetValues(sv->w, args, ONE);
2194         break;
2195     default:
2196         panic("set_widths: unknown type %d", sv->type);
2197     }
2198 }
2199 
2200 static Widget
init_column(const char * name,Widget parent,Widget top,Widget left,int * col_indices,int xtrawidth)2201 init_column(const char *name, Widget parent, Widget top, Widget left,
2202             int *col_indices, int xtrawidth)
2203 {
2204     Widget form;
2205     Arg args[4];
2206     Cardinal num_args;
2207     int max_width1, width1, max_width2, width2;
2208     int *ip;
2209     struct X_status_value *sv;
2210 
2211     num_args = 0;
2212     if (top != (Widget) 0) {
2213         XtSetArg(args[num_args], nhStr(XtNfromVert), top); num_args++;
2214     }
2215     if (left != (Widget) 0) {
2216         XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++;
2217     }
2218     /* this was 0 but that resulted in the text being crammed together */
2219     XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 2); num_args++;
2220     form = XtCreateManagedWidget(name, formWidgetClass, parent,
2221                                  args, num_args);
2222 
2223     max_width1 = max_width2 = 0;
2224     for (ip = col_indices; *ip >= 0; ip++) {
2225         sv = &shown_stats[*ip];
2226         create_widget(form, sv, *ip); /* will set init width */
2227         if (ip != col_indices) {      /* not first */
2228             num_args = 0;
2229             XtSetArg(args[num_args], nhStr(XtNfromVert),
2230                      shown_stats[*(ip - 1)].w); num_args++;
2231             XtSetValues(sv->w, args, num_args);
2232         }
2233         get_widths(sv, &width1, &width2);
2234         if (width1 > max_width1)
2235             max_width1 = width1;
2236         if (width2 > max_width2)
2237             max_width2 = width2;
2238     }
2239 
2240     /* insert some extra spacing between columns */
2241     max_width1 += xtrawidth;
2242 
2243     for (ip = col_indices; *ip >= 0; ip++) {
2244         set_widths(&shown_stats[*ip], max_width1, max_width2);
2245     }
2246 
2247     /* There is room behind the end marker for the two widths. */
2248     *++ip = max_width1;
2249     *++ip = max_width2;
2250 
2251     return form;
2252 }
2253 
2254 /*
2255  * These are the orders of the displayed columns.  Change to suit.  The -1
2256  * indicates the end of the column.  The two numbers after that are used
2257  * to store widths that are calculated at run-time.
2258  *
2259  * 3.7:  changed so that all 6 columns have 8 rows, but a few entries
2260  * are left blank <>.  Exp-points, Score, and Time are optional depending
2261  * on run-time settings; Xp-level is replaced by Hit-Dice (and Exp-points
2262  * suppressed) when the hero is polymorphed.  Title and Dungeon-Level span
2263  * two columns and might expand to more if 'hitpointbar' is implemented.
2264  *
2265  Title ("Plname the Rank")   <>            <>           <>          <>
2266  Dungeon-Branch-and-Level    <>           Hunger       Grabbed     Held
2267  Hit-points    Max-HP       Strength      Encumbrance  Petrifying  Blind
2268  Power-points  Max-Power    Dexterity     Trapped      Slimed      Deaf
2269  Armor-class   Alignment    Constitution  Levitation   Strangled   Stunned
2270  Xp-level     [Exp-points]  Intelligence  Flying       Food-Pois   Confused
2271  Gold         [Score]       Wisdom        Riding       Term-Ill    Hallucinatg
2272   <>          [Time]        Charisma       <>          Sinking      <>
2273  *
2274  * A seventh column is going to be needed to fit in more conditions.
2275  *
2276  * Possible enhancement:  if Exp-points and Score are both disabled, move
2277  * Gold to the Exp-points slot.
2278  */
2279 
2280 /* including F_DUMMY makes the three status condition columns evenly
2281    spaced with regard to the adjacent characteristics (Str,Dex,&c) column;
2282    we lose track of the Widget pointer for F_DUMMY, each use clobbering the
2283    one before, leaving the one from leftover_indices[]; since they're never
2284    updated, that shouldn't matter */
2285 static int status_indices[3][11] = {
2286     { F_DUMMY, F_HUNGER, F_ENCUMBER, F_TRAPPED,
2287       F_LEV, F_FLY, F_RIDE, F_DUMMY, -1, 0, 0 },
2288     { F_DUMMY, F_GRABBED, F_STONE, F_SLIME, F_STRNGL,
2289       F_FOODPOIS, F_TERMILL, F_IN_LAVA, -1, 0, 0 },
2290     { F_DUMMY, F_HELD, F_BLIND, F_DEAF, F_STUN,
2291       F_CONF, F_HALLU, F_WITHER, -1, 0, 0 },
2292 };
2293 /* used to fill up the empty space to right of 3rd status condition column */
2294 static int leftover_indices[] = { F_DUMMY, -1, 0, 0 };
2295 /* -2: top two rows of these columns are reserved for title and location */
2296 static int col1_indices[11 - 2] = {
2297     F_HP,    F_POWER,    F_AC,    F_XP_LEVL, F_GOLD,  F_DUMMY,  -1, 0, 0
2298 };
2299 static int col2_indices[11 - 2] = {
2300     F_MAXHP, F_MAXPOWER, F_ALIGN, F_EXP_PTS, F_SCORE, F_TIME,   -1, 0, 0
2301 };
2302 static int characteristics_indices[11 - 2] = {
2303     F_STR, F_DEX, F_CON, F_INT, F_WIS, F_CHA, -1, 0, 0
2304 };
2305 
2306 /*
2307  * Produce a form that looks like the following:
2308  *
2309  *		  title
2310  *               location
2311  * col1_indices[0]	col2_indices[0]      col3_indices[0]
2312  * col1_indices[1]	col2_indices[1]      col3_indices[1]
2313  *    ...                  ...                  ...
2314  * col1_indices[5]	col2_indices[5]      col3_indices[5]
2315  *
2316  * The status conditions are managed separately and appear to the right
2317  * of this form.
2318  *
2319  * TODO:  widen title field and implement hitpoint bar on it.
2320  */
2321 static Widget
init_info_form(Widget parent,Widget top,Widget left)2322 init_info_form(Widget parent, Widget top, Widget left)
2323 {
2324     Widget form, col1, col2;
2325     struct X_status_value *sv_name, *sv_dlevel;
2326     Arg args[6];
2327     Cardinal num_args;
2328     int total_width, *ip;
2329 
2330     num_args = 0;
2331     if (top != (Widget) 0) {
2332         XtSetArg(args[num_args], nhStr(XtNfromVert), top); num_args++;
2333     }
2334     if (left != (Widget) 0) {
2335         XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++;
2336     }
2337     XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 2); num_args++;
2338     form = XtCreateManagedWidget("status_info", formWidgetClass, parent,
2339                                  args, num_args);
2340 
2341     /* top line/row of form */
2342     sv_name = &shown_stats[F_NAME]; /* title */
2343     create_widget(form, sv_name, F_NAME);
2344 
2345     /* second line/row */
2346     sv_dlevel = &shown_stats[F_DLEVEL]; /* location */
2347     create_widget(form, sv_dlevel, F_DLEVEL);
2348 
2349     num_args = 0;
2350     XtSetArg(args[num_args], nhStr(XtNfromVert), sv_name->w); num_args++;
2351     XtSetValues(sv_dlevel->w, args, num_args);
2352 
2353     /* there are 3 columns beneath but top 2 rows are centered over first 2 */
2354     col1 = init_column("name_col1", form, sv_dlevel->w, (Widget) 0,
2355                        col1_indices, 0);
2356     col2 = init_column("name_col2", form, sv_dlevel->w, col1,
2357                        col2_indices, 5);
2358     (void) init_column("status_characteristics", form, sv_dlevel->w, col2,
2359                        characteristics_indices, 15);
2360 
2361     /* Add calculated widths. */
2362     for (ip = col1_indices; *ip >= 0; ip++)
2363         ; /* skip to end */
2364     total_width = *++ip;
2365     total_width += *++ip;
2366     for (ip = col2_indices; *ip >= 0; ip++)
2367         ; /* skip to end */
2368     total_width += *++ip;
2369     total_width += *++ip;
2370 
2371     XtSetArg(args[0], XtNwidth, total_width);
2372     XtSetValues(sv_name->w, args, ONE);
2373     XtSetArg(args[0], XtNwidth, total_width);
2374     XtSetValues(sv_dlevel->w, args, ONE);
2375 
2376     return form;
2377 }
2378 
2379 /* give the three status condition columns the same width */
2380 static void
fixup_cond_widths(void)2381 fixup_cond_widths(void)
2382 {
2383     int pass, i, *ip, w1, w2;
2384 
2385     w1 = w2 = 0;
2386     for (pass = 1; pass <= 2; ++pass) { /* two passes... */
2387         for (i = 0; i < 3; i++) { /* three columns */
2388             for (ip = status_indices[i]; *ip != -1; ++ip) { /* X fields */
2389                 /* pass 1: find -1;  pass 2: update field widths, find -1 */
2390                 if (pass == 2)
2391                     set_widths(&shown_stats[*ip], w1, w2);
2392             }
2393             /* found -1; the two slots beyond it contain column widths */
2394             if (pass == 1) { /* pass 1: collect maxima */
2395                 if (ip[1] > w1)
2396                     w1 = ip[1];
2397                 if (ip[2] > w2)
2398                     w2 = ip[2];
2399             } else { /* pass 2: update column widths with maxima */
2400                 ip[1] = w1;
2401                 ip[2] = w2;
2402             }
2403         }
2404         /* ascetics:  expand the maximum width to make cond columns wider */
2405         if (pass == 1) {
2406             w1 += 15;
2407             if (w2 > 0)
2408                 w2 += 15;
2409         }
2410     }
2411 }
2412 
2413 /*
2414  * Create the layout for the fancy status.  Return a form widget that
2415  * contains everything.
2416  */
2417 static Widget
create_fancy_status(Widget parent,Widget top)2418 create_fancy_status(Widget parent, Widget top)
2419 {
2420     Widget form; /* The form that surrounds everything. */
2421     Widget w;
2422     Arg args[8];
2423     Cardinal num_args;
2424     char buf[32];
2425     const struct f_overload *fo;
2426     int i, ff;
2427 
2428     num_args = 0;
2429     if (top != (Widget) 0) {
2430         XtSetArg(args[num_args], nhStr(XtNfromVert), top); num_args++;
2431     }
2432     XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 2); num_args++;
2433     XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
2434     XtSetArg(args[num_args], XtNorientation, XtorientHorizontal); num_args++;
2435     form = XtCreateManagedWidget("fancy_status", panedWidgetClass, parent,
2436                                  args, num_args);
2437 
2438     w = init_info_form(form, (Widget) 0, (Widget) 0);
2439 #if 0   /* moved to init_info_form() */
2440     w = init_column("status_characteristics", form, (Widget) 0, w,
2441                     characteristics_indices, 15);
2442 #endif
2443     for (i = 0; i < 3; i++) {
2444         Sprintf(buf, "status_condition%d", i + 1);
2445         w = init_column(buf, form, (Widget) 0, w, status_indices[i], 0);
2446     }
2447     fixup_cond_widths(); /* make all 3 status_conditionN columns same width */
2448     /* extra dummy 'column' to allocate any remaining space below the map */
2449     (void) init_column("status_leftover", form, (Widget) 0, w,
2450                        leftover_indices, 0);
2451 
2452     /* handle overloading; extra conditions don't start out in any column
2453        so need to be initialized separately; the only initialization they
2454        need is to share the widget of the base condition which is present
2455        in one of the columns [could be deferred until first use] */
2456     for (fo = cond_ovl; fo < cond_ovl + SIZE(cond_ovl); ++fo)
2457         for (i = 1; i < NUM_OVLD && (ff = fo->conds[i].ff) > 0; ++i)
2458             if (!shown_stats[ff].w)
2459                 shown_stats[ff].w = shown_stats[fo->conds[0].ff].w;
2460 
2461     return form;
2462 }
2463 
2464 static void
destroy_fancy_status(struct xwindow * wp)2465 destroy_fancy_status(struct xwindow *wp)
2466 {
2467     int i;
2468     struct X_status_value *sv;
2469 
2470     if (!wp->keep_window)
2471         XtDestroyWidget(wp->w), wp->w = (Widget) 0;
2472 
2473     for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++)
2474         if (sv->type == SV_LABEL) {
2475             free((genericptr_t) sv->name);
2476             sv->name = 0;
2477         }
2478 }
2479 
2480 /*winstat.c*/
2481