OSDN Git Service

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