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