Document explicitly what m-prefix does to each command
[aNetHack.git] / win / X11 / winstat.c
blob8eb9469bb77ad4f308b8c4ba6bbac03163626b3e
1 /* NetHack 3.6 winstat.c $NHDT-Date: 1452920162 2016/01/16 04:56:02 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.16 $ */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
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, otherewise 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.
13 #ifndef SYSV
14 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
15 #endif
17 #include <X11/Intrinsic.h>
18 #include <X11/StringDefs.h>
19 #include <X11/Shell.h>
20 #include <X11/Xaw/AsciiText.h>
21 #include <X11/Xaw/Cardinals.h>
22 #include <X11/Xaw/Form.h>
23 #include <X11/Xaw/Paned.h>
24 #include <X11/Xaw/Label.h>
25 #include <X11/Xatom.h>
27 #ifdef PRESERVE_NO_SYSV
28 #ifdef SYSV
29 #undef SYSV
30 #endif
31 #undef PRESERVE_NO_SYSV
32 #endif
34 #include "hack.h"
35 #include "winX.h"
37 extern const char *hu_stat[]; /* from eat.c */
38 extern const char *enc_stat[]; /* from botl.c */
40 static void FDECL(update_fancy_status, (struct xwindow *));
41 static Widget FDECL(create_fancy_status, (Widget, Widget));
42 static void FDECL(destroy_fancy_status, (struct xwindow *));
44 void
45 create_status_window(wp, create_popup, parent)
46 struct xwindow *wp; /* window pointer */
47 boolean create_popup;
48 Widget parent;
50 XFontStruct *fs;
51 Arg args[8];
52 Cardinal num_args;
53 Position top_margin, bottom_margin, left_margin, right_margin;
55 wp->type = NHW_STATUS;
57 if (!create_popup) {
59 * If we are not creating a popup, then we must be the "main" status
60 * window.
62 if (!parent)
63 panic("create_status_window: no parent for fancy status");
64 wp->status_information = 0;
65 wp->w = create_fancy_status(parent, (Widget) 0);
66 return;
69 wp->status_information =
70 (struct status_info_t *) alloc(sizeof(struct status_info_t));
72 init_text_buffer(&wp->status_information->text);
74 num_args = 0;
75 XtSetArg(args[num_args], XtNallowShellResize, False);
76 num_args++;
77 XtSetArg(args[num_args], XtNinput, False);
78 num_args++;
80 wp->popup = parent = XtCreatePopupShell(
81 "status_popup", topLevelShellWidgetClass, toplevel, args, num_args);
83 * If we're here, then this is an auxiliary status window. If we're
84 * cancelled via a delete window message, we should just pop down.
87 num_args = 0;
88 XtSetArg(args[num_args], nhStr(XtNdisplayCaret), False);
89 num_args++;
90 XtSetArg(args[num_args], nhStr(XtNscrollHorizontal),
91 XawtextScrollWhenNeeded);
92 num_args++;
93 XtSetArg(args[num_args], nhStr(XtNscrollVertical),
94 XawtextScrollWhenNeeded);
95 num_args++;
97 wp->w = XtCreateManagedWidget("status", /* name */
98 asciiTextWidgetClass,
99 parent, /* parent widget */
100 args, /* set some values */
101 num_args); /* number of values to set */
104 * Adjust the height and width of the message window so that it
105 * is two lines high and COLNO of the widest characters wide.
108 /* Get the font and margin information. */
109 num_args = 0;
110 XtSetArg(args[num_args], XtNfont, &fs);
111 num_args++;
112 XtSetArg(args[num_args], nhStr(XtNtopMargin), &top_margin);
113 num_args++;
114 XtSetArg(args[num_args], nhStr(XtNbottomMargin), &bottom_margin);
115 num_args++;
116 XtSetArg(args[num_args], nhStr(XtNleftMargin), &left_margin);
117 num_args++;
118 XtSetArg(args[num_args], nhStr(XtNrightMargin), &right_margin);
119 num_args++;
120 XtGetValues(wp->w, args, num_args);
122 wp->pixel_height = 2 * nhFontHeight(wp->w) + top_margin + bottom_margin;
123 wp->pixel_width =
124 COLNO * fs->max_bounds.width + left_margin + right_margin;
126 /* Set the new width and height. */
127 num_args = 0;
128 XtSetArg(args[num_args], XtNwidth, wp->pixel_width);
129 num_args++;
130 XtSetArg(args[num_args], XtNheight, wp->pixel_height);
131 num_args++;
132 XtSetValues(wp->w, args, num_args);
135 void
136 destroy_status_window(wp)
137 struct xwindow *wp;
139 /* If status_information is defined, then it a "text" status window. */
140 if (wp->status_information) {
141 if (wp->popup) {
142 nh_XtPopdown(wp->popup);
143 if (!wp->keep_window)
144 XtDestroyWidget(wp->popup), wp->popup = (Widget) 0;
146 free((genericptr_t) wp->status_information);
147 wp->status_information = 0;
148 } else {
149 destroy_fancy_status(wp);
151 if (!wp->keep_window)
152 wp->type = NHW_NONE;
156 * This assumes several things:
157 * + Status has only 2 lines
158 * + That both lines are updated in succession in line order.
159 * + We didn't set stringInPlace on the widget.
161 void
162 adjust_status(wp, str)
163 struct xwindow *wp;
164 const char *str;
166 Arg args[2];
167 Cardinal num_args;
169 if (!wp->status_information) {
170 update_fancy_status(wp);
171 return;
174 if (wp->cursy == 0) {
175 clear_text_buffer(&wp->status_information->text);
176 append_text_buffer(&wp->status_information->text, str, FALSE);
177 return;
179 append_text_buffer(&wp->status_information->text, str, FALSE);
181 /* Set new buffer as text. */
182 num_args = 0;
183 XtSetArg(args[num_args], XtNstring, wp->status_information->text.text);
184 num_args++;
185 XtSetValues(wp->w, args, num_args);
188 /* Fancy Status
189 * -------------------------------------------------------------*/
190 static int hilight_time = 1; /* number of turns to hilight a changed value */
192 struct X_status_value {
193 /* we have to cast away 'const' when assigning new names */
194 const char *name; /* text name */
195 int type; /* status type */
196 Widget w; /* widget of name/value pair */
197 long last_value; /* value displayed */
198 int turn_count; /* last time the value changed */
199 boolean set; /* if highlighted */
200 boolean after_init; /* don't highlight on first change (init) */
203 /* valid type values */
204 #define SV_VALUE 0 /* displays a label:value pair */
205 #define SV_LABEL 1 /* displays a changable label */
206 #define SV_NAME 2 /* displays an unchangeable name */
208 static void FDECL(hilight_label, (Widget));
209 static void FDECL(update_val, (struct X_status_value *, long));
210 static const char *FDECL(width_string, (int));
211 static void FDECL(create_widget, (Widget, struct X_status_value *, int));
212 static void FDECL(get_widths, (struct X_status_value *, int *, int *));
213 static void FDECL(set_widths, (struct X_status_value *, int, int));
214 static Widget FDECL(init_column, (const char *, Widget, Widget, Widget,
215 int *));
216 static void NDECL(fixup_cond_widths);
217 static Widget FDECL(init_info_form, (Widget, Widget, Widget));
220 * Form entry storage indices.
222 #define F_DUMMY 0
223 #define F_STR 1
224 #define F_DEX 2
225 #define F_CON 3
226 #define F_INT 4
227 #define F_WIS 5
228 #define F_CHA 6
230 #define F_NAME 7
231 #define F_DLEVEL 8
232 #define F_GOLD 9
233 #define F_HP 10
234 #define F_MAXHP 11
235 #define F_POWER 12
236 #define F_MAXPOWER 13
237 #define F_AC 14
238 #define F_LEVEL 15
239 #define F_EXP 16
240 #define F_ALIGN 17
241 #define F_TIME 18
242 #define F_SCORE 19
244 /* status conditions grouped by columns; tty orders these differently */
245 #define F_STONE 20
246 #define F_SLIME 21
247 #define F_STRNGL 22
248 #define F_FOODPOIS 23
249 #define F_TERMILL 24
251 #define F_HUNGER 25
252 #define F_ENCUMBER 26
253 #define F_LEV 27
254 #define F_FLY 28
255 #define F_RIDE 29
257 #define F_BLIND 30
258 #define F_DEAF 31
259 #define F_STUN 32
260 #define F_CONF 33
261 #define F_HALLU 34
263 #define NUM_STATS 35
266 * Notes:
267 * + Alignment needs a different init value, because -1 is an alignment.
268 * + Armor Class is an schar, so 256 is out of range.
269 * + Blank value is 0 and should never change.
271 static struct X_status_value shown_stats[NUM_STATS] = {
272 { "", SV_NAME, (Widget) 0, -1, 0, FALSE, FALSE }, /* 0*/
274 { "Strength", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
275 { "Dexterity", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
276 { "Constitution", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
277 { "Intelligence", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
278 { "Wisdom", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /* 5*/
279 { "Charisma", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
281 { "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* name */
282 { "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* dlvl */
283 { "Gold", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
284 { "Hit Points", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /*10*/
285 { "Max HP", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
286 { "Power", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
287 { "Max Power", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
288 { "Armor Class", SV_VALUE, (Widget) 0, 256, 0, FALSE, FALSE },
289 { "Level", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /*15*/
290 { "Experience", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
291 { "Alignment", SV_VALUE, (Widget) 0, -2, 0, FALSE, FALSE },
292 { "Time", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
293 { "Score", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
295 { "Petrifying", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*20*/
296 { "Slimed", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
297 { "Strangled", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
298 { "Food Pois", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
299 { "Term Ill", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
301 { "", SV_NAME, (Widget) 0, -1, 0, FALSE, TRUE }, /*25*/ /* hunger */
302 { "", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*encumbr */
303 { "Levitating", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
304 { "Flying", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
305 { "Riding", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
307 { "Blind", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*30*/
308 { "Deaf", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
309 { "Stunned", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
310 { "Confused", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
311 { "Hallucinating", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
315 * Set all widget values to a null string. This is used after all spacings
316 * have been calculated so that when the window is popped up we don't get all
317 * kinds of funny values being displayed.
319 void
320 null_out_status()
322 int i;
323 struct X_status_value *sv;
324 Arg args[1];
326 for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++) {
327 switch (sv->type) {
328 case SV_VALUE:
329 set_value(sv->w, "");
330 break;
332 case SV_LABEL:
333 case SV_NAME:
334 XtSetArg(args[0], XtNlabel, "");
335 XtSetValues(sv->w, args, ONE);
336 break;
338 default:
339 impossible("null_out_status: unknown type %d\n", sv->type);
340 break;
345 /* This is almost an exact duplicate of hilight_value() */
346 static void
347 hilight_label(w)
348 Widget w; /* label widget */
350 Arg args[2];
351 Pixel fg, bg;
353 XtSetArg(args[0], XtNforeground, &fg);
354 XtSetArg(args[1], XtNbackground, &bg);
355 XtGetValues(w, args, TWO);
357 XtSetArg(args[0], XtNforeground, bg);
358 XtSetArg(args[1], XtNbackground, fg);
359 XtSetValues(w, args, TWO);
362 static void
363 update_val(attr_rec, new_value)
364 struct X_status_value *attr_rec;
365 long new_value;
367 char buf[BUFSZ];
368 Arg args[4];
370 if (attr_rec->type == SV_LABEL) {
371 if (attr_rec == &shown_stats[F_NAME]) {
372 Strcpy(buf, plname);
373 buf[0] = highc(buf[0]);
374 Strcat(buf, " the ");
375 if (Upolyd) {
376 char mname[BUFSZ];
377 int k;
379 Strcpy(mname, mons[u.umonnum].mname);
380 for (k = 0; mname[k] != '\0'; k++) {
381 if (k == 0 || mname[k - 1] == ' ')
382 mname[k] = highc(mname[k]);
384 Strcat(buf, mname);
385 } else
386 Strcat(buf, rank_of(u.ulevel, pl_character[0], flags.female));
388 } else if (attr_rec == &shown_stats[F_DLEVEL]) {
389 if (!describe_level(buf)) {
390 Strcpy(buf, dungeons[u.uz.dnum].dname);
391 Sprintf(eos(buf), ", level %d", depth(&u.uz));
393 } else {
394 impossible("update_val: unknown label type \"%s\"",
395 attr_rec->name);
396 return;
399 if (strcmp(buf, attr_rec->name) == 0)
400 return; /* same */
402 /* Set the label. 'name' field is const for most entries;
403 we need to cast away that const for this assignment */
404 Strcpy((char *) attr_rec->name, buf);
405 XtSetArg(args[0], XtNlabel, buf);
406 XtSetValues(attr_rec->w, args, ONE);
408 } else if (attr_rec->type == SV_NAME) {
409 if (attr_rec->last_value == new_value)
410 return; /* no change */
412 attr_rec->last_value = new_value;
414 /* special cases: hunger, encumbrance, sickness */
415 if (attr_rec == &shown_stats[F_HUNGER]) {
416 XtSetArg(args[0], XtNlabel, hu_stat[new_value]);
417 } else if (attr_rec == &shown_stats[F_ENCUMBER]) {
418 XtSetArg(args[0], XtNlabel, enc_stat[new_value]);
419 } else if (new_value) {
420 XtSetArg(args[0], XtNlabel, attr_rec->name);
421 } else {
422 XtSetArg(args[0], XtNlabel, "");
424 XtSetValues(attr_rec->w, args, ONE);
426 } else { /* a value pair */
427 boolean force_update = FALSE;
429 /* special case: time can be enabled & disabled */
430 if (attr_rec == &shown_stats[F_TIME]) {
431 static boolean flagtime = TRUE;
433 if (flags.time && !flagtime) {
434 set_name(attr_rec->w, shown_stats[F_TIME].name);
435 force_update = TRUE;
436 flagtime = flags.time;
437 } else if (!flags.time && flagtime) {
438 set_name(attr_rec->w, "");
439 set_value(attr_rec->w, "");
440 flagtime = flags.time;
442 if (!flagtime)
443 return;
446 /* special case: exp can be enabled & disabled */
447 else if (attr_rec == &shown_stats[F_EXP]) {
448 static boolean flagexp = TRUE;
450 if (flags.showexp && !flagexp) {
451 set_name(attr_rec->w, shown_stats[F_EXP].name);
452 force_update = TRUE;
453 flagexp = flags.showexp;
454 } else if (!flags.showexp && flagexp) {
455 set_name(attr_rec->w, "");
456 set_value(attr_rec->w, "");
457 flagexp = flags.showexp;
459 if (!flagexp)
460 return;
463 /* special case: score can be enabled & disabled */
464 else if (attr_rec == &shown_stats[F_SCORE]) {
465 static boolean flagscore = TRUE;
466 #ifdef SCORE_ON_BOTL
468 if (flags.showscore && !flagscore) {
469 set_name(attr_rec->w, shown_stats[F_SCORE].name);
470 force_update = TRUE;
471 flagscore = flags.showscore;
472 } else if (!flags.showscore && flagscore) {
473 set_name(attr_rec->w, "");
474 set_value(attr_rec->w, "");
475 flagscore = flags.showscore;
477 if (!flagscore)
478 return;
479 #else
480 if (flagscore) {
481 set_name(attr_rec->w, "");
482 set_value(attr_rec->w, "");
483 flagscore = FALSE;
485 return;
486 #endif
489 /* special case: when polymorphed, show "HD", disable exp */
490 else if (attr_rec == &shown_stats[F_LEVEL]) {
491 static boolean lev_was_poly = FALSE;
493 if (Upolyd && !lev_was_poly) {
494 force_update = TRUE;
495 set_name(attr_rec->w, "HD");
496 lev_was_poly = TRUE;
497 } else if (Upolyd && lev_was_poly) {
498 force_update = TRUE;
499 set_name(attr_rec->w, shown_stats[F_LEVEL].name);
500 lev_was_poly = FALSE;
502 } else if (attr_rec == &shown_stats[F_EXP]) {
503 static boolean exp_was_poly = FALSE;
505 if (Upolyd && !exp_was_poly) {
506 force_update = TRUE;
507 set_name(attr_rec->w, "");
508 set_value(attr_rec->w, "");
509 exp_was_poly = TRUE;
510 } else if (Upolyd && exp_was_poly) {
511 force_update = TRUE;
512 set_name(attr_rec->w, shown_stats[F_EXP].name);
513 exp_was_poly = FALSE;
515 if (Upolyd)
516 return; /* no display for exp when poly */
519 if (attr_rec->last_value == new_value && !force_update) /* same */
520 return;
522 attr_rec->last_value = new_value;
524 /* Special cases: strength, alignment and "clear". */
525 if (attr_rec == &shown_stats[F_STR]) {
526 if (new_value > 18) {
527 if (new_value > 118)
528 Sprintf(buf, "%ld", new_value - 100);
529 else if (new_value < 118)
530 Sprintf(buf, "18/%02ld", new_value - 18);
531 else
532 Strcpy(buf, "18/**");
533 } else {
534 Sprintf(buf, "%ld", new_value);
536 } else if (attr_rec == &shown_stats[F_ALIGN]) {
537 Strcpy(buf,
538 (new_value == A_CHAOTIC)
539 ? "Chaotic"
540 : (new_value == A_NEUTRAL) ? "Neutral" : "Lawful");
541 } else {
542 Sprintf(buf, "%ld", new_value);
544 set_value(attr_rec->w, buf);
548 * Now hilight the changed information. Names, time and score don't
549 * hilight. If first time, don't hilight. If already lit, don't do
550 * it again.
552 if (attr_rec->type != SV_NAME && attr_rec != &shown_stats[F_TIME]) {
553 if (attr_rec->after_init) {
554 if (!attr_rec->set) {
555 if (attr_rec->type == SV_LABEL)
556 hilight_label(attr_rec->w);
557 else
558 hilight_value(attr_rec->w);
559 attr_rec->set = TRUE;
561 attr_rec->turn_count = 0;
562 } else {
563 attr_rec->after_init = TRUE;
569 * Update the displayed status. The current code in botl.c updates
570 * two lines of information. Both lines are always updated one after
571 * the other. So only do our update when we update the second line.
573 * Information on the first line:
574 * name, attributes, alignment, score
576 * Information on the second line:
577 * dlvl, gold, hp, power, ac, {level & exp or HD **}, time,
578 * status * (stone, slime, strngl, foodpois, termill,
579 * hunger, encumbrance, lev, fly, ride,
580 * blind, deaf, stun, conf, hallu)
582 * [*] order of status fields is different on tty.
583 * [**] HD is shown instead of level and exp if Upolyd.
585 static void
586 update_fancy_status(wp)
587 struct xwindow *wp;
589 struct X_status_value *sv;
590 long val;
591 int i;
593 if (wp->cursy != 0)
594 return; /* do a complete update when line 0 is done */
596 for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++) {
597 switch (i) {
598 case F_DUMMY:
599 val = 0L;
600 break;
601 case F_STR:
602 val = (long) ACURR(A_STR);
603 break;
604 case F_DEX:
605 val = (long) ACURR(A_DEX);
606 break;
607 case F_CON:
608 val = (long) ACURR(A_CON);
609 break;
610 case F_INT:
611 val = (long) ACURR(A_INT);
612 break;
613 case F_WIS:
614 val = (long) ACURR(A_WIS);
615 break;
616 case F_CHA:
617 val = (long) ACURR(A_CHA);
618 break;
620 * Label stats. With the exceptions of hunger, encumbrance, sick
621 * these are either on or off. Pleae leave the ternary operators
622 * the way they are. I want to specify 0 or 1, not a boolean.
624 case F_HUNGER:
625 val = (long) u.uhs;
626 break;
627 case F_ENCUMBER:
628 val = (long) near_capacity();
629 break;
630 case F_LEV:
631 val = Levitation ? 1L : 0L;
632 break;
633 case F_FLY:
634 val = Flying ? 1L : 0L;
635 break;
636 case F_RIDE:
637 val = u.usteed ? 1L : 0L;
638 break;
639 /* fatal status conditions */
640 case F_STONE:
641 val = Stoned ? 1L : 0L;
642 break;
643 case F_SLIME:
644 val = Slimed ? 1L : 0L;
645 break;
646 case F_STRNGL:
647 val = Strangled ? 1L : 0L;
648 break;
649 case F_FOODPOIS:
650 val = (Sick && (u.usick_type & SICK_VOMITABLE)) ? 1L : 0L;
651 break;
652 case F_TERMILL:
653 val = (Sick && (u.usick_type & SICK_NONVOMITABLE)) ? 1L : 0L;
654 break;
655 /* non-fatal status conditions */
656 case F_BLIND:
657 val = Blind ? 1L : 0L;
658 break;
659 case F_DEAF:
660 val = Deaf ? 1L : 0L;
661 break;
662 case F_STUN:
663 val = Stunned ? 1L : 0L;
664 break;
665 case F_CONF:
666 val = Confusion ? 1L : 0L;
667 break;
668 case F_HALLU:
669 val = Hallucination ? 1L : 0L;
670 break;
672 case F_NAME:
673 val = (long) 0L;
674 break; /* special */
675 case F_DLEVEL:
676 val = (long) 0L;
677 break; /* special */
678 case F_GOLD:
679 val = money_cnt(invent);
680 if (val < 0L)
681 val = 0L; /* ought to issue impossible() and discard gold */
682 break;
683 case F_HP:
684 val = (long) (Upolyd ? (u.mh > 0 ? u.mh : 0)
685 : (u.uhp > 0 ? u.uhp : 0));
686 break;
687 case F_MAXHP:
688 val = (long) (Upolyd ? u.mhmax : u.uhpmax);
689 break;
690 case F_POWER:
691 val = (long) u.uen;
692 break;
693 case F_MAXPOWER:
694 val = (long) u.uenmax;
695 break;
696 case F_AC:
697 val = (long) u.uac;
698 break;
699 case F_LEVEL:
700 val = (long) (Upolyd ? mons[u.umonnum].mlevel : u.ulevel);
701 break;
702 case F_EXP:
703 val = flags.showexp ? u.uexp : 0L;
704 break;
705 case F_ALIGN:
706 val = (long) u.ualign.type;
707 break;
708 case F_TIME:
709 val = flags.time ? (long) moves : 0L;
710 break;
711 case F_SCORE:
712 #ifdef SCORE_ON_BOTL
713 val = flags.showscore ? botl_score() : 0L;
714 #else
715 val = 0L;
716 #endif
717 break;
718 default: {
720 * There is a possible infinite loop that occurs with:
722 * impossible->pline->flush_screen->bot->bot{1,2}->
723 * putstr->adjust_status->update_other->impossible
725 * Break out with this.
727 static boolean active = FALSE;
729 if (!active) {
730 active = TRUE;
731 impossible("update_other: unknown shown value");
732 active = FALSE;
734 val = 0L;
735 break;
736 } /* default */
737 } /* switch */
738 update_val(sv, val);
743 * Turn off hilighted status values after a certain amount of turns.
745 void
746 check_turn_events()
748 int i;
749 struct X_status_value *sv;
751 for (sv = shown_stats, i = 0; i < NUM_STATS; i++, sv++) {
752 if (!sv->set)
753 continue;
755 if (sv->turn_count++ >= hilight_time) {
756 if (sv->type == SV_LABEL)
757 hilight_label(sv->w);
758 else
759 hilight_value(sv->w);
760 sv->set = FALSE;
765 /* Initialize alternate status =============================================
768 /* Return a string for the initial width. */
769 static const char *
770 width_string(sv_index)
771 int sv_index;
773 switch (sv_index) {
774 case F_DUMMY:
775 return " ";
777 case F_STR:
778 return "018/**";
779 case F_DEX:
780 case F_CON:
781 case F_INT:
782 case F_WIS:
783 case F_CHA:
784 return "088"; /* all but str never get bigger */
786 case F_HUNGER:
787 return "Satiated";
788 case F_ENCUMBER:
789 return "Overloaded";
791 case F_LEV:
792 case F_FLY:
793 case F_RIDE:
794 case F_STONE:
795 case F_SLIME:
796 case F_STRNGL:
797 case F_FOODPOIS:
798 case F_TERMILL:
799 case F_BLIND:
800 case F_DEAF:
801 case F_STUN:
802 case F_CONF:
803 case F_HALLU:
804 return shown_stats[sv_index].name;
806 case F_NAME:
807 case F_DLEVEL:
808 return "";
809 case F_HP:
810 case F_MAXHP:
811 return "9999";
812 case F_POWER:
813 case F_MAXPOWER:
814 return "9999";
815 case F_AC:
816 return "-127";
817 case F_LEVEL:
818 return "99";
819 case F_GOLD:
820 /* strongest hero can pick up roughly 30% of this much */
821 return "999999"; /* same limit as tty */
822 case F_EXP:
823 case F_TIME:
824 case F_SCORE:
825 return "123456789"; /* a tenth digit will still fit legibly */
826 case F_ALIGN:
827 return "Neutral";
829 impossible("width_string: unknown index %d\n", sv_index);
830 return "";
833 static void
834 create_widget(parent, sv, sv_index)
835 Widget parent;
836 struct X_status_value *sv;
837 int sv_index;
839 Arg args[4];
840 Cardinal num_args;
842 switch (sv->type) {
843 case SV_VALUE:
844 sv->w = create_value(parent, sv->name);
845 set_value(sv->w, width_string(sv_index));
846 break;
847 case SV_LABEL:
848 /* Labels get their own buffer. */
849 sv->name = (char *) alloc(BUFSZ);
850 /* we need to cast away 'const' when assigning a value */
851 *(char *) (sv->name) = '\0';
853 num_args = 0;
854 XtSetArg(args[num_args], XtNborderWidth, 0);
855 num_args++;
856 XtSetArg(args[num_args], XtNinternalHeight, 0);
857 num_args++;
858 sv->w = XtCreateManagedWidget((sv_index == F_NAME)
859 ? "name"
860 : "dlevel",
861 labelWidgetClass, parent,
862 args, num_args);
863 break;
864 case SV_NAME:
865 num_args = 0;
866 XtSetArg(args[0], XtNlabel, width_string(sv_index)); num_args++;
867 XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
868 XtSetArg(args[num_args], XtNinternalHeight, 0); num_args++;
869 sv->w = XtCreateManagedWidget(sv->name, labelWidgetClass, parent,
870 args, num_args);
871 break;
872 default:
873 panic("create_widget: unknown type %d", sv->type);
878 * Get current width of value. width2p is only valid for SV_VALUE types.
880 static void
881 get_widths(sv, width1p, width2p)
882 struct X_status_value *sv;
883 int *width1p, *width2p;
885 Arg args[1];
886 Dimension width;
888 switch (sv->type) {
889 case SV_VALUE:
890 *width1p = get_name_width(sv->w);
891 *width2p = get_value_width(sv->w);
892 break;
893 case SV_LABEL:
894 case SV_NAME:
895 XtSetArg(args[0], XtNwidth, &width);
896 XtGetValues(sv->w, args, ONE);
897 *width1p = width;
898 *width2p = 0;
899 break;
900 default:
901 panic("get_widths: unknown type %d", sv->type);
905 static void
906 set_widths(sv, width1, width2)
907 struct X_status_value *sv;
908 int width1, width2;
910 Arg args[1];
912 switch (sv->type) {
913 case SV_VALUE:
914 set_name_width(sv->w, width1);
915 set_value_width(sv->w, width2);
916 break;
917 case SV_LABEL:
918 case SV_NAME:
919 XtSetArg(args[0], XtNwidth, (width1 + width2));
920 XtSetValues(sv->w, args, ONE);
921 break;
922 default:
923 panic("set_widths: unknown type %d", sv->type);
927 static Widget
928 init_column(name, parent, top, left, col_indices)
929 const char *name;
930 Widget parent, top, left;
931 int *col_indices;
933 Widget form;
934 Arg args[4];
935 Cardinal num_args;
936 int max_width1, width1, max_width2, width2;
937 int *ip;
938 struct X_status_value *sv;
940 num_args = 0;
941 if (top != (Widget) 0) {
942 XtSetArg(args[num_args], nhStr(XtNfromVert), top);
943 num_args++;
945 if (left != (Widget) 0) {
946 XtSetArg(args[num_args], nhStr(XtNfromHoriz), left);
947 num_args++;
949 XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0);
950 num_args++;
951 form = XtCreateManagedWidget(name, formWidgetClass, parent,
952 args, num_args);
954 max_width1 = max_width2 = 0;
955 for (ip = col_indices; *ip >= 0; ip++) {
956 sv = &shown_stats[*ip];
957 create_widget(form, sv, *ip); /* will set init width */
958 if (ip != col_indices) { /* not first */
959 num_args = 0;
960 XtSetArg(args[num_args], nhStr(XtNfromVert),
961 shown_stats[*(ip - 1)].w);
962 num_args++;
963 XtSetValues(sv->w, args, num_args);
965 get_widths(sv, &width1, &width2);
966 if (width1 > max_width1)
967 max_width1 = width1;
968 if (width2 > max_width2)
969 max_width2 = width2;
971 for (ip = col_indices; *ip >= 0; ip++) {
972 set_widths(&shown_stats[*ip], max_width1, max_width2);
975 /* There is room behind the end marker for the two widths. */
976 *++ip = max_width1;
977 *++ip = max_width2;
979 return form;
983 * These are the orders of the displayed columns. Change to suit. The -1
984 * indicates the end of the column. The two numbers after that are used
985 * to store widths that are calculated at run-time.
987 static int attrib_indices[] = { F_STR, F_DEX, F_CON, F_INT, F_WIS, F_CHA,
988 -1, 0, 0 };
989 /* including F_DUMMY makes the three status condition columns evenly
990 spaced with regard to the adjacent characteristics (Str,Dex,&c) column;
991 we lose track of the Widget pointer for them, each use clobbering the
992 one before, leaving the one from leftover_indices[]; since they're never
993 updated, that shouldn't matter */
994 static int status_indices[3][9] = { { F_STONE, F_SLIME, F_STRNGL,
995 F_FOODPOIS, F_TERMILL, F_DUMMY,
996 -1, 0, 0 },
997 { F_HUNGER, F_ENCUMBER,
998 F_LEV, F_FLY, F_RIDE, F_DUMMY,
999 -1, 0, 0 },
1000 { F_BLIND, F_DEAF, F_STUN,
1001 F_CONF, F_HALLU, F_DUMMY,
1002 -1, 0, 0 } };
1003 /* used to fill up the empty space to right of 3rd status condition column */
1004 static int leftover_indices[] = { F_DUMMY, -1, 0, 0 };
1006 static int col1_indices[] = { F_HP, F_POWER, F_AC, F_LEVEL, F_GOLD,
1007 F_SCORE, -1, 0, 0 };
1008 static int col2_indices[] = { F_MAXHP, F_MAXPOWER, F_ALIGN, F_EXP, F_TIME,
1009 -1, 0, 0 };
1011 * Produce a form that looks like the following:
1013 * name
1014 * dlevel
1015 * col1_indices[0] col2_indices[0]
1016 * col1_indices[1] col2_indices[1]
1017 * . .
1018 * . .
1019 * col1_indices[n] col2_indices[n]
1021 * TODO: increase the space between the two columns.
1023 static Widget
1024 init_info_form(parent, top, left)
1025 Widget parent, top, left;
1027 Widget form, col1;
1028 struct X_status_value *sv_name, *sv_dlevel;
1029 Arg args[6];
1030 Cardinal num_args;
1031 int total_width, *ip;
1033 num_args = 0;
1034 if (top != (Widget) 0) {
1035 XtSetArg(args[num_args], nhStr(XtNfromVert), top);
1036 num_args++;
1038 if (left != (Widget) 0) {
1039 XtSetArg(args[num_args], nhStr(XtNfromHoriz), left);
1040 num_args++;
1042 XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0);
1043 num_args++;
1044 form = XtCreateManagedWidget("status_info", formWidgetClass, parent, args,
1045 num_args);
1047 /* top of form */
1048 sv_name = &shown_stats[F_NAME];
1049 create_widget(form, sv_name, F_NAME);
1051 /* second */
1052 sv_dlevel = &shown_stats[F_DLEVEL];
1053 create_widget(form, sv_dlevel, F_DLEVEL);
1055 num_args = 0;
1056 XtSetArg(args[num_args], nhStr(XtNfromVert), sv_name->w);
1057 num_args++;
1058 XtSetValues(sv_dlevel->w, args, num_args);
1060 /* two columns beneath */
1061 col1 = init_column("name_col1", form, sv_dlevel->w, (Widget) 0,
1062 col1_indices);
1063 (void) init_column("name_col2", form, sv_dlevel->w, col1, col2_indices);
1065 /* Add calculated widths. */
1066 for (ip = col1_indices; *ip >= 0; ip++)
1067 ; /* skip to end */
1068 total_width = *++ip;
1069 total_width += *++ip;
1070 for (ip = col2_indices; *ip >= 0; ip++)
1071 ; /* skip to end */
1072 total_width += *++ip;
1073 total_width += *++ip;
1075 XtSetArg(args[0], XtNwidth, total_width);
1076 XtSetValues(sv_name->w, args, ONE);
1077 XtSetArg(args[0], XtNwidth, total_width);
1078 XtSetValues(sv_dlevel->w, args, ONE);
1080 return form;
1083 /* give the three status condition columns the same width */
1084 static void
1085 fixup_cond_widths()
1087 int pass, i, *ip, w1, w2;
1089 w1 = w2 = 0;
1090 for (pass = 1; pass <= 2; ++pass) { /* two passes... */
1091 for (i = 0; i < 3; i++) { /* three columns */
1092 for (ip = status_indices[i]; *ip != -1; ++ip) { /* X fields */
1093 /* pass 1: find -1; pass 2: update field widths, find -1 */
1094 if (pass == 2)
1095 set_widths(&shown_stats[*ip], w1, w2);
1097 /* found -1; the two slots beyond it contain column widths */
1098 if (pass == 1) { /* pass 1: collect maxima */
1099 if (ip[1] > w1)
1100 w1 = ip[1];
1101 if (ip[2] > w2)
1102 w2 = ip[2];
1103 } else { /* pass 2: update column widths with maxima */
1104 ip[1] = w1;
1105 ip[2] = w2;
1108 /* ascetics: expand the maximum width to make cond columns wider */
1109 if (pass == 1) {
1110 w1 += 20;
1111 if (w2 > 0)
1112 w2 += 20;
1118 * Create the layout for the fancy status. Return a form widget that
1119 * contains everything.
1121 static Widget
1122 create_fancy_status(parent, top)
1123 Widget parent, top;
1125 Widget form; /* The form that surrounds everything. */
1126 Widget w;
1127 Arg args[8];
1128 Cardinal num_args;
1129 char buf[32];
1130 int i;
1132 num_args = 0;
1133 if (top != (Widget) 0) {
1134 XtSetArg(args[num_args], nhStr(XtNfromVert), top);
1135 num_args++;
1137 XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0);
1138 num_args++;
1139 XtSetArg(args[num_args], XtNborderWidth, 0);
1140 num_args++;
1141 XtSetArg(args[num_args], XtNorientation, XtorientHorizontal);
1142 num_args++;
1143 form = XtCreateManagedWidget("fancy_status", panedWidgetClass, parent,
1144 args, num_args);
1146 w = init_info_form(form, (Widget) 0, (Widget) 0);
1147 w = init_column("status_attributes", form, (Widget) 0, w, attrib_indices);
1148 for (i = 0; i < 3; i++) {
1149 Sprintf(buf, "status_condition%d", i + 1);
1150 w = init_column(buf, form, (Widget) 0, w, status_indices[i]);
1152 fixup_cond_widths(); /* make all 3 status_conditionN columns same width */
1153 w = init_column("status_leftover", form, (Widget) 0, w, leftover_indices);
1154 nhUse(w);
1155 return form;
1158 static void
1159 destroy_fancy_status(wp)
1160 struct xwindow *wp;
1162 int i;
1163 struct X_status_value *sv;
1165 if (!wp->keep_window)
1166 XtDestroyWidget(wp->w), wp->w = (Widget) 0;
1168 for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++)
1169 if (sv->type == SV_LABEL) {
1170 free((genericptr_t) sv->name);
1171 sv->name = 0;
1175 /*winstat.c*/