OSDN Git Service

update year to 2020
[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-2020            */
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     XtSetArg(args[num_args], nhStr(XtNlabel), "Name"); num_args++;
827     XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
828
829     namelabel = XtCreateManagedWidget("name_label",
830                                       labelWidgetClass, name_form,
831                                       args, num_args);
832
833     num_args = 0;
834     XtSetArg(args[num_args], nhStr(XtNfromVert), namelabel); num_args++;
835     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
836     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
837     XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
838     XtSetArg(args[num_args], nhStr(XtNeditType),
839              !plsel_ask_name ? XawtextRead : XawtextEdit); num_args++;
840     XtSetArg(args[num_args], nhStr(XtNresize), XawtextResizeWidth); num_args++;
841     XtSetArg(args[num_args], nhStr(XtNstring), plname); num_args++;
842     XtSetArg(args[num_args], XtNinsertPosition, strlen(plname)); num_args++;
843     XtSetArg(args[num_args], nhStr(XtNaccelerators),
844              XtParseAcceleratorTable(plsel_input_accelerators)); num_args++;
845     plsel_name_input = XtCreateManagedWidget("name_input",
846                                              asciiTextWidgetClass, name_form,
847                                              args, num_args);
848
849     XtInstallAccelerators(plsel_name_input, plsel_name_input);
850     if (plsel_ask_name) {
851         XtSetKeyboardFocus(form, plsel_name_input);
852     } else {
853         XtSetArg(args[0], nhStr(XtNdisplayCaret), False);
854         XtSetValues(plsel_name_input, args, ONE);
855     }
856
857     return name_vp;
858 }
859
860 void
861 X11_player_selection_dialog()
862 {
863     Widget popup, popup_vp;
864     Widget form;
865     Widget name_vp;
866     Widget racelabel, race_form, race_vp, race_form2;
867     Widget rolelabel, role_form, role_vp, role_form2;
868     Widget gendlabel, gend_form,
869            gend_radio_m, gend_radio_f, gend_vp, gend_form2;
870     Widget alignlabel, align_form,
871            align_radio_l, align_radio_n, align_radio_c, align_vp, align_form2;
872     Widget btn_vp, btn_form, random_btn, play_btn, quit_btn;
873     Widget tmpwidget;
874     Arg args[10];
875     Cardinal num_args;
876     int i;
877     int winwid = 400;
878     int cwid = (winwid / 3) - 14;
879
880     num_args = 0;
881     XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
882     XtSetArg(args[num_args], XtNtitle, "Player Selection"); num_args++;
883     popup = XtCreatePopupShell("player_selection_dialog",
884                                transientShellWidgetClass,
885                                toplevel, args, num_args);
886
887     num_args = 0;
888     XtSetArg(args[num_args], XtNtranslations,
889              XtParseTranslationTable(plsel_dialog_translations)); num_args++;
890     popup_vp = XtCreateManagedWidget("plsel_vp", viewportWidgetClass,
891                                      popup, args, num_args);
892
893     num_args = 0;
894     XtSetArg(args[num_args], XtNtranslations,
895              XtParseTranslationTable(plsel_dialog_translations)); num_args++;
896     form = XtCreateManagedWidget("plsel_form", formWidgetClass, popup_vp,
897                                  args, num_args);
898
899     name_vp = X11_create_player_selection_name(form);
900
901     num_args = 0;
902     XtSetArg(args[num_args], XtNwidth, winwid); num_args++;
903     XtSetValues(name_vp, args, num_args);
904
905     /*
906      * Layout; role is centered rather than first:
907      *
908      *   +------------------------------------+
909      *   | name                               |
910      *   +------------------------------------+
911      *   +--------+   +------------+   +---------+
912      *   | human  |   |archeologist|   |  male   |
913      *   |  elf   |   | barbarian  |   | female  |
914      *   | dwarf  |   |  caveman   |   +---------+
915      *   | gnome  |   |   healer   |   +---------+
916      *   |  orc   |   |   knight   |   | lawful  |
917      *   +--------+   |    monk    |   | neutral |
918      *                |   priest   |   | chaotic |
919      *                |   rogue    |   +---------+
920      *                |   ranger   |   +--------+
921      *                |  samurai   |   + Random +
922      *                |  tourist   |   +  Play  +
923      *                |  valkyrie  |   +  Quit  +
924      *                |   wizard   |   +--------+
925      *                +------------+
926      *
927      * TODO:
928      *  make name box same width as race+gap+role+gap+gender/alignment
929      *  (resize it after the other boxes have been placed);
930      *  make Random/Play/Quit buttons same width as gender/alignment and
931      *  align bottom of them with bottom of role (they already specify
932      *  the same width for the label text but different decorations--
933      *  room for radio button box--of the other widgets results in the
934      *  total width being different);
935      *  add 'random' to each of the four boxes and Choose to the Random/
936      *  Play/Quit buttons; if none of the four 'random's are currently
937      *  selected, gray-out Choose; conversely, when Choose or Play is
938      *  clicked on, make the random assignments for any/all of the four
939      *  boxes which have 'random' selected.
940      *  Maybe:  move gender box underneath race, bottom aligned with role
941      *  and move alignment up to where gender currently is.  If that's
942      *  done, move role column first and race+gender to middle.
943      */
944
945     /********************************************/
946
947     /* Race */
948
949     num_args = 0;
950     XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++;
951     race_form = XtCreateManagedWidget("race_form", formWidgetClass, form,
952                                       args, num_args);
953
954     /* race label */
955     num_args = 0;
956     XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
957     XtSetArg(args[num_args], nhStr(XtNlabel), "Race"); num_args++;
958     racelabel = XtCreateManagedWidget("race_label",
959                                       labelWidgetClass, race_form,
960                                       args, num_args);
961
962     num_args = 0;
963     XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++;
964     XtSetArg(args[num_args], nhStr(XtNfromVert), racelabel); num_args++;
965     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
966     XtSetArg(args[num_args], XtNallowVert, True); num_args++;
967     XtSetArg(args[num_args], XtNallowHoriz, False); num_args++;
968     race_vp = XtCreateManagedWidget("race_vp", viewportWidgetClass, race_form,
969                                     args, num_args);
970
971     num_args = 0;
972     race_form2 = XtCreateManagedWidget("race_form2", formWidgetClass, race_vp,
973                                       args, num_args);
974
975     for (i = 0; races[i].noun; i++)
976         continue;
977     plsel_n_races = i;
978
979     plsel_race_radios = (Widget *) alloc(sizeof (Widget) * plsel_n_races);
980
981     /* race radio buttons */
982     for (i = 0; races[i].noun; i++) {
983         Widget racewidget;
984
985         num_args = 0;
986         if (i > 0)
987             XtSetArg(args[num_args], nhStr(XtNfromVert),
988                      tmpwidget); num_args++;
989         XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
990         if (i > 0)
991             XtSetArg(args[num_args], nhStr(XtNradioGroup),
992                      plsel_race_radios[0]); num_args++;
993         XtSetArg(args[num_args], nhStr(XtNradioData), (i + 1)); num_args++;
994
995         racewidget = XtCreateManagedWidget(races[i].noun,
996                                            toggleWidgetClass,
997                                            race_form2,
998                                            args, num_args);
999         XtAddCallback(racewidget, XtNcallback, racetoggleCallback, i2xtp(i));
1000         tmpwidget = racewidget;
1001         plsel_race_radios[i] = racewidget;
1002     }
1003
1004     XawToggleUnsetCurrent(plsel_race_radios[0]);
1005
1006     /********************************************/
1007
1008     /* Role */
1009     num_args = 0;
1010     XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++;
1011     XtSetArg(args[num_args], nhStr(XtNfromHoriz), race_form); num_args++;
1012     role_form = XtCreateManagedWidget("role_form", formWidgetClass, form,
1013                                       args, num_args);
1014
1015     /* role label */
1016     num_args = 0;
1017     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1018     XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
1019     XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
1020     XtSetArg(args[num_args], nhStr(XtNlabel), "Role"); num_args++;
1021     rolelabel = XtCreateManagedWidget("role_label", labelWidgetClass,
1022                                       role_form, args, num_args);
1023
1024     num_args = 0;
1025     XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++;
1026     XtSetArg(args[num_args], nhStr(XtNfromVert), rolelabel); num_args++;
1027     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
1028     XtSetArg(args[num_args], XtNallowVert, True); num_args++;
1029     XtSetArg(args[num_args], XtNallowHoriz, False); num_args++;
1030     role_vp = XtCreateManagedWidget("role_vp", viewportWidgetClass, role_form,
1031                                     args, num_args);
1032
1033     num_args = 0;
1034     role_form2 = XtCreateManagedWidget("role_form2", formWidgetClass, role_vp,
1035                                       args, num_args);
1036
1037     for (i = 0; roles[i].name.m; i++)
1038         continue;
1039     plsel_n_roles = i;
1040
1041     plsel_role_radios = (Widget *) alloc(sizeof (Widget) * plsel_n_roles);
1042
1043     /* role radio buttons */
1044     for (i = 0; roles[i].name.m; i++) {
1045         Widget rolewidget;
1046
1047         num_args = 0;
1048         if (i > 0)
1049             XtSetArg(args[num_args], nhStr(XtNfromVert),
1050                      tmpwidget); num_args++;
1051         XtSetArg(args[num_args], nhStr(XtNwidth), cwid); num_args++;
1052         if (i > 0)
1053             XtSetArg(args[num_args], nhStr(XtNradioGroup),
1054                      plsel_role_radios[0]); num_args++;
1055         XtSetArg(args[num_args], nhStr(XtNradioData), (i + 1)); num_args++;
1056
1057         rolewidget = XtCreateManagedWidget(roles[i].name.m, toggleWidgetClass,
1058                                            role_form2, args, num_args);
1059         XtAddCallback(rolewidget, XtNcallback, roletoggleCallback, i2xtp(i));
1060         tmpwidget = rolewidget;
1061         plsel_role_radios[i] = rolewidget;
1062     }
1063     XawToggleUnsetCurrent(plsel_role_radios[0]);
1064
1065     /********************************************/
1066
1067     /* Gender*/
1068
1069     plsel_gend_radios = (Widget *) alloc(sizeof (Widget) * ROLE_GENDERS);
1070
1071     num_args = 0;
1072     XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++;
1073     XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++;
1074     gend_form = XtCreateManagedWidget("gender_form", formWidgetClass, form,
1075                                       args, num_args);
1076
1077     /* gender label */
1078     num_args = 0;
1079     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1080     XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
1081     XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
1082     XtSetArg(args[num_args], nhStr(XtNlabel), "Gender"); num_args++;
1083     gendlabel = XtCreateManagedWidget("gender_label", labelWidgetClass,
1084                                       gend_form, args, num_args);
1085
1086     num_args = 0;
1087     XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++;
1088     XtSetArg(args[num_args], nhStr(XtNfromVert), gendlabel); num_args++;
1089     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
1090     XtSetArg(args[num_args], XtNallowVert, True); num_args++;
1091     XtSetArg(args[num_args], XtNallowHoriz, False); num_args++;
1092     gend_vp = XtCreateManagedWidget("gender_vp", viewportWidgetClass,
1093                                     gend_form, args, num_args);
1094
1095     num_args = 0;
1096     gend_form2 = XtCreateManagedWidget("gender_form2", formWidgetClass,
1097                                        gend_vp, args, num_args);
1098
1099     /* gender radio buttons */
1100     num_args = 0;
1101     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1102     XtSetArg(args[num_args], nhStr(XtNradioData), 1); num_args++;
1103     plsel_gend_radios[0] = gend_radio_m
1104         =  XtCreateManagedWidget("Male", toggleWidgetClass,
1105                                  gend_form2, args, num_args);
1106     num_args = 0;
1107     XtSetArg(args[num_args], nhStr(XtNfromVert), gend_radio_m); num_args++;
1108     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1109     XtSetArg(args[num_args], nhStr(XtNradioGroup),
1110              plsel_gend_radios[0]); num_args++;
1111     XtSetArg(args[num_args], nhStr(XtNradioData), 2); num_args++;
1112     plsel_gend_radios[1] = gend_radio_f
1113         =  XtCreateManagedWidget("Female", toggleWidgetClass,
1114                                  gend_form2, args, num_args);
1115
1116     XawToggleUnsetCurrent(plsel_gend_radios[0]);
1117
1118     XtAddCallback(gend_radio_m, XtNcallback,
1119                   gendertoggleCallback, (XtPointer) (1));
1120     XtAddCallback(gend_radio_f, XtNcallback,
1121                   gendertoggleCallback, (XtPointer) (2));
1122
1123     /********************************************/
1124
1125     /* Alignment */
1126
1127     plsel_align_radios = (Widget *) alloc(sizeof (Widget) * ROLE_ALIGNS);
1128
1129     num_args = 0;
1130     XtSetArg(args[num_args], nhStr(XtNfromVert), gend_form); num_args++;
1131     XtSetArg(args[num_args], nhStr(XtNvertDistance), 30); num_args++;
1132     XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++;
1133     align_form = XtCreateManagedWidget("align_form", formWidgetClass, form,
1134                                        args, num_args);
1135
1136     /* align label */
1137     num_args = 0;
1138     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1139     XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
1140     XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
1141     XtSetArg(args[num_args], nhStr(XtNlabel), "Alignment"); num_args++;
1142     alignlabel = XtCreateManagedWidget("align_label", labelWidgetClass,
1143                                        align_form, args, num_args);
1144
1145     num_args = 0;
1146     XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++;
1147     XtSetArg(args[num_args], nhStr(XtNfromVert), alignlabel); num_args++;
1148     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
1149     XtSetArg(args[num_args], XtNallowVert, True); num_args++;
1150     XtSetArg(args[num_args], XtNallowHoriz, False); num_args++;
1151     align_vp = XtCreateManagedWidget("align_vp", viewportWidgetClass,
1152                                      align_form, args, num_args);
1153
1154     num_args = 0;
1155     align_form2 = XtCreateManagedWidget("align_form2", formWidgetClass,
1156                                         align_vp, args, num_args);
1157
1158     num_args = 0;
1159     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1160     XtSetArg(args[num_args], nhStr(XtNradioData), 1); num_args++;
1161     plsel_align_radios[0] = align_radio_l
1162         =  XtCreateManagedWidget("Lawful", toggleWidgetClass,
1163                                  align_form2, args, num_args);
1164     num_args = 0;
1165     XtSetArg(args[num_args], nhStr(XtNfromVert), align_radio_l); num_args++;
1166     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1167     XtSetArg(args[num_args], nhStr(XtNradioGroup),
1168              plsel_align_radios[0]); num_args++;
1169     XtSetArg(args[num_args], nhStr(XtNradioData), 2); num_args++;
1170     plsel_align_radios[1] = align_radio_n
1171         = XtCreateManagedWidget("Neutral", toggleWidgetClass,
1172                                 align_form2, args, num_args);
1173     num_args = 0;
1174     XtSetArg(args[num_args], nhStr(XtNfromVert), align_radio_n); num_args++;
1175     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1176     XtSetArg(args[num_args], nhStr(XtNradioGroup),
1177              plsel_align_radios[0]); num_args++;
1178     XtSetArg(args[num_args], nhStr(XtNradioData), 3); num_args++;
1179     plsel_align_radios[2] = align_radio_c
1180         =  XtCreateManagedWidget("Chaotic", toggleWidgetClass,
1181                                  align_form2, args, num_args);
1182
1183     XawToggleUnsetCurrent(plsel_align_radios[0]);
1184
1185     XtAddCallback(align_radio_l, XtNcallback,
1186                   aligntoggleCallback, (XtPointer) (1));
1187     XtAddCallback(align_radio_n, XtNcallback,
1188                   aligntoggleCallback, (XtPointer) (2));
1189     XtAddCallback(align_radio_c, XtNcallback,
1190                   aligntoggleCallback, (XtPointer) (3));
1191
1192     /********************************************/
1193
1194     /* Buttons! */
1195
1196     num_args = 0;
1197     XtSetArg(args[num_args], nhStr(XtNfromVert), align_form); num_args++;
1198     XtSetArg(args[num_args], nhStr(XtNvertDistance), 30); num_args++;
1199     XtSetArg(args[num_args], nhStr(XtNrightMargin), 0); num_args++;
1200     XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++;
1201     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
1202     XtSetArg(args[num_args], XtNallowVert, False); num_args++;
1203     XtSetArg(args[num_args], XtNallowHoriz, False); num_args++;
1204     btn_vp = XtCreateManagedWidget("btn_vp", viewportWidgetClass, form,
1205                                    args, num_args);
1206
1207     num_args = 0;
1208     btn_form = XtCreateManagedWidget("btn_form", formWidgetClass, btn_vp,
1209                                      args, num_args);
1210
1211     /* "Random" button */
1212
1213     num_args = 0;
1214     XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
1215     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1216     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
1217     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1218     XtSetArg(args[num_args], nhStr(XtNlabel), "Random"); num_args++;
1219     random_btn = XtCreateManagedWidget("random", commandWidgetClass, btn_form,
1220                                        args, num_args);
1221     XtAddCallback(random_btn, XtNcallback, plsel_random_btn_callback, form);
1222
1223     /* "Play" button */
1224
1225     num_args = 0;
1226     XtSetArg(args[num_args], nhStr(XtNfromVert), random_btn); num_args++;
1227     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1228     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
1229     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1230     XtSetArg(args[num_args], nhStr(XtNlabel), "Play"); num_args++;
1231     plsel_btn_play = play_btn
1232         = XtCreateManagedWidget("play", commandWidgetClass, btn_form,
1233                                 args, num_args);
1234     XtAddCallback(play_btn, XtNcallback, plsel_play_btn_callback, form);
1235
1236     /* "Quit" button */
1237
1238     num_args = 0;
1239     XtSetArg(args[num_args], nhStr(XtNfromVert), play_btn); num_args++;
1240     XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++;
1241     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
1242     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
1243     XtSetArg(args[num_args], XtNwidth, cwid); num_args++;
1244     XtSetArg(args[num_args], nhStr(XtNlabel), "Quit"); num_args++;
1245     quit_btn = XtCreateManagedWidget("quit", commandWidgetClass, btn_form,
1246                                      args, num_args);
1247     XtAddCallback(quit_btn, XtNcallback, plsel_quit_btn_callback, form);
1248
1249     /********************************************/
1250
1251     XtRealizeWidget(popup);
1252     X11_player_selection_randomize();
1253
1254     if (flags.randomall) {
1255         plsel_dialog_acceptvalues();
1256     } else {
1257         ps_selected = -1;
1258         nh_XtPopup(popup, (int) XtGrabExclusive, form);
1259         /* The callback will enable the event loop exit. */
1260         (void) x_event(EXIT_ON_EXIT);
1261     }
1262
1263     nh_XtPopdown(popup);
1264     XtDestroyWidget(popup);
1265
1266     if (plsel_race_radios)
1267         free(plsel_race_radios);
1268
1269     if (plsel_role_radios)
1270         free(plsel_role_radios);
1271
1272     if (plsel_gend_radios)
1273         free(plsel_gend_radios);
1274
1275     if (plsel_align_radios)
1276         free(plsel_align_radios);
1277
1278     if (ps_selected == PS_QUIT || program_state.done_hup) {
1279         clearlocks();
1280         X11_exit_nhwindows((char *) 0);
1281         nh_terminate(0);
1282     }
1283 }
1284
1285 /* Global functions ======================================================== */
1286 void
1287 X11_player_selection_prompts()
1288 {
1289     int num_roles, num_races, num_gends, num_algns, i, availcount, availindex;
1290     Widget popup, player_form;
1291     const char **choices;
1292     char qbuf[QBUFSZ], plbuf[QBUFSZ];
1293
1294 #ifdef XI18N
1295     char **jroles;
1296     char jtmp[256];
1297 #endif
1298
1299     /* avoid unnecessary prompts further down */
1300     rigid_role_checks();
1301
1302     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
1303                                    flags.initrace, flags.initgend,
1304                                    flags.initalign);
1305
1306     while (flags.initrole < 0) {
1307         if (flags.initrole == ROLE_RANDOM || flags.randomall) {
1308             flags.initrole = pick_role(flags.initrace, flags.initgend,
1309                                        flags.initalign, PICK_RANDOM);
1310             break;
1311         }
1312
1313         /* select a role */
1314         for (num_roles = 0; roles[num_roles].name.m; ++num_roles)
1315             continue;
1316         choices = (const char **) alloc(sizeof (char *) * num_roles);
1317         for (;;) {
1318             availcount = 0;
1319             for (i = 0; i < num_roles; i++) {
1320                 choices[i] = 0;
1321                 if (ok_role(i, flags.initrace, flags.initgend,
1322                             flags.initalign)) {
1323                     choices[i] = roles[i].name.m;
1324                     if (flags.initgend >= 0 && flags.female
1325                         && roles[i].name.f)
1326                         choices[i] = roles[i].name.f;
1327                     ++availcount;
1328                 }
1329             }
1330             if (availcount > 0)
1331                 break;
1332             else if (flags.initalign >= 0)
1333                 flags.initalign = -1; /* reset */
1334             else if (flags.initgend >= 0)
1335                 flags.initgend = -1;
1336             else if (flags.initrace >= 0)
1337                 flags.initrace = -1;
1338             else
1339                 panic("no available ROLE+race+gender+alignment combinations");
1340         }
1341 #if 0 /*JP*/
1342         Sprintf(qbuf, "Choose your %s Role", s_suffix(plbuf));
1343 #else
1344         Sprintf(qbuf, "%s\90E\8bÆ\82ð\91I\91ð\82µ\82Ä\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
1345 #endif
1346         popup =
1347 #if 0 /*JP*/
1348             make_menu("player_selection", qbuf, player_select_translations,
1349                       "quit", ps_quit, "random", ps_random, num_roles,
1350                       choices, (Widget **) 0, ps_select, &player_form);
1351 #else
1352             make_menu("player_selection", qbuf, player_select_translations,
1353                       "\94²\82¯\82é", ps_quit, "\83\89\83\93\83_\83\80", ps_random, num_roles,
1354                       choices, (Widget **) 0, ps_select, &player_form);
1355 #endif
1356
1357         ps_selected = -1;
1358         positionpopup(popup, FALSE);
1359         nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
1360
1361         /* The callbacks will enable the event loop exit. */
1362         (void) x_event(EXIT_ON_EXIT);
1363
1364         nh_XtPopdown(popup);
1365         XtDestroyWidget(popup);
1366         free((genericptr_t) choices), choices = 0;
1367
1368         if (ps_selected == PS_QUIT || program_state.done_hup) {
1369             clearlocks();
1370             X11_exit_nhwindows((char *) 0);
1371             nh_terminate(0);
1372         } else if (ps_selected == PS_RANDOM) {
1373             flags.initrole = ROLE_RANDOM;
1374         } else if (ps_selected < 0 || ps_selected >= num_roles) {
1375             panic("player_selection: bad role select value %d", ps_selected);
1376         } else {
1377             flags.initrole = ps_selected;
1378         }
1379     }
1380
1381     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
1382                                    flags.initrace, flags.initgend,
1383                                    flags.initalign);
1384
1385     while (!validrace(flags.initrole, flags.initrace)) {
1386         if (flags.initrace == ROLE_RANDOM || flags.randomall) {
1387             flags.initrace = pick_race(flags.initrole, flags.initgend,
1388                                        flags.initalign, PICK_RANDOM);
1389             break;
1390         }
1391         /* select a race */
1392         for (num_races = 0; races[num_races].noun; ++num_races)
1393             continue;
1394         choices = (const char **) alloc(sizeof(char *) * num_races);
1395         for (;;) {
1396             availcount = availindex = 0;
1397             for (i = 0; i < num_races; i++) {
1398                 choices[i] = 0;
1399                 if (ok_race(flags.initrole, i, flags.initgend,
1400                             flags.initalign)) {
1401                     choices[i] = races[i].noun;
1402                     ++availcount;
1403                     availindex = i; /* used iff only one */
1404                 }
1405             }
1406             if (availcount > 0)
1407                 break;
1408             else if (flags.initalign >= 0)
1409                 flags.initalign = -1; /* reset */
1410             else if (flags.initgend >= 0)
1411                 flags.initgend = -1;
1412             else
1413                 panic("no available role+RACE+gender+alignment combinations");
1414         }
1415
1416         if (availcount == 1) {
1417             flags.initrace = availindex;
1418             free((genericptr_t) choices), choices = 0;
1419         } else {
1420 #if 0 /*JP*/
1421             Sprintf(qbuf, "Pick your %s race", s_suffix(plbuf));
1422 #else
1423             Sprintf(qbuf, "%s\8eí\91°\82ð\91I\91ð\82µ\82Ä\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
1424 #endif
1425 #if 0 /*JP*/
1426             popup =
1427                 make_menu("race_selection", qbuf, race_select_translations,
1428                           "quit", ps_quit, "random", ps_random, num_races,
1429                           choices, (Widget **) 0, ps_select, &player_form);
1430 #else
1431             popup =
1432                 make_menu("race_selection", qbuf, race_select_translations,
1433                           "\94²\82¯\82é", ps_quit, "\83\89\83\93\83_\83\80", ps_random, num_races,
1434                           choices, (Widget **) 0, ps_select, &player_form);
1435 #endif
1436
1437             ps_selected = -1;
1438             positionpopup(popup, FALSE);
1439             nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
1440
1441             /* The callbacks will enable the event loop exit. */
1442             (void) x_event(EXIT_ON_EXIT);
1443
1444             nh_XtPopdown(popup);
1445             XtDestroyWidget(popup);
1446             free((genericptr_t) choices), choices = 0;
1447
1448             if (ps_selected == PS_QUIT || program_state.done_hup) {
1449                 clearlocks();
1450                 X11_exit_nhwindows((char *) 0);
1451                 nh_terminate(0);
1452             } else if (ps_selected == PS_RANDOM) {
1453                 flags.initrace = ROLE_RANDOM;
1454             } else if (ps_selected < 0 || ps_selected >= num_races) {
1455                 panic("player_selection: bad race select value %d",
1456                       ps_selected);
1457             } else {
1458                 flags.initrace = ps_selected;
1459             }
1460         } /* more than one race choice available */
1461     }
1462
1463     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
1464                                    flags.initrace, flags.initgend,
1465                                    flags.initalign);
1466
1467     while (!validgend(flags.initrole, flags.initrace, flags.initgend)) {
1468         if (flags.initgend == ROLE_RANDOM || flags.randomall) {
1469             flags.initgend = pick_gend(flags.initrole, flags.initrace,
1470                                        flags.initalign, PICK_RANDOM);
1471             break;
1472         }
1473         /* select a gender */
1474         num_gends = 2; /* genders[2] isn't allowed */
1475         choices = (const char **) alloc(sizeof(char *) * num_gends);
1476         for (;;) {
1477             availcount = availindex = 0;
1478             for (i = 0; i < num_gends; i++) {
1479                 choices[i] = 0;
1480                 if (ok_gend(flags.initrole, flags.initrace, i,
1481                             flags.initalign)) {
1482                     choices[i] = genders[i].adj;
1483                     ++availcount;
1484                     availindex = i; /* used iff only one */
1485                 }
1486             }
1487             if (availcount > 0)
1488                 break;
1489             else if (flags.initalign >= 0)
1490                 flags.initalign = -1; /* reset */
1491             else
1492                 panic("no available role+race+GENDER+alignment combinations");
1493         }
1494
1495         if (availcount == 1) {
1496             flags.initgend = availindex;
1497             free((genericptr_t) choices), choices = 0;
1498         } else {
1499 /*JP
1500             Sprintf(qbuf, "Your %s gender?", s_suffix(plbuf));
1501 */
1502             Sprintf(qbuf, "%s\90«\95Ê\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
1503 #if 0 /*JP*/
1504             popup =
1505                 make_menu("gender_selection", qbuf, gend_select_translations,
1506                           "quit", ps_quit, "random", ps_random, num_gends,
1507                           choices, (Widget **) 0, ps_select, &player_form);
1508 #else
1509             popup =
1510                 make_menu("gender_selection", qbuf, gend_select_translations,
1511                           "\94²\82¯\82é", ps_quit, "\83\89\83\93\83_\83\80", ps_random, num_gends,
1512                           choices, (Widget **) 0, ps_select, &player_form);
1513 #endif
1514
1515             ps_selected = -1;
1516             positionpopup(popup, FALSE);
1517             nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
1518
1519             /* The callbacks will enable the event loop exit. */
1520             (void) x_event(EXIT_ON_EXIT);
1521
1522             nh_XtPopdown(popup);
1523             XtDestroyWidget(popup);
1524             free((genericptr_t) choices), choices = 0;
1525
1526             if (ps_selected == PS_QUIT || program_state.done_hup) {
1527                 clearlocks();
1528                 X11_exit_nhwindows((char *) 0);
1529                 nh_terminate(0);
1530             } else if (ps_selected == PS_RANDOM) {
1531                 flags.initgend = ROLE_RANDOM;
1532             } else if (ps_selected < 0 || ps_selected >= num_gends) {
1533                 panic("player_selection: bad gender select value %d",
1534                       ps_selected);
1535             } else {
1536                 flags.initgend = ps_selected;
1537             }
1538         } /* more than one gender choice available */
1539     }
1540
1541     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
1542                                    flags.initrace, flags.initgend,
1543                                    flags.initalign);
1544
1545     while (!validalign(flags.initrole, flags.initrace, flags.initalign)) {
1546         if (flags.initalign == ROLE_RANDOM || flags.randomall) {
1547             flags.initalign = pick_align(flags.initrole, flags.initrace,
1548                                          flags.initgend, PICK_RANDOM);
1549             break;
1550         }
1551         /* select an alignment */
1552         num_algns = 3; /* aligns[3] isn't allowed */
1553         choices = (const char **) alloc(sizeof(char *) * num_algns);
1554         for (;;) {
1555             availcount = availindex = 0;
1556             for (i = 0; i < num_algns; i++) {
1557                 choices[i] = 0;
1558                 if (ok_align(flags.initrole, flags.initrace, flags.initgend,
1559                              i)) {
1560                     choices[i] = aligns[i].adj;
1561                     ++availcount;
1562                     availindex = i; /* used iff only one */
1563                 }
1564             }
1565             if (availcount > 0)
1566                 break;
1567             else
1568                 panic("no available role+race+gender+ALIGNMENT combinations");
1569         }
1570
1571         if (availcount == 1) {
1572             flags.initalign = availindex;
1573             free((genericptr_t) choices), choices = 0;
1574         } else {
1575 /*JP
1576             Sprintf(qbuf, "Your %s alignment?", s_suffix(plbuf));
1577 */
1578             Sprintf(qbuf, "%s\91®\90«\82ð\91I\91ð\82µ\82Ä\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
1579 #if 0 /*JP*/
1580             popup = make_menu("alignment_selection", qbuf,
1581                               algn_select_translations, "quit", ps_quit,
1582                               "random", ps_random, num_algns, choices,
1583                               (Widget **) 0, ps_select, &player_form);
1584 #else
1585             popup = make_menu("alignment_selection", qbuf,
1586                               algn_select_translations, "\94²\82¯\82é", ps_quit,
1587                               "\83\89\83\93\83_\83\80", ps_random, num_algns, choices,
1588                               (Widget **) 0, ps_select, &player_form);
1589 #endif
1590
1591             ps_selected = -1;
1592             positionpopup(popup, FALSE);
1593             nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
1594
1595             /* The callbacks will enable the event loop exit. */
1596             (void) x_event(EXIT_ON_EXIT);
1597
1598             nh_XtPopdown(popup);
1599             XtDestroyWidget(popup);
1600             free((genericptr_t) choices), choices = 0;
1601
1602             if (ps_selected == PS_QUIT || program_state.done_hup) {
1603                 clearlocks();
1604                 X11_exit_nhwindows((char *) 0);
1605                 nh_terminate(0);
1606             } else if (ps_selected == PS_RANDOM) {
1607                 flags.initalign = ROLE_RANDOM;
1608             } else if (ps_selected < 0 || ps_selected >= num_algns) {
1609                 panic("player_selection: bad alignment select value %d",
1610                       ps_selected);
1611             } else {
1612                 flags.initalign = ps_selected;
1613             }
1614         } /* more than one alignment choice available */
1615     }
1616 }
1617
1618 void
1619 X11_player_selection()
1620 {
1621     if (iflags.wc_player_selection == VIA_DIALOG) {
1622         if (!*plname) {
1623 #ifdef UNIX
1624             char *defplname = get_login_name();
1625 #else
1626             char *defplname = (char *)0;
1627 #endif
1628             (void) strncpy(plname, defplname ? defplname : "Mumbles",
1629                            sizeof plname - 1);
1630             plname[sizeof plname - 1] = '\0';
1631             iflags.renameinprogress = TRUE;
1632         }
1633         X11_player_selection_dialog();
1634     } else { /* iflags.wc_player_selection == VIA_PROMPTS */
1635         X11_player_selection_prompts();
1636     }
1637 }
1638
1639 /* called by core to have the player pick an extended command */
1640 int
1641 X11_get_ext_cmd()
1642 {
1643     if (iflags.extmenu != ec_full_list) {
1644         /* player has toggled the 'extmenu' option, toss the old widgets */
1645         if (extended_commands)
1646             release_extended_cmds(); /* will set extended_commands to Null */
1647         ec_full_list = iflags.extmenu;
1648     }
1649     if (!extended_commands)
1650         init_extended_commands_popup();
1651
1652     extended_cmd_selected = -1; /* reset selected value */
1653     ec_scroll_to_view(-1); /* force scroll bar to top */
1654
1655     positionpopup(extended_command_popup, FALSE); /* center on cursor */
1656     nh_XtPopup(extended_command_popup, (int) XtGrabExclusive,
1657                extended_command_form);
1658
1659     /* The callbacks will enable the event loop exit. */
1660     (void) x_event(EXIT_ON_EXIT);
1661
1662     if (extended_cmd_selected < 0)
1663         return -1;
1664     return command_indx[extended_cmd_selected];
1665 }
1666
1667 void
1668 release_extended_cmds()
1669 {
1670     if (extended_commands) {
1671         XtDestroyWidget(extended_command_popup), extended_command_popup = 0;
1672         free((genericptr_t) extended_commands), extended_commands = 0;
1673         free((genericptr_t) command_list), command_list = (const char **) 0;
1674         free((genericptr_t) command_indx), command_indx = (short *) 0;
1675     }
1676 }
1677
1678 /* End global functions =================================================== */
1679
1680 /* Extended Command ------------------------------------------------------- */
1681 /* ARGSUSED */
1682 static void
1683 extend_select(w, client_data, call_data)
1684 Widget w;
1685 XtPointer client_data, call_data;
1686 {
1687     int selected = (int) (ptrdiff_t) client_data;
1688
1689     nhUse(w);
1690     nhUse(call_data);
1691
1692     if (extended_cmd_selected != selected) {
1693         /* visibly deselect old one */
1694         if (extended_cmd_selected >= 0)
1695             swap_fg_bg(extended_commands[extended_cmd_selected]);
1696
1697         /* select new one */
1698         swap_fg_bg(extended_commands[selected]);
1699         extended_cmd_selected = selected;
1700     }
1701
1702     nh_XtPopdown(extended_command_popup);
1703     /* reset colors while popped down */
1704     swap_fg_bg(extended_commands[extended_cmd_selected]);
1705     ec_active = FALSE;
1706     exit_x_event = TRUE; /* leave event loop */
1707 }
1708
1709 /* ARGSUSED */
1710 static void
1711 extend_dismiss(w, client_data, call_data)
1712 Widget w;
1713 XtPointer client_data, call_data;
1714 {
1715     nhUse(w);
1716     nhUse(client_data);
1717     nhUse(call_data);
1718
1719     ec_dismiss();
1720 }
1721
1722 /* ARGSUSED */
1723 static void
1724 extend_help(w, client_data, call_data)
1725 Widget w;
1726 XtPointer client_data, call_data;
1727 {
1728     nhUse(w);
1729     nhUse(client_data);
1730     nhUse(call_data);
1731
1732     /* We might need to make it known that we already have one listed. */
1733     (void) doextlist();
1734 }
1735
1736 /* ARGSUSED */
1737 void
1738 ec_delete(w, event, params, num_params)
1739 Widget w;
1740 XEvent *event;
1741 String *params;
1742 Cardinal *num_params;
1743 {
1744     if (w == extended_command_popup) {
1745         ec_dismiss();
1746     } else {
1747         popup_delete(w, event, params, num_params);
1748     }
1749 }
1750
1751 /* ARGSUSED */
1752 static void
1753 popup_delete(w, event, params, num_params)
1754 Widget w;
1755 XEvent *event;
1756 String *params;
1757 Cardinal *num_params;
1758 {
1759     nhUse(event);
1760     nhUse(params);
1761     nhUse(num_params);
1762
1763     ps_selected = PS_QUIT;
1764     nh_XtPopdown(w);
1765     exit_x_event = TRUE; /* leave event loop */
1766 }
1767
1768 static void
1769 ec_dismiss()
1770 {
1771     /* unselect while still visible */
1772     if (extended_cmd_selected >= 0)
1773         swap_fg_bg(extended_commands[extended_cmd_selected]);
1774     extended_cmd_selected = -1; /* dismiss */
1775     nh_XtPopdown(extended_command_popup);
1776     ec_active = FALSE;
1777     exit_x_event = TRUE; /* leave event loop */
1778 }
1779
1780 /* scroll the extended command menu if necessary
1781    so that choices extended_cmd_selected through ec_indx will be visible */
1782 static void
1783 ec_scroll_to_view(ec_indx)
1784 int ec_indx; /* might be greater than extended_cmd_selected */
1785 {
1786     Widget viewport, scrollbar, tmpw;
1787     Arg args[5];
1788     Cardinal num_args;
1789     Position lo_y, hi_y; /* ext cmd label y */
1790     float s_shown, s_top; /* scrollbar pos */
1791     float s_min, s_max;
1792     Dimension h, hh, wh, vh; /* widget and viewport heights */
1793     Dimension border_width;
1794     int distance = 0;
1795     boolean force_top = (ec_indx < 0);
1796
1797     /*
1798      * If the extended command menu needs to be scrolled in order to move
1799      * either the highlighted entry (extended_cmd_selected) or the target
1800      * entry (ec_indx) into view, we want to make both end up visible.
1801      * [Highligthed one is the first matching entry when the user types
1802      * something, such as "adjust" after typing 'a', and will be chosen
1803      * by pressing <return>.  Target entry is one past the last matching
1804      * entry (or last matching entry itself if at end of command list),
1805      * showing the user the other potential matches so far.]
1806      *
1807      * If that's not possible (maybe menu has been resized so that it's
1808      * too small), the highlighted entry takes precedence and the target
1809      * will be decremented until close enough to fit.
1810      */
1811
1812     if (force_top)
1813         ec_indx = 0;
1814
1815     /* get viewport and scrollbar widgets */
1816     tmpw = extended_commands[ec_indx];
1817     viewport = XtParent(tmpw);
1818     do {
1819         scrollbar = XtNameToWidget(tmpw, "*vertical");
1820         if (scrollbar)
1821             break;
1822         tmpw = XtParent(tmpw);
1823     } while (tmpw);
1824
1825     if (scrollbar && viewport) {
1826         /* get selected ext command label y position and height */
1827         num_args = 0;
1828         XtSetArg(args[num_args], XtNy, &hi_y); num_args++;
1829         XtSetArg(args[num_args], XtNheight, &h); num_args++;
1830         XtSetArg(args[num_args], nhStr(XtNborderWidth), &border_width);
1831                                                                    num_args++;
1832         XtSetArg(args[num_args], nhStr(XtNdefaultDistance), &distance);
1833                                                                    num_args++;
1834         XtGetValues(extended_commands[ec_indx], args, num_args);
1835         if (distance < 1 || distance > 32766) /* defaultDistance is weird */
1836             distance = 4;
1837         /* vertical distance between top of one command widget and the next */
1838         hh = h + distance + 2 * border_width;
1839         /* location of the highlighted entry, if any */
1840         if (extended_cmd_selected >= 0) {
1841             XtSetArg(args[0], XtNy, &lo_y);
1842             XtGetValues(extended_commands[extended_cmd_selected], args, ONE);
1843         } else
1844             lo_y = hi_y;
1845
1846         /* get menu widget and viewport heights */
1847         XtSetArg(args[0], XtNheight, &wh);
1848         XtGetValues(tmpw, args, ONE);
1849         XtSetArg(args[0], XtNheight, &vh);
1850         XtGetValues(viewport, args, ONE);
1851
1852         /* widget might be too small if it has been resized or
1853            there are a very large number of ambiguous choices */
1854         if (hi_y - lo_y > wh) {
1855             ec_indx = extended_cmd_selected;
1856             if (wh > hh)
1857                 ec_indx += (wh / hh);
1858             XtSetArg(args[0], XtNy, &hi_y);
1859             XtGetValues(extended_commands[ec_indx], args, num_args);
1860         }
1861
1862         /* get scrollbar "height" and "top" position; floats between 0-1 */
1863         num_args = 0;
1864         XtSetArg(args[num_args], XtNshown, &s_shown); num_args++;
1865         XtSetArg(args[num_args], nhStr(XtNtopOfThumb), &s_top); num_args++;
1866         XtGetValues(scrollbar, args, num_args);
1867
1868         s_min = s_top * vh;
1869         s_max = (s_top + s_shown) * vh;
1870
1871         /* scroll if outside the view */
1872         if (force_top) {
1873             s_min = 0.0;
1874             XtCallCallbacks(scrollbar, XtNjumpProc, &s_min);
1875         } else if ((int) lo_y <= (int) s_min) {
1876             s_min = (float) (lo_y / (float) vh);
1877             XtCallCallbacks(scrollbar, XtNjumpProc, &s_min);
1878         } else if ((int) (hi_y + h) >= (int) s_max) {
1879             s_min = (float) ((hi_y + h) / (float) vh) - s_shown;
1880             XtCallCallbacks(scrollbar, XtNjumpProc, &s_min);
1881         }
1882     }
1883 }
1884
1885 /* decide whether extcmdlist[idx] should be part of extended commands menu */
1886 static boolean
1887 ignore_extcmd(idx)
1888 int idx;
1889 {
1890     /* #shell or #suspect might not be available;
1891        'extmenu' option controls whether we show full list
1892        or just the traditional extended commands */
1893     if ((extcmdlist[idx].flags & CMD_NOT_AVAILABLE) != 0
1894         || ((extcmdlist[idx].flags & AUTOCOMPLETE) == 0 && !ec_full_list)
1895         || strlen(extcmdlist[idx].ef_txt) < 2) /* ignore "#" and "?" */
1896         return TRUE;
1897
1898     return FALSE;
1899 }
1900
1901 /* ARGSUSED */
1902 void
1903 ec_key(w, event, params, num_params)
1904 Widget w;
1905 XEvent *event;
1906 String *params;
1907 Cardinal *num_params;
1908 {
1909     char ch;
1910     int i, pass;
1911     float shown, top;
1912     Arg arg[2];
1913     Widget hbar, vbar;
1914     XKeyEvent *xkey = (XKeyEvent *) event;
1915
1916     nhUse(params);
1917     nhUse(num_params);
1918
1919     ch = key_event_to_char(xkey);
1920
1921     if (ch == '\0') { /* don't accept nul char/modifier event */
1922         /* don't beep */
1923         return;
1924     } else if (ch == '?') {
1925         extend_help((Widget) 0, (XtPointer) 0, (XtPointer) 0);
1926         return;
1927     } else if (index("\033\n\r", ch)) {
1928         if (ch == '\033') {
1929             /* unselect while still visible */
1930             if (extended_cmd_selected >= 0)
1931                 swap_fg_bg(extended_commands[extended_cmd_selected]);
1932             extended_cmd_selected = -1; /* dismiss */
1933         }
1934
1935         nh_XtPopdown(extended_command_popup);
1936         /* unselect while invisible */
1937         if (extended_cmd_selected >= 0)
1938             swap_fg_bg(extended_commands[extended_cmd_selected]);
1939
1940         exit_x_event = TRUE; /* leave event loop */
1941         ec_active = FALSE;
1942         return;
1943     } else if (ch == MENU_FIRST_PAGE || ch == MENU_LAST_PAGE) {
1944         hbar = vbar = (Widget) 0;
1945         find_scrollbars(w, &hbar, &vbar);
1946         if (vbar) {
1947             top = (ch == MENU_FIRST_PAGE) ? 0.0 : 1.0;
1948             XtCallCallbacks(vbar, XtNjumpProc, &top);
1949         }
1950         return;
1951     } else if (ch == MENU_NEXT_PAGE || ch == MENU_PREVIOUS_PAGE) {
1952         hbar = vbar = (Widget) 0;
1953         find_scrollbars(w, &hbar, &vbar);
1954         if (vbar) {
1955             XtSetArg(arg[0], nhStr(XtNshown), &shown);
1956             XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
1957             XtGetValues(vbar, arg, TWO);
1958             top += ((ch == MENU_NEXT_PAGE) ? shown : -shown);
1959             XtCallCallbacks(vbar, XtNjumpProc, &top);
1960         }
1961         return;
1962     }
1963
1964     /*
1965      * If too much time has elapsed, treat current key as starting a new
1966      * choice, otherwise it is a continuation of the choice in progress.
1967      * Extra letters might be needed to disambiguate between choices
1968      * ("ride" vs "rub", for instance), or player may just be typing in
1969      * the whole word.
1970      */
1971     if (ec_active && (xkey->time - ec_time) > 2500) /* 2.5 seconds */
1972         ec_active = FALSE;
1973
1974     if (!ec_active) {
1975         ec_nchars = 0;
1976         ec_active = TRUE;
1977     }
1978
1979     ec_time = xkey->time;
1980     ec_chars[ec_nchars++] = ch;
1981     if (ec_nchars >= EC_NCHARS)
1982         ec_nchars = EC_NCHARS - 1; /* don't overflow */
1983
1984     for (pass = 0; pass < 2; pass++) {
1985         if (pass == 1) {
1986             /* first pass finished, but no matching command was found */
1987             /* start a new one with the last char entered */
1988             if (extended_cmd_selected >= 0)
1989                 swap_fg_bg(extended_commands[extended_cmd_selected]);
1990             extended_cmd_selected = -1; /* dismiss */
1991             ec_chars[0] = ec_chars[ec_nchars - 1];
1992             ec_nchars = 1;
1993         }
1994         for (i = 0; command_list[i]; ++i) {
1995             if (!strncmp(ec_chars, command_list[i], ec_nchars)) {
1996                 if (extended_cmd_selected != i) {
1997                     /* I should use set() and unset() actions, but how do
1998                        I send the an action to the widget? */
1999                     if (extended_cmd_selected >= 0)
2000                         swap_fg_bg(extended_commands[extended_cmd_selected]);
2001                     extended_cmd_selected = i;
2002                     swap_fg_bg(extended_commands[extended_cmd_selected]);
2003                 }
2004                 /* advance to one past last matching entry, so that all
2005                    ambiguous choices, plus one to show thare aren't any
2006                    more such, will scroll into view */
2007                 do {
2008                     if (!command_list[i + 1])
2009                         break; /* end of list */
2010                     ++i;
2011                 } while (!strncmp(ec_chars, command_list[i], ec_nchars));
2012
2013                 ec_scroll_to_view(i);
2014                 return;
2015             }
2016         }
2017     }
2018 }
2019
2020 /*
2021  * Use our own home-brewed version menu because simpleMenu is designed to
2022  * be used from a menubox.
2023  */
2024 static void
2025 init_extended_commands_popup()
2026 {
2027     int i, j, num_commands, ignore_cmds = 0;
2028
2029     /* count commands */
2030     for (num_commands = 0; extcmdlist[num_commands].ef_txt; num_commands++)
2031         if (ignore_extcmd(num_commands))
2032             ++ignore_cmds;
2033
2034     j = num_commands - ignore_cmds;
2035     command_list = (const char **) alloc((unsigned) (j * sizeof (char *) + 1));
2036     command_indx = (short *) alloc((unsigned) (j * sizeof (short) + 1));
2037
2038     for (i = j = 0; i < num_commands; i++) {
2039         if (ignore_extcmd(i))
2040             continue;
2041         command_indx[j] = (short) i;
2042         command_list[j++] = extcmdlist[i].ef_txt;
2043     }
2044     command_list[j] = (char *) 0;
2045     command_indx[j] = -1;
2046     num_commands = j;
2047
2048 #if 0 /*JP*/
2049     extended_command_popup =
2050         make_menu("extended_commands", "Extended Commands",
2051                   extended_command_translations, "dismiss", extend_dismiss,
2052                   "help", extend_help, num_commands, command_list,
2053                   &extended_commands, extend_select, &extended_command_form);
2054 #else
2055     extended_command_popup =
2056         make_menu("extended_commands", "\8ag\92£\83R\83}\83\93\83h",
2057                   extended_command_translations, "\8eæ\8fÁ", extend_dismiss,
2058                   "\83w\83\8b\83v", extend_help, num_commands, command_list,
2059                   &extended_commands, extend_select, &extended_command_form);
2060 #endif
2061 }
2062
2063 /* ------------------------------------------------------------------------ */
2064
2065 /*
2066  * Create a popup widget of the following form:
2067  *
2068  *                    popup_label
2069  *              ----------- ------------
2070  *              |left_name| |right_name|
2071  *              ----------- ------------
2072  *              ------------------------
2073  *              |       name1          |
2074  *              ------------------------
2075  *              ------------------------
2076  *              |       name2          |
2077  *              ------------------------
2078  *                        .
2079  *                        .
2080  *              ------------------------
2081  *              |       nameN          |
2082  *              ------------------------
2083  */
2084 static Widget
2085 make_menu(popup_name, popup_label, popup_translations, left_name,
2086           left_callback, right_name, right_callback, num_names, widget_names,
2087           command_widgets, name_callback, formp)
2088 const char *popup_name;
2089 const char *popup_label;
2090 const char *popup_translations;
2091 const char *left_name;
2092 XtCallbackProc left_callback;
2093 const char *right_name;
2094 XtCallbackProc right_callback;
2095 int num_names;
2096 const char **widget_names; /* return array of command widgets */
2097 Widget **command_widgets;
2098 XtCallbackProc name_callback;
2099 Widget *formp; /* return */
2100 {
2101     Widget popup, popform, form, label, above, left, right, view;
2102     Widget *commands, *curr;
2103     int i;
2104     Arg args[12];
2105     Cardinal num_args;
2106     Dimension width, other_width, max_width, border_width,
2107               height, cumulative_height, screen_height;
2108     int distance, skip;
2109     char btnname[BUFSZ];
2110
2111     commands = (Widget *) alloc((unsigned) num_names * sizeof (Widget));
2112
2113     num_args = 0;
2114     XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
2115     XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
2116     popup = XtCreatePopupShell(popup_name, transientShellWidgetClass,
2117                                toplevel, args, num_args);
2118     XtOverrideTranslations(
2119         popup, XtParseTranslationTable("<Message>WM_PROTOCOLS: ec_delete()"));
2120
2121     num_args = 0;
2122     XtSetArg(args[num_args], XtNtranslations,
2123              XtParseTranslationTable(popup_translations)); num_args++;
2124     XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
2125     XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++;
2126     popform = XtCreateManagedWidget("topmenuform", formWidgetClass, popup,
2127                                     args, num_args);
2128
2129
2130     num_args = 0;
2131     XtSetArg(args[num_args], XtNforceBars, False); num_args++;
2132     XtSetArg(args[num_args], XtNallowVert, True); num_args++;
2133     /*XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;*/
2134     XtSetArg(args[num_args], nhStr(XtNuseBottom), True); num_args++;
2135     XtSetArg(args[num_args], nhStr(XtNuseRight), True); num_args++;
2136     XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
2137     XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++;
2138     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
2139     XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
2140     XtSetArg(args[num_args], XtNtranslations,
2141              XtParseTranslationTable(popup_translations)); num_args++;
2142     view = XtCreateManagedWidget("menuformview", viewportWidgetClass, popform,
2143                                  args, num_args);
2144
2145     num_args = 0;
2146     XtSetArg(args[num_args], XtNtranslations,
2147              XtParseTranslationTable(popup_translations)); num_args++;
2148     XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
2149     XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++;
2150     *formp = form = XtCreateManagedWidget("menuform", formWidgetClass, view,
2151                                           args, num_args);
2152
2153     /*
2154      * Get the default distance between objects in the viewport widget.
2155      * (Something is fishy here:  'distance' ends up being 0 but there
2156      * is a non-zero gap between the borders of the internal widgets.
2157      * It matches exactly the default value of 4 for defaultDistance.)
2158      */
2159     num_args = 0;
2160     XtSetArg(args[num_args], nhStr(XtNdefaultDistance), &distance); num_args++;
2161     XtSetArg(args[num_args], nhStr(XtNborderWidth), &border_width); num_args++;
2162     XtGetValues(view, args, num_args);
2163     if (distance < 1 || distance > 32766)
2164         distance = 4;
2165
2166     /*
2167      * Create the label.
2168      */
2169     num_args = 0;
2170 #if defined(X11R6) && defined(XI18N)
2171     XtSetArg(args[num_args], XtNinternational, True); num_args++;
2172 #endif
2173     XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
2174     label = XtCreateManagedWidget(popup_label, labelWidgetClass, form, args,
2175                                   num_args);
2176
2177     cumulative_height = 0;
2178     XtSetArg(args[0], XtNheight, &height);
2179     XtGetValues(label, args, ONE);
2180     cumulative_height += distance + height; /* no border for label */
2181
2182     /*
2183      * Create the left button.
2184      */
2185     num_args = 0;
2186     XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++;
2187     XtSetArg(args[num_args], nhStr(XtNlabel), left_name); num_args++;
2188 #if 0
2189     XtSetArg(args[num_args], nhStr(XtNshapeStyle),
2190                               XmuShapeRoundedRectangle); num_args++;
2191 #endif
2192     Sprintf(btnname, "btn_%s", left_name);
2193     left = XtCreateManagedWidget(btnname, commandWidgetClass, form, args,
2194                                  num_args);
2195     XtAddCallback(left, XtNcallback, left_callback, (XtPointer) 0);
2196     skip = (distance < 4) ? 8 : 2 * distance;
2197
2198     num_args = 0;
2199     XtSetArg(args[0], XtNheight, &height);
2200     XtGetValues(left, args, ONE);
2201     cumulative_height += distance + height + 2 * border_width;
2202
2203     /*
2204      * Create right button.
2205      */
2206     num_args = 0;
2207     XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++;
2208     XtSetArg(args[num_args], nhStr(XtNhorizDistance), skip); num_args++;
2209     XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++;
2210     XtSetArg(args[num_args], nhStr(XtNlabel), right_name); num_args++;
2211 #if 0
2212     XtSetArg(args[num_args], nhStr(XtNshapeStyle),
2213                               XmuShapeRoundedRectangle); num_args++;
2214 #endif
2215 #if defined(X11R6) && defined(XI18N)
2216     XtSetArg(args[num_args], XtNinternational, True); num_args++;
2217 #endif
2218     Sprintf(btnname, "btn_%s", right_name);
2219     right = XtCreateManagedWidget(btnname, commandWidgetClass, form, args,
2220                                   num_args);
2221     XtAddCallback(right, XtNcallback, right_callback, (XtPointer) 0);
2222
2223     XtInstallAccelerators(form, left);
2224     XtInstallAccelerators(form, right);
2225
2226     /*
2227      * Create and place the command widgets.
2228      */
2229     for (i = 0, above = left, curr = commands; i < num_names; i++) {
2230         if (!widget_names[i])
2231             continue;
2232         num_args = 0;
2233         XtSetArg(args[num_args], XtNtranslations,
2234                  XtParseTranslationTable(popup_entry_translations)); num_args++;
2235         XtSetArg(args[num_args], nhStr(XtNfromVert), above); num_args++;
2236         if (above == left) {
2237             /* if first, we are farther apart */
2238             XtSetArg(args[num_args], nhStr(XtNvertDistance), skip); num_args++;
2239             cumulative_height += skip;
2240         } else
2241             cumulative_height += distance;
2242         cumulative_height += height + 2 * border_width;
2243
2244 #if defined(X11R6) && defined(XI18N)
2245         XtSetArg(args[num_args], XtNinternational, True);
2246         num_args++;
2247 #endif
2248         *curr = XtCreateManagedWidget(widget_names[i], commandWidgetClass,
2249                                       form, args, num_args);
2250         XtAddCallback(*curr, XtNcallback, name_callback,
2251                       (XtPointer) (ptrdiff_t) i);
2252         above = *curr++;
2253     }
2254     cumulative_height += distance; /* space at bottom of form */
2255
2256     /*
2257      * Now find the largest width.  Start with width of left + right buttons
2258      * ('dismiss' + 'help' or 'quit' + 'random'), since they are adjacent.
2259      */
2260     XtSetArg(args[0], XtNwidth, &max_width);
2261     XtGetValues(left, args, ONE);
2262     XtSetArg(args[0], XtNwidth, &width);
2263     XtGetValues(right, args, ONE);
2264     /* doesn't count leftmost 'distance + border_width' and
2265        rightmost 'border_width + distance' since all entries have those */
2266     max_width = max_width + border_width + skip + border_width + width;
2267
2268     /* Next, the title. */
2269     XtSetArg(args[0], XtNwidth, &width);
2270     XtGetValues(label, args, ONE);
2271     if (width > max_width)
2272         max_width = width;
2273
2274     /* Finally, the commands. */
2275     for (i = 0, curr = commands; i < num_names; i++) {
2276         if (!widget_names[i])
2277             continue;
2278         XtSetArg(args[0], XtNwidth, &width);
2279         XtGetValues(*curr, args, ONE);
2280         if (width > max_width)
2281             max_width = width;
2282         curr++;
2283     }
2284
2285     /*
2286      * Re-do the two side-by-side widgets to take up half the width each.
2287      *
2288      * With max_width and skip both having even values, we never have to
2289      * tweak left or right to maybe be one pixel wider than the other.
2290      */
2291     if (max_width % 2)
2292         ++max_width;
2293     XtSetArg(args[0], XtNwidth, &width);
2294     XtGetValues(left, args, ONE);
2295     XtSetArg(args[0], XtNwidth, &other_width);
2296     XtGetValues(right, args, ONE);
2297     if (width + border_width + skip / 2 < max_width / 2
2298         && other_width + border_width + skip / 2 < max_width / 2) {
2299         /* both are narrower than half */
2300         width = other_width = max_width / 2 - border_width - skip / 2;
2301         XtSetArg(args[0], XtNwidth, width);
2302         XtSetValues(left, args, ONE);
2303         XtSetArg(args[0], XtNwidth, other_width);
2304         XtSetValues(right, args, ONE);
2305     } else if (width + border_width + skip / 2 < max_width / 2) {
2306         /* 'other_width' (right) is half or more */
2307         width = max_width - other_width - 2 * border_width - skip;
2308         XtSetArg(args[0], XtNwidth, width);
2309         XtSetValues(left, args, ONE);
2310     } else if (other_width + border_width + skip / 2 < max_width / 2) {
2311         /* 'width' (left) is half or more */
2312         other_width = max_width - width - 2 * border_width - skip;
2313         XtSetArg(args[0], XtNwidth, other_width);
2314         XtSetValues(right, args, ONE);
2315     } else {
2316         ; /* both are exactly half... */
2317     }
2318
2319     /*
2320      * Finally, set all of the single line widgets to the largest width.
2321      */
2322     XtSetArg(args[0], XtNwidth, max_width);
2323     XtSetValues(label, args, ONE);
2324
2325     for (i = 0, curr = commands; i < num_names; i++) {
2326         if (!widget_names[i])
2327             continue;
2328         XtSetArg(args[0], XtNwidth, max_width);
2329         XtSetValues(*curr, args, ONE);
2330         curr++;
2331     }
2332
2333     if (command_widgets)
2334         *command_widgets = commands;
2335     else
2336         free((char *) commands);
2337
2338     /*
2339      * If the menu's complete height is too big for the display,
2340      * forcing the height to be smaller will cause the vertical
2341      * scroll bar (enabled but not forced above) to be included.
2342      */
2343     screen_height = XHeightOfScreen(XtScreen(popup));
2344     screen_height -= appResources.extcmd_height_delta; /* NetHack.ad */
2345     if (cumulative_height >= screen_height) {
2346         /* 25 is a guesstimate for scrollbar width;
2347            window manager might override the request for y==1 */
2348         num_args = 0;
2349         XtSetArg(args[num_args], XtNy, 1); num_args++;
2350         XtSetArg(args[num_args], XtNwidth, max_width + 25); num_args++;
2351         XtSetArg(args[num_args], XtNheight, screen_height - 1); num_args++;
2352         XtSetValues(popup, args, num_args);
2353     }
2354     XtRealizeWidget(popup);
2355     XSetWMProtocols(XtDisplay(popup), XtWindow(popup), &wm_delete_window, 1);
2356
2357     /* during role selection, highlight "random" as pre-selected choice */
2358     if (right_callback == ps_random && index(ps_randchars, '\n'))
2359         swap_fg_bg(right);
2360
2361     return popup;
2362 }
2363
2364 /*winmisc.c*/