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. */
6 * File for creating menus.
8 * + Global functions: start_menu, add_menu, end_menu, select_menu
10 /*#define USE_FWF*/ /* use FWF's list widget */
13 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
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>
26 #include <X11/Xfwf/MultiList.h>
28 #include <X11/Xaw/List.h>
32 #ifdef PRESERVE_NO_SYSV
36 # undef PRESERVE_NO_SYSV
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 *));
59 static void FDECL(sync_selected, (struct menu_info_t *, int, int *));
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 *));
68 #define reset_menu_count(mi) ((mi)->counting = FALSE, (mi)->menu_count = 0L)
71 static const char menu_translations[] =
73 <Key>Left: scroll(4)\n\
74 <Key>Right: scroll(6)\n\
76 <Key>Down: scroll(2)\n\
84 menu_select(w, client_data, call_data)
86 XtPointer client_data, call_data;
89 struct menu_info_t *menu_info;
91 XfwfMultiListReturnStruct *lrs = (XfwfMultiListReturnStruct *) call_data;
93 XawListReturnStruct *lrs = (XawListReturnStruct *) call_data;
100 menu_info = wp->menu_information;
101 how_many = menu_info->counting ? menu_info->menu_count : -1L;
102 reset_menu_count(menu_info);
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?");
110 case XfwfMultiListActionStatus:
111 pline("menu_select: status action?");
113 case XfwfMultiListActionHighlight:
114 case XfwfMultiListActionUnhighlight:
115 sync_selected(menu_info,lrs->num_selected,lrs->selected_items);
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!");
123 XawListUnhighlight(w); /* unhilight item */
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) {
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;
138 curr->pick_count = -1L;
140 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True);
143 if (menu_info->how == PICK_ONE)
148 * Called when menu window is deleted.
152 menu_delete(w, event, params, num_params)
156 Cardinal *num_params;
158 menu_cancel((Widget)None, (XtPointer) find_widget(w), (XtPointer) 0);
162 * Invert the count'th line (curr) in the given window.
166 invert_line(wp, curr, which, how_many)
172 reset_menu_count(wp->menu_information);
173 curr->selected = !curr->selected;
174 if (curr->selected) {
176 XfwfMultiListHighlightItem((XfwfMultiListWidget)wp->w, which);
178 curr->str[2] = (how_many != -1) ? '#' : '+';
180 curr->pick_count = how_many;
183 XfwfMultiListUnhighlightItem((XfwfMultiListWidget)wp->w, which);
187 curr->pick_count = -1L;
192 * Called when we get a key press event on a menu window.
196 menu_key(w, event, params, num_params)
200 Cardinal *num_params;
202 struct menu_info_t *menu_info;
209 menu_info = wp->menu_information;
211 ch = key_event_to_char((XKeyEvent *) event);
213 if (ch == '\0') { /* don't accept nul char/modifier event */
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);
228 } else if (ch == '\n' || ch == '\r') {
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))
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;
240 } else if (ch == MENU_SEARCH) { /* search */
241 if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) {
243 X11_getlin("Search for:", buf);
244 if (!*buf || *buf == '\033') return;
245 if (menu_info->how == PICK_ANY) {
246 invert_match(wp, buf);
249 select_match(wp, buf);
255 } else if (ch == MENU_SELECT_ALL) { /* select all */
256 if (menu_info->how == PICK_ANY)
261 } else if (ch == MENU_UNSELECT_ALL) { /* unselect all */
262 if (menu_info->how == PICK_ANY)
267 } else if (ch == MENU_INVERT_ALL) { /* invert all */
268 if (menu_info->how == PICK_ANY)
273 } else if (index(menu_info->curr_menu.gacc, ch)) {
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 */
289 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True);
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;
301 invert_line(wp, curr, count,
302 menu_info->counting ? menu_info->menu_count : -1L);
304 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True);
306 selected_something = curr->selected;
308 X11_nhbell(); /* no match */
310 if (!(selected_something && menu_info->how == PICK_ONE))
311 return; /* keep going */
314 } else { /* permanent inventory window */
319 /* pop down on ESC */
328 menu_ok(w, client_data, call_data)
330 XtPointer client_data, call_data;
332 struct xwindow *wp = (struct xwindow *) client_data;
339 menu_cancel(w, client_data, call_data)
340 Widget w; /* don't use - may be None */
341 XtPointer client_data, call_data;
343 struct xwindow *wp = (struct xwindow *) client_data;
345 if (wp->menu_information->is_active) {
347 wp->menu_information->cancelled = TRUE;
354 menu_all(w, client_data, call_data)
356 XtPointer client_data, call_data;
358 select_all((struct xwindow *) client_data);
363 menu_none(w, client_data, call_data)
365 XtPointer client_data, call_data;
367 select_none((struct xwindow *) client_data);
372 menu_invert(w, client_data, call_data)
374 XtPointer client_data, call_data;
376 invert_all((struct xwindow *) client_data);
381 menu_search(w, client_data, call_data)
383 XtPointer client_data, call_data;
385 struct xwindow *wp = (struct xwindow *) client_data;
386 struct menu_info_t *menu_info = wp->menu_information;
389 X11_getlin("Search for:", buf);
390 if (!*buf || *buf == '\033') return;
392 if (menu_info->how == PICK_ANY)
393 invert_match(wp, buf);
395 select_match(wp, buf);
397 if (menu_info->how == PICK_ONE)
407 boolean changed = FALSE;
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);
420 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer,
431 boolean changed = FALSE;
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);
444 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer,
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);
463 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer,
469 invert_match(wp, match)
475 boolean changed = FALSE;
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);
487 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer,
493 select_match(wp, match)
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);
507 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer,
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 */
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.
537 sync_selected(menu_info, num_selected, items)
538 struct menu_info_t *menu_info;
546 for (i=0, curr = menu_info->curr_menu.base; curr; i++, curr = curr->next) {
548 for (j = 0, ip = items; j < num_selected; j++, ip++)
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);
559 curr->selected = found ? TRUE : FALSE;
565 /* Global functions ======================================================== */
568 X11_start_menu(window)
574 wp = &window_list[window];
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);
580 wp->menu_information->is_menu = TRUE;
586 X11_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
588 int glyph; /* unused (for now) */
589 const anything *identifier;
591 char gch; /* group accelerator (0 = no group) */
597 struct menu_info_t *menu_info;
600 menu_info = window_list[window].menu_information;
601 if (!menu_info->is_menu) {
602 impossible("add_menu: called before start_menu");
606 item = (x11_menu_item *) alloc((unsigned)sizeof(x11_menu_item));
607 item->next = (x11_menu_item *) 0;
608 item->identifier = *identifier;
610 /* item->selected = preselected; */
611 item->selected = FALSE;
612 item->pick_count = -1L;
614 if (identifier->a_void) {
616 int len = strlen(str);
619 /* Supply a keyboard accelerator. Only the first 52 get one. */
621 if (menu_info->new_menu.curr_selector) {
622 ch = menu_info->new_menu.curr_selector++;
624 menu_info->new_menu.curr_selector = 'A';
626 menu_info->new_menu.curr_selector = 0; /* out */
631 /* We *think* everything's coming in off at most BUFSZ bufs... */
632 impossible("Menu item too long (%d).", len);
635 Sprintf(buf, "%c - ", ch ? ch : ' ');
636 (void) strncpy(buf+4, str, len);
638 item->str = copy_of(buf);
640 /* no keyboard accelerator */
641 item->str = copy_of(str);
646 item->gselector = gch;
648 if (menu_info->new_menu.last) {
649 menu_info->new_menu.last->next = item;
651 menu_info->new_menu.base = item;
653 menu_info->new_menu.last = item;
654 menu_info->new_menu.count++;
658 X11_end_menu(window, query)
662 struct menu_info_t *menu_info;
665 menu_info = window_list[window].menu_information;
666 if (!menu_info->is_menu) {
667 impossible("end_menu: called before start_menu");
670 menu_info->new_menu.query = copy_of(query);
674 X11_select_menu(window, how, menu_list)
677 menu_item **menu_list;
681 struct menu_info_t *menu_info;
686 Dimension v_pixel_width, v_pixel_height;
688 Widget viewport_widget, form, label, ok, cancel, all, none, invert, search;
693 char gacc[QBUFSZ], *ap;
695 *menu_list = (menu_item *) 0;
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");
704 menu_info->how = (short) how;
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 */
710 if (menu_info->how != PICK_NONE) {
712 #define GSELIDX(c) ((c) & 127) /* guard against `signed char' */
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) {
718 ++gcnt[GSELIDX(curr->gselector)];
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() */
731 menu_info->new_menu.gacc = copy_of(gacc);
732 reset_menu_count(menu_info);
735 * Create a string and sensitive list for the new menu.
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 */
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);
749 menu_info->new_menu.sensitive = (Boolean *) 0;
751 labeled = (menu_info->new_menu.query && *(menu_info->new_menu.query))
755 * Menus don't appear to size components correctly, except
756 * when first created. For 3.2.0 release, just recreate
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;
766 if (!menu_info->valid_widgets) {
767 Dimension row_spacing;
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()"));
781 XtSetArg(args[num_args], XtNtranslations,
782 XtParseTranslationTable(menu_translations)); num_args++;
783 form = XtCreateManagedWidget("mform",
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++;
796 label = XtCreateManagedWidget(menu_info->new_menu.query,
803 * Create ok, cancel, all, none, invert, and search buttons..
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",
815 XtAddCallback(ok, XtNcallback, menu_ok, (XtPointer) wp);
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",
829 XtAddCallback(cancel, XtNcallback, menu_cancel, (XtPointer) wp);
831 sens = (how == PICK_ANY);
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",
844 XtAddCallback(all, XtNcallback, menu_all, (XtPointer) wp);
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",
858 XtAddCallback(none, XtNcallback, menu_none, (XtPointer) wp);
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",
872 XtAddCallback(invert, XtNcallback, menu_invert, (XtPointer) wp);
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",
886 XtAddCallback(search, XtNcallback, menu_search, (XtPointer) wp);
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++;
894 XtSetArg(args[num_args], XtNforceBars, True); num_args++;
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 */
904 form, /* parent widget */
905 args, num_args); /* values, and number of values */
907 /* make new menu the current menu */
908 move_menu(&menu_info->new_menu, &menu_info->curr_menu);
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++;
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++;
922 wp->w = XtCreateManagedWidget(
923 "menu_list", /* name */
925 xfwfMultiListWidgetClass,
929 viewport_widget, /* parent widget */
930 args, /* set some values */
931 num_args); /* number of values to set */
933 XtAddCallback(wp->w, XtNcallback, menu_select, (XtPointer) 0);
935 /* Get the font and margin information. */
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);
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;
950 menu_info->valid_widgets = TRUE;
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);
959 viewport_widget = XtParent(wp->w);
961 /* get the longest string on new menu */
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;
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);
973 /* make new menu the current menu */
974 move_menu(&menu_info->new_menu, &menu_info->curr_menu);
976 XfwfMultiListSetNewData((XfwfMultiListWidget)wp->w,
977 menu_info->curr_menu.list_pointer, 0, 0, TRUE,
978 menu_info->curr_menu.sensitive);
980 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, TRUE);
984 /* if viewport will be bigger than the screen, limit its height */
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. */
993 /* shrink to fit vertically */
994 v_pixel_height = XtScreen(wp->w)->height * 5 / 6;
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);
1001 XtRealizeWidget(wp->popup); /* need to realize before we position */
1003 /* if menu is not up, position it */
1004 if (!menu_info->is_up) positionpopup(wp->popup, FALSE);
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);
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)
1025 for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
1026 if (curr->selected) retval++;
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;
1044 /* End global functions ==================================================== */
1047 * Allocate a copy of the given string. If null, return a string of
1050 * This is an exact duplicate of copy_of() in tty/wintty.c.
1057 return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s);
1062 move_menu(src_menu, dest_menu)
1063 struct menu *src_menu, *dest_menu;
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);
1077 mp->last = mp->base;
1078 mp->base = mp->base->next;
1080 free((genericptr_t)mp->last->str);
1081 free((genericptr_t)mp->last);
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);
1091 reset_menu_to_default(mp)
1094 mp->base = mp->last = (x11_menu_item *)0;
1095 mp->query = (const char *)0;
1096 mp->gacc = (const char *)0;
1098 mp->list_pointer = (String *)0;
1099 mp->sensitive = (Boolean *)0;
1100 mp->curr_selector = 'a'; /* first accelerator */
1107 struct menu_info_t *menu_info = wp->menu_information;
1109 free_menu(&menu_info->curr_menu);
1110 free_menu(&menu_info->new_menu);
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;
1122 create_menu_window(wp)
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;
1137 destroy_menu_window(wp)
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 */