OSDN Git Service

update year to 2022
[jnethack/source.git] / win / X11 / winmisc.c
1 /* NetHack 3.6  winmisc.c       $NHDT-Date: 1554135506 2019/04/01 16:18:26 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.44 $ */
2 /* Copyright (c) Dean Luick, 1992                                 */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /* JNetHack Copyright */
6 /* (c) Issei Numata 1994-1999                                      */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2022            */
8 /* JNetHack may be freely redistributed.  See license for details. */
9
10 /*
11  * Misc. popup windows: player selection and extended commands.
12  *
13  *      + Global functions: player_selection() and get_ext_cmd().
14  */
15
16 #ifndef SYSV
17 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
18 #endif
19
20 #include <X11/Intrinsic.h>
21 #include <X11/StringDefs.h>
22 #include <X11/Shell.h>
23 #include <X11/Xaw/Command.h>
24 #include <X11/Xaw/Form.h>
25 #include <X11/Xaw/Label.h>
26 #include <X11/Xaw/AsciiText.h>
27 #include <X11/Xaw/Scrollbar.h>
28 #include <X11/Xaw/Toggle.h>
29 #include <X11/Xaw/Viewport.h>
30 #include <X11/Xaw/Cardinals.h>
31 #include <X11/Xaw/List.h>
32 #include <X11/Xos.h> /* for index() */
33 #include <X11/Xatom.h>
34
35 #ifdef PRESERVE_NO_SYSV
36 #ifdef SYSV
37 #undef SYSV
38 #endif
39 #undef PRESERVE_NO_SYSV
40 #endif
41
42 #include "hack.h"
43 #include "func_tab.h"
44 #include "winX.h"
45
46 static Widget extended_command_popup = 0;
47 static Widget extended_command_form;
48 static Widget *extended_commands = 0;
49 static const char **command_list;
50 static short *command_indx;
51 static int extended_cmd_selected; /* index of the selected command; */
52 static int ps_selected;               /* index of selected role */
53 #define PS_RANDOM (-50)
54 #define PS_QUIT (-75)
55 /* 'r' for random won't work for role but will for race, gender, alignment */
56 static const char ps_randchars[] = "*@\n\rrR";
57 static const char ps_quitchars[] = "\033qQ";
58
59 #define EC_NCHARS 32
60 static boolean ec_full_list = FALSE;
61 static boolean ec_active = FALSE;
62 static int ec_nchars = 0;
63 static char ec_chars[EC_NCHARS];
64 static Time ec_time;
65
66 boolean plsel_ask_name;
67
68 static const char plsel_dialog_translations[] = "#override\n\
69      <Key>Escape: plsel_quit()\n\
70      <Key>Return: plsel_play()";
71
72 static const char plsel_input_accelerators[] = "#override\n\
73      <Key>Escape: plsel_quit()\n\
74      <Key>Return: plsel_play()\n\
75      <Key>Tab: \n";
76
77 static const char extended_command_translations[] = "#override\n\
78      <Key>Left: scroll(4)\n\
79      <Key>Right: scroll(6)\n\
80      <Key>Up: scroll(8)\n\
81      <Key>Down: scroll(2)\n\
82      <Btn4Down>: scroll(8)\n\
83      <Btn5Down>: scroll(2)\n\
84      <Key>: ec_key()";
85
86 static const char player_select_translations[] = "#override\n\
87      <Key>: ps_key()";
88 static const char race_select_translations[] = "#override\n\
89      <Key>: race_key()";
90 static const char gend_select_translations[] = "#override\n\
91      <Key>: gend_key()";
92 static const char algn_select_translations[] = "#override\n\
93      <Key>: algn_key()";
94
95 static const char popup_entry_translations[] = "#override\n\
96      <Btn4Down>: scroll(8)\n\
97      <Btn5Down>: scroll(2)";
98
99 static void FDECL(ps_quit, (Widget, XtPointer, XtPointer));
100 static void FDECL(ps_random, (Widget, XtPointer, XtPointer));
101 static void FDECL(ps_select, (Widget, XtPointer, XtPointer));
102 static void FDECL(extend_select, (Widget, XtPointer, XtPointer));
103 static void FDECL(extend_dismiss, (Widget, XtPointer, XtPointer));
104 static void FDECL(extend_help, (Widget, XtPointer, XtPointer));
105 static void FDECL(popup_delete, (Widget, XEvent *, String *, Cardinal *));
106 static void NDECL(ec_dismiss);
107 static void FDECL(ec_scroll_to_view, (int));
108 static void NDECL(init_extended_commands_popup);
109 static Widget FDECL(make_menu, (const char *, const char *, const char *,
110                                 const char *, XtCallbackProc, const char *,
111                                 XtCallbackProc, int, const char **,
112                                 Widget **, XtCallbackProc, Widget *));
113
114 void NDECL(X11_player_selection_setupOthers);
115 void NDECL(X11_player_selection_randomize);
116
117 /* Bad Hack alert. Using integers instead of XtPointers */
118 XtPointer
119 i2xtp(i)
120 int i;
121 {
122     return (XtPointer) (ptrdiff_t) i;
123 }
124
125 int
126 xtp2i(x)
127 XtPointer x;
128 {
129     return (int) (ptrdiff_t) x;
130 }
131
132 /* Player Selection ------------------------------------------------------- */
133 /* ARGSUSED */
134 static void
135 ps_quit(w, client_data, call_data)
136 Widget w;
137 XtPointer client_data, call_data;
138 {
139     nhUse(w);
140     nhUse(client_data);
141     nhUse(call_data);
142
143     ps_selected = PS_QUIT;
144     exit_x_event = TRUE; /* leave event loop */
145 }
146
147 /* ARGSUSED */
148 static void
149 ps_random(w, client_data, call_data)
150 Widget w;
151 XtPointer client_data, call_data;
152 {
153     nhUse(w);
154     nhUse(client_data);
155     nhUse(call_data);
156
157     ps_selected = PS_RANDOM;
158     exit_x_event = TRUE; /* leave event loop */
159 }
160
161 /* ARGSUSED */
162 static void
163 ps_select(w, client_data, call_data)
164 Widget w;
165 XtPointer client_data, call_data;
166 {
167     nhUse(w);
168     nhUse(call_data);
169
170     ps_selected = (int) (ptrdiff_t) client_data;
171     exit_x_event = TRUE; /* leave event loop */
172 }
173
174 /* ARGSUSED */
175 void
176 ps_key(w, event, params, num_params)
177 Widget w;
178 XEvent *event;
179 String *params;
180 Cardinal *num_params;
181 {
182     char ch, *mark;
183     char rolechars[QBUFSZ];
184     int i;
185
186     nhUse(w);
187     nhUse(params);
188     nhUse(num_params);
189
190     (void) memset(rolechars, '\0', sizeof rolechars); /* for index() */
191     for (i = 0; roles[i].name.m; ++i) {
192         ch = lowc(*roles[i].name.m);
193         /* if (flags.female && roles[i].name.f) ch = lowc(*roles[i].name.f);
194          */
195         /* this supports at most two roles with the same first letter */
196         if (index(rolechars, ch))
197             ch = highc(ch);
198         rolechars[i] = ch;
199     }
200     ch = key_event_to_char((XKeyEvent *) event);
201     if (ch == '\0') { /* don't accept nul char/modifier event */
202         /* don't beep */
203         return;
204     }
205     mark = index(rolechars, ch);
206     if (!mark)
207         mark = index(rolechars, lowc(ch));
208     if (!mark)
209         mark = index(rolechars, highc(ch));
210     if (!mark) {
211         if (index(ps_randchars, ch))
212             ps_selected = PS_RANDOM;
213         else if (index(ps_quitchars, ch))
214             ps_selected = PS_QUIT;
215         else {
216             X11_nhbell(); /* no such class */
217             return;
218         }
219     } else
220         ps_selected = (int) (mark - rolechars);
221     exit_x_event = TRUE;
222 }
223
224 /* ARGSUSED */
225 void
226 race_key(w, event, params, num_params)
227 Widget w;
228 XEvent *event;
229 String *params;
230 Cardinal *num_params;
231 {
232     char ch, *mark;
233     char racechars[QBUFSZ];
234     int i;
235
236     nhUse(w);
237     nhUse(params);
238     nhUse(num_params);
239
240     (void) memset(racechars, '\0', sizeof racechars); /* for index() */
241     for (i = 0; races[i].noun; ++i) {
242         ch = lowc(*races[i].noun);
243         /* this supports at most two races with the same first letter */
244         if (index(racechars, ch))
245             ch = highc(ch);
246         racechars[i] = ch;
247     }
248     ch = key_event_to_char((XKeyEvent *) event);
249     if (ch == '\0') { /* don't accept nul char/modifier event */
250         /* don't beep */
251         return;
252     }
253     mark = index(racechars, ch);
254     if (!mark)
255         mark = index(racechars, lowc(ch));
256     if (!mark)
257         mark = index(racechars, highc(ch));
258     if (!mark) {
259         if (index(ps_randchars, ch))
260             ps_selected = PS_RANDOM;
261         else if (index(ps_quitchars, ch))
262             ps_selected = PS_QUIT;
263         else {
264             X11_nhbell(); /* no such race */
265             return;
266         }
267     } else
268         ps_selected = (int) (mark - racechars);
269     exit_x_event = TRUE;
270 }
271
272 /* ARGSUSED */
273 void
274 gend_key(w, event, params, num_params)
275 Widget w;
276 XEvent *event;
277 String *params;
278 Cardinal *num_params;
279 {
280     char ch, *mark;
281     static char gendchars[] = "mf";
282
283     nhUse(w);
284     nhUse(params);
285     nhUse(num_params);
286
287     ch = key_event_to_char((XKeyEvent *) event);
288     if (ch == '\0') { /* don't accept nul char/modifier event */
289         /* don't beep */
290         return;
291     }
292     mark = index(gendchars, ch);
293     if (!mark)
294         mark = index(gendchars, lowc(ch));
295     if (!mark) {
296         if (index(ps_randchars, ch))
297             ps_selected = PS_RANDOM;
298         else if (index(ps_quitchars, ch))
299             ps_selected = PS_QUIT;
300         else {
301             X11_nhbell(); /* no such gender */
302             return;
303         }
304     } else
305         ps_selected = (int) (mark - gendchars);
306     exit_x_event = TRUE;
307 }
308
309 /* ARGSUSED */
310 void
311 algn_key(w, event, params, num_params)
312 Widget w;
313 XEvent *event;
314 String *params;
315 Cardinal *num_params;
316 {
317     char ch, *mark;
318     static char algnchars[] = "LNC";
319
320     nhUse(w);
321     nhUse(params);
322     nhUse(num_params);
323
324     ch = key_event_to_char((XKeyEvent *) event);
325     if (ch == '\0') { /* don't accept nul char/modifier event */
326         /* don't beep */
327         return;
328     }
329     mark = index(algnchars, ch);
330     if (!mark)
331         mark = index(algnchars, highc(ch));
332     if (!mark) {
333         if (index(ps_randchars, ch))
334             ps_selected = PS_RANDOM;
335         else if (index(ps_quitchars, ch))
336             ps_selected = PS_QUIT;
337         else {
338             X11_nhbell(); /* no such alignment */
339             return;
340         }
341     } else
342         ps_selected = (int) (mark - algnchars);
343     exit_x_event = TRUE;
344 }
345
346 int plsel_n_races, plsel_n_roles;
347 Widget *plsel_race_radios = (Widget *) 0;
348 Widget *plsel_role_radios = (Widget *) 0;
349 Widget *plsel_gend_radios = (Widget *) 0;
350 Widget *plsel_align_radios = (Widget *) 0;
351
352 Widget plsel_name_input;
353
354 Widget plsel_btn_play;
355
356 void
357 plsel_dialog_acceptvalues()
358 {
359     Arg args[2];
360     String s;
361
362     flags.initrace = xtp2i(XawToggleGetCurrent(plsel_race_radios[0])) - 1;
363     flags.initrole = xtp2i(XawToggleGetCurrent(plsel_role_radios[0])) - 1;
364     flags.initgend = xtp2i(XawToggleGetCurrent(plsel_gend_radios[0])) - 1;
365     flags.initalign = xtp2i(XawToggleGetCurrent(plsel_align_radios[0])) - 1;
366
367     XtSetArg(args[0], nhStr(XtNstring), &s);
368     XtGetValues(plsel_name_input, args, ONE);
369
370     (void) strncpy(plname, (char *) s, sizeof plname - 1);
371     plname[sizeof plname - 1] = '\0';
372     (void) mungspaces(plname);
373     if (strlen(plname) < 1)
374         (void) strcpy(plname, "Mumbles");
375     iflags.renameinprogress = FALSE;
376 }
377
378 /* ARGSUSED */
379 void
380 plsel_quit(w, event, params, num_params)
381 Widget w;
382 XEvent *event;
383 String *params;
384 Cardinal *num_params;
385 {
386     nhUse(w);
387     nhUse(event);
388     nhUse(params);
389     nhUse(num_params);
390
391     ps_selected = PS_QUIT;
392     exit_x_event = TRUE; /* leave event loop */
393 }
394
395 /* ARGSUSED */
396 void
397 plsel_play(w, event, params, num_params)
398 Widget w;
399 XEvent *event;
400 String *params;
401 Cardinal *num_params;
402 {
403     Arg args[2];
404     Boolean state;
405
406     nhUse(w);
407     nhUse(event);
408     nhUse(params);
409     nhUse(num_params);
410
411     XtSetArg(args[0], nhStr(XtNsensitive), &state);
412     XtGetValues(plsel_btn_play, args, ONE);
413
414     if (state) {
415         plsel_dialog_acceptvalues();
416         exit_x_event = TRUE; /* leave event loop */
417     } else {
418         X11_nhbell();
419     }
420 }
421
422 /* ARGSUSED */
423 void
424 plsel_randomize(w, event, params, num_params)
425 Widget w;
426 XEvent *event;
427 String *params;
428 Cardinal *num_params;
429 {
430     nhUse(w);
431     nhUse(event);
432     nhUse(params);
433     nhUse(num_params);
434
435     X11_player_selection_randomize();
436 }
437
438 /* enable or disable the Play button */
439 void
440 plsel_set_play_button(state)
441 boolean state;
442 {
443     Arg args[2];
444
445     XtSetArg(args[0], nhStr(XtNsensitive), !state);
446     XtSetValues(plsel_btn_play, args, ONE);
447 }
448
449 void
450 plsel_set_sensitivities(setcurr)
451 boolean setcurr;
452 {
453     Arg args[2];
454     int j, valid;
455     int c = 0;
456     int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0]))-1;
457     int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0]))-1;
458
459     plsel_set_play_button(ra < 0 || ro < 0);
460
461     if (ra < 0 || ro < 0)
462         return;
463
464     valid = -1;
465
466     for (j = 0; roles[j].name.m; j++) {
467         boolean v = validrace(j, ra);
468
469         if (j == ro)
470             c = j;
471         XtSetArg(args[0], nhStr(XtNsensitive), v);
472         XtSetValues(plsel_role_radios[j], args, ONE);
473         if (valid < 0 && v)
474             valid = j;
475     }
476     if (!validrace(ro, c))
477         c = valid;
478
479     if (setcurr)
480         XawToggleSetCurrent(plsel_role_radios[0], i2xtp(c + 1));
481
482     valid = -1;
483
484     for (j = 0; races[j].noun; j++) {
485         boolean v = validrace(ro, j);
486
487         if (j == ra)
488             c = j;
489         XtSetArg(args[0], nhStr(XtNsensitive), v);
490         XtSetValues(plsel_race_radios[j], args, ONE);
491         if (valid < 0 && v)
492             valid = j;
493     }
494     if (!validrace(ro, c))
495         c = valid;
496
497     if (setcurr)
498         XawToggleSetCurrent(plsel_race_radios[0], i2xtp(c + 1));
499
500     X11_player_selection_setupOthers();
501 }
502
503 void
504 X11_player_selection_randomize()
505 {
506     int nrole = plsel_n_roles;
507     int nrace = plsel_n_races;
508     int ro, ra, a, g;
509     boolean fully_specified_role, choose_race_first;
510     boolean picksomething = (flags.initrole == ROLE_NONE
511                              || flags.initrace == ROLE_NONE
512                              || flags.initgend == ROLE_NONE
513                              || flags.initalign == ROLE_NONE);
514
515     if (flags.randomall && picksomething) {
516         if (flags.initrole == ROLE_NONE)
517             flags.initrole = ROLE_RANDOM;
518         if (flags.initrace == ROLE_NONE)
519             flags.initrace = ROLE_RANDOM;
520         if (flags.initgend == ROLE_NONE)
521             flags.initgend = ROLE_RANDOM;
522         if (flags.initalign == ROLE_NONE)
523             flags.initalign = ROLE_RANDOM;
524     }
525
526     rigid_role_checks();
527
528     /* Randomize race and role, unless specified in config */
529     ro = flags.initrole;
530     if (ro == ROLE_NONE || ro == ROLE_RANDOM) {
531         ro = rn2(nrole);
532         if (flags.initrole != ROLE_RANDOM) {
533             fully_specified_role = FALSE;
534         }
535     }
536     ra = flags.initrace;
537     if (ra == ROLE_NONE || ra == ROLE_RANDOM) {
538         ra = rn2(nrace);
539         if (flags.initrace != ROLE_RANDOM) {
540             fully_specified_role = FALSE;
541         }
542     }
543
544     /* make sure we have a valid combination, honoring
545        the users request if possible. */
546     choose_race_first = FALSE;
547     if (flags.initrace >= 0 && flags.initrole < 0) {
548         choose_race_first = TRUE;
549     }
550
551     while (!validrace(ro,ra)) {
552         if (choose_race_first) {
553             ro = rn2(nrole);
554             if (flags.initrole != ROLE_RANDOM) {
555                 fully_specified_role = FALSE;
556             }
557         } else {
558             ra = rn2(nrace);
559             if (flags.initrace != ROLE_RANDOM) {
560                 fully_specified_role = FALSE;
561             }
562         }
563     }
564
565     g = flags.initgend;
566     if (g == ROLE_NONE) {
567         g = rn2(ROLE_GENDERS);
568         fully_specified_role = FALSE;
569     }
570     while (!validgend(ro,ra,g)) {
571         g = rn2(ROLE_GENDERS);
572     }
573
574     a = flags.initalign;
575     if (a == ROLE_NONE) {
576         a = rn2(ROLE_ALIGNS);
577         fully_specified_role = FALSE;
578     }
579     while (!validalign(ro,ra,a)) {
580         a = rn2(ROLE_ALIGNS);
581     }
582
583     XawToggleSetCurrent(plsel_gend_radios[0], i2xtp(g + 1));
584     XawToggleSetCurrent(plsel_align_radios[0], i2xtp(a + 1));
585     XawToggleSetCurrent(plsel_race_radios[0], i2xtp(ra + 1));
586     XawToggleSetCurrent(plsel_role_radios[0], i2xtp(ro + 1));
587     plsel_set_sensitivities(FALSE);
588 }
589
590 void
591 X11_player_selection_setupOthers()
592 {
593     Arg args[2];
594     int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0])) - 1;
595     int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0])) - 1;
596     int valid = -1, c = 0, j;
597     int gchecked = xtp2i(XawToggleGetCurrent(plsel_gend_radios[0])) - 1;
598     int achecked = xtp2i(XawToggleGetCurrent(plsel_align_radios[0])) - 1;
599
600     if (ro < 0 || ra < 0)
601         return;
602
603     for (j = 0; j < ROLE_GENDERS; j++) {
604         boolean v = validgend(ro, ra, j);
605
606         if (j == gchecked)
607             c = j;
608         XtSetArg(args[0], nhStr(XtNsensitive), v);
609         XtSetValues(plsel_gend_radios[j], args, ONE);
610         if (valid < 0 && v)
611             valid = j;
612     }
613     if (!validgend(ro, ra, c))
614         c = valid;
615
616     XawToggleSetCurrent(plsel_gend_radios[0], i2xtp(c + 1));
617
618     valid = -1;
619
620     for (j = 0; j < ROLE_ALIGNS; j++) {
621         boolean v = validalign(ro, ra, j);
622
623         if (j == achecked)
624             c = j;
625         XtSetArg(args[0], nhStr(XtNsensitive), v);
626         XtSetValues(plsel_align_radios[j], args, ONE);
627         if (valid < 0 && v)
628             valid = j;
629     }
630     if (!validalign(ro, ra, c))
631         c = valid;
632
633     XawToggleSetCurrent(plsel_align_radios[0], i2xtp(c + 1));
634 }
635
636 static void
637 racetoggleCallback(w, client, call)
638 Widget w;
639 XtPointer client, call;
640 {
641     Arg args[2];
642     int j, valid;
643     int c = 0;
644     int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0])) - 1;
645     int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0])) - 1;
646
647     nhUse(w);
648     nhUse(client);
649     nhUse(call);
650
651     plsel_set_play_button(ra < 0 || ro < 0);
652
653     if (ra < 0 || ro < 0)
654         return;
655
656     valid = -1;
657
658     for (j = 0; roles[j].name.m; j++) {
659         boolean v = validrace(j, ra);
660
661         if (j == ro)
662             c = j;
663         XtSetArg(args[0], nhStr(XtNsensitive), v);
664         XtSetValues(plsel_role_radios[j], args, ONE);
665         if (valid < 0 && v)
666             valid = j;
667     }
668     if (!validrace(c, ra))
669         c = valid;
670
671     j = c + 1;
672     XawToggleSetCurrent(plsel_role_radios[0], i2xtp(j));
673
674     X11_player_selection_setupOthers();
675 }
676
677 static void
678 roletoggleCallback(w, client, call)
679 Widget w;
680 XtPointer client, call;
681 {
682     Arg args[2];
683     int j, valid;
684     int c = 0;
685     int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0])) - 1;
686     int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0])) - 1;
687
688     nhUse(w);
689     nhUse(client);
690     nhUse(call);
691
692     plsel_set_play_button(ra < 0 || ro < 0);
693
694     if (ra < 0 || ro < 0)
695         return;
696
697     valid = -1;
698
699     for (j = 0; races[j].noun; j++) {
700         boolean v = validrace(ro, j);
701
702         if (j == ra)
703             c = j;
704         XtSetArg(args[0], nhStr(XtNsensitive), v);
705         XtSetValues(plsel_race_radios[j], args, ONE);
706         if (valid < 0 && v)
707             valid = j;
708     }
709     if (!validrace(ro, c))
710         c = valid;
711
712     j = c + 1;
713     XawToggleSetCurrent(plsel_race_radios[0], i2xtp(j));
714
715     X11_player_selection_setupOthers();
716 }
717
718 static void
719 gendertoggleCallback(w, client, call)
720 Widget w;
721 XtPointer client, call;
722 {
723     int i, r = xtp2i(XawToggleGetCurrent(plsel_gend_radios[0])) - 1;
724
725     nhUse(w);
726     nhUse(client);
727     nhUse(call);
728
729     plsel_set_play_button(r < 0);
730
731     for (i = 0; roles[i].name.m; i++) {
732         if (roles[i].name.f) {
733             Arg args[2];
734
735             XtSetArg(args[0], XtNlabel,
736                      (r < 1) ? roles[i].name.m : roles[i].name.f);
737             XtSetValues(plsel_role_radios[i], args, ONE);
738         }
739     }
740 }
741
742 static void
743 aligntoggleCallback(w, client, call)
744 Widget w;
745 XtPointer client, call;
746 {
747     int r = xtp2i(XawToggleGetCurrent(plsel_align_radios[0])) - 1;
748
749     nhUse(w);
750     nhUse(client);
751     nhUse(call);
752
753     plsel_set_play_button(r < 0);
754 }
755
756 static void
757 plsel_random_btn_callback(w, client, call)
758 Widget w;
759 XtPointer client;
760 XtPointer call;
761 {
762     nhUse(w);
763     nhUse(client);
764     nhUse(call);
765
766     X11_player_selection_randomize();
767 }
768
769 static void
770 plsel_play_btn_callback(w, client, call)
771 Widget w;
772 XtPointer client;
773 XtPointer call;
774 {
775     nhUse(w);
776     nhUse(client);
777     nhUse(call);
778
779     plsel_dialog_acceptvalues();
780     exit_x_event = TRUE; /* leave event loop */
781 }
782
783 static void
784 plsel_quit_btn_callback(w, client, call)
785 Widget w;
786 XtPointer client;
787 XtPointer call;
788 {
789     nhUse(w);
790     nhUse(client);
791     nhUse(call);
792
793     ps_selected = PS_QUIT;
794     exit_x_event = TRUE; /* leave event loop */
795 }
796
797 Widget
798 X11_create_player_selection_name(form)
799 Widget form;
800 {
801     Widget namelabel, name_vp, name_form;
802     Arg args[10];
803     Cardinal num_args;
804
805     /* name viewport */
806     num_args = 0;
807     XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
808     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
809     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
810     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
811     XtSetArg(args[num_args], XtNallowVert, False); num_args++;
812     XtSetArg(args[num_args], XtNallowHoriz, False); num_args++;
813     name_vp = XtCreateManagedWidget("name_vp", viewportWidgetClass, form,
814                                     args, num_args);
815
816     num_args = 0;
817     XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
818     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
819     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
820     name_form = XtCreateManagedWidget("name_form", formWidgetClass, name_vp,
821                                       args, num_args);
822
823     num_args = 0;
824     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
825     XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
826 /*JP
827     XtSetArg(args[num_args], nhStr(XtNlabel), "Name"); num_args++;
828 */
829     XtSetArg(args[num_args], nhStr(XtNlabel), "\96¼\91O"); num_args++;
830     XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
831
832     namelabel = XtCreateManagedWidget("name_label",
833                                       labelWidgetClass, name_form,
834                                       args, num_args);
835
836     num_args = 0;
837     XtSetArg(args[num_args], nhStr(XtNfromVert), namelabel); num_args++;
838     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
839     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
840     XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
841     XtSetArg(args[num_args], nhStr(XtNeditType),
842              !plsel_ask_name ? XawtextRead : XawtextEdit); num_args++;
843     XtSetArg(args[num_args], nhStr(XtNresize), XawtextResizeWidth); num_args++;
844     XtSetArg(args[num_args], nhStr(XtNstring), plname); num_args++;
845     XtSetArg(args[num_args], XtNinsertPosition, strlen(plname)); num_args++;
846     XtSetArg(args[num_args], nhStr(XtNaccelerators),
847              XtParseAcceleratorTable(plsel_input_accelerators)); num_args++;
848     plsel_name_input = XtCreateManagedWidget("name_input",
849                                              asciiTextWidgetClass, name_form,
850                                              args, num_args);
851
852     XtInstallAccelerators(plsel_name_input, plsel_name_input);
853     if (plsel_ask_name) {
854         XtSetKeyboardFocus(form, plsel_name_input);
855     } else {
856         XtSetArg(args[0], nhStr(XtNdisplayCaret), False);
857         XtSetValues(plsel_name_input, args, ONE);
858     }
859
860     return name_vp;
861 }
862
863 void
864 X11_player_selection_dialog()
865 {
866     Widget popup, popup_vp;
867     Widget form;
868     Widget name_vp;
869     Widget racelabel, race_form, race_vp, race_form2;
870     Widget rolelabel, role_form, role_vp, role_form2;
871     Widget gendlabel, gend_form,
872            gend_radio_m, gend_radio_f, gend_vp, gend_form2;
873     Widget alignlabel, align_form,
874            align_radio_l, align_radio_n, align_radio_c, align_vp, align_form2;
875     Widget btn_vp, btn_form, random_btn, play_btn, quit_btn;
876     Widget tmpwidget;
877     Arg args[10];
878     Cardinal num_args;
879     int i;
880     int winwid = 400;
881     int cwid = (winwid / 3) - 14;
882
883     num_args = 0;
884     XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
885 /*JP
886     XtSetArg(args[num_args], XtNtitle, "Player Selection"); num_args++;
887 */
888     XtSetArg(args[num_args], XtNtitle, "\83v\83\8c\83C\83\84\81[\91I\91ð"); num_args++;
889     popup = XtCreatePopupShell("player_selection_dialog",
890                                transientShellWidgetClass,
891                                toplevel, args, num_args);
892
893     num_args = 0;
894     XtSetArg(args[num_args], XtNtranslations,
895              XtParseTranslationTable(plsel_dialog_translations)); num_args++;
896     popup_vp = XtCreateManagedWidget("plsel_vp", viewportWidgetClass,
897                                      popup, args, num_args);
898
899     num_args = 0;
900     XtSetArg(args[num_args], XtNtranslations,
901              XtParseTranslationTable(plsel_dialog_translations)); num_args++;
902     form = XtCreateManagedWidget("plsel_form", formWidgetClass, popup_vp,
903                                  args, num_args);
904
905     name_vp = X11_create_player_selection_name(form);
906
907     num_args = 0;
908     XtSetArg(args[num_args], XtNwidth, winwid); num_args++;
909     XtSetValues(name_vp, args, num_args);
910
911     /*
912      * Layout; role is centered rather than first:
913      *
914      *   +------------------------------------+
915      *   | name                               |
916      *   +------------------------------------+
917      *   +--------+   +------------+   +---------+
918      *   | human  |   |archeologist|   |  male   |
919      *   |  elf   |   | barbarian  |   | female  |
920      *   | dwarf  |   |  caveman   |   +---------+
921      *   | gnome  |   |   healer   |   +---------+
922      *   |  orc   |   |   knight   |   | lawful  |
923      *   +--------+   |    monk    |   | neutral |
924      *                |   priest   |   | chaotic |
925      *                |   rogue    |   +---------+
926      *                |   ranger   |   +--------+
927      *                |  samurai   |   + Random +
928      *                |  tourist   |   +  Play  +
929      *                |  valkyrie  |   +  Quit  +
930      *                |   wizard   |   +--------+
931      *                +------------+
932      *
933      * TODO:
934      *  make name box same width as race+gap+role+gap+gender/alignment
935      *  (resize it after the other boxes have been placed);
936      *  make Random/Play/Quit buttons same width as gender/alignment and
937      *  align bottom of them with bottom of role (they already specify
938      *  the same width for the label text but different decorations--
939      *  room for radio button box--of the other widgets results in the
940      *  total width being different);
941      *  add 'random' to each of the four boxes and Choose to the Random/
942      *  Play/Quit buttons; if none of the four 'random's are currently
943      *  selected, gray-out Choose; conversely, when Choose or Play is
944      *  clicked on, make the random assignments for any/all of the four
945      *  boxes which have 'random' selected.
946      *  Maybe:  move gender box underneath race, bottom aligned with role
947      *  and move alignment up to where gender currently is.  If that's
948      *  done, move role column first and race+gender to middle.
949      */
950
951     /********************************************/
952
953     /* Race */
954
955     num_args = 0;
956     XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++;
957     race_form = XtCreateManagedWidget("race_form", formWidgetClass, form,
958                                       args, num_args);
959
960     /* race label */
961     num_args = 0;
962     XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
963 /*JP
964     XtSetArg(args[num_args], nhStr(XtNlabel), "Race"); num_args++;
965 */
966     XtSetArg(args[num_args], nhStr(XtNlabel), "\8eí\91°"); num_args++;
967     racelabel = XtCreateManagedWidget("race_label",
968                                       labelWidgetClass, race_form,
969                                       args, num_args);
970
971     num_args = 0;
972     XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++;
973     XtSetArg(args[num_args], nhStr(XtNfromVert), racelabel); num_args++;
974     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
975     XtSetArg(args[num_args], XtNallowVert, True); num_args++;
976     XtSetArg(args[num_args], XtNallowHoriz, False); num_args++;
977     race_vp = XtCreateManagedWidget("race_vp", viewportWidgetClass, race_form,
978                                     args, num_args);
979
980     num_args = 0;
981     race_form2 = XtCreateManagedWidget("race_form2", formWidgetClass, race_vp,
982                                       args, num_args);
983
984     for (i = 0; races[i].noun; i++)
985         continue;
986     plsel_n_races = i;
987
988     plsel_race_radios = (Widget *) alloc(sizeof (Widget) * plsel_n_races);
989
990     /* race radio buttons */
991     for (i = 0; races[i].noun; i++) {
992         Widget racewidget;
993
994         num_args = 0;
995         if (i > 0)
996             XtSetArg(args[num_args], nhStr(XtNfromVert),
997                      tmpwidget); num_args++;
998         XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
999         if (i > 0)
1000             XtSetArg(args[num_args], nhStr(XtNradioGroup),
1001                      plsel_race_radios[0]); num_args++;
1002         XtSetArg(args[num_args], nhStr(XtNradioData), (i + 1)); num_args++;
1003
1004         racewidget = XtCreateManagedWidget(races[i].noun,
1005                                            toggleWidgetClass,
1006                                            race_form2,
1007                                            args, num_args);
1008         XtAddCallback(racewidget, XtNcallback, racetoggleCallback, i2xtp(i));
1009         tmpwidget = racewidget;
1010         plsel_race_radios[i] = racewidget;
1011     }
1012
1013     XawToggleUnsetCurrent(plsel_race_radios[0]);
1014
1015     /********************************************/
1016
1017     /* Role */
1018     num_args = 0;
1019     XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++;
1020     XtSetArg(args[num_args], nhStr(XtNfromHoriz), race_form); num_args++;
1021     role_form = XtCreateManagedWidget("role_form", formWidgetClass, form,
1022                                       args, num_args);
1023
1024     /* role label */
1025     num_args = 0;
1026     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1027     XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
1028     XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
1029 /*JP
1030     XtSetArg(args[num_args], nhStr(XtNlabel), "Role"); num_args++;
1031 */
1032     XtSetArg(args[num_args], nhStr(XtNlabel), "\90E\8bÆ"); num_args++;
1033     rolelabel = XtCreateManagedWidget("role_label", labelWidgetClass,
1034                                       role_form, args, num_args);
1035
1036     num_args = 0;
1037     XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++;
1038     XtSetArg(args[num_args], nhStr(XtNfromVert), rolelabel); num_args++;
1039     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
1040     XtSetArg(args[num_args], XtNallowVert, True); num_args++;
1041     XtSetArg(args[num_args], XtNallowHoriz, False); num_args++;
1042     role_vp = XtCreateManagedWidget("role_vp", viewportWidgetClass, role_form,
1043                                     args, num_args);
1044
1045     num_args = 0;
1046     role_form2 = XtCreateManagedWidget("role_form2", formWidgetClass, role_vp,
1047                                       args, num_args);
1048
1049     for (i = 0; roles[i].name.m; i++)
1050         continue;
1051     plsel_n_roles = i;
1052
1053     plsel_role_radios = (Widget *) alloc(sizeof (Widget) * plsel_n_roles);
1054
1055     /* role radio buttons */
1056     for (i = 0; roles[i].name.m; i++) {
1057         Widget rolewidget;
1058
1059         num_args = 0;
1060         if (i > 0)
1061             XtSetArg(args[num_args], nhStr(XtNfromVert),
1062                      tmpwidget); num_args++;
1063         XtSetArg(args[num_args], nhStr(XtNwidth), cwid); num_args++;
1064         if (i > 0)
1065             XtSetArg(args[num_args], nhStr(XtNradioGroup),
1066                      plsel_role_radios[0]); num_args++;
1067         XtSetArg(args[num_args], nhStr(XtNradioData), (i + 1)); num_args++;
1068
1069         rolewidget = XtCreateManagedWidget(roles[i].name.m, toggleWidgetClass,
1070                                            role_form2, args, num_args);
1071         XtAddCallback(rolewidget, XtNcallback, roletoggleCallback, i2xtp(i));
1072         tmpwidget = rolewidget;
1073         plsel_role_radios[i] = rolewidget;
1074     }
1075     XawToggleUnsetCurrent(plsel_role_radios[0]);
1076
1077     /********************************************/
1078
1079     /* Gender*/
1080
1081     plsel_gend_radios = (Widget *) alloc(sizeof (Widget) * ROLE_GENDERS);
1082
1083     num_args = 0;
1084     XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++;
1085     XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++;
1086     gend_form = XtCreateManagedWidget("gender_form", formWidgetClass, form,
1087                                       args, num_args);
1088
1089     /* gender label */
1090     num_args = 0;
1091     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1092     XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
1093     XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
1094 /*JP
1095     XtSetArg(args[num_args], nhStr(XtNlabel), "Gender"); num_args++;
1096 */
1097     XtSetArg(args[num_args], nhStr(XtNlabel), "\90«\95Ê"); num_args++;
1098     gendlabel = XtCreateManagedWidget("gender_label", labelWidgetClass,
1099                                       gend_form, args, num_args);
1100
1101     num_args = 0;
1102     XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++;
1103     XtSetArg(args[num_args], nhStr(XtNfromVert), gendlabel); num_args++;
1104     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
1105     XtSetArg(args[num_args], XtNallowVert, True); num_args++;
1106     XtSetArg(args[num_args], XtNallowHoriz, False); num_args++;
1107     gend_vp = XtCreateManagedWidget("gender_vp", viewportWidgetClass,
1108                                     gend_form, args, num_args);
1109
1110     num_args = 0;
1111     gend_form2 = XtCreateManagedWidget("gender_form2", formWidgetClass,
1112                                        gend_vp, args, num_args);
1113
1114     /* gender radio buttons */
1115     num_args = 0;
1116     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1117     XtSetArg(args[num_args], nhStr(XtNradioData), 1); num_args++;
1118     plsel_gend_radios[0] = gend_radio_m
1119 /*JP
1120         =  XtCreateManagedWidget("Male", toggleWidgetClass,
1121 */
1122         =  XtCreateManagedWidget("\92j\90«", toggleWidgetClass,
1123                                  gend_form2, args, num_args);
1124     num_args = 0;
1125     XtSetArg(args[num_args], nhStr(XtNfromVert), gend_radio_m); num_args++;
1126     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1127     XtSetArg(args[num_args], nhStr(XtNradioGroup),
1128              plsel_gend_radios[0]); num_args++;
1129     XtSetArg(args[num_args], nhStr(XtNradioData), 2); num_args++;
1130     plsel_gend_radios[1] = gend_radio_f
1131 /*JP
1132         =  XtCreateManagedWidget("Female", toggleWidgetClass,
1133 */
1134         =  XtCreateManagedWidget("\8f\97\90«", toggleWidgetClass,
1135                                  gend_form2, args, num_args);
1136
1137     XawToggleUnsetCurrent(plsel_gend_radios[0]);
1138
1139     XtAddCallback(gend_radio_m, XtNcallback,
1140                   gendertoggleCallback, (XtPointer) (1));
1141     XtAddCallback(gend_radio_f, XtNcallback,
1142                   gendertoggleCallback, (XtPointer) (2));
1143
1144     /********************************************/
1145
1146     /* Alignment */
1147
1148     plsel_align_radios = (Widget *) alloc(sizeof (Widget) * ROLE_ALIGNS);
1149
1150     num_args = 0;
1151     XtSetArg(args[num_args], nhStr(XtNfromVert), gend_form); num_args++;
1152     XtSetArg(args[num_args], nhStr(XtNvertDistance), 30); num_args++;
1153     XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++;
1154     align_form = XtCreateManagedWidget("align_form", formWidgetClass, form,
1155                                        args, num_args);
1156
1157     /* align label */
1158     num_args = 0;
1159     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1160     XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
1161     XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
1162 /*JP
1163     XtSetArg(args[num_args], nhStr(XtNlabel), "Alignment"); num_args++;
1164 */
1165     XtSetArg(args[num_args], nhStr(XtNlabel), "\91®\90«"); num_args++;
1166     alignlabel = XtCreateManagedWidget("align_label", labelWidgetClass,
1167                                        align_form, args, num_args);
1168
1169     num_args = 0;
1170     XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++;
1171     XtSetArg(args[num_args], nhStr(XtNfromVert), alignlabel); num_args++;
1172     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
1173     XtSetArg(args[num_args], XtNallowVert, True); num_args++;
1174     XtSetArg(args[num_args], XtNallowHoriz, False); num_args++;
1175     align_vp = XtCreateManagedWidget("align_vp", viewportWidgetClass,
1176                                      align_form, args, num_args);
1177
1178     num_args = 0;
1179     align_form2 = XtCreateManagedWidget("align_form2", formWidgetClass,
1180                                         align_vp, args, num_args);
1181
1182     num_args = 0;
1183     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1184     XtSetArg(args[num_args], nhStr(XtNradioData), 1); num_args++;
1185     plsel_align_radios[0] = align_radio_l
1186 /*JP
1187         =  XtCreateManagedWidget("Lawful", toggleWidgetClass,
1188 */
1189         =  XtCreateManagedWidget("\92\81\8f\98", toggleWidgetClass,
1190                                  align_form2, args, num_args);
1191     num_args = 0;
1192     XtSetArg(args[num_args], nhStr(XtNfromVert), align_radio_l); num_args++;
1193     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1194     XtSetArg(args[num_args], nhStr(XtNradioGroup),
1195              plsel_align_radios[0]); num_args++;
1196     XtSetArg(args[num_args], nhStr(XtNradioData), 2); num_args++;
1197     plsel_align_radios[1] = align_radio_n
1198 /*JP
1199         = XtCreateManagedWidget("Neutral", toggleWidgetClass,
1200 */
1201         = XtCreateManagedWidget("\92\86\97§", toggleWidgetClass,
1202                                 align_form2, args, num_args);
1203     num_args = 0;
1204     XtSetArg(args[num_args], nhStr(XtNfromVert), align_radio_n); num_args++;
1205     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1206     XtSetArg(args[num_args], nhStr(XtNradioGroup),
1207              plsel_align_radios[0]); num_args++;
1208     XtSetArg(args[num_args], nhStr(XtNradioData), 3); num_args++;
1209     plsel_align_radios[2] = align_radio_c
1210 /*JP
1211         =  XtCreateManagedWidget("Chaotic", toggleWidgetClass,
1212 */
1213         =  XtCreateManagedWidget("\8d¬\93×", toggleWidgetClass,
1214                                  align_form2, args, num_args);
1215
1216     XawToggleUnsetCurrent(plsel_align_radios[0]);
1217
1218     XtAddCallback(align_radio_l, XtNcallback,
1219                   aligntoggleCallback, (XtPointer) (1));
1220     XtAddCallback(align_radio_n, XtNcallback,
1221                   aligntoggleCallback, (XtPointer) (2));
1222     XtAddCallback(align_radio_c, XtNcallback,
1223                   aligntoggleCallback, (XtPointer) (3));
1224
1225     /********************************************/
1226
1227     /* Buttons! */
1228
1229     num_args = 0;
1230     XtSetArg(args[num_args], nhStr(XtNfromVert), align_form); num_args++;
1231     XtSetArg(args[num_args], nhStr(XtNvertDistance), 30); num_args++;
1232     XtSetArg(args[num_args], nhStr(XtNrightMargin), 0); num_args++;
1233     XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++;
1234     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
1235     XtSetArg(args[num_args], XtNallowVert, False); num_args++;
1236     XtSetArg(args[num_args], XtNallowHoriz, False); num_args++;
1237     btn_vp = XtCreateManagedWidget("btn_vp", viewportWidgetClass, form,
1238                                    args, num_args);
1239
1240     num_args = 0;
1241     btn_form = XtCreateManagedWidget("btn_form", formWidgetClass, btn_vp,
1242                                      args, num_args);
1243
1244     /* "Random" button */
1245
1246     num_args = 0;
1247     XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
1248     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1249     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
1250     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1251 /*JP
1252     XtSetArg(args[num_args], nhStr(XtNlabel), "Random"); num_args++;
1253 */
1254     XtSetArg(args[num_args], nhStr(XtNlabel), "\83\89\83\93\83_\83\80"); num_args++;
1255     random_btn = XtCreateManagedWidget("random", commandWidgetClass, btn_form,
1256                                        args, num_args);
1257     XtAddCallback(random_btn, XtNcallback, plsel_random_btn_callback, form);
1258
1259     /* "Play" button */
1260
1261     num_args = 0;
1262     XtSetArg(args[num_args], nhStr(XtNfromVert), random_btn); num_args++;
1263     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1264     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
1265     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1266 /*JP
1267     XtSetArg(args[num_args], nhStr(XtNlabel), "Play"); num_args++;
1268 */
1269     XtSetArg(args[num_args], nhStr(XtNlabel), "\83v\83\8c\83C\8aJ\8en"); num_args++;
1270     plsel_btn_play = play_btn
1271         = XtCreateManagedWidget("play", commandWidgetClass, btn_form,
1272                                 args, num_args);
1273     XtAddCallback(play_btn, XtNcallback, plsel_play_btn_callback, form);
1274
1275     /* "Quit" button */
1276
1277     num_args = 0;
1278     XtSetArg(args[num_args], nhStr(XtNfromVert), play_btn); num_args++;
1279     XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++;
1280     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1281     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
1282     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1283 /*JP
1284     XtSetArg(args[num_args], nhStr(XtNlabel), "Quit"); num_args++;
1285 */
1286     XtSetArg(args[num_args], nhStr(XtNlabel), "\8fI\97¹"); num_args++;
1287     quit_btn = XtCreateManagedWidget("quit", commandWidgetClass, btn_form,
1288                                      args, num_args);
1289     XtAddCallback(quit_btn, XtNcallback, plsel_quit_btn_callback, form);
1290
1291     /********************************************/
1292
1293     XtRealizeWidget(popup);
1294     X11_player_selection_randomize();
1295
1296     if (flags.randomall) {
1297         plsel_dialog_acceptvalues();
1298     } else {
1299         ps_selected = -1;
1300         nh_XtPopup(popup, (int) XtGrabExclusive, form);
1301         /* The callback will enable the event loop exit. */
1302         (void) x_event(EXIT_ON_EXIT);
1303     }
1304
1305     nh_XtPopdown(popup);
1306     XtDestroyWidget(popup);
1307
1308     if (plsel_race_radios)
1309         free(plsel_race_radios);
1310
1311     if (plsel_role_radios)
1312         free(plsel_role_radios);
1313
1314     if (plsel_gend_radios)
1315         free(plsel_gend_radios);
1316
1317     if (plsel_align_radios)
1318         free(plsel_align_radios);
1319
1320     if (ps_selected == PS_QUIT || program_state.done_hup) {
1321         clearlocks();
1322         X11_exit_nhwindows((char *) 0);
1323         nh_terminate(0);
1324     }
1325 }
1326
1327 /* Global functions ======================================================== */
1328 void
1329 X11_player_selection_prompts()
1330 {
1331     int num_roles, num_races, num_gends, num_algns, i, availcount, availindex;
1332     Widget popup, player_form;
1333     const char **choices;
1334     char qbuf[QBUFSZ], plbuf[QBUFSZ];
1335
1336 #ifdef XI18N
1337     char **jroles;
1338     char jtmp[256];
1339 #endif
1340
1341     /* avoid unnecessary prompts further down */
1342     rigid_role_checks();
1343
1344     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
1345                                    flags.initrace, flags.initgend,
1346                                    flags.initalign);
1347
1348     while (flags.initrole < 0) {
1349         if (flags.initrole == ROLE_RANDOM || flags.randomall) {
1350             flags.initrole = pick_role(flags.initrace, flags.initgend,
1351                                        flags.initalign, PICK_RANDOM);
1352             break;
1353         }
1354
1355         /* select a role */
1356         for (num_roles = 0; roles[num_roles].name.m; ++num_roles)
1357             continue;
1358         choices = (const char **) alloc(sizeof (char *) * num_roles);
1359         for (;;) {
1360             availcount = 0;
1361             for (i = 0; i < num_roles; i++) {
1362                 choices[i] = 0;
1363                 if (ok_role(i, flags.initrace, flags.initgend,
1364                             flags.initalign)) {
1365                     choices[i] = roles[i].name.m;
1366                     if (flags.initgend >= 0 && flags.female
1367                         && roles[i].name.f)
1368                         choices[i] = roles[i].name.f;
1369                     ++availcount;
1370                 }
1371             }
1372             if (availcount > 0)
1373                 break;
1374             else if (flags.initalign >= 0)
1375                 flags.initalign = -1; /* reset */
1376             else if (flags.initgend >= 0)
1377                 flags.initgend = -1;
1378             else if (flags.initrace >= 0)
1379                 flags.initrace = -1;
1380             else
1381                 panic("no available ROLE+race+gender+alignment combinations");
1382         }
1383 #if 0 /*JP*/
1384         Sprintf(qbuf, "Choose your %s Role", s_suffix(plbuf));
1385 #else
1386         Sprintf(qbuf, "%s\90E\8bÆ\82ð\91I\91ð\82µ\82Ä\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
1387 #endif
1388         popup =
1389 #if 0 /*JP*/
1390             make_menu("player_selection", qbuf, player_select_translations,
1391                       "quit", ps_quit, "random", ps_random, num_roles,
1392                       choices, (Widget **) 0, ps_select, &player_form);
1393 #else
1394             make_menu("player_selection", qbuf, player_select_translations,
1395                       "\94²\82¯\82é", ps_quit, "\83\89\83\93\83_\83\80", ps_random, num_roles,
1396                       choices, (Widget **) 0, ps_select, &player_form);
1397 #endif
1398
1399         ps_selected = -1;
1400         positionpopup(popup, FALSE);
1401         nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
1402
1403         /* The callbacks will enable the event loop exit. */
1404         (void) x_event(EXIT_ON_EXIT);
1405
1406         nh_XtPopdown(popup);
1407         XtDestroyWidget(popup);
1408         free((genericptr_t) choices), choices = 0;
1409
1410         if (ps_selected == PS_QUIT || program_state.done_hup) {
1411             clearlocks();
1412             X11_exit_nhwindows((char *) 0);
1413             nh_terminate(0);
1414         } else if (ps_selected == PS_RANDOM) {
1415             flags.initrole = ROLE_RANDOM;
1416         } else if (ps_selected < 0 || ps_selected >= num_roles) {
1417             panic("player_selection: bad role select value %d", ps_selected);
1418         } else {
1419             flags.initrole = ps_selected;
1420         }
1421     }
1422
1423     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
1424                                    flags.initrace, flags.initgend,
1425                                    flags.initalign);
1426
1427     while (!validrace(flags.initrole, flags.initrace)) {
1428         if (flags.initrace == ROLE_RANDOM || flags.randomall) {
1429             flags.initrace = pick_race(flags.initrole, flags.initgend,
1430                                        flags.initalign, PICK_RANDOM);
1431             break;
1432         }
1433         /* select a race */
1434         for (num_races = 0; races[num_races].noun; ++num_races)
1435             continue;
1436         choices = (const char **) alloc(sizeof(char *) * num_races);
1437         for (;;) {
1438             availcount = availindex = 0;
1439             for (i = 0; i < num_races; i++) {
1440                 choices[i] = 0;
1441                 if (ok_race(flags.initrole, i, flags.initgend,
1442                             flags.initalign)) {
1443                     choices[i] = races[i].noun;
1444                     ++availcount;
1445                     availindex = i; /* used iff only one */
1446                 }
1447             }
1448             if (availcount > 0)
1449                 break;
1450             else if (flags.initalign >= 0)
1451                 flags.initalign = -1; /* reset */
1452             else if (flags.initgend >= 0)
1453                 flags.initgend = -1;
1454             else
1455                 panic("no available role+RACE+gender+alignment combinations");
1456         }
1457
1458         if (availcount == 1) {
1459             flags.initrace = availindex;
1460             free((genericptr_t) choices), choices = 0;
1461         } else {
1462 #if 0 /*JP*/
1463             Sprintf(qbuf, "Pick your %s race", s_suffix(plbuf));
1464 #else
1465             Sprintf(qbuf, "%s\8eí\91°\82ð\91I\91ð\82µ\82Ä\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
1466 #endif
1467 #if 0 /*JP*/
1468             popup =
1469                 make_menu("race_selection", qbuf, race_select_translations,
1470                           "quit", ps_quit, "random", ps_random, num_races,
1471                           choices, (Widget **) 0, ps_select, &player_form);
1472 #else
1473             popup =
1474                 make_menu("race_selection", qbuf, race_select_translations,
1475                           "\94²\82¯\82é", ps_quit, "\83\89\83\93\83_\83\80", ps_random, num_races,
1476                           choices, (Widget **) 0, ps_select, &player_form);
1477 #endif
1478
1479             ps_selected = -1;
1480             positionpopup(popup, FALSE);
1481             nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
1482
1483             /* The callbacks will enable the event loop exit. */
1484             (void) x_event(EXIT_ON_EXIT);
1485
1486             nh_XtPopdown(popup);
1487             XtDestroyWidget(popup);
1488             free((genericptr_t) choices), choices = 0;
1489
1490             if (ps_selected == PS_QUIT || program_state.done_hup) {
1491                 clearlocks();
1492                 X11_exit_nhwindows((char *) 0);
1493                 nh_terminate(0);
1494             } else if (ps_selected == PS_RANDOM) {
1495                 flags.initrace = ROLE_RANDOM;
1496             } else if (ps_selected < 0 || ps_selected >= num_races) {
1497                 panic("player_selection: bad race select value %d",
1498                       ps_selected);
1499             } else {
1500                 flags.initrace = ps_selected;
1501             }
1502         } /* more than one race choice available */
1503     }
1504
1505     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
1506                                    flags.initrace, flags.initgend,
1507                                    flags.initalign);
1508
1509     while (!validgend(flags.initrole, flags.initrace, flags.initgend)) {
1510         if (flags.initgend == ROLE_RANDOM || flags.randomall) {
1511             flags.initgend = pick_gend(flags.initrole, flags.initrace,
1512                                        flags.initalign, PICK_RANDOM);
1513             break;
1514         }
1515         /* select a gender */
1516         num_gends = 2; /* genders[2] isn't allowed */
1517         choices = (const char **) alloc(sizeof(char *) * num_gends);
1518         for (;;) {
1519             availcount = availindex = 0;
1520             for (i = 0; i < num_gends; i++) {
1521                 choices[i] = 0;
1522                 if (ok_gend(flags.initrole, flags.initrace, i,
1523                             flags.initalign)) {
1524                     choices[i] = genders[i].adj;
1525                     ++availcount;
1526                     availindex = i; /* used iff only one */
1527                 }
1528             }
1529             if (availcount > 0)
1530                 break;
1531             else if (flags.initalign >= 0)
1532                 flags.initalign = -1; /* reset */
1533             else
1534                 panic("no available role+race+GENDER+alignment combinations");
1535         }
1536
1537         if (availcount == 1) {
1538             flags.initgend = availindex;
1539             free((genericptr_t) choices), choices = 0;
1540         } else {
1541 /*JP
1542             Sprintf(qbuf, "Your %s gender?", s_suffix(plbuf));
1543 */
1544             Sprintf(qbuf, "%s\90«\95Ê\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
1545 #if 0 /*JP*/
1546             popup =
1547                 make_menu("gender_selection", qbuf, gend_select_translations,
1548                           "quit", ps_quit, "random", ps_random, num_gends,
1549                           choices, (Widget **) 0, ps_select, &player_form);
1550 #else
1551             popup =
1552                 make_menu("gender_selection", qbuf, gend_select_translations,
1553                           "\94²\82¯\82é", ps_quit, "\83\89\83\93\83_\83\80", ps_random, num_gends,
1554                           choices, (Widget **) 0, ps_select, &player_form);
1555 #endif
1556
1557             ps_selected = -1;
1558             positionpopup(popup, FALSE);
1559             nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
1560
1561             /* The callbacks will enable the event loop exit. */
1562             (void) x_event(EXIT_ON_EXIT);
1563
1564             nh_XtPopdown(popup);
1565             XtDestroyWidget(popup);
1566             free((genericptr_t) choices), choices = 0;
1567
1568             if (ps_selected == PS_QUIT || program_state.done_hup) {
1569                 clearlocks();
1570                 X11_exit_nhwindows((char *) 0);
1571                 nh_terminate(0);
1572             } else if (ps_selected == PS_RANDOM) {
1573                 flags.initgend = ROLE_RANDOM;
1574             } else if (ps_selected < 0 || ps_selected >= num_gends) {
1575                 panic("player_selection: bad gender select value %d",
1576                       ps_selected);
1577             } else {
1578                 flags.initgend = ps_selected;
1579             }
1580         } /* more than one gender choice available */
1581     }
1582
1583     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
1584                                    flags.initrace, flags.initgend,
1585                                    flags.initalign);
1586
1587     while (!validalign(flags.initrole, flags.initrace, flags.initalign)) {
1588         if (flags.initalign == ROLE_RANDOM || flags.randomall) {
1589             flags.initalign = pick_align(flags.initrole, flags.initrace,
1590                                          flags.initgend, PICK_RANDOM);
1591             break;
1592         }
1593         /* select an alignment */
1594         num_algns = 3; /* aligns[3] isn't allowed */
1595         choices = (const char **) alloc(sizeof(char *) * num_algns);
1596         for (;;) {
1597             availcount = availindex = 0;
1598             for (i = 0; i < num_algns; i++) {
1599                 choices[i] = 0;
1600                 if (ok_align(flags.initrole, flags.initrace, flags.initgend,
1601                              i)) {
1602                     choices[i] = aligns[i].adj;
1603                     ++availcount;
1604                     availindex = i; /* used iff only one */
1605                 }
1606             }
1607             if (availcount > 0)
1608                 break;
1609             else
1610                 panic("no available role+race+gender+ALIGNMENT combinations");
1611         }
1612
1613         if (availcount == 1) {
1614             flags.initalign = availindex;
1615             free((genericptr_t) choices), choices = 0;
1616         } else {
1617 /*JP
1618             Sprintf(qbuf, "Your %s alignment?", s_suffix(plbuf));
1619 */
1620             Sprintf(qbuf, "%s\91®\90«\82ð\91I\91ð\82µ\82Ä\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
1621 #if 0 /*JP*/
1622             popup = make_menu("alignment_selection", qbuf,
1623                               algn_select_translations, "quit", ps_quit,
1624                               "random", ps_random, num_algns, choices,
1625                               (Widget **) 0, ps_select, &player_form);
1626 #else
1627             popup = make_menu("alignment_selection", qbuf,
1628                               algn_select_translations, "\94²\82¯\82é", ps_quit,
1629                               "\83\89\83\93\83_\83\80", ps_random, num_algns, choices,
1630                               (Widget **) 0, ps_select, &player_form);
1631 #endif
1632
1633             ps_selected = -1;
1634             positionpopup(popup, FALSE);
1635             nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
1636
1637             /* The callbacks will enable the event loop exit. */
1638             (void) x_event(EXIT_ON_EXIT);
1639
1640             nh_XtPopdown(popup);
1641             XtDestroyWidget(popup);
1642             free((genericptr_t) choices), choices = 0;
1643
1644             if (ps_selected == PS_QUIT || program_state.done_hup) {
1645                 clearlocks();
1646                 X11_exit_nhwindows((char *) 0);
1647                 nh_terminate(0);
1648             } else if (ps_selected == PS_RANDOM) {
1649                 flags.initalign = ROLE_RANDOM;
1650             } else if (ps_selected < 0 || ps_selected >= num_algns) {
1651                 panic("player_selection: bad alignment select value %d",
1652                       ps_selected);
1653             } else {
1654                 flags.initalign = ps_selected;
1655             }
1656         } /* more than one alignment choice available */
1657     }
1658 }
1659
1660 void
1661 X11_player_selection()
1662 {
1663     if (iflags.wc_player_selection == VIA_DIALOG) {
1664         if (!*plname) {
1665 #ifdef UNIX
1666             char *defplname = get_login_name();
1667 #else
1668             char *defplname = (char *)0;
1669 #endif
1670             (void) strncpy(plname, defplname ? defplname : "Mumbles",
1671                            sizeof plname - 1);
1672             plname[sizeof plname - 1] = '\0';
1673             iflags.renameinprogress = TRUE;
1674         }
1675         X11_player_selection_dialog();
1676     } else { /* iflags.wc_player_selection == VIA_PROMPTS */
1677         X11_player_selection_prompts();
1678     }
1679 }
1680
1681 /* called by core to have the player pick an extended command */
1682 int
1683 X11_get_ext_cmd()
1684 {
1685     if (iflags.extmenu != ec_full_list) {
1686         /* player has toggled the 'extmenu' option, toss the old widgets */
1687         if (extended_commands)
1688             release_extended_cmds(); /* will set extended_commands to Null */
1689         ec_full_list = iflags.extmenu;
1690     }
1691     if (!extended_commands)
1692         init_extended_commands_popup();
1693
1694     extended_cmd_selected = -1; /* reset selected value */
1695     ec_scroll_to_view(-1); /* force scroll bar to top */
1696
1697     positionpopup(extended_command_popup, FALSE); /* center on cursor */
1698     nh_XtPopup(extended_command_popup, (int) XtGrabExclusive,
1699                extended_command_form);
1700
1701     /* The callbacks will enable the event loop exit. */
1702     (void) x_event(EXIT_ON_EXIT);
1703
1704     if (extended_cmd_selected < 0)
1705         return -1;
1706     return command_indx[extended_cmd_selected];
1707 }
1708
1709 void
1710 release_extended_cmds()
1711 {
1712     if (extended_commands) {
1713         XtDestroyWidget(extended_command_popup), extended_command_popup = 0;
1714         free((genericptr_t) extended_commands), extended_commands = 0;
1715         free((genericptr_t) command_list), command_list = (const char **) 0;
1716         free((genericptr_t) command_indx), command_indx = (short *) 0;
1717     }
1718 }
1719
1720 /* End global functions =================================================== */
1721
1722 /* Extended Command ------------------------------------------------------- */
1723 /* ARGSUSED */
1724 static void
1725 extend_select(w, client_data, call_data)
1726 Widget w;
1727 XtPointer client_data, call_data;
1728 {
1729     int selected = (int) (ptrdiff_t) client_data;
1730
1731     nhUse(w);
1732     nhUse(call_data);
1733
1734     if (extended_cmd_selected != selected) {
1735         /* visibly deselect old one */
1736         if (extended_cmd_selected >= 0)
1737             swap_fg_bg(extended_commands[extended_cmd_selected]);
1738
1739         /* select new one */
1740         swap_fg_bg(extended_commands[selected]);
1741         extended_cmd_selected = selected;
1742     }
1743
1744     nh_XtPopdown(extended_command_popup);
1745     /* reset colors while popped down */
1746     swap_fg_bg(extended_commands[extended_cmd_selected]);
1747     ec_active = FALSE;
1748     exit_x_event = TRUE; /* leave event loop */
1749 }
1750
1751 /* ARGSUSED */
1752 static void
1753 extend_dismiss(w, client_data, call_data)
1754 Widget w;
1755 XtPointer client_data, call_data;
1756 {
1757     nhUse(w);
1758     nhUse(client_data);
1759     nhUse(call_data);
1760
1761     ec_dismiss();
1762 }
1763
1764 /* ARGSUSED */
1765 static void
1766 extend_help(w, client_data, call_data)
1767 Widget w;
1768 XtPointer client_data, call_data;
1769 {
1770     nhUse(w);
1771     nhUse(client_data);
1772     nhUse(call_data);
1773
1774     /* We might need to make it known that we already have one listed. */
1775     (void) doextlist();
1776 }
1777
1778 /* ARGSUSED */
1779 void
1780 ec_delete(w, event, params, num_params)
1781 Widget w;
1782 XEvent *event;
1783 String *params;
1784 Cardinal *num_params;
1785 {
1786     if (w == extended_command_popup) {
1787         ec_dismiss();
1788     } else {
1789         popup_delete(w, event, params, num_params);
1790     }
1791 }
1792
1793 /* ARGSUSED */
1794 static void
1795 popup_delete(w, event, params, num_params)
1796 Widget w;
1797 XEvent *event;
1798 String *params;
1799 Cardinal *num_params;
1800 {
1801     nhUse(event);
1802     nhUse(params);
1803     nhUse(num_params);
1804
1805     ps_selected = PS_QUIT;
1806     nh_XtPopdown(w);
1807     exit_x_event = TRUE; /* leave event loop */
1808 }
1809
1810 static void
1811 ec_dismiss()
1812 {
1813     /* unselect while still visible */
1814     if (extended_cmd_selected >= 0)
1815         swap_fg_bg(extended_commands[extended_cmd_selected]);
1816     extended_cmd_selected = -1; /* dismiss */
1817     nh_XtPopdown(extended_command_popup);
1818     ec_active = FALSE;
1819     exit_x_event = TRUE; /* leave event loop */
1820 }
1821
1822 /* scroll the extended command menu if necessary
1823    so that choices extended_cmd_selected through ec_indx will be visible */
1824 static void
1825 ec_scroll_to_view(ec_indx)
1826 int ec_indx; /* might be greater than extended_cmd_selected */
1827 {
1828     Widget viewport, scrollbar, tmpw;
1829     Arg args[5];
1830     Cardinal num_args;
1831     Position lo_y, hi_y; /* ext cmd label y */
1832     float s_shown, s_top; /* scrollbar pos */
1833     float s_min, s_max;
1834     Dimension h, hh, wh, vh; /* widget and viewport heights */
1835     Dimension border_width;
1836     int distance = 0;
1837     boolean force_top = (ec_indx < 0);
1838
1839     /*
1840      * If the extended command menu needs to be scrolled in order to move
1841      * either the highlighted entry (extended_cmd_selected) or the target
1842      * entry (ec_indx) into view, we want to make both end up visible.
1843      * [Highligthed one is the first matching entry when the user types
1844      * something, such as "adjust" after typing 'a', and will be chosen
1845      * by pressing <return>.  Target entry is one past the last matching
1846      * entry (or last matching entry itself if at end of command list),
1847      * showing the user the other potential matches so far.]
1848      *
1849      * If that's not possible (maybe menu has been resized so that it's
1850      * too small), the highlighted entry takes precedence and the target
1851      * will be decremented until close enough to fit.
1852      */
1853
1854     if (force_top)
1855         ec_indx = 0;
1856
1857     /* get viewport and scrollbar widgets */
1858     tmpw = extended_commands[ec_indx];
1859     viewport = XtParent(tmpw);
1860     do {
1861         scrollbar = XtNameToWidget(tmpw, "*vertical");
1862         if (scrollbar)
1863             break;
1864         tmpw = XtParent(tmpw);
1865     } while (tmpw);
1866
1867     if (scrollbar && viewport) {
1868         /* get selected ext command label y position and height */
1869         num_args = 0;
1870         XtSetArg(args[num_args], XtNy, &hi_y); num_args++;
1871         XtSetArg(args[num_args], XtNheight, &h); num_args++;
1872         XtSetArg(args[num_args], nhStr(XtNborderWidth), &border_width);
1873                                                                    num_args++;
1874         XtSetArg(args[num_args], nhStr(XtNdefaultDistance), &distance);
1875                                                                    num_args++;
1876         XtGetValues(extended_commands[ec_indx], args, num_args);
1877         if (distance < 1 || distance > 32766) /* defaultDistance is weird */
1878             distance = 4;
1879         /* vertical distance between top of one command widget and the next */
1880         hh = h + distance + 2 * border_width;
1881         /* location of the highlighted entry, if any */
1882         if (extended_cmd_selected >= 0) {
1883             XtSetArg(args[0], XtNy, &lo_y);
1884             XtGetValues(extended_commands[extended_cmd_selected], args, ONE);
1885         } else
1886             lo_y = hi_y;
1887
1888         /* get menu widget and viewport heights */
1889         XtSetArg(args[0], XtNheight, &wh);
1890         XtGetValues(tmpw, args, ONE);
1891         XtSetArg(args[0], XtNheight, &vh);
1892         XtGetValues(viewport, args, ONE);
1893
1894         /* widget might be too small if it has been resized or
1895            there are a very large number of ambiguous choices */
1896         if (hi_y - lo_y > wh) {
1897             ec_indx = extended_cmd_selected;
1898             if (wh > hh)
1899                 ec_indx += (wh / hh);
1900             XtSetArg(args[0], XtNy, &hi_y);
1901             XtGetValues(extended_commands[ec_indx], args, num_args);
1902         }
1903
1904         /* get scrollbar "height" and "top" position; floats between 0-1 */
1905         num_args = 0;
1906         XtSetArg(args[num_args], XtNshown, &s_shown); num_args++;
1907         XtSetArg(args[num_args], nhStr(XtNtopOfThumb), &s_top); num_args++;
1908         XtGetValues(scrollbar, args, num_args);
1909
1910         s_min = s_top * vh;
1911         s_max = (s_top + s_shown) * vh;
1912
1913         /* scroll if outside the view */
1914         if (force_top) {
1915             s_min = 0.0;
1916             XtCallCallbacks(scrollbar, XtNjumpProc, &s_min);
1917         } else if ((int) lo_y <= (int) s_min) {
1918             s_min = (float) (lo_y / (float) vh);
1919             XtCallCallbacks(scrollbar, XtNjumpProc, &s_min);
1920         } else if ((int) (hi_y + h) >= (int) s_max) {
1921             s_min = (float) ((hi_y + h) / (float) vh) - s_shown;
1922             XtCallCallbacks(scrollbar, XtNjumpProc, &s_min);
1923         }
1924     }
1925 }
1926
1927 /* decide whether extcmdlist[idx] should be part of extended commands menu */
1928 static boolean
1929 ignore_extcmd(idx)
1930 int idx;
1931 {
1932     /* #shell or #suspect might not be available;
1933        'extmenu' option controls whether we show full list
1934        or just the traditional extended commands */
1935     if ((extcmdlist[idx].flags & CMD_NOT_AVAILABLE) != 0
1936         || ((extcmdlist[idx].flags & AUTOCOMPLETE) == 0 && !ec_full_list)
1937         || strlen(extcmdlist[idx].ef_txt) < 2) /* ignore "#" and "?" */
1938         return TRUE;
1939
1940     return FALSE;
1941 }
1942
1943 /* ARGSUSED */
1944 void
1945 ec_key(w, event, params, num_params)
1946 Widget w;
1947 XEvent *event;
1948 String *params;
1949 Cardinal *num_params;
1950 {
1951     char ch;
1952     int i, pass;
1953     float shown, top;
1954     Arg arg[2];
1955     Widget hbar, vbar;
1956     XKeyEvent *xkey = (XKeyEvent *) event;
1957
1958     nhUse(params);
1959     nhUse(num_params);
1960
1961     ch = key_event_to_char(xkey);
1962
1963     if (ch == '\0') { /* don't accept nul char/modifier event */
1964         /* don't beep */
1965         return;
1966     } else if (ch == '?') {
1967         extend_help((Widget) 0, (XtPointer) 0, (XtPointer) 0);
1968         return;
1969     } else if (index("\033\n\r", ch)) {
1970         if (ch == '\033') {
1971             /* unselect while still visible */
1972             if (extended_cmd_selected >= 0)
1973                 swap_fg_bg(extended_commands[extended_cmd_selected]);
1974             extended_cmd_selected = -1; /* dismiss */
1975         }
1976
1977         nh_XtPopdown(extended_command_popup);
1978         /* unselect while invisible */
1979         if (extended_cmd_selected >= 0)
1980             swap_fg_bg(extended_commands[extended_cmd_selected]);
1981
1982         exit_x_event = TRUE; /* leave event loop */
1983         ec_active = FALSE;
1984         return;
1985     } else if (ch == MENU_FIRST_PAGE || ch == MENU_LAST_PAGE) {
1986         hbar = vbar = (Widget) 0;
1987         find_scrollbars(w, &hbar, &vbar);
1988         if (vbar) {
1989             top = (ch == MENU_FIRST_PAGE) ? 0.0 : 1.0;
1990             XtCallCallbacks(vbar, XtNjumpProc, &top);
1991         }
1992         return;
1993     } else if (ch == MENU_NEXT_PAGE || ch == MENU_PREVIOUS_PAGE) {
1994         hbar = vbar = (Widget) 0;
1995         find_scrollbars(w, &hbar, &vbar);
1996         if (vbar) {
1997             XtSetArg(arg[0], nhStr(XtNshown), &shown);
1998             XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
1999             XtGetValues(vbar, arg, TWO);
2000             top += ((ch == MENU_NEXT_PAGE) ? shown : -shown);
2001             XtCallCallbacks(vbar, XtNjumpProc, &top);
2002         }
2003         return;
2004     }
2005
2006     /*
2007      * If too much time has elapsed, treat current key as starting a new
2008      * choice, otherwise it is a continuation of the choice in progress.
2009      * Extra letters might be needed to disambiguate between choices
2010      * ("ride" vs "rub", for instance), or player may just be typing in
2011      * the whole word.
2012      */
2013     if (ec_active && (xkey->time - ec_time) > 2500) /* 2.5 seconds */
2014         ec_active = FALSE;
2015
2016     if (!ec_active) {
2017         ec_nchars = 0;
2018         ec_active = TRUE;
2019     }
2020
2021     ec_time = xkey->time;
2022     ec_chars[ec_nchars++] = ch;
2023     if (ec_nchars >= EC_NCHARS)
2024         ec_nchars = EC_NCHARS - 1; /* don't overflow */
2025
2026     for (pass = 0; pass < 2; pass++) {
2027         if (pass == 1) {
2028             /* first pass finished, but no matching command was found */
2029             /* start a new one with the last char entered */
2030             if (extended_cmd_selected >= 0)
2031                 swap_fg_bg(extended_commands[extended_cmd_selected]);
2032             extended_cmd_selected = -1; /* dismiss */
2033             ec_chars[0] = ec_chars[ec_nchars - 1];
2034             ec_nchars = 1;
2035         }
2036         for (i = 0; command_list[i]; ++i) {
2037             if (!strncmp(ec_chars, command_list[i], ec_nchars)) {
2038                 if (extended_cmd_selected != i) {
2039                     /* I should use set() and unset() actions, but how do
2040                        I send the an action to the widget? */
2041                     if (extended_cmd_selected >= 0)
2042                         swap_fg_bg(extended_commands[extended_cmd_selected]);
2043                     extended_cmd_selected = i;
2044                     swap_fg_bg(extended_commands[extended_cmd_selected]);
2045                 }
2046                 /* advance to one past last matching entry, so that all
2047                    ambiguous choices, plus one to show thare aren't any
2048                    more such, will scroll into view */
2049                 do {
2050                     if (!command_list[i + 1])
2051                         break; /* end of list */
2052                     ++i;
2053                 } while (!strncmp(ec_chars, command_list[i], ec_nchars));
2054
2055                 ec_scroll_to_view(i);
2056                 return;
2057             }
2058         }
2059     }
2060 }
2061
2062 /*
2063  * Use our own home-brewed version menu because simpleMenu is designed to
2064  * be used from a menubox.
2065  */
2066 static void
2067 init_extended_commands_popup()
2068 {
2069     int i, j, num_commands, ignore_cmds = 0;
2070
2071     /* count commands */
2072     for (num_commands = 0; extcmdlist[num_commands].ef_txt; num_commands++)
2073         if (ignore_extcmd(num_commands))
2074             ++ignore_cmds;
2075
2076     j = num_commands - ignore_cmds;
2077     command_list = (const char **) alloc((unsigned) (j * sizeof (char *) + 1));
2078     command_indx = (short *) alloc((unsigned) (j * sizeof (short) + 1));
2079
2080     for (i = j = 0; i < num_commands; i++) {
2081         if (ignore_extcmd(i))
2082             continue;
2083         command_indx[j] = (short) i;
2084         command_list[j++] = extcmdlist[i].ef_txt;
2085     }
2086     command_list[j] = (char *) 0;
2087     command_indx[j] = -1;
2088     num_commands = j;
2089
2090 #if 0 /*JP*/
2091     extended_command_popup =
2092         make_menu("extended_commands", "Extended Commands",
2093                   extended_command_translations, "dismiss", extend_dismiss,
2094                   "help", extend_help, num_commands, command_list,
2095                   &extended_commands, extend_select, &extended_command_form);
2096 #else
2097     extended_command_popup =
2098         make_menu("extended_commands", "\8ag\92£\83R\83}\83\93\83h",
2099                   extended_command_translations, "\8eæ\8fÁ", extend_dismiss,
2100                   "\83w\83\8b\83v", extend_help, num_commands, command_list,
2101                   &extended_commands, extend_select, &extended_command_form);
2102 #endif
2103 }
2104
2105 /* ------------------------------------------------------------------------ */
2106
2107 /*
2108  * Create a popup widget of the following form:
2109  *
2110  *                    popup_label
2111  *              ----------- ------------
2112  *              |left_name| |right_name|
2113  *              ----------- ------------
2114  *              ------------------------
2115  *              |       name1          |
2116  *              ------------------------
2117  *              ------------------------
2118  *              |       name2          |
2119  *              ------------------------
2120  *                        .
2121  *                        .
2122  *              ------------------------
2123  *              |       nameN          |
2124  *              ------------------------
2125  */
2126 static Widget
2127 make_menu(popup_name, popup_label, popup_translations, left_name,
2128           left_callback, right_name, right_callback, num_names, widget_names,
2129           command_widgets, name_callback, formp)
2130 const char *popup_name;
2131 const char *popup_label;
2132 const char *popup_translations;
2133 const char *left_name;
2134 XtCallbackProc left_callback;
2135 const char *right_name;
2136 XtCallbackProc right_callback;
2137 int num_names;
2138 const char **widget_names; /* return array of command widgets */
2139 Widget **command_widgets;
2140 XtCallbackProc name_callback;
2141 Widget *formp; /* return */
2142 {
2143     Widget popup, popform, form, label, above, left, right, view;
2144     Widget *commands, *curr;
2145     int i;
2146     Arg args[12];
2147     Cardinal num_args;
2148     Dimension width, other_width, max_width, border_width,
2149               height, cumulative_height, screen_height;
2150     int distance, skip;
2151     char btnname[BUFSZ];
2152
2153     commands = (Widget *) alloc((unsigned) num_names * sizeof (Widget));
2154
2155     num_args = 0;
2156     XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
2157     XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
2158     popup = XtCreatePopupShell(popup_name, transientShellWidgetClass,
2159                                toplevel, args, num_args);
2160     XtOverrideTranslations(
2161         popup, XtParseTranslationTable("<Message>WM_PROTOCOLS: ec_delete()"));
2162
2163     num_args = 0;
2164     XtSetArg(args[num_args], XtNtranslations,
2165              XtParseTranslationTable(popup_translations)); num_args++;
2166     XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
2167     XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++;
2168     popform = XtCreateManagedWidget("topmenuform", formWidgetClass, popup,
2169                                     args, num_args);
2170
2171
2172     num_args = 0;
2173     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
2174     XtSetArg(args[num_args], XtNallowVert, True); num_args++;
2175     /*XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;*/
2176     XtSetArg(args[num_args], nhStr(XtNuseBottom), True); num_args++;
2177     XtSetArg(args[num_args], nhStr(XtNuseRight), True); num_args++;
2178     XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
2179     XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++;
2180     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
2181     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
2182     XtSetArg(args[num_args], XtNtranslations,
2183              XtParseTranslationTable(popup_translations)); num_args++;
2184     view = XtCreateManagedWidget("menuformview", viewportWidgetClass, popform,
2185                                  args, num_args);
2186
2187     num_args = 0;
2188     XtSetArg(args[num_args], XtNtranslations,
2189              XtParseTranslationTable(popup_translations)); num_args++;
2190     XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
2191     XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++;
2192     *formp = form = XtCreateManagedWidget("menuform", formWidgetClass, view,
2193                                           args, num_args);
2194
2195     /*
2196      * Get the default distance between objects in the viewport widget.
2197      * (Something is fishy here:  'distance' ends up being 0 but there
2198      * is a non-zero gap between the borders of the internal widgets.
2199      * It matches exactly the default value of 4 for defaultDistance.)
2200      */
2201     num_args = 0;
2202     XtSetArg(args[num_args], nhStr(XtNdefaultDistance), &distance); num_args++;
2203     XtSetArg(args[num_args], nhStr(XtNborderWidth), &border_width); num_args++;
2204     XtGetValues(view, args, num_args);
2205     if (distance < 1 || distance > 32766)
2206         distance = 4;
2207
2208     /*
2209      * Create the label.
2210      */
2211     num_args = 0;
2212 #if defined(X11R6) && defined(XI18N)
2213     XtSetArg(args[num_args], XtNinternational, True); num_args++;
2214 #endif
2215     XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
2216     label = XtCreateManagedWidget(popup_label, labelWidgetClass, form, args,
2217                                   num_args);
2218
2219     cumulative_height = 0;
2220     XtSetArg(args[0], XtNheight, &height);
2221     XtGetValues(label, args, ONE);
2222     cumulative_height += distance + height; /* no border for label */
2223
2224     /*
2225      * Create the left button.
2226      */
2227     num_args = 0;
2228     XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++;
2229     XtSetArg(args[num_args], nhStr(XtNlabel), left_name); num_args++;
2230 #if 0
2231     XtSetArg(args[num_args], nhStr(XtNshapeStyle),
2232                               XmuShapeRoundedRectangle); num_args++;
2233 #endif
2234     Sprintf(btnname, "btn_%s", left_name);
2235     left = XtCreateManagedWidget(btnname, commandWidgetClass, form, args,
2236                                  num_args);
2237     XtAddCallback(left, XtNcallback, left_callback, (XtPointer) 0);
2238     skip = (distance < 4) ? 8 : 2 * distance;
2239
2240     num_args = 0;
2241     XtSetArg(args[0], XtNheight, &height);
2242     XtGetValues(left, args, ONE);
2243     cumulative_height += distance + height + 2 * border_width;
2244
2245     /*
2246      * Create right button.
2247      */
2248     num_args = 0;
2249     XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++;
2250     XtSetArg(args[num_args], nhStr(XtNhorizDistance), skip); num_args++;
2251     XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++;
2252     XtSetArg(args[num_args], nhStr(XtNlabel), right_name); num_args++;
2253 #if 0
2254     XtSetArg(args[num_args], nhStr(XtNshapeStyle),
2255                               XmuShapeRoundedRectangle); num_args++;
2256 #endif
2257 #if defined(X11R6) && defined(XI18N)
2258     XtSetArg(args[num_args], XtNinternational, True); num_args++;
2259 #endif
2260     Sprintf(btnname, "btn_%s", right_name);
2261     right = XtCreateManagedWidget(btnname, commandWidgetClass, form, args,
2262                                   num_args);
2263     XtAddCallback(right, XtNcallback, right_callback, (XtPointer) 0);
2264
2265     XtInstallAccelerators(form, left);
2266     XtInstallAccelerators(form, right);
2267
2268     /*
2269      * Create and place the command widgets.
2270      */
2271     for (i = 0, above = left, curr = commands; i < num_names; i++) {
2272         if (!widget_names[i])
2273             continue;
2274         num_args = 0;
2275         XtSetArg(args[num_args], XtNtranslations,
2276                  XtParseTranslationTable(popup_entry_translations)); num_args++;
2277         XtSetArg(args[num_args], nhStr(XtNfromVert), above); num_args++;
2278         if (above == left) {
2279             /* if first, we are farther apart */
2280             XtSetArg(args[num_args], nhStr(XtNvertDistance), skip); num_args++;
2281             cumulative_height += skip;
2282         } else
2283             cumulative_height += distance;
2284         cumulative_height += height + 2 * border_width;
2285
2286 #if defined(X11R6) && defined(XI18N)
2287         XtSetArg(args[num_args], XtNinternational, True);
2288         num_args++;
2289 #endif
2290         *curr = XtCreateManagedWidget(widget_names[i], commandWidgetClass,
2291                                       form, args, num_args);
2292         XtAddCallback(*curr, XtNcallback, name_callback,
2293                       (XtPointer) (ptrdiff_t) i);
2294         above = *curr++;
2295     }
2296     cumulative_height += distance; /* space at bottom of form */
2297
2298     /*
2299      * Now find the largest width.  Start with width of left + right buttons
2300      * ('dismiss' + 'help' or 'quit' + 'random'), since they are adjacent.
2301      */
2302     XtSetArg(args[0], XtNwidth, &max_width);
2303     XtGetValues(left, args, ONE);
2304     XtSetArg(args[0], XtNwidth, &width);
2305     XtGetValues(right, args, ONE);
2306     /* doesn't count leftmost 'distance + border_width' and
2307        rightmost 'border_width + distance' since all entries have those */
2308     max_width = max_width + border_width + skip + border_width + width;
2309
2310     /* Next, the title. */
2311     XtSetArg(args[0], XtNwidth, &width);
2312     XtGetValues(label, args, ONE);
2313     if (width > max_width)
2314         max_width = width;
2315
2316     /* Finally, the commands. */
2317     for (i = 0, curr = commands; i < num_names; i++) {
2318         if (!widget_names[i])
2319             continue;
2320         XtSetArg(args[0], XtNwidth, &width);
2321         XtGetValues(*curr, args, ONE);
2322         if (width > max_width)
2323             max_width = width;
2324         curr++;
2325     }
2326
2327     /*
2328      * Re-do the two side-by-side widgets to take up half the width each.
2329      *
2330      * With max_width and skip both having even values, we never have to
2331      * tweak left or right to maybe be one pixel wider than the other.
2332      */
2333     if (max_width % 2)
2334         ++max_width;
2335     XtSetArg(args[0], XtNwidth, &width);
2336     XtGetValues(left, args, ONE);
2337     XtSetArg(args[0], XtNwidth, &other_width);
2338     XtGetValues(right, args, ONE);
2339     if (width + border_width + skip / 2 < max_width / 2
2340         && other_width + border_width + skip / 2 < max_width / 2) {
2341         /* both are narrower than half */
2342         width = other_width = max_width / 2 - border_width - skip / 2;
2343         XtSetArg(args[0], XtNwidth, width);
2344         XtSetValues(left, args, ONE);
2345         XtSetArg(args[0], XtNwidth, other_width);
2346         XtSetValues(right, args, ONE);
2347     } else if (width + border_width + skip / 2 < max_width / 2) {
2348         /* 'other_width' (right) is half or more */
2349         width = max_width - other_width - 2 * border_width - skip;
2350         XtSetArg(args[0], XtNwidth, width);
2351         XtSetValues(left, args, ONE);
2352     } else if (other_width + border_width + skip / 2 < max_width / 2) {
2353         /* 'width' (left) is half or more */
2354         other_width = max_width - width - 2 * border_width - skip;
2355         XtSetArg(args[0], XtNwidth, other_width);
2356         XtSetValues(right, args, ONE);
2357     } else {
2358         ; /* both are exactly half... */
2359     }
2360
2361     /*
2362      * Finally, set all of the single line widgets to the largest width.
2363      */
2364     XtSetArg(args[0], XtNwidth, max_width);
2365     XtSetValues(label, args, ONE);
2366
2367     for (i = 0, curr = commands; i < num_names; i++) {
2368         if (!widget_names[i])
2369             continue;
2370         XtSetArg(args[0], XtNwidth, max_width);
2371         XtSetValues(*curr, args, ONE);
2372         curr++;
2373     }
2374
2375     if (command_widgets)
2376         *command_widgets = commands;
2377     else
2378         free((char *) commands);
2379
2380     /*
2381      * If the menu's complete height is too big for the display,
2382      * forcing the height to be smaller will cause the vertical
2383      * scroll bar (enabled but not forced above) to be included.
2384      */
2385     screen_height = XHeightOfScreen(XtScreen(popup));
2386     screen_height -= appResources.extcmd_height_delta; /* NetHack.ad */
2387     if (cumulative_height >= screen_height) {
2388         /* 25 is a guesstimate for scrollbar width;
2389            window manager might override the request for y==1 */
2390         num_args = 0;
2391         XtSetArg(args[num_args], XtNy, 1); num_args++;
2392         XtSetArg(args[num_args], XtNwidth, max_width + 25); num_args++;
2393         XtSetArg(args[num_args], XtNheight, screen_height - 1); num_args++;
2394         XtSetValues(popup, args, num_args);
2395     }
2396     XtRealizeWidget(popup);
2397     XSetWMProtocols(XtDisplay(popup), XtWindow(popup), &wm_delete_window, 1);
2398
2399     /* during role selection, highlight "random" as pre-selected choice */
2400     if (right_callback == ps_random && index(ps_randchars, '\n'))
2401         swap_fg_bg(right);
2402
2403     return popup;
2404 }
2405
2406 /*winmisc.c*/