From 5d99d6459a061c09c6b320317fd771be05522517 Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:06:03 +0000 Subject: [PATCH] *** empty log message *** --- win/X11/winmenu.c | 1143 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1143 insertions(+) create mode 100644 win/X11/winmenu.c diff --git a/win/X11/winmenu.c b/win/X11/winmenu.c new file mode 100644 index 000000000..2912fb12c --- /dev/null +++ b/win/X11/winmenu.c @@ -0,0 +1,1143 @@ +/* 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*/