OSDN Git Service

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