OSDN Git Service

patch for win/X11
[jnethack/source.git] / win / X11 / winmisc.c
1 /* NetHack 3.6  winmisc.c       $NHDT-Date: 1432512807 2015/05/25 00:13:27 $  $NHDT-Branch: master $:$NHDT-Revision: 1.12 $ */
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/Cardinals.h>
29 #include <X11/Xos.h> /* for index() */
30 #include <X11/Xatom.h>
31
32 #ifdef PRESERVE_NO_SYSV
33 #ifdef SYSV
34 #undef SYSV
35 #endif
36 #undef PRESERVE_NO_SYSV
37 #endif
38
39 #include "hack.h"
40 #include "func_tab.h"
41 #include "winX.h"
42
43 static Widget extended_command_popup = 0;
44 static Widget extended_command_form;
45 static Widget *extended_commands = 0;
46 static int extended_command_selected; /* index of the selected command; */
47 static int ps_selected;               /* index of selected role */
48 #define PS_RANDOM (-50)
49 #define PS_QUIT (-75)
50 static const char ps_randchars[] = "*@";
51 static const char ps_quitchars[] = "\033qQ";
52
53 #define EC_NCHARS 32
54 static boolean ec_active = FALSE;
55 static int ec_nchars = 0;
56 static char ec_chars[EC_NCHARS];
57 static Time ec_time;
58
59 static const char extended_command_translations[] = "#override\n\
60      <Key>: ec_key()";
61
62 static const char player_select_translations[] = "#override\n\
63      <Key>: ps_key()";
64 static const char race_select_translations[] = "#override\n\
65      <Key>: race_key()";
66 static const char gend_select_translations[] = "#override\n\
67      <Key>: gend_key()";
68 static const char algn_select_translations[] = "#override\n\
69      <Key>: algn_key()";
70
71 static void FDECL(popup_delete, (Widget, XEvent *, String *, Cardinal *));
72 static void NDECL(ec_dismiss);
73 static Widget FDECL(make_menu,
74                     (const char *, const char *, const char *, const char *,
75                      XtCallbackProc, const char *, XtCallbackProc, int,
76                      const char **, Widget **, XtCallbackProc, Widget *));
77 static void NDECL(init_extended_commands_popup);
78 static void FDECL(ps_quit, (Widget, XtPointer, XtPointer));
79 static void FDECL(ps_random, (Widget, XtPointer, XtPointer));
80 static void FDECL(ps_select, (Widget, XtPointer, XtPointer));
81
82 /* Player Selection --------------------------------------------------------
83  */
84 /* ARGSUSED */
85 static void
86 ps_quit(w, client_data, call_data)
87 Widget w;
88 XtPointer client_data, call_data;
89 {
90     nhUse(w);
91     nhUse(client_data);
92     nhUse(call_data);
93
94     ps_selected = PS_QUIT;
95     exit_x_event = TRUE; /* leave event loop */
96 }
97
98 /* ARGSUSED */
99 static void
100 ps_random(w, client_data, call_data)
101 Widget w;
102 XtPointer client_data, call_data;
103 {
104     nhUse(w);
105     nhUse(client_data);
106     nhUse(call_data);
107
108     ps_selected = PS_RANDOM;
109     exit_x_event = TRUE; /* leave event loop */
110 }
111
112 /* ARGSUSED */
113 static void
114 ps_select(w, client_data, call_data)
115 Widget w;
116 XtPointer client_data, call_data;
117 {
118     nhUse(w);
119     nhUse(call_data);
120
121     ps_selected = (int) client_data;
122     exit_x_event = TRUE; /* leave event loop */
123 }
124
125 /* ARGSUSED */
126 void
127 ps_key(w, event, params, num_params)
128 Widget w;
129 XEvent *event;
130 String *params;
131 Cardinal *num_params;
132 {
133     char ch, *mark;
134     char rolechars[QBUFSZ];
135     int i;
136
137     nhUse(w);
138     nhUse(params);
139     nhUse(num_params);
140
141     (void) memset(rolechars, '\0', sizeof rolechars); /* for index() */
142     for (i = 0; roles[i].name.m; ++i) {
143         ch = lowc(*roles[i].name.m);
144         /* if (flags.female && roles[i].name.f) ch = lowc(*roles[i].name.f);
145          */
146         /* this supports at most two roles with the same first letter */
147         if (index(rolechars, ch))
148             ch = highc(ch);
149         rolechars[i] = ch;
150     }
151     ch = key_event_to_char((XKeyEvent *) event);
152     if (ch == '\0') { /* don't accept nul char/modifier event */
153         /* don't beep */
154         return;
155     }
156     mark = index(rolechars, ch);
157     if (!mark)
158         mark = index(rolechars, lowc(ch));
159     if (!mark)
160         mark = index(rolechars, highc(ch));
161     if (!mark) {
162         if (index(ps_randchars, ch))
163             ps_selected = PS_RANDOM;
164         else if (index(ps_quitchars, ch))
165             ps_selected = PS_QUIT;
166         else {
167             X11_nhbell(); /* no such class */
168             return;
169         }
170     } else
171         ps_selected = (int) (mark - rolechars);
172     exit_x_event = TRUE;
173 }
174
175 /* ARGSUSED */
176 void
177 race_key(w, event, params, num_params)
178 Widget w;
179 XEvent *event;
180 String *params;
181 Cardinal *num_params;
182 {
183     char ch, *mark;
184     char racechars[QBUFSZ];
185     int i;
186
187     nhUse(w);
188     nhUse(params);
189     nhUse(num_params);
190
191     (void) memset(racechars, '\0', sizeof racechars); /* for index() */
192     for (i = 0; races[i].noun; ++i) {
193         ch = lowc(*races[i].noun);
194         /* this supports at most two races with the same first letter */
195         if (index(racechars, ch))
196             ch = highc(ch);
197         racechars[i] = ch;
198     }
199     ch = key_event_to_char((XKeyEvent *) event);
200     if (ch == '\0') { /* don't accept nul char/modifier event */
201         /* don't beep */
202         return;
203     }
204     mark = index(racechars, ch);
205     if (!mark)
206         mark = index(racechars, lowc(ch));
207     if (!mark)
208         mark = index(racechars, highc(ch));
209     if (!mark) {
210         if (index(ps_randchars, ch))
211             ps_selected = PS_RANDOM;
212         else if (index(ps_quitchars, ch))
213             ps_selected = PS_QUIT;
214         else {
215             X11_nhbell(); /* no such race */
216             return;
217         }
218     } else
219         ps_selected = (int) (mark - racechars);
220     exit_x_event = TRUE;
221 }
222
223 /* ARGSUSED */
224 void
225 gend_key(w, event, params, num_params)
226 Widget w;
227 XEvent *event;
228 String *params;
229 Cardinal *num_params;
230 {
231     char ch, *mark;
232     static char gendchars[] = "mf";
233
234     nhUse(w);
235     nhUse(params);
236     nhUse(num_params);
237
238     ch = key_event_to_char((XKeyEvent *) event);
239     if (ch == '\0') { /* don't accept nul char/modifier event */
240         /* don't beep */
241         return;
242     }
243     mark = index(gendchars, ch);
244     if (!mark)
245         mark = index(gendchars, lowc(ch));
246     if (!mark) {
247         if (index(ps_randchars, ch))
248             ps_selected = PS_RANDOM;
249         else if (index(ps_quitchars, ch))
250             ps_selected = PS_QUIT;
251         else {
252             X11_nhbell(); /* no such gender */
253             return;
254         }
255     } else
256         ps_selected = (int) (mark - gendchars);
257     exit_x_event = TRUE;
258 }
259
260 /* ARGSUSED */
261 void
262 algn_key(w, event, params, num_params)
263 Widget w;
264 XEvent *event;
265 String *params;
266 Cardinal *num_params;
267 {
268     char ch, *mark;
269     static char algnchars[] = "LNC";
270
271     nhUse(w);
272     nhUse(params);
273     nhUse(num_params);
274
275     ch = key_event_to_char((XKeyEvent *) event);
276     if (ch == '\0') { /* don't accept nul char/modifier event */
277         /* don't beep */
278         return;
279     }
280     mark = index(algnchars, ch);
281     if (!mark)
282         mark = index(algnchars, highc(ch));
283     if (!mark) {
284         if (index(ps_randchars, ch))
285             ps_selected = PS_RANDOM;
286         else if (index(ps_quitchars, ch))
287             ps_selected = PS_QUIT;
288         else {
289             X11_nhbell(); /* no such alignment */
290             return;
291         }
292     } else
293         ps_selected = (int) (mark - algnchars);
294     exit_x_event = TRUE;
295 }
296
297 /* Global functions =========================================================
298  */
299 void
300 X11_player_selection()
301 {
302     int num_roles, num_races, num_gends, num_algns, i, availcount, availindex;
303     Widget popup, player_form;
304     const char **choices;
305     char qbuf[QBUFSZ], plbuf[QBUFSZ];
306
307 #ifdef XI18N
308     char **jroles;
309     char jtmp[256];
310 #endif
311
312     /* avoid unnecessary prompts further down */
313     rigid_role_checks();
314
315     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
316                                    flags.initrace, flags.initgend,
317                                    flags.initalign);
318
319     while (flags.initrole < 0) {
320         if (flags.initrole == ROLE_RANDOM || flags.randomall) {
321             flags.initrole = pick_role(flags.initrace, flags.initgend,
322                                        flags.initalign, PICK_RANDOM);
323             break;
324         }
325
326         /* select a role */
327         for (num_roles = 0; roles[num_roles].name.m; ++num_roles)
328             continue;
329         choices = (const char **) alloc(sizeof(char *) * num_roles);
330         for (;;) {
331             availcount = 0;
332             for (i = 0; i < num_roles; i++) {
333                 choices[i] = 0;
334                 if (ok_role(i, flags.initrace, flags.initgend,
335                             flags.initalign)) {
336                     choices[i] = roles[i].name.m;
337                     if (flags.initgend >= 0 && flags.female
338                         && roles[i].name.f)
339                         choices[i] = roles[i].name.f;
340                     ++availcount;
341                 }
342             }
343             if (availcount > 0)
344                 break;
345             else if (flags.initalign >= 0)
346                 flags.initalign = -1; /* reset */
347             else if (flags.initgend >= 0)
348                 flags.initgend = -1;
349             else if (flags.initrace >= 0)
350                 flags.initrace = -1;
351             else
352                 panic("no available ROLE+race+gender+alignment combinations");
353         }
354 #if 0 /*JP*/
355         Sprintf(qbuf, "Choose your %s Role", s_suffix(plbuf));
356 #else
357         Sprintf(qbuf, "%s\90E\8bÆ\82ð\91I\91ð\82µ\82Ä\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
358 #endif
359         popup =
360 #if 0 /*JP*/
361             make_menu("player_selection", qbuf, player_select_translations,
362                       "quit", ps_quit, "random", ps_random, num_roles,
363                       choices, (Widget **) 0, ps_select, &player_form);
364 #else
365             make_menu("player_selection", qbuf, player_select_translations,
366                       "\94²\82¯\82é", ps_quit, "\83\89\83\93\83_\83\80", ps_random, num_roles,
367                       choices, (Widget **) 0, ps_select, &player_form);
368 #endif
369
370         ps_selected = -1;
371         positionpopup(popup, FALSE);
372         nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
373
374         /* The callbacks will enable the event loop exit. */
375         (void) x_event(EXIT_ON_EXIT);
376
377         nh_XtPopdown(popup);
378         XtDestroyWidget(popup);
379         free((genericptr_t) choices), choices = 0;
380
381         if (ps_selected == PS_QUIT || program_state.done_hup) {
382             clearlocks();
383             X11_exit_nhwindows((char *) 0);
384             terminate(0);
385         } else if (ps_selected == PS_RANDOM) {
386             flags.initrole = ROLE_RANDOM;
387         } else if (ps_selected < 0 || ps_selected >= num_roles) {
388             panic("player_selection: bad role select value %d", ps_selected);
389         } else {
390             flags.initrole = ps_selected;
391         }
392     }
393
394     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
395                                    flags.initrace, flags.initgend,
396                                    flags.initalign);
397
398     while (!validrace(flags.initrole, flags.initrace)) {
399         if (flags.initrace == ROLE_RANDOM || flags.randomall) {
400             flags.initrace = pick_race(flags.initrole, flags.initgend,
401                                        flags.initalign, PICK_RANDOM);
402             break;
403         }
404         /* select a race */
405         for (num_races = 0; races[num_races].noun; ++num_races)
406             continue;
407         choices = (const char **) alloc(sizeof(char *) * num_races);
408         for (;;) {
409             availcount = availindex = 0;
410             for (i = 0; i < num_races; i++) {
411                 choices[i] = 0;
412                 if (ok_race(flags.initrole, i, flags.initgend,
413                             flags.initalign)) {
414                     choices[i] = races[i].noun;
415                     ++availcount;
416                     availindex = i; /* used iff only one */
417                 }
418             }
419             if (availcount > 0)
420                 break;
421             else if (flags.initalign >= 0)
422                 flags.initalign = -1; /* reset */
423             else if (flags.initgend >= 0)
424                 flags.initgend = -1;
425             else
426                 panic("no available role+RACE+gender+alignment combinations");
427         }
428
429         if (availcount == 1) {
430             flags.initrace = availindex;
431             free((genericptr_t) choices), choices = 0;
432         } else {
433 #if 0 /*JP*/
434             Sprintf(qbuf, "Pick your %s race", s_suffix(plbuf));
435 #else
436             Sprintf(qbuf, "%s\8eí\91°\82ð\91I\91ð\82µ\82Ä\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
437 #endif
438 #if 0 /*JP*/
439             popup =
440                 make_menu("race_selection", qbuf, race_select_translations,
441                           "quit", ps_quit, "random", ps_random, num_races,
442                           choices, (Widget **) 0, ps_select, &player_form);
443 #else
444             popup =
445                 make_menu("race_selection", qbuf, race_select_translations,
446                           "\94²\82¯\82é", ps_quit, "\83\89\83\93\83_\83\80", ps_random, num_races,
447                           choices, (Widget **) 0, ps_select, &player_form);
448 #endif
449
450             ps_selected = -1;
451             positionpopup(popup, FALSE);
452             nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
453
454             /* The callbacks will enable the event loop exit. */
455             (void) x_event(EXIT_ON_EXIT);
456
457             nh_XtPopdown(popup);
458             XtDestroyWidget(popup);
459             free((genericptr_t) choices), choices = 0;
460
461             if (ps_selected == PS_QUIT || program_state.done_hup) {
462                 clearlocks();
463                 X11_exit_nhwindows((char *) 0);
464                 terminate(0);
465             } else if (ps_selected == PS_RANDOM) {
466                 flags.initrace = ROLE_RANDOM;
467             } else if (ps_selected < 0 || ps_selected >= num_races) {
468                 panic("player_selection: bad race select value %d",
469                       ps_selected);
470             } else {
471                 flags.initrace = ps_selected;
472             }
473         } /* more than one race choice available */
474     }
475
476     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
477                                    flags.initrace, flags.initgend,
478                                    flags.initalign);
479
480     while (!validgend(flags.initrole, flags.initrace, flags.initgend)) {
481         if (flags.initgend == ROLE_RANDOM || flags.randomall) {
482             flags.initgend = pick_gend(flags.initrole, flags.initrace,
483                                        flags.initalign, PICK_RANDOM);
484             break;
485         }
486         /* select a gender */
487         num_gends = 2; /* genders[2] isn't allowed */
488         choices = (const char **) alloc(sizeof(char *) * num_gends);
489         for (;;) {
490             availcount = availindex = 0;
491             for (i = 0; i < num_gends; i++) {
492                 choices[i] = 0;
493                 if (ok_gend(flags.initrole, flags.initrace, i,
494                             flags.initalign)) {
495                     choices[i] = genders[i].adj;
496                     ++availcount;
497                     availindex = i; /* used iff only one */
498                 }
499             }
500             if (availcount > 0)
501                 break;
502             else if (flags.initalign >= 0)
503                 flags.initalign = -1; /* reset */
504             else
505                 panic("no available role+race+GENDER+alignment combinations");
506         }
507
508         if (availcount == 1) {
509             flags.initgend = availindex;
510             free((genericptr_t) choices), choices = 0;
511         } else {
512 /*JP
513             Sprintf(qbuf, "Your %s gender?", s_suffix(plbuf));
514 */
515             Sprintf(qbuf, "%s\90«\95Ê\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
516 #if 0 /*JP*/
517             popup =
518                 make_menu("gender_selection", qbuf, gend_select_translations,
519                           "quit", ps_quit, "random", ps_random, num_gends,
520                           choices, (Widget **) 0, ps_select, &player_form);
521 #else
522             popup =
523                 make_menu("gender_selection", qbuf, gend_select_translations,
524                           "\94²\82¯\82é", ps_quit, "\83\89\83\93\83_\83\80", ps_random, num_gends,
525                           choices, (Widget **) 0, ps_select, &player_form);
526 #endif
527
528             ps_selected = -1;
529             positionpopup(popup, FALSE);
530             nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
531
532             /* The callbacks will enable the event loop exit. */
533             (void) x_event(EXIT_ON_EXIT);
534
535             nh_XtPopdown(popup);
536             XtDestroyWidget(popup);
537             free((genericptr_t) choices), choices = 0;
538
539             if (ps_selected == PS_QUIT || program_state.done_hup) {
540                 clearlocks();
541                 X11_exit_nhwindows((char *) 0);
542                 terminate(0);
543             } else if (ps_selected == PS_RANDOM) {
544                 flags.initgend = ROLE_RANDOM;
545             } else if (ps_selected < 0 || ps_selected >= num_gends) {
546                 panic("player_selection: bad gender select value %d",
547                       ps_selected);
548             } else {
549                 flags.initgend = ps_selected;
550             }
551         } /* more than one gender choice available */
552     }
553
554     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
555                                    flags.initrace, flags.initgend,
556                                    flags.initalign);
557
558     while (!validalign(flags.initrole, flags.initrace, flags.initalign)) {
559         if (flags.initalign == ROLE_RANDOM || flags.randomall) {
560             flags.initalign = pick_align(flags.initrole, flags.initrace,
561                                          flags.initgend, PICK_RANDOM);
562             break;
563         }
564         /* select an alignment */
565         num_algns = 3; /* aligns[3] isn't allowed */
566         choices = (const char **) alloc(sizeof(char *) * num_algns);
567         for (;;) {
568             availcount = availindex = 0;
569             for (i = 0; i < num_algns; i++) {
570                 choices[i] = 0;
571                 if (ok_align(flags.initrole, flags.initrace, flags.initgend,
572                              i)) {
573                     choices[i] = aligns[i].adj;
574                     ++availcount;
575                     availindex = i; /* used iff only one */
576                 }
577             }
578             if (availcount > 0)
579                 break;
580             else
581                 panic("no available role+race+gender+ALIGNMENT combinations");
582         }
583
584         if (availcount == 1) {
585             flags.initalign = availindex;
586             free((genericptr_t) choices), choices = 0;
587         } else {
588 /*JP
589             Sprintf(qbuf, "Your %s alignment?", s_suffix(plbuf));
590 */
591             Sprintf(qbuf, "%s\91®\90«\82ð\91I\91ð\82µ\82Ä\82­\82¾\82³\82¢\81D", s_suffix(plbuf));
592 #if 0 /*JP*/
593             popup = make_menu("alignment_selection", qbuf,
594                               algn_select_translations, "quit", ps_quit,
595                               "random", ps_random, num_algns, choices,
596                               (Widget **) 0, ps_select, &player_form);
597 #else
598             popup = make_menu("alignment_selection", qbuf,
599                               algn_select_translations, "\94²\82¯\82é", ps_quit,
600                               "\83\89\83\93\83_\83\80", ps_random, num_algns, choices,
601                               (Widget **) 0, ps_select, &player_form);
602 #endif
603
604             ps_selected = -1;
605             positionpopup(popup, FALSE);
606             nh_XtPopup(popup, (int) XtGrabExclusive, player_form);
607
608             /* The callbacks will enable the event loop exit. */
609             (void) x_event(EXIT_ON_EXIT);
610
611             nh_XtPopdown(popup);
612             XtDestroyWidget(popup);
613             free((genericptr_t) choices), choices = 0;
614
615             if (ps_selected == PS_QUIT || program_state.done_hup) {
616                 clearlocks();
617                 X11_exit_nhwindows((char *) 0);
618                 terminate(0);
619             } else if (ps_selected == PS_RANDOM) {
620                 flags.initalign = ROLE_RANDOM;
621             } else if (ps_selected < 0 || ps_selected >= num_algns) {
622                 panic("player_selection: bad alignment select value %d",
623                       ps_selected);
624             } else {
625                 flags.initalign = ps_selected;
626             }
627         } /* more than one alignment choice available */
628     }
629 }
630
631 int
632 X11_get_ext_cmd()
633 {
634     static Boolean initialized = False;
635
636     if (!initialized) {
637         init_extended_commands_popup();
638         initialized = True;
639     }
640
641     extended_command_selected = -1; /* reset selected value */
642
643     positionpopup(extended_command_popup, FALSE); /* center on cursor */
644     nh_XtPopup(extended_command_popup, (int) XtGrabExclusive,
645                extended_command_form);
646
647     /* The callbacks will enable the event loop exit. */
648     (void) x_event(EXIT_ON_EXIT);
649
650     return extended_command_selected;
651 }
652
653 /* End global functions =====================================================
654  */
655
656 /* Extended Command --------------------------------------------------------
657  */
658 /* ARGSUSED */
659 static void
660 extend_select(w, client_data, call_data)
661 Widget w;
662 XtPointer client_data, call_data;
663 {
664     int selected = (int) client_data;
665
666     nhUse(w);
667     nhUse(call_data);
668
669     if (extended_command_selected != selected) {
670         /* visibly deselect old one */
671         if (extended_command_selected >= 0)
672             swap_fg_bg(extended_commands[extended_command_selected]);
673
674         /* select new one */
675         swap_fg_bg(extended_commands[selected]);
676         extended_command_selected = selected;
677     }
678
679     nh_XtPopdown(extended_command_popup);
680     /* reset colors while popped down */
681     swap_fg_bg(extended_commands[extended_command_selected]);
682     ec_active = FALSE;
683     exit_x_event = TRUE; /* leave event loop */
684 }
685
686 /* ARGSUSED */
687 static void
688 extend_dismiss(w, client_data, call_data)
689 Widget w;
690 XtPointer client_data, call_data;
691 {
692     nhUse(w);
693     nhUse(client_data);
694     nhUse(call_data);
695
696     ec_dismiss();
697 }
698
699 /* ARGSUSED */
700 static void
701 extend_help(w, client_data, call_data)
702 Widget w;
703 XtPointer client_data, call_data;
704 {
705     nhUse(w);
706     nhUse(client_data);
707     nhUse(call_data);
708
709     /* We might need to make it known that we already have one listed. */
710     (void) doextlist();
711 }
712
713 /* ARGSUSED */
714 void
715 ec_delete(w, event, params, num_params)
716 Widget w;
717 XEvent *event;
718 String *params;
719 Cardinal *num_params;
720 {
721     if (w == extended_command_popup) {
722         ec_dismiss();
723     } else {
724         popup_delete(w, event, params, num_params);
725     }
726 }
727
728 /* ARGSUSED */
729 static void
730 popup_delete(w, event, params, num_params)
731 Widget w;
732 XEvent *event;
733 String *params;
734 Cardinal *num_params;
735 {
736     nhUse(event);
737     nhUse(params);
738     nhUse(num_params);
739
740     ps_selected = PS_QUIT;
741     nh_XtPopdown(w);
742     exit_x_event = TRUE; /* leave event loop */
743 }
744
745 static void
746 ec_dismiss()
747 {
748     /* unselect while still visible */
749     if (extended_command_selected >= 0)
750         swap_fg_bg(extended_commands[extended_command_selected]);
751     extended_command_selected = -1; /* dismiss */
752     nh_XtPopdown(extended_command_popup);
753     ec_active = FALSE;
754     exit_x_event = TRUE; /* leave event loop */
755 }
756
757 /* ARGSUSED */
758 void
759 ec_key(w, event, params, num_params)
760 Widget w;
761 XEvent *event;
762 String *params;
763 Cardinal *num_params;
764 {
765     char ch;
766     int i;
767     XKeyEvent *xkey = (XKeyEvent *) event;
768
769     nhUse(w);
770     nhUse(params);
771     nhUse(num_params);
772
773     ch = key_event_to_char(xkey);
774
775     if (ch == '\0') { /* don't accept nul char/modifier event */
776         /* don't beep */
777         return;
778     } else if (index("\033\n\r", ch)) {
779         if (ch == '\033') {
780             /* unselect while still visible */
781             if (extended_command_selected >= 0)
782                 swap_fg_bg(extended_commands[extended_command_selected]);
783             extended_command_selected = -1; /* dismiss */
784         }
785
786         nh_XtPopdown(extended_command_popup);
787         /* unselect while invisible */
788         if (extended_command_selected >= 0)
789             swap_fg_bg(extended_commands[extended_command_selected]);
790
791         exit_x_event = TRUE; /* leave event loop */
792         ec_active = FALSE;
793         return;
794     }
795
796     /* too much time has elapsed */
797     if ((xkey->time - ec_time) > 500)
798         ec_active = FALSE;
799
800     if (!ec_active) {
801         ec_nchars = 0;
802         ec_active = TRUE;
803     }
804
805     ec_time = xkey->time;
806     ec_chars[ec_nchars++] = ch;
807     if (ec_nchars >= EC_NCHARS)
808         ec_nchars = EC_NCHARS - 1; /* don't overflow */
809
810     for (i = 0; extcmdlist[i].ef_txt; i++) {
811         if (extcmdlist[i].ef_txt[0] == '?')
812             continue;
813
814         if (!strncmp(ec_chars, extcmdlist[i].ef_txt, ec_nchars)) {
815             if (extended_command_selected != i) {
816                 /* I should use set() and unset() actions, but how do */
817                 /* I send the an action to the widget? */
818                 if (extended_command_selected >= 0)
819                     swap_fg_bg(extended_commands[extended_command_selected]);
820                 extended_command_selected = i;
821                 swap_fg_bg(extended_commands[extended_command_selected]);
822             }
823             break;
824         }
825     }
826 }
827
828 /*
829  * Use our own home-brewed version menu because simpleMenu is designed to
830  * be used from a menubox.
831  */
832 static void
833 init_extended_commands_popup()
834 {
835     int i, num_commands;
836     const char **command_list;
837
838     /* count commands */
839     for (num_commands = 0; extcmdlist[num_commands].ef_txt; num_commands++)
840         ; /* do nothing */
841
842     /* If the last entry is "help", don't use it. */
843     if (strcmp(extcmdlist[num_commands - 1].ef_txt, "?") == 0)
844         --num_commands;
845
846     command_list =
847         (const char **) alloc((unsigned) num_commands * sizeof(char *));
848
849     for (i = 0; i < num_commands; i++)
850         command_list[i] = extcmdlist[i].ef_txt;
851
852 #if 0 /*JP*/
853     extended_command_popup =
854         make_menu("extended_commands", "Extended Commands",
855                   extended_command_translations, "dismiss", extend_dismiss,
856                   "help", extend_help, num_commands, command_list,
857                   &extended_commands, extend_select, &extended_command_form);
858 #else
859     extended_command_popup =
860         make_menu("extended_commands", "\8ag\92£\83R\83}\83\93\83h",
861                   extended_command_translations, "\8eæ\8fÁ", extend_dismiss,
862                   "\83w\83\8b\83v", extend_help, num_commands, command_list,
863                   &extended_commands, extend_select, &extended_command_form);
864 #endif
865
866     free((char *) command_list);
867 }
868
869 /* -------------------------------------------------------------------------
870  */
871
872 /*
873  * Create a popup widget of the following form:
874  *
875  *                    popup_label
876  *              ----------- ------------
877  *              |left_name| |right_name|
878  *              ----------- ------------
879  *              ------------------------
880  *              |       name1          |
881  *              ------------------------
882  *              ------------------------
883  *              |       name2          |
884  *              ------------------------
885  *                        .
886  *                        .
887  *              ------------------------
888  *              |       nameN          |
889  *              ------------------------
890  */
891 static Widget
892 make_menu(popup_name, popup_label, popup_translations, left_name,
893           left_callback, right_name, right_callback, num_names, widget_names,
894           command_widgets, name_callback, formp)
895 const char *popup_name;
896 const char *popup_label;
897 const char *popup_translations;
898 const char *left_name;
899 XtCallbackProc left_callback;
900 const char *right_name;
901 XtCallbackProc right_callback;
902 int num_names;
903 const char **widget_names; /* return array of command widgets */
904 Widget **command_widgets;
905 XtCallbackProc name_callback;
906 Widget *formp; /* return */
907 {
908     Widget popup, form, label, above, left, right;
909     Widget *commands, *curr;
910     int i;
911     Arg args[8];
912     Cardinal num_args;
913     Dimension width, max_width;
914     int distance, skip;
915
916     commands = (Widget *) alloc((unsigned) num_names * sizeof(Widget));
917
918     num_args = 0;
919     XtSetArg(args[num_args], XtNallowShellResize, True);
920     num_args++;
921
922     popup = XtCreatePopupShell(popup_name, transientShellWidgetClass,
923                                toplevel, args, num_args);
924     XtOverrideTranslations(
925         popup, XtParseTranslationTable("<Message>WM_PROTOCOLS: ec_delete()"));
926
927     num_args = 0;
928     XtSetArg(args[num_args], XtNtranslations,
929              XtParseTranslationTable(popup_translations));
930     num_args++;
931     *formp = form = XtCreateManagedWidget("menuform", formWidgetClass, popup,
932                                           args, num_args);
933
934     /* Get the default distance between objects in the form widget. */
935     num_args = 0;
936     XtSetArg(args[num_args], nhStr(XtNdefaultDistance), &distance);
937     num_args++;
938     XtGetValues(form, args, num_args);
939
940     /*
941      * Create the label.
942      */
943     num_args = 0;
944 #if defined(X11R6) && defined(XI18N)
945     XtSetArg(args[num_args], XtNinternational, True);
946     num_args++;
947 #endif
948     XtSetArg(args[num_args], XtNborderWidth, 0);
949     num_args++;
950     label = XtCreateManagedWidget(popup_label, labelWidgetClass, form, args,
951                                   num_args);
952
953     /*
954      * Create the left button.
955      */
956     num_args = 0;
957     XtSetArg(args[num_args], nhStr(XtNfromVert), label);
958     num_args++;
959     /*
960         XtSetArg(args[num_args], nhStr(XtNshapeStyle),
961                                     XmuShapeRoundedRectangle);  num_args++;
962     */
963     left = XtCreateManagedWidget(left_name, commandWidgetClass, form, args,
964                                  num_args);
965     XtAddCallback(left, XtNcallback, left_callback, (XtPointer) 0);
966     skip = 3 * distance; /* triple the spacing */
967     if (!skip)
968         skip = 3;
969
970     /*
971      * Create right button.
972      */
973     num_args = 0;
974     XtSetArg(args[num_args], nhStr(XtNfromHoriz), left);
975     num_args++;
976     XtSetArg(args[num_args], nhStr(XtNfromVert), label);
977     num_args++;
978     /*
979         XtSetArg(args[num_args], nhStr(XtNshapeStyle),
980                                     XmuShapeRoundedRectangle);  num_args++;
981     */
982 #if defined(X11R6) && defined(XI18N)
983     XtSetArg(args[num_args], XtNinternational, True);
984     num_args++;
985 #endif
986     right = XtCreateManagedWidget(right_name, commandWidgetClass, form, args,
987                                   num_args);
988     XtAddCallback(right, XtNcallback, right_callback, (XtPointer) 0);
989
990     XtInstallAccelerators(form, left);
991     XtInstallAccelerators(form, right);
992
993     /*
994      * Create and place the command widgets.
995      */
996     for (i = 0, above = left, curr = commands; i < num_names; i++) {
997         if (!widget_names[i])
998             continue;
999         num_args = 0;
1000         XtSetArg(args[num_args], nhStr(XtNfromVert), above);
1001         num_args++;
1002         if (above == left) {
1003             /* if first, we are farther apart */
1004             XtSetArg(args[num_args], nhStr(XtNvertDistance), skip);
1005             num_args++;
1006         }
1007
1008 #if defined(X11R6) && defined(XI18N)
1009         XtSetArg(args[num_args], XtNinternational, True);
1010         num_args++;
1011 #endif
1012         *curr = XtCreateManagedWidget(widget_names[i], commandWidgetClass,
1013                                       form, args, num_args);
1014         XtAddCallback(*curr, XtNcallback, name_callback, (XtPointer) i);
1015         above = *curr++;
1016     }
1017
1018     /*
1019      * Now find the largest width.  Start with the width dismiss + help
1020      * buttons, since they are adjacent.
1021      */
1022     XtSetArg(args[0], XtNwidth, &max_width);
1023     XtGetValues(left, args, ONE);
1024     XtSetArg(args[0], XtNwidth, &width);
1025     XtGetValues(right, args, ONE);
1026     max_width = max_width + width + distance;
1027
1028     /* Next, the title. */
1029     XtSetArg(args[0], XtNwidth, &width);
1030     XtGetValues(label, args, ONE);
1031     if (width > max_width)
1032         max_width = width;
1033
1034     /* Finally, the commands. */
1035     for (i = 0, curr = commands; i < num_names; i++) {
1036         if (!widget_names[i])
1037             continue;
1038         XtSetArg(args[0], XtNwidth, &width);
1039         XtGetValues(*curr, args, ONE);
1040         if (width > max_width)
1041             max_width = width;
1042         curr++;
1043     }
1044
1045     /*
1046      * Finally, set all of the single line widgets to the largest width.
1047      */
1048     XtSetArg(args[0], XtNwidth, max_width);
1049     XtSetValues(label, args, ONE);
1050
1051     for (i = 0, curr = commands; i < num_names; i++) {
1052         if (!widget_names[i])
1053             continue;
1054         XtSetArg(args[0], XtNwidth, max_width);
1055         XtSetValues(*curr, args, ONE);
1056         curr++;
1057     }
1058
1059     if (command_widgets)
1060         *command_widgets = commands;
1061     else
1062         free((char *) commands);
1063
1064     XtRealizeWidget(popup);
1065     XSetWMProtocols(XtDisplay(popup), XtWindow(popup), &wm_delete_window, 1);
1066
1067     return popup;
1068 }