1 | /* SCCS Id: @(#)winstat.c 3.3 96/04/05 */
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, 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.
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/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>
26 |
27 | #ifdef PRESERVE_NO_SYSV
28 | # ifdef SYSV
29 | # undef SYSV
30 | # endif
31 | # undef PRESERVE_NO_SYSV
32 | #endif
33 |
34 | #include "hack.h"
35 | #include "winX.h"
36 |
37 | extern const char *hu_stat[]; /* from eat.c */
38 | extern const char *enc_stat[]; /* from botl.c */
39 |
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 *));
43 |
44 | void
45 | create_status_window(wp, create_popup, parent)
46 | struct xwindow *wp; /* window pointer */
47 | boolean create_popup;
48 | Widget parent;
49 | {
50 | XFontStruct *fs;
51 | Arg args[8];
52 | Cardinal num_args;
53 | Position top_margin, bottom_margin, left_margin, right_margin;
54 |
55 | wp->type = NHW_STATUS;
56 |
57 | if (!create_popup) {
58 | /*
59 | * If we are not creating a popup, then we must be the "main" status
60 | * window.
61 | */
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;
67 | }
68 |
69 | wp->status_information =
70 | (struct status_info_t *) alloc(sizeof(struct status_info_t));
71 |
72 | init_text_buffer(&wp->status_information->text);
73 |
74 | num_args = 0;
75 | XtSetArg(args[num_args], XtNallowShellResize, False); num_args++;
76 | XtSetArg(args[num_args], XtNinput, False); num_args++;
77 |
78 | wp->popup = parent = XtCreatePopupShell("status_popup",
79 | topLevelShellWidgetClass,
80 | toplevel, args, num_args);
81 | /*
82 | * If we're here, then this is an auxiliary status window. If we're
83 | * cancelled via a delete window message, we should just pop down.
84 | */
85 |
86 | num_args = 0;
87 | XtSetArg(args[num_args], XtNdisplayCaret, False); num_args++;
88 | XtSetArg(args[num_args], XtNscrollHorizontal,
89 | XawtextScrollWhenNeeded); num_args++;
90 | XtSetArg(args[num_args], XtNscrollVertical,
91 | XawtextScrollWhenNeeded); num_args++;
92 |
93 | wp->w = XtCreateManagedWidget(
94 | "status", /* name */
95 | asciiTextWidgetClass,
96 | parent, /* parent widget */
97 | args, /* set some values */
98 | num_args); /* number of values to set */
99 |
100 | /*
101 | * Adjust the height and width of the message window so that it
102 | * is two lines high and COLNO of the widest characters wide.
103 | */
104 |
105 | /* Get the font and margin information. */
106 | num_args = 0;
107 | XtSetArg(args[num_args], XtNfont, &fs); num_args++;
108 | XtSetArg(args[num_args], XtNtopMargin, &top_margin); num_args++;
109 | XtSetArg(args[num_args], XtNbottomMargin, &bottom_margin); num_args++;
110 | XtSetArg(args[num_args], XtNleftMargin, &left_margin); num_args++;
111 | XtSetArg(args[num_args], XtNrightMargin, &right_margin); num_args++;
112 | XtGetValues(wp->w, args, num_args);
113 |
114 | /* font height is ascent + descent */
115 | wp->pixel_height = 2 * (fs->ascent + fs->descent) +
116 | top_margin + bottom_margin;
117 | wp->pixel_width = COLNO * fs->max_bounds.width +
118 | left_margin + right_margin;
119 |
120 | /* Set the new width and height. */
121 | num_args = 0;
122 | XtSetArg(args[num_args], XtNwidth, wp->pixel_width); num_args++;
123 | XtSetArg(args[num_args], XtNheight, wp->pixel_height); num_args++;
124 | XtSetValues(wp->w, args, num_args);
125 | }
126 |
127 | void
128 | destroy_status_window(wp)
129 | struct xwindow *wp;
130 | {
131 | /* If status_information is defined, then it a "text" status window. */
132 | if (wp->status_information) {
133 | if (wp->popup) {
134 | nh_XtPopdown(wp->popup);
135 | if (!wp->keep_window)
136 | XtDestroyWidget(wp->popup), wp->popup = (Widget)0;
137 | }
138 | free((genericptr_t)wp->status_information);
139 | wp->status_information = 0;
140 | } else {
141 | destroy_fancy_status(wp);
142 | }
143 | if (!wp->keep_window)
144 | wp->type = NHW_NONE;
145 | }
146 |
147 |
148 | /*
149 | * This assumes several things:
150 | * + Status has only 2 lines
151 | * + That both lines are updated in succession in line order.
152 | * + We didn't set stringInPlace on the widget.
153 | */
154 | void
155 | adjust_status(wp, str)
156 | struct xwindow *wp;
157 | const char *str;
158 | {
159 | Arg args[2];
160 | Cardinal num_args;
161 |
162 | if (!wp->status_information) {
163 | update_fancy_status(wp);
164 | return;
165 | }
166 |
167 | if (wp->cursy == 0) {
168 | clear_text_buffer(&wp->status_information->text);
169 | append_text_buffer(&wp->status_information->text, str, FALSE);
170 | return;
171 | }
172 | append_text_buffer(&wp->status_information->text, str, FALSE);
173 |
174 | /* Set new buffer as text. */
175 | num_args = 0;
176 | XtSetArg(args[num_args], XtNstring, wp->status_information->text.text);
177 | num_args++;
178 | XtSetValues(wp->w, args, num_args);
179 | }
180 |
181 |
182 | /* Fancy Status -------------------------------------------------------------*/
183 | static int hilight_time = 1; /* number of turns to hilight a changed value */
184 |
185 | struct X_status_value {
186 | char *name; /* text name */
187 | int type; /* status type */
188 | Widget w; /* widget of name/value pair */
189 | long last_value; /* value displayed */
190 | int turn_count; /* last time the value changed */
191 | boolean set; /* if hilighed */
192 | boolean after_init; /* don't hilight on first change (init) */
193 | };
194 |
195 | /* valid type values */
196 | #define SV_VALUE 0 /* displays a label:value pair */
197 | #define SV_LABEL 1 /* displays a changable label */
198 | #define SV_NAME 2 /* displays an unchangeable name */
199 |
200 | static void FDECL(hilight_label, (Widget));
201 | static void FDECL(update_val, (struct X_status_value *,long));
202 | static const char *FDECL(width_string, (int));
203 | static void FDECL(create_widget, (Widget,struct X_status_value *,int));
204 | static void FDECL(get_widths, (struct X_status_value *,int *,int *));
205 | static void FDECL(set_widths, (struct X_status_value *,int,int));
206 | static Widget FDECL(init_column, (char *,Widget,Widget,Widget,int *));
207 | static Widget FDECL(init_info_form, (Widget,Widget,Widget));
208 |
209 | /*
210 | * Form entry storage indices.
211 | */
212 | #define F_STR 0
213 | #define F_DEX 1
214 | #define F_CON 2
215 | #define F_INT 3
216 | #define F_WIS 4
217 | #define F_CHA 5
218 |
219 | #define F_NAME 6
220 | #define F_DLEVEL 7
221 | #define F_GOLD 8
222 | #define F_HP 9
223 | #define F_MAXHP 10
224 | #define F_POWER 11
225 | #define F_MAXPOWER 12
226 | #define F_AC 13
227 | #define F_LEVEL 14
228 | #define F_EXP 15
229 | #define F_ALIGN 16
230 | #define F_TIME 17
231 | #define F_SCORE 18
232 |
233 | #define F_HUNGER 19
234 | #define F_CONFUSED 20
235 | #define F_SICK 21
236 | #define F_BLIND 22
237 | #define F_STUNNED 23
238 | #define F_HALLU 24
239 | #define F_ENCUMBER 25
240 |
241 | #define NUM_STATS 26
242 |
243 | /*
244 | * Notes:
245 | * + Alignment needs a different init value, because -1 is an alignment.
246 | * + Armor Class is an schar, so 256 is out of range.
247 | * + Blank value is 0 and should never change.
248 | */
249 | static struct X_status_value shown_stats[NUM_STATS] = {
250 | { "Strength", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /* 0*/
251 | { "Dexterity", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
252 | { "Constitution", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
253 | { "Intelligence", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
254 | { "Wisdom", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
255 | { "Charisma", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /* 5*/
256 |
257 | { "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* name */
258 | { "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* dlvl */
259 | { "Gold", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
260 | { "Hit Points", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
261 | { "Max HP", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /*10*/
262 | { "Power", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
263 | { "Max Power", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
264 | { "Armor Class", SV_VALUE, (Widget) 0,256, 0, FALSE, FALSE },
265 | { "Level", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
266 | { "Experience", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /*15*/
267 | { "Alignment", SV_VALUE, (Widget) 0, -2, 0, FALSE, FALSE },
268 | { "Time", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
269 | { "Score", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
270 |
271 | { "", SV_NAME, (Widget) 0, -1, 0, FALSE, TRUE }, /* hunger*/
272 | { "Confused", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*20*/
273 | { "", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /* sick */
274 | { "Blind", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
275 | { "Stunned", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
276 | { "Hallucinating", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
277 | { "", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*encumbr*/
278 | };
279 |
280 |
281 | /*
282 | * Set all widget values to a null string. This is used after all spacings
283 | * have been calculated so that when the window is popped up we don't get all
284 | * kinds of funny values being displayed.
285 | */
286 | void
287 | null_out_status()
288 | {
289 | int i;
290 | struct X_status_value *sv;
291 | Arg args[1];
292 |
293 | for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++) {
294 | switch (sv->type) {
295 | case SV_VALUE:
296 | set_value(sv->w, "");
297 | break;
298 |
299 | case SV_LABEL:
300 | case SV_NAME:
301 | XtSetArg(args[0], XtNlabel, "");
302 | XtSetValues(sv->w, args, ONE);
303 | break;
304 |
305 | default:
306 | impossible("null_out_status: unknown type %d\n", sv->type);
307 | break;
308 | }
309 | }
310 | }
311 |
312 | /* This is almost an exact duplicate of hilight_value() */
313 | static void
314 | hilight_label(w)
315 | Widget w; /* label widget */
316 | {
317 | Arg args[2];
318 | Pixel fg, bg;
319 |
320 | XtSetArg(args[0], XtNforeground, &fg);
321 | XtSetArg(args[1], XtNbackground, &bg);
322 | XtGetValues(w, args, TWO);
323 |
324 | XtSetArg(args[0], XtNforeground, bg);
325 | XtSetArg(args[1], XtNbackground, fg);
326 | XtSetValues(w, args, TWO);
327 | }
328 |
329 |
330 | static void
331 | update_val(attr_rec, new_value)
332 | struct X_status_value *attr_rec;
333 | long new_value;
334 | {
335 | char buf[BUFSZ];
336 | Arg args[4];
337 |
338 | if (attr_rec->type == SV_LABEL) {
339 |
340 | if (attr_rec == &shown_stats[F_NAME]) {
341 |
342 | Strcpy(buf, plname);
343 | if ('a' <= buf[0] && buf[0] <= 'z') buf[0] += 'A'-'a';
344 | Strcat(buf, " the ");
345 | if (u.mtimedone) {
346 | char mname[BUFSZ];
347 | int k = 0;
348 |
349 | Strcpy(mname, mons[u.umonnum].mname);
350 | while(mname[k] != 0) {
351 | if ((k == 0 || (k > 0 && mname[k-1] == ' ')) &&
352 | 'a' <= mname[k] && mname[k] <= 'z')
353 | mname[k] += 'A' - 'a';
354 | k++;
355 | }
356 | Strcat(buf, mname);
357 | } else
358 | Strcat(buf, rank_of(u.ulevel, pl_character[0], flags.female));
359 |
360 | } else if (attr_rec == &shown_stats[F_DLEVEL]) {
361 | if (!describe_level(buf)) {
362 | Strcpy(buf, dungeons[u.uz.dnum].dname);
363 | Sprintf(eos(buf), ", level %d", depth(&u.uz));
364 | }
365 | } else {
366 | impossible("update_val: unknown label type \"%s\"",
367 | attr_rec->name);
368 | return;
369 | }
370 |
371 | if (strcmp(buf, attr_rec->name) == 0) return; /* same */
372 |
373 | /* Set the label. */
374 | Strcpy(attr_rec->name, buf);
375 | XtSetArg(args[0], XtNlabel, buf);
376 | XtSetValues(attr_rec->w, args, ONE);
377 |
378 | } else if (attr_rec->type == SV_NAME) {
379 |
380 | if (attr_rec->last_value == new_value) return; /* no change */
381 |
382 | attr_rec->last_value = new_value;
383 |
384 | /* special cases: hunger, encumbrance, sickness */
385 | if (attr_rec == &shown_stats[F_HUNGER]) {
386 | XtSetArg(args[0], XtNlabel, hu_stat[new_value]);
387 | } else if (attr_rec == &shown_stats[F_ENCUMBER]) {
388 | XtSetArg(args[0], XtNlabel, enc_stat[new_value]);
389 | } else if (attr_rec == &shown_stats[F_SICK]) {
390 | buf[0] = 0;
391 | if (Sick) {
392 | if (u.usick_type & SICK_VOMITABLE)
393 | Strcat(buf, "FoodPois");
394 | if (u.usick_type & SICK_NONVOMITABLE) {
395 | if (u.usick_type & SICK_VOMITABLE)
396 | Strcat(buf, " ");
397 | Strcat(buf, "Ill");
398 | }
399 | }
400 | XtSetArg(args[0], XtNlabel, buf);
401 | } else if (new_value) {
402 | XtSetArg(args[0], XtNlabel, attr_rec->name);
403 | } else {
404 | XtSetArg(args[0], XtNlabel, "");
405 | }
406 | XtSetValues(attr_rec->w, args, ONE);
407 |
408 | } else { /* a value pair */
409 | boolean force_update = FALSE;
410 |
411 | /* special case: time can be enabled & disabled */
412 | if (attr_rec == &shown_stats[F_TIME]) {
413 | static boolean flagtime = TRUE;
414 |
415 | if(flags.time && !flagtime) {
416 | set_name(attr_rec->w, shown_stats[F_TIME].name);
417 | force_update = TRUE;
418 | flagtime = flags.time;
419 | } else if(!flags.time && flagtime) {
420 | set_name(attr_rec->w, "");
421 | set_value(attr_rec->w, "");
422 | flagtime = flags.time;
423 | }
424 | if(!flagtime) return;
425 | }
426 |
427 | /* special case: exp can be enabled & disabled */
428 | else if (attr_rec == &shown_stats[F_EXP]) {
429 | static boolean flagexp = TRUE;
430 | #ifdef EXP_ON_BOTL
431 |
432 | if (flags.showexp && !flagexp) {
433 | set_name(attr_rec->w, shown_stats[F_EXP].name);
434 | force_update = TRUE;
435 | flagexp = flags.showexp;
436 | } else if(!flags.showexp && flagexp) {
437 | set_name(attr_rec->w, "");
438 | set_value(attr_rec->w, "");
439 | flagexp = flags.showexp;
440 | }
441 | if (!flagexp) return;
442 | #else
443 | if (flagexp) {
444 | set_name(attr_rec->w, "");
445 | set_value(attr_rec->w, "");
446 | flagexp = FALSE;
447 | }
448 | return; /* don't show it at all */
449 | #endif
450 | }
451 |
452 | /* special case: score can be enabled & disabled */
453 | else if (attr_rec == &shown_stats[F_SCORE]) {
454 | static boolean flagscore = TRUE;
455 | #ifdef SCORE_ON_BOTL
456 |
457 | if(flags.showscore && !flagscore) {
458 | set_name(attr_rec->w, shown_stats[F_SCORE].name);
459 | force_update = TRUE;
460 | flagscore = flags.showscore;
461 | } else if(!flags.showscore && flagscore) {
462 | set_name(attr_rec->w, "");
463 | set_value(attr_rec->w, "");
464 | flagscore = flags.showscore;
465 | }
466 | if(!flagscore) return;
467 | #else
468 | if (flagscore) {
469 | set_name(attr_rec->w, "");
470 | set_value(attr_rec->w, "");
471 | flagscore = FALSE;
472 | }
473 | return;
474 | #endif
475 | }
476 |
477 | /* special case: when polymorphed, show "HD", disable exp */
478 | else if (attr_rec == &shown_stats[F_LEVEL]) {
479 | static boolean lev_was_poly = FALSE;
480 |
481 | if (u.mtimedone && !lev_was_poly) {
482 | force_update = TRUE;
483 | set_name(attr_rec->w, "HD");
484 | lev_was_poly = TRUE;
485 | } else if (!u.mtimedone && lev_was_poly) {
486 | force_update = TRUE;
487 | set_name(attr_rec->w, shown_stats[F_LEVEL].name);
488 | lev_was_poly = FALSE;
489 | }
490 | } else if (attr_rec == &shown_stats[F_EXP]) {
491 | static boolean exp_was_poly = FALSE;
492 |
493 | if (u.mtimedone && !exp_was_poly) {
494 | force_update = TRUE;
495 | set_name(attr_rec->w, "");
496 | set_value(attr_rec->w, "");
497 | exp_was_poly = TRUE;
498 | } else if (!u.mtimedone && exp_was_poly) {
499 | force_update = TRUE;
500 | set_name(attr_rec->w, shown_stats[F_EXP].name);
501 | exp_was_poly = FALSE;
502 | }
503 | if (u.mtimedone) return; /* no display for exp when poly */
504 | }
505 |
506 | if (attr_rec->last_value == new_value && !force_update) /* same */
507 | return;
508 |
509 | attr_rec->last_value = new_value;
510 |
511 | /* Special cases: strength, alignment and "clear". */
512 | if (attr_rec == &shown_stats[F_STR]) {
513 | if(new_value > 18) {
514 | if (new_value > 118)
515 | Sprintf(buf,"%ld", new_value-100);
516 | else if(new_value < 118)
517 | Sprintf(buf, "18/%02ld", new_value-18);
518 | else
519 | Strcpy(buf, "18/**");
520 | } else {
521 | Sprintf(buf, "%ld", new_value);
522 | }
523 | } else if (attr_rec == &shown_stats[F_ALIGN]) {
524 |
525 | Strcpy(buf, (new_value == A_CHAOTIC) ? "Chaotic" :
526 | (new_value == A_NEUTRAL) ? "Neutral" :
527 | "Lawful" );
528 | } else {
529 | Sprintf(buf, "%ld", new_value);
530 | }
531 | set_value(attr_rec->w, buf);
532 | }
533 |
534 | /*
535 | * Now hilight the changed information. Names, time and score don't
536 | * hilight. If first time, don't hilight. If already lit, don't do
537 | * it again.
538 | */
539 | if (attr_rec->type != SV_NAME && attr_rec != &shown_stats[F_TIME]) {
540 | if (attr_rec->after_init) {
541 | if(!attr_rec->set) {
542 | if (attr_rec->type == SV_LABEL)
543 | hilight_label(attr_rec->w);
544 | else
545 | hilight_value(attr_rec->w);
546 | attr_rec->set = TRUE;
547 | }
548 | attr_rec->turn_count = 0;
549 | } else {
550 | attr_rec->after_init = TRUE;
551 | }
552 | }
553 | }
554 |
555 | /*
556 | * Update the displayed status. The current code in botl.c updates
557 | * two lines of information. Both lines are always updated one after
558 | * the other. So only do our update when we update the second line.
559 | *
560 | * Information on the first line:
561 | * name, attributes, alignment, score
562 | *
563 | * Information on the second line:
564 | * dlvl, gold, hp, power, ac, {level & exp or HD **}
565 | * status (hunger, conf, halu, stun, sick, blind), time, encumbrance
566 | *
567 | * [**] HD is shown instead of level and exp if mtimedone is non-zero.
568 | */
569 | static void
570 | update_fancy_status(wp)
571 | struct xwindow *wp;
572 | {
573 | struct X_status_value *sv;
574 | long val;
575 | int i;
576 |
577 | if (wp->cursy != 0) return; /* do a complete update when line 0 is done */
578 |
579 | for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++) {
580 | switch (i) {
581 | case F_STR: val = (long) ACURR(A_STR); break;
582 | case F_DEX: val = (long) ACURR(A_DEX); break;
583 | case F_CON: val = (long) ACURR(A_CON); break;
584 | case F_INT: val = (long) ACURR(A_INT); break;
585 | case F_WIS: val = (long) ACURR(A_WIS); break;
586 | case F_CHA: val = (long) ACURR(A_CHA); break;
587 | /*
588 | * Label stats. With the exceptions of hunger, encumbrance, sick
589 | * these are either on or off. Pleae leave the ternary operators
590 | * the way they are. I want to specify 0 or 1, not a boolean.
591 | */
592 | case F_HUNGER: val = (long) u.uhs; break;
593 | case F_CONFUSED: val = (long) Confusion ? 1L : 0L; break;
594 | case F_SICK: val = (long) Sick ? (long)u.usick_type
595 | : 0L; break;
596 | case F_BLIND: val = (long) Blind ? 1L : 0L; break;
597 | case F_STUNNED: val = (long) Stunned ? 1L : 0L; break;
598 | case F_HALLU: val = (long) Hallucination ? 1L : 0L; break;
599 | case F_ENCUMBER: val = (long) near_capacity(); break;
600 |
601 | case F_NAME: val = (long) 0L; break; /* special */
602 | case F_DLEVEL: val = (long) 0L; break; /* special */
603 | case F_GOLD: val = (long) u.ugold; break;
604 | case F_HP: val = (long) (u.mtimedone ?
605 | (u.mh > 0 ? u.mh : 0):
606 | (u.uhp > 0 ? u.uhp : 0)); break;
607 | case F_MAXHP: val = (long) (u.mtimedone ? u.mhmax :
608 | u.uhpmax); break;
609 | case F_POWER: val = (long) u.uen; break;
610 | case F_MAXPOWER: val = (long) u.uenmax; break;
611 | case F_AC: val = (long) u.uac; break;
612 | case F_LEVEL: val = (long) (u.mtimedone ?
613 | mons[u.umonnum].mlevel :
614 | u.ulevel); break;
615 | #ifdef EXP_ON_BOTL
616 | case F_EXP: val = flags.showexp ? u.uexp : 0L; break;
617 | #else
618 | case F_EXP: val = 0L; break;
619 | #endif
620 | case F_ALIGN: val = (long) u.ualign.type; break;
621 | case F_TIME: val = flags.time ? (long) moves : 0L; break;
622 | #ifdef SCORE_ON_BOTL
623 | case F_SCORE: val = flags.showscore ? botl_score():0L; break;
624 | #else
625 | case F_SCORE: val = 0L; break;
626 | #endif
627 | default:
628 | {
629 | /*
630 | * There is a possible infinite loop that occurs with:
631 | *
632 | * impossible->pline->flush_screen->bot->bot{1,2}->
633 | * putstr->adjust_status->update_other->impossible
634 | *
635 | * Break out with this.
636 | */
637 | static boolean active = FALSE;
638 | if (!active) {
639 | active = TRUE;
640 | impossible("update_other: unknown shown value");
641 | active = FALSE;
642 | }
643 | val = 0;
644 | break;
645 | }
646 | }
647 | update_val(sv, val);
648 | }
649 | }
650 |
651 | /*
652 | * Turn off hilighted status values after a certain amount of turns.
653 | */
654 | void
655 | check_turn_events()
656 | {
657 | int i;
658 | struct X_status_value *sv;
659 |
660 | for (sv = shown_stats, i = 0; i < NUM_STATS; i++, sv++) {
661 | if (!sv->set) continue;
662 |
663 | if (sv->turn_count++ >= hilight_time) {
664 | if (sv->type == SV_LABEL)
665 | hilight_label(sv->w);
666 | else
667 | hilight_value(sv->w);
668 | sv->set = FALSE;
669 | }
670 | }
671 | }
672 |
673 | /* Initialize alternate status ============================================= */
674 |
675 | /* Return a string for the initial width. */
676 | static const char *
677 | width_string(sv_index)
678 | int sv_index;
679 | {
680 | switch (sv_index) {
681 | case F_STR: return "018/**";
682 | case F_DEX:
683 | case F_CON:
684 | case F_INT:
685 | case F_WIS:
686 | case F_CHA: return "088"; /* all but str never get bigger */
687 |
688 | case F_HUNGER: return shown_stats[F_HUNGER].name;
689 | case F_CONFUSED:return shown_stats[F_CONFUSED].name;
690 | case F_SICK: return shown_stats[F_SICK].name;
691 | case F_BLIND: return shown_stats[F_BLIND].name;
692 | case F_STUNNED: return shown_stats[F_STUNNED].name;
693 | case F_HALLU: return shown_stats[F_HALLU].name;
694 | case F_ENCUMBER:return shown_stats[F_ENCUMBER].name;
695 |
696 | case F_NAME:
697 | case F_DLEVEL: return "";
698 | case F_HP:
699 | case F_MAXHP: return "9999";
700 | case F_POWER:
701 | case F_MAXPOWER:return "999";
702 | case F_AC: return "-99";
703 | case F_LEVEL: return "99";
704 | case F_GOLD:
705 | case F_EXP: return "4294967295"; /* max ulong */
706 | case F_ALIGN: return "Neutral";
707 | case F_TIME: return "4294967295"; /* max ulong */
708 | case F_SCORE: return "4294967295"; /* max ulong */
709 | }
710 | impossible("width_string: unknown index %d\n", sv_index);
711 | return "";
712 | }
713 |
714 | static void
715 | create_widget(parent, sv, sv_index)
716 | Widget parent;
717 | struct X_status_value *sv;
718 | int sv_index;
719 | {
720 | Arg args[4];
721 | Cardinal num_args;
722 |
723 | switch (sv->type) {
724 | case SV_VALUE:
725 | sv->w = create_value(parent, sv->name);
726 | set_value(sv->w, width_string(sv_index));
727 | break;
728 | case SV_LABEL:
729 | /* Labels get their own buffer. */
730 | sv->name = (char *) alloc(BUFSZ);
731 | sv->name[0] = '\0';
732 |
733 | num_args = 0;
734 | XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
735 | XtSetArg(args[num_args], XtNinternalHeight, 0); num_args++;
736 | sv->w = XtCreateManagedWidget(
737 | sv_index == F_NAME ? "name" : "dlevel",
738 | labelWidgetClass,
739 | parent,
740 | args, num_args);
741 | break;
742 | case SV_NAME:
743 | num_args = 0;
744 | XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
745 | XtSetArg(args[num_args], XtNinternalHeight, 0); num_args++;
746 | sv->w = XtCreateManagedWidget(sv->name,
747 | labelWidgetClass,
748 | parent,
749 | args, num_args);
750 | break;
751 | default:
752 | panic("create_widget: unknown type %d", sv->type);
753 | }
754 | }
755 |
756 | /*
757 | * Get current width of value. width2p is only valid for SV_LABEL types.
758 | */
759 | static void
760 | get_widths(sv, width1p, width2p)
761 | struct X_status_value *sv;
762 | int *width1p, *width2p;
763 | {
764 | Arg args[1];
765 | Dimension width;
766 |
767 | switch (sv->type) {
768 | case SV_VALUE:
769 | *width1p = get_name_width(sv->w);
770 | *width2p = get_value_width(sv->w);
771 | break;
772 | case SV_LABEL:
773 | case SV_NAME:
774 | XtSetArg(args[0], XtNwidth, &width);
775 | XtGetValues(sv->w, args, ONE);
776 | *width1p = width;
777 | *width2p = 0;
778 | break;
779 | default:
780 | panic("get_widths: unknown type %d", sv->type);
781 | }
782 | }
783 |
784 | static void
785 | set_widths(sv, width1, width2)
786 | struct X_status_value *sv;
787 | int width1, width2;
788 | {
789 | Arg args[1];
790 |
791 | switch (sv->type) {
792 | case SV_VALUE:
793 | set_name_width(sv->w, width1);
794 | set_value_width(sv->w, width2);
795 | break;
796 | case SV_LABEL:
797 | case SV_NAME:
798 | XtSetArg(args[0], XtNwidth, (width1+width2));
799 | XtSetValues(sv->w, args, ONE);
800 | break;
801 | default:
802 | panic("set_widths: unknown type %d", sv->type);
803 | }
804 | }
805 |
806 | static Widget
807 | init_column(name, parent, top, left, col_indices)
808 | char *name;
809 | Widget parent, top, left;
810 | int *col_indices;
811 | {
812 | Widget form;
813 | Arg args[4];
814 | Cardinal num_args;
815 | int max_width1, width1, max_width2, width2;
816 | int *ip;
817 | struct X_status_value *sv;
818 |
819 | num_args = 0;
820 | if (top != (Widget) 0) {
821 | XtSetArg(args[num_args], XtNfromVert, top); num_args++;
822 | }
823 | if (left != (Widget) 0) {
824 | XtSetArg(args[num_args], XtNfromHoriz, left); num_args++;
825 | }
826 | XtSetArg(args[num_args], XtNdefaultDistance, 0); num_args++;
827 | form = XtCreateManagedWidget(name,
828 | formWidgetClass,
829 | parent, args, num_args);
830 |
831 | max_width1 = max_width2 = 0;
832 | for (ip = col_indices; *ip >= 0; ip++) {
833 | sv = &shown_stats[*ip];
834 | create_widget(form, sv, *ip); /* will set init width */
835 | if (ip != col_indices) { /* not first */
836 | num_args = 0;
837 | XtSetArg(args[num_args], XtNfromVert, shown_stats[*(ip-1)].w);
838 | num_args++;
839 | XtSetValues(sv->w, args, num_args);
840 | }
841 | get_widths(sv, &width1, &width2);
842 | if (width1 > max_width1) max_width1 = width1;
843 | if (width2 > max_width2) max_width2 = width2;
844 | }
845 | for (ip = col_indices; *ip >= 0 ; ip++) {
846 | set_widths(&shown_stats[*ip], max_width1, max_width2);
847 | }
848 |
849 | /* There is room behind the end marker for the two widths. */
850 | *++ip = max_width1;
851 | *++ip = max_width2;
852 |
853 | return form;
854 | }
855 |
856 | /*
857 | * These are the orders of the displayed columns. Change to suit. The -1
858 | * indicates the end of the column. The two numbers after that are used
859 | * to store widths that are calculated at run-time.
860 | */
861 | static int attrib_indices[] = { F_STR,F_DEX,F_CON,F_INT,F_WIS,F_CHA, -1,0,0 };
862 | static int status_indices[] = { F_HUNGER, F_CONFUSED, F_SICK, F_BLIND,
863 | F_STUNNED, F_HALLU, F_ENCUMBER, -1,0,0 };
864 |
865 | static int col2_indices[] = { F_MAXHP, F_ALIGN, F_TIME, F_EXP,
866 | F_MAXPOWER, -1,0,0 };
867 | static int col1_indices[] = { F_HP, F_AC, F_GOLD, F_LEVEL,
868 | F_POWER, F_SCORE, -1,0,0 };
869 |
870 |
871 | /*
872 | * Produce a form that looks like the following:
873 | *
874 | * name
875 | * dlevel
876 | * col1_indices[0] col2_indices[0]
877 | * col1_indices[1] col2_indices[1]
878 | * . .
879 | * . .
880 | * col1_indices[n] col2_indices[n]
881 | */
882 | static Widget
883 | init_info_form(parent, top, left)
884 | Widget parent, top, left;
885 | {
886 | Widget form, col1;
887 | struct X_status_value *sv_name, *sv_dlevel;
888 | Arg args[6];
889 | Cardinal num_args;
890 | int total_width, *ip;
891 |
892 | num_args = 0;
893 | if (top != (Widget) 0) {
894 | XtSetArg(args[num_args], XtNfromVert, top); num_args++;
895 | }
896 | if (left != (Widget) 0) {
897 | XtSetArg(args[num_args], XtNfromHoriz, left); num_args++;
898 | }
899 | XtSetArg(args[num_args], XtNdefaultDistance, 0); num_args++;
900 | form = XtCreateManagedWidget("status_info",
901 | formWidgetClass,
902 | parent,
903 | args, num_args);
904 |
905 | /* top of form */
906 | sv_name = &shown_stats[F_NAME];
907 | create_widget(form, sv_name, F_NAME);
908 |
909 | /* second */
910 | sv_dlevel = &shown_stats[F_DLEVEL];
911 | create_widget(form, sv_dlevel, F_DLEVEL);
912 |
913 | num_args = 0;
914 | XtSetArg(args[num_args], XtNfromVert, sv_name->w); num_args++;
915 | XtSetValues(sv_dlevel->w, args, num_args);
916 |
917 | /* two columns beneath */
918 | col1 = init_column("name_col1", form, sv_dlevel->w,
919 | (Widget) 0, col1_indices);
920 | (void) init_column("name_col2", form, sv_dlevel->w,
921 | col1, col2_indices);
922 |
923 | /* Add calculated widths. */
924 | for (ip = col1_indices; *ip >= 0; ip++)
925 | ; /* skip to end */
926 | total_width = *++ip;
927 | total_width += *++ip;
928 | for (ip = col2_indices; *ip >= 0; ip++)
929 | ; /* skip to end */
930 | total_width += *++ip;
931 | total_width += *++ip;
932 |
933 | XtSetArg(args[0], XtNwidth, total_width);
934 | XtSetValues(sv_name->w, args, ONE);
935 | XtSetArg(args[0], XtNwidth, total_width);
936 | XtSetValues(sv_dlevel->w, args, ONE);
937 |
938 | return form;
939 | }
940 |
941 | /*
942 | * Create the layout for the fancy status. Return a form widget that
943 | * contains everything.
944 | */
945 | static Widget
946 | create_fancy_status(parent, top)
947 | Widget parent, top;
948 | {
949 | Widget form; /* The form that surrounds everything. */
950 | Widget w;
951 | Arg args[8];
952 | Cardinal num_args;
953 |
954 | num_args = 0;
955 | if (top != (Widget) 0) {
956 | XtSetArg(args[num_args], XtNfromVert, top); num_args++;
957 | }
958 | XtSetArg(args[num_args], XtNdefaultDistance, 0); num_args++;
959 | XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
960 | XtSetArg(args[num_args], XtNorientation, XtorientHorizontal); num_args++;
961 | form = XtCreateManagedWidget("fancy_status",
962 | panedWidgetClass,
963 | parent,
964 | args, num_args);
965 |
966 | w = init_info_form(form, (Widget) 0, (Widget) 0);
967 | w = init_column("status_attributes",form, (Widget) 0, w, attrib_indices);
968 | (void) init_column("status_condition", form, (Widget) 0, w, status_indices);
969 | return form;
970 | }
971 |
972 | static void
973 | destroy_fancy_status(wp)
974 | struct xwindow *wp;
975 | {
976 | int i;
977 | struct X_status_value *sv;
978 |
979 | if (!wp->keep_window)
980 | XtDestroyWidget(wp->w), wp->w = (Widget)0;
981 |
982 | for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++)
983 | if (sv->type == SV_LABEL) {
984 | free((genericptr_t)sv->name);
985 | sv->name = 0;
986 | }
987 | }
988 |
989 | /*winstat.c*/