/* SCCS Id: @(#)winmenu.c 3.3 96/08/15 */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ /* * File for creating menus. * * + Global functions: start_menu, add_menu, end_menu, select_menu */ /*#define USE_FWF*/ /* use FWF's list widget */ #ifndef SYSV #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ #endif #include #include #include #include #include #include #include #include #include #ifdef USE_FWF #include #else #include #endif #include #ifdef PRESERVE_NO_SYSV # ifdef SYSV # undef SYSV # endif # undef PRESERVE_NO_SYSV #endif #include "hack.h" #include "winX.h" #include static void FDECL(menu_select, (Widget, XtPointer, XtPointer)); static void FDECL(invert_line, (struct xwindow *,x11_menu_item *,int,long)); static void FDECL(menu_ok, (Widget, XtPointer, XtPointer)); static void FDECL(menu_cancel, (Widget, XtPointer, XtPointer)); static void FDECL(menu_all, (Widget, XtPointer, XtPointer)); static void FDECL(menu_none, (Widget, XtPointer, XtPointer)); static void FDECL(menu_invert, (Widget, XtPointer, XtPointer)); static void FDECL(menu_search, (Widget, XtPointer, XtPointer)); static void FDECL(select_all, (struct xwindow *)); static void FDECL(select_none, (struct xwindow *)); static void FDECL(select_match, (struct xwindow *, char*)); static void FDECL(invert_all, (struct xwindow *)); static void FDECL(invert_match, (struct xwindow *, char*)); static void FDECL(menu_popdown, (struct xwindow *)); #ifdef USE_FWF static void FDECL(sync_selected, (struct menu_info_t *, int, int *)); #endif static void FDECL(move_menu, (struct menu *, struct menu *)); static void FDECL(free_menu, (struct menu *)); static void FDECL(reset_menu_to_default, (struct menu *)); static void FDECL(clear_old_menu, (struct xwindow *)); static char *FDECL(copy_of, (const char *)); #define reset_menu_count(mi) ((mi)->counting = FALSE, (mi)->menu_count = 0L) static const char menu_translations[] = "#override\n\ Left: scroll(4)\n\ Right: scroll(6)\n\ Up: scroll(8)\n\ Down: scroll(2)\n\ : menu_key()"; /* * Menu callback. */ /* ARGSUSED */ static void menu_select(w, client_data, call_data) Widget w; XtPointer client_data, call_data; { struct xwindow *wp; struct menu_info_t *menu_info; #ifdef USE_FWF XfwfMultiListReturnStruct *lrs = (XfwfMultiListReturnStruct *) call_data; #else XawListReturnStruct *lrs = (XawListReturnStruct *) call_data; int i; x11_menu_item *curr; #endif long how_many; wp = find_widget(w); menu_info = wp->menu_information; how_many = menu_info->counting ? menu_info->menu_count : -1L; reset_menu_count(menu_info); #ifdef USE_FWF /* if we've reached here, we've found our selected item */ switch (lrs->action) { case XfwfMultiListActionNothing: pline("menu_select: nothing action?"); break; case XfwfMultiListActionStatus: pline("menu_select: status action?"); break; case XfwfMultiListActionHighlight: case XfwfMultiListActionUnhighlight: sync_selected(menu_info,lrs->num_selected,lrs->selected_items); break; } #else for (i = 0, curr = menu_info->curr_menu.base; i < lrs->list_index; i++) { if (!curr) panic("menu_select: out of menu items!"); curr = curr->next; } XawListUnhighlight(w); /* unhilight item */ /* if the menu is not active or don't have an identifier, try again */ if (!menu_info->is_active || curr->identifier.a_void == 0) { X11_nhbell(); return; } /* if we've reached here, we've found our selected item */ curr->selected = !curr->selected; if (curr->selected) { curr->str[2] = (how_many != -1L) ? '#' : '+'; curr->pick_count = how_many; } else { curr->str[2] = '-'; curr->pick_count = -1L; } XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True); #endif if (menu_info->how == PICK_ONE) menu_popdown(wp); } /* * Called when menu window is deleted. */ /* ARGSUSED */ void menu_delete(w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { menu_cancel((Widget)None, (XtPointer) find_widget(w), (XtPointer) 0); } /* * Invert the count'th line (curr) in the given window. */ /*ARGSUSED*/ static void invert_line(wp, curr, which, how_many) struct xwindow *wp; x11_menu_item *curr; int which; long how_many; { reset_menu_count(wp->menu_information); curr->selected = !curr->selected; if (curr->selected) { #ifdef USE_FWF XfwfMultiListHighlightItem((XfwfMultiListWidget)wp->w, which); #else curr->str[2] = (how_many != -1) ? '#' : '+'; #endif curr->pick_count = how_many; } else { #ifdef USE_FWF XfwfMultiListUnhighlightItem((XfwfMultiListWidget)wp->w, which); #else curr->str[2] = '-'; #endif curr->pick_count = -1L; } } /* * Called when we get a key press event on a menu window. */ /* ARGSUSED */ void menu_key(w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { struct menu_info_t *menu_info; x11_menu_item *curr; struct xwindow *wp; char ch; int count; wp = find_widget(w); menu_info = wp->menu_information; ch = key_event_to_char((XKeyEvent *) event); if (ch == '\0') { /* don't accept nul char/modifier event */ /* don't beep */ return; } if (menu_info->is_active) { /* waiting for input */ ch = map_menu_cmd(ch); if (ch == '\033') { /* quit */ if (menu_info->counting) { /* when there's a count in progress, ESC discards it rather than dismissing the whole menu */ reset_menu_count(menu_info); return; } select_none(wp); } else if (ch == '\n' || ch == '\r') { ; /* accept */ } else if (isdigit(ch)) { /* special case: '0' is also the default ball class */ if (ch == '0' && !menu_info->counting && index(menu_info->curr_menu.gacc, ch)) goto group_accel; menu_info->menu_count *= 10L; menu_info->menu_count += (long)(ch - '0'); if (menu_info->menu_count != 0L) /* ignore leading zeros */ menu_info->counting = TRUE; return; } else if (ch == MENU_SEARCH) { /* search */ if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) { char buf[BUFSZ]; X11_getlin("Search for:", buf); if (!*buf || *buf == '\033') return; if (menu_info->how == PICK_ANY) { invert_match(wp, buf); return; } else { select_match(wp, buf); } } else { X11_nhbell(); return; } } else if (ch == MENU_SELECT_ALL) { /* select all */ if (menu_info->how == PICK_ANY) select_all(wp); else X11_nhbell(); return; } else if (ch == MENU_UNSELECT_ALL) { /* unselect all */ if (menu_info->how == PICK_ANY) select_none(wp); else X11_nhbell(); return; } else if (ch == MENU_INVERT_ALL) { /* invert all */ if (menu_info->how == PICK_ANY) invert_all(wp); else X11_nhbell(); return; } else if (index(menu_info->curr_menu.gacc, ch)) { group_accel: /* matched a group accelerator */ if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) { for (count = 0, curr = menu_info->curr_menu.base; curr; curr = curr->next, count++) { if (curr->identifier.a_void != 0 && curr->gselector == ch) { invert_line(wp, curr, count, -1L); /* for PICK_ONE, a group accelerator will only be included in gacc[] if it matches exactly one entry, so this must be it... */ if (menu_info->how == PICK_ONE) goto menu_done; /* pop down */ } } #ifndef USE_FWF XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True); #endif } else X11_nhbell(); return; } else { boolean selected_something = FALSE; for (count = 0, curr = menu_info->curr_menu.base; curr; curr = curr->next, count++) if (curr->identifier.a_void != 0 && curr->selector == ch) break; if (curr) { invert_line(wp, curr, count, menu_info->counting ? menu_info->menu_count : -1L); #ifndef USE_FWF XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True); #endif selected_something = curr->selected; } else { X11_nhbell(); /* no match */ } if (!(selected_something && menu_info->how == PICK_ONE)) return; /* keep going */ } /* pop down */ } else { /* permanent inventory window */ if (ch != '\033') { X11_nhbell(); return; } /* pop down on ESC */ } menu_done: menu_popdown(wp); } /* ARGSUSED */ static void menu_ok(w, client_data, call_data) Widget w; XtPointer client_data, call_data; { struct xwindow *wp = (struct xwindow *) client_data; menu_popdown(wp); } /* ARGSUSED */ static void menu_cancel(w, client_data, call_data) Widget w; /* don't use - may be None */ XtPointer client_data, call_data; { struct xwindow *wp = (struct xwindow *) client_data; if (wp->menu_information->is_active) { select_none(wp); wp->menu_information->cancelled = TRUE; } menu_popdown(wp); } /* ARGSUSED */ static void menu_all(w, client_data, call_data) Widget w; XtPointer client_data, call_data; { select_all((struct xwindow *) client_data); } /* ARGSUSED */ static void menu_none(w, client_data, call_data) Widget w; XtPointer client_data, call_data; { select_none((struct xwindow *) client_data); } /* ARGSUSED */ static void menu_invert(w, client_data, call_data) Widget w; XtPointer client_data, call_data; { invert_all((struct xwindow *) client_data); } /* ARGSUSED */ static void menu_search(w, client_data, call_data) Widget w; XtPointer client_data, call_data; { struct xwindow *wp = (struct xwindow *) client_data; struct menu_info_t *menu_info = wp->menu_information; char buf[BUFSZ]; X11_getlin("Search for:", buf); if (!*buf || *buf == '\033') return; if (menu_info->how == PICK_ANY) invert_match(wp, buf); else select_match(wp, buf); if (menu_info->how == PICK_ONE) menu_popdown(wp); } static void select_all(wp) struct xwindow *wp; { x11_menu_item *curr; int count; boolean changed = FALSE; reset_menu_count(wp->menu_information); for (count = 0, curr = wp->menu_information->curr_menu.base; curr; curr = curr->next, count++) if (curr->identifier.a_void != 0) if (!curr->selected) { invert_line(wp, curr, count, -1L); changed = TRUE; } #ifndef USE_FWF if (changed) XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0, 0, True); #endif } static void select_none(wp) struct xwindow *wp; { x11_menu_item *curr; int count; boolean changed = FALSE; reset_menu_count(wp->menu_information); for (count = 0, curr = wp->menu_information->curr_menu.base; curr; curr = curr->next, count++) if (curr->identifier.a_void != 0) if (curr->selected) { invert_line(wp, curr, count, -1L); changed = TRUE; } #ifndef USE_FWF if (changed) XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0, 0, True); #endif } static void invert_all(wp) struct xwindow *wp; { x11_menu_item *curr; int count; reset_menu_count(wp->menu_information); for (count = 0, curr = wp->menu_information->curr_menu.base; curr; curr = curr->next, count++) if (curr->identifier.a_void != 0) invert_line(wp, curr, count, -1L); #ifndef USE_FWF XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0, 0, True); #endif } static void invert_match(wp, match) struct xwindow *wp; char *match; { x11_menu_item *curr; int count; boolean changed = FALSE; reset_menu_count(wp->menu_information); for (count = 0, curr = wp->menu_information->curr_menu.base; curr; curr = curr->next, count++) if (curr->identifier.a_void != 0 && strstri(curr->str, match)) { invert_line(wp, curr, count, -1L); changed = TRUE; } #ifndef USE_FWF if (changed) XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0, 0, True); #endif } static void select_match(wp, match) struct xwindow *wp; char *match; { x11_menu_item *curr; int count; reset_menu_count(wp->menu_information); for (count = 0, curr = wp->menu_information->curr_menu.base; curr; curr = curr->next, count++) if (curr->identifier.a_void != 0 && strstri(curr->str, match)) { if (!curr->selected) { invert_line(wp, curr, count, -1L); #ifndef USE_FWF XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0, 0, True); #endif } return; } /* no match */ X11_nhbell(); } static void menu_popdown(wp) struct xwindow *wp; { nh_XtPopdown(wp->popup); /* remove the event grab */ if (wp->menu_information->is_active) exit_x_event = TRUE; /* exit our event handler */ wp->menu_information->is_up = FALSE; /* menu is down */ } #ifdef USE_FWF /* * Make sure our idea of selected matches the FWF Multilist's idea of what * is currently selected. The MultiList's selected list can change without * notifying us if one or more items are selected and then another is * selected (not toggled). Then the items that were selected are deselected * but we are not notified. */ static void sync_selected(menu_info, num_selected, items) struct menu_info_t *menu_info; int num_selected; int *items; { int i, j, *ip; x11_menu_item *curr; Boolean found; for (i=0, curr = menu_info->curr_menu.base; curr; i++, curr = curr->next) { found = False; for (j = 0, ip = items; j < num_selected; j++, ip++) if (*ip == i) { found = True; break; } #if 0 if (curr->selected && !found) printf("sync: deselecting %s\n", curr->str); else if (!curr->selected && found) printf("sync: selecting %s\n", curr->str); #endif curr->selected = found ? TRUE : FALSE; } } #endif /* USE_FWF */ /* Global functions ======================================================== */ void X11_start_menu(window) winid window; { struct xwindow *wp; check_winid(window); wp = &window_list[window]; if (wp->menu_information->is_menu) { /* make sure we'ere starting with a clean slate */ free_menu(&wp->menu_information->new_menu); } else { wp->menu_information->is_menu = TRUE; } } /*ARGSUSED*/ void X11_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected) winid window; int glyph; /* unused (for now) */ const anything *identifier; char ch; char gch; /* group accelerator (0 = no group) */ int attr; const char *str; boolean preselected; { x11_menu_item *item; struct menu_info_t *menu_info; check_winid(window); menu_info = window_list[window].menu_information; if (!menu_info->is_menu) { impossible("add_menu: called before start_menu"); return; } item = (x11_menu_item *) alloc((unsigned)sizeof(x11_menu_item)); item->next = (x11_menu_item *) 0; item->identifier = *identifier; item->attr = attr; /* item->selected = preselected; */ item->selected = FALSE; item->pick_count = -1L; if (identifier->a_void) { char buf[4+BUFSZ]; int len = strlen(str); if (!ch) { /* Supply a keyboard accelerator. Only the first 52 get one. */ if (menu_info->new_menu.curr_selector) { ch = menu_info->new_menu.curr_selector++; if (ch == 'z') menu_info->new_menu.curr_selector = 'A'; else if (ch == 'Z') menu_info->new_menu.curr_selector = 0; /* out */ } } if (len >= BUFSZ) { /* We *think* everything's coming in off at most BUFSZ bufs... */ impossible("Menu item too long (%d).", len); len = BUFSZ - 1; } Sprintf(buf, "%c - ", ch ? ch : ' '); (void) strncpy(buf+4, str, len); buf[4+len] = '\0'; item->str = copy_of(buf); } else { /* no keyboard accelerator */ item->str = copy_of(str); ch = 0; } item->selector = ch; item->gselector = gch; if (menu_info->new_menu.last) { menu_info->new_menu.last->next = item; } else { menu_info->new_menu.base = item; } menu_info->new_menu.last = item; menu_info->new_menu.count++; } void X11_end_menu(window, query) winid window; const char *query; { struct menu_info_t *menu_info; check_winid(window); menu_info = window_list[window].menu_information; if (!menu_info->is_menu) { impossible("end_menu: called before start_menu"); return; } menu_info->new_menu.query = copy_of(query); } int X11_select_menu(window, how, menu_list) winid window; int how; menu_item **menu_list; { x11_menu_item *curr; struct xwindow *wp; struct menu_info_t *menu_info; Arg args[10]; Cardinal num_args; String *ptr; int retval; Dimension v_pixel_width, v_pixel_height; boolean labeled; Widget viewport_widget, form, label, ok, cancel, all, none, invert, search; Boolean sens; #ifdef USE_FWF Boolean *boolp; #endif char gacc[QBUFSZ], *ap; *menu_list = (menu_item *) 0; check_winid(window); wp = &window_list[window]; menu_info = wp->menu_information; if (!menu_info->is_menu) { impossible("select_menu: called before start_menu"); return 0; } menu_info->how = (short) how; /* collect group accelerators; for PICK_NONE, they're ignored; for PICK_ONE, only those which match exactly one entry will be accepted; for PICK_ANY, those which match any entry are okay */ gacc[0] = '\0'; if (menu_info->how != PICK_NONE) { int i, n, gcnt[128]; #define GSELIDX(c) ((c) & 127) /* guard against `signed char' */ for (i = 0; i < SIZE(gcnt); i++) gcnt[i] = 0; for (n = 0, curr = menu_info->new_menu.base; curr; curr = curr->next) if (curr->gselector) ++n, ++gcnt[GSELIDX(curr->gselector)]; if (n > 0) /* at least one group accelerator found */ for (ap = gacc, curr = menu_info->new_menu.base; curr; curr = curr->next) if (curr->gselector && !index(gacc, curr->gselector) && (menu_info->how == PICK_ANY || gcnt[GSELIDX(curr->gselector)] == 1)) { *ap++ = curr->gselector; *ap = '\0'; /* re-terminate for index() */ } } menu_info->new_menu.gacc = copy_of(gacc); reset_menu_count(menu_info); /* * Create a string and sensitive list for the new menu. */ menu_info->new_menu.list_pointer = ptr = (String *) alloc((unsigned) (sizeof(String) * (menu_info->new_menu.count+1))); for (curr = menu_info->new_menu.base; curr; ptr++, curr = curr->next) *ptr = (String) curr->str; *ptr = 0; /* terminate list with null */ #ifdef USE_FWF menu_info->new_menu.sensitive = boolp = (Boolean *) alloc((unsigned) (sizeof(Boolean) * (menu_info->new_menu.count))); for (curr = menu_info->new_menu.base; curr; boolp++, curr = curr->next) *boolp = (curr->identifier.a_void != 0); #else menu_info->new_menu.sensitive = (Boolean *) 0; #endif labeled = (menu_info->new_menu.query && *(menu_info->new_menu.query)) ? TRUE : FALSE; /* * Menus don't appear to size components correctly, except * when first created. For 3.2.0 release, just recreate * each time. */ if (menu_info->valid_widgets && (window != WIN_INVEN || !flags.perm_invent)) { XtDestroyWidget(wp->popup); menu_info->valid_widgets = FALSE; menu_info->is_up = FALSE; } if (!menu_info->valid_widgets) { Dimension row_spacing; num_args = 0; XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; wp->popup = XtCreatePopupShell( window == WIN_INVEN ? "inventory" : "menu", how == PICK_NONE ? topLevelShellWidgetClass: transientShellWidgetClass, toplevel, args, num_args); XtOverrideTranslations(wp->popup, XtParseTranslationTable("WM_PROTOCOLS: menu_delete()")); num_args = 0; XtSetArg(args[num_args], XtNtranslations, XtParseTranslationTable(menu_translations)); num_args++; form = XtCreateManagedWidget("mform", formWidgetClass, wp->popup, args, num_args); num_args = 0; XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; if (labeled) label = XtCreateManagedWidget(menu_info->new_menu.query, labelWidgetClass, form, args, num_args); else label = NULL; /* * Create ok, cancel, all, none, invert, and search buttons.. */ num_args = 0; XtSetArg(args[num_args], XtNfromVert, label); num_args++; XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; ok = XtCreateManagedWidget("OK", commandWidgetClass, form, args, num_args); XtAddCallback(ok, XtNcallback, menu_ok, (XtPointer) wp); num_args = 0; XtSetArg(args[num_args], XtNfromVert, label); num_args++; XtSetArg(args[num_args], XtNfromHoriz, ok); num_args++; XtSetArg(args[num_args], XtNsensitive, how!=PICK_NONE); num_args++; XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; cancel = XtCreateManagedWidget("cancel", commandWidgetClass, form, args, num_args); XtAddCallback(cancel, XtNcallback, menu_cancel, (XtPointer) wp); sens = (how == PICK_ANY); num_args = 0; XtSetArg(args[num_args], XtNfromVert, label); num_args++; XtSetArg(args[num_args], XtNfromHoriz, cancel); num_args++; XtSetArg(args[num_args], XtNsensitive, sens); num_args++; XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; all = XtCreateManagedWidget("all", commandWidgetClass, form, args, num_args); XtAddCallback(all, XtNcallback, menu_all, (XtPointer) wp); num_args = 0; XtSetArg(args[num_args], XtNfromVert, label); num_args++; XtSetArg(args[num_args], XtNfromHoriz, all); num_args++; XtSetArg(args[num_args], XtNsensitive, sens); num_args++; XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; none = XtCreateManagedWidget("none", commandWidgetClass, form, args, num_args); XtAddCallback(none, XtNcallback, menu_none, (XtPointer) wp); num_args = 0; XtSetArg(args[num_args], XtNfromVert, label); num_args++; XtSetArg(args[num_args], XtNfromHoriz, none); num_args++; XtSetArg(args[num_args], XtNsensitive, sens); num_args++; XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; invert = XtCreateManagedWidget("invert", commandWidgetClass, form, args, num_args); XtAddCallback(invert, XtNcallback, menu_invert, (XtPointer) wp); num_args = 0; XtSetArg(args[num_args], XtNfromVert, label); num_args++; XtSetArg(args[num_args], XtNfromHoriz, invert); num_args++; XtSetArg(args[num_args], XtNsensitive, how!=PICK_NONE); num_args++; XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; search = XtCreateManagedWidget("search", commandWidgetClass, form, args, num_args); XtAddCallback(search, XtNcallback, menu_search, (XtPointer) wp); num_args = 0; XtSetArg(args[num_args], XtNallowVert, True); num_args++; XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; XtSetArg(args[num_args], XtNuseBottom, True); num_args++; XtSetArg(args[num_args], XtNuseRight, True); num_args++; /* XtSetArg(args[num_args], XtNforceBars, True); num_args++; */ XtSetArg(args[num_args], XtNfromVert, all); num_args++; XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; XtSetArg(args[num_args], XtNbottom, XtChainBottom); num_args++; XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; XtSetArg(args[num_args], XtNright, XtChainRight); num_args++; viewport_widget = XtCreateManagedWidget( "menu_viewport", /* name */ viewportWidgetClass, form, /* parent widget */ args, num_args); /* values, and number of values */ /* make new menu the current menu */ move_menu(&menu_info->new_menu, &menu_info->curr_menu); num_args = 0; XtSetArg(args[num_args], XtNforceColumns, True); num_args++; XtSetArg(args[num_args], XtNcolumnSpacing, 1); num_args++; XtSetArg(args[num_args], XtNdefaultColumns, 1); num_args++; XtSetArg(args[num_args], XtNlist, menu_info->curr_menu.list_pointer); num_args++; #ifdef USE_FWF XtSetArg(args[num_args], XtNsensitiveArray, menu_info->curr_menu.sensitive); num_args++; XtSetArg(args[num_args], XtNmaxSelectable, menu_info->curr_menu.count); num_args++; #endif wp->w = XtCreateManagedWidget( "menu_list", /* name */ #ifdef USE_FWF xfwfMultiListWidgetClass, #else listWidgetClass, #endif viewport_widget, /* parent widget */ args, /* set some values */ num_args); /* number of values to set */ XtAddCallback(wp->w, XtNcallback, menu_select, (XtPointer) 0); /* Get the font and margin information. */ num_args = 0; XtSetArg(args[num_args], XtNfont, &menu_info->fs); num_args++; XtSetArg(args[num_args], XtNinternalHeight, &menu_info->internal_height); num_args++; XtSetArg(args[num_args], XtNinternalWidth, &menu_info->internal_width); num_args++; XtSetArg(args[num_args], XtNrowSpacing, &row_spacing); num_args++; XtGetValues(wp->w, args, num_args); /* font height is ascent + descent */ menu_info->line_height = menu_info->fs->max_bounds.ascent + menu_info->fs->max_bounds.descent + row_spacing; menu_info->valid_widgets = TRUE; num_args = 0; XtSetArg(args[num_args], XtNwidth, &v_pixel_width); num_args++; XtSetArg(args[num_args], XtNheight, &v_pixel_height); num_args++; XtGetValues(wp->w, args, num_args); } else { Dimension len; viewport_widget = XtParent(wp->w); /* get the longest string on new menu */ v_pixel_width = 0; for (ptr = menu_info->new_menu.list_pointer; *ptr; ptr++) { len = XTextWidth(menu_info->fs, *ptr, strlen(*ptr)); if (len > v_pixel_width) v_pixel_width = len; } /* add viewport internal border */ v_pixel_width += 2 * menu_info->internal_width; v_pixel_height = (2 * menu_info->internal_height) + (menu_info->new_menu.count * menu_info->line_height); /* make new menu the current menu */ move_menu(&menu_info->new_menu, &menu_info->curr_menu); #ifdef USE_FWF XfwfMultiListSetNewData((XfwfMultiListWidget)wp->w, menu_info->curr_menu.list_pointer, 0, 0, TRUE, menu_info->curr_menu.sensitive); #else XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, TRUE); #endif } /* if viewport will be bigger than the screen, limit its height */ num_args = 0; XtSetArg(args[num_args], XtNwidth, &v_pixel_width); num_args++; XtSetArg(args[num_args], XtNheight, &v_pixel_height); num_args++; XtGetValues(wp->w, args, num_args); if ((Dimension) XtScreen(wp->w)->height * 5 / 6 < v_pixel_height) { /* scrollbar is 14 pixels wide. Widen the form to accommodate it. */ v_pixel_width += 14; /* shrink to fit vertically */ v_pixel_height = XtScreen(wp->w)->height * 5 / 6; num_args = 0; XtSetArg(args[num_args], XtNwidth, v_pixel_width); num_args++; XtSetArg(args[num_args], XtNheight, v_pixel_height); num_args++; XtSetValues(wp->w, args, num_args); } XtRealizeWidget(wp->popup); /* need to realize before we position */ /* if menu is not up, position it */ if (!menu_info->is_up) positionpopup(wp->popup, FALSE); menu_info->is_up = TRUE; if (window == WIN_INVEN && how == PICK_NONE) { /* cant use nh_XtPopup() because it may try to grab the focus */ XtPopup(wp->popup, (int)XtGrabNone); if (!updated_inventory) XMapRaised(XtDisplay(wp->popup), XtWindow(wp->popup)); XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup), &wm_delete_window, 1); retval = 0; } else { menu_info->is_active = TRUE; /* waiting for user response */ menu_info->cancelled = FALSE; nh_XtPopup(wp->popup, (int)XtGrabExclusive, wp->w); (void) x_event(EXIT_ON_EXIT); menu_info->is_active = FALSE; if (menu_info->cancelled) return -1; retval = 0; for (curr = menu_info->curr_menu.base; curr; curr = curr->next) if (curr->selected) retval++; if (retval) { menu_item *mi; *menu_list = mi = (menu_item *) alloc(retval * sizeof(menu_item)); for (curr = menu_info->curr_menu.base; curr; curr = curr->next) if (curr->selected) { mi->item = curr->identifier; mi->count = curr->pick_count; mi++; } } } return retval; } /* End global functions ==================================================== */ /* * Allocate a copy of the given string. If null, return a string of * zero length. * * This is an exact duplicate of copy_of() in tty/wintty.c. */ static char * copy_of(s) const char *s; { if (!s) s = ""; return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s); } static void move_menu(src_menu, dest_menu) struct menu *src_menu, *dest_menu; { free_menu(dest_menu); /* toss old menu */ *dest_menu = *src_menu; /* make new menu current */ /* leave no dangling ptrs */ reset_menu_to_default(src_menu); } static void free_menu(mp) struct menu *mp; { while (mp->base) { mp->last = mp->base; mp->base = mp->base->next; free((genericptr_t)mp->last->str); free((genericptr_t)mp->last); } if (mp->query) free((genericptr_t) mp->query); if (mp->gacc) free((genericptr_t) mp->gacc); if (mp->list_pointer) free((genericptr_t) mp->list_pointer); if (mp->sensitive) free((genericptr_t) mp->sensitive); reset_menu_to_default(mp); } static void reset_menu_to_default(mp) struct menu *mp; { mp->base = mp->last = (x11_menu_item *)0; mp->query = (const char *)0; mp->gacc = (const char *)0; mp->count = 0; mp->list_pointer = (String *)0; mp->sensitive = (Boolean *)0; mp->curr_selector = 'a'; /* first accelerator */ } static void clear_old_menu(wp) struct xwindow *wp; { struct menu_info_t *menu_info = wp->menu_information; free_menu(&menu_info->curr_menu); free_menu(&menu_info->new_menu); if (menu_info->valid_widgets) { nh_XtPopdown(wp->popup); menu_info->is_up = FALSE; XtDestroyWidget(wp->popup); menu_info->valid_widgets = FALSE; wp->w = wp->popup = (Widget) 0; } } void create_menu_window(wp) struct xwindow *wp; { wp->type = NHW_MENU; wp->menu_information = (struct menu_info_t *) alloc(sizeof(struct menu_info_t)); (void) memset((genericptr_t) wp->menu_information, '\0', sizeof(struct menu_info_t)); reset_menu_to_default(&wp->menu_information->curr_menu); reset_menu_to_default(&wp->menu_information->new_menu); reset_menu_count(wp->menu_information); wp->w = wp->popup = (Widget) 0; } void destroy_menu_window(wp) struct xwindow *wp; { clear_old_menu(wp); /* this will also destroy the widgets */ free((genericptr_t) wp->menu_information); wp->menu_information = (struct menu_info_t *) 0; wp->type = NHW_NONE; /* allow re-use */ } /*winmenu.c*/