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. */
6 * File for creating menus.
8 * + Global functions: start_menu, add_menu, end_menu, select_menu
12 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
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>
28 #ifdef PRESERVE_NO_SYSV
32 #undef PRESERVE_NO_SYSV
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);
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 *));
67 #define reset_menu_count(mi) ((mi)->counting = FALSE, (mi)->menu_count = 0L)
69 static const char menu_translations[] = "#override\n\
70 <Key>Left: scroll(4)\n\
71 <Key>Right: scroll(6)\n\
73 <Key>Down: scroll(2)\n\
74 <Btn4Down>: scroll(8)\n\
75 <Btn5Down>: scroll(2)\n\
78 static const char menu_entry_translations[] = "#override\n\
79 <Btn4Down>: scroll(8)\n\
80 <Btn5Down>: scroll(2)";
82 XtTranslations menu_entry_translation_table = (XtTranslations) 0;
83 XtTranslations menu_translation_table = (XtTranslations) 0;
84 XtTranslations menu_del_translation_table = (XtTranslations) 0;
87 create_menu_translation_tables()
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()");
100 menu_size_change_handler(w, ptr, event, flag)
106 struct xwindow *wp = (struct xwindow *) ptr;
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);
130 menu_select(w, client_data, call_data)
132 XtPointer client_data, call_data;
134 struct menu_info_t *menu_info;
136 x11_menu_item *curr = (x11_menu_item *) client_data;
145 wp = &window_list[curr->window];
147 menu_info = wp->menu_information;
148 how_many = menu_info->counting ? menu_info->menu_count : -1L;
149 reset_menu_count(menu_info);
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) {
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;
166 curr->pick_count = -1L;
169 XtSetArg(args[0], nhStr(XtNlabel), curr->str);
170 XtSetValues(w, args, ONE);
172 if (menu_info->how == PICK_ONE)
177 * Called when menu window is deleted.
181 menu_delete(w, event, params, num_params)
185 Cardinal *num_params;
191 menu_cancel((Widget) None, (XtPointer) find_widget(w), (XtPointer) 0);
195 * Invert the count'th line (curr) in the given window.
199 invert_line(wp, curr, which, how_many)
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;
221 XtSetArg(args[0], nhStr(XtNlabel), curr->str);
222 XtSetValues(curr->w, args, ONE);
223 curr->pick_count = -1L;
228 * Called when we get a key press event on a menu window.
232 menu_key(w, event, params, num_params)
236 Cardinal *num_params;
238 struct menu_info_t *menu_info;
243 boolean selected_something;
249 menu_info = wp->menu_information;
251 ch = key_event_to_char((XKeyEvent *) event);
253 if (ch == '\0') { /* don't accept nul char/modifier event */
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)
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);
276 } else if (ch == '\n' || ch == '\r') {
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))
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;
288 } else if (ch == MENU_SEARCH) { /* search */
290 if (menu_info->how == PICK_ANY)
292 } else if (ch == MENU_SELECT_ALL || ch == MENU_SELECT_PAGE) {
293 if (menu_info->how == PICK_ANY)
298 } else if (ch == MENU_UNSELECT_ALL || ch == MENU_UNSELECT_PAGE) {
299 if (menu_info->how == PICK_ANY)
304 } else if (ch == MENU_INVERT_ALL || ch == MENU_INVERT_PAGE) {
305 if (menu_info->how == PICK_ANY)
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;
314 find_scrollbars(wp->w, &hbar, &vbar);
316 XtCallCallbacks(vbar, XtNjumpProc, &top);
318 } else if (ch == MENU_NEXT_PAGE || ch == MENU_PREVIOUS_PAGE) {
319 Widget hbar = (Widget) 0, vbar = (Widget) 0;
321 find_scrollbars(wp->w, &hbar, &vbar);
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);
330 XtCallCallbacks(vbar, XtNjumpProc, &top);
333 } else if (index(menu_info->curr_menu.gacc, ch)) {
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 */
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)
361 invert_line(wp, curr, count,
362 menu_info->counting ? menu_info->menu_count : -1L);
363 selected_something = curr->selected;
365 X11_nhbell(); /* no match */
367 if (!(selected_something && menu_info->how == PICK_ONE))
368 return; /* keep going */
371 } else { /* permanent inventory window */
376 /* pop down on ESC */
385 menu_ok(w, client_data, call_data)
387 XtPointer client_data, call_data;
389 struct xwindow *wp = (struct xwindow *) client_data;
399 menu_cancel(w, client_data, call_data)
400 Widget w; /* don't use - may be None */
401 XtPointer client_data, call_data;
403 struct xwindow *wp = (struct xwindow *) client_data;
408 if (wp->menu_information->is_active) {
410 wp->menu_information->cancelled = TRUE;
417 menu_all(w, client_data, call_data)
419 XtPointer client_data, call_data;
424 select_all((struct xwindow *) client_data);
429 menu_none(w, client_data, call_data)
431 XtPointer client_data, call_data;
436 select_none((struct xwindow *) client_data);
441 menu_invert(w, client_data, call_data)
443 XtPointer client_data, call_data;
448 invert_all((struct xwindow *) client_data);
453 menu_search(w, client_data, call_data)
455 XtPointer client_data, call_data;
457 struct xwindow *wp = (struct xwindow *) client_data;
458 struct menu_info_t *menu_info = wp->menu_information;
464 if (menu_info->how == PICK_ONE)
468 /* common to menu_search and menu_key */
473 char *pat, buf[BUFSZ + 2]; /* room for '*' + BUFSZ-1 + '*' + '\0' */
474 struct menu_info_t *menu_info = wp->menu_information;
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')
482 /* convert "string" into "*string*" for use with pmatch() */
484 *--pat = '*'; /* now points to &buf[0] */
485 if (*(eos(pat) - 1) != '*')
489 switch (menu_info->how) {
491 invert_match(wp, pat);
494 select_match(wp, pat);
496 default: /* PICK_NONE */
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);
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);
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);
552 invert_match(wp, match)
554 char *match; /* wildcard pattern for pmatch() */
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);
566 curr->preselected = FALSE;
572 select_match(wp, match)
574 char *match; /* wildcard pattern for pmatch() */
576 x11_menu_item *curr, *found = 0;
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))
585 curr->preselected = FALSE;
589 if (!found->selected) {
590 invert_line(wp, found, count, -1L);
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 */
610 /* Global functions ======================================================= */
613 X11_start_menu(window)
619 wp = &window_list[window];
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);
625 wp->menu_information->is_menu = TRUE;
631 X11_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
633 int glyph; /* unused (for now) */
634 const anything *identifier;
636 char gch; /* group accelerator (0 = no group) */
642 struct menu_info_t *menu_info;
647 menu_info = window_list[window].menu_information;
648 if (!menu_info->is_menu) {
649 impossible("add_menu: called before start_menu");
653 item = (x11_menu_item *) alloc((unsigned) sizeof(x11_menu_item));
654 item->next = (x11_menu_item *) 0;
655 item->identifier = *identifier;
657 item->selected = item->preselected = preselected;
658 item->pick_count = -1L;
659 item->window = window;
660 item->w = (Widget) 0;
662 if (identifier->a_void) {
664 int len = strlen(str);
667 /* Supply a keyboard accelerator. Only the first 52 get one. */
669 if (menu_info->new_menu.curr_selector) {
670 ch = menu_info->new_menu.curr_selector++;
672 menu_info->new_menu.curr_selector = 'A';
674 menu_info->new_menu.curr_selector = 0; /* out */
679 /* We *think* everything's coming in off at most BUFSZ bufs... */
680 impossible("Menu item too long (%d).", len);
683 Sprintf(buf, "%c %c ", ch ? ch : ' ', preselected ? '+' : '-');
684 (void) strncpy(buf + 4, str, len);
686 item->str = copy_of(buf);
688 /* no keyboard accelerator */
689 item->str = copy_of(str);
694 item->gselector = gch;
696 debugpline2("X11_add_menu(%i,%s)", window, item->str);
698 if (menu_info->new_menu.last) {
699 menu_info->new_menu.last->next = item;
701 menu_info->new_menu.base = item;
703 menu_info->new_menu.last = item;
704 menu_info->new_menu.count++;
708 X11_end_menu(window, query)
712 struct menu_info_t *menu_info;
715 menu_info = window_list[window].menu_information;
716 if (!menu_info->is_menu) {
717 impossible("end_menu: called before start_menu");
720 debugpline2("X11_end_menu(%i, %s)", window, query);
721 menu_info->new_menu.query = copy_of(query);
725 X11_select_menu(window, how, menu_list)
728 menu_item **menu_list;
732 struct menu_info_t *menu_info;
736 Dimension v_pixel_width, v_pixel_height;
738 Widget viewport_widget, form, label, all;
739 char gacc[QBUFSZ], *ap;
742 /* XFontSet fontset;*/
743 XFontSetExtents *extent;
746 *menu_list = (menu_item *) 0;
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");
755 debugpline2("X11_select_menu(%i, %i)", window, how);
757 create_menu_translation_tables();
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];
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;
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);
778 menu_info->how = (short) how;
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 */
784 if (menu_info->how != PICK_NONE) {
786 #define GSELIDX(c) ((c) & 127) /* guard against `signed char' */
788 for (i = 0; i < SIZE(gcnt); i++)
790 for (n = 0, curr = menu_info->new_menu.base; curr; curr = curr->next)
791 if (curr->gselector && curr->gselector != curr->selector) {
793 ++gcnt[GSELIDX(curr->gselector)];
796 if (n > 0) /* at least one group accelerator found */
797 for (ap = gacc, curr = menu_info->new_menu.base; curr;
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() */
806 menu_info->new_menu.gacc = copy_of(gacc);
807 reset_menu_count(menu_info);
809 labeled = (menu_info->new_menu.query && *(menu_info->new_menu.query))
813 permi = (window == WIN_INVEN && iflags.perm_invent && how == PICK_NONE);
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;
825 if (!menu_info->is_up) {
826 menu_info->permi = permi;
829 XtSetArg(args[num_args], XtNallowShellResize, True);
831 if (permi && menu_info->permi_x != -1) {
832 XtSetArg(args[num_args], nhStr(XtNwidth), menu_info->permi_w);
834 XtSetArg(args[num_args], nhStr(XtNheight), menu_info->permi_h);
838 XtSetArg(args[num_args], nhStr(XtNtitle), wp->title); num_args++;
840 wp->popup = XtCreatePopupShell((window == WIN_INVEN)
841 ? "inventory" : "menu",
843 ? topLevelShellWidgetClass
844 : transientShellWidgetClass,
845 toplevel, args, num_args);
846 XtOverrideTranslations(wp->popup, menu_del_translation_table);
849 XtAddEventHandler(wp->popup, StructureNotifyMask, False,
850 menu_size_change_handler, (XtPointer) wp);
853 XtSetArg(args[num_args], XtNtranslations,
854 menu_translation_table); num_args++;
855 form = XtCreateManagedWidget("mform", formWidgetClass, wp->popup,
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++;
865 label = labeled ? XtCreateManagedWidget(menu_info->new_menu.query,
866 labelWidgetClass, form,
870 all = menu_create_buttons(wp, form, label);
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++;
878 XtSetArg(args[num_args], nhStr(XtNforceBars), True); num_args++;
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 */
893 XtSetArg(args[num_args], XtNwidth, 100);
895 XtSetArg(args[num_args], XtNheight, 500);
898 #if defined(X11R6) && defined(XI18N)
899 XtSetArg(args[num_args], XtNinternational, True);
902 wp->w = XtCreateManagedWidget("menu_list", formWidgetClass,
903 viewport_widget, args, num_args);
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);
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);
918 /* if viewport will be bigger than the screen, limit its height */
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. */
927 /* shrink to fit vertically */
928 v_pixel_height = XtScreen(wp->w)->height * 5 / 6;
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);
935 XtRealizeWidget(wp->popup); /* need to realize before we position */
937 /* if menu is not up, position it */
938 if (!menu_info->is_up) {
939 positionpopup(wp->popup, FALSE);
942 menu_info->is_up = TRUE;
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);
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,
958 &menu_info->permi_h);
960 if (!updated_inventory) {
961 XMapRaised(XtDisplay(wp->popup), XtWindow(wp->popup));
963 XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
964 &wm_delete_window, 1);
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)
976 for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
982 boolean toomany = (how == PICK_ONE && 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;
993 if (how == PICK_ONE && !curr->preselected)
997 } /* ?(WIN_INVEN && PICK_NONE) */
1002 /* End global functions =================================================== */
1005 * Allocate a copy of the given string. If null, return a string of
1018 * Create ok, cancel, all, none, invert, and search buttons.
1021 menu_create_buttons(wp, form, under)
1027 int how = wp->menu_information->how;
1029 Widget ok, cancel, all, none, invert, search, lblwidget[6];
1030 Dimension lblwidth[6], maxlblwidth;
1031 Widget label = under;
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,
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];
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++;
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++;
1059 cancel = XtCreateManagedWidget("cancel", commandWidgetClass, form,
1062 cancel = XtCreateManagedWidget("
\83L
\83\83\83\93\83Z
\83\8b", commandWidgetClass, form,
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];
1071 sens = (how == PICK_ANY);
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++;
1081 all = XtCreateManagedWidget("all", commandWidgetClass, form,
1084 all = XtCreateManagedWidget("
\82ยท
\82ร
\82ร
\91I
\91รฐ", commandWidgetClass, form,
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];
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++;
1102 none = XtCreateManagedWidget("none", commandWidgetClass, form,
1105 none = XtCreateManagedWidget("
\82ยท
\82ร
\82ร
\89รฐ
\8f\9c", commandWidgetClass, form,
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];
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++;
1123 invert = XtCreateManagedWidget("invert", commandWidgetClass, form,
1126 invert = XtCreateManagedWidget("
\94ยฝ
\93]", commandWidgetClass, form,
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];
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++;
1144 search = XtCreateManagedWidget("search", commandWidgetClass, form,
1147 search = XtCreateManagedWidget("
\8c\9f\8dรต", commandWidgetClass, form,
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];
1156 /* make all buttons be the same width */
1160 XtSetArg(args[0], XtNwidth, maxlblwidth);
1161 for (i = 0; i < 6; ++i)
1162 if (lblwidth[i] < maxlblwidth)
1163 XtSetValues(lblwidget[i], args, ONE);
1170 menu_create_entries(wp, curr_menu)
1172 struct menu *curr_menu;
1174 x11_menu_item *curr;
1175 int menulineidx = 0;
1176 Widget prevlinewidget;
1177 int how = wp->menu_information->how;
1181 for (curr = curr_menu->base; curr; curr = curr->next) {
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);
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++;
1198 if (!iflags.use_menu_color || wp->menu_information->disable_mcolors
1199 || !get_menu_coloring(curr->str, &color, &attr))
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++;
1208 /* TODO: ATR_DIM, ATR_ULINE, ATR_BLINK */
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++;
1218 XtSetArg(args[num_args], nhStr(XtNfromVert), prevlinewidget); num_args++;
1220 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
1223 XtSetArg(args[num_args], XtNtranslations,
1224 menu_entry_translation_table); num_args++;
1227 Sprintf(tmpbuf, "menuline_%s", (canpick) ? "command" : "label");
1228 curr->w = linewidget = XtCreateManagedWidget(tmpbuf,
1230 ? commandWidgetClass
1232 wp->w, args, num_args);
1234 if (attr == ATR_BOLD) {
1235 load_boldfont(wp, curr->w);
1237 XtSetArg(args[num_args], nhStr(XtNfont),
1238 wp->boldfs); num_args++;
1239 XtSetValues(curr->w, args, num_args);
1243 XtAddCallback(linewidget, XtNcallback, menu_select,
1245 prevlinewidget = linewidget;
1250 destroy_menu_entry_widgets(wp)
1257 x11_menu_item *curr;
1258 struct menu_info_t *menu_info;
1263 menu_info = wp->menu_information;
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)
1271 XtDestroyWidget(curr->w);
1275 move_menu(src_menu, dest_menu)
1276 struct menu *src_menu, *dest_menu;
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);
1285 free_menu_line_entries(mp)
1288 /* We're not freeing menu entry widgets here, but let XtDestroyWidget()
1289 on the parent widget take care of that */
1291 mp->last = mp->base;
1292 mp->base = mp->base->next;
1293 free((genericptr_t) mp->last->str);
1294 free((genericptr_t) mp->last);
1302 free_menu_line_entries(mp);
1304 free((genericptr_t) mp->query);
1306 free((genericptr_t) mp->gacc);
1307 reset_menu_to_default(mp);
1311 reset_menu_to_default(mp)
1314 mp->base = mp->last = (x11_menu_item *) 0;
1315 mp->query = (const char *) 0;
1316 mp->gacc = (const char *) 0;
1318 mp->curr_selector = 'a'; /* first accelerator */
1325 struct menu_info_t *menu_info = wp->menu_information;
1327 free_menu(&menu_info->curr_menu);
1328 free_menu(&menu_info->new_menu);
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;
1339 create_menu_window(wp)
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;
1358 destroy_menu_window(wp)
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 */