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