Mercurial > emacs
changeset 6058:662b9cd767fe
(Fx_popup_menu): Allow t as position, meaning use mouse pos.
Allow nil as position, meaning just precompute equiv-key data.
Mouse events have coords in pixel units.
(menu_item_equiv_key): Cached equiv-key data is a sublist.
Most of file rewritten.
(menu_items, menu_items_*): New variables.
(MENU_ITEMS_*): New macros.
(init_menu_items, discard_menu_items, push_menu_pane, push_menu_item)
(finish_menu_items): New functions.
(menu_item_enabled_p): New function.
(keymap_panes, single_keymap_panes): Major rewrite; most args changed.
(list_of_panes, list_of_items): Major rewrite; most args changed.
(Fx_popup_menu): Major rewrite. Now independent of display mechanism.
No more conditionals here.
(set_menu_items, free_menu_items): Functions deleted.
(xmenu_show): Both versions rewritten to work from menu_items
and to do all the conditionalized things that were in Fx_popup_menu.
(unread_menu_bar_button, other_menu_bar_item_p): New functions.
(check_mouse_other_menu_bar): New function.
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Thu, 24 Feb 1994 08:07:16 +0000 |
parents | b2cc63a56415 |
children | 0021cbdc591b |
files | src/xmenu.c |
diffstat | 1 files changed, 1062 insertions(+), 1095 deletions(-) [+] |
line wrap: on
line diff
--- a/src/xmenu.c Thu Feb 24 08:02:10 1994 +0000 +++ b/src/xmenu.c Thu Feb 24 08:07:16 1994 +0000 @@ -27,6 +27,8 @@ /* Modified by Fred Pierresteguy on December 93 to make the popup menus and menubar use the Xt. */ +/* Rewritten for clarity and GC protection by rms in Feb 94. */ + #include <stdio.h> /* On 4.3 this loses if it comes after xterm.h. */ @@ -70,12 +72,10 @@ #define min(x,y) (((x) < (y)) ? (x) : (y)) #define max(x,y) (((x) > (y)) ? (x) : (y)) -#define NUL 0 - #ifndef TRUE #define TRUE 1 #define FALSE 0 -#endif /* TRUE */ +#endif /* no TRUE */ #ifdef HAVE_X11 extern Display *x_current_display; @@ -83,31 +83,460 @@ #define ButtonReleaseMask ButtonReleased #endif /* not HAVE_X11 */ +/* We need a unique id for each popup menu and dialog box. */ +static unsigned int popup_id_tick; + extern Lisp_Object Qmenu_enable; extern Lisp_Object Qmenu_bar; -Lisp_Object xmenu_show (); -extern int x_error_handler (); + #ifdef USE_X_TOOLKIT -static widget_value *set_menu_items (); +extern void process_expose_from_menu (); +extern XtAppContext Xt_app_con; + static int string_width (); -static void free_menu_items (); #endif -/* we need a unique id for each popup menu and dialog box */ -unsigned int popup_id_tick; +static Lisp_Object xmenu_show (); +static void keymap_panes (); +static void single_keymap_panes (); +static void list_of_panes (); +static void list_of_items (); + +/* This holds a Lisp vector that holds the results of decoding + the keymaps or alist-of-alists that specify a menu. + + It describes the panes and items within the panes. + + Each pane is described by 3 elements in the vector: + t, the pane name, the pane's prefix key. + Then follow the pane's items, with 4 elements per item: + the item string, the enable flag, the item's value, + and the equivalent keyboard key's description string. + + Using a Lisp vector to hold this information while we decode it + takes care of protecting all the data from GC. */ + +#define MENU_ITEMS_PANE_NAME 1 +#define MENU_ITEMS_PANE_PREFIX 2 +#define MENU_ITEMS_PANE_LENGTH 3 + +#define MENU_ITEMS_ITEM_NAME 0 +#define MENU_ITEMS_ITEM_ENABLE 1 +#define MENU_ITEMS_ITEM_VALUE 2 +#define MENU_ITEMS_ITEM_EQUIV_KEY 3 +#define MENU_ITEMS_ITEM_LENGTH 4 + +static Lisp_Object menu_items; + +/* Number of slots currently allocated in menu_items. */ +static int menu_items_allocated; + +/* This is the index in menu_items of the first empty slot. */ +static int menu_items_used; + +/* The number of panes currently recorded in menu_items. */ +static int menu_items_n_panes; + +/* Initialize the menu_items structure if we haven't already done so. + Also mark it as currently empty. */ + +static void +init_menu_items () +{ + if (NILP (menu_items)) + { + menu_items_allocated = 60; + menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil); + } + + menu_items_used = 0; + menu_items_n_panes = 0; +} + +/* Call at the end of generating the data in menu_items. + This fills in the number of items in the last pane. */ + +static void +finish_menu_items () +{ +} + +/* Call when finished using the data for the current menu + in menu_items. */ + +static void +discard_menu_items () +{ + /* Free the structure if it is especially large. + Otherwise, hold on to it, to save time. */ + if (menu_items_allocated > 200) + { + menu_items = Qnil; + menu_items_allocated = 0; + } +} + +/* Start a new menu pane in menu_items.. + NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */ + +static void +push_menu_pane (name, prefix_vec) + Lisp_Object name, prefix_vec; +{ + if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated) + { + Lisp_Object old; + int old_size = menu_items_allocated; + old = menu_items; + + menu_items_allocated *= 2; + menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil); + bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents, + old_size * sizeof (Lisp_Object)); + } + + menu_items_n_panes++; + XVECTOR (menu_items)->contents[menu_items_used++] = Qt; + XVECTOR (menu_items)->contents[menu_items_used++] = name; + XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec; +} -/*************************************************************/ +/* Push one menu item into the current pane. + NAME is the string to display. ENABLE if non-nil means + this item can be selected. KEY is the key generated by + choosing this item. EQUIV is the textual description + of the keyboard equivalent for this item (or nil if none). */ + +static void +push_menu_item (name, enable, key, equiv) + Lisp_Object name, enable, key, equiv; +{ + if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated) + { + Lisp_Object old; + int old_size = menu_items_allocated; + old = menu_items; + + menu_items_allocated *= 2; + menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil); + bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents, + old_size * sizeof (Lisp_Object)); + } + + XVECTOR (menu_items)->contents[menu_items_used++] = name; + XVECTOR (menu_items)->contents[menu_items_used++] = enable; + XVECTOR (menu_items)->contents[menu_items_used++] = key; + XVECTOR (menu_items)->contents[menu_items_used++] = equiv; +} + +/* Figure out the current keyboard equivalent of a menu item ITEM1. + The item string for menu display should be ITEM_STRING. + Store the equivalent keyboard key sequence's + textual description into *DESCRIP_PTR. + Also cache them in the item itself. + Return the real definition to execute. */ + +static Lisp_Object +menu_item_equiv_key (item_string, item1, descrip_ptr) + Lisp_Object item_string; + Lisp_Object item1; + Lisp_Object *descrip_ptr; +{ + /* This is the real definition--the function to run. */ + Lisp_Object def; + /* This is the sublist that records cached equiv key data + so we can save time. */ + Lisp_Object cachelist; + /* These are the saved equivalent keyboard key sequence + and its key-description. */ + Lisp_Object savedkey, descrip; + Lisp_Object def1; + int changed = 0; + + /* If a help string follows the item string, skip it. */ + if (CONSP (XCONS (item1)->cdr) + && STRINGP (XCONS (XCONS (item1)->cdr)->car)) + item1 = XCONS (item1)->cdr; + + def = Fcdr (item1); + + /* Get out the saved equivalent-keyboard-key info. */ + cachelist = savedkey = descrip = Qnil; + if (CONSP (def) && CONSP (XCONS (def)->car) + && (NILP (XCONS (XCONS (def)->car)->car) + || VECTORP (XCONS (XCONS (def)->car)->car))) + { + cachelist = XCONS (def)->car; + def = XCONS (def)->cdr; + savedkey = XCONS (cachelist)->car; + descrip = XCONS (cachelist)->cdr; + } + + /* Is it still valid? */ + def1 = Qnil; + if (!NILP (savedkey)) + def1 = Fkey_binding (savedkey, Qnil); + /* If not, update it. */ + if (! EQ (def1, def) + /* If something had no key binding before, don't recheck it-- + doing that takes too much time and makes menus too slow. */ + && !(!NILP (cachelist) && NILP (savedkey))) + { + changed = 1; + descrip = Qnil; + savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil); + if (VECTORP (savedkey) + && EQ (XVECTOR (savedkey)->contents[0], Qmenu_bar)) + savedkey = Qnil; + if (!NILP (savedkey)) + { + descrip = Fkey_description (savedkey); + descrip = concat2 (make_string (" (", 3), descrip); + descrip = concat2 (descrip, make_string (")", 1)); + } + } + + /* Cache the data we just got in a sublist of the menu binding. */ + if (NILP (cachelist)) + XCONS (item1)->cdr = Fcons (Fcons (savedkey, descrip), def); + else if (changed) + { + XCONS (cachelist)->car = savedkey; + XCONS (cachelist)->cdr = descrip; + } + + *descrip_ptr = descrip; + return def; +} + +/* This is used as the handler when calling internal_condition_case_1. */ -#if 0 -/* Ignoring the args is easiest. */ -xmenu_quit () +static Lisp_Object +menu_item_enabled_p_1 (arg) + Lisp_Object arg; +{ + return Qnil; +} + +/* Return non-nil if the command DEF is enabled when used as a menu item. + This is based on looking for a menu-enable property. */ + +static Lisp_Object +menu_item_enabled_p (def) + Lisp_Object def; +{ + Lisp_Object enabled, tem; + + enabled = Qt; + if (XTYPE (def) == Lisp_Symbol) + { + /* No property, or nil, means enable. + Otherwise, enable if value is not nil. */ + tem = Fget (def, Qmenu_enable); + if (!NILP (tem)) + /* (condition-case nil (eval tem) + (error nil)) */ + enabled = internal_condition_case_1 (Feval, tem, Qerror, + menu_item_enabled_p_1); + } + return enabled; +} + +/* Look through KEYMAPS, a vector of keymaps that is NMAPS long, + and generate menu panes for them in menu_items. */ + +static void +keymap_panes (keymaps, nmaps) + Lisp_Object *keymaps; + int nmaps; +{ + int mapno; + + init_menu_items (); + + /* Loop over the given keymaps, making a pane for each map. + But don't make a pane that is empty--ignore that map instead. + P is the number of panes we have made so far. */ + for (mapno = 0; mapno < nmaps; mapno++) + single_keymap_panes (keymaps[mapno], Qnil, Qnil); + + finish_menu_items (); +} + +/* This is a recursive subroutine of keymap_panes. + It handles one keymap, KEYMAP. + The other arguments are passed along + or point to local variables of the previous function. */ + +static void +single_keymap_panes (keymap, pane_name, prefix) + Lisp_Object keymap; + Lisp_Object pane_name; + Lisp_Object prefix; { - error ("Unknown XMenu error"); + Lisp_Object pending_maps; + Lisp_Object tail, item, item1, item_string, table; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + + pending_maps = Qnil; + + push_menu_pane (pane_name, prefix); + + for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr) + { + /* Look at each key binding, and if it has a menu string, + make a menu item from it. */ + item = XCONS (tail)->car; + if (XTYPE (item) == Lisp_Cons) + { + item1 = XCONS (item)->cdr; + if (XTYPE (item1) == Lisp_Cons) + { + item_string = XCONS (item1)->car; + if (XTYPE (item_string) == Lisp_String) + { + /* This is the real definition--the function to run. */ + Lisp_Object def; + /* These are the saved equivalent keyboard key sequence + and its key-description. */ + Lisp_Object descrip; + Lisp_Object tem, enabled; + + def = menu_item_equiv_key (item_string, item1, &descrip); + + /* GCPRO because we will call eval. + Protecting KEYMAP preserves everything we use; + aside from that, must protect whatever might be + a string. Since there's no GCPRO5, we refetch + item_string instead of protecting it. */ + GCPRO4 (keymap, pending_maps, def, descrip); + enabled = menu_item_enabled_p (def); + UNGCPRO; + + item_string = XCONS (item1)->car; + + tem = Fkeymapp (def); + if (XSTRING (item_string)->data[0] == '@' && !NILP (tem)) + pending_maps = Fcons (Fcons (def, Fcons (item_string, XCONS (item)->car)), + pending_maps); + else + push_menu_item (item_string, enabled, XCONS (item)->car, + descrip); + } + } + } + else if (XTYPE (item) == Lisp_Vector) + { + /* Loop over the char values represented in the vector. */ + int len = XVECTOR (item)->size; + int c; + for (c = 0; c < len; c++) + { + Lisp_Object character; + XFASTINT (character) = c; + item1 = XVECTOR (item)->contents[c]; + if (XTYPE (item1) == Lisp_Cons) + { + item_string = XCONS (item1)->car; + if (XTYPE (item_string) == Lisp_String) + { + Lisp_Object def; + + /* These are the saved equivalent keyboard key sequence + and its key-description. */ + Lisp_Object descrip; + Lisp_Object tem, enabled; + + def = menu_item_equiv_key (item_string, item1, &descrip); + + /* GCPRO because we will call eval. + Protecting KEYMAP preserves everything we use; + aside from that, must protect whatever might be + a string. Since there's no GCPRO5, we refetch + item_string instead of protecting it. */ + GCPRO4 (keymap, pending_maps, def, descrip); + enabled = menu_item_enabled_p (def); + UNGCPRO; + + item_string = XCONS (item1)->car; + + tem = Fkeymapp (def); + if (XSTRING (item_string)->data[0] == '@' && !NILP (tem)) + pending_maps = Fcons (Fcons (def, Fcons (item_string, character)), + pending_maps); + else + push_menu_item (item_string, enabled, + character, descrip); + } + } + } + } + } + + /* Process now any submenus which want to be panes at this level. */ + while (!NILP (pending_maps)) + { + Lisp_Object elt, eltcdr; + elt = Fcar (pending_maps); + eltcdr = XCONS (elt)->cdr; + single_keymap_panes (Fcar (elt), + /* Fails to discard the @. */ + XCONS (eltcdr)->car, XCONS (eltcdr)->cdr); + pending_maps = Fcdr (pending_maps); + } } -#endif + +/* Push all the panes and items of a menu decsribed by the + alist-of-alists MENU. + This handles old-fashioned calls to x-popup-menu. */ + +static void +list_of_panes (menu) + Lisp_Object menu; +{ + Lisp_Object tail; + + init_menu_items (); + + for (tail = menu; !NILP (tail); tail = Fcdr (tail)) + { + Lisp_Object elt, pane_name, pane_data; + elt = Fcar (tail); + pane_name = Fcar (elt); + CHECK_STRING (pane_name, 0); + push_menu_pane (pane_name, Qnil); + pane_data = Fcdr (elt); + CHECK_CONS (pane_data, 0); + list_of_items (pane_data); + } + finish_menu_items (); +} + +/* Push the items in a single pane defined by the alist PANE. */ + +static void +list_of_items (pane) + Lisp_Object pane; +{ + Lisp_Object tail, item, item1; + + for (tail = pane; !NILP (tail); tail = Fcdr (tail)) + { + item = Fcar (tail); + if (STRINGP (item)) + push_menu_item (item, Qnil, Qnil); + else + { + CHECK_CONS (item, 0); + item1 = Fcar (item); + CHECK_STRING (item1, 1); + push_menu_item (item1, Qt, Fcdr (item), Qnil); + } + } +} -DEFUN ("x-popup-menu",Fx_popup_menu, Sx_popup_menu, 1, 2, 0, +DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 1, 2, 0, "Pop up a deck-of-cards menu and return user's selection.\n\ POSITION is a position specification. This is either a mouse button event\n\ or a list ((XOFFSET YOFFSET) WINDOW)\n\ @@ -115,6 +544,7 @@ corner of WINDOW's frame. (WINDOW may be a frame object instead of a window.)\n\ This controls the position of the center of the first line\n\ in the first pane of the menu, not the top left of the menu as a whole.\n\ +If POSITION is t, it means to use the current mouse position.\n\ \n\ MENU is a specifier for a menu. For the simplest case, MENU is a keymap.\n\ The menu items come from key bindings that have a menu string as well as\n\ @@ -131,166 +561,101 @@ Each ITEM is normally a cons cell (STRING . VALUE);\n\ but a string can appear as an item--that makes a nonselectable line\n\ in the menu.\n\ -With this form of menu, the return value is VALUE from the chosen item.") +With this form of menu, the return value is VALUE from the chosen item.\n\ +\n\ +If POSITION is nil, don't display the menu at all, just precalculate the\n\ +cached information about equivalent key sequences.") (position, menu) Lisp_Object position, menu; { int number_of_panes, panes; - Lisp_Object XMenu_return, keymap, tem; - int XMenu_xpos, XMenu_ypos; - char **menus; - char ***names; - int **enables; - Lisp_Object **obj_list; - Lisp_Object *prefixes; - int *items; - char *title; + Lisp_Object keymap, tem; + int xpos, ypos; + Lisp_Object title; char *error_name; - Lisp_Object ltitle, selection; - int i, j, menubarp = 0; + Lisp_Object selection; + int i, j; FRAME_PTR f; Lisp_Object x, y, window; -#ifdef USE_X_TOOLKIT - widget_value *val, *vw = 0; -#endif /* USE_X_TOOLKIT */ + int keymaps = 0; + int menubarp = 0; + struct gcpro gcpro1; check_x (); - /* Decode the first argument: find the window and the coordinates. */ - tem = Fcar (position); - if (XTYPE (tem) == Lisp_Cons) - { - window = Fcar (Fcdr (position)); - x = Fcar (tem); - y = Fcar (Fcdr (tem)); - } - else - { - tem = Fcar (Fcdr (position)); /* EVENT_START (position) */ - window = Fcar (tem); /* POSN_WINDOW (tem) */ - tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */ - x = Fcar (tem); - y = Fcdr (tem); - } - CHECK_NUMBER (x, 0); - CHECK_NUMBER (y, 0); - if (XTYPE (window) == Lisp_Frame) - { - f = XFRAME (window); - - XMenu_xpos = 0; - XMenu_ypos = 0; - } - else if (XTYPE (window) == Lisp_Window) + if (! NILP (position)) { - CHECK_LIVE_WINDOW (window, 0); - f = XFRAME (WINDOW_FRAME (XWINDOW (window))); - - XMenu_xpos = FONT_WIDTH (f->display.x->font) - * XWINDOW (window)->left; - XMenu_ypos = FONT_HEIGHT (f->display.x->font) - * XWINDOW (window)->top; - } - else - /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME, - but I don't want to make one now. */ - CHECK_WINDOW (window, 0); + /* Decode the first argument: find the window and the coordinates. */ + if (EQ (position, Qt)) + { + /* Use the mouse's current position. */ + FRAME_PTR new_f; + Lisp_Object bar_window; + int part; + unsigned long time; -#ifdef USE_X_TOOLKIT - tem = Fcar (Fcdr (Fcar (Fcdr (position)))); - if (XTYPE (Fcar (position)) != Lisp_Cons - && CONSP (tem) - && EQ (Fcar (tem), Qmenu_bar)) - { - /* We are in the menubar */ - XlwMenuWidget mw; - int w1 = 0, w2; - - mw = (XlwMenuWidget)f->display.x->menubar_widget; - menubarp = 1; - for (vw = mw->menu.old_stack [0]->contents; vw; vw = vw->next) + (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time); + XSET (window, Lisp_Frame, new_f); + } + else { - w2 = w1; - w1 += string_width (mw, vw->name) - + 2 * (mw->menu.horizontal_spacing + - mw->menu.shadow_thickness); - if (XINT (x) < w1) + tem = Fcar (position); + if (XTYPE (tem) == Lisp_Cons) + { + window = Fcar (Fcdr (position)); + x = Fcar (tem); + y = Fcar (Fcdr (tem)); + } + else { - XMenu_xpos = w2 + 4; - XMenu_ypos = 0; - break; + tem = Fcar (Fcdr (position)); /* EVENT_START (position) */ + window = Fcar (tem); /* POSN_WINDOW (tem) */ + tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */ + x = Fcar (tem); + y = Fcdr (tem); + + /* Determine whether this menu is handling a menu bar click. */ + tem = Fcar (Fcdr (Fcar (Fcdr (position)))); + if (XTYPE (Fcar (position)) != Lisp_Cons + && CONSP (tem) + && EQ (Fcar (tem), Qmenu_bar)) + menubarp = 1; } } - } - else - { - XMenu_xpos += FONT_WIDTH (f->display.x->font) * XINT (x); - XMenu_ypos += FONT_HEIGHT (f->display.x->font) * XINT (y); - } + + CHECK_NUMBER (x, 0); + CHECK_NUMBER (y, 0); - BLOCK_INPUT; - XMenu_xpos += (f->display.x->widget->core.x - + f->display.x->widget->core.border_width); - XMenu_ypos += (f->display.x->widget->core.y - + f->display.x->widget->core.border_width - + f->display.x->menubar_widget->core.height); - UNBLOCK_INPUT; + /* Decode where to put the menu. */ + + if (XTYPE (window) == Lisp_Frame) + { + f = XFRAME (window); - val = set_menu_items (menu, &prefixes, &panes, &names, - &enables, &menus, &items, &number_of_panes, &obj_list, - &title, &error_name); - selection = xmenu_show (f, val, XMenu_xpos, XMenu_ypos, - menubarp, vw); + xpos = 0; + ypos = 0; + } + else if (XTYPE (window) == Lisp_Window) + { + CHECK_LIVE_WINDOW (window, 0); + f = XFRAME (WINDOW_FRAME (XWINDOW (window))); - free_menu_items (names, enables, menus, items, number_of_panes, obj_list, - title, error_name); + xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left); + ypos = (FONT_HEIGHT (f->display.x->font) * XWINDOW (window)->top); + } + else + /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME, + but I don't want to make one now. */ + CHECK_WINDOW (window, 0); - if (selection != NUL) - { /* selected something */ - XMenu_return = selection; - } - else - { /* nothing selected */ - XMenu_return = Qnil; + xpos += XINT (x); + ypos += XINT (y); } - return XMenu_return; - -#else /* not USE_X_TOOLKIT */ -#ifdef HAVE_X11 - { - Window child; - int win_x = 0, win_y = 0; - - /* Find the position of the outside upper-left corner of - the inner window, with respect to the outer window. */ - if (f->display.x->parent_desc != ROOT_WINDOW) - { - BLOCK_INPUT; - XTranslateCoordinates (x_current_display, - - /* From-window, to-window. */ - f->display.x->window_desc, - f->display.x->parent_desc, + title = Qnil; + GCPRO1 (title); - /* From-position, to-position. */ - 0, 0, &win_x, &win_y, - - /* Child of window. */ - &child); - UNBLOCK_INPUT; - XMenu_xpos += win_x; - XMenu_ypos += win_y; - } - } -#endif /* HAVE_X11 */ - - XMenu_xpos += FONT_WIDTH (f->display.x->font) * XINT (x); - XMenu_ypos += FONT_HEIGHT (f->display.x->font) * XINT (y); - - XMenu_xpos += f->display.x->left_pos; - XMenu_ypos += f->display.x->top_pos; - + /* Decode the menu items from what was specified. */ keymap = Fkeymapp (menu); tem = Qnil; @@ -302,121 +667,82 @@ Lisp_Object prompt; keymap = get_keymap (menu); + /* Extract the detailed info to make one pane. */ + keymap_panes (&menu, 1); + /* Search for a string appearing directly as an element of the keymap. That string is the title of the menu. */ prompt = map_prompt (keymap); - if (!NILP (prompt)) - title = (char *) XSTRING (prompt)->data; - /* Extract the detailed info to make one pane. */ - number_of_panes = keymap_panes (&obj_list, &menus, &names, &enables, - &items, &prefixes, &menu, 1); - /* The menu title seems to be ignored, - so put it in the pane title. */ - if (menus[0] == 0) - menus[0] = title; + /* Make that be the pane title of the first pane. */ + if (!NILP (prompt) && menu_items_n_panes >= 0) + XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt; + + keymaps = 1; } else if (!NILP (tem)) { /* We were given a list of keymaps. */ - Lisp_Object prompt; int nmaps = XFASTINT (Flength (menu)); Lisp_Object *maps = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object)); int i; - title = 0; + + title = Qnil; /* The first keymap that has a prompt string supplies the menu title. */ for (tem = menu, i = 0; XTYPE (tem) == Lisp_Cons; tem = Fcdr (tem)) { + Lisp_Object prompt; + maps[i++] = keymap = get_keymap (Fcar (tem)); prompt = map_prompt (keymap); - if (title == 0 && !NILP (prompt)) - title = (char *) XSTRING (prompt)->data; + if (NILP (title) && !NILP (prompt)) + title = prompt; } /* Extract the detailed info to make one pane. */ - number_of_panes = keymap_panes (&obj_list, &menus, &names, &enables, - &items, &prefixes, maps, nmaps); - /* The menu title seems to be ignored, - so put it in the pane title. */ - if (menus[0] == 0) - menus[0] = title; + keymap_panes (maps, nmaps); + + /* Make the title be the pane title of the first pane. */ + if (!NILP (title) && menu_items_n_panes >= 0) + XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title; + + keymaps = 1; } else { /* We were given an old-fashioned menu. */ - ltitle = Fcar (menu); - CHECK_STRING (ltitle, 1); - title = (char *) XSTRING (ltitle)->data; - prefixes = 0; - number_of_panes = list_of_panes (&obj_list, &menus, &names, &enables, - &items, Fcdr (menu)); - } -#ifdef XDEBUG - fprintf (stderr, "Panes = %d\n", number_of_panes); - for (i = 0; i < number_of_panes; i++) - { - fprintf (stderr, "Pane %d has lines %d title %s\n", - i, items[i], menus[i]); - for (j = 0; j < items[i]; j++) - fprintf (stderr, " Item %d %s\n", j, names[i][j]); - } -#endif + title = Fcar (menu); + CHECK_STRING (title, 1); - BLOCK_INPUT; - { - Window root; - int root_x, root_y; - int dummy_int; - unsigned int dummy_uint; - Window dummy_window; - - /* Figure out which root window F is on. */ - XGetGeometry (x_current_display, FRAME_X_WINDOW (f), &root, - &dummy_int, &dummy_int, &dummy_uint, &dummy_uint, - &dummy_uint, &dummy_uint); + list_of_panes (Fcdr (menu)); - /* Translate the menu co-ordinates within f to menu co-ordinates - on that root window. */ - if (! XTranslateCoordinates (x_current_display, - FRAME_X_WINDOW (f), root, - XMenu_xpos, XMenu_ypos, &root_x, &root_y, - &dummy_window)) - /* But XGetGeometry said root was the root window of f's screen! */ - abort (); - selection = xmenu_show (root, XMenu_xpos, XMenu_ypos, names, enables, - menus, prefixes, items, number_of_panes, obj_list, - title, &error_name); - } - UNBLOCK_INPUT; - /* fprintf (stderr, "selection = %x\n", selection); */ - if (selection != NUL) - { /* selected something */ - XMenu_return = selection; + keymaps = 0; + } + + if (NILP (position)) + { + discard_menu_items (); + UNGCPRO; + return Qnil; } - else - { /* nothing selected */ - XMenu_return = Qnil; - } - /* now free up the strings */ - for (i = 0; i < number_of_panes; i++) - { - xfree (names[i]); - xfree (enables[i]); - xfree (obj_list[i]); - } - xfree (menus); - xfree (obj_list); - xfree (names); - xfree (enables); - xfree (items); - /* free (title); */ + + /* Display them in a menu. */ + BLOCK_INPUT; + + selection = xmenu_show (f, xpos, ypos, menubarp, + keymaps, title, &error_name); + UNBLOCK_INPUT; + + discard_menu_items (); + + UNGCPRO; + if (error_name) error (error_name); - return XMenu_return; -#endif /* not USE_X_TOOLKIT */ + return selection; } #ifdef USE_X_TOOLKIT @@ -460,9 +786,10 @@ char *name; int *string_w; { - *string_w += string_width (mw, name) - + 2 * (mw->menu.horizontal_spacing + mw->menu.shadow_thickness); - return (XINT (event->x) < *string_w); + *string_w += (string_width (mw, name) + + 2 * (mw->menu.horizontal_spacing + + mw->menu.shadow_thickness)); + return XINT (event->x) < *string_w; } @@ -495,185 +822,7 @@ return Qnil; } -static widget_value * -set_menu_items (menu, prefixes, panes, names, enables, menus, - items, number_of_panes, obj_list, title, error_name) - Lisp_Object menu; - Lisp_Object **prefixes; - int *panes; - char ***names[]; - int ***enables; - char ***menus; - int **items; - int *number_of_panes; - Lisp_Object ***obj_list; - char **title; - char **error_name; -{ - Lisp_Object keymap, tem; - Lisp_Object ltitle, selection; - int i, j; - widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0; - int last, selidx, lpane, status; - int lines, sofar; - - keymap = Fkeymapp (menu); - tem = Qnil; - - if (XTYPE (menu) == Lisp_Cons) - tem = Fkeymapp (Fcar (menu)); - if (!NILP (keymap)) - { - /* We were given a keymap. Extract menu info from the keymap. */ - Lisp_Object prompt; - keymap = get_keymap (menu); - - /* Search for a string appearing directly as an element of the keymap. - That string is the title of the menu. */ - prompt = map_prompt (keymap); - if (!NILP (prompt)) - *title = (char *) XSTRING (prompt)->data; - - /* Extract the detailed info to make one pane. */ - *number_of_panes = keymap_panes (obj_list, menus, names, enables, - items, prefixes, menu, 1); - /* The menu title seems to be ignored, - so put it in the pane title. */ - if ((*menus)[0] == 0) - (*menus)[0] = *title; - } - else if (!NILP (tem)) - { - /* We were given a list of keymaps. */ - Lisp_Object prompt; - int nmaps = XFASTINT (Flength (menu)); - Lisp_Object *maps - = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object)); - int i; - *title = 0; - - /* The first keymap that has a prompt string - supplies the menu title. */ - for (tem = menu, i = 0; XTYPE (tem) == Lisp_Cons; tem = Fcdr (tem)) - { - maps[i++] = keymap = get_keymap (Fcar (tem)); - - prompt = map_prompt (keymap); - if (*title == 0 && !NILP (prompt)) - *title = (char *) XSTRING (prompt)->data; - } - - /* Extract the detailed info to make one pane. */ - *number_of_panes = keymap_panes (obj_list, menus, names, enables, - items, prefixes, maps, nmaps); - /* The menu title seems to be ignored, - so put it in the pane title. */ - if ((*menus)[0] == 0) - (*menus)[0] = *title; - } - else - { - /* We were given an old-fashioned menu. */ - ltitle = Fcar (menu); - CHECK_STRING (ltitle, 1); - *title = (char *) XSTRING (ltitle)->data; - *prefixes = 0; - *number_of_panes = list_of_panes (obj_list, menus, names, enables, - items, Fcdr (menu)); - } - - *error_name = 0; - if (*number_of_panes == 0) - return 0; - - *error_name = (char *) 0; /* Initialize error pointer to null */ - - wv = malloc_widget_value (); - wv->name = "menu"; - wv->value = 0; - wv->enabled = 1; - first_wv = wv; - - for (*panes = 0, lines = 0; *panes < *number_of_panes; - lines += (*items)[*panes], (*panes)++) - ; - /* datap = (struct indices *) xmalloc (lines * sizeof (struct indices)); */ - /* datap = (char *) xmalloc (lines * sizeof (char)); - datap_save = datap;*/ - - for (*panes = 0, sofar = 0; *panes < *number_of_panes; - sofar += (*items)[*panes], (*panes)++) - { - if (strcmp((*menus)[*panes], "")) - { - wv = malloc_widget_value (); - if (save_wv) - save_wv->next = wv; - else - first_wv->contents = wv; - wv->name = (*menus)[*panes]; - wv->value = 0; - wv->enabled = 1; - } - prev_wv = 0; - save_wv = wv; - - for (selidx = 0; selidx < (*items)[*panes]; selidx++) - { - wv = malloc_widget_value (); - if (prev_wv) - prev_wv->next = wv; - else - save_wv->contents = wv; - wv->name = (*names)[*panes][selidx]; - wv->value = 0; - selection = (*obj_list)[*panes][selidx]; - if (*prefixes != 0) - { - selection = Fcons (selection, Qnil); - if (!NILP ((*prefixes)[*panes])) - selection = Fcons ((*prefixes)[*panes], selection); - } - wv->call_data = LISP_TO_VOID(selection); - wv->enabled = (*enables)[*panes][selidx]; - prev_wv = wv; - } - } - - return (first_wv); -} - -static void -free_menu_items (names, enables, menus, items, number_of_panes, - obj_list, title, error_name) - char **names[]; - int *enables[]; - char **menus; - int *items; - int number_of_panes; - Lisp_Object **obj_list; - char *title; - char *error_name; -{ - int i; - /* now free up the strings */ - for (i = 0; i < number_of_panes; i++) - { - xfree (names[i]); - xfree (enables[i]); - xfree (obj_list[i]); - } - xfree (menus); - xfree (obj_list); - xfree (names); - xfree (enables); - xfree (items); - /* free (title); */ - if (error_name) error (error_name); - -} - -static Lisp_Object menu_item_selection; +static Lisp_Object *menu_item_selection; static void popup_selection_callback (widget, id, client_data) @@ -681,7 +830,7 @@ LWLIB_ID id; XtPointer client_data; { - VOID_TO_LISP (menu_item_selection, client_data); + menu_item_selection = (Lisp_Object *) client_data; } static void @@ -697,16 +846,14 @@ /* This recursively calls free_widget_value() on the tree of widgets. It must free all data that was malloc'ed for these widget_values. - Currently, emacs only allocates new storage for the `key' slot. - All other slots are pointers into the data of Lisp_Strings, and - must be left alone. - */ + In Emacs, many slots are pointers into the data of Lisp_Strings, and + must be left alone. */ + void free_menubar_widget_value_tree (wv) widget_value *wv; { if (! wv) return; - if (wv->key) xfree (wv->key); wv->name = wv->value = wv->key = (char *) 0xDEADBEEF; @@ -843,64 +990,268 @@ UNBLOCK_INPUT; } } + +/* Nonzero if position X, Y relative to inside of frame F + is in some other menu bar item. */ + +static int this_menu_bar_item_beg; +static int this_menu_bar_item_end; + +static int +other_menu_bar_item_p (f, x, y) + FRAME_PTR f; + int x, y; +{ + return (y >= 0 + && y < f->display.x->menubar_widget->core.height + && x >= 0 + && x < f->display.x->menubar_widget->core.width + && (x >= this_menu_bar_item_end + || x < this_menu_bar_item_beg)); +} + +/* Unread a button-press event in the menu bar of frame F + at x position XPOS relative to the inside of the frame. */ + +static void +unread_menu_bar_button (f, xpos) + FRAME_PTR f; + int xpos; +{ + XEvent event; + + event.type = ButtonPress; + event.xbutton.display = x_current_display; + event.xbutton.serial = 0; + event.xbutton.send_event = 0; + event.xbutton.time = CurrentTime; + event.xbutton.button = Button1; + event.xbutton.window = XtWindow (f->display.x->menubar_widget); + event.xbutton.x = xpos; + XPutBackEvent (XDISPLAY &event); +} + +/* If the mouse has moved to another menu bar item, + return 1 and unread a button press event for that item. + Otherwise return 0. */ + +static int +check_mouse_other_menu_bar (f) + FRAME_PTR f; +{ + FRAME_PTR new_f; + Lisp_Object bar_window; + int part; + Lisp_Object x, y; + unsigned long time; + + (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time); + + if (f == new_f && other_menu_bar_item_p (f, x, y)) + { + unread_menu_bar_button (f, x); + return 1; + } + + return 0; +} #endif /* USE_X_TOOLKIT */ -struct indices { - int pane; - int line; -}; +/* xmenu_show actually displays a menu using the panes and items in menu_items + and returns the value selected from it. + There are two versions of xmenu_show, one for Xt and one for Xlib. + Both assume input is blocked by the caller. */ -extern void process_expose_from_menu (); +/* F is the frame the menu is for. + X and Y are the frame-relative specified position, + relative to the inside upper left corner of the frame F. + MENUBARP is 1 if the click that asked for this menu came from the menu bar. + KEYMAPS is 1 if this menu was specified with keymaps; + in that case, we return a list containing the chosen item's value + and perhaps also the pane's prefix. + TITLE is the specified menu title. + ERROR is a place to store an error message string in case of failure. + (We return nil on failure, but the value doesn't actually matter.) */ #ifdef USE_X_TOOLKIT -extern XtAppContext Xt_app_con; -Lisp_Object -xmenu_show (f, val, x, y, menubarp, vw) +static Lisp_Object +xmenu_show (f, x, y, menubarp, keymaps, title, error) FRAME_PTR f; - widget_value *val; int x; int y; int menubarp; - widget_value *vw; + int keymaps; + Lisp_Object title; + char **error; { - int menu_id, item_length; - Lisp_Object selection; + int i; + int menu_id; Widget menu; - XlwMenuWidget menuw = (XlwMenuWidget) f->display.x->menubar_widget; + XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget; - /* - * Define and allocate a foreign event queue to hold events - * that don't belong to XMenu. These events are later restored - * to the X event queue. - */ - typedef struct _xmeventque + /* This is the menu bar item (if any) that led to this menu. */ + widget_value *menubar_item = 0; + + widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0; + + /* Define a queue to save up for later unreading + all X events that don't pertain to the menu. */ + struct event_queue { XEvent event; - struct _xmeventque *next; - } XMEventQue; - - XMEventQue *feq = NULL; /* Foreign event queue. */ - XMEventQue *feq_tmp; /* Foreign event queue temporary. */ + struct event_queue *next; + }; - BLOCK_INPUT; - if (val == 0) return Qnil; + struct event_queue *queue = NULL; + struct event_queue *queue_tmp; + + *error = NULL; + + this_menu_bar_item_beg = -1; + this_menu_bar_item_end = -1; + + /* Figure out which menu bar item, if any, this menu is for. */ + if (menubarp) + { + int xbeg; + int xend = 0; + + for (menubar_item = menubar->menu.old_stack[0]->contents; + menubar_item; + menubar_item = menubar_item->next) + { + xbeg = xend; + xend += (string_width (menubar, menubar_item->name) + + 2 * (menubar->menu.horizontal_spacing + + menubar->menu.shadow_thickness)); + if (x < xend) + { + x = xbeg + 4; + y = 0; + /* Arrange to show a different menu if we move in the menu bar + to a different item. */ + this_menu_bar_item_beg = xbeg; + this_menu_bar_item_end = xend; + break; + } + } + } + if (menubar_item == 0) + menubarp = 0; + + /* Offset the coordinates to root-relative. */ + x += (f->display.x->widget->core.x + + f->display.x->widget->core.border_width); + y += (f->display.x->widget->core.y + + f->display.x->widget->core.border_width + + f->display.x->menubar_widget->core.height); + /* Create a tree of widget_value objects + representing the panes and their items. */ + wv = malloc_widget_value (); + wv->name = "menu"; + wv->value = 0; + wv->enabled = 1; + first_wv = wv; + + /* Loop over all panes and items, filling in the tree. */ + i = 0; + while (i < menu_items_used) + { + if (EQ (XVECTOR (menu_items)->contents[i], Qt)) + { + /* Create a new pane. */ + Lisp_Object pane_name, prefix; + char *pane_string; + pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME]; + prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; + pane_string = (NILP (pane_name) + ? "" : (char *) XSTRING (pane_name)->data); + /* If there is just one pane, put all its items directly + under the top-level menu. */ + if (menu_items_n_panes == 1) + pane_string = ""; + + /* If the pane has a meaningful name, + make the pane a top-level menu item + with its items as a submenu beneath it. */ + if (strcmp (pane_string, "")) + { + wv = malloc_widget_value (); + if (save_wv) + save_wv->next = wv; + else + first_wv->contents = wv; + wv->name = pane_string; + if (keymaps && !NILP (prefix)) + wv->name++; + wv->value = 0; + wv->enabled = 1; + } + save_wv = wv; + prev_wv = 0; + i += MENU_ITEMS_PANE_LENGTH; + } + else + { + /* Create a new item within current pane. */ + Lisp_Object item_name, enable, descrip; + item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME]; + enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE]; + descrip + = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY]; + + wv = malloc_widget_value (); + if (prev_wv) + prev_wv->next = wv; + else + save_wv->contents = wv; + wv->name = XSTRING (item_name)->data; + if (!NILP (descrip)) + wv->key = XSTRING (descrip)->data; + wv->value = 0; + wv->call_data = (void *) &XVECTOR (menu_items)->contents[i]; + wv->enabled = !NILP (enable); + prev_wv = wv; + + i += MENU_ITEMS_ITEM_LENGTH; + } + } + + /* Actually create the menu. */ menu_id = ++popup_id_tick; - menu = lw_create_widget ("popup", val->name, menu_id, val, + menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv, f->display.x->widget, 1, 0, popup_selection_callback, popup_down_callback); - free_menubar_widget_value_tree (val); + /* Free the widget_value objects we used to specify the contents. */ + free_menubar_widget_value_tree (first_wv); + + /* No selection has been chosen yet. */ + menu_item_selection = 0; - /* reset the selection */ - menu_item_selection = Qnil; + /* If the mouse moves out of the menu before we show the menu, + don't show it at all. */ + if (check_mouse_other_menu_bar (f)) + { + lw_destroy_all_widgets (menu_id); + return Qnil; + } + + /* Highlight the menu bar item (if any) that led to this menu. */ + if (menubarp) + { + menubar_item->call_data = (XtPointer) 1; + dispatch_dummy_expose (f->display.x->menubar_widget, x, y); + } + + /* Display the menu. */ { XButtonPressedEvent dummy; XlwMenuWidget mw; - mw = ((XlwMenuWidget) - ((CompositeWidget)menu)->composite.children[0]); + mw = (XlwMenuWidget) ((CompositeWidget)menu)->composite.children[0]; dummy.type = ButtonPress; dummy.serial = 0; @@ -912,731 +1263,347 @@ dummy.x_root = x; dummy.y_root = y; - if (menubarp) - { - vw->call_data = (XtPointer) 1; - dispatch_dummy_expose (f->display.x->menubar_widget, x, y); - } - - - /* We activate directly the lucid implementation */ + /* We activate directly the lucid implementation. */ pop_up_menu (mw, &dummy); } - if (menubarp) + /* Check again whether the mouse has moved to another menu bar item. */ + if (check_mouse_other_menu_bar (f)) { - item_length = (x + string_width (menuw, vw->name) - + (2 * (menuw->menu.horizontal_spacing - + menuw->menu.shadow_thickness)) - - 4); + /* The mouse moved into a different menu bar item. + We should bring up that item's menu instead. + First pop down this menu. */ + XtUngrabPointer ((Widget) + ((XlwMenuWidget) + ((CompositeWidget)menu)->composite.children[0]), + CurrentTime); + lw_destroy_all_widgets (menu_id); + goto pop_down; } - /* Enters XEvent loop */ + /* Process events that apply to the menu. */ while (1) { + XEvent event; - XEvent event; XtAppNextEvent (Xt_app_con, &event); if (event.type == ButtonRelease) { XtDispatchEvent (&event); break; } - else - if (event.type == Expose) - process_expose_from_menu (event); - else - if (event.type == MotionNotify - && menubarp - && ((event.xmotion.y_root - >= (f->display.x->widget->core.y - + f->display.x->widget->core.border_width)) - && (event.xmotion.y_root - < (f->display.x->widget->core.y - + f->display.x->widget->core.border_width - + f->display.x->menubar_widget->core.height))) - && ((event.xmotion.x_root - >= (f->display.x->widget->core.x - + f->display.x->widget->core.border_width)) - && (event.xmotion.x_root - < (f->display.x->widget->core.x - + f->display.x->widget->core.border_width - + f->display.x->widget->core.width))) - && (event.xmotion.x_root >= item_length - || event.xmotion.x_root < (x - 4))) - { - BLOCK_INPUT; - XtUngrabPointer ((Widget) - ((XlwMenuWidget) - ((CompositeWidget)menu)->composite.children[0]), - event.xbutton.time); - lw_destroy_all_widgets (menu_id); - UNBLOCK_INPUT; + else if (event.type == Expose) + process_expose_from_menu (event); + else if (event.type == MotionNotify) + { + int event_x = (event.xmotion.x_root + - (f->display.x->widget->core.x + + f->display.x->widget->core.border_width)); + int event_y = (event.xmotion.y_root + - (f->display.x->widget->core.y + + f->display.x->widget->core.border_width)); - event.type = ButtonPress; - event.xbutton.time = CurrentTime; - event.xbutton.button = Button1; - event.xbutton.window = XtWindow (f->display.x->menubar_widget); - event.xbutton.x = (event.xbutton.x_root - - (f->display.x->widget->core.x - + f->display.x->widget->core.border_width)); - XPutBackEvent (XDISPLAY &event); - break; - } + if (other_menu_bar_item_p (f, event_x, event_y)) + { + /* The mouse moved into a different menu bar item. + We should bring up that item's menu instead. + First pop down this menu. */ + XtUngrabPointer ((Widget) + ((XlwMenuWidget) + ((CompositeWidget)menu)->composite.children[0]), + event.xbutton.time); + lw_destroy_all_widgets (menu_id); + + /* Put back an event that will bring up the other item's menu. */ + unread_menu_bar_button (f, event_x); + /* Don't let us select anything in this case. */ + menu_item_selection = 0; + break; + } + } XtDispatchEvent (&event); - feq_tmp = (XMEventQue *) malloc (sizeof (XMEventQue)); - - if (feq_tmp == NULL) - return(Qnil); + queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue)); - feq_tmp->event = event; - feq_tmp->next = feq; - feq = feq_tmp; + if (queue_tmp != NULL) + { + queue_tmp->event = event; + queue_tmp->next = queue; + queue = queue_tmp; + } } - + + pop_down: + /* Unhighlight the menu bar item (if any) that led to this menu. */ if (menubarp) { - vw->call_data = (XtPointer) 0; + menubar_item->call_data = (XtPointer) 0; dispatch_dummy_expose (f->display.x->menubar_widget, x, y); } - /* Return any foreign events that were queued to the X event queue. */ - while (feq != NULL) + /* Make sure the menu disappears. */ + lw_destroy_all_widgets (menu_id); + + /* Unread any events that we got but did not handle. */ + while (queue != NULL) { - feq_tmp = feq; - XPutBackEvent (XDISPLAY &feq_tmp->event); - feq = feq_tmp->next; - free ((char *)feq_tmp); + queue_tmp = queue; + XPutBackEvent (XDISPLAY &queue_tmp->event); + queue = queue_tmp->next; + free ((char *)queue_tmp); } - UNBLOCK_INPUT; + /* Find the selected item, and its pane, to return + the proper value. */ + if (menu_item_selection != 0) + { + Lisp_Object prefix; + + prefix = Qnil; + i = 0; + while (i < menu_items_used) + { + Lisp_Object entry; - return menu_item_selection; + if (EQ (XVECTOR (menu_items)->contents[i], Qt)) + { + prefix + = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; + i += MENU_ITEMS_PANE_LENGTH; + } + else + { + entry + = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE]; + if (menu_item_selection == &XVECTOR (menu_items)->contents[i]) + { + if (keymaps != 0) + { + entry = Fcons (entry, Qnil); + if (!NILP (prefix)) + entry = Fcons (prefix, entry); + } + return entry; + } + i += MENU_ITEMS_ITEM_LENGTH; + } + } + } + + return Qnil; } #else /* not USE_X_TOOLKIT */ -xmenu_show (parent, startx, starty, line_list, enable_list, pane_list, - prefixes, line_cnt, pane_cnt, item_list, title, error) - Window parent; - int startx, starty; /* upper left corner position BROKEN */ - char **line_list[]; /* list of strings for items */ - int *enable_list[]; /* enable flags of lines */ - char *pane_list[]; /* list of pane titles */ - Lisp_Object *prefixes; /* Prefix key for each pane */ - char *title; - int pane_cnt; /* total number of panes */ - Lisp_Object *item_list[]; /* All items */ - int line_cnt[]; /* Lines in each pane */ - char **error; /* Error returned */ + +static Lisp_Object +xmenu_show (f, x, y, menubarp, keymaps, title, error) + FRAME_PTR f; + int x, y; + int keymaps; + int menubarp; + Lisp_Object title; + char **error; { - XMenu *GXMenu; - int last, panes, selidx, lpane, status; - int lines, sofar; - Lisp_Object entry; - /* struct indices *datap, *datap_save; */ + Window root; + XMenu *menu; + int pane, selidx, lpane, status; + Lisp_Object entry, pane_prefix; char *datap; int ulx, uly, width, height; int dispwidth, dispheight; + int i; + int dummy_int; + unsigned int dummy_uint; *error = 0; - if (pane_cnt == 0) - return 0; + if (menu_items_n_panes == 0) + return Qnil; - BLOCK_INPUT; - *error = (char *) 0; /* Initialize error pointer to null */ + /* Figure out which root window F is on. */ + XGetGeometry (x_current_display, FRAME_X_WINDOW (f), &root, + &dummy_int, &dummy_int, &dummy_uint, &dummy_uint, + &dummy_uint, &dummy_uint); - GXMenu = XMenuCreate (XDISPLAY parent, "emacs"); - if (GXMenu == NUL) + /* Make the menu on that window. */ + menu = XMenuCreate (XDISPLAY root, "emacs"); + if (menu == NULL) { *error = "Can't create menu"; - UNBLOCK_INPUT; - return (0); + return Qnil; } + + /* Adjust coordinates to relative to the outer (window manager) window. */ +#ifdef HAVE_X11 + { + Window child; + int win_x = 0, win_y = 0; + + /* Find the position of the outside upper-left corner of + the inner window, with respect to the outer window. */ + if (f->display.x->parent_desc != ROOT_WINDOW) + { + BLOCK_INPUT; + XTranslateCoordinates (x_current_display, + + /* From-window, to-window. */ + f->display.x->window_desc, + f->display.x->parent_desc, + + /* From-position, to-position. */ + 0, 0, &win_x, &win_y, + + /* Child of window. */ + &child); + UNBLOCK_INPUT; + x += win_x; + y += win_y; + } + } +#endif /* HAVE_X11 */ + + /* Adjust coordinates to be root-window-relative. */ + x += f->display.x->left_pos; + y += f->display.x->top_pos; - for (panes = 0, lines = 0; panes < pane_cnt; - lines += line_cnt[panes], panes++) - ; - /* datap = (struct indices *) xmalloc (lines * sizeof (struct indices)); */ - /* datap = (char *) xmalloc (lines * sizeof (char)); - datap_save = datap;*/ - - for (panes = 0, sofar = 0; panes < pane_cnt; - sofar += line_cnt[panes], panes++) + /* Create all the necessary panes and their items. */ + i = 0; + while (i < menu_items_used) { - /* create all the necessary panes */ - lpane = XMenuAddPane (XDISPLAY GXMenu, pane_list[panes], TRUE); - if (lpane == XM_FAILURE) + if (EQ (XVECTOR (menu_items)->contents[i], Qt)) { - XMenuDestroy (XDISPLAY GXMenu); - *error = "Can't create pane"; - UNBLOCK_INPUT; - return (0); + /* Create a new pane. */ + Lisp_Object pane_name, prefix; + char *pane_string; + + pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME]; + prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; + pane_string = (NILP (pane_name) + ? "" : (char *) XSTRING (pane_name)->data); + if (keymaps && !NILP (prefix)) + pane_string++; + + lpane = XMenuAddPane (XDISPLAY menu, pane_string, TRUE); + if (lpane == XM_FAILURE) + { + XMenuDestroy (XDISPLAY menu); + *error = "Can't create pane"; + return Qnil; + } + i += MENU_ITEMS_PANE_LENGTH; } - - for (selidx = 0; selidx < line_cnt[panes]; selidx++) + else { - /* add the selection stuff to the menus */ - /* datap[selidx+sofar].pane = panes; - datap[selidx+sofar].line = selidx; */ - if (XMenuAddSelection (XDISPLAY GXMenu, lpane, 0, - line_list[panes][selidx], - enable_list[panes][selidx]) + /* Create a new item within current pane. */ + Lisp_Object item_name, enable, descrip; + + item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME]; + enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE]; + descrip + = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY]; + if (!NILP (descrip)) + item_name = concat2 (item_name, descrip); + + if (XMenuAddSelection (XDISPLAY menu, lpane, 0, + XSTRING (item_name)->data, + !NILP (enable)) == XM_FAILURE) { - XMenuDestroy (XDISPLAY GXMenu); - /* free (datap); */ + XMenuDestroy (XDISPLAY menu); *error = "Can't add selection to menu"; - /* error ("Can't add selection to menu"); */ - UNBLOCK_INPUT; - return (0); + return Qnil; } + i += MENU_ITEMS_ITEM_LENGTH; } } - /* all set and ready to fly */ - XMenuRecompute (XDISPLAY GXMenu); + + /* All set and ready to fly. */ + XMenuRecompute (XDISPLAY menu); dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display)); dispheight = DisplayHeight (x_current_display, XDefaultScreen (x_current_display)); - startx = min (startx, dispwidth); - starty = min (starty, dispheight); - startx = max (startx, 1); - starty = max (starty, 1); - XMenuLocate (XDISPLAY GXMenu, 0, 0, startx, starty, + x = min (x, dispwidth); + y = min (y, dispheight); + x = max (x, 1); + y = max (y, 1); + XMenuLocate (XDISPLAY menu, 0, 0, x, y, &ulx, &uly, &width, &height); if (ulx+width > dispwidth) { - startx -= (ulx + width) - dispwidth; + x -= (ulx + width) - dispwidth; ulx = dispwidth - width; } if (uly+height > dispheight) { - starty -= (uly + height) - dispheight; + y -= (uly + height) - dispheight; uly = dispheight - height; } - if (ulx < 0) startx -= ulx; - if (uly < 0) starty -= uly; + if (ulx < 0) x -= ulx; + if (uly < 0) y -= uly; - XMenuSetFreeze (GXMenu, TRUE); - panes = selidx = 0; + XMenuSetFreeze (menu, TRUE); + pane = selidx = 0; - status = XMenuActivate (XDISPLAY GXMenu, &panes, &selidx, - startx, starty, ButtonReleaseMask, &datap); + status = XMenuActivate (XDISPLAY menu, &pane, &selidx, + x, y, ButtonReleaseMask, &datap); switch (status) { case XM_SUCCESS: #ifdef XDEBUG fprintf (stderr, "pane= %d line = %d\n", panes, selidx); #endif - entry = item_list[panes][selidx]; - if (prefixes != 0) + + /* Find the item number SELIDX in pane number PANE. */ + i = 0; + while (i < menu_items_used) { - entry = Fcons (entry, Qnil); - if (!NILP (prefixes[panes])) - entry = Fcons (prefixes[panes], entry); + if (EQ (XVECTOR (menu_items)->contents[i], Qt)) + { + if (pane == 0) + pane_prefix + = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; + pane--; + i += MENU_ITEMS_PANE_LENGTH; + } + else + { + if (pane == -1) + { + if (selidx == 0) + { + entry + = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE]; + if (keymaps != 0) + { + entry = Fcons (entry, Qnil); + if (!NILP (pane_prefix)) + entry = Fcons (pane_prefix, entry); + } + break; + } + selidx--; + } + i += MENU_ITEMS_ITEM_LENGTH; + } } break; + case XM_FAILURE: - /* free (datap_save); */ - XMenuDestroy (XDISPLAY GXMenu); + XMenuDestroy (XDISPLAY menu); *error = "Can't activate menu"; - /* error ("Can't activate menu"); */ case XM_IA_SELECT: case XM_NO_SELECT: entry = Qnil; break; } - XMenuDestroy (XDISPLAY GXMenu); - UNBLOCK_INPUT; - /* free (datap_save);*/ - return (entry); + XMenuDestroy (XDISPLAY menu); + return entry; } #endif /* not USE_X_TOOLKIT */ - + syms_of_xmenu () { + staticpro (&menu_items); + menu_items = Qnil; + popup_id_tick = (1<<16); defsubr (&Sx_popup_menu); } - -/* Figure out the current keyboard equivalent of a menu item ITEM1. - Store the equivalent key sequence in *SAVEDKEY_PTR - and the textual description (to use in the menu display) in *DESCRIP_PTR. - Also cache them in the item itself. - Return the real definition to execute. */ - -static Lisp_Object -menu_item_equiv_key (item1, savedkey_ptr, descrip_ptr) - Lisp_Object item1; - Lisp_Object *savedkey_ptr, *descrip_ptr; -{ - /* This is what is left after the menu item name. */ - Lisp_Object overdef; - /* This is the real definition--the function to run. */ - Lisp_Object def; - /* These are the saved equivalent keyboard key sequence - and its key-description. */ - Lisp_Object savedkey, descrip; - Lisp_Object def1; - int changed = 0; - - overdef = def = Fcdr (item1); - - /* Get out the saved equivalent-keyboard-key info. */ - savedkey = descrip = Qnil; - if (CONSP (overdef) && VECTORP (XCONS (overdef)->car)) - { - savedkey = XCONS (overdef)->car; - def = XCONS (def)->cdr; - if (CONSP (def) && STRINGP (XCONS (def)->car)) - { - descrip = XCONS (def)->car; - def = XCONS (def)->cdr; - } - } - - /* Is it still valid? */ - def1 = Qnil; - if (!NILP (savedkey)) - def1 = Fkey_binding (savedkey, Qnil); - /* If not, update it. */ - if (! EQ (def1, def)) - { - changed = 1; - descrip = Qnil; - savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil); - if (VECTORP (savedkey) - && EQ (XVECTOR (savedkey)->contents[0], Qmenu_bar)) - savedkey = Qnil; - if (!NILP (savedkey)) - { - descrip = Fkey_description (savedkey); - descrip = concat2 (make_string (" (", 3), descrip); - descrip = concat2 (descrip, make_string (")", 1)); - } - } - - /* Store back the recorded keyboard key sequence - if we changed it. */ - if (!NILP (savedkey) - && CONSP (overdef) && VECTORP (XCONS (overdef)->car)) - { - if (changed) - { - XCONS (overdef)->car = savedkey; - def1 = XCONS (overdef)->cdr; - if (CONSP (def1) && STRINGP (XCONS (def1)->car)) - XCONS (def1)->car = descrip; - } - } - /* If we had none but need one now, add it. */ - else if (!NILP (savedkey)) - XCONS (item1)->cdr - = overdef = Fcons (savedkey, Fcons (descrip, def)); - /* If we had one but no longer should have one, delete it. */ - else if (CONSP (overdef) && VECTORP (XCONS (overdef)->car)) - { - XCONS (item1)->cdr = overdef = XCONS (overdef)->cdr; - if (CONSP (overdef) && STRINGP (XCONS (overdef)->car)) - XCONS (item1)->cdr = overdef = XCONS (overdef)->cdr; - } - - *savedkey_ptr = savedkey; - *descrip_ptr = descrip; - return def; -} - -/* Construct the vectors that describe a menu - and store them in *VECTOR, *PANES, *NAMES, *ENABLES and *ITEMS. - Each of those four values is a vector indexed by pane number. - Return the number of panes. - - KEYMAPS is a vector of keymaps. NMAPS gives the length of KEYMAPS. */ - -int -keymap_panes (vector, panes, names, enables, items, prefixes, keymaps, nmaps) - Lisp_Object ***vector; /* RETURN all menu objects */ - char ***panes; /* RETURN pane names */ - char ****names; /* RETURN all line names */ - int ***enables; /* RETURN enable-flags of lines */ - int **items; /* RETURN number of items per pane */ - Lisp_Object **prefixes; /* RETURN vector of prefix keys, per pane */ - Lisp_Object *keymaps; - int nmaps; -{ - /* Number of panes we have made. */ - int p = 0; - /* Number of panes we have space for. */ - int npanes_allocated = nmaps; - int mapno; - - if (npanes_allocated < 4) - npanes_allocated = 4; - - /* Make space for an estimated number of panes. */ - *vector = (Lisp_Object **) xmalloc (npanes_allocated * sizeof (Lisp_Object *)); - *panes = (char **) xmalloc (npanes_allocated * sizeof (char *)); - *items = (int *) xmalloc (npanes_allocated * sizeof (int)); - *names = (char ***) xmalloc (npanes_allocated * sizeof (char **)); - *enables = (int **) xmalloc (npanes_allocated * sizeof (int *)); - *prefixes = (Lisp_Object *) xmalloc (npanes_allocated * sizeof (Lisp_Object)); - - /* Loop over the given keymaps, making a pane for each map. - But don't make a pane that is empty--ignore that map instead. - P is the number of panes we have made so far. */ - for (mapno = 0; mapno < nmaps; mapno++) - single_keymap_panes (keymaps[mapno], panes, vector, names, enables, items, - prefixes, &p, &npanes_allocated, ""); - - /* Return the number of panes. */ - return p; -} - -/* This is used as the handler when calling internal_condition_case_1. */ - -static Lisp_Object -single_keymap_panes_1 (arg) - Lisp_Object arg; -{ - return Qnil; -} - -/* This is a recursive subroutine of keymap_panes. - It handles one keymap, KEYMAP. - The other arguments are passed along - or point to local variables of the previous function. */ - -single_keymap_panes (keymap, panes, vector, names, enables, items, prefixes, - p_ptr, npanes_allocated_ptr, pane_name) - Lisp_Object keymap; - Lisp_Object ***vector; /* RETURN all menu objects */ - char ***panes; /* RETURN pane names */ - char ****names; /* RETURN all line names */ - int ***enables; /* RETURN enable flags of lines */ - int **items; /* RETURN number of items per pane */ - Lisp_Object **prefixes; /* RETURN vector of prefix keys, per pane */ - int *p_ptr; - int *npanes_allocated_ptr; - char *pane_name; -{ - int i; - Lisp_Object pending_maps; - Lisp_Object tail, item, item1, item_string, table; - struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; - - pending_maps = Qnil; - - /* Make sure we have room for another pane. */ - if (*p_ptr == *npanes_allocated_ptr) - { - *npanes_allocated_ptr *= 2; - - *vector - = (Lisp_Object **) xrealloc (*vector, - *npanes_allocated_ptr * sizeof (Lisp_Object *)); - *panes - = (char **) xrealloc (*panes, - *npanes_allocated_ptr * sizeof (char *)); - *items - = (int *) xrealloc (*items, - *npanes_allocated_ptr * sizeof (int)); - *prefixes - = (Lisp_Object *) xrealloc (*prefixes, - (*npanes_allocated_ptr - * sizeof (Lisp_Object))); - *names - = (char ***) xrealloc (*names, - *npanes_allocated_ptr * sizeof (char **)); - *enables - = (int **) xrealloc (*enables, - *npanes_allocated_ptr * sizeof (int *)); - } - - /* When a menu comes from keymaps, don't give names to the panes. */ - (*panes)[*p_ptr] = pane_name; - - /* Normally put nil as pane's prefix key. - Caller will override this if appropriate. */ - (*prefixes)[*p_ptr] = Qnil; - - /* Get the length of the list level of the keymap. */ - i = XFASTINT (Flength (keymap)); - - /* Add in lengths of any arrays. */ - for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr) - if (XTYPE (XCONS (tail)->car) == Lisp_Vector) - i += XVECTOR (XCONS (tail)->car)->size; - - /* Create vectors for the names and values of the items in the pane. - I is an upper bound for the number of items. */ - (*vector)[*p_ptr] = (Lisp_Object *) xmalloc (i * sizeof (Lisp_Object)); - (*names)[*p_ptr] = (char **) xmalloc (i * sizeof (char *)); - (*enables)[*p_ptr] = (int *) xmalloc (i * sizeof (int)); - - /* I is now the index of the next unused slots. */ - i = 0; - for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr) - { - /* Look at each key binding, and if it has a menu string, - make a menu item from it. */ - item = XCONS (tail)->car; - if (XTYPE (item) == Lisp_Cons) - { - item1 = XCONS (item)->cdr; - if (XTYPE (item1) == Lisp_Cons) - { - item_string = XCONS (item1)->car; - if (XTYPE (item_string) == Lisp_String) - { - /* This is the real definition--the function to run. */ - Lisp_Object def; - /* These are the saved equivalent keyboard key sequence - and its key-description. */ - Lisp_Object savedkey, descrip; - Lisp_Object tem, enabled; - - /* If a help string follows the item string, - skip it. */ - if (CONSP (XCONS (item1)->cdr) - && STRINGP (XCONS (XCONS (item1)->cdr)->car)) - item1 = XCONS (item1)->cdr; - - def = menu_item_equiv_key (item1, &savedkey, &descrip); - - enabled = Qt; - if (XTYPE (def) == Lisp_Symbol) - { - /* No property, or nil, means enable. - Otherwise, enable if value is not nil. */ - tem = Fget (def, Qmenu_enable); - /* GCPRO because we will call eval. - Protecting KEYMAP preserves everything we use; - aside from that, must protect whatever might be - a string. */ - GCPRO3 (keymap, def, descrip, item_string); - if (!NILP (tem)) - /* (condition-case nil (eval tem) - (error nil)) */ - enabled = internal_condition_case_1 (Feval, tem, - Qerror, - single_keymap_panes_1); - UNGCPRO; - } - tem = Fkeymapp (def); - if (XSTRING (item_string)->data[0] == '@' && !NILP (tem)) - pending_maps = Fcons (Fcons (def, Fcons (item_string, XCONS (item)->car)), - pending_maps); - else - { - Lisp_Object concat; - if (!NILP (descrip)) - concat = concat2 (item_string, descrip); - else - concat = item_string; - (*names)[*p_ptr][i] = (char *) XSTRING (concat)->data; - /* The menu item "value" is the key bound here. */ - (*vector)[*p_ptr][i] = XCONS (item)->car; - (*enables)[*p_ptr][i] - = (NILP (def) ? -1 : !NILP (enabled) ? 1 : 0); - i++; - } - } - } - } - else if (XTYPE (item) == Lisp_Vector) - { - /* Loop over the char values represented in the vector. */ - int len = XVECTOR (item)->size; - int c; - for (c = 0; c < len; c++) - { - Lisp_Object character; - XFASTINT (character) = c; - item1 = XVECTOR (item)->contents[c]; - if (XTYPE (item1) == Lisp_Cons) - { - item_string = XCONS (item1)->car; - if (XTYPE (item_string) == Lisp_String) - { - Lisp_Object def; - - /* These are the saved equivalent keyboard key sequence - and its key-description. */ - Lisp_Object savedkey, descrip; - Lisp_Object tem, enabled; - - /* If a help string follows the item string, - skip it. */ - if (CONSP (XCONS (item1)->cdr) - && STRINGP (XCONS (XCONS (item1)->cdr)->car)) - item1 = XCONS (item1)->cdr; - - def = menu_item_equiv_key (item1, &savedkey, &descrip); - - enabled = Qt; - if (XTYPE (def) == Lisp_Symbol) - { - tem = Fget (def, Qmenu_enable); - /* GCPRO because we will call eval. - Protecting KEYMAP preserves everything we use; - aside from that, must protect whatever might be - a string. */ - GCPRO3 (keymap, def, descrip, item_string); - /* No property, or nil, means enable. - Otherwise, enable if value is not nil. */ - if (!NILP (tem)) - /* (condition-case nil (eval tem) - (error nil)) */ - enabled = internal_condition_case_1 (Feval, tem, - Qerror, - single_keymap_panes_1); - UNGCPRO; - } - - tem = Fkeymapp (def); - if (XSTRING (item_string)->data[0] == '@' && !NILP (tem)) - pending_maps = Fcons (Fcons (def, Fcons (item_string, character)), - pending_maps); - else - { - Lisp_Object concat; - if (!NILP (descrip)) - concat = concat2 (item_string, descrip); - else - concat = item_string; - (*names)[*p_ptr][i] - = (char *) XSTRING (concat)->data; - /* The menu item "value" is the key bound here. */ - (*vector)[*p_ptr][i] = character; - (*enables)[*p_ptr][i] - = (NILP (def) ? -1 : !NILP (enabled) ? 1 : 0); - i++; - } - } - } - } - } - } - /* Record the number of items in the pane. */ - (*items)[*p_ptr] = i; - - /* If we just made an empty pane, get rid of it. */ - if (i == 0) - { - xfree ((*vector)[*p_ptr]); - xfree ((*names)[*p_ptr]); - xfree ((*enables)[*p_ptr]); - } - /* Otherwise, advance past it. */ - else - (*p_ptr)++; - - /* Process now any submenus which want to be panes at this level. */ - while (!NILP (pending_maps)) - { - Lisp_Object elt, eltcdr; - int panenum = *p_ptr; - elt = Fcar (pending_maps); - eltcdr = XCONS (elt)->cdr; - single_keymap_panes (Fcar (elt), panes, vector, names, enables, items, - prefixes, p_ptr, npanes_allocated_ptr, - /* Add 1 to discard the @. */ - (char *) XSTRING (XCONS (eltcdr)->car)->data + 1); - (*prefixes)[panenum] = XCONS (eltcdr)->cdr; - pending_maps = Fcdr (pending_maps); - } -} - -/* Construct the vectors that describe a menu - and store them in *VECTOR, *PANES, *NAMES, *ENABLES and *ITEMS. - Each of those four values is a vector indexed by pane number. - Return the number of panes. - - MENU is the argument that was given to Fx_popup_menu. */ - -int -list_of_panes (vector, panes, names, enables, items, menu) - Lisp_Object ***vector; /* RETURN all menu objects */ - char ***panes; /* RETURN pane names */ - char ****names; /* RETURN all line names */ - int ***enables; /* RETURN enable flags of lines */ - int **items; /* RETURN number of items per pane */ - Lisp_Object menu; -{ - Lisp_Object tail, item, item1; - int i; - - if (XTYPE (menu) != Lisp_Cons) menu = wrong_type_argument (Qlistp, menu); - - i = XFASTINT (Flength (menu)); - - *vector = (Lisp_Object **) xmalloc (i * sizeof (Lisp_Object *)); - *panes = (char **) xmalloc (i * sizeof (char *)); - *items = (int *) xmalloc (i * sizeof (int)); - *names = (char ***) xmalloc (i * sizeof (char **)); - *enables = (int **) xmalloc (i * sizeof (int *)); - - for (i = 0, tail = menu; !NILP (tail); tail = Fcdr (tail), i++) - { - item = Fcdr (Fcar (tail)); - if (XTYPE (item) != Lisp_Cons) (void) wrong_type_argument (Qlistp, item); -#ifdef XDEBUG - fprintf (stderr, "list_of_panes check tail, i=%d\n", i); -#endif - item1 = Fcar (Fcar (tail)); - CHECK_STRING (item1, 1); -#ifdef XDEBUG - fprintf (stderr, "list_of_panes check pane, i=%d%s\n", i, - XSTRING (item1)->data); -#endif - (*panes)[i] = (char *) XSTRING (item1)->data; - (*items)[i] = list_of_items ((*vector)+i, (*names)+i, (*enables)+i, item); - /* (*panes)[i] = (char *) xmalloc ((XSTRING (item1)->size)+1); - bcopy (XSTRING (item1)->data, (*panes)[i], XSTRING (item1)->size + 1) - ; */ - } - return i; -} - -/* Construct the lists of values and names for a single pane, from the - alist PANE. Put them in *VECTOR and *NAMES. Put the enable flags - int *ENABLES. Return the number of items. */ - -int -list_of_items (vector, names, enables, pane) - Lisp_Object **vector; /* RETURN menu "objects" */ - char ***names; /* RETURN line names */ - int **enables; /* RETURN enable flags of lines */ - Lisp_Object pane; -{ - Lisp_Object tail, item, item1; - int i; - - if (XTYPE (pane) != Lisp_Cons) pane = wrong_type_argument (Qlistp, pane); - - i = XFASTINT (Flength (pane)); - - *vector = (Lisp_Object *) xmalloc (i * sizeof (Lisp_Object)); - *names = (char **) xmalloc (i * sizeof (char *)); - *enables = (int *) xmalloc (i * sizeof (int)); - - for (i = 0, tail = pane; !NILP (tail); tail = Fcdr (tail), i++) - { - item = Fcar (tail); - if (STRINGP (item)) - { - (*vector)[i] = Qnil; - (*names)[i] = (char *) XSTRING (item)->data; - (*enables)[i] = -1; - } - else - { - CHECK_CONS (item, 0); - (*vector)[i] = Fcdr (item); - item1 = Fcar (item); - CHECK_STRING (item1, 1); - (*names)[i] = (char *) XSTRING (item1)->data; - (*enables)[i] = 1; - } - } - return i; -}