Mercurial > emacs
changeset 21240:bff697e03fe0
Include puresize.h for CHECK_IMPURE.
(parse_menu_item): New function.
(menu_item_eval_property_1, menu_item_eval_property) New suroutines.
(menu_bar_one_keymap): Moved some code to menu_bar_item.
(menu_bar_item): Rewritten to use parse_menu_item.
(menu_bar_item_1): Function deleted.
(QCenable, QCvisible, QChelp, QCfilter, QCbutton, QCtoggle, QCradio):
(Qmenu_alias): New variables.
(syms_of_keyboard): Initialize them, and item_properties.
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Sat, 21 Mar 1998 05:49:49 +0000 |
parents | 81154898ac20 |
children | 31bd04a792c2 |
files | src/keyboard.c |
diffstat | 1 files changed, 372 insertions(+), 73 deletions(-) [+] |
line wrap: on
line diff
--- a/src/keyboard.c Sat Mar 21 05:48:03 1998 +0000 +++ b/src/keyboard.c Sat Mar 21 05:49:49 1998 +0000 @@ -39,6 +39,7 @@ #include "syntax.h" #include "intervals.h" #include "blockinput.h" +#include "puresize.h" #include <setjmp.h> #include <errno.h> @@ -456,7 +457,12 @@ Lisp_Object Qevent_kind; Lisp_Object Qevent_symbol_elements; +/* menu item parts */ +Lisp_Object Qmenu_alias; Lisp_Object Qmenu_enable; +Lisp_Object QCenable, QCvisible, QChelp, QCfilter, QCbutton, QCtoggle, QCradio; +extern Lisp_Object Vdefine_key_rebound_commands; +extern Lisp_Object Qmenu_item; /* An event header symbol HEAD may have a property named Qevent_symbol_element_mask, which is of the form (BASE MODIFIERS); @@ -477,8 +483,6 @@ Lisp_Object Qvertical_scroll_bar; Lisp_Object Qmenu_bar; -extern Lisp_Object Qmenu_enable; - Lisp_Object recursive_edit_unwind (), command_loop (); Lisp_Object Fthis_command_keys (); Lisp_Object Qextended_command_history; @@ -3505,7 +3509,7 @@ /* * VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys. - * Used only as parameters to GetAsyncKeyState() and GetKeyState(). + * Used only as parameters to GetAsyncKeyState and GetKeyState. * No other API or message will distinguish left and right keys this way. */ /* 0xA0 .. 0xEF */ @@ -4114,12 +4118,12 @@ portion_whole = Fcons (event->x, event->y); part = *scroll_bar_parts[(int) event->part]; - position = - Fcons (window, - Fcons (Qvertical_scroll_bar, - Fcons (portion_whole, - Fcons (make_number (event->timestamp), - Fcons (part, Qnil))))); + position + = Fcons (window, + Fcons (Qvertical_scroll_bar, + Fcons (portion_whole, + Fcons (make_number (event->timestamp), + Fcons (part, Qnil))))); } /* Always treat W32 scroll bar events as clicks. */ @@ -5084,7 +5088,7 @@ #endif /* POSIX infers that processes which are not in the session leader's process group won't get SIGHUP's at logout time. BSDI adheres to - this part standard and returns -1 from read(0) with errno==EIO + this part standard and returns -1 from read (0) with errno==EIO when the control tty is taken away. Jeffrey Honig <jch@bsdi.com> says this is generally safe. */ if (nread == -1 && errno == EIO) @@ -5398,25 +5402,14 @@ menu_bar_one_keymap (keymap) Lisp_Object keymap; { - Lisp_Object tail, item, key, binding, item_string, table; + Lisp_Object tail, item, table; /* Loop over all keymap entries that have menu strings. */ for (tail = keymap; CONSP (tail); tail = XCONS (tail)->cdr) { item = XCONS (tail)->car; if (CONSP (item)) - { - key = XCONS (item)->car; - binding = XCONS (item)->cdr; - if (CONSP (binding)) - { - item_string = XCONS (binding)->car; - if (STRINGP (item_string)) - menu_bar_item (key, item_string, Fcdr (binding)); - } - else if (EQ (binding, Qundefined)) - menu_bar_item (key, Qnil, binding); - } + menu_bar_item (XCONS (item)->car, XCONS (item)->cdr); else if (VECTORP (item)) { /* Loop over the char values represented in the vector. */ @@ -5426,45 +5419,25 @@ { Lisp_Object character; XSETFASTINT (character, c); - binding = XVECTOR (item)->contents[c]; - if (CONSP (binding)) - { - item_string = XCONS (binding)->car; - if (STRINGP (item_string)) - menu_bar_item (key, item_string, Fcdr (binding)); - } - else if (EQ (binding, Qundefined)) - menu_bar_item (key, Qnil, binding); + menu_bar_item (character, XVECTOR (item)->contents[c]); } } } } -/* This is used as the handler when calling internal_condition_case_1. */ - -static Lisp_Object -menu_bar_item_1 (arg) - Lisp_Object arg; -{ - return Qnil; -} - /* Add one item to menu_bar_items_vector, for KEY, ITEM_STRING and DEF. If there's already an item for KEY, add this DEF to it. */ +Lisp_Object item_properties; + static void -menu_bar_item (key, item_string, def) - Lisp_Object key, item_string, def; -{ - Lisp_Object tem; - Lisp_Object enabled; +menu_bar_item (key, item) + Lisp_Object key, item; +{ + struct gcpro gcpro1; int i; - /* Skip menu-bar equiv keys data. */ - if (CONSP (def) && CONSP (XCONS (def)->car)) - def = XCONS (def)->cdr; - - if (EQ (def, Qundefined)) + if (EQ (item, Qundefined)) { /* If a map has an explicit `undefined' as definition, discard any previously made menu bar item. */ @@ -5485,25 +5458,14 @@ return; } - /* See if this entry is enabled. */ - enabled = Qt; - - if (SYMBOLP (def)) - { - /* 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_bar_item_1); - } - - /* Ignore this item if it's not enabled. */ - if (NILP (enabled)) + GCPRO1 (key); /* Is this necessary? */ + i = parse_menu_item (item, 0, 1); + UNGCPRO; + if (!i) return; + item = XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF]; + /* Find any existing item for this KEY. */ for (i = 0; i < menu_bar_items_index; i += 4) if (EQ (key, XVECTOR (menu_bar_items_vector)->contents[i])) @@ -5522,20 +5484,340 @@ XVECTOR (tem)->contents, i * sizeof (Lisp_Object)); menu_bar_items_vector = tem; } + /* Add this item. */ XVECTOR (menu_bar_items_vector)->contents[i++] = key; - XVECTOR (menu_bar_items_vector)->contents[i++] = item_string; - XVECTOR (menu_bar_items_vector)->contents[i++] = Fcons (def, Qnil); + XVECTOR (menu_bar_items_vector)->contents[i++] + = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME]; + XVECTOR (menu_bar_items_vector)->contents[i++] = Fcons (item, Qnil); XVECTOR (menu_bar_items_vector)->contents[i++] = make_number (0); menu_bar_items_index = i; } - /* We did find an item for this KEY. Add DEF to its list of maps. */ + /* We did find an item for this KEY. Add ITEM to its list of maps. */ else { Lisp_Object old; old = XVECTOR (menu_bar_items_vector)->contents[i + 2]; - XVECTOR (menu_bar_items_vector)->contents[i + 2] = Fcons (def, old); - } + XVECTOR (menu_bar_items_vector)->contents[i + 2] = Fcons (item, old); + } +} + + /* This is used as the handler when calling menu_item_eval_property. */ +static Lisp_Object +menu_item_eval_property_1 (arg) + Lisp_Object arg; +{ + /* If we got a quit from within the menu computation, + quit all the way out of it. This takes care of C-] in the debugger. */ + if (CONSP (arg) && EQ (XCONS (arg)->car, Qquit)) + Fsignal (Qquit, Qnil); + + return Qnil; +} + +/* Evaluate an expression and return the result (or nil if something + went wrong). Used to evaluate dynamic parts of menu items. */ +static Lisp_Object +menu_item_eval_property (sexpr) + Lisp_Object sexpr; +{ + Lisp_Object val; + val = internal_condition_case_1 (Feval, sexpr, Qerror, + menu_item_eval_property_1); + return val; +} + +/* This function parses a menu item and leaves the result in the + vector item_properties. + ITEM is a key binding, a possible menu item. + If NOTREAL is nonzero, only check for equivalent key bindings, don't + evaluate dynamic expressions in the menu item. + INMENUBAR is true when this is considered for an entry in a menu bar + top level. + parse_menu_item returns true if the item is a menu item and false + otherwise. */ + +int +parse_menu_item (item, notreal, inmenubar) + Lisp_Object item; + int notreal, inmenubar; +{ + Lisp_Object def, tem; + + Lisp_Object type = Qnil; + Lisp_Object cachelist = Qnil; + Lisp_Object filter = Qnil; + Lisp_Object item_string, start; + int i; + struct gcpro gcpro1, gcpro2, gcpro3; + +#define RET0 \ + if (1) \ + { \ + UNGCPRO; \ + return 0; \ + } \ + else + + if (!CONSP (item)) + return 0; + + GCPRO3 (item, notreal, inmenubar); + + /* Create item_properties vector if necessary. */ + if (NILP (item_properties)) + item_properties + = Fmake_vector (make_number (ITEM_PROPERTY_ENABLE + 1), Qnil); + + /* Initialize optional entries. */ + for (i = ITEM_PROPERTY_DEF; i < ITEM_PROPERTY_ENABLE; i++) + XVECTOR (item_properties)->contents[i] = Qnil; + XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE] = Qt; + + /* Save the item here to protect it from GC. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_ITEM] = item; + + item_string = XCONS (item)->car; + + start = item; + item = XCONS (item)->cdr; + if (STRINGP (item_string)) + { + /* Old format menu item. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME] = item_string; + + /* Maybe help string. */ + if (CONSP (item) && STRINGP (XCONS (item)->car)) + { + XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP] + = XCONS (item)->car; + start = item; + item = XCONS (item)->cdr; + } + + /* Maybee key binding cache. */ + if (CONSP (item) && CONSP (XCONS (item)->car) + && (NILP (XCONS (XCONS (item)->car)->car) + || VECTORP (XCONS (XCONS (item)->car)->car))) + { + cachelist = XCONS (item)->car; + item = XCONS (item)->cdr; + } + + /* This is the real definition--the function to run. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF] = item; + + /* Get enable property, if any. */ + if (SYMBOLP (item)) + { + tem = Fget (item, Qmenu_enable); + if (!NILP (tem)) + XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE] = tem; + } + } + else if (EQ (item_string, Qmenu_item) && CONSP (item)) + { + /* New format menu item. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME] + = XCONS (item)->car; + start = XCONS (item)->cdr; + if (CONSP (start)) + { + /* We have a real binding. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF] + = XCONS (start)->car; + + item = XCONS (start)->cdr; + /* Is there a cache list with key equivalences. */ + if (CONSP (item) && CONSP (XCONS (item)->car)) + { + cachelist = XCONS (item)->car; + item = XCONS (item)->cdr; + } + + /* Parse properties. */ + while (CONSP (item) && CONSP (XCONS (item)->cdr)) + { + tem = XCONS (item)->car; + item = XCONS (item)->cdr; + + if (EQ (tem, QCenable)) + XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE] + = XCONS (item)->car; + else if (EQ (tem, QCvisible) && !notreal) + { + /* If got a visible property and that evaluates to nil + then ignore this item. */ + tem = menu_item_eval_property (XCONS (item)->car); + if (NILP (tem)) + RET0; + } + else if (EQ (tem, QChelp)) + XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP] + = XCONS (item)->car; + else if (EQ (tem, QCfilter)) + filter = XCONS (item)->car; + else if (EQ (tem, QCbutton) && CONSP (XCONS (item)->car)) + { + tem = XCONS (item)->car; + type = XCONS (tem)->car; + if (EQ (type, QCtoggle) || EQ (type, QCradio)) + { + XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED] + = XCONS (tem)->cdr; + XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE] + = type; + } + } + item = XCONS (item)->cdr; + } + } + else if (inmenubar || !NILP (start)) + RET0; + } + else + RET0; + + /* If item string is not a string, evaluate it to get string. + If we don't get a string, skip this item. */ + item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME]; + if (!(STRINGP (item_string) || notreal)) + { + item_string = menu_item_eval_property (item_string); + if (!STRINGP (item_string)) + RET0; + XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME] = item_string; + } + + /* If got a filter apply it on definition. */ + def = XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF]; + if (!NILP (filter)) + { + def = menu_item_eval_property (Fcons (filter, Fcons (def, Qnil))); + XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF] = def; + } + + /* If we got no definition, this item is just unselectable text which + is ok when in a submenu and if there is an item string. */ + item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME]; + if (NILP (def)) + { + UNGCPRO; + return (!inmenubar && STRINGP (item_string) ? 1 : 0); + } + + /* Enable or disable selection of item. */ + tem = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE]; + if (!EQ (tem, Qt)) + { + if (notreal) + tem = Qt; + else + tem = menu_item_eval_property (tem); + if (inmenubar && NILP (tem)) + RET0; /* Ignore disabled items in menu bar. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE] = tem; + } + + /* See if this is a separate pane or a submenu. */ + tem = get_keymap_1 (def, 0, 1); + if (!NILP (tem)) + { + XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP] = tem; + XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF] = tem; + UNGCPRO; + return 1; + } + else if (inmenubar) + RET0; /* Entries in menu bar must be submenus. */ + + /* This is a command. See if there is an equivalent key binding. */ + if (NILP (cachelist)) + { + /* We have to create a cachelist. */ + CHECK_IMPURE (start); + XCONS (start)->cdr = Fcons (Fcons (Qnil, Qnil), XCONS (start)->cdr); + cachelist = XCONS (XCONS (start)->cdr)->car; + /* We have not checked this before so check it now. */ + tem = def; + } + else if (VECTORP (XCONS (cachelist)->car)) /* Saved key */ + { + tem = Fkey_binding (XCONS (cachelist)->car, Qnil); + if (EQ (tem, def) + /* If the command is an alias for another + (such as easymenu.el and lmenu.el set it up), + check if the original command matches the cached command. */ + || (SYMBOLP (def) && EQ (tem, XSYMBOL (def)->function))) + tem = Qnil; /* Don't need to recompute key binding. */ + else + tem = def; + } + /* If something had no key binding before, don't recheck it + because that is too slow--except if we have a list of rebound + commands in Vdefine_key_rebound_commands, do recheck any command + that appears in that list. */ + else if (!NILP (XCONS (cachelist)->car)) + tem = def; /* Should signal an error here. */ + else if ( + /* Should we check everything when precomputing key bindings? */ + /* notreal || */ + CONSP (Vdefine_key_rebound_commands) + && !NILP (Fmemq (def, Vdefine_key_rebound_commands))) + tem = def; + else + tem = Qnil; + + if (!NILP (tem)) + { + /* Recompute equivalent key binding. + If the command is an alias for another + (such as easymenu.el and lmenu.el set it up), + see if the original command name has equivalent keys. + Otherwise look up the specified command itself. + We don't try both, because that makes easymenu menus slow. */ + if (SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function) + && ! NILP (Fget (def, Qmenu_alias))) + tem = XSYMBOL (def)->function; + tem = Fwhere_is_internal (tem, Qnil, Qt, Qnil); + XCONS (cachelist)->car = tem; + XCONS (cachelist)->cdr + = (NILP (tem) ? Qnil + : + concat2 (build_string (" ("), + concat2 (Fkey_description (tem), build_string (")")))); + } + + /* If we only want to precompute equivalent key bindings, stop here. */ + if (notreal) + { + UNGCPRO; + return 1; + } + + /* If we have an equivalent key binding, use that. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ] + = XCONS (cachelist)->cdr; + + /* Include this when menu help is implemented. + tem = XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]; + if (!(NILP (tem) || STRINGP (tem))) + { + tem = menu_item_eval_property (tem); + if (!STRINGP (tem)) + tem = Qnil; + XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP] = tem; + } + */ + + /* Handle radio buttons or toggle boxes. */ + tem = XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED]; + if (!NILP (tem)) + XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED] + = menu_item_eval_property (tem); + + UNGCPRO; + return 1; } /* Read a character using menus based on maps in the array MAPS. @@ -8124,6 +8406,9 @@ syms_of_keyboard () { + staticpro (&item_properties); + item_properties = Qnil; + Qtimer_event_handler = intern ("timer-event-handler"); staticpro (&Qtimer_event_handler); @@ -8171,6 +8456,20 @@ Qmenu_enable = intern ("menu-enable"); staticpro (&Qmenu_enable); + Qmenu_alias = intern ("menu-alias"); + staticpro (&Qmenu_alias); + QCenable = intern (":enable"); + staticpro (&QCenable); + QCvisible = intern (":visible"); + staticpro (&QCvisible); + QCfilter = intern (":filter"); + staticpro (&QCfilter); + QCbutton = intern (":button"); + staticpro (&QCbutton); + QCtoggle = intern (":toggle"); + staticpro (&QCtoggle); + QCradio = intern (":radio"); + staticpro (&QCradio); Qmode_line = intern ("mode-line"); staticpro (&Qmode_line);