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*/