1 /* NetHack 3.6 winmenu.c $NHDT-Date: 1432512809 2015/05/25 00:13:29 $ $NHDT-Branch: master $:$NHDT-Revision: 1.12 $ */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
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
43 static void FDECL(menu_select, (Widget, XtPointer, XtPointer));
44 static void FDECL(invert_line,
45 (struct xwindow *, x11_menu_item *, int, long));
46 static void FDECL(menu_ok, (Widget, XtPointer, XtPointer));
47 static void FDECL(menu_cancel, (Widget, XtPointer, XtPointer));
48 static void FDECL(menu_all, (Widget, XtPointer, XtPointer));
49 static void FDECL(menu_none, (Widget, XtPointer, XtPointer));
50 static void FDECL(menu_invert, (Widget, XtPointer, XtPointer));
51 static void FDECL(menu_search, (Widget, XtPointer, XtPointer));
52 static void FDECL(select_all, (struct xwindow *));
53 static void FDECL(select_none, (struct xwindow *));
54 static void FDECL(select_match, (struct xwindow *, char *));
55 static void FDECL(invert_all, (struct xwindow *));
56 static void FDECL(invert_match, (struct xwindow *, char *));
57 static void FDECL(menu_popdown, (struct xwindow *));
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)
70 static const char menu_translations[] = "#override\n\
71 <Key>Left: scroll(4)\n\
72 <Key>Right: scroll(6)\n\
74 <Key>Down: scroll(2)\n\
82 menu_select(w, client_data, call_data)
84 XtPointer client_data, call_data;
87 struct menu_info_t *menu_info;
89 XfwfMultiListReturnStruct *lrs = (XfwfMultiListReturnStruct *) call_data;
91 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++) {
121 panic("menu_select: out of menu items!");
124 XawListUnhighlight(w); /* unhilight item */
126 /* if the menu is not active or don't have an identifier, try again */
127 if (!menu_info->is_active || curr->identifier.a_void == 0) {
132 /* if we've reached here, we've found our selected item */
133 curr->selected = !curr->selected;
134 if (curr->selected) {
135 curr->str[2] = (how_many != -1L) ? '#' : '+';
136 curr->pick_count = how_many;
139 curr->pick_count = -1L;
141 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True);
144 if (menu_info->how == PICK_ONE)
149 * Called when menu window is deleted.
153 menu_delete(w, event, params, num_params)
157 Cardinal *num_params;
163 menu_cancel((Widget) None, (XtPointer) find_widget(w), (XtPointer) 0);
167 * Invert the count'th line (curr) in the given window.
171 invert_line(wp, curr, which, how_many)
180 reset_menu_count(wp->menu_information);
181 curr->selected = !curr->selected;
182 if (curr->selected) {
184 XfwfMultiListHighlightItem((XfwfMultiListWidget) wp->w, which);
186 curr->str[2] = (how_many != -1) ? '#' : '+';
188 curr->pick_count = how_many;
191 XfwfMultiListUnhighlightItem((XfwfMultiListWidget) wp->w, which);
195 curr->pick_count = -1L;
200 * Called when we get a key press event on a menu window.
204 menu_key(w, event, params, num_params)
208 Cardinal *num_params;
210 struct menu_info_t *menu_info;
215 boolean selected_something;
221 menu_info = wp->menu_information;
223 ch = key_event_to_char((XKeyEvent *) event);
225 if (ch == '\0') { /* don't accept nul char/modifier event */
230 if (menu_info->is_active) { /* waiting for input */
231 /* first check for an explicit selector match, so that it won't be
232 overridden if it happens to duplicate a mapped menu command (':'
233 to look inside a container vs ':' to select via search string) */
234 for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
235 if (curr->identifier.a_void != 0 && curr->selector == ch)
238 ch = map_menu_cmd(ch);
239 if (ch == '\033') { /* quit */
240 if (menu_info->counting) {
241 /* when there's a count in progress, ESC discards it
242 rather than dismissing the whole menu */
243 reset_menu_count(menu_info);
247 } else if (ch == '\n' || ch == '\r') {
249 } else if (isdigit(ch)) {
250 /* special case: '0' is also the default ball class */
251 if (ch == '0' && !menu_info->counting
252 && index(menu_info->curr_menu.gacc, ch))
254 menu_info->menu_count *= 10L;
255 menu_info->menu_count += (long) (ch - '0');
256 if (menu_info->menu_count != 0L) /* ignore leading zeros */
257 menu_info->counting = TRUE;
259 } else if (ch == MENU_SEARCH) { /* search */
260 if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) {
261 char buf[BUFSZ + 2], tmpbuf[BUFSZ];
263 X11_getlin("Search for:", tmpbuf);
264 if (!*tmpbuf || *tmpbuf == '\033')
266 /* convert "string" into "*string*" for use with pmatch() */
267 Sprintf(buf, "*%s*", tmpbuf);
269 if (menu_info->how == PICK_ANY) {
270 invert_match(wp, buf);
273 select_match(wp, buf);
279 } else if (ch == MENU_SELECT_ALL) { /* select all */
280 if (menu_info->how == PICK_ANY)
285 } else if (ch == MENU_UNSELECT_ALL) { /* unselect all */
286 if (menu_info->how == PICK_ANY)
291 } else if (ch == MENU_INVERT_ALL) { /* invert all */
292 if (menu_info->how == PICK_ANY)
297 } else if (index(menu_info->curr_menu.gacc, ch)) {
299 /* matched a group accelerator */
300 if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) {
301 for (count = 0, curr = menu_info->curr_menu.base; curr;
302 curr = curr->next, count++) {
303 if (curr->identifier.a_void != 0
304 && curr->gselector == ch) {
305 invert_line(wp, curr, count, -1L);
306 /* for PICK_ONE, a group accelerator will
307 only be included in gacc[] if it matches
308 exactly one entry, so this must be it... */
309 if (menu_info->how == PICK_ONE)
310 goto menu_done; /* pop down */
314 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0,
322 selected_something = FALSE;
323 for (count = 0, curr = menu_info->curr_menu.base; curr;
324 curr = curr->next, count++)
325 if (curr->identifier.a_void != 0 && curr->selector == ch)
329 invert_line(wp, curr, count, menu_info->counting
330 ? menu_info->menu_count
333 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0,
336 selected_something = curr->selected;
338 X11_nhbell(); /* no match */
340 if (!(selected_something && menu_info->how == PICK_ONE))
341 return; /* keep going */
344 } else { /* permanent inventory window */
349 /* pop down on ESC */
358 menu_ok(w, client_data, call_data)
360 XtPointer client_data, call_data;
362 struct xwindow *wp = (struct xwindow *) client_data;
372 menu_cancel(w, client_data, call_data)
373 Widget w; /* don't use - may be None */
374 XtPointer client_data, call_data;
376 struct xwindow *wp = (struct xwindow *) client_data;
381 if (wp->menu_information->is_active) {
383 wp->menu_information->cancelled = TRUE;
390 menu_all(w, client_data, call_data)
392 XtPointer client_data, call_data;
397 select_all((struct xwindow *) client_data);
402 menu_none(w, client_data, call_data)
404 XtPointer client_data, call_data;
409 select_none((struct xwindow *) client_data);
414 menu_invert(w, client_data, call_data)
416 XtPointer client_data, call_data;
421 invert_all((struct xwindow *) client_data);
426 menu_search(w, client_data, call_data)
428 XtPointer client_data, call_data;
430 struct xwindow *wp = (struct xwindow *) client_data;
431 struct menu_info_t *menu_info = wp->menu_information;
432 char buf[BUFSZ + 2], tmpbuf[BUFSZ];
437 X11_getlin("Search for:", tmpbuf);
438 if (!*tmpbuf || *tmpbuf == '\033')
440 /* convert "string" into "*string*" for use with pmatch() */
441 Sprintf(buf, "*%s*", tmpbuf);
443 if (menu_info->how == PICK_ANY)
444 invert_match(wp, buf);
446 select_match(wp, buf);
448 if (menu_info->how == PICK_ONE)
458 boolean changed = FALSE;
460 reset_menu_count(wp->menu_information);
461 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
462 curr = curr->next, count++)
463 if (curr->identifier.a_void != 0)
464 if (!curr->selected) {
465 invert_line(wp, curr, count, -1L);
471 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0,
482 boolean changed = FALSE;
484 reset_menu_count(wp->menu_information);
485 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
486 curr = curr->next, count++)
487 if (curr->identifier.a_void != 0)
488 if (curr->selected) {
489 invert_line(wp, curr, count, -1L);
495 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0,
507 reset_menu_count(wp->menu_information);
508 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
509 curr = curr->next, count++)
510 if (curr->identifier.a_void != 0)
511 invert_line(wp, curr, count, -1L);
514 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0, 0,
520 invert_match(wp, match)
522 char *match; /* wildcard pattern for pmatch() */
526 boolean changed = FALSE;
528 reset_menu_count(wp->menu_information);
529 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
530 curr = curr->next, count++)
531 if (curr->identifier.a_void != 0 && pmatchi(match, curr->str)) {
532 invert_line(wp, curr, count, -1L);
538 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0,
544 select_match(wp, match)
546 char *match; /* wildcard pattern for pmatch() */
551 reset_menu_count(wp->menu_information);
552 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
553 curr = curr->next, count++)
554 if (curr->identifier.a_void != 0 && pmatchi(match, curr->str)) {
555 if (!curr->selected) {
556 invert_line(wp, curr, count, -1L);
559 wp->menu_information->curr_menu.list_pointer, 0,
574 nh_XtPopdown(wp->popup); /* remove the event grab */
575 if (wp->menu_information->is_active)
576 exit_x_event = TRUE; /* exit our event handler */
577 wp->menu_information->is_up = FALSE; /* menu is down */
582 * Make sure our idea of selected matches the FWF Multilist's idea of what
583 * is currently selected. The MultiList's selected list can change without
584 * notifying us if one or more items are selected and then another is
585 * selected (not toggled). Then the items that were selected are deselected
586 * but we are not notified.
589 sync_selected(menu_info, num_selected, items)
590 struct menu_info_t *menu_info;
598 for (i = 0, curr = menu_info->curr_menu.base; curr;
599 i++, curr = curr->next) {
601 for (j = 0, ip = items; j < num_selected; j++, ip++)
607 if (curr->selected && !found)
608 printf("sync: deselecting %s\n", curr->str);
609 else if (!curr->selected && found)
610 printf("sync: selecting %s\n", curr->str);
612 curr->selected = found ? TRUE : FALSE;
617 /* Global functions ========================================================
621 X11_start_menu(window)
627 wp = &window_list[window];
629 if (wp->menu_information->is_menu) {
630 /* make sure we'ere starting with a clean slate */
631 free_menu(&wp->menu_information->new_menu);
633 wp->menu_information->is_menu = TRUE;
639 X11_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
641 int glyph; /* unused (for now) */
642 const anything *identifier;
644 char gch; /* group accelerator (0 = no group) */
650 struct menu_info_t *menu_info;
653 nhUse(preselected); /*FIXME*/
656 menu_info = window_list[window].menu_information;
657 if (!menu_info->is_menu) {
658 impossible("add_menu: called before start_menu");
662 item = (x11_menu_item *) alloc((unsigned) sizeof(x11_menu_item));
663 item->next = (x11_menu_item *) 0;
664 item->identifier = *identifier;
666 /* item->selected = preselected; */
667 item->selected = FALSE;
668 item->pick_count = -1L;
670 if (identifier->a_void) {
672 int len = strlen(str);
675 /* Supply a keyboard accelerator. Only the first 52 get one. */
677 if (menu_info->new_menu.curr_selector) {
678 ch = menu_info->new_menu.curr_selector++;
680 menu_info->new_menu.curr_selector = 'A';
682 menu_info->new_menu.curr_selector = 0; /* out */
687 /* We *think* everything's coming in off at most BUFSZ bufs... */
688 impossible("Menu item too long (%d).", len);
691 Sprintf(buf, "%c - ", ch ? ch : ' ');
692 (void) strncpy(buf + 4, str, len);
694 item->str = copy_of(buf);
696 /* no keyboard accelerator */
697 item->str = copy_of(str);
702 item->gselector = gch;
704 if (menu_info->new_menu.last) {
705 menu_info->new_menu.last->next = item;
707 menu_info->new_menu.base = item;
709 menu_info->new_menu.last = item;
710 menu_info->new_menu.count++;
714 X11_end_menu(window, query)
718 struct menu_info_t *menu_info;
721 menu_info = window_list[window].menu_information;
722 if (!menu_info->is_menu) {
723 impossible("end_menu: called before start_menu");
726 menu_info->new_menu.query = copy_of(query);
730 X11_select_menu(window, how, menu_list)
733 menu_item **menu_list;
737 struct menu_info_t *menu_info;
742 Dimension v_pixel_width, v_pixel_height;
744 Widget viewport_widget, form, label, ok, cancel, all, none, invert,
750 char gacc[QBUFSZ], *ap;
752 /* XFontSet fontset;*/
753 XFontSetExtents *extent;
756 *menu_list = (menu_item *) 0;
758 wp = &window_list[window];
759 menu_info = wp->menu_information;
760 if (!menu_info->is_menu) {
761 impossible("select_menu: called before start_menu");
765 menu_info->how = (short) how;
767 /* collect group accelerators; for PICK_NONE, they're ignored;
768 for PICK_ONE, only those which match exactly one entry will be
769 accepted; for PICK_ANY, those which match any entry are okay */
771 if (menu_info->how != PICK_NONE) {
773 #define GSELIDX(c) ((c) &127) /* guard against `signed char' */
775 for (i = 0; i < SIZE(gcnt); i++)
777 for (n = 0, curr = menu_info->new_menu.base; curr; curr = curr->next)
778 if (curr->gselector && curr->gselector != curr->selector) {
780 ++gcnt[GSELIDX(curr->gselector)];
783 if (n > 0) /* at least one group accelerator found */
784 for (ap = gacc, curr = menu_info->new_menu.base; curr;
786 if (curr->gselector && !index(gacc, curr->gselector)
787 && (menu_info->how == PICK_ANY
788 || gcnt[GSELIDX(curr->gselector)] == 1)) {
789 *ap++ = curr->gselector;
790 *ap = '\0'; /* re-terminate for index() */
793 menu_info->new_menu.gacc = copy_of(gacc);
794 reset_menu_count(menu_info);
797 * Create a string and sensitive list for the new menu.
799 menu_info->new_menu.list_pointer = ptr = (String *) alloc(
800 (unsigned) (sizeof(String) * (menu_info->new_menu.count + 1)));
801 for (curr = menu_info->new_menu.base; curr; ptr++, curr = curr->next)
802 *ptr = (String) curr->str;
803 *ptr = 0; /* terminate list with null */
806 menu_info->new_menu.sensitive = boolp = (Boolean *) alloc(
807 (unsigned) (sizeof(Boolean) * (menu_info->new_menu.count)));
808 for (curr = menu_info->new_menu.base; curr; boolp++, curr = curr->next)
809 *boolp = (curr->identifier.a_void != 0);
811 menu_info->new_menu.sensitive = (Boolean *) 0;
813 labeled = (menu_info->new_menu.query && *(menu_info->new_menu.query))
818 * Menus don't appear to size components correctly, except
819 * when first created. For 3.2.0 release, just recreate
822 if (menu_info->valid_widgets
823 && (window != WIN_INVEN || !flags.perm_invent)) {
824 XtDestroyWidget(wp->popup);
825 menu_info->valid_widgets = FALSE;
826 menu_info->is_up = FALSE;
829 if (!menu_info->valid_widgets) {
830 Dimension row_spacing;
833 XtSetArg(args[num_args], XtNallowShellResize, True);
836 XtCreatePopupShell(window == WIN_INVEN ? "inventory" : "menu",
837 how == PICK_NONE ? topLevelShellWidgetClass
838 : transientShellWidgetClass,
839 toplevel, args, num_args);
840 XtOverrideTranslations(
842 XtParseTranslationTable("<Message>WM_PROTOCOLS: menu_delete()"));
845 XtSetArg(args[num_args], XtNtranslations,
846 XtParseTranslationTable(menu_translations));
848 form = XtCreateManagedWidget("mform", formWidgetClass, wp->popup,
852 XtSetArg(args[num_args], XtNborderWidth, 0);
854 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
856 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
858 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
860 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
865 XtCreateManagedWidget(menu_info->new_menu.query,
866 labelWidgetClass, form, args, num_args);
871 * Create ok, cancel, all, none, invert, and search buttons..
874 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
876 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
878 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
880 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
882 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
884 ok = XtCreateManagedWidget("OK", commandWidgetClass, form, args,
886 XtAddCallback(ok, XtNcallback, menu_ok, (XtPointer) wp);
889 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
891 XtSetArg(args[num_args], nhStr(XtNfromHoriz), ok);
893 XtSetArg(args[num_args], nhStr(XtNsensitive), how != PICK_NONE);
895 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
897 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
899 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
901 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
904 cancel = XtCreateManagedWidget("cancel", commandWidgetClass, form,
906 cancel = XtCreateManagedWidget("
\83L
\83\83\83\93\83Z
\83\8b", commandWidgetClass, form,
908 XtAddCallback(cancel, XtNcallback, menu_cancel, (XtPointer) wp);
910 sens = (how == PICK_ANY);
912 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
914 XtSetArg(args[num_args], nhStr(XtNfromHoriz), cancel);
916 XtSetArg(args[num_args], nhStr(XtNsensitive), sens);
918 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
920 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
922 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
924 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
927 all = XtCreateManagedWidget("all", commandWidgetClass, form, args,
929 all = XtCreateManagedWidget("
\82·
\82×
\82Ä
\91I
\91ð", commandWidgetClass, form, args,
931 XtAddCallback(all, XtNcallback, menu_all, (XtPointer) wp);
934 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
936 XtSetArg(args[num_args], nhStr(XtNfromHoriz), all);
938 XtSetArg(args[num_args], nhStr(XtNsensitive), sens);
940 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
942 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
944 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
946 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
949 none = XtCreateManagedWidget("none", commandWidgetClass, form, args,
951 none = XtCreateManagedWidget("
\82·
\82×
\82Ä
\89ð
\8f\9c", commandWidgetClass, form, args,
953 XtAddCallback(none, XtNcallback, menu_none, (XtPointer) wp);
956 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
958 XtSetArg(args[num_args], nhStr(XtNfromHoriz), none);
960 XtSetArg(args[num_args], nhStr(XtNsensitive), sens);
962 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
964 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
966 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
968 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
971 invert = XtCreateManagedWidget("invert", commandWidgetClass, form,
973 invert = XtCreateManagedWidget("
\94½
\93]", commandWidgetClass, form,
975 XtAddCallback(invert, XtNcallback, menu_invert, (XtPointer) wp);
978 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
980 XtSetArg(args[num_args], nhStr(XtNfromHoriz), invert);
982 XtSetArg(args[num_args], nhStr(XtNsensitive), how != PICK_NONE);
984 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
986 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
988 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
990 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
993 search = XtCreateManagedWidget("search", commandWidgetClass, form,
995 search = XtCreateManagedWidget("
\8c\9f\8dõ", commandWidgetClass, form,
997 XtAddCallback(search, XtNcallback, menu_search, (XtPointer) wp);
1000 XtSetArg(args[num_args], nhStr(XtNallowVert), True);
1002 XtSetArg(args[num_args], nhStr(XtNallowHoriz), False);
1004 XtSetArg(args[num_args], nhStr(XtNuseBottom), True);
1006 XtSetArg(args[num_args], nhStr(XtNuseRight), True);
1009 XtSetArg(args[num_args], nhStr(XtNforceBars), True);
1012 XtSetArg(args[num_args], nhStr(XtNfromVert), all);
1014 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
1016 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom);
1018 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
1020 XtSetArg(args[num_args], nhStr(XtNright), XtChainRight);
1022 viewport_widget = XtCreateManagedWidget(
1023 "menu_viewport", /* name */
1024 viewportWidgetClass, form, /* parent widget */
1025 args, num_args); /* values, and number of values */
1027 /* make new menu the current menu */
1028 move_menu(&menu_info->new_menu, &menu_info->curr_menu);
1031 XtSetArg(args[num_args], nhStr(XtNforceColumns), True);
1033 XtSetArg(args[num_args], nhStr(XtNcolumnSpacing), 1);
1035 XtSetArg(args[num_args], nhStr(XtNdefaultColumns), 1);
1037 XtSetArg(args[num_args], nhStr(XtNlist),
1038 menu_info->curr_menu.list_pointer);
1041 XtSetArg(args[num_args], nhStr(XtNsensitiveArray),
1042 menu_info->curr_menu.sensitive);
1044 XtSetArg(args[num_args], nhStr(XtNmaxSelectable),
1045 menu_info->curr_menu.count);
1048 #if defined(X11R6) && defined(XI18N)
1049 XtSetArg(args[num_args], XtNinternational, True);
1052 wp->w = XtCreateManagedWidget("menu_list", /* name */
1054 xfwfMultiListWidgetClass,
1058 viewport_widget, /* parent widget */
1059 args, /* set some values */
1060 num_args); /* number of values to set */
1062 XtAddCallback(wp->w, XtNcallback, menu_select, (XtPointer) 0);
1064 /* Get the font and margin information. */
1067 XtSetArg(args[num_args], XtNfont, &menu_info->fs);
1069 XtSetArg(args[num_args], XtNfontSet, &menu_info->fontset);
1072 XtSetArg(args[num_args], XtNinternalHeight,
1073 &menu_info->internal_height);
1075 XtSetArg(args[num_args], XtNinternalWidth,
1076 &menu_info->internal_width);
1078 XtSetArg(args[num_args], nhStr(XtNrowSpacing), &row_spacing);
1080 XtGetValues(wp->w, args, num_args);
1082 /* font height is ascent + descent */
1084 menu_info->line_height = menu_info->fs->max_bounds.ascent
1085 + menu_info->fs->max_bounds.descent
1088 extent = XExtentsOfFontSet(menu_info->fontset);
1089 menu_info->line_height =
1090 extent->max_logical_extent.height + row_spacing;
1093 menu_info->valid_widgets = TRUE;
1096 XtSetArg(args[num_args], XtNwidth, &v_pixel_width);
1098 XtSetArg(args[num_args], XtNheight, &v_pixel_height);
1100 XtGetValues(wp->w, args, num_args);
1104 viewport_widget = XtParent(wp->w);
1106 /* get the longest string on new menu */
1108 for (ptr = menu_info->new_menu.list_pointer; *ptr; ptr++) {
1110 len = XTextWidth(menu_info->fs, *ptr, strlen(*ptr));
1112 len = XmbTextEscapement(menu_info->fontset, *ptr, strlen(*ptr));
1114 if (len > v_pixel_width)
1115 v_pixel_width = len;
1118 /* add viewport internal border */
1119 v_pixel_width += 2 * menu_info->internal_width;
1121 (2 * menu_info->internal_height)
1122 + (menu_info->new_menu.count * menu_info->line_height);
1124 /* make new menu the current menu */
1125 move_menu(&menu_info->new_menu, &menu_info->curr_menu);
1127 XfwfMultiListSetNewData((XfwfMultiListWidget) wp->w,
1128 menu_info->curr_menu.list_pointer, 0, 0, TRUE,
1129 menu_info->curr_menu.sensitive);
1131 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, TRUE);
1135 /* if viewport will be bigger than the screen, limit its height */
1137 XtSetArg(args[num_args], XtNwidth, &v_pixel_width);
1139 XtSetArg(args[num_args], XtNheight, &v_pixel_height);
1141 XtGetValues(wp->w, args, num_args);
1142 if ((Dimension) XtScreen(wp->w)->height * 5 / 6 < v_pixel_height) {
1143 /* scrollbar is 14 pixels wide. Widen the form to accommodate it. */
1144 v_pixel_width += 14;
1146 /* shrink to fit vertically */
1147 v_pixel_height = XtScreen(wp->w)->height * 5 / 6;
1150 XtSetArg(args[num_args], XtNwidth, v_pixel_width);
1152 XtSetArg(args[num_args], XtNheight, v_pixel_height);
1154 XtSetValues(wp->w, args, num_args);
1156 XtRealizeWidget(wp->popup); /* need to realize before we position */
1158 /* if menu is not up, position it */
1159 if (!menu_info->is_up)
1160 positionpopup(wp->popup, FALSE);
1162 menu_info->is_up = TRUE;
1163 if (window == WIN_INVEN && how == PICK_NONE) {
1164 /* cant use nh_XtPopup() because it may try to grab the focus */
1165 XtPopup(wp->popup, (int) XtGrabNone);
1166 if (!updated_inventory)
1167 XMapRaised(XtDisplay(wp->popup), XtWindow(wp->popup));
1168 XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
1169 &wm_delete_window, 1);
1172 menu_info->is_active = TRUE; /* waiting for user response */
1173 menu_info->cancelled = FALSE;
1174 nh_XtPopup(wp->popup, (int) XtGrabExclusive, wp->w);
1175 (void) x_event(EXIT_ON_EXIT);
1176 menu_info->is_active = FALSE;
1177 if (menu_info->cancelled)
1181 for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
1188 *menu_list = mi = (menu_item *) alloc(retval * sizeof(menu_item));
1189 for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
1190 if (curr->selected) {
1191 mi->item = curr->identifier;
1192 mi->count = curr->pick_count;
1201 /* End global functions ====================================================
1205 * Allocate a copy of the given string. If null, return a string of
1208 * This is an exact duplicate of copy_of() in tty/wintty.c.
1216 return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s);
1220 move_menu(src_menu, dest_menu)
1221 struct menu *src_menu, *dest_menu;
1223 free_menu(dest_menu); /* toss old menu */
1224 *dest_menu = *src_menu; /* make new menu current */
1225 /* leave no dangling ptrs */
1226 reset_menu_to_default(src_menu);
1234 mp->last = mp->base;
1235 mp->base = mp->base->next;
1237 free((genericptr_t) mp->last->str);
1238 free((genericptr_t) mp->last);
1241 free((genericptr_t) mp->query);
1243 free((genericptr_t) mp->gacc);
1244 if (mp->list_pointer)
1245 free((genericptr_t) mp->list_pointer);
1247 free((genericptr_t) mp->sensitive);
1248 reset_menu_to_default(mp);
1252 reset_menu_to_default(mp)
1255 mp->base = mp->last = (x11_menu_item *) 0;
1256 mp->query = (const char *) 0;
1257 mp->gacc = (const char *) 0;
1259 mp->list_pointer = (String *) 0;
1260 mp->sensitive = (Boolean *) 0;
1261 mp->curr_selector = 'a'; /* first accelerator */
1268 struct menu_info_t *menu_info = wp->menu_information;
1270 free_menu(&menu_info->curr_menu);
1271 free_menu(&menu_info->new_menu);
1273 if (menu_info->valid_widgets) {
1274 nh_XtPopdown(wp->popup);
1275 menu_info->is_up = FALSE;
1276 XtDestroyWidget(wp->popup);
1277 menu_info->valid_widgets = FALSE;
1278 wp->w = wp->popup = (Widget) 0;
1283 create_menu_window(wp)
1286 wp->type = NHW_MENU;
1287 wp->menu_information =
1288 (struct menu_info_t *) alloc(sizeof(struct menu_info_t));
1289 (void) memset((genericptr_t) wp->menu_information, '\0',
1290 sizeof(struct menu_info_t));
1291 reset_menu_to_default(&wp->menu_information->curr_menu);
1292 reset_menu_to_default(&wp->menu_information->new_menu);
1293 reset_menu_count(wp->menu_information);
1294 wp->w = wp->popup = (Widget) 0;
1298 destroy_menu_window(wp)
1301 clear_old_menu(wp); /* this will also destroy the widgets */
1302 free((genericptr_t) wp->menu_information);
1303 wp->menu_information = (struct menu_info_t *) 0;
1304 wp->type = NHW_NONE; /* allow re-use */