]> granicus.if.org Git - nethack/commitdiff
*** empty log message ***
authorjwalz <jwalz>
Sat, 5 Jan 2002 21:06:03 +0000 (21:06 +0000)
committerjwalz <jwalz>
Sat, 5 Jan 2002 21:06:03 +0000 (21:06 +0000)
win/X11/winmenu.c [new file with mode: 0644]

diff --git a/win/X11/winmenu.c b/win/X11/winmenu.c
new file mode 100644 (file)
index 0000000..2912fb1
--- /dev/null
@@ -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 <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xatom.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xaw/Cardinals.h>
+#include <X11/Xaw/Box.h>
+#ifdef USE_FWF
+#include <X11/Xfwf/MultiList.h>
+#else
+#include <X11/Xaw/List.h>
+#endif
+#include <X11/Xos.h>
+
+#ifdef PRESERVE_NO_SYSV
+# ifdef SYSV
+#  undef SYSV
+# endif
+# undef PRESERVE_NO_SYSV
+#endif
+
+#include "hack.h"
+#include "winX.h"
+#include <ctype.h>
+
+
+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\
+     <Key>Left: scroll(4)\n\
+     <Key>Right: scroll(6)\n\
+     <Key>Up: scroll(8)\n\
+     <Key>Down: scroll(2)\n\
+     <Key>: 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("<Message>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*/