OSDN Git Service

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