+
+
+\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;
+
+ /* Store the binding. */
+ PROP (TOOLBAR_ITEM_BINDING) = XCAR (item);
+ item = XCDR (item);
+
+ /* Process the rest of the properties. */
+ for (; CONSP (item) && CONSP (XCDR (item)); item = XCDR (XCDR (item)))
+ {
+ Lisp_Object key, value;
+
+ key = XCAR (item);
+ value = XCAR (XCDR (item));
+
+ if (EQ (key, QCenable))
+ /* `:enable FORM'. */
+ PROP (TOOLBAR_ITEM_ENABLED_P) = value;
+ else if (EQ (key, QCvisible))
+ {
+ /* `:visible FORM'. If got a visible property and that
+ evaluates to nil then ignore this item. */
+ if (NILP (menu_item_eval_property (value)))
+ return 0;
+ }
+ else if (EQ (key, QChelp))
+ /* `:help HELP-STRING'. */
+ PROP (TOOLBAR_ITEM_HELP) = value;
+ else if (EQ (key, QCfilter))
+ /* ':filter FORM'. */
+ filter = value;
+ else if (EQ (key, QCbutton) && CONSP (value))
+ {
+ /* `:button (TYPE . SELECTED)'. */
+ Lisp_Object type, selected;
+
+ type = XCAR (value);
+ selected = XCDR (value);
+ if (EQ (type, QCtoggle) || EQ (type, QCradio))
+ {
+ PROP (TOOLBAR_ITEM_SELECTED_P) = selected;
+ PROP (TOOLBAR_ITEM_TYPE) = type;
+ }
+ }
+ else if (EQ (key, QCimage)
+ && (CONSP (value)
+ || (VECTORP (value) && XVECTOR (value)->size == 4)))
+ /* Value is either a single image specification or a vector
+ of 4 such specifications for the different buttion states. */
+ PROP (TOOLBAR_ITEM_IMAGES) = value;
+ }
+
+ /* If got a filter apply it on binding. */
+ if (!NILP (filter))
+ PROP (TOOLBAR_ITEM_BINDING)
+ = menu_item_eval_property (list2 (filter,
+ list2 (Qquote,
+ PROP (TOOLBAR_ITEM_BINDING))));
+
+ /* See if the binding is a keymap. Give up if it is. */
+ if (!NILP (get_keymap_1 (PROP (TOOLBAR_ITEM_BINDING), 0, 1)))
+ return 0;
+
+ /* Enable or disable selection of item. */
+ if (!EQ (PROP (TOOLBAR_ITEM_ENABLED_P), Qt))
+ PROP (TOOLBAR_ITEM_ENABLED_P)
+ = menu_item_eval_property (PROP (TOOLBAR_ITEM_ENABLED_P));
+
+ /* Handle radio buttons or toggle boxes. */
+ if (!NILP (PROP (TOOLBAR_ITEM_SELECTED_P)))
+ PROP (TOOLBAR_ITEM_SELECTED_P)
+ = menu_item_eval_property (PROP (TOOLBAR_ITEM_SELECTED_P));
+
+ return 1;
+
+#undef PROP
+}
+
+
+/* Initialize Vtoolbar_items. REUSE, if non-nil, is a vector that can
+ be reused. */
+
+static void
+init_toolbar_items (reuse)
+ Lisp_Object reuse;
+{
+ if (VECTORP (reuse))
+ toolbar_items_vector = reuse;
+ else
+ toolbar_items_vector = Fmake_vector (make_number (64), Qnil);
+ ntoolbar_items = 0;
+}
+
+
+/* Append parsed toolbar item properties from toolbar_item_properties */
+
+static void
+append_toolbar_item ()
+{
+ Lisp_Object *to, *from;
+
+ /* Enlarge toolbar_items_vector if necessary. */
+ if (ntoolbar_items + TOOLBAR_ITEM_NSLOTS
+ >= XVECTOR (toolbar_items_vector)->size)
+ {
+ Lisp_Object new_vector;
+ int old_size = XVECTOR (toolbar_items_vector)->size;
+
+ new_vector = Fmake_vector (make_number (2 * old_size), Qnil);
+ bcopy (XVECTOR (toolbar_items_vector)->contents,
+ XVECTOR (new_vector)->contents,
+ old_size * sizeof (Lisp_Object));
+ toolbar_items_vector = new_vector;
+ }
+
+ /* Append entries from toolbar_item_properties to the end of
+ toolbar_items_vector. */
+ to = XVECTOR (toolbar_items_vector)->contents + ntoolbar_items;
+ from = XVECTOR (toolbar_item_properties)->contents;
+ bcopy (from, to, TOOLBAR_ITEM_NSLOTS * sizeof *to);
+ ntoolbar_items += TOOLBAR_ITEM_NSLOTS;
+}
+
+
+
+