OSDN Git Service

version++
[jnethack/source.git] / win / X11 / winmenu.c
1 /* NetHack 3.6  winmenu.c       $NHDT-Date: 1432512809 2015/05/25 00:13:29 $  $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  * File for creating menus.
7  *
8  *      + Global functions: start_menu, add_menu, end_menu, select_menu
9  */
10 /*#define USE_FWF*/ /* use FWF's list widget */
11
12 #ifndef SYSV
13 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
14 #endif
15
16 #include <X11/Intrinsic.h>
17 #include <X11/StringDefs.h>
18 #include <X11/Shell.h>
19 #include <X11/Xatom.h>
20 #include <X11/Xaw/Label.h>
21 #include <X11/Xaw/Command.h>
22 #include <X11/Xaw/Viewport.h>
23 #include <X11/Xaw/Cardinals.h>
24 #include <X11/Xaw/Box.h>
25 #ifdef USE_FWF
26 #include <X11/Xfwf/MultiList.h>
27 #else
28 #include <X11/Xaw/List.h>
29 #endif
30 #include <X11/Xos.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 "winX.h"
41 #include <ctype.h>
42
43 static void FDECL(menu_select, (Widget, XtPointer, XtPointer));
44 static void FDECL(invert_line,
45                   (struct xwindow *, x11_menu_item *, int, long));
46 static void FDECL(menu_ok, (Widget, XtPointer, XtPointer));
47 static void FDECL(menu_cancel, (Widget, XtPointer, XtPointer));
48 static void FDECL(menu_all, (Widget, XtPointer, XtPointer));
49 static void FDECL(menu_none, (Widget, XtPointer, XtPointer));
50 static void FDECL(menu_invert, (Widget, XtPointer, XtPointer));
51 static void FDECL(menu_search, (Widget, XtPointer, XtPointer));
52 static void FDECL(select_all, (struct xwindow *));
53 static void FDECL(select_none, (struct xwindow *));
54 static void FDECL(select_match, (struct xwindow *, char *));
55 static void FDECL(invert_all, (struct xwindow *));
56 static void FDECL(invert_match, (struct xwindow *, char *));
57 static void FDECL(menu_popdown, (struct xwindow *));
58 #ifdef USE_FWF
59 static void FDECL(sync_selected, (struct menu_info_t *, int, int *));
60 #endif
61
62 static void FDECL(move_menu, (struct menu *, struct menu *));
63 static void FDECL(free_menu, (struct menu *));
64 static void FDECL(reset_menu_to_default, (struct menu *));
65 static void FDECL(clear_old_menu, (struct xwindow *));
66 static char *FDECL(copy_of, (const char *));
67
68 #define reset_menu_count(mi) ((mi)->counting = FALSE, (mi)->menu_count = 0L)
69
70 static const char menu_translations[] = "#override\n\
71      <Key>Left: scroll(4)\n\
72      <Key>Right: scroll(6)\n\
73      <Key>Up: scroll(8)\n\
74      <Key>Down: scroll(2)\n\
75      <Key>: menu_key()";
76
77 /*
78  * Menu callback.
79  */
80 /* ARGSUSED */
81 static void
82 menu_select(w, client_data, call_data)
83 Widget w;
84 XtPointer client_data, call_data;
85 {
86     struct xwindow *wp;
87     struct menu_info_t *menu_info;
88 #ifdef USE_FWF
89     XfwfMultiListReturnStruct *lrs = (XfwfMultiListReturnStruct *) call_data;
90 #else
91     XawListReturnStruct *lrs = (XawListReturnStruct *) call_data;
92     int i;
93     x11_menu_item *curr;
94 #endif
95     long how_many;
96
97     nhUse(client_data);
98
99     wp = find_widget(w);
100     menu_info = wp->menu_information;
101     how_many = menu_info->counting ? menu_info->menu_count : -1L;
102     reset_menu_count(menu_info);
103
104 #ifdef USE_FWF
105     /* if we've reached here, we've found our selected item */
106     switch (lrs->action) {
107     case XfwfMultiListActionNothing:
108         pline("menu_select: nothing action?");
109         break;
110     case XfwfMultiListActionStatus:
111         pline("menu_select: status action?");
112         break;
113     case XfwfMultiListActionHighlight:
114     case XfwfMultiListActionUnhighlight:
115         sync_selected(menu_info, lrs->num_selected, lrs->selected_items);
116         break;
117     }
118 #else
119     for (i = 0, curr = menu_info->curr_menu.base; i < lrs->list_index; i++) {
120         if (!curr)
121             panic("menu_select: out of menu items!");
122         curr = curr->next;
123     }
124     XawListUnhighlight(w); /* unhilight item */
125
126     /* if the menu is not active or don't have an identifier, try again */
127     if (!menu_info->is_active || curr->identifier.a_void == 0) {
128         X11_nhbell();
129         return;
130     }
131
132     /* if we've reached here, we've found our selected item */
133     curr->selected = !curr->selected;
134     if (curr->selected) {
135         curr->str[2] = (how_many != -1L) ? '#' : '+';
136         curr->pick_count = how_many;
137     } else {
138         curr->str[2] = '-';
139         curr->pick_count = -1L;
140     }
141     XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True);
142 #endif
143
144     if (menu_info->how == PICK_ONE)
145         menu_popdown(wp);
146 }
147
148 /*
149  * Called when menu window is deleted.
150  */
151 /* ARGSUSED */
152 void
153 menu_delete(w, event, params, num_params)
154 Widget w;
155 XEvent *event;
156 String *params;
157 Cardinal *num_params;
158 {
159     nhUse(event);
160     nhUse(params);
161     nhUse(num_params);
162
163     menu_cancel((Widget) None, (XtPointer) find_widget(w), (XtPointer) 0);
164 }
165
166 /*
167  * Invert the count'th line (curr) in the given window.
168  */
169 /*ARGSUSED*/
170 static void
171 invert_line(wp, curr, which, how_many)
172 struct xwindow *wp;
173 x11_menu_item *curr;
174 int which;
175 long how_many;
176 {
177 #ifndef USE_FWF
178     nhUse(which);
179 #endif
180     reset_menu_count(wp->menu_information);
181     curr->selected = !curr->selected;
182     if (curr->selected) {
183 #ifdef USE_FWF
184         XfwfMultiListHighlightItem((XfwfMultiListWidget) wp->w, which);
185 #else
186         curr->str[2] = (how_many != -1) ? '#' : '+';
187 #endif
188         curr->pick_count = how_many;
189     } else {
190 #ifdef USE_FWF
191         XfwfMultiListUnhighlightItem((XfwfMultiListWidget) wp->w, which);
192 #else
193         curr->str[2] = '-';
194 #endif
195         curr->pick_count = -1L;
196     }
197 }
198
199 /*
200  * Called when we get a key press event on a menu window.
201  */
202 /* ARGSUSED */
203 void
204 menu_key(w, event, params, num_params)
205 Widget w;
206 XEvent *event;
207 String *params;
208 Cardinal *num_params;
209 {
210     struct menu_info_t *menu_info;
211     x11_menu_item *curr;
212     struct xwindow *wp;
213     char ch;
214     int count;
215     boolean selected_something;
216
217     nhUse(params);
218     nhUse(num_params);
219
220     wp = find_widget(w);
221     menu_info = wp->menu_information;
222
223     ch = key_event_to_char((XKeyEvent *) event);
224
225     if (ch == '\0') { /* don't accept nul char/modifier event */
226         /* don't beep */
227         return;
228     }
229
230     if (menu_info->is_active) { /* waiting for input */
231         /* first check for an explicit selector match, so that it won't be
232            overridden if it happens to duplicate a mapped menu command (':'
233            to look inside a container vs ':' to select via search string) */
234         for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
235             if (curr->identifier.a_void != 0 && curr->selector == ch)
236                 goto make_selection;
237
238         ch = map_menu_cmd(ch);
239         if (ch == '\033') { /* quit */
240             if (menu_info->counting) {
241                 /* when there's a count in progress, ESC discards it
242                    rather than dismissing the whole menu */
243                 reset_menu_count(menu_info);
244                 return;
245             }
246             select_none(wp);
247         } else if (ch == '\n' || ch == '\r') {
248             ; /* accept */
249         } else if (isdigit(ch)) {
250             /* special case: '0' is also the default ball class */
251             if (ch == '0' && !menu_info->counting
252                 && index(menu_info->curr_menu.gacc, ch))
253                 goto group_accel;
254             menu_info->menu_count *= 10L;
255             menu_info->menu_count += (long) (ch - '0');
256             if (menu_info->menu_count != 0L) /* ignore leading zeros */
257                 menu_info->counting = TRUE;
258             return;
259         } else if (ch == MENU_SEARCH) { /* search */
260             if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) {
261                 char buf[BUFSZ + 2], tmpbuf[BUFSZ];
262
263                 X11_getlin("Search for:", tmpbuf);
264                 if (!*tmpbuf || *tmpbuf == '\033')
265                     return;
266                 /* convert "string" into "*string*" for use with pmatch() */
267                 Sprintf(buf, "*%s*", tmpbuf);
268
269                 if (menu_info->how == PICK_ANY) {
270                     invert_match(wp, buf);
271                     return;
272                 } else {
273                     select_match(wp, buf);
274                 }
275             } else {
276                 X11_nhbell();
277                 return;
278             }
279         } else if (ch == MENU_SELECT_ALL) { /* select all */
280             if (menu_info->how == PICK_ANY)
281                 select_all(wp);
282             else
283                 X11_nhbell();
284             return;
285         } else if (ch == MENU_UNSELECT_ALL) { /* unselect all */
286             if (menu_info->how == PICK_ANY)
287                 select_none(wp);
288             else
289                 X11_nhbell();
290             return;
291         } else if (ch == MENU_INVERT_ALL) { /* invert all */
292             if (menu_info->how == PICK_ANY)
293                 invert_all(wp);
294             else
295                 X11_nhbell();
296             return;
297         } else if (index(menu_info->curr_menu.gacc, ch)) {
298         group_accel:
299             /* matched a group accelerator */
300             if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) {
301                 for (count = 0, curr = menu_info->curr_menu.base; curr;
302                      curr = curr->next, count++) {
303                     if (curr->identifier.a_void != 0
304                         && curr->gselector == ch) {
305                         invert_line(wp, curr, count, -1L);
306                         /* for PICK_ONE, a group accelerator will
307                            only be included in gacc[] if it matches
308                            exactly one entry, so this must be it... */
309                         if (menu_info->how == PICK_ONE)
310                             goto menu_done; /* pop down */
311                     }
312                 }
313 #ifndef USE_FWF
314                 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0,
315                               True);
316 #endif
317             } else
318                 X11_nhbell();
319             return;
320         } else {
321         make_selection:
322             selected_something = FALSE;
323             for (count = 0, curr = menu_info->curr_menu.base; curr;
324                  curr = curr->next, count++)
325                 if (curr->identifier.a_void != 0 && curr->selector == ch)
326                     break;
327
328             if (curr) {
329                 invert_line(wp, curr, count, menu_info->counting
330                                                  ? menu_info->menu_count
331                                                  : -1L);
332 #ifndef USE_FWF
333                 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0,
334                               True);
335 #endif
336                 selected_something = curr->selected;
337             } else {
338                 X11_nhbell(); /* no match */
339             }
340             if (!(selected_something && menu_info->how == PICK_ONE))
341                 return; /* keep going */
342         }
343         /* pop down */
344     } else { /* permanent inventory window */
345         if (ch != '\033') {
346             X11_nhbell();
347             return;
348         }
349         /* pop down on ESC */
350     }
351
352 menu_done:
353     menu_popdown(wp);
354 }
355
356 /* ARGSUSED */
357 static void
358 menu_ok(w, client_data, call_data)
359 Widget w;
360 XtPointer client_data, call_data;
361 {
362     struct xwindow *wp = (struct xwindow *) client_data;
363
364     nhUse(w);
365     nhUse(call_data);
366
367     menu_popdown(wp);
368 }
369
370 /* ARGSUSED */
371 static void
372 menu_cancel(w, client_data, call_data)
373 Widget w; /* don't use - may be None */
374 XtPointer client_data, call_data;
375 {
376     struct xwindow *wp = (struct xwindow *) client_data;
377
378     nhUse(w);
379     nhUse(call_data);
380
381     if (wp->menu_information->is_active) {
382         select_none(wp);
383         wp->menu_information->cancelled = TRUE;
384     }
385     menu_popdown(wp);
386 }
387
388 /* ARGSUSED */
389 static void
390 menu_all(w, client_data, call_data)
391 Widget w;
392 XtPointer client_data, call_data;
393 {
394     nhUse(w);
395     nhUse(call_data);
396
397     select_all((struct xwindow *) client_data);
398 }
399
400 /* ARGSUSED */
401 static void
402 menu_none(w, client_data, call_data)
403 Widget w;
404 XtPointer client_data, call_data;
405 {
406     nhUse(w);
407     nhUse(call_data);
408
409     select_none((struct xwindow *) client_data);
410 }
411
412 /* ARGSUSED */
413 static void
414 menu_invert(w, client_data, call_data)
415 Widget w;
416 XtPointer client_data, call_data;
417 {
418     nhUse(w);
419     nhUse(call_data);
420
421     invert_all((struct xwindow *) client_data);
422 }
423
424 /* ARGSUSED */
425 static void
426 menu_search(w, client_data, call_data)
427 Widget w;
428 XtPointer client_data, call_data;
429 {
430     struct xwindow *wp = (struct xwindow *) client_data;
431     struct menu_info_t *menu_info = wp->menu_information;
432     char buf[BUFSZ + 2], tmpbuf[BUFSZ];
433
434     nhUse(w);
435     nhUse(call_data);
436
437     X11_getlin("Search for:", tmpbuf);
438     if (!*tmpbuf || *tmpbuf == '\033')
439         return;
440     /* convert "string" into "*string*" for use with pmatch() */
441     Sprintf(buf, "*%s*", tmpbuf);
442
443     if (menu_info->how == PICK_ANY)
444         invert_match(wp, buf);
445     else
446         select_match(wp, buf);
447
448     if (menu_info->how == PICK_ONE)
449         menu_popdown(wp);
450 }
451
452 static void
453 select_all(wp)
454 struct xwindow *wp;
455 {
456     x11_menu_item *curr;
457     int count;
458     boolean changed = FALSE;
459
460     reset_menu_count(wp->menu_information);
461     for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
462          curr = curr->next, count++)
463         if (curr->identifier.a_void != 0)
464             if (!curr->selected) {
465                 invert_line(wp, curr, count, -1L);
466                 changed = TRUE;
467             }
468
469 #ifndef USE_FWF
470     if (changed)
471         XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0,
472                       0, True);
473 #endif
474 }
475
476 static void
477 select_none(wp)
478 struct xwindow *wp;
479 {
480     x11_menu_item *curr;
481     int count;
482     boolean changed = FALSE;
483
484     reset_menu_count(wp->menu_information);
485     for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
486          curr = curr->next, count++)
487         if (curr->identifier.a_void != 0)
488             if (curr->selected) {
489                 invert_line(wp, curr, count, -1L);
490                 changed = TRUE;
491             }
492
493 #ifndef USE_FWF
494     if (changed)
495         XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0,
496                       0, True);
497 #endif
498 }
499
500 static void
501 invert_all(wp)
502 struct xwindow *wp;
503 {
504     x11_menu_item *curr;
505     int count;
506
507     reset_menu_count(wp->menu_information);
508     for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
509          curr = curr->next, count++)
510         if (curr->identifier.a_void != 0)
511             invert_line(wp, curr, count, -1L);
512
513 #ifndef USE_FWF
514     XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0, 0,
515                   True);
516 #endif
517 }
518
519 static void
520 invert_match(wp, match)
521 struct xwindow *wp;
522 char *match; /* wildcard pattern for pmatch() */
523 {
524     x11_menu_item *curr;
525     int count;
526     boolean changed = FALSE;
527
528     reset_menu_count(wp->menu_information);
529     for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
530          curr = curr->next, count++)
531         if (curr->identifier.a_void != 0 && pmatchi(match, curr->str)) {
532             invert_line(wp, curr, count, -1L);
533             changed = TRUE;
534         }
535
536 #ifndef USE_FWF
537     if (changed)
538         XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0,
539                       0, True);
540 #endif
541 }
542
543 static void
544 select_match(wp, match)
545 struct xwindow *wp;
546 char *match; /* wildcard pattern for pmatch() */
547 {
548     x11_menu_item *curr;
549     int count;
550
551     reset_menu_count(wp->menu_information);
552     for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
553          curr = curr->next, count++)
554         if (curr->identifier.a_void != 0 && pmatchi(match, curr->str)) {
555             if (!curr->selected) {
556                 invert_line(wp, curr, count, -1L);
557 #ifndef USE_FWF
558                 XawListChange(wp->w,
559                               wp->menu_information->curr_menu.list_pointer, 0,
560                               0, True);
561 #endif
562             }
563             return;
564         }
565
566     /* no match */
567     X11_nhbell();
568 }
569
570 static void
571 menu_popdown(wp)
572 struct xwindow *wp;
573 {
574     nh_XtPopdown(wp->popup); /* remove the event grab */
575     if (wp->menu_information->is_active)
576         exit_x_event = TRUE;             /* exit our event handler */
577     wp->menu_information->is_up = FALSE; /* menu is down */
578 }
579
580 #ifdef USE_FWF
581 /*
582  * Make sure our idea of selected matches the FWF Multilist's idea of what
583  * is currently selected.  The MultiList's selected list can change without
584  * notifying us if one or more items are selected and then another is
585  * selected (not toggled).  Then the items that were selected are deselected
586  * but we are not notified.
587  */
588 static void
589 sync_selected(menu_info, num_selected, items)
590 struct menu_info_t *menu_info;
591 int num_selected;
592 int *items;
593 {
594     int i, j, *ip;
595     x11_menu_item *curr;
596     Boolean found;
597
598     for (i = 0, curr = menu_info->curr_menu.base; curr;
599          i++, curr = curr->next) {
600         found = False;
601         for (j = 0, ip = items; j < num_selected; j++, ip++)
602             if (*ip == i) {
603                 found = True;
604                 break;
605             }
606 #if 0
607         if (curr->selected && !found)
608             printf("sync: deselecting %s\n", curr->str);
609         else if (!curr->selected && found)
610             printf("sync: selecting %s\n", curr->str);
611 #endif
612         curr->selected = found ? TRUE : FALSE;
613     }
614 }
615 #endif /* USE_FWF */
616
617 /* Global functions ========================================================
618  */
619
620 void
621 X11_start_menu(window)
622 winid window;
623 {
624     struct xwindow *wp;
625     check_winid(window);
626
627     wp = &window_list[window];
628
629     if (wp->menu_information->is_menu) {
630         /* make sure we'ere starting with a clean slate */
631         free_menu(&wp->menu_information->new_menu);
632     } else {
633         wp->menu_information->is_menu = TRUE;
634     }
635 }
636
637 /*ARGSUSED*/
638 void
639 X11_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
640 winid window;
641 int glyph; /* unused (for now) */
642 const anything *identifier;
643 char ch;
644 char gch; /* group accelerator (0 = no group) */
645 int attr;
646 const char *str;
647 boolean preselected;
648 {
649     x11_menu_item *item;
650     struct menu_info_t *menu_info;
651
652     nhUse(glyph);
653     nhUse(preselected); /*FIXME*/
654
655     check_winid(window);
656     menu_info = window_list[window].menu_information;
657     if (!menu_info->is_menu) {
658         impossible("add_menu:  called before start_menu");
659         return;
660     }
661
662     item = (x11_menu_item *) alloc((unsigned) sizeof(x11_menu_item));
663     item->next = (x11_menu_item *) 0;
664     item->identifier = *identifier;
665     item->attr = attr;
666     /*    item->selected = preselected; */
667     item->selected = FALSE;
668     item->pick_count = -1L;
669
670     if (identifier->a_void) {
671         char buf[4 + BUFSZ];
672         int len = strlen(str);
673
674         if (!ch) {
675             /* Supply a keyboard accelerator.  Only the first 52 get one. */
676
677             if (menu_info->new_menu.curr_selector) {
678                 ch = menu_info->new_menu.curr_selector++;
679                 if (ch == 'z')
680                     menu_info->new_menu.curr_selector = 'A';
681                 else if (ch == 'Z')
682                     menu_info->new_menu.curr_selector = 0; /* out */
683             }
684         }
685
686         if (len >= BUFSZ) {
687             /* We *think* everything's coming in off at most BUFSZ bufs... */
688             impossible("Menu item too long (%d).", len);
689             len = BUFSZ - 1;
690         }
691         Sprintf(buf, "%c - ", ch ? ch : ' ');
692         (void) strncpy(buf + 4, str, len);
693         buf[4 + len] = '\0';
694         item->str = copy_of(buf);
695     } else {
696         /* no keyboard accelerator */
697         item->str = copy_of(str);
698         ch = 0;
699     }
700
701     item->selector = ch;
702     item->gselector = gch;
703
704     if (menu_info->new_menu.last) {
705         menu_info->new_menu.last->next = item;
706     } else {
707         menu_info->new_menu.base = item;
708     }
709     menu_info->new_menu.last = item;
710     menu_info->new_menu.count++;
711 }
712
713 void
714 X11_end_menu(window, query)
715 winid window;
716 const char *query;
717 {
718     struct menu_info_t *menu_info;
719
720     check_winid(window);
721     menu_info = window_list[window].menu_information;
722     if (!menu_info->is_menu) {
723         impossible("end_menu:  called before start_menu");
724         return;
725     }
726     menu_info->new_menu.query = copy_of(query);
727 }
728
729 int
730 X11_select_menu(window, how, menu_list)
731 winid window;
732 int how;
733 menu_item **menu_list;
734 {
735     x11_menu_item *curr;
736     struct xwindow *wp;
737     struct menu_info_t *menu_info;
738     Arg args[10];
739     Cardinal num_args;
740     String *ptr;
741     int retval;
742     Dimension v_pixel_width, v_pixel_height;
743     boolean labeled;
744     Widget viewport_widget, form, label, ok, cancel, all, none, invert,
745         search;
746     Boolean sens;
747 #ifdef USE_FWF
748     Boolean *boolp;
749 #endif
750     char gacc[QBUFSZ], *ap;
751 #ifdef XI18N
752 /*    XFontSet fontset;*/
753     XFontSetExtents *extent;
754 #endif
755
756     *menu_list = (menu_item *) 0;
757     check_winid(window);
758     wp = &window_list[window];
759     menu_info = wp->menu_information;
760     if (!menu_info->is_menu) {
761         impossible("select_menu:  called before start_menu");
762         return 0;
763     }
764
765     menu_info->how = (short) how;
766
767     /* collect group accelerators; for PICK_NONE, they're ignored;
768        for PICK_ONE, only those which match exactly one entry will be
769        accepted; for PICK_ANY, those which match any entry are okay */
770     gacc[0] = '\0';
771     if (menu_info->how != PICK_NONE) {
772         int i, n, gcnt[128];
773 #define GSELIDX(c) ((c) &127) /* guard against `signed char' */
774
775         for (i = 0; i < SIZE(gcnt); i++)
776             gcnt[i] = 0;
777         for (n = 0, curr = menu_info->new_menu.base; curr; curr = curr->next)
778             if (curr->gselector && curr->gselector != curr->selector) {
779                 ++n;
780                 ++gcnt[GSELIDX(curr->gselector)];
781             }
782
783         if (n > 0) /* at least one group accelerator found */
784             for (ap = gacc, curr = menu_info->new_menu.base; curr;
785                  curr = curr->next)
786                 if (curr->gselector && !index(gacc, curr->gselector)
787                     && (menu_info->how == PICK_ANY
788                         || gcnt[GSELIDX(curr->gselector)] == 1)) {
789                     *ap++ = curr->gselector;
790                     *ap = '\0'; /* re-terminate for index() */
791                 }
792     }
793     menu_info->new_menu.gacc = copy_of(gacc);
794     reset_menu_count(menu_info);
795
796     /*
797      * Create a string and sensitive list for the new menu.
798      */
799     menu_info->new_menu.list_pointer = ptr = (String *) alloc(
800         (unsigned) (sizeof(String) * (menu_info->new_menu.count + 1)));
801     for (curr = menu_info->new_menu.base; curr; ptr++, curr = curr->next)
802         *ptr = (String) curr->str;
803     *ptr = 0; /* terminate list with null */
804
805 #ifdef USE_FWF
806     menu_info->new_menu.sensitive = boolp = (Boolean *) alloc(
807         (unsigned) (sizeof(Boolean) * (menu_info->new_menu.count)));
808     for (curr = menu_info->new_menu.base; curr; boolp++, curr = curr->next)
809         *boolp = (curr->identifier.a_void != 0);
810 #else
811     menu_info->new_menu.sensitive = (Boolean *) 0;
812 #endif
813     labeled = (menu_info->new_menu.query && *(menu_info->new_menu.query))
814                   ? TRUE
815                   : FALSE;
816
817     /*
818      * Menus don't appear to size components correctly, except
819      * when first created.  For 3.2.0 release, just recreate
820      * each time.
821      */
822     if (menu_info->valid_widgets
823         && (window != WIN_INVEN || !flags.perm_invent)) {
824         XtDestroyWidget(wp->popup);
825         menu_info->valid_widgets = FALSE;
826         menu_info->is_up = FALSE;
827     }
828
829     if (!menu_info->valid_widgets) {
830         Dimension row_spacing;
831
832         num_args = 0;
833         XtSetArg(args[num_args], XtNallowShellResize, True);
834         num_args++;
835         wp->popup =
836             XtCreatePopupShell(window == WIN_INVEN ? "inventory" : "menu",
837                                how == PICK_NONE ? topLevelShellWidgetClass
838                                                 : transientShellWidgetClass,
839                                toplevel, args, num_args);
840         XtOverrideTranslations(
841             wp->popup,
842             XtParseTranslationTable("<Message>WM_PROTOCOLS: menu_delete()"));
843
844         num_args = 0;
845         XtSetArg(args[num_args], XtNtranslations,
846                  XtParseTranslationTable(menu_translations));
847         num_args++;
848         form = XtCreateManagedWidget("mform", formWidgetClass, wp->popup,
849                                      args, num_args);
850
851         num_args = 0;
852         XtSetArg(args[num_args], XtNborderWidth, 0);
853         num_args++;
854         XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
855         num_args++;
856         XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
857         num_args++;
858         XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
859         num_args++;
860         XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
861         num_args++;
862
863         if (labeled)
864             label =
865                 XtCreateManagedWidget(menu_info->new_menu.query,
866                                       labelWidgetClass, form, args, num_args);
867         else
868             label = NULL;
869
870         /*
871          * Create ok, cancel, all, none, invert, and search buttons..
872          */
873         num_args = 0;
874         XtSetArg(args[num_args], nhStr(XtNfromVert), label);
875         num_args++;
876         XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
877         num_args++;
878         XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
879         num_args++;
880         XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
881         num_args++;
882         XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
883         num_args++;
884         ok = XtCreateManagedWidget("OK", commandWidgetClass, form, args,
885                                    num_args);
886         XtAddCallback(ok, XtNcallback, menu_ok, (XtPointer) wp);
887
888         num_args = 0;
889         XtSetArg(args[num_args], nhStr(XtNfromVert), label);
890         num_args++;
891         XtSetArg(args[num_args], nhStr(XtNfromHoriz), ok);
892         num_args++;
893         XtSetArg(args[num_args], nhStr(XtNsensitive), how != PICK_NONE);
894         num_args++;
895         XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
896         num_args++;
897         XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
898         num_args++;
899         XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
900         num_args++;
901         XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
902         num_args++;
903 /*JP
904         cancel = XtCreateManagedWidget("cancel", commandWidgetClass, form,
905 */
906         cancel = XtCreateManagedWidget("\83L\83\83\83\93\83Z\83\8b", commandWidgetClass, form,
907                                        args, num_args);
908         XtAddCallback(cancel, XtNcallback, menu_cancel, (XtPointer) wp);
909
910         sens = (how == PICK_ANY);
911         num_args = 0;
912         XtSetArg(args[num_args], nhStr(XtNfromVert), label);
913         num_args++;
914         XtSetArg(args[num_args], nhStr(XtNfromHoriz), cancel);
915         num_args++;
916         XtSetArg(args[num_args], nhStr(XtNsensitive), sens);
917         num_args++;
918         XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
919         num_args++;
920         XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
921         num_args++;
922         XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
923         num_args++;
924         XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
925         num_args++;
926 /*JP
927         all = XtCreateManagedWidget("all", commandWidgetClass, form, args,
928 */
929         all = XtCreateManagedWidget("\82·\82×\82Ä\91I\91ð", commandWidgetClass, form, args,
930                                     num_args);
931         XtAddCallback(all, XtNcallback, menu_all, (XtPointer) wp);
932
933         num_args = 0;
934         XtSetArg(args[num_args], nhStr(XtNfromVert), label);
935         num_args++;
936         XtSetArg(args[num_args], nhStr(XtNfromHoriz), all);
937         num_args++;
938         XtSetArg(args[num_args], nhStr(XtNsensitive), sens);
939         num_args++;
940         XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
941         num_args++;
942         XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
943         num_args++;
944         XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
945         num_args++;
946         XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
947         num_args++;
948 /*JP
949         none = XtCreateManagedWidget("none", commandWidgetClass, form, args,
950 */
951         none = XtCreateManagedWidget("\82·\82×\82Ä\89ð\8f\9c", commandWidgetClass, form, args,
952                                      num_args);
953         XtAddCallback(none, XtNcallback, menu_none, (XtPointer) wp);
954
955         num_args = 0;
956         XtSetArg(args[num_args], nhStr(XtNfromVert), label);
957         num_args++;
958         XtSetArg(args[num_args], nhStr(XtNfromHoriz), none);
959         num_args++;
960         XtSetArg(args[num_args], nhStr(XtNsensitive), sens);
961         num_args++;
962         XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
963         num_args++;
964         XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
965         num_args++;
966         XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
967         num_args++;
968         XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
969         num_args++;
970 /*JP
971         invert = XtCreateManagedWidget("invert", commandWidgetClass, form,
972 */
973         invert = XtCreateManagedWidget("\94½\93]", commandWidgetClass, form,
974                                        args, num_args);
975         XtAddCallback(invert, XtNcallback, menu_invert, (XtPointer) wp);
976
977         num_args = 0;
978         XtSetArg(args[num_args], nhStr(XtNfromVert), label);
979         num_args++;
980         XtSetArg(args[num_args], nhStr(XtNfromHoriz), invert);
981         num_args++;
982         XtSetArg(args[num_args], nhStr(XtNsensitive), how != PICK_NONE);
983         num_args++;
984         XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
985         num_args++;
986         XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
987         num_args++;
988         XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
989         num_args++;
990         XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
991         num_args++;
992 /*JP
993         search = XtCreateManagedWidget("search", commandWidgetClass, form,
994 */
995         search = XtCreateManagedWidget("\8c\9f\8dõ", commandWidgetClass, form,
996                                        args, num_args);
997         XtAddCallback(search, XtNcallback, menu_search, (XtPointer) wp);
998
999         num_args = 0;
1000         XtSetArg(args[num_args], nhStr(XtNallowVert), True);
1001         num_args++;
1002         XtSetArg(args[num_args], nhStr(XtNallowHoriz), False);
1003         num_args++;
1004         XtSetArg(args[num_args], nhStr(XtNuseBottom), True);
1005         num_args++;
1006         XtSetArg(args[num_args], nhStr(XtNuseRight), True);
1007         num_args++;
1008         /*
1009                 XtSetArg(args[num_args], nhStr(XtNforceBars), True);
1010            num_args++;
1011         */
1012         XtSetArg(args[num_args], nhStr(XtNfromVert), all);
1013         num_args++;
1014         XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
1015         num_args++;
1016         XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom);
1017         num_args++;
1018         XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
1019         num_args++;
1020         XtSetArg(args[num_args], nhStr(XtNright), XtChainRight);
1021         num_args++;
1022         viewport_widget = XtCreateManagedWidget(
1023             "menu_viewport",           /* name */
1024             viewportWidgetClass, form, /* parent widget */
1025             args, num_args);           /* values, and number of values */
1026
1027         /* make new menu the current menu */
1028         move_menu(&menu_info->new_menu, &menu_info->curr_menu);
1029
1030         num_args = 0;
1031         XtSetArg(args[num_args], nhStr(XtNforceColumns), True);
1032         num_args++;
1033         XtSetArg(args[num_args], nhStr(XtNcolumnSpacing), 1);
1034         num_args++;
1035         XtSetArg(args[num_args], nhStr(XtNdefaultColumns), 1);
1036         num_args++;
1037         XtSetArg(args[num_args], nhStr(XtNlist),
1038                  menu_info->curr_menu.list_pointer);
1039         num_args++;
1040 #ifdef USE_FWF
1041         XtSetArg(args[num_args], nhStr(XtNsensitiveArray),
1042                  menu_info->curr_menu.sensitive);
1043         num_args++;
1044         XtSetArg(args[num_args], nhStr(XtNmaxSelectable),
1045                  menu_info->curr_menu.count);
1046         num_args++;
1047 #endif
1048 #if defined(X11R6) && defined(XI18N)
1049         XtSetArg(args[num_args], XtNinternational, True);
1050         num_args++;
1051 #endif
1052         wp->w = XtCreateManagedWidget("menu_list", /* name */
1053 #ifdef USE_FWF
1054                                       xfwfMultiListWidgetClass,
1055 #else
1056                                       listWidgetClass,
1057 #endif
1058                                       viewport_widget, /* parent widget */
1059                                       args,            /* set some values */
1060                                       num_args); /* number of values to set */
1061
1062         XtAddCallback(wp->w, XtNcallback, menu_select, (XtPointer) 0);
1063
1064         /* Get the font and margin information. */
1065         num_args = 0;
1066 #ifndef XI18N
1067         XtSetArg(args[num_args], XtNfont, &menu_info->fs);
1068 #else
1069         XtSetArg(args[num_args], XtNfontSet, &menu_info->fontset);
1070 #endif
1071         num_args++;
1072         XtSetArg(args[num_args], XtNinternalHeight,
1073                  &menu_info->internal_height);
1074         num_args++;
1075         XtSetArg(args[num_args], XtNinternalWidth,
1076                  &menu_info->internal_width);
1077         num_args++;
1078         XtSetArg(args[num_args], nhStr(XtNrowSpacing), &row_spacing);
1079         num_args++;
1080         XtGetValues(wp->w, args, num_args);
1081
1082         /* font height is ascent + descent */
1083 #ifndef XI18N
1084         menu_info->line_height = menu_info->fs->max_bounds.ascent
1085                                  + menu_info->fs->max_bounds.descent
1086                                  + row_spacing;
1087 #else
1088         extent = XExtentsOfFontSet(menu_info->fontset);
1089         menu_info->line_height =
1090                 extent->max_logical_extent.height + row_spacing;
1091 #endif
1092
1093         menu_info->valid_widgets = TRUE;
1094
1095         num_args = 0;
1096         XtSetArg(args[num_args], XtNwidth, &v_pixel_width);
1097         num_args++;
1098         XtSetArg(args[num_args], XtNheight, &v_pixel_height);
1099         num_args++;
1100         XtGetValues(wp->w, args, num_args);
1101     } else {
1102         Dimension len;
1103
1104         viewport_widget = XtParent(wp->w);
1105
1106         /* get the longest string on new menu */
1107         v_pixel_width = 0;
1108         for (ptr = menu_info->new_menu.list_pointer; *ptr; ptr++) {
1109 #ifndef XI18N
1110             len = XTextWidth(menu_info->fs, *ptr, strlen(*ptr));
1111 #else
1112             len = XmbTextEscapement(menu_info->fontset, *ptr, strlen(*ptr));
1113 #endif
1114             if (len > v_pixel_width)
1115                 v_pixel_width = len;
1116         }
1117
1118         /* add viewport internal border */
1119         v_pixel_width += 2 * menu_info->internal_width;
1120         v_pixel_height =
1121             (2 * menu_info->internal_height)
1122             + (menu_info->new_menu.count * menu_info->line_height);
1123
1124         /* make new menu the current menu */
1125         move_menu(&menu_info->new_menu, &menu_info->curr_menu);
1126 #ifdef USE_FWF
1127         XfwfMultiListSetNewData((XfwfMultiListWidget) wp->w,
1128                                 menu_info->curr_menu.list_pointer, 0, 0, TRUE,
1129                                 menu_info->curr_menu.sensitive);
1130 #else
1131         XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, TRUE);
1132 #endif
1133     }
1134
1135     /* if viewport will be bigger than the screen, limit its height */
1136     num_args = 0;
1137     XtSetArg(args[num_args], XtNwidth, &v_pixel_width);
1138     num_args++;
1139     XtSetArg(args[num_args], XtNheight, &v_pixel_height);
1140     num_args++;
1141     XtGetValues(wp->w, args, num_args);
1142     if ((Dimension) XtScreen(wp->w)->height * 5 / 6 < v_pixel_height) {
1143         /* scrollbar is 14 pixels wide.  Widen the form to accommodate it. */
1144         v_pixel_width += 14;
1145
1146         /* shrink to fit vertically */
1147         v_pixel_height = XtScreen(wp->w)->height * 5 / 6;
1148
1149         num_args = 0;
1150         XtSetArg(args[num_args], XtNwidth, v_pixel_width);
1151         num_args++;
1152         XtSetArg(args[num_args], XtNheight, v_pixel_height);
1153         num_args++;
1154         XtSetValues(wp->w, args, num_args);
1155     }
1156     XtRealizeWidget(wp->popup); /* need to realize before we position */
1157
1158     /* if menu is not up, position it */
1159     if (!menu_info->is_up)
1160         positionpopup(wp->popup, FALSE);
1161
1162     menu_info->is_up = TRUE;
1163     if (window == WIN_INVEN && how == PICK_NONE) {
1164         /* cant use nh_XtPopup() because it may try to grab the focus */
1165         XtPopup(wp->popup, (int) XtGrabNone);
1166         if (!updated_inventory)
1167             XMapRaised(XtDisplay(wp->popup), XtWindow(wp->popup));
1168         XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
1169                         &wm_delete_window, 1);
1170         retval = 0;
1171     } else {
1172         menu_info->is_active = TRUE; /* waiting for user response */
1173         menu_info->cancelled = FALSE;
1174         nh_XtPopup(wp->popup, (int) XtGrabExclusive, wp->w);
1175         (void) x_event(EXIT_ON_EXIT);
1176         menu_info->is_active = FALSE;
1177         if (menu_info->cancelled)
1178             return -1;
1179
1180         retval = 0;
1181         for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
1182             if (curr->selected)
1183                 retval++;
1184
1185         if (retval) {
1186             menu_item *mi;
1187
1188             *menu_list = mi = (menu_item *) alloc(retval * sizeof(menu_item));
1189             for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
1190                 if (curr->selected) {
1191                     mi->item = curr->identifier;
1192                     mi->count = curr->pick_count;
1193                     mi++;
1194                 }
1195         }
1196     }
1197
1198     return retval;
1199 }
1200
1201 /* End global functions ====================================================
1202  */
1203
1204 /*
1205  * Allocate a copy of the given string.  If null, return a string of
1206  * zero length.
1207  *
1208  * This is an exact duplicate of copy_of() in tty/wintty.c.
1209  */
1210 static char *
1211 copy_of(s)
1212 const char *s;
1213 {
1214     if (!s)
1215         s = "";
1216     return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s);
1217 }
1218
1219 static void
1220 move_menu(src_menu, dest_menu)
1221 struct menu *src_menu, *dest_menu;
1222 {
1223     free_menu(dest_menu);   /* toss old menu */
1224     *dest_menu = *src_menu; /* make new menu current */
1225     /* leave no dangling ptrs */
1226     reset_menu_to_default(src_menu);
1227 }
1228
1229 static void
1230 free_menu(mp)
1231 struct menu *mp;
1232 {
1233     while (mp->base) {
1234         mp->last = mp->base;
1235         mp->base = mp->base->next;
1236
1237         free((genericptr_t) mp->last->str);
1238         free((genericptr_t) mp->last);
1239     }
1240     if (mp->query)
1241         free((genericptr_t) mp->query);
1242     if (mp->gacc)
1243         free((genericptr_t) mp->gacc);
1244     if (mp->list_pointer)
1245         free((genericptr_t) mp->list_pointer);
1246     if (mp->sensitive)
1247         free((genericptr_t) mp->sensitive);
1248     reset_menu_to_default(mp);
1249 }
1250
1251 static void
1252 reset_menu_to_default(mp)
1253 struct menu *mp;
1254 {
1255     mp->base = mp->last = (x11_menu_item *) 0;
1256     mp->query = (const char *) 0;
1257     mp->gacc = (const char *) 0;
1258     mp->count = 0;
1259     mp->list_pointer = (String *) 0;
1260     mp->sensitive = (Boolean *) 0;
1261     mp->curr_selector = 'a'; /* first accelerator */
1262 }
1263
1264 static void
1265 clear_old_menu(wp)
1266 struct xwindow *wp;
1267 {
1268     struct menu_info_t *menu_info = wp->menu_information;
1269
1270     free_menu(&menu_info->curr_menu);
1271     free_menu(&menu_info->new_menu);
1272
1273     if (menu_info->valid_widgets) {
1274         nh_XtPopdown(wp->popup);
1275         menu_info->is_up = FALSE;
1276         XtDestroyWidget(wp->popup);
1277         menu_info->valid_widgets = FALSE;
1278         wp->w = wp->popup = (Widget) 0;
1279     }
1280 }
1281
1282 void
1283 create_menu_window(wp)
1284 struct xwindow *wp;
1285 {
1286     wp->type = NHW_MENU;
1287     wp->menu_information =
1288         (struct menu_info_t *) alloc(sizeof(struct menu_info_t));
1289     (void) memset((genericptr_t) wp->menu_information, '\0',
1290                   sizeof(struct menu_info_t));
1291     reset_menu_to_default(&wp->menu_information->curr_menu);
1292     reset_menu_to_default(&wp->menu_information->new_menu);
1293     reset_menu_count(wp->menu_information);
1294     wp->w = wp->popup = (Widget) 0;
1295 }
1296
1297 void
1298 destroy_menu_window(wp)
1299 struct xwindow *wp;
1300 {
1301     clear_old_menu(wp); /* this will also destroy the widgets */
1302     free((genericptr_t) wp->menu_information);
1303     wp->menu_information = (struct menu_info_t *) 0;
1304     wp->type = NHW_NONE; /* allow re-use */
1305 }
1306
1307 /*winmenu.c*/