+ Lisp_Object tail, item, table;
+
+ menu_bar_one_keymap_changed_items = Qnil;
+
+ /* 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))
+ menu_bar_item (XCONS (item)->car, XCONS (item)->cdr);
+ else if (VECTORP (item))
+ {
+ /* 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;
+ XSETFASTINT (character, c);
+ menu_bar_item (character, XVECTOR (item)->contents[c]);
+ }
+ }
+ }
+}
+
+/* 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)
+ Lisp_Object key, item;
+{
+ struct gcpro gcpro1;
+ int i;
+ Lisp_Object tem;
+
+ if (EQ (item, Qundefined))
+ {
+ /* If a map has an explicit `undefined' as definition,
+ discard any previously made menu bar item. */
+
+ for (i = 0; i < menu_bar_items_index; i += 4)
+ if (EQ (key, XVECTOR (menu_bar_items_vector)->contents[i]))
+ {
+ if (menu_bar_items_index > i + 4)
+ bcopy (&XVECTOR (menu_bar_items_vector)->contents[i + 4],
+ &XVECTOR (menu_bar_items_vector)->contents[i],
+ (menu_bar_items_index - i - 4) * sizeof (Lisp_Object));
+ menu_bar_items_index -= 4;
+ return;
+ }
+
+ /* If there's no definition for this key yet,
+ just ignore `undefined'. */
+ return;
+ }
+
+ GCPRO1 (key); /* Is this necessary? */
+ i = parse_menu_item (item, 0, 1);
+ UNGCPRO;
+ if (!i)
+ return;
+
+ /* If this keymap has already contributed to this KEY,
+ don't contribute to it a second time. */
+ tem = Fmemq (key, menu_bar_one_keymap_changed_items);
+ if (!NILP (tem))
+ return;
+
+ menu_bar_one_keymap_changed_items
+ = Fcons (key, menu_bar_one_keymap_changed_items);
+
+ 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]))
+ break;
+
+ /* If we did not find this KEY, add it at the end. */
+ if (i == menu_bar_items_index)
+ {
+ /* If vector is too small, get a bigger one. */
+ if (i + 4 > XVECTOR (menu_bar_items_vector)->size)
+ {
+ Lisp_Object tem;
+ int newsize = 2 * i;
+ tem = Fmake_vector (make_number (2 * i), Qnil);
+ bcopy (XVECTOR (menu_bar_items_vector)->contents,
+ 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++]
+ = 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 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 (item, old);
+ }
+}
+\f
+ /* 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. */
+Lisp_Object
+menu_item_eval_property (sexpr)
+ Lisp_Object sexpr;
+{
+ int count = specpdl_ptr - specpdl;
+ Lisp_Object val;
+ specbind (Qinhibit_redisplay, Qt);
+ val = internal_condition_case_1 (Feval, sexpr, Qerror,
+ menu_item_eval_property_1);
+ return unbind_to (count, 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 > 0 when this is considered for an entry in a menu bar
+ top level.
+ INMENUBAR is < 0 when this is considered for an entry in a keyboard menu.
+ 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, item_string, start;
+ Lisp_Object cachelist;
+ Lisp_Object filter;
+ Lisp_Object keyhint;
+ int i;
+ int newcache = 0;
+
+ cachelist = Qnil;
+ filter = Qnil;
+ keyhint = Qnil;
+
+ if (!CONSP (item))
+ return 0;
+
+ /* 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))
+ return 0;
+ }
+ else if (EQ (tem, QChelp))
+ XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]
+ = XCONS (item)->car;
+ else if (EQ (tem, QCfilter))
+ filter = item;
+ else if (EQ (tem, QCkey_sequence))
+ {
+ tem = XCONS (item)->car;
+ if (NILP (cachelist)
+ && (SYMBOLP (tem) || STRINGP (tem) || VECTORP (tem)))
+ /* Be GC protected. Set keyhint to item instead of tem. */
+ keyhint = item;
+ }
+ else if (EQ (tem, QCkeys))
+ {
+ tem = XCONS (item)->car;
+ if (CONSP (tem) || STRINGP (tem) && NILP (cachelist))
+ XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ]
+ = tem;
+ }
+ else if (EQ (tem, QCbutton) && CONSP (XCONS (item)->car))
+ {
+ Lisp_Object type;
+ 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))
+ return 0;
+ }
+ else
+ return 0; /* not a menu item */
+
+ /* 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))
+ return 0;
+ 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 (list2 (XCONS (filter)->car,
+ list2 (Qquote, def)));
+
+ XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF] = def;
+ }
+
+ /* If we got no definition, this item is just unselectable text which
+ is OK in a submenu but not in the menubar. */
+ if (NILP (def))
+ return (inmenubar ? 0 : 1);
+
+ /* 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))
+ return 0; /* Ignore disabled items in menu bar. */
+ XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE] = tem;
+ }
+
+ /* See if this is a separate pane or a submenu. */
+ def = XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF];
+ tem = get_keymap_1 (def, 0, 1);
+ /* For a subkeymap, just record its details and exit. */
+ if (!NILP (tem))
+ {
+ XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP] = tem;
+ XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF] = tem;
+ return 1;
+ }
+ /* At the top level in the menu bar, do likewise for commands also.
+ The menu bar does not display equivalent key bindings anyway.
+ ITEM_PROPERTY_DEF is already set up properly. */
+ if (inmenubar > 0)
+ return 1;
+
+ /* 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;
+ newcache = 1;
+ tem = XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ];
+ if (!NILP (keyhint))
+ {
+ XCONS (cachelist)->car = XCONS (keyhint)->car;
+ newcache = 0;
+ }
+ else if (STRINGP (tem))
+ {
+ XCONS (cachelist)->cdr = Fsubstitute_command_keys (tem);
+ XCONS (cachelist)->car = Qt;
+ }
+ }
+ tem = XCONS (cachelist)->car;
+ if (!EQ (tem, Qt))
+ {
+ int chkcache = 0;
+ Lisp_Object prefix;
+
+ if (!NILP (tem))
+ tem = Fkey_binding (tem, Qnil);
+
+ prefix = XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ];
+ if (CONSP (prefix))
+ {
+ def = XCONS (prefix)->car;
+ prefix = XCONS (prefix)->cdr;
+ }
+ else
+ def = XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF];
+
+ if (NILP (XCONS (cachelist)->car)) /* Have no saved key. */
+ {
+ if (newcache /* Always check first time. */
+ /* Should we check everything when precomputing key
+ bindings? */
+ /* || notreal */
+ /* 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. */
+ || (CONSP (Vdefine_key_rebound_commands)
+ && !NILP (Fmemq (def, Vdefine_key_rebound_commands))))
+ chkcache = 1;
+ }
+ /* We had a saved key. Is it still bound to the command? */
+ else if (NILP (tem)
+ || !EQ (tem, def)
+ /* If the command is an alias for another
+ (such as lmenu.el set it up), check if the
+ original command matches the cached command. */
+ && !(SYMBOLP (def) && EQ (tem, XSYMBOL (def)->function)))
+ chkcache = 1; /* Need to recompute key binding. */
+
+ if (chkcache)
+ {
+ /* Recompute equivalent key binding. If the command is an alias
+ for another (such as 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 lmenu menus slow. */
+ if (SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function)
+ && ! NILP (Fget (def, Qmenu_alias)))
+ def = XSYMBOL (def)->function;
+ tem = Fwhere_is_internal (def, Qnil, Qt, Qnil);
+ XCONS (cachelist)->car = tem;
+ if (NILP (tem))
+ {
+ XCONS (cachelist)->cdr = Qnil;
+ chkcache = 0;
+ }
+ }
+ else if (!NILP (keyhint) && !NILP (XCONS (cachelist)->car))
+ {
+ tem = XCONS (cachelist)->car;
+ chkcache = 1;
+ }
+
+ newcache = chkcache;
+ if (chkcache)
+ {
+ tem = Fkey_description (tem);
+ if (CONSP (prefix))
+ {
+ if (STRINGP (XCONS (prefix)->car))
+ tem = concat2 (XCONS (prefix)->car, tem);
+ if (STRINGP (XCONS (prefix)->cdr))
+ tem = concat2 (tem, XCONS (prefix)->cdr);
+ }
+ XCONS (cachelist)->cdr = tem;
+ }
+ }
+
+ tem = XCONS (cachelist)->cdr;
+ if (newcache && !NILP (tem))
+ {
+ tem = concat3 (build_string (" ("), tem, build_string (")"));
+ XCONS (cachelist)->cdr = tem;
+ }
+
+ /* If we only want to precompute equivalent key bindings, stop here. */
+ if (notreal)
+ return 1;
+
+ /* If we have an equivalent key binding, use that. */
+ XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ] = tem;
+
+ /* 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);
+
+ return 1;
+}
+
+
+\f
+/***********************************************************************
+ Tool-bars
+ ***********************************************************************/
+
+/* A vector holding toolbar items while they are parsed in function
+ toolbar_items runs Each item occupies TOOLBAR_ITEM_NSCLOTS
+ elements in the vector. */
+
+static Lisp_Object toolbar_items_vector;
+
+/* A vector holding the result of parse_toolbar_item. Layout is like
+ the one for a single item in toolbar_items_vector. */
+
+static Lisp_Object toolbar_item_properties;
+
+/* Next free index in toolbar_items_vector. */
+
+static int ntoolbar_items;
+
+/* The symbols `toolbar', `toolbar-item', and `:image'. */
+
+extern Lisp_Object Qtoolbar;
+Lisp_Object QCimage;
+
+/* Function prototypes. */
+
+static void init_toolbar_items P_ ((Lisp_Object));
+static void process_toolbar_item P_ ((Lisp_Object, Lisp_Object));
+static int parse_toolbar_item P_ ((Lisp_Object, Lisp_Object));
+static void append_toolbar_item P_ ((void));
+
+
+/* Return a vector of toolbar items for keymaps currently in effect.
+ Reuse vector REUSE if non-nil. Return in *NITEMS the number of
+ toolbar items found. */
+
+Lisp_Object
+toolbar_items (reuse, nitems)
+ Lisp_Object reuse;
+ int *nitems;
+{
+ Lisp_Object *maps;
+ int nmaps, i;
+ Lisp_Object oquit;
+ Lisp_Object *tmaps;
+ extern Lisp_Object Voverriding_local_map_menu_flag;
+ extern Lisp_Object Voverriding_local_map;
+
+ *nitems = 0;
+
+ /* In order to build the menus, we need to call the keymap
+ accessors. They all call QUIT. But this function is called
+ during redisplay, during which a quit is fatal. So inhibit
+ quitting while building the menus. We do this instead of
+ specbind because (1) errors will clear it anyway and (2) this
+ avoids risk of specpdl overflow. */
+ oquit = Vinhibit_quit;
+ Vinhibit_quit = Qt;
+
+ /* Initialize toolbar_items_vector and protect it from GC. */
+ init_toolbar_items (reuse);
+
+ /* Build list of keymaps in maps. Set nmaps to the number of maps
+ to process. */
+
+ /* Should overriding-terminal-local-map and overriding-local-map apply? */
+ if (!NILP (Voverriding_local_map_menu_flag))
+ {
+ /* Yes, use them (if non-nil) as well as the global map. */
+ maps = (Lisp_Object *) alloca (3 * sizeof (maps[0]));
+ nmaps = 0;
+ if (!NILP (current_kboard->Voverriding_terminal_local_map))
+ maps[nmaps++] = current_kboard->Voverriding_terminal_local_map;
+ if (!NILP (Voverriding_local_map))
+ maps[nmaps++] = Voverriding_local_map;
+ }
+ else
+ {
+ /* No, so use major and minor mode keymaps. */
+ nmaps = current_minor_maps (NULL, &tmaps);
+ maps = (Lisp_Object *) alloca ((nmaps + 2) * sizeof (maps[0]));
+ bcopy (tmaps, maps, nmaps * sizeof (maps[0]));
+#ifdef USE_TEXT_PROPERTIES
+ maps[nmaps++] = get_local_map (PT, current_buffer);
+#else
+ maps[nmaps++] = current_buffer->keymap;
+#endif
+ }
+
+ /* Add global keymap at the end. */
+ maps[nmaps++] = current_global_map;
+
+ /* Process maps in reverse order and look up in each map the prefix
+ key `toolbar'. */
+ for (i = nmaps - 1; i >= 0; --i)
+ if (!NILP (maps[i]))
+ {
+ Lisp_Object keymap;
+
+ keymap = get_keyelt (access_keymap (maps[i], Qtoolbar, 1, 1), 0);
+ if (!NILP (Fkeymapp (keymap)))
+ {
+ Lisp_Object tail;
+
+ /* KEYMAP is a list `(keymap (KEY . BINDING) ...)'. */
+ for (tail = keymap; CONSP (tail); tail = XCONS (tail)->cdr)
+ {
+ Lisp_Object keydef = XCAR (tail);
+ if (CONSP (keydef))
+ process_toolbar_item (XCAR (keydef), XCDR (keydef));
+ }
+ }
+ }
+
+ Vinhibit_quit = oquit;
+ *nitems = ntoolbar_items / TOOLBAR_ITEM_NSLOTS;
+ return toolbar_items_vector;
+}
+
+
+/* Process the definition of KEY which is DEF. */
+
+static void
+process_toolbar_item (key, def)
+ Lisp_Object key, def;
+{
+ int i;
+ extern Lisp_Object Qundefined;
+ struct gcpro gcpro1, gcpro2;
+
+ /* Protect KEY and DEF from GC because parse_toolbar_item may call
+ eval. */
+ GCPRO2 (key, def);
+
+ if (EQ (def, Qundefined))
+ {
+ /* If a map has an explicit `undefined' as definition,
+ discard any previously made item. */
+ for (i = 0; i < ntoolbar_items; i += TOOLBAR_ITEM_NSLOTS)
+ {
+ Lisp_Object *v = XVECTOR (toolbar_items_vector)->contents + i;
+
+ if (EQ (key, v[TOOLBAR_ITEM_KEY]))
+ {
+ if (ntoolbar_items > i + TOOLBAR_ITEM_NSLOTS)
+ bcopy (v + TOOLBAR_ITEM_NSLOTS, v,
+ ((ntoolbar_items - i - TOOLBAR_ITEM_NSLOTS)
+ * sizeof (Lisp_Object)));
+ ntoolbar_items -= TOOLBAR_ITEM_NSLOTS;
+ break;
+ }
+ }
+ }
+ else if (parse_toolbar_item (key, def))
+ /* Append a new toolbar item to toolbar_items_vector. Accept
+ more than one definition for the same key. */
+ append_toolbar_item ();
+
+ UNGCPRO;
+}
+
+
+/* Parse a toolbar item specification ITEM for key KEY and return the
+ result in toolbar_item_properties. Value is zero if ITEM is
+ invalid.
+
+ ITEM is a list `(menu-item CAPTION BINDING PROPS...)'.
+
+ CAPTION is the caption of the item, If it's not a string, it is
+ evaluated to get a string.
+
+ BINDING is the toolbar item's binding. Toolbar items with keymaps
+ as binding are currently ignored.
+
+ The following properties are recognized:
+
+ - `:enable FORM'.
+
+ FORM is evaluated and specifies whether the toolbar item is enabled
+ or disabled.
+
+ - `:visible FORM'
+
+ FORM is evaluated and specifies whether the toolbar item is visible.
+
+ - `:filter FUNCTION'
+
+ FUNCTION is invoked with one parameter `(quote BINDING)'. Its
+ result is stored as the new binding.
+
+ - `:button (TYPE SELECTED)'
+
+ TYPE must be one of `:radio' or `:toggle'. SELECTED is evaluated
+ and specifies whether the button is selected (pressed) or not.
+
+ - `:image IMAGES'
+
+ IMAGES is either a single image specification or a vector of four
+ image specifications. See enum toolbar_item_images.
+
+ - `:help HELP-STRING'.
+
+ Gives a help string to display for the toolbar item. */
+
+static int
+parse_toolbar_item (key, item)
+ Lisp_Object key, item;
+{
+ /* Access slot with index IDX of vector toolbar_item_properties. */
+#define PROP(IDX) XVECTOR (toolbar_item_properties)->contents[IDX]
+
+ Lisp_Object filter = Qnil;
+ Lisp_Object caption;
+ extern Lisp_Object QCenable, QCvisible, QChelp, QCfilter;
+ extern Lisp_Object QCbutton, QCtoggle, QCradio;
+ int i;
+ struct gcpro gcpro1;
+
+ /* Defininition looks like `(toolbar-item CAPTION BINDING
+ PROPS...)'. Rule out items that aren't lists, don't start with
+ `toolbar-item' or whose rest following `toolbar-item' is not a
+ list. */
+ if (!CONSP (item)
+ || !EQ (XCAR (item), Qmenu_item)
+ || (item = XCDR (item),
+ !CONSP (item)))
+ return 0;
+
+ /* Create toolbar_item_properties vector if necessary. Reset it to
+ defaults. */
+ if (VECTORP (toolbar_item_properties))
+ {
+ for (i = 0; i < TOOLBAR_ITEM_NSLOTS; ++i)
+ PROP (i) = Qnil;
+ }
+ else
+ toolbar_item_properties
+ = Fmake_vector (make_number (TOOLBAR_ITEM_NSLOTS), Qnil);
+
+ /* Set defaults. */
+ PROP (TOOLBAR_ITEM_KEY) = key;
+ PROP (TOOLBAR_ITEM_ENABLED_P) = Qt;
+
+ /* Get the caption of the item. If the caption is not a string,
+ evaluate it to get a string. If we don't get a string, skip this
+ item. */
+ caption = XCAR (item);
+ if (!STRINGP (caption))
+ {
+ caption = menu_item_eval_property (caption);
+ if (!STRINGP (caption))
+ return 0;
+ }
+ PROP (TOOLBAR_ITEM_CAPTION) = caption;
+
+ /* Give up if rest following the caption is not a list. */
+ item = XCDR (item);
+ if (!CONSP (item))
+ return 0;