Logo Search packages:      
Sourcecode: falconseye version File versions  Download package

winmenu.c

/*    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*/

Generated by  Doxygen 1.6.0   Back to index