X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/091556892016a825e82a152545fc48e4728b4b0e..00e3ab56664fbf759daa5a98e31976e90a8fca8e:/src/xmenu.c diff --git a/src/xmenu.c b/src/xmenu.c index 09cc1af864..4724b2e7ff 100644 --- a/src/xmenu.c +++ b/src/xmenu.c @@ -1,5 +1,6 @@ /* X Communication module for terminals which understand the X protocol. - Copyright (C) 1986, 1988, 1993, 1994, 1996 Free Software Foundation, Inc. + Copyright (C) 1986, 88, 93, 94, 96, 99, 2000, 2001 + Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -18,7 +19,7 @@ along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* X pop-up deck-of-cards menu facility for gnuemacs. +/* X pop-up deck-of-cards menu facility for GNU Emacs. * * Written by Jon Arnold and Roman Budzianowski * Mods and rewrite by Robert Krawitz @@ -30,18 +31,23 @@ Boston, MA 02111-1307, USA. */ /* Rewritten for clarity and GC protection by rms in Feb 94. */ +#include + /* On 4.3 this loses if it comes after xterm.h. */ #include -#include #include + #include "lisp.h" #include "termhooks.h" +#include "keyboard.h" +#include "keymap.h" #include "frame.h" #include "window.h" -#include "keyboard.h" #include "blockinput.h" #include "buffer.h" +#include "charset.h" +#include "coding.h" #ifdef MSDOS #include "msdos.h" @@ -62,7 +68,9 @@ Boston, MA 02111-1307, USA. */ #include "dispextern.h" #ifdef HAVE_X_WINDOWS +#undef HAVE_MULTILINGUAL_MENU #ifdef USE_X_TOOLKIT +#include "widget.h" #include #include #include @@ -77,9 +85,6 @@ Boston, MA 02111-1307, USA. */ #endif /* not USE_X_TOOLKIT */ #endif /* HAVE_X_WINDOWS */ -#define min(x,y) (((x) < (y)) ? (x) : (y)) -#define max(x,y) (((x) > (y)) ? (x) : (y)) - #ifndef TRUE #define TRUE 1 #define FALSE 0 @@ -107,15 +112,28 @@ extern void process_expose_from_menu (); extern XtAppContext Xt_app_con; static Lisp_Object xdialog_show (); -void popup_get_selection (); +static void popup_get_selection (); + +/* Define HAVE_BOXES if menus can handle radio and toggle buttons. */ + +#define HAVE_BOXES 1 #endif -static Lisp_Object xmenu_show (); -static void keymap_panes (); -static void single_keymap_panes (); -static void single_menu_item (); -static void list_of_panes (); -static void list_of_items (); +static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object, + Lisp_Object, Lisp_Object, Lisp_Object, + Lisp_Object, Lisp_Object)); +static int update_frame_menubar P_ ((struct frame *)); +static Lisp_Object xmenu_show P_ ((struct frame *, int, int, int, int, + Lisp_Object, char **)); +static void keymap_panes P_ ((Lisp_Object *, int, int)); +static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object, + int, int)); +static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object *, + int, int, int *)); +static void list_of_panes P_ ((Lisp_Object)); +static void list_of_items P_ ((Lisp_Object)); + +extern EMACS_TIME timer_check P_ ((int)); /* This holds a Lisp vector that holds the results of decoding the keymaps or alist-of-alists that specify a menu. @@ -143,15 +161,25 @@ static void list_of_items (); #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_DEFINITION 4 -#define MENU_ITEMS_ITEM_LENGTH 5 +enum menu_item_idx +{ + MENU_ITEMS_ITEM_NAME = 0, + MENU_ITEMS_ITEM_ENABLE, + MENU_ITEMS_ITEM_VALUE, + MENU_ITEMS_ITEM_EQUIV_KEY, + MENU_ITEMS_ITEM_DEFINITION, + MENU_ITEMS_ITEM_TYPE, + MENU_ITEMS_ITEM_SELECTED, + MENU_ITEMS_ITEM_HELP, + MENU_ITEMS_ITEM_LENGTH +}; static Lisp_Object menu_items; +/* If non-nil, means that the global vars defined here are already in use. + Used to detect cases where we try to re-enter this non-reentrant code. */ +static Lisp_Object menu_items_inuse; + /* Number of slots currently allocated in menu_items. */ static int menu_items_allocated; @@ -167,7 +195,7 @@ static int menu_items_submenu_depth; /* Flag which when set indicates a dialog or menu has been posted by Xt on behalf of one of the widget sets. */ -static int popup_activated_flag; +int popup_activated_flag; static int next_menubar_widget_id; @@ -191,13 +219,13 @@ menubar_id_to_frame (id) Lisp_Object tail, frame; FRAME_PTR f; - for (tail = Vframe_list; GC_CONSP (tail); tail = XCONS (tail)->cdr) + for (tail = Vframe_list; GC_CONSP (tail); tail = XCDR (tail)) { - frame = XCONS (tail)->car; + frame = XCAR (tail); if (!GC_FRAMEP (frame)) continue; f = XFRAME (frame); - if (f->output_data.nothing == 1) + if (!FRAME_WINDOW_P (f)) continue; if (f->output_data.x->id == id) return f; @@ -219,19 +247,28 @@ init_menu_items () menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil); } + if (!NILP (menu_items_inuse)) + error ("Trying to use a menu from within a menu-entry"); + menu_items_inuse = Qt; menu_items_used = 0; menu_items_n_panes = 0; menu_items_submenu_depth = 0; } -/* Call at the end of generating the data in menu_items. - This fills in the number of items in the last pane. */ +/* Call at the end of generating the data in menu_items. */ static void finish_menu_items () { } +static Lisp_Object +unuse_menu_items (dummy) + int dummy; +{ + return menu_items_inuse = Qnil; +} + /* Call when finished using the data for the current menu in menu_items. */ @@ -245,6 +282,7 @@ discard_menu_items () menu_items = Qnil; menu_items_allocated = 0; } + xassert (NILP (menu_items_inuse)); } /* Make the menu_items vector twice as large. */ @@ -297,7 +335,7 @@ push_left_right_boundary () XVECTOR (menu_items)->contents[menu_items_used++] = Qquote; } -/* Start a new menu pane in menu_items.. +/* Start a new menu pane in menu_items. NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */ static void @@ -314,17 +352,17 @@ push_menu_pane (name, prefix_vec) 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, or nil if this item doesn't really have a definition. - DEF is the definition of this item. - EQUIV is the textual description of the keyboard equivalent for - this item (or nil if none). */ +/* 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, or nil if this item + doesn't really have a definition. DEF is the definition of this + item. EQUIV is the textual description of the keyboard equivalent + for this item (or nil if none). TYPE is the type of this menu + item, one of nil, `toggle' or `radio'. */ static void -push_menu_item (name, enable, key, def, equiv) - Lisp_Object name, enable, key, def, equiv; +push_menu_item (name, enable, key, def, equiv, type, selected, help) + Lisp_Object name, enable, key, def, equiv, type, selected, help; { if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated) grow_menu_items (); @@ -334,6 +372,9 @@ push_menu_item (name, enable, key, def, equiv) XVECTOR (menu_items)->contents[menu_items_used++] = key; XVECTOR (menu_items)->contents[menu_items_used++] = equiv; XVECTOR (menu_items)->contents[menu_items_used++] = def; + XVECTOR (menu_items)->contents[menu_items_used++] = type; + XVECTOR (menu_items)->contents[menu_items_used++] = selected; + XVECTOR (menu_items)->contents[menu_items_used++] = help; } /* Look through KEYMAPS, a vector of keymaps that is NMAPS long, @@ -355,7 +396,8 @@ keymap_panes (keymaps, nmaps, notreal) 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, notreal, 10); + single_keymap_panes (keymaps[mapno], + Fkeymap_prompt (keymaps[mapno]), Qnil, notreal, 10); finish_menu_items (); } @@ -395,14 +437,14 @@ single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth) notbuttons = menu_items_used; #endif - for (tail = keymap; CONSP (tail); tail = XCONS (tail)->cdr) + for (tail = keymap; CONSP (tail); tail = XCDR (tail)) { GCPRO2 (keymap, pending_maps); /* Look at each key binding, and if it is a menu item add it to this menu. */ - item = XCONS (tail)->car; + item = XCAR (tail); if (CONSP (item)) - single_menu_item (XCONS (item)->car, XCONS (item)->cdr, + single_menu_item (XCAR (item), XCDR (item), &pending_maps, notreal, maxdepth, ¬buttons); else if (VECTORP (item)) { @@ -425,12 +467,12 @@ single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth) { Lisp_Object elt, eltcdr, string; elt = Fcar (pending_maps); - eltcdr = XCONS (elt)->cdr; - string = XCONS (eltcdr)->car; + eltcdr = XCDR (elt); + string = XCAR (eltcdr); /* We no longer discard the @ from the beginning of the string here. Instead, we do this in xmenu_show. */ single_keymap_panes (Fcar (elt), string, - XCONS (eltcdr)->cdr, notreal, maxdepth - 1); + XCDR (eltcdr), notreal, maxdepth - 1); pending_maps = Fcdr (pending_maps); } } @@ -455,7 +497,7 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth, int maxdepth, notreal; int *notbuttons_ptr; { - Lisp_Object def, map, item_string, enabled; + Lisp_Object map, item_string, enabled; struct gcpro gcpro1, gcpro2; int res; @@ -480,7 +522,7 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth, enabled = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE]; item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME]; - if (!NILP (map) && XSTRING (item_string)->data[0] == '@') + if (!NILP (map) && SREF (item_string, 0) == '@') { if (!NILP (enabled)) /* An enabled separate pane. Remember this to handle it later. */ @@ -526,8 +568,8 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth, index++; /* Skip a left, right divider. */ else { - if (!submenu && XSTRING (tem)->data[0] != '\0' - && XSTRING (tem)->data[0] != '-') + if (!submenu && SREF (tem, 0) != '\0' + && SREF (tem, 0) != '-') XVECTOR (menu_items)->contents[index + MENU_ITEMS_ITEM_NAME] = concat2 (build_string (" "), tem); index += MENU_ITEMS_ITEM_LENGTH; @@ -543,8 +585,8 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth, prefix = build_string (NILP (selected) ? "( ) " : "(*) "); } /* Not a button. If we have earlier buttons, then we need a prefix. */ - else if (!*notbuttons_ptr && XSTRING (item_string)->data[0] != '\0' - && XSTRING (item_string)->data[0] != '-') + else if (!*notbuttons_ptr && SREF (item_string, 0) != '\0' + && SREF (item_string, 0) != '-') prefix = build_string (" "); if (!NILP (prefix)) @@ -560,7 +602,10 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth, push_menu_item (item_string, enabled, key, XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF], - XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ]); + XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ], + XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE], + XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED], + XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]); #ifdef USE_X_TOOLKIT /* Display a submenu using the toolkit. */ @@ -590,10 +635,10 @@ list_of_panes (menu) Lisp_Object elt, pane_name, pane_data; elt = Fcar (tail); pane_name = Fcar (elt); - CHECK_STRING (pane_name, 0); + CHECK_STRING (pane_name); push_menu_pane (pane_name, Qnil); pane_data = Fcdr (elt); - CHECK_CONS (pane_data, 0); + CHECK_CONS (pane_data); list_of_items (pane_data); } @@ -612,66 +657,66 @@ list_of_items (pane) { item = Fcar (tail); if (STRINGP (item)) - push_menu_item (item, Qnil, Qnil, Qt, Qnil); + push_menu_item (item, Qnil, Qnil, Qt, Qnil, Qnil, Qnil, Qnil); else if (NILP (item)) push_left_right_boundary (); else { - CHECK_CONS (item, 0); + CHECK_CONS (item); item1 = Fcar (item); - CHECK_STRING (item1, 1); - push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil); + CHECK_STRING (item1); + push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil, Qnil, Qnil, Qnil); } } } DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 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\ -where XOFFSET and YOFFSET are positions in pixels from the top left\n\ -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\ -a definition; actually, the \"definition\" in such a key binding looks like\n\ -\(STRING . REAL-DEFINITION). To give the menu a title, put a string into\n\ -the keymap as a top-level element.\n\n\ -If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.\n\ -Otherwise, REAL-DEFINITION should be a valid key binding definition.\n\ -\n\ -You can also use a list of keymaps as MENU.\n\ - Then each keymap makes a separate pane.\n\ -When MENU is a keymap or a list of keymaps, the return value\n\ -is a list of events.\n\n\ -\n\ -Alternatively, you can specify a menu of multiple panes\n\ - with a list of the form (TITLE PANE1 PANE2...),\n\ -where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\ -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.\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) + doc: /* Pop up a deck-of-cards menu and return user's selection. +POSITION is a position specification. This is either a mouse button event +or a list ((XOFFSET YOFFSET) WINDOW) +where XOFFSET and YOFFSET are positions in pixels from the top left +corner of WINDOW's frame. (WINDOW may be a frame object instead of a window.) +This controls the position of the center of the first line +in the first pane of the menu, not the top left of the menu as a whole. +If POSITION is t, it means to use the current mouse position. + +MENU is a specifier for a menu. For the simplest case, MENU is a keymap. +The menu items come from key bindings that have a menu string as well as +a definition; actually, the "definition" in such a key binding looks like +\(STRING . REAL-DEFINITION). To give the menu a title, put a string into +the keymap as a top-level element. + +If REAL-DEFINITION is nil, that puts a nonselectable string in the menu. +Otherwise, REAL-DEFINITION should be a valid key binding definition. + +You can also use a list of keymaps as MENU. + Then each keymap makes a separate pane. +When MENU is a keymap or a list of keymaps, the return value +is a list of events. + +Alternatively, you can specify a menu of multiple panes + with a list of the form (TITLE PANE1 PANE2...), +where each pane is a list of form (TITLE ITEM1 ITEM2...). +Each ITEM is normally a cons cell (STRING . VALUE); +but a string can appear as an item--that makes a nonselectable line +in the menu. +With this form of menu, the return value is VALUE from the chosen item. + +If POSITION is nil, don't display the menu at all, just precalculate the +cached information about equivalent key sequences. */) + (position, menu) Lisp_Object position, menu; { - int number_of_panes, panes; Lisp_Object keymap, tem; - int xpos, ypos; + int xpos = 0, ypos = 0; Lisp_Object title; char *error_name; Lisp_Object selection; - int i, j; - FRAME_PTR f; + FRAME_PTR f = NULL; Lisp_Object x, y, window; int keymaps = 0; int for_click = 0; + int specpdl_count = SPECPDL_INDEX (); struct gcpro gcpro1; #ifdef HAVE_MENUS @@ -681,12 +726,13 @@ cached information about equivalent key sequences.") /* Decode the first argument: find the window and the coordinates. */ if (EQ (position, Qt) - || (CONSP (position) && EQ (XCONS (position)->car, Qmenu_bar))) + || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar) + || EQ (XCAR (position), Qtool_bar)))) { /* Use the mouse's current position. */ - FRAME_PTR new_f = selected_frame; + FRAME_PTR new_f = SELECTED_FRAME (); Lisp_Object bar_window; - int part; + enum scroll_bar_part part; unsigned long time; if (mouse_position_hook) @@ -721,8 +767,8 @@ cached information about equivalent key sequences.") } } - CHECK_NUMBER (x, 0); - CHECK_NUMBER (y, 0); + CHECK_NUMBER (x); + CHECK_NUMBER (y); /* Decode where to put the menu. */ @@ -734,48 +780,43 @@ cached information about equivalent key sequences.") } else if (WINDOWP (window)) { - CHECK_LIVE_WINDOW (window, 0); + CHECK_LIVE_WINDOW (window); f = XFRAME (WINDOW_FRAME (XWINDOW (window))); - xpos = (FONT_WIDTH (f->output_data.x->font) + xpos = (FONT_WIDTH (FRAME_FONT (f)) * XFASTINT (XWINDOW (window)->left)); - ypos = (f->output_data.x->line_height + ypos = (FRAME_LINE_HEIGHT (f) * XFASTINT (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); + CHECK_WINDOW (window); xpos += XINT (x); ypos += XINT (y); - - XSETFRAME (Vmenu_updating_frame, f); } Vmenu_updating_frame = Qnil; #endif /* HAVE_MENUS */ + record_unwind_protect (unuse_menu_items, Qnil); title = Qnil; GCPRO1 (title); /* Decode the menu items from what was specified. */ - keymap = Fkeymapp (menu); - tem = Qnil; - if (CONSP (menu)) - tem = Fkeymapp (Fcar (menu)); - if (!NILP (keymap)) + keymap = get_keymap (menu, 0, 0); + if (CONSP (keymap)) { /* We were given a keymap. Extract menu info from the keymap. */ Lisp_Object prompt; - keymap = get_keymap (menu); /* Extract the detailed info to make one pane. */ keymap_panes (&menu, 1, NILP (position)); /* Search for a string appearing directly as an element of the keymap. That string is the title of the menu. */ - prompt = map_prompt (keymap); + prompt = Fkeymap_prompt (keymap); if (NILP (title) && !NILP (prompt)) title = prompt; @@ -785,7 +826,7 @@ cached information about equivalent key sequences.") keymaps = 1; } - else if (!NILP (tem)) + else if (CONSP (menu) && KEYMAPP (XCAR (menu))) { /* We were given a list of keymaps. */ int nmaps = XFASTINT (Flength (menu)); @@ -801,9 +842,9 @@ cached information about equivalent key sequences.") { Lisp_Object prompt; - maps[i++] = keymap = get_keymap (Fcar (tem)); + maps[i++] = keymap = get_keymap (Fcar (tem), 1, 0); - prompt = map_prompt (keymap); + prompt = Fkeymap_prompt (keymap); if (NILP (title) && !NILP (prompt)) title = prompt; } @@ -821,13 +862,15 @@ cached information about equivalent key sequences.") { /* We were given an old-fashioned menu. */ title = Fcar (menu); - CHECK_STRING (title, 1); + CHECK_STRING (title); list_of_panes (Fcdr (menu)); keymaps = 0; } + unbind_to (specpdl_count, Qnil); + if (NILP (position)) { discard_menu_items (); @@ -855,37 +898,39 @@ cached information about equivalent key sequences.") #ifdef HAVE_MENUS DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0, - "Pop up a dialog box and return user's selection.\n\ -POSITION specifies which frame to use.\n\ -This is normally a mouse button event or a window or frame.\n\ -If POSITION is t, it means to use the frame the mouse is on.\n\ -The dialog box appears in the middle of the specified frame.\n\ -\n\ -CONTENTS specifies the alternatives to display in the dialog box.\n\ -It is a list of the form (TITLE ITEM1 ITEM2...).\n\ -Each ITEM is a cons cell (STRING . VALUE).\n\ -The return value is VALUE from the chosen item.\n\n\ -An ITEM may also be just a string--that makes a nonselectable item.\n\ -An ITEM may also be nil--that means to put all preceding items\n\ -on the left of the dialog box and all following items on the right.\n\ -\(By default, approximately half appear on each side.)") - (position, contents) + doc: /* Pop up a dialog box and return user's selection. +POSITION specifies which frame to use. +This is normally a mouse button event or a window or frame. +If POSITION is t, it means to use the frame the mouse is on. +The dialog box appears in the middle of the specified frame. + +CONTENTS specifies the alternatives to display in the dialog box. +It is a list of the form (TITLE ITEM1 ITEM2...). +Each ITEM is a cons cell (STRING . VALUE). +The return value is VALUE from the chosen item. + +An ITEM may also be just a string--that makes a nonselectable item. +An ITEM may also be nil--that means to put all preceding items +on the left of the dialog box and all following items on the right. +\(By default, approximately half appear on each side.) */) + (position, contents) Lisp_Object position, contents; { - FRAME_PTR f; + FRAME_PTR f = NULL; Lisp_Object window; check_x (); /* Decode the first argument: find the window or frame to use. */ if (EQ (position, Qt) - || (CONSP (position) && EQ (XCONS (position)->car, Qmenu_bar))) + || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar) + || EQ (XCAR (position), Qtool_bar)))) { #if 0 /* Using the frame the mouse is on may not be right. */ /* Use the mouse's current position. */ - FRAME_PTR new_f = selected_frame; + FRAME_PTR new_f = SELECTED_FRAME (); Lisp_Object bar_window; - int part; + enum scroll_bar_part part; unsigned long time; Lisp_Object x, y; @@ -921,13 +966,13 @@ on the left of the dialog box and all following items on the right.\n\ f = XFRAME (window); else if (WINDOWP (window)) { - CHECK_LIVE_WINDOW (window, 0); + CHECK_LIVE_WINDOW (window); f = XFRAME (WINDOW_FRAME (XWINDOW (window))); } else /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME, but I don't want to make one now. */ - CHECK_WINDOW (window, 0); + CHECK_WINDOW (window); #ifndef USE_X_TOOLKIT /* Display a menu with these alternatives @@ -947,10 +992,12 @@ on the left of the dialog box and all following items on the right.\n\ Lisp_Object title; char *error_name; Lisp_Object selection; + int specpdl_count = SPECPDL_INDEX (); /* Decode the dialog items from what was specified. */ title = Fcar (contents); - CHECK_STRING (title, 1); + CHECK_STRING (title); + record_unwind_protect (unuse_menu_items, Qnil); list_of_panes (Fcons (contents, Qnil)); @@ -959,6 +1006,7 @@ on the left of the dialog box and all following items on the right.\n\ selection = xdialog_show (f, 0, title, &error_name); UNBLOCK_INPUT; + unbind_to (specpdl_count, Qnil); discard_menu_items (); if (error_name) error (error_name); @@ -969,31 +1017,44 @@ on the left of the dialog box and all following items on the right.\n\ #ifdef USE_X_TOOLKIT +/* 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 event_queue *next; + }; + +/* It is ok that this queue is a static variable, + because init_menu_items won't allow the menu mechanism + to be entered recursively. */ +static struct event_queue *popup_get_selection_queue; + +static Lisp_Object popup_get_selection_unwind (); + /* Loop in Xt until the menu pulldown or dialog popup has been popped down (deactivated). This is used for x-popup-menu - and x-popup-dialog; it is not used for the menu bar any more. + and x-popup-dialog; it is not used for the menu bar. + + If DO_TIMERS is nonzero, run timers. NOTE: All calls to popup_get_selection should be protected with BLOCK_INPUT, UNBLOCK_INPUT wrappers. */ -void -popup_get_selection (initial_event, dpyinfo, id) +static void +popup_get_selection (initial_event, dpyinfo, id, do_timers) XEvent *initial_event; struct x_display_info *dpyinfo; LWLIB_ID id; + int do_timers; { XEvent event; - - /* 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 event_queue *next; - }; - - struct event_queue *queue = NULL; struct event_queue *queue_tmp; + int count = SPECPDL_INDEX (); + + popup_get_selection_queue = NULL; + + record_unwind_protect (popup_get_selection_unwind, Qnil); if (initial_event) event = *initial_event; @@ -1052,30 +1113,41 @@ popup_get_selection (initial_event, dpyinfo, id) && (event.xany.display != dpyinfo->display || x_non_menubar_window_to_frame (dpyinfo, event.xany.window))) { - queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue)); - - if (queue_tmp != NULL) - { - queue_tmp->event = event; - queue_tmp->next = queue; - queue = queue_tmp; - } + queue_tmp = (struct event_queue *) xmalloc (sizeof *queue_tmp); + queue_tmp->event = event; + queue_tmp->next = popup_get_selection_queue; + popup_get_selection_queue = queue_tmp; } else XtDispatchEvent (&event); - if (!popup_activated ()) + /* If the event deactivated the menu, we are finished. */ + if (!popup_activated_flag) break; + + /* If we have no events to run, consider timers. */ + if (do_timers && !XtAppPending (Xt_app_con)) + timer_check (1); + XtAppNextEvent (Xt_app_con, &event); } - /* Unread any events that we got but did not handle. */ - while (queue != NULL) + unbind_to (count, Qnil); +} + +/* Unread any events that popup_get_selection read but did not handle. */ + +static Lisp_Object +popup_get_selection_unwind (ignore) + Lisp_Object ignore; +{ + while (popup_get_selection_queue != NULL) { - queue_tmp = queue; + struct event_queue *queue_tmp; + queue_tmp = popup_get_selection_queue; XPutBackEvent (queue_tmp->event.xany.display, &queue_tmp->event); - queue = queue_tmp->next; - free ((char *)queue_tmp); + popup_get_selection_queue = queue_tmp->next; + xfree ((char *)queue_tmp); /* Cause these events to get read as soon as we UNBLOCK_INPUT. */ interrupt_input_pending = 1; } @@ -1083,7 +1155,7 @@ popup_get_selection (initial_event, dpyinfo, id) /* Activate the menu bar of frame F. This is called from keyboard.c when it gets the - menu_bar_activate_event out of the Emacs event queue. + MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue. To activate the menu bar, we use the X button-press event that was saved in saved_menu_event. @@ -1095,6 +1167,7 @@ popup_get_selection (initial_event, dpyinfo, id) passing it to the toolkit right away, is that we can safely execute Lisp code. */ +void x_activate_menubar (f) FRAME_PTR f; { @@ -1103,7 +1176,7 @@ x_activate_menubar (f) set_frame_menubar (f, 0, 1); BLOCK_INPUT; - XtDispatchEvent ((XEvent *) f->output_data.x->saved_menu_event); + XtDispatchEvent (f->output_data.x->saved_menu_event); UNBLOCK_INPUT; #ifdef USE_MOTIF if (f->output_data.x->saved_menu_event->type == ButtonRelease) @@ -1122,7 +1195,6 @@ popup_activated () return popup_activated_flag; } - /* This callback is invoked when the user selects a menubar cascade pushbutton, but before the pulldown menu is posted. */ @@ -1135,6 +1207,63 @@ popup_activate_callback (widget, id, client_data) popup_activated_flag = 1; } +/* This callback is invoked when a dialog or menu is finished being + used and has been unposted. */ + +static void +popup_deactivate_callback (widget, id, client_data) + Widget widget; + LWLIB_ID id; + XtPointer client_data; +{ + popup_activated_flag = 0; +} + +/* Lwlib callback called when menu items are highlighted/unhighlighted + while moving the mouse over them. WIDGET is the menu bar or menu + popup widget. ID is its LWLIB_ID. CALL_DATA contains a pointer to + the widget_value structure for the menu item, or null in case of + unhighlighting. */ + +void +menu_highlight_callback (widget, id, call_data) + Widget widget; + LWLIB_ID id; + void *call_data; +{ + widget_value *wv = (widget_value *) call_data; + struct frame *f; + Lisp_Object frame, help; + + help = wv ? wv->help : Qnil; + + /* Determine the frame for the help event. */ + f = menubar_id_to_frame (id); + if (f) + { + XSETFRAME (frame, f); + kbd_buffer_store_help_event (frame, help); + } + else + { + /* WIDGET is the popup menu. It's parent is the frame's + widget. See which frame that is. */ + Widget frame_widget = XtParent (widget); + Lisp_Object tail; + + for (tail = Vframe_list; GC_CONSP (tail); tail = XCDR (tail)) + { + frame = XCAR (tail); + if (GC_FRAMEP (frame) + && (f = XFRAME (frame), + FRAME_X_P (f) && f->output_data.x->widget == frame_widget)) + break; + } + + show_help_echo (help, Qnil, Qnil, Qnil, 1); + } +} + /* This callback is called from the menu bar pulldown menu when the user makes a selection. Figure out what the user chose @@ -1155,6 +1284,7 @@ menubar_selection_callback (widget, id, client_data) if (!f) return; + entry = Qnil; subprefix_stack = (Lisp_Object *) alloca (f->menu_bar_items_used * sizeof (Lisp_Object)); vector = f->menu_bar_vector; prefix = Qnil; @@ -1189,27 +1319,31 @@ menubar_selection_callback (widget, id, client_data) Lisp_Object frame; XSETFRAME (frame, f); - buf.kind = menu_bar_event; - buf.frame_or_window = Fcons (frame, Fcons (Qmenu_bar, Qnil)); + buf.kind = MENU_BAR_EVENT; + buf.frame_or_window = frame; + buf.arg = frame; kbd_buffer_store_event (&buf); for (j = 0; j < submenu_depth; j++) if (!NILP (subprefix_stack[j])) { - buf.kind = menu_bar_event; - buf.frame_or_window = Fcons (frame, subprefix_stack[j]); + buf.kind = MENU_BAR_EVENT; + buf.frame_or_window = frame; + buf.arg = subprefix_stack[j]; kbd_buffer_store_event (&buf); } if (!NILP (prefix)) { - buf.kind = menu_bar_event; - buf.frame_or_window = Fcons (frame, prefix); + buf.kind = MENU_BAR_EVENT; + buf.frame_or_window = frame; + buf.arg = prefix; kbd_buffer_store_event (&buf); } - buf.kind = menu_bar_event; - buf.frame_or_window = Fcons (frame, entry); + buf.kind = MENU_BAR_EVENT; + buf.frame_or_window = frame; + buf.arg = entry; kbd_buffer_store_event (&buf); return; @@ -1219,18 +1353,6 @@ menubar_selection_callback (widget, id, client_data) } } -/* This callback is invoked when a dialog or menu is finished being - used and has been unposted. */ - -static void -popup_deactivate_callback (widget, id, client_data) - Widget widget; - LWLIB_ID id; - XtPointer client_data; -{ - popup_activated_flag = 0; -} - /* Allocate a widget_value, blocking input. */ widget_value * @@ -1273,23 +1395,18 @@ free_menubar_widget_value_tree (wv) UNBLOCK_INPUT; } -/* Return a tree of widget_value structures for a menu bar item +/* Set up data in menu_items for a menu bar item whose event type is ITEM_KEY (with string ITEM_NAME) and whose contents come from the list of keymaps MAPS. */ -static widget_value * -single_submenu (item_key, item_name, maps) +static int +parse_single_submenu (item_key, item_name, maps) Lisp_Object item_key, item_name, maps; { - widget_value *wv, *prev_wv, *save_wv, *first_wv; - int i; - int submenu_depth = 0; Lisp_Object length; int len; Lisp_Object *mapvec; - widget_value **submenu_stack; - int mapno; - int previous_items = menu_items_used; + int i; int top_level_items = 0; length = Flength (maps); @@ -1303,28 +1420,44 @@ single_submenu (item_key, item_name, maps) maps = Fcdr (maps); } - menu_items_n_panes = 0; - /* Loop over the given keymaps, making a pane for each map. But don't make a pane that is empty--ignore that map instead. */ for (i = 0; i < len; i++) { - if (SYMBOLP (mapvec[i]) - || (CONSP (mapvec[i]) - && NILP (Fkeymapp (mapvec[i])))) + if (!KEYMAPP (mapvec[i])) { /* Here we have a command at top level in the menu bar as opposed to a submenu. */ top_level_items = 1; push_menu_pane (Qnil, Qnil); - push_menu_item (item_name, Qt, item_key, mapvec[i], Qnil); + push_menu_item (item_name, Qt, item_key, mapvec[i], + Qnil, Qnil, Qnil, Qnil); } else - single_keymap_panes (mapvec[i], item_name, item_key, 0, 10); + { + Lisp_Object prompt; + prompt = Fkeymap_prompt (mapvec[i]); + single_keymap_panes (mapvec[i], + !NILP (prompt) ? prompt : item_name, + item_key, 0, 10); + } } - /* Create a tree of widget_value objects - representing the panes and their items. */ + return top_level_items; +} + +/* Create a tree of widget_value objects + representing the panes and items + in menu_items starting at index START, up to index END. */ + +static widget_value * +digest_single_submenu (start, end, top_level_items) + int start, end, top_level_items; +{ + widget_value *wv, *prev_wv, *save_wv, *first_wv; + int i; + int submenu_depth = 0; + widget_value **submenu_stack; submenu_stack = (widget_value **) alloca (menu_items_used * sizeof (widget_value *)); @@ -1332,16 +1465,18 @@ single_submenu (item_key, item_name, maps) wv->name = "menu"; wv->value = 0; wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; first_wv = wv; save_wv = 0; prev_wv = 0; - /* Loop over all panes and items made during this call - and construct a tree of widget_value objects. - Ignore the panes and items made by previous calls to - single_submenu, even though those are also in menu_items. */ - i = previous_items; - while (i < menu_items_used) + /* Loop over all panes and items made by the preceding call + to parse_single_submenu and construct a tree of widget_value objects. + Ignore the panes and items used by previous calls to + digest_single_submenu, even though those are also in menu_items. */ + i = start; + while (i < end) { if (EQ (XVECTOR (menu_items)->contents[i], Qnil)) { @@ -1368,10 +1503,19 @@ single_submenu (item_key, item_name, maps) /* 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]; + +#ifndef HAVE_MULTILINGUAL_MENU + if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name)) + { + pane_name = ENCODE_SYSTEM (pane_name); + AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name; + } +#endif pane_string = (NILP (pane_name) - ? "" : (char *) XSTRING (pane_name)->data); + ? "" : (char *) SDATA (pane_name)); /* If there is just one top-level pane, put all its items directly under the top-level menu. */ if (menu_items_n_panes == 1) @@ -1394,6 +1538,8 @@ single_submenu (item_key, item_name, maps) wv->name++; wv->value = 0; wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; } save_wv = wv; prev_wv = 0; @@ -1402,12 +1548,30 @@ single_submenu (item_key, item_name, maps) else { /* Create a new item within current pane. */ - Lisp_Object item_name, enable, descrip, def; - 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]; - def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION]; + Lisp_Object item_name, enable, descrip, def, type, selected; + Lisp_Object help; + + item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); + enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); + descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY); + def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION); + type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE); + selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED); + help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP); + +#ifndef HAVE_MULTILINGUAL_MENU + if (STRING_MULTIBYTE (item_name)) + { + item_name = ENCODE_SYSTEM (item_name); + AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name; + } + + if (STRINGP (descrip) && STRING_MULTIBYTE (descrip)) + { + descrip = ENCODE_SYSTEM (descrip); + AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip; + } +#endif /* not HAVE_MULTILINGUAL_MENU */ wv = xmalloc_widget_value (); if (prev_wv) @@ -1415,14 +1579,30 @@ single_submenu (item_key, item_name, maps) else save_wv->contents = wv; - wv->name = (char *) XSTRING (item_name)->data; + wv->name = (char *) SDATA (item_name); if (!NILP (descrip)) - wv->key = (char *) XSTRING (descrip)->data; + wv->key = (char *) SDATA (descrip); wv->value = 0; /* The EMACS_INT cast avoids a warning. There's no problem as long as pointers have enough bits to hold small integers. */ wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0); wv->enabled = !NILP (enable); + + if (NILP (type)) + wv->button_type = BUTTON_TYPE_NONE; + else if (EQ (type, QCradio)) + wv->button_type = BUTTON_TYPE_RADIO; + else if (EQ (type, QCtoggle)) + wv->button_type = BUTTON_TYPE_TOGGLE; + else + abort (); + + wv->selected = !NILP (selected); + if (! STRINGP (help)) + help = Qnil; + + wv->help = help; + prev_wv = wv; i += MENU_ITEMS_ITEM_LENGTH; @@ -1441,56 +1621,39 @@ single_submenu (item_key, item_name, maps) return first_wv; } -extern void EmacsFrameSetCharSize (); +/* Recompute all the widgets of frame F, when the menu bar has been + changed. Value is non-zero if widgets were updated. */ -/* Recompute all the widgets of frame F, when the menu bar - has been changed. */ - -static void +static int update_frame_menubar (f) FRAME_PTR f; { struct x_output *x = f->output_data.x; int columns, rows; - int menubar_changed; - Dimension shell_height; - - /* We assume the menubar contents has changed if the global flag is set, - or if the current buffer has changed, or if the menubar has never - been updated before. - */ - menubar_changed = (x->menubar_widget - && !XtIsManaged (x->menubar_widget)); - - if (! (menubar_changed)) - return; + if (!x->menubar_widget || XtIsManaged (x->menubar_widget)) + return 0; BLOCK_INPUT; - /* Save the size of the frame because the pane widget doesn't accept to - resize itself. So force it. */ + /* Save the size of the frame because the pane widget doesn't accept + to resize itself. So force it. */ columns = f->width; rows = f->height; - /* Do the voodoo which means "I'm changing lots of things, don't try to - refigure sizes until I'm done." */ + /* Do the voodoo which means "I'm changing lots of things, don't try + to refigure sizes until I'm done." */ lw_refigure_widget (x->column_widget, False); - /* the order in which children are managed is the top to - bottom order in which they are displayed in the paned window. - First, remove the text-area widget. - */ + /* The order in which children are managed is the top to bottom + order in which they are displayed in the paned window. First, + remove the text-area widget. */ XtUnmanageChild (x->edit_widget); - /* remove the menubar that is there now, and put up the menubar that - should be there. - */ - if (menubar_changed) - { - XtManageChild (x->menubar_widget); - XtMapWidget (x->menubar_widget); - XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, 0); - } + /* Remove the menubar that is there now, and put up the menubar that + should be there. */ + XtManageChild (x->menubar_widget); + XtMapWidget (x->menubar_widget); + XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, NULL); /* Re-manage the text-area widget, and then thrash the sizes. */ XtManageChild (x->edit_widget); @@ -1498,8 +1661,8 @@ update_frame_menubar (f) /* Force the pane widget to resize itself with the right values. */ EmacsFrameSetCharSize (x->edit_widget, columns, rows); - UNBLOCK_INPUT; + return 1; } /* Set the contents of the menubar widgets of frame F. @@ -1513,9 +1676,12 @@ set_frame_menubar (f, first_time, deep_p) int deep_p; { Widget menubar_widget = f->output_data.x->menubar_widget; - Lisp_Object tail, items, frame; + Lisp_Object items; widget_value *wv, *first_wv, *prev_wv = 0; - int i; + int i, last_i; + int *submenu_start, *submenu_end; + int *submenu_top_level_items, *submenu_n_panes; + LWLIB_ID id; XSETFRAME (Vmenu_updating_frame, f); @@ -1536,19 +1702,13 @@ set_frame_menubar (f, first_time, deep_p) f->output_data.x->saved_menu_event->type = 0; } - wv = xmalloc_widget_value (); - wv->name = "menubar"; - wv->value = 0; - wv->enabled = 1; - first_wv = wv; - if (deep_p) { /* Make a widget-value tree representing the entire menu trees. */ struct buffer *prev = current_buffer; Lisp_Object buffer; - int specpdl_count = specpdl_ptr - specpdl; + int specpdl_count = SPECPDL_INDEX (); int previous_menu_items_used = f->menu_bar_items_used; Lisp_Object *previous_items = (Lisp_Object *) alloca (previous_menu_items_used @@ -1566,6 +1726,7 @@ set_frame_menubar (f, first_time, deep_p) specbind (Qdebug_on_next_call, Qnil); record_unwind_protect (Fset_match_data, Fmatch_data (Qnil, Qnil)); + record_unwind_protect (unuse_menu_items, Qnil); if (NILP (Voverriding_local_map_menu_flag)) { specbind (Qoverriding_terminal_local_map, Qnil); @@ -1575,7 +1736,8 @@ set_frame_menubar (f, first_time, deep_p) set_buffer_internal_1 (XBUFFER (buffer)); /* Run the Lucid hook. */ - call1 (Vrun_hooks, Qactivate_menubar_hook); + safe_run_hooks (Qactivate_menubar_hook); + /* If it has changed current-menubar from previous value, really recompute the menubar from the value. */ if (! NILP (Vlucid_menu_bar_dirty_flag)) @@ -1585,38 +1747,71 @@ set_frame_menubar (f, first_time, deep_p) items = FRAME_MENU_BAR_ITEMS (f); - inhibit_garbage_collection (); - /* Save the frame's previous menu bar contents data. */ - bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items, - previous_menu_items_used * sizeof (Lisp_Object)); + if (previous_menu_items_used) + bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items, + previous_menu_items_used * sizeof (Lisp_Object)); - /* Fill in the current menu bar contents. */ + /* Fill in menu_items with the current menu bar contents. + This can evaluate Lisp code. */ menu_items = f->menu_bar_vector; - menu_items_allocated = XVECTOR (menu_items)->size; + menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0; + submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *)); + submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *)); + submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int)); + submenu_top_level_items + = (int *) alloca (XVECTOR (items)->size * sizeof (int *)); init_menu_items (); for (i = 0; i < XVECTOR (items)->size; i += 4) { Lisp_Object key, string, maps; + last_i = i; + key = XVECTOR (items)->contents[i]; string = XVECTOR (items)->contents[i + 1]; maps = XVECTOR (items)->contents[i + 2]; if (NILP (string)) break; - wv = single_submenu (key, string, maps); + submenu_start[i] = menu_items_used; + + menu_items_n_panes = 0; + submenu_top_level_items[i] + = parse_single_submenu (key, string, maps); + submenu_n_panes[i] = menu_items_n_panes; + + submenu_end[i] = menu_items_used; + } + + finish_menu_items (); + + /* Convert menu_items into widget_value trees + to display the menu. This cannot evaluate Lisp code. */ + + wv = xmalloc_widget_value (); + wv->name = "menubar"; + wv->value = 0; + wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; + first_wv = wv; + + for (i = 0; i < last_i; i += 4) + { + menu_items_n_panes = submenu_n_panes[i]; + wv = digest_single_submenu (submenu_start[i], submenu_end[i], + submenu_top_level_items[i]); if (prev_wv) prev_wv->next = wv; else first_wv->contents = wv; /* Don't set wv->name here; GC during the loop might relocate it. */ wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; prev_wv = wv; } - finish_menu_items (); - set_buffer_internal_1 (prev); unbind_to (specpdl_count, Qnil); @@ -1630,7 +1825,7 @@ set_frame_menubar (f, first_time, deep_p) if (i == menu_items_used && i == previous_menu_items_used && i != 0) { free_menubar_widget_value_tree (first_wv); - menu_items = Qnil; + discard_menu_items (); return; } @@ -1644,19 +1839,27 @@ set_frame_menubar (f, first_time, deep_p) string = XVECTOR (items)->contents[i + 1]; if (NILP (string)) break; - wv->name = (char *) XSTRING (string)->data; + wv->name = (char *) SDATA (string); wv = wv->next; } f->menu_bar_vector = menu_items; f->menu_bar_items_used = menu_items_used; - menu_items = Qnil; + discard_menu_items (); } else { /* Make a widget-value tree containing just the top level menu bar strings. */ + wv = xmalloc_widget_value (); + wv->name = "menubar"; + wv->value = 0; + wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; + first_wv = wv; + items = FRAME_MENU_BAR_ITEMS (f); for (i = 0; i < XVECTOR (items)->size; i += 4) { @@ -1667,9 +1870,11 @@ set_frame_menubar (f, first_time, deep_p) break; wv = xmalloc_widget_value (); - wv->name = (char *) XSTRING (string)->data; + wv->name = (char *) SDATA (string); wv->value = 0; wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; /* This prevents lwlib from assuming this menu item is really supposed to be empty. */ /* The EMACS_INT cast avoids a warning. @@ -1712,7 +1917,8 @@ set_frame_menubar (f, first_time, deep_p) 0, popup_activate_callback, menubar_selection_callback, - popup_deactivate_callback); + popup_deactivate_callback, + menu_highlight_callback); f->output_data.x->menubar_widget = menubar_widget; } @@ -1740,7 +1946,6 @@ set_frame_menubar (f, first_time, deep_p) } free_menubar_widget_value_tree (first_wv); - update_frame_menubar (f); UNBLOCK_INPUT; @@ -1761,6 +1966,7 @@ initialize_frame_menubar (f) set_frame_menubar (f, 1, 1); } + /* Get rid of the menu bar of frame F, and free its storage. This is used when deleting a frame, and when turning off the menu bar. */ @@ -1769,7 +1975,6 @@ free_frame_menubar (f) FRAME_PTR f; { Widget menubar_widget; - int id; menubar_widget = f->output_data.x->menubar_widget; @@ -1777,8 +1982,37 @@ free_frame_menubar (f) if (menubar_widget) { +#ifdef USE_MOTIF + /* Removing the menu bar magically changes the shell widget's x + and y position of (0, 0) which, when the menu bar is turned + on again, leads to pull-down menuss appearing in strange + positions near the upper-left corner of the display. This + happens only with some window managers like twm and ctwm, + but not with other like Motif's mwm or kwm, because the + latter generate ConfigureNotify events when the menu bar + is switched off, which fixes the shell position. */ + Position x0, y0, x1, y1; +#endif + BLOCK_INPUT; + +#ifdef USE_MOTIF + if (f->output_data.x->widget) + XtVaGetValues (f->output_data.x->widget, XtNx, &x0, XtNy, &y0, NULL); +#endif + lw_destroy_all_widgets ((LWLIB_ID) f->output_data.x->id); + f->output_data.x->menubar_widget = NULL; + +#ifdef USE_MOTIF + if (f->output_data.x->widget) + { + XtVaGetValues (f->output_data.x->widget, XtNx, &x1, XtNy, &y1, NULL); + if (x1 == 0 && y1 == 0) + XtVaSetValues (f->output_data.x->widget, XtNx, x0, XtNy, y0, NULL); + } +#endif + UNBLOCK_INPUT; } } @@ -1813,11 +2047,7 @@ free_frame_menubar (f) next_menubar_widget_id. */ LWLIB_ID widget_id_tick; -#ifdef __STDC__ static Lisp_Object *volatile menu_item_selection; -#else -static Lisp_Object *menu_item_selection; -#endif static void popup_selection_callback (widget, id, client_data) @@ -1852,7 +2082,6 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) XButtonPressedEvent dummy; int first_pane; - int next_release_must_exit = 0; *error = NULL; @@ -1868,6 +2097,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) wv->name = "menu"; wv->value = 0; wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help =Qnil; first_wv = wv; first_pane = 1; @@ -1902,10 +2133,19 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) /* 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_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME); + prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX); + +#ifndef HAVE_MULTILINGUAL_MENU + if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name)) + { + pane_name = ENCODE_SYSTEM (pane_name); + AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name; + } +#endif pane_string = (NILP (pane_name) - ? "" : (char *) XSTRING (pane_name)->data); + ? "" : (char *) SDATA (pane_name)); /* If there is just one top-level pane, put all its items directly under the top-level menu. */ if (menu_items_n_panes == 1) @@ -1926,6 +2166,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) wv->name++; wv->value = 0; wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; save_wv = wv; prev_wv = 0; } @@ -1940,21 +2182,37 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) else { /* Create a new item within current pane. */ - Lisp_Object item_name, enable, descrip, def; - 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]; - def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION]; - + Lisp_Object item_name, enable, descrip, def, type, selected, help; + item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); + enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); + descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY); + def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION); + type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE); + selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED); + help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP); + +#ifndef HAVE_MULTILINGUAL_MENU + if (STRINGP (item_name) && STRING_MULTIBYTE (item_name)) + { + item_name = ENCODE_SYSTEM (item_name); + AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name; + } + + if (STRINGP (descrip) && STRING_MULTIBYTE (descrip)) + { + descrip = ENCODE_SYSTEM (descrip); + AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip; + } +#endif /* not HAVE_MULTILINGUAL_MENU */ + wv = xmalloc_widget_value (); if (prev_wv) prev_wv->next = wv; else save_wv->contents = wv; - wv->name = (char *) XSTRING (item_name)->data; + wv->name = (char *) SDATA (item_name); if (!NILP (descrip)) - wv->key = (char *) XSTRING (descrip)->data; + wv->key = (char *) SDATA (descrip); wv->value = 0; /* If this item has a null value, make the call_data null so that it won't display a box @@ -1962,6 +2220,23 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) wv->call_data = (!NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0); wv->enabled = !NILP (enable); + + if (NILP (type)) + wv->button_type = BUTTON_TYPE_NONE; + else if (EQ (type, QCtoggle)) + wv->button_type = BUTTON_TYPE_TOGGLE; + else if (EQ (type, QCradio)) + wv->button_type = BUTTON_TYPE_RADIO; + else + abort (); + + wv->selected = !NILP (selected); + + if (! STRINGP (help)) + help = Qnil; + + wv->help = help; + prev_wv = wv; i += MENU_ITEMS_ITEM_LENGTH; @@ -1977,13 +2252,22 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) wv_sep2->name = "--"; wv_sep2->next = first_wv->contents; + wv_sep2->help = Qnil; wv_sep1->name = "--"; wv_sep1->next = wv_sep2; + wv_sep1->help = Qnil; - wv_title->name = (char *) XSTRING (title)->data; - wv_title->enabled = True; +#ifndef HAVE_MULTILINGUAL_MENU + if (STRING_MULTIBYTE (title)) + title = ENCODE_SYSTEM (title); +#endif + + wv_title->name = (char *) SDATA (title); + wv_title->enabled = TRUE; + wv_title->button_type = BUTTON_TYPE_NONE; wv_title->next = wv_sep1; + wv_title->help = Qnil; first_wv->contents = wv_title; } @@ -1992,7 +2276,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv, f->output_data.x->widget, 1, 0, popup_selection_callback, - popup_deactivate_callback); + popup_deactivate_callback, + menu_highlight_callback); /* Adjust coordinates to relative to the outer (window manager) window. */ { @@ -2054,11 +2339,11 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) menu_item_selection = 0; /* Display the menu. */ - lw_popup_menu (menu, &dummy); + lw_popup_menu (menu, (XEvent *) &dummy); popup_activated_flag = 1; /* Process events that apply to the menu. */ - popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id); + popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 0); /* fp turned off the following statement and wrote a comment that it is unnecessary--that the menu has already disappeared. @@ -2072,7 +2357,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) { Lisp_Object prefix, entry; - prefix = Qnil; + prefix = entry = Qnil; i = 0; while (i < menu_items_used) { @@ -2140,6 +2425,22 @@ dialog_selection_callback (widget, id, client_data) popup_activated_flag = 0; } +/* ARG is the LWLIB ID of the dialog box, represented + as a Lisp object as (HIGHPART . LOWPART). */ + +Lisp_Object +xdialog_show_unwind (arg) + Lisp_Object arg; +{ + LWLIB_ID id = (XINT (XCAR (arg)) << 4 * sizeof (LWLIB_ID) + | XINT (XCDR (arg))); + BLOCK_INPUT; + lw_destroy_all_widgets (id); + UNBLOCK_INPUT; + popup_activated_flag = 0; + return Qnil; +} + static char * button_names [] = { "button1", "button2", "button3", "button4", "button5", "button6", "button7", "button8", "button9", "button10" }; @@ -2153,10 +2454,9 @@ xdialog_show (f, keymaps, title, error) { int i, nb_buttons=0; LWLIB_ID dialog_id; - Widget menu; char dialog_name[6]; - widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0; + widget_value *wv, *first_wv = 0, *prev_wv = 0; /* Number of elements seen so far, before boundary. */ int left_count = 0; @@ -2179,13 +2479,14 @@ xdialog_show (f, keymaps, title, error) pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME]; prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX]; pane_string = (NILP (pane_name) - ? "" : (char *) XSTRING (pane_name)->data); + ? "" : (char *) SDATA (pane_name)); prev_wv = xmalloc_widget_value (); prev_wv->value = pane_string; if (keymaps && !NILP (prefix)) prev_wv->name++; prev_wv->enabled = 1; prev_wv->name = "message"; + prev_wv->help = Qnil; first_wv = prev_wv; /* Loop over all panes and items, filling in the tree. */ @@ -2225,10 +2526,11 @@ xdialog_show (f, keymaps, title, error) prev_wv->next = wv; wv->name = (char *) button_names[nb_buttons]; if (!NILP (descrip)) - wv->key = (char *) XSTRING (descrip)->data; - wv->value = (char *) XSTRING (item_name)->data; + wv->key = (char *) SDATA (descrip); + wv->value = (char *) SDATA (item_name); wv->call_data = (void *) &XVECTOR (menu_items)->contents[i]; wv->enabled = !NILP (enable); + wv->help = Qnil; prev_wv = wv; if (! boundary_seen) @@ -2245,7 +2547,7 @@ xdialog_show (f, keymaps, title, error) wv = xmalloc_widget_value (); wv->name = dialog_name; - + wv->help = Qnil; /* Dialog boxes use a really stupid name encoding which specifies how many buttons to use and how many buttons are on the right. @@ -2263,9 +2565,9 @@ xdialog_show (f, keymaps, title, error) /* Actually create the dialog. */ dialog_id = widget_id_tick++; - menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv, - f->output_data.x->widget, 1, 0, - dialog_selection_callback, 0); + lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv, + f->output_data.x->widget, 1, 0, + dialog_selection_callback, 0, 0); lw_modify_all_widgets (dialog_id, first_wv->contents, True); /* Free the widget_value objects we used to specify the contents. */ free_menubar_widget_value_tree (first_wv); @@ -2273,17 +2575,26 @@ xdialog_show (f, keymaps, title, error) /* No selection has been chosen yet. */ menu_item_selection = 0; - /* Display the menu. */ + /* Display the dialog box. */ lw_pop_up_all_widgets (dialog_id); popup_activated_flag = 1; - /* Process events that apply to the menu. */ - popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id); + /* Process events that apply to the dialog box. + Also handle timers. */ + { + int count = SPECPDL_INDEX (); - lw_destroy_all_widgets (dialog_id); + /* xdialog_show_unwind is responsible for popping the dialog box down. */ + record_unwind_protect (xdialog_show_unwind, + Fcons (make_number (dialog_id >> (4 * sizeof (LWLIB_ID))), + make_number (dialog_id & ~(-1 << (4 * sizeof (LWLIB_ID)))))); - /* Find the selected item, and its pane, to return - the proper value. */ + popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id, 1); + + unbind_to (count, Qnil); + } + + /* Find the selected item and pane, and return the corresponding value. */ if (menu_item_selection != 0) { Lisp_Object prefix; @@ -2300,6 +2611,12 @@ xdialog_show (f, keymaps, title, error) = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; i += MENU_ITEMS_PANE_LENGTH; } + else if (EQ (XVECTOR (menu_items)->contents[i], Qquote)) + { + /* This is the boundary between left-side elts and + right-side elts. */ + ++i; + } else { entry @@ -2321,8 +2638,52 @@ xdialog_show (f, keymaps, title, error) return Qnil; } + #else /* not USE_X_TOOLKIT */ +/* The frame of the last activated non-toolkit menu bar. + Used to generate menu help events. */ + +static struct frame *menu_help_frame; + + +/* Show help HELP_STRING, or clear help if HELP_STRING is null. + + PANE is the pane number, and ITEM is the menu item number in + the menu (currently not used). + + This cannot be done with generating a HELP_EVENT because + XMenuActivate contains a loop that doesn't let Emacs process + keyboard events. */ + +static void +menu_help_callback (help_string, pane, item) + char *help_string; + int pane, item; +{ + extern Lisp_Object Qmenu_item; + Lisp_Object *first_item; + Lisp_Object pane_name; + Lisp_Object menu_object; + + first_item = XVECTOR (menu_items)->contents; + if (EQ (first_item[0], Qt)) + pane_name = first_item[MENU_ITEMS_PANE_NAME]; + else if (EQ (first_item[0], Qquote)) + /* This shouldn't happen, see xmenu_show. */ + pane_name = empty_string; + else + pane_name = first_item[MENU_ITEMS_ITEM_NAME]; + + /* (menu-item MENU-NAME PANE-NUMBER) */ + menu_object = Fcons (Qmenu_item, + Fcons (pane_name, + Fcons (make_number (pane), Qnil))); + show_help_echo (help_string ? build_string (help_string) : Qnil, + Qnil, menu_object, make_number (item), 1); +} + + static Lisp_Object xmenu_show (f, x, y, for_click, keymaps, title, error) FRAME_PTR f; @@ -2413,7 +2774,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) 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); + ? "" : (char *) SDATA (pane_name)); if (keymaps && !NILP (prefix)) pane_string++; @@ -2440,7 +2801,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) j++; continue; } - width = STRING_BYTES (XSTRING (item)); + width = SBYTES (item); if (width > maxwidth) maxwidth = width; @@ -2454,43 +2815,47 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) else { /* Create a new item within current pane. */ - Lisp_Object item_name, enable, descrip; + Lisp_Object item_name, enable, descrip, help; unsigned char *item_data; + char *help_string; 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]; + help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP]; + help_string = STRINGP (help) ? SDATA (help) : NULL; + if (!NILP (descrip)) { - int gap = maxwidth - STRING_BYTES (XSTRING (item_name)); + int gap = maxwidth - SBYTES (item_name); #ifdef C_ALLOCA Lisp_Object spacer; spacer = Fmake_string (make_number (gap), make_number (' ')); item_name = concat2 (item_name, spacer); item_name = concat2 (item_name, descrip); - item_data = XSTRING (item_name)->data; + item_data = SDATA (item_name); #else /* if alloca is fast, use that to make the space, to reduce gc needs. */ item_data = (unsigned char *) alloca (maxwidth - + STRING_BYTES (XSTRING (descrip)) + 1); - bcopy (XSTRING (item_name)->data, item_data, - STRING_BYTES (XSTRING (item_name))); - for (j = XSTRING (item_name)->size; j < maxwidth; j++) + + SBYTES (descrip) + 1); + bcopy (SDATA (item_name), item_data, + SBYTES (item_name)); + for (j = SCHARS (item_name); j < maxwidth; j++) item_data[j] = ' '; - bcopy (XSTRING (descrip)->data, item_data + j, - STRING_BYTES (XSTRING (descrip))); - item_data[j + STRING_BYTES (XSTRING (descrip))] = 0; + bcopy (SDATA (descrip), item_data + j, + SBYTES (descrip)); + item_data[j + SBYTES (descrip)] = 0; #endif } else - item_data = XSTRING (item_name)->data; + item_data = SDATA (item_name); if (XMenuAddSelection (FRAME_X_DISPLAY (f), menu, lpane, 0, item_data, - !NILP (enable)) + !NILP (enable), help_string) == XM_FAILURE) { XMenuDestroy (FRAME_X_DISPLAY (f), menu); @@ -2503,10 +2868,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) /* All set and ready to fly. */ XMenuRecompute (FRAME_X_DISPLAY (f), menu); - dispwidth = DisplayWidth (FRAME_X_DISPLAY (f), - XScreenNumberOfScreen (FRAME_X_SCREEN (f))); - dispheight = DisplayHeight (FRAME_X_DISPLAY (f), - XScreenNumberOfScreen (FRAME_X_SCREEN (f))); + dispwidth = DisplayWidth (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f)); + dispheight = DisplayHeight (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f)); x = min (x, dispwidth); y = min (y, dispheight); x = max (x, 1); @@ -2529,9 +2892,13 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) XMenuSetAEQ (menu, TRUE); XMenuSetFreeze (menu, TRUE); pane = selidx = 0; - + + /* Help display under X won't work because XMenuActivate contains + a loop that doesn't give Emacs a chance to process it. */ + menu_help_frame = f; status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx, - x, y, ButtonReleaseMask, &datap); + x, y, ButtonReleaseMask, &datap, + menu_help_callback); #ifdef HAVE_X_WINDOWS @@ -2606,17 +2973,19 @@ xmenu_show (f, x, y, for_click, keymaps, title, error) #endif /* HAVE_MENUS */ +void syms_of_xmenu () { staticpro (&menu_items); menu_items = Qnil; + menu_items_inuse = Qnil; Qdebug_on_next_call = intern ("debug-on-next-call"); staticpro (&Qdebug_on_next_call); DEFVAR_LISP ("menu-updating-frame", &Vmenu_updating_frame, - "Frame for which we are updating a menu.\n\ -The enable predicate for a menu command should check this variable."); + doc: /* Frame for which we are updating a menu. +The enable predicate for a menu command should check this variable. */); Vmenu_updating_frame = Qnil; #ifdef USE_X_TOOLKIT