]> code.delx.au - gnu-emacs-elpa/blobdiff - packages/auctex/toolbar-x.el
Remove version numbers in packages/ directory
[gnu-emacs-elpa] / packages / auctex / toolbar-x.el
diff --git a/packages/auctex/toolbar-x.el b/packages/auctex/toolbar-x.el
new file mode 100644 (file)
index 0000000..ef4edc8
--- /dev/null
@@ -0,0 +1,2154 @@
+;;; toolbar-x.el --- fancy toolbar handling in Emacs and XEmacs
+
+;; Copyright (C) 2004, 2005, 2008 Free Software Foundation, Inc.
+
+;; This program is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 3 of
+;; the License, or (at your option) any later version.
+
+;; This program is distributed in the hope that it will be
+;; useful, but WITHOUT ANY WARRANTY; without even the implied
+;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+;; PURPOSE.  See the GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public
+;; License along with this program; if not, write to the Free
+;; Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+;; MA 02110-1301 USA
+
+;;; Author: Miguel Vinicius Santini Frasson
+
+;;; Commentary:
+;; This program implements a common interface to display toolbar
+;; buttons in both Emacs and XEmacs.  A toolbar should be basicly
+;; defined by a image and a command to run when the button is pressed,
+;; and additional properties could be added.  This is the idea of this
+;; program.  See the documentation of function
+;; `toolbarx-install-toolbar' for a description of how to specify
+;; toolbars.
+
+;;; Features:
+
+;; * Button properties are given in the toolbar definition (BUTTON
+;; paramenter in `toolbarx-install-toolbar') and/or in an alist with
+;; associates the symbol with properties (MEANING-ALIST paramenter in
+;; `toolbarx-install-toolbar').
+
+;; * Supported properties:
+;; - All editors: `:insert', `:image', `:command', `:help', `:enable',
+;;               `:append-command' and `:prepend-command';
+;; - Emacs only: `:visible' and `:button';
+;; - XEmacs only: `:toolbar'.
+;; For the precise value-type for each property, see documentation of
+;; the function `toolbarx-install-toolbar'.
+;; (ps: properties that are particular to an editor are just ignored
+;; the other editor flavour.)
+
+;; * Button properties may depend on the editor flavour, if the value
+;; is a vector; the first element will be used for Emacs and the 2nd
+;; for XEmacs. Example: `:image ["new" toolbar-file-icon]'
+
+;; * Properties can have value specified by function (with no
+;; argument) or variables that evaluate to an object of the correct
+;; type for a particular property.  The evaluation is done when the
+;; roolbar is refresh (a call of `toolbarx-refresh'.)
+;; (ps: this is valid only for properties that *not* have \`form\' as
+;; value type.)
+
+;; * On `refresh time' (a call `toolbarx-refresh', necessary when the
+;; toolbar should change), the `:insert' property (if present) is
+;; evaluated to decide if button will be displayed.
+
+;; Properties can be distributed to several buttons, using \`groups\'.
+;; Example: (for (bar baz :toolbar (bottom . top) :insert foo-form)
+;; means that `foo', `bar' and `baz' have `:insert foo-form' and `bar' and
+;; `baz' have the property `:toolbar (bottom . top)'.  (ps: this type
+;; of value for the `:toolbar' property (XEmacs only) means that the
+;; buttons will be in the bottom toolbar unless the default toolbar is
+;; in the bottom, and in this case, this buttons go to the top
+;; toolbar).
+
+;; * (Part of) the toolbar definition can be stored in a variable,
+;; evaluated in `installation time'.  See `:eval-group' on the
+;; documentation of the function `toolbarx-install-toolbar'.
+
+;; * It is possible to define sets of buttons that appear according to
+;; an option selected in a dropdown menu.  See `:dropdown-group' on
+;; the documentation of the function `toolbarx-install-toolbar'.
+
+;;; Rough description of the implementation
+;; There are 3 \`engines\' implemented:
+
+;; == the 1st one (parsing) parses the toolbar definition
+;; independently of editor flavour and store the parsed buttons with
+;; their properties, in the same order that they appear in the
+;; definitions, in a variable `toolbarx-internal-button-switches';
+
+;; == the 2nd one (refresh for Emacs) inserts buttons in the Emacs
+;; toolbar in the same order that they appear in the definitions;
+;; buttons with a `:insert' property value that evaluates to nil are
+;; ignored; if a (real) button does not have at least (valid) image
+;; and command properties, they are silently ignored;
+
+;; == the 3rd engine (refresh for XEmacs) is similar to the 2nd, but
+;; inserts buttons in XEmacs.
+
+;;; History:
+
+;; This program was motivated by the intention of implementation of a
+;; good toolbar for AUCTeX, that would work in both Emacs and XEmacs.
+;; Since toolbars are very different in behaviour and implementation
+;; (for instance, in Emacs one can display as many toolbar buttons as
+;; wanted, because it becomes mult-line, and in XEmacs, there is one
+;; line, but toolbars and all sides of a frame.)
+
+\f
+;;; Code:
+
+;; Note that this just gives a useful default.  Icons are expected to
+;; be in subdirectory "images" or "toolbar" relative to the load-path.
+;; Packages loading toolbarx are advised to explicitly add their own
+;; searchpath with add-to-list here even when they fulfill that
+;; criterion: another package might have loaded toolbar-x previously
+;; when load-path was not yet correctly set.  The default setting
+;; really caters only for toolbar-x' stock icons.
+
+(defvar toolbarx-image-path
+  (nconc
+   (delq nil (mapcar #'(lambda(x)
+                        (and x
+                             (member
+                              (file-name-nondirectory
+                               (directory-file-name x))
+                              '("toolbar" "images"))
+                             ;;(file-directory-p x)
+                             x))
+                    load-path))
+   (list data-directory))
+  "List of directories where toolbarx finds its images.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; First engine: Parsing buttons
+
+;; it obtains button information, process it and stores result in
+;; `toolbarx-internal-button-switches', which is a list with 1st
+;; element the symbol `:switches', the 2nd element as a list of
+;; processed buttons, and the 3rd element is used for Emacs to store
+;; the keys used in ``constant'' buttons.
+
+;; The 2nd element of `toolbarx-internal-button-switches' is a list
+;; where each element is either:
+;;  * a button-list, that is, a list with elements to define a button.
+;;  * a list where 1st elem is `:insert' and 2nd is a form, and the
+;; following elements are in the same format of the 2nd element of
+;; `toolbarx-internal-button-switches'.
+
+(defun toolbarx-make-string-from-symbol (symbol)
+  "Return a string from the name of a SYMBOL.
+Upcase initials and replace dashes by spaces."
+  (let* ((str (upcase-initials (symbol-name symbol)))
+        (str2))
+    (dolist (i (append str nil))
+      (if (eq i 45)                    ; if dash, push space
+         (push 32 str2)
+       (push i str2)))                 ; else push identical
+    (concat (nreverse str2))))
+
+(defun toolbarx-make-symbol-from-string (string)
+  "Return a (intern) symbol from STRING.
+Downcase string and replace spaces by dashes."
+  (let* ((str1 (append (downcase string) nil))
+        (str2))
+    (dolist (i str1)
+      (if (eq i 32)                    ; if dash, push space
+         (push 45 str2)
+       (push i str2)))
+    (intern (concat (nreverse str2)))))
+
+(defun toolbarx-good-option-list-p (option-list valid-options)
+  "Non-nil means the OPTION-LIST is of form (OPT FORM ... OPT FORM).
+Each OPT is member of VALID-OPTIONS and OPT are pairwise
+different.  OPTION-LIST equal to nil is a good option list."
+  (let ((elt-in-valid t)
+       (temp-opt-list option-list)
+       (list-diff)
+       (n (/ (length option-list) 2)))
+    (dotimes (i n)
+      (when (> i 0)
+       (setq temp-opt-list (cddr temp-opt-list)))
+      (add-to-list 'list-diff
+                  (car temp-opt-list))
+      (setq elt-in-valid (and elt-in-valid
+                             (memq (car temp-opt-list)
+                                   valid-options))))
+    (and elt-in-valid                  ; options are on VALID-OPTOPNS
+        ;; OPTION-LIST has all option different from each other
+        (eq (length list-diff) n)
+        ;; OPTION-LIST has even number of elements
+        (eq (% (length option-list) 2) 0))))
+
+(defun toolbarx-separate-options (group-list valid-options &optional check)
+  "Return a cons cell with non-options and options of GROUP-LIST.
+The options-part is the largest tail of the list GROUP-LIST that
+has an element of VALID-OPTIONS (the comparation is made with
+`memq'.)  The non-options-part is the beginning of GROUP-LIST
+less its tail. Return a cons cell which `car' is the
+non-options-part and the `cdr' is the options-part.
+
+If CHECK is non-nil, the tail is the largest that yield non-nil
+when applied to `toolbarx-good-option-list-p'."
+  (let ((maximal)
+       (temp))
+    (dolist (i valid-options)
+      (setq temp (memq i group-list))
+      (when (and (> (length temp) (length maximal))
+                (if check
+                    (toolbarx-good-option-list-p temp valid-options)
+                  t))
+       (setq maximal (memq i group-list))))
+    (cons (butlast group-list (length maximal)) maximal)))
+
+
+(defun toolbarx-merge-props (inner-props outer-props override add)
+  "Merge property lists INNER-PROPS and OUTER-PROPS.
+INNER-PROPS and OUTER-PROPS are two lists in the format
+ (PROP VAL PROP VAL ... PROP VAL).
+Returns a list with properties and values merged.
+
+OVERRIDE and ADD are supposed to be lists of symbols.  The value
+of a property in OVERRIDE is the one on OUTER-PROPS or
+INNER-PROPS, but if the property is in both, the value in
+INNER-PROPS is used.  The value of a property in ADD will be a
+list with first element the symbol `:add-value-list' and the rest
+are the properties, inner properties first."
+  (let* ((merged)
+        (inner-prop)
+        (outer-prop))
+    (dolist (prop override)
+      (if (memq prop inner-props)
+         (setq merged (append merged
+                              (list prop (cadr (memq prop inner-props)))))
+       (when (memq prop outer-props)
+         (setq merged (append merged
+                              (list prop (cadr (memq prop outer-props))))))))
+    (dolist (prop add merged)
+      (setq inner-prop (memq prop inner-props))
+      (when inner-prop
+       (if (and (listp (cadr inner-prop))
+                (eq (car (cadr inner-prop)) :add-value-list))
+           (setq inner-prop (cdr (cadr inner-prop)))
+         (setq inner-prop (list (cadr inner-prop)))))
+      (setq outer-prop (memq prop outer-props))
+      (when outer-prop
+       (if (and (listp (cadr outer-prop))
+                (eq (car (cadr outer-prop)) :add-value-list))
+           (setq outer-prop (cdr (cadr outer-prop)))
+         (setq outer-prop (list (cadr outer-prop)))))
+      (when (append inner-prop outer-prop)
+       (setq merged (append merged
+                            (list prop (cons :add-value-list
+                                             (append inner-prop
+                                                     outer-prop)))))))))
+
+(defun toolbarx-make-command (comm prep app)
+  "Return a command made from COMM, PREP and APP.
+COMM is a command or a form.  PREP and APP are forms.  If PREP or
+APP are non-nil, they are added to the resulting command at the
+beginning and end, respectively.  If both are nil and COMM is a
+command, COMM is returned."
+  (let ((comm-is-command (commandp comm)))
+    (if (and (not prep)
+            (not app)
+            comm-is-command)
+       comm
+      (append '(lambda nil (interactive))
+             (when prep (list prep))
+             (when comm
+               (if comm-is-command
+                   `((call-interactively (function ,comm)))
+                 (list comm)))
+             (when app (list app))))))
+
+;; in Emacs, menus are made of keymaps (vectors are possible, but editors
+;; handle `menu titles' differently) meanwhile in XEmacs, menus are lists of
+;; vectors
+
+(defun toolbarx-emacs-mount-popup-menu
+  (strings var type &optional title save)
+  "Return an interactive `lambda'-expression that shows a popup menu.
+This function is the action of `toolbarx-mount-popup-menu' if
+inside Emacs. See documentation of that function for more."
+  ;; making the menu keymap by adding each menu-item definition
+  ;; see (info "(elisp)Menu keymaps")
+  (let* ((keymap (make-sparse-keymap title))
+        (count 1)
+        (used-symbols '(nil))
+        (key)
+        (real-type (if (eq type 'toggle) 'toggle 'radio))
+        (real-save (when save (if (eq save 'offer) 'offer 'always))))
+    ;; warn if type is not `radio' ot `toggle'; use `radio' if incorrect.
+    (unless (eq type real-type)
+      (display-warning 'toolbarx
+                      (format (concat "TYPE should be symbols `radio' or "
+                                      "`toggle', but %s found; using `radio'")
+                              type)))
+    ;; warn if save is not `nil', `offer' or `always'; use nil when incorrect
+    (unless (eq save real-save)
+      (setq real-save nil)
+      (display-warning 'toolbarx
+                      (format (concat "SAVE should be symbols `nil', "
+                                      "`offer' or `always', but %s found; "
+                                      "using `nil'")
+                              save)))
+    (dolist (i strings)
+      ;; finding a new symbol
+      (let* ((aux-count 0)
+           (i-symb (toolbarx-make-symbol-from-string i)))
+       (setq key i-symb)
+       (while (memq key used-symbols)
+         (setq aux-count (1+ aux-count))
+         (setq key (intern (format "%s-%d" i-symb aux-count))))
+       (setq used-symbols (cons key used-symbols)))
+      (define-key-after keymap (vector key)
+       `(menu-item ,i
+                   ,(append
+                     `(lambda nil (interactive)
+                        ,(if (eq real-type 'radio)
+                             `(setq ,var ,count)
+                           `(if (memq ,count ,var)
+                               (setq ,var (delete ,count ,var))
+                              (setq ,var (sort (cons ,count ,var) '<))))
+                        (toolbarx-refresh))
+                     (when (eq real-save 'always)
+                       `((customize-save-variable
+                          (quote ,var) ,var)))
+                     `(,var))
+                   :button ,(if (eq real-type 'radio)
+                                `(:radio eq ,var ,count)
+                              `(:toggle memq ,count ,var))))
+      (setq count (1+ count)))
+    (when (eq real-save 'offer)
+      (define-key-after keymap [sep] '(menu-item "--shadow-etched-in-dash"))
+      (let* ((aux-count 0)
+            (i-symb 'custom-save))
+       (setq key i-symb)
+       (while (memq key used-symbols)
+         (setq aux-count (1+ aux-count))
+         (setq key (intern (format "%s-%d" i-symb aux-count))))
+       (setq used-symbols (cons key used-symbols)))
+      (define-key-after keymap (vector key)
+       `(menu-item "Save state of this menu"
+                  (lambda nil (interactive)
+                    (customize-save-variable (quote ,var) ,var)))))
+    ;; returns a `lambda'-expression
+    `(lambda nil (interactive) (popup-menu (quote ,keymap)))))
+
+(defun toolbarx-xemacs-mount-popup-menu
+  (strings var type &optional title save)
+  "Return an interactive `lambda'-expression that shows a popup menu.
+This function is the action of `toolbarx-mount-popup-menu' if
+inside XEmacs. See documentation of that function for more."
+  (let* ((menu (if (and title (stringp title))
+                  (list title)
+                (setq title nil)
+                (list "Dropdown menu")))
+        (count 0)
+        (menu-item)
+        (menu-callback)
+        (real-type (if (eq type 'toggle) 'toggle 'radio))
+        (real-save (when save (if (eq save 'offer) 'offer 'always))))
+    ;; warn if type is not `radio' ot `toggle'; use `radio' if incorrect.
+    (unless (eq type real-type)
+      (warn (concat "TYPE should be symbols `radio' or `toggle', "
+                   "but %s found; using `radio'") type))
+    ;; warn if save is not `nil', `offer' or `always'; use nil when incorrect
+    (unless (eq save real-save)
+      (setq real-save nil)
+      (display-warning 'toolbarx
+                      (format (concat "SAVE should be symbols `nil', "
+                                      "`offer' or `always', but %s found; "
+                                      "using `nil'")
+                              save)))
+    ;; making the menu list of vectors
+    (dolist (str strings)
+      (setq count (1+ count))
+      (setq menu-callback (list 'progn
+                               (if (eq real-type 'radio)
+                                   `(setq ,var ,count)
+                                 `(if (memq ,count ,var)
+                                      (setq ,var (delete ,count ,var))
+                                    (setq ,var (sort (cons ,count ,var) '<))))
+                               '(toolbarx-refresh)))
+      (when (eq real-save 'always)
+       (setq menu-callback (append menu-callback
+                                   (list (list 'customize-save-variable
+                                               (list 'quote var) var)))))
+      (setq menu-item (vector str menu-callback
+                             :style real-type
+                             :selected (if (eq real-type 'radio)
+                                            `(eq ,var ,count)
+                                          `(memq ,count ,var))))
+      (setq menu (append menu (list menu-item))))
+    (when (eq real-save 'offer)
+      (setq menu (append menu (list "--:shadowEtchedInDash")))
+      (setq menu (append menu (list
+                              (vector
+                               "Save state of this menu"
+                               `(customize-save-variable (quote ,var)
+                                                         ,var))))))
+    ;; returnung the lambda-expression
+    `(lambda nil (interactive)
+       (let ((popup-menu-titles ,(if title t nil)))
+        (popup-menu (quote ,menu))))))
+
+(defun toolbarx-mount-popup-menu (strings var type &optional title save)
+  "Return a command that show a popup menu.
+The return is a `lambda'-expression with a interactive declaration.
+
+STRINGS is a list of strings which will be the itens of the menu.
+
+VAR is a symbol that is set when an item is clicked.  TYPE should
+be one of the symbols `radio' or `toggle': `radio' means that the
+nth item is selected if VAR is `n' and this item sets VAR to `n';
+`toggle' means that VAR should be a list of integers and the nth
+item is selected if `n' belongs to VAR.         The item inserts or
+deletes `n' from VAR.
+
+TITLE is a string (the title of the popup menu) or nil for no
+title.
+
+SAVE is one of the symbols nil, `offer' or `always'.  If value
+is nil, do not try to save anything.  If it is `offer', a menu
+item is added offering the user the possibiity to save state of
+that dropdown menu for future sesseions (using `custom').  If it
+is `always', state is saved every time that a item is clicked."
+  (if (featurep 'xemacs)
+      (toolbarx-xemacs-mount-popup-menu strings var type title save)
+    (toolbarx-emacs-mount-popup-menu strings var type title save)))
+
+(defun toolbarx-option-value (opt)
+  "Return option value according to Emacs flavour.
+If OPT is a vector, return first element if in Emacs or
+second if in XEmacs.  Otherwise, return OPT.
+If OPT is vector and length is smaller than the necessary (like
+if in XEmacs and vector has length 1), then nil is returned."
+  (if (vectorp opt)
+      (if (featurep 'xemacs)
+         (when (> (length opt) 1)
+           (aref opt 1))
+       (when (> (length opt) 0)
+         (aref opt 0)))
+    opt))
+
+(defun toolbarx-eval-function-or-symbol (object type-test-func)
+  "Return a cons cell (GOOD-OBJ . VAL).
+GOOD-OBJ non-nil means that VAL is a valid value, according to
+the car of the result of TYPE-TEST-FUNCTION, that should return a
+cons cell in the same format as the return of this function.
+
+If OBJECT applied to TYPE-TEST-FUNC return (GOOD-OBJ . VAL), and
+GOOD-OBJ is non-nil, return that.  Else, check if OBJECT is a
+function.  If so, evaluate and test again with TYPE-TEST-FUNC. If
+not a function or if GOOD-OBJ is again nil, test if OBJECT is a
+bound symbol, evaluate that and return the result of
+TYPE-TEST-FUNC."
+  (let* ((ret (funcall type-test-func object)))
+    (unless (car ret)
+      (if (functionp object)
+         (progn
+           (setq ret (funcall type-test-func (funcall object)))
+           (unless (car ret)
+             (when (and (symbolp object) (boundp object))
+               (setq ret (funcall type-test-func (symbol-value object))))))
+       ;; ok, obj is not function; try symbol
+       (when (and (symbolp object) (boundp object))
+         (setq ret (funcall type-test-func (symbol-value object))))))
+    ret))
+
+(defun toolbarx-test-image-type (obj)
+  "Return a cons cell (GOOD-OBJ . VAL).
+GOOD-OBJ is non-nil if OBJ yields a valid image object VAL (see
+documentation of function `toolbarx-process-symbol')."
+  (let ((toolbarx-test-image-type-simple
+        (lambda (img)
+          (let* ((val (toolbarx-option-value img))
+                 (all-obj-ok t)
+                 (good-obj
+                  (if (featurep 'xemacs)
+                      ;; if XEmacs
+                      (or (stringp val) ; a string
+                          (glyphp val)  ; or a glyph
+                          (and (symbolp val) ; or a symbol bound to a
+                               (boundp val)  ; glyph-list
+                               (check-toolbar-button-syntax
+                                (vector val
+                                        (lambda nil (interactive))
+                                        nil nil) t))
+                          (and (listp val) ; or a glyph-or-string list
+                               (> (length val) 0)
+                               (< (length val) 7)
+                               (dolist (i val all-obj-ok)
+                                 (setq all-obj-ok
+                                       (and all-obj-ok
+                                            (or (not i)
+                                                (stringp i)
+                                                (glyphp i)))))))
+                    ;; if Emacs
+                    (or (stringp val)    ; string
+                        (and (consp val) ; or image descriptor
+                             (eq (car val) 'image))
+                        (and (symbolp val) ; or a symbol bound to a
+                             (boundp val)  ; image descriptor
+                                           ; (defined with `defimage')
+                             (consp (eval val))
+                             (eq (car (eval val)) 'image))
+                        (and (listp val) ; or list with 4 strings or
+                                         ; image descriptors
+                             (= (length val) 4)
+                             (dolist (i val all-obj-ok)
+                               (setq all-obj-ok
+                                     (and all-obj-ok
+                                          (or (stringp i)
+                                              (and (consp i)
+                                                   (eq (car i)
+                                                       'image)))))))))))
+            (cons good-obj val)))))
+    (toolbarx-eval-function-or-symbol obj toolbarx-test-image-type-simple)))
+
+(defun toolbarx-test-button-type (obj)
+  "Return a cons cell (GOOD-OBJ . VAL).
+GOOD-OBJ is non-nil if OBJ yields a valid button object VAL (see
+documentation of function `toolbarx-process-symbol')."
+  (let ((toolbarx-test-button-type-simple
+        (lambda (but)
+          (let* ((val (toolbarx-option-value but))
+                 (good-obj (if (featurep 'xemacs)
+                               ;; if XEmacs
+                               t
+                             ;; if Emacs
+                             (and (consp val)
+                                  (memq (car val) '(:toggle :radio))))))
+            (cons good-obj val)))))
+    (toolbarx-eval-function-or-symbol obj toolbarx-test-button-type-simple)))
+
+(defun toolbarx-test-any-type (obj)
+  "Return a cons cell (t . VAL).
+If OBJ is vector, return VAL according to editor.  Else, return
+OBJ, because it is a form anyway."
+  (cons t (toolbarx-option-value obj)))
+
+(defun toolbarx-test-string-or-nil (obj)
+  "Return a cons cell (GOOD-OBJ . VAL).
+GOOD-OBJ is non-nil if OBJ yields a valid help object VAL (see
+documentation of function `toolbarx-process-symbol')."
+  (let ((toolbarx-test-string-or-nil-simple
+        (lambda (obj)
+          (let* ((val (toolbarx-option-value obj))
+                 (good-obj (or (stringp val)
+                               (not val))))
+            (cons good-obj val)))))
+    (toolbarx-eval-function-or-symbol obj toolbarx-test-string-or-nil-simple)))
+
+(defun toolbarx-test-toolbar-type (obj)
+  "Return a cons cell (GOOD-OBJ . VAL).
+GOOD-OBJ is non-nil if OBJ yields a valid toolbar property object
+VAL (see documentation of function `toolbarx-process-symbol')."
+  (let ((toolbarx-test-toolbar-type-simple
+        (lambda (obj)
+          (let* ((val (toolbarx-option-value obj))
+                 (all-but-def-opts '(top bottom left right))
+                 (all-opts '(default top bottom left right))
+                 (good-obj
+                  (if (featurep 'xemacs)
+                      ;; if XEmacs
+                      (if (symbolp val)
+                          (memq val all-opts)
+                        (and (consp val)
+                             (memq (car val) all-but-def-opts)
+                             (memq (cdr val) all-but-def-opts)))
+                    ;; if Emacs
+                    t)))
+            (cons good-obj val)))))
+    (toolbarx-eval-function-or-symbol obj toolbarx-test-toolbar-type-simple)))
+
+(defun toolbarx-test-dropdown-type (obj)
+  "Return a cons cell (GOOD-OBJ . VAL).
+GOOD-OBJ is non-nil if OBJ yields a valid `:type' property object
+VAL of a dropdown group (see documentation of function
+`toolbarx-process-dropdown-group'."
+  (let ((toolbarx-test-dropdown-type-simple
+        (lambda (obj)
+          (let* ((val (toolbarx-option-value obj))
+                 (good-obj (memq val '(radio toggle))))
+            (cons good-obj val)))))
+    (toolbarx-eval-function-or-symbol obj toolbarx-test-dropdown-type-simple)))
+
+(defun toolbarx-test-symbol (obj)
+  "Return a cons cell (GOOD-OBJ . VAL).
+GOOD-OBJ is non-nil if OBJ yields a valid `:variable' property
+object VAL of a dropdown group (see documentation of function
+`toolbarx-process-dropdown-group'."
+  (let ((toolbarx-test-symbol-simple
+        (lambda (obj)
+          (let* ((val (toolbarx-option-value obj))
+                 (good-obj (symbolp val)))
+            (cons good-obj val)))))
+    (toolbarx-eval-function-or-symbol obj toolbarx-test-symbol-simple)))
+
+(defun toolbarx-test-dropdown-default (obj)
+  "Return a cons cell (GOOD-OBJ . VAL).
+GOOD-OBJ is non-nil if OBJ yields a valid `:default' property
+object VAL of a dropdown group (see documentation of function
+`toolbarx-process-dropdown-group'."
+  (let ((toolbarx-test-dropdown-default-simple
+        (lambda (obj)
+          (let* ((val (toolbarx-option-value obj))
+                 (good-obj (or (integerp val)
+                               (and (listp val)
+                                    (let ((ok t))
+                                      (dolist (i val ok)
+                                        (setq ok (and ok (integerp i)))))))))
+            (cons good-obj val)))))
+    (toolbarx-eval-function-or-symbol obj
+                                     toolbarx-test-dropdown-default-simple)))
+
+(defun toolbarx-test-dropdown-save (obj)
+  "Return a cons cell (GOOD-OBJ . VAL).
+GOOD-OBJ is non-nil if OBJ yields a valid `:save' property
+object VAL of a dropdown group (see documentation of function
+`toolbarx-process-dropdown-group'."
+  (let ((toolbarx-test-dropdown-save-simple
+        (lambda (obj)
+          (let* ((val (toolbarx-option-value obj))
+                 (good-obj (memq val '(nil offer always))))
+            (cons good-obj val)))))
+    (toolbarx-eval-function-or-symbol obj toolbarx-test-dropdown-save-simple)))
+
+(defconst toolbarx-button-props
+  (let* ((props-types-alist
+         '((:image           toolbarx-test-image-type)
+           (:command         toolbarx-test-any-type)
+           (:enable          toolbarx-test-any-type)
+           (:visible         toolbarx-test-any-type)
+           (:help            toolbarx-test-string-or-nil)
+           (:insert          toolbarx-test-any-type       . and)
+           (:toolbar         toolbarx-test-toolbar-type)
+           (:button          toolbarx-test-button-type)
+           (:append-command  toolbarx-test-any-type       . progn)
+           (:prepend-command toolbarx-test-any-type       . progn)))
+        (possible-props (nreverse (let* ((props ()))
+                                    (dolist (p props-types-alist props)
+                                      (setq props (cons (car p) props))))))
+        (props-override (nreverse (let* ((props ()))
+                                    (dolist (p props-types-alist props)
+                                      (unless (cddr p)
+                                        (setq props (cons (car p) props)))))))
+        (props-add (nreverse (let* ((props ()))
+                               (dolist (p props-types-alist props)
+                                 (when (cddr p)
+                                   (setq props (cons (car p) props))))))))
+    (list props-types-alist possible-props props-override props-add))
+  "List yielding all encarnations of properties of a button.
+First element: alist, where each element is of form
+ (PROP . (TYPE-TEST-FUNCTION . ADD-OR-NIL))
+Second is a list with all properties.
+Third, a list with properties that override when merging.
+Fourth, a list of lists, each in the format (PROP ADD).")
+
+(defconst toolbarx-dropdown-props
+  ;; for naming dropdown properties see `Convention' in the doc string
+  (let* ((props-types-alist
+         '((:type                     toolbarx-test-dropdown-type)
+           (:variable                 toolbarx-test-symbol)
+           (:default                  toolbarx-test-dropdown-default)
+           (:save                     toolbarx-test-dropdown-save)
+           (:title                    toolbarx-test-string-or-nil)
+           (:dropdown-image           toolbarx-test-image-type)
+           (:dropdown-enable          toolbarx-test-any-type)
+           (:dropdown-visible         toolbarx-test-any-type)
+           (:dropdown-insert          toolbarx-test-any-type       . and)
+           (:dropdown-help            toolbarx-test-string-or-nil)
+           (:dropdown-toolbar         toolbarx-test-toolbar-type)
+           (:dropdown-append-command  toolbarx-test-any-type       . progn)
+           (:dropdown-prepend-command toolbarx-test-any-type       . progn)))
+        (possible-props (nreverse (let* ((props ()))
+                                    (dolist (p props-types-alist props)
+                                      (setq props (cons (car p) props))))))
+        (props-override (nreverse (let* ((props ()))
+                                    (dolist (p props-types-alist props)
+                                      (unless (cddr p)
+                                        (setq props (cons (car p) props)))))))
+        (props-add (nreverse (let* ((props ()))
+                               (dolist (p props-types-alist props)
+                                 (when (cddr p)
+                                   (setq props (cons (car p) props))))))))
+    (list props-types-alist possible-props props-override props-add))
+  "List yielding all encarnations of properties of a dropdown group.
+First element: alist, where each element is of form
+ (PROP . (TYPE-TEST-FUNCTION . ADD-OR-NIL))
+Second is a list with all properties.
+Third, a list with properties that override when merging.
+Fourth, a list of lists, each in the format (PROP ADD).
+
+Convention: properties for the dropdown button should be formed
+with the strings \":dropdown-\" with the button property name
+without `:'. This is used on the implementation.")
+
+(defun toolbarx-process-group-without-insert (group-without-props
+                                             merged-props-without-insert
+                                             meaning-alist switches)
+  "Return an updated version of SWITCHES.
+GROUP-WITHOUT-PROPS and MERGED-PROPS-WITHOUT-INSERT are
+preprocessed variables in `toolbarx-process-group'."
+  (let ((current-switches switches))
+    (dolist (i group-without-props current-switches)
+      (setq i (toolbarx-option-value i))
+      (if (symbolp i)
+         (setq current-switches
+               (toolbarx-process-symbol i meaning-alist
+                                        merged-props-without-insert
+                                        current-switches))
+       (when (listp i)
+         (setq current-switches
+               (toolbarx-process-group i meaning-alist
+                                       merged-props-without-insert
+                                       current-switches)))))))
+
+(defun toolbarx-process-group (group meaning-alist props switches)
+  "Return an updated version of SWITCHES.
+Append to already processed buttons (stored in SWITCHES) a
+processed version of GROUP.  Groups are useful to distribute
+properties.  External properties are given in PROPS, and merged
+with the internal properties that are in the end of GROUP.  If
+properties (after merge) contain a `:insert' property, return a
+list where the first and second elements are `:insert' and its
+value, and after that a list in the same format as SWITCHES."
+  (cond
+   ;; if DROPDOWN group
+   ((eq (car group) :dropdown-group)
+    (toolbarx-process-dropdown-group group meaning-alist props switches))
+   ;; if EVAL group
+   ((eq (car group) :eval-group)
+    (let ((current-switches switches))
+      (dolist (elt (cdr group) current-switches)
+       (let ((eval-elt (eval elt)))
+         (setq current-switches
+               (toolbarx-process-group (if (listp eval-elt)
+                                           eval-elt
+                                         (list eval-elt))
+                                       meaning-alist props
+                                       current-switches))))))
+   ;; if normal group
+   (t
+    (let* ((splited-props
+           (toolbarx-separate-options
+            group (append (nth 1 toolbarx-button-props)
+                          (nth 1 toolbarx-dropdown-props))))
+          (intern-props (cdr splited-props))
+          (group-without-props (car splited-props))
+          (merged-props
+           (toolbarx-merge-props intern-props props
+                                 (append (nth 2 toolbarx-button-props)
+                                         (nth 2 toolbarx-dropdown-props))
+                                 (append (nth 3 toolbarx-button-props)
+                                         (nth 3 toolbarx-dropdown-props)))))
+      ;; check whether merged props have an `:insert'
+      (if (memq :insert merged-props)
+         ;; if yes, prepend switches with a (:insert cond elements)
+         (let* ((memq-ins (memq :insert merged-props))
+                (ins-val (if (and (listp (cadr memq-ins))
+                                  (eq :add-value-list
+                                      (car (cadr memq-ins))))
+                             ;; if property is add-value property
+                             (let* ((p (assq
+                                        :insert
+                                        (nth 0 toolbarx-button-props)))
+                                    (add-list (list (cddr p)))
+                                    (prop-good-val))
+                               (dolist (val (cdr (cadr memq-ins)))
+                                 (setq prop-good-val (funcall (cadr p) val))
+                                 (when (car prop-good-val)
+                                   (setq add-list (cons (cdr prop-good-val)
+                                                        add-list))))
+                               ;; return: (nreverse add-list)
+                               (setq add-list (nreverse add-list))
+                               (if (eq 2 (length add-list))
+                                   (cadr add-list) ; just 1 value, no
+                                 add-list))        ; add-function
+                           ;; if property is not add-value
+                           (cadr memq-ins)))
+                (merged-props-without-insert
+                 (append (butlast merged-props (length memq-ins))
+                         (cddr memq-ins)))
+                (group-switches
+                 (toolbarx-process-group-without-insert
+                  group-without-props merged-props-without-insert
+                  meaning-alist nil)))
+           ;; return
+           (nreverse (cons (append (list :insert ins-val)
+                                   group-switches)
+                           (nreverse switches))))
+       ;; if not, just append what is processed to switches
+       (toolbarx-process-group-without-insert group-without-props
+                                              merged-props meaning-alist
+                                              switches))))))
+
+(defun toolbarx-process-symbol (symbol meaning-alist props switches)
+  "Process a button given by SYMBOL in MEANING-ALIST.
+The processed button is appended in SWITCHES, which is returned.
+Look for a association of SYMBOL in MEANING-ALIST for collecting
+properties.  Such association is a list that represents either a
+normal button (a description of the button) or an alias
+group (the symbol is an alias for a group of buttons). PROPS is
+a externel list of properties that are merged and then applied to
+the button.  Scope is given by GLOBAL-FLAG."
+  ;; there are 3 situations: symbol is :new-line, there is an alias group
+  ;; or a normal button
+  (let ((button-assq (cdr (assq symbol meaning-alist))))
+    (cond
+     ((eq (car button-assq) :alias)
+      ;; button association is ALIAS GROUP is passed to
+      ;; `toolbarx-process-group' as is but without the car.
+      ;; return: (toolbarx-process-group... returns updates switch
+      (toolbarx-process-group (cdr button-assq) meaning-alist props switches))
+     (t
+      ;; NORMAL BUTTON (association is a list of properties)
+      ;;
+      ;; properties need to be processed, that is, merge internal
+      ;; and external (given by PROPS) properties
+      (let* (;; button properties defined in `toolbarx-button-props'
+            (props-override    (nth 2 toolbarx-button-props))
+            (props-add         (nth 3 toolbarx-button-props))
+            ;; split considering also dropdown-group properties
+            (button-assq-split
+             (toolbarx-separate-options
+              button-assq
+              (append (nth 1 toolbarx-button-props)
+                      (nth 1 toolbarx-dropdown-props))))
+            (button-split-no-props (car button-assq-split))
+            (button-split-props (cdr button-assq-split))
+            ;; if there is no :image or :command in the props,
+            ;; try to get them from no-props part
+            (button-image-no-prop
+             (unless (memq :image button-split-props)
+               (when (> (length button-split-no-props) 0)
+                 (list :image (nth 0 button-split-no-props)))))
+            (button-command-no-prop
+             (unless (memq :command button-split-props)
+               (when (> (length button-split-no-props) 1)
+                 (list :command (nth 1 button-split-no-props)))))
+            (button-props (append button-split-props
+                                  button-image-no-prop
+                                  button-command-no-prop))
+            ;; merge props
+            (merged-props (toolbarx-merge-props button-props props
+                                                props-override
+                                                props-add)))
+       ;; return:
+       (nreverse (cons (cons symbol merged-props) (nreverse switches))))))))
+
+(defun toolbarx-process-dropdown-group (dropdown meaning-alist props switches)
+  "Process buttons that appear according to dropdown menu.
+Process a dropdown group DROPDOWN with meaning alist
+MEANING-ALIST, external property list PROP and GLOBAL-FLAG
+specifying scope. For a complete description, see documentation
+of `toolbarx-install-toolbar'. The processed buttons are stored
+in the end of SWITCHES, which is returned."
+  (let* ((dropdown-group (if (eq (car dropdown) :dropdown-group)
+                            (cdr dropdown)
+                          dropdown))
+        (dropdown-list-splited
+         (toolbarx-separate-options dropdown-group
+                                    (append
+                                     (nth 1 toolbarx-button-props)
+                                     (nth 1 toolbarx-dropdown-props))))
+        (dropdown-list  (car dropdown-list-splited))
+        (dropdown-props (cdr dropdown-list-splited))
+        (merged-props
+         (toolbarx-merge-props dropdown-props props
+                               (append (nth 2 toolbarx-button-props)
+                                       (nth 2 toolbarx-dropdown-props))
+                               (append (nth 3 toolbarx-button-props)
+                                       (nth 3 toolbarx-dropdown-props))))
+        (merged-props-button-only
+         (let* ((props-button-only)
+                (prop))
+           (dolist (p (nth 1 toolbarx-button-props) props-button-only)
+             (setq prop (memq p merged-props))
+             (when prop
+               (setq props-button-only
+                     (append (list p (cadr prop))
+                             props-button-only))))))
+        (merged-props-dropdown-only
+         (let* ((props-dropdown-only)
+                (prop))
+           (dolist (p (nth 1 toolbarx-dropdown-props) props-dropdown-only)
+             (setq prop (memq p merged-props))
+             (when prop
+               (setq props-dropdown-only
+                     (append (list p (cadr prop))
+                             props-dropdown-only))))))
+        ;; get value for each property and check type ONLY for props that do
+        ;; not concern the dropdown button, like `:type', `:save', etc. The
+        ;; props that concern the button are going to be handled in refresh
+        ;; time.
+        (filtered-dropdown-group-props-only
+         (let* ((filtered-props-temp)
+                (prop-good-val)
+                (prop))
+           (save-match-data
+             (dolist (p (nth 0 toolbarx-dropdown-props) filtered-props-temp)
+               (unless (string-match "^:dropdown-.*$"
+                                     (symbol-name (car p)))
+                 ;;    property           -> (car p)
+                 ;;    test type function -> (cadr p)
+                 (setq prop (memq (car p) merged-props-dropdown-only))
+                 ;; if so, check if value is of correct type
+                 (when prop
+                   (setq prop-good-val (funcall (cadr p) (cadr prop)))
+                   (if (car prop-good-val)
+                       (setq filtered-props-temp
+                             (append filtered-props-temp
+                                     (list (car p) (cdr prop-good-val))))
+                     (display-warning
+                      'toolbarx
+                      (format (concat "Wrong type for value in "
+                                      "property `%s' in dropdown group")
+                              (car p))))))))))
+        ;; properties for the dropdown button from dropdown merged properties
+        (dropdown-button-props
+         (let* ((props))
+           (save-match-data
+             (dolist (pr (nth 1 toolbarx-dropdown-props))
+               (when (and (memq pr merged-props-dropdown-only)
+                          (string-match "^:dropdown-\\(.*\\)$"
+                                        (symbol-name pr)))
+                 (let* ((new-pr (intern (concat ":"
+                                                (substring (symbol-name pr)
+                                                           (match-beginning 1)
+                                                           (match-end 1)))))
+                        (val (cadr (memq pr merged-props-dropdown-only))))
+                   (setq props (append (list new-pr val) props))))))
+           (unless (memq :image props)
+             (setq props (append (list :image "dropdown") props)))
+           props))
+        (dropdown-button-without-command
+         (cons 'dropdown dropdown-button-props))
+        ;; `:type' defaults to `radio'
+        (type (if (memq :type filtered-dropdown-group-props-only)
+                  (cadr (memq :type filtered-dropdown-group-props-only))
+                'radio))
+        ;; `:default' defaults to 1 or nil depending on `type'
+        ;; if type is toggle and default is not a list, but a
+        ;; integer, set as the list with integer
+        (default
+          (let* ((memq-default (memq :default
+                                     filtered-dropdown-group-props-only))
+                 (def-temp (cadr memq-default))
+                 (default-temp (if memq-default
+                                   def-temp
+                                 (if (eq type 'radio) 1 (list 1)))))
+            default-temp))
+        ;; `:save' defaults to nil and require `:variable'
+        (save (let* ((save-temp
+                      (when (memq :save filtered-dropdown-group-props-only)
+                        (cadr (memq :save
+                                    filtered-dropdown-group-props-only)))))
+                (if (and save-temp
+                         (not (memq :variable
+                                    filtered-dropdown-group-props-only)))
+                    (progn
+                      (display-warning
+                       'toolbarx
+                       (concat "`:save' property with non-nil value should "
+                               "be used only with the `:variable' property; "
+                               "using value nil for `:save'."))
+                      nil)
+                  save-temp)))
+        ;; `:title' defaults to nil
+        (title (when (memq :title filtered-dropdown-group-props-only)
+                 (cadr (memq :title filtered-dropdown-group-props-only))))
+        ;; the menu variable is buildt from the `:variable' option or
+        ;; make a symbol not used
+        (variable (if (memq :variable filtered-dropdown-group-props-only)
+                      (cadr (memq :variable
+                                  filtered-dropdown-group-props-only))
+                    (let* ((count 0)
+                           (symb (intern (format
+                                          "toolbarx-internal-menu-var-%d"
+                                          count))))
+                      (while (boundp symb)
+                        (setq count (1+ count))
+                        (setq symb
+                              (intern (format "toolbarx-internal-menu-var-%d"
+                                              count))))
+                      symb)))
+        ;; auxiliary variables
+        (list-strings)
+        (list-buttons))
+    ;; setting `variable'
+    (if save
+       (custom-declare-variable
+        variable default
+        "Used as variable of dropdown menu defined with `toolbarx'.")
+      (when (not (boundp variable))
+       (set variable default)))
+    ;; now check `variable' content
+    (set variable
+        (let ((val (eval variable)))
+          (if (eq type 'toggle)
+              (if (listp val)
+                  val
+                (if (integerp val)
+                    (list val)
+                  (list 1)))
+            ;; then, type is radio
+            (if (integerp val)
+                val
+              (if (and val
+                       (listp val)
+                       (integerp (car val)))
+                  (car val)
+                1)))))
+    ;; === buiding `list-strings' and `list-buttons' ===
+    ;; if only symbols, build `list-strings' and `list-buttons' from symbols
+    (if (let ((only-symbols-flag t))
+         (dolist (i dropdown-list only-symbols-flag)
+           (setq only-symbols-flag (and only-symbols-flag (symbolp i)))))
+       (let ((count 0))
+         (dolist (i dropdown-list)
+           ;; list-strings and list-buttons are buildt reversed
+           (setq list-strings (cons (toolbarx-make-string-from-symbol i)
+                                    list-strings))
+           (setq count (1+ count))
+           (setq list-buttons (cons (list i
+                                          :insert
+                                          (if (eq type 'radio)
+                                              (list 'eq count variable)
+                                            (list 'memq count variable)))
+                                    list-buttons))))
+      ;; if not, the it must start with string
+      (unless (stringp (car dropdown-list))
+       (error "%s %s %s"
+              "If not all itens on dropdown are symbols, then a string"
+              "must come before each set of buttons; no string found"
+              "in first position."))
+      (let ((count 0)
+           (elem)
+           (temp-list-buttons))
+       (while dropdown-list
+         (setq elem (car dropdown-list))
+         (setq dropdown-list (cdr dropdown-list))
+         (if (stringp elem)
+             ;; if string, output `temp-list-buttons' and prepair it again
+             (progn
+               ;; list-strings and list-buttons are buildt reversed
+               (setq list-strings (cons elem list-strings))
+               (when temp-list-buttons
+                 (setq list-buttons (cons (append (nreverse temp-list-buttons)
+                                                  (list :insert
+                                                        (if (eq type 'radio)
+                                                            (list 'eq count
+                                                                  variable)
+                                                          (list 'memq count
+                                                                variable))))
+                                          list-buttons)))
+               (setq temp-list-buttons nil)
+               (setq count (1+ count)))
+           ;; else, if not string, just insert it to `temp-list-buttons'
+           ;; which is also buildt reversed
+           (setq temp-list-buttons (cons elem temp-list-buttons))))
+       ;; output last temp list, left behind
+       (when temp-list-buttons
+         (setq list-buttons (cons (append (nreverse
+                                           temp-list-buttons)
+                                          (list
+                                           :insert (if (eq type 'radio)
+                                                       (list 'eq count
+                                                             variable)
+                                                     (list 'memq count
+                                                           variable))))
+                                  list-buttons)))))
+    ;; lists were made reversed (elements inserted at the beginning)
+    (setq list-strings (nreverse list-strings))
+    (setq list-buttons (nreverse list-buttons))
+    ;; now, pass `list-buttons' as a group to `toolbarx-process-group'
+    (let ((current-switches switches))
+      (setq current-switches
+           (toolbarx-process-group list-buttons meaning-alist
+                                   merged-props ; pass non-processed props
+                                   current-switches))
+      (setq current-switches
+           ;; outputing dropdown button
+           (toolbarx-process-group (append dropdown-button-without-command
+                                           (list :command
+                                                 (toolbarx-mount-popup-menu
+                                                  list-strings variable type
+                                                  title save)))
+                                   meaning-alist merged-props-button-only
+                                   switches))
+      current-switches)))
+
+
+
+;; Still functions `toolbarx-install-toolbar' and `toolbarx-refresh'to
+;; complete the parsing engine.         Since they interface with other engines,
+;; they must come in the end.
+
+;;; How a image is made, giving a string as (part of) file name.
+
+;; look at function `image-type-available-p' for Emacs !!!!
+
+(defun toolbarx-find-image (image)
+  "Return image descriptor or glyph for IMAGE.
+In Emacs, return an image descriptor for IMAGE.  In XEmacs,
+return a glyph.
+
+IMAGE is string.  Usually IMAGE neither contains a directory nor
+an extension.  If the extension is omitted, `xpm', `xbm' and
+`pbm' are tried.  If the directory is omitted,
+`toolbarx-image-path' is searched."
+  ;; `find-image' in Emacs 21 looks in `load-path' and `data-directory'.  In
+  ;; Emacs 22, we have `image-load-path' which includes `load-path' and
+  ;; `data-directory'.
+  ;;
+  ;; If there's some API in XEmacs to find the images, we should use it
+  ;; instead of locate-library.
+  ;;
+  ;; Emacs 22 has locate-file, but the other Emacsen don't.  The
+  ;; following should hopefully get us to all images ultimately.
+
+  (let ((file))
+    (dolist (i '("" ".xpm" ".xbm" ".pbm"))
+      (unless file
+       (setq file (locate-library (concat image i) t toolbarx-image-path))))
+    (if (featurep 'xemacs)
+       (and file (make-glyph file))
+      (if file
+         (create-image file)
+       (find-image `((:type xpm :file ,(concat image ".xpm"))
+                     (:type xbm :file ,(concat image ".xbm"))
+                     (:type pbm :file ,(concat image ".pbm"))))))))
+
+;; next variable interfaces between parsing and display engines
+(defvar toolbarx-internal-button-switches nil
+  "Store the list of processed buttons, used by `toolbarx-refresh'.
+This variable can store different values for the different buffers.")
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Second engine: display parsed buttons in Emacs
+
+(defun toolbarx-emacs-add-button (button used-keys keymap)
+  "Insert a button where BUTTON is its description.
+USED-KEYS should be a list of symbols, where the first element is
+`:used-symbols'.  This list should store the symbols of the
+buttons already inserted.  This list is changed by side effect.
+KEYMAP is the keymap where the menu-item corresponding to the
+tool-bal button is going to be inserted.  Insertion is made in
+the end of KEYMAP.
+
+BUTTON should be a list of form (SYMBOL . PROP-LIST).  SYMBOL is
+a symbol that \"names\" this button.  PROP-LIST is a list in the
+format (PROP VAL ... PROP VAL).         The supported properties are
+`:image', `:command', `:append-command', `:prepend-command',
+`:help', `:enable', `:visible', `:button', `:insert' and
+`:toolbar'. For a description of properties, see documentation of
+function `toolbar-install-toolbar'."
+  (let* ((symbol (nth 0 button))
+        (used-keys-list (when used-keys
+                          (cdr used-keys)))
+        (filtered-props
+         (let* ((filtered-props-temp)
+                (prop-good-val)
+                (prop))
+           (dolist (p (nth 0 toolbarx-button-props) filtered-props-temp)
+             ;;    property           -> (car p)
+             ;;    test type function -> (cadr p)
+             ;;    add-function       -> (cddr p)
+             (setq prop (memq (car p) button))
+             ;; if so, check if value is of correct type
+             (when prop
+               ;; if property is of add-type, them the value is a list
+               ;; (:add-value-list VAL VAL). Each VAL should be checked.
+               (if (and (cddr p) (eq :add-value-list (car (cadr prop))))
+                   (let* ((add-list (list (cddr p))))
+                     (dolist (val (cdr (cadr prop)))
+                       (setq prop-good-val (funcall (cadr p) val))
+                       (when (car prop-good-val)
+                         (setq add-list (cons (cdr prop-good-val) add-list))))
+                     (setq add-list (nreverse add-list))
+                     (when (eq 2 (length add-list)) ; just 1 value, no
+                                                    ; add-function
+                       (setq add-list (cadr add-list)))
+                     (setq filtered-props-temp (append
+                                                (list (car p) add-list)
+                                                filtered-props-temp)))
+                 ;; if override-property
+                 (setq prop-good-val (funcall (cadr p) (cadr prop)))
+                 (when (car prop-good-val)
+                   (setq filtered-props-temp (append
+                                              (list (car p)
+                                                    (cdr prop-good-val))
+                                              filtered-props-temp))))))))
+        (insert (or (not (memq :insert filtered-props))
+                    ;; (memq :insert filtered-props)
+                    (eval (nth 1 (memq :insert filtered-props))))))
+    (when insert
+      (cond
+       (t
+       ;; symbol is not :new-line, therefore a normal button
+       (let* ((image (cadr (memq :image filtered-props)))
+              (image-descriptor
+               (when (memq :image filtered-props)
+                 (cond
+                  ((stringp image)     ; string
+                   (toolbarx-find-image image))
+                  ((and (consp image)  ; or image descriptor
+                        (eq (car image) 'image))
+                   image)
+                  ((and (symbolp image) ; or a symbol bound to a
+                        (boundp image)  ; image descriptor (defined
+                                      ; with `defimage')g
+                        (consp (eval image))
+                        (eq (car (eval image)) 'image))
+                   (eval image))
+                  (t                   ; otherwise, must be a list
+                                       ; with 4 strings or image
+                                       ; descriptors
+                   (apply 'vector (mapcar (lambda (img)
+                                             (if (stringp img)
+                                                 (toolbarx-find-image img)
+                                               img))
+                                          image))))))
+              (command
+               (let* ((com (nth 1 (memq :command filtered-props)))
+                      (app (nth 1 (memq :append-command filtered-props)))
+                      (prep (nth 1 (memq :prepend-command filtered-props))))
+                 (when (or com app prep)
+                   (toolbarx-make-command com prep app))))
+              (help (cons (memq :help filtered-props)
+                          (cadr (memq :help filtered-props))))
+              (enable (cons (memq :enable filtered-props)
+                            (cadr (memq :enable filtered-props))))
+              (visible (cons (memq :visible filtered-props)
+                             (cadr (memq :visible filtered-props))))
+              (button (cons (memq :button filtered-props)
+                            (cadr (memq :button filtered-props))))
+              (menuitem (append
+                         (list 'menu-item
+                               (toolbarx-make-string-from-symbol symbol)
+                               command
+                               :image image-descriptor)
+                         (when (car help)
+                           (list :help (cdr help)))
+                         (when (car enable)
+                           (list :enable (cdr enable)))
+                         (when (car visible)
+                           (list :visible (cdr visible)))
+                         (when (car button)
+                           (list :button (cdr button)))))
+              (key-not-used
+               (let* ((count 0)
+                      (symb symbol))
+                 (while (memq symb used-keys-list)
+                   (setq count (1+ count))
+                   (setq symb (intern (format "%s-%d" symbol count))))
+                 symb)))
+         (when (and image-descriptor command)
+           (setq used-keys-list (cons key-not-used used-keys-list))
+           (define-key-after keymap
+             (vector key-not-used) menuitem))))))
+    (when used-keys (setcdr used-keys used-keys-list))))
+
+
+(defun toolbarx-emacs-refresh-process-button-or-insert-list (switches
+                                                            used-keys
+                                                            keymap)
+  "Process SWITCHES, inserting buttons in `tool-bar-map'.
+If a button is actually a `:insert' clause group (if `car' is
+`:insert') and evaluation of `cdr' yields non-nil, process `cddr'
+recursively as SWITCHES.  USED-KEYS is a list which `car' is
+`:used-symbols' and which `cdr' is a list of symbols that have already
+been used as keys in the keymap `tool-bar-map'."
+  (dolist (button switches)
+    (if (eq (car button) :insert)
+       (when (eval (cadr button))
+         (toolbarx-emacs-refresh-process-button-or-insert-list (cddr button)
+                                                               used-keys
+                                                               keymap))
+      (toolbarx-emacs-add-button button used-keys keymap))))
+
+
+
+(defun toolbarx-emacs-refresh (&optional global-flag)
+  "Refresh and redraw the toolbar in Emacs.
+If GLOBAL-FLAG is non-nil, the default value of toolbar switches
+is used and the default value of `toolbarx-map' is changed."
+  (let* ((switches (if global-flag
+                      (if (default-boundp 'toolbarx-internal-button-switches)
+                          (default-value 'toolbarx-internal-button-switches)
+                        toolbarx-internal-button-switches)
+                    toolbarx-internal-button-switches))
+        (used-keys (list :used-symbols nil))
+        (tool-bar-map-temp (make-sparse-keymap)))
+    (toolbarx-emacs-refresh-process-button-or-insert-list switches used-keys
+                                                         tool-bar-map-temp)
+    (if global-flag
+       (setq-default tool-bar-map tool-bar-map-temp)
+      (setq tool-bar-map tool-bar-map-temp))))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Third engine: display parsed buttons in XEmacs
+
+(defun toolbarx-xemacs-image-properties (image)
+  "Return a list of properties of IMAGE.
+IMAGE should be a string or a list of one to six strings or
+glyphs or nil, or a symbol bound to a list of one to six
+glyphs (them must be a valid image list, like one created with
+the function `toolbar-make-button-list').  Return a
+list (GLYPH-LIST HEIGHT WIDTH) where HEIGHT (resp. WIDTH) is the
+maximum of the heights (resp. widths) of all glyphs (or strings
+converted to glyphs) in GLYPH-LIST.  If IMAGE is not a list, it
+is treated as a list with IMAGE as only element.  Strings are
+converted to glyphs with the function `toolbarx-find-image'.  If,
+after possible string-to-glyph convertions, the list of glyphs
+has nil as first element, GLYPH-LIST becomes nil."
+  (let* ((glyph-list
+         (if (symbolp image)           ; if symbol, them must be a
+                                       ; valid image list, like
+                                       ; created by function
+                                       ; `toolbar-make-button-list'
+             (eval image)
+           (let ((img-list (if (listp image)
+                               image
+                             (list image)))
+                 (glyph-list-temp))
+             ;; glyph-list-temp
+             (setq glyph-list-temp
+                   (dolist (glyph img-list (nreverse glyph-list-temp))
+                     (if (stringp glyph)
+                         (setq glyph-list-temp
+                               (cons (toolbarx-find-image glyph)
+                                     glyph-list-temp))
+                       (setq glyph-list-temp (cons glyph glyph-list-temp)))))
+             (unless (car glyph-list-temp)
+               (setq glyph-list-temp nil))
+             glyph-list-temp)))
+        (usable-buttons
+         ;; computing inheritage
+         (let* ((usable-temp))
+           (if toolbar-captioned-p     ; problematic point :-(
+               (progn
+                 ;; CAP-UP:  cap-up -> up
+                 (setq usable-temp (cons (cond
+                                          ((nth 3 glyph-list))
+                                          ((nth 0 glyph-list)))
+                                         usable-temp))
+                 ;; CAP-DOWN:  cap-down -> cap-up -> down -> up
+                 (setq usable-temp (cons (cond
+                                          ((nth 4 glyph-list))
+                                          ((nth 3 glyph-list))
+                                          ((nth 1 glyph-list))
+                                          ((nth 0 glyph-list)))
+                                         usable-temp))
+                 ;; CAP-DISABLED:  cap-disabled -> cap-up -> disabled -> up
+                 (setq usable-temp (cons (cond
+                                          ((nth 5 glyph-list))
+                                          ((nth 3 glyph-list))
+                                          ((nth 2 glyph-list))
+                                          ((nth 0 glyph-list)))
+                                         usable-temp)))
+             ;; UP:  up
+             (setq usable-temp (cons (nth 0 glyph-list) usable-temp))
+             ;; DOWN:  down -> up
+             (setq usable-temp (cons (cond
+                                      ((nth 1 glyph-list))
+                                      ((nth 0 glyph-list)))
+                                     usable-temp))
+             ;; DISABLED:  disabled -> up
+             (setq usable-temp (cons (cond
+                                      ((nth 2 glyph-list))
+                                      ((nth 0 glyph-list)))
+                                     usable-temp)))
+           usable-temp))
+        (height (apply 'max 0 (mapcar (lambda (glyph)
+                                        (if glyph
+                                            (glyph-height glyph)
+                                          0))
+                                      usable-buttons)))
+        (width (apply 'max 0 (mapcar (lambda (glyph)
+                                       (if glyph
+                                           (glyph-width glyph)
+                                         0))
+                                     usable-buttons))))
+    (list (if (symbolp image) image glyph-list) height width)))
+
+
+
+(defun toolbarx-xemacs-button-properties (button)
+  "Return a list of properties of BUTTON.
+The result is either nil (if not to be inserted) or a list in the format
+ (TOOLBAR HEIGHT WIDTH BUTTON-DESCRIPTION)
+where
+
+TOOLBAR is one of the symbols `default', `top', `right', `bottom'
+  or `left'.
+
+HEIGHT and WIDTH are the maximal dimentions of all the glyphs
+  involved.
+
+BUTTON-DESCRIPTION is button definition in XEmacs; see the
+  documentation of variable `default-toolbar'."
+  (let* ((filtered-props
+         (let* ((filtered-props-temp)
+                (prop-good-val)
+                (prop))
+           (dolist (p (nth 0 toolbarx-button-props) filtered-props-temp)
+             ;;    property           -> (car p)
+             ;;    test type function -> (cadr p)
+             ;;    add-function       -> (cddr p)
+             (setq prop (memq (car p) button))
+             ;; if so, check if value is of correct type
+             (when prop
+               ;; if property is of add-type, them the value is a list
+               ;; (:add-value-list VAL VAL). Each VAL should be checked.
+               (if (and (cddr p) (eq :add-value-list (car (cadr prop))))
+                   (let* ((add-list (list (cddr p))))
+                     (dolist (val (cdr (cadr prop)))
+                       (setq prop-good-val (funcall (cadr p) val))
+                       (when (car prop-good-val)
+                         (setq add-list (cons (cdr prop-good-val) add-list))))
+                     (setq add-list (nreverse add-list))
+                     (when (eq 2 (length add-list)) ; just 1 value, no
+                                                    ; add-function
+                       (setq add-list (cadr add-list)))
+                     (setq filtered-props-temp (append
+                                                (list (car p) add-list)
+                                                filtered-props-temp)))
+                 ;; if override-property
+                 (setq prop-good-val (funcall (cadr p) (cadr prop)))
+                 (when (car prop-good-val)
+                   (setq filtered-props-temp (append
+                                              (list (car p)
+                                                    (cdr prop-good-val))
+                                              filtered-props-temp))))))))
+        (insert (or (not (memq :insert filtered-props))
+                    ;; (memq :insert filtered-props) holds
+                    (eval (nth 1 (memq :insert filtered-props))))))
+    (when insert
+      (let* ((image-props (toolbarx-xemacs-image-properties
+                          (cadr (memq :image filtered-props))))
+            (glyph-list (car image-props))
+            (image-height (nth 1 image-props))
+            (image-width (nth 2 image-props))
+            (command
+             (let* ((com (nth 1 (memq :command filtered-props)))
+                    (app (nth 1 (memq :append-command filtered-props)))
+                    (prep (nth 1 (memq :prepend-command filtered-props))))
+               (when (or com app prep)
+                 (toolbarx-make-command com prep app))))
+            ;; enable defaults to `t'
+            (enable (if (memq :enable filtered-props)
+                        (cadr (memq :enable filtered-props))
+                      t))
+           ;; help defaults to nil
+            (help (when (memq :help filtered-props)
+                    (cadr (memq :help filtered-props))))
+            ;; toolbar defaults to `default'
+            (toolbar-prop (cons (memq :toolbar filtered-props)
+                                (cadr (memq :toolbar filtered-props))))
+            (toolbar (if (car toolbar-prop)
+                         (if (symbolp (cdr toolbar-prop))
+                             (cdr toolbar-prop)
+                           ;; (cdr toolbar-prop) is cons cell
+                           (if (eq (cadr toolbar-prop)
+                                         (default-toolbar-position))
+                                     (cddr toolbar-prop)
+                                  (cadr toolbar-prop)))
+                       'default)))
+       (when glyph-list
+         (list toolbar image-height image-width
+               (vector glyph-list command enable help)))))))
+
+(defun toolbarx-xemacs-refresh-process-button-or-insert-list (switches
+                                                             toolbar-props)
+  "Process SWITCHES, returning an updated version of TOOLBAR-PROPS.
+TOOLBAR-PROPS should be a list with 12 elements, each one representing
+properties (in this order) `locale', `default', `top', `right',
+`bottom', `left', `default-height', `default-width', `top-height',
+`right-width', `bottom-height' and `left-width'.  The return is a list
+with the same properties updated.
+
+NB: Buttons (vectors) are inserted in front of the lists
+represented by `default', `top', `right', `bottom' and `left', so
+the lists are built reversed."
+  (let ((locale                 (nth 0  toolbar-props))
+       (default         (nth 1  toolbar-props))
+       (top             (nth 2  toolbar-props))
+       (right           (nth 3  toolbar-props))
+       (bottom          (nth 4  toolbar-props))
+       (left            (nth 5  toolbar-props))
+       (default-height  (nth 6  toolbar-props))
+       (default-width   (nth 7  toolbar-props))
+       (top-height      (nth 8  toolbar-props))
+       (right-width     (nth 9  toolbar-props))
+       (bottom-height   (nth 10 toolbar-props))
+       (left-width      (nth 11 toolbar-props))
+       (toolbar-props-temp))
+    (dolist (button switches)
+      (if (eq (car button) :insert)
+         (when (eval (cadr button))
+           ;; if insert group, process `cddr'
+           (progn
+             (setq toolbar-props-temp
+                   (toolbarx-xemacs-refresh-process-button-or-insert-list
+                    (cddr button)
+                    (list locale default top right bottom left
+                          default-height default-width top-height
+                          right-width bottom-height left-width)))
+             (setq default        (nth 1  toolbar-props-temp))
+             (setq top            (nth 2  toolbar-props-temp))
+             (setq right          (nth 3  toolbar-props-temp))
+             (setq bottom         (nth 4  toolbar-props-temp))
+             (setq left           (nth 5  toolbar-props-temp))
+             (setq default-height (nth 6  toolbar-props-temp))
+             (setq default-width  (nth 7  toolbar-props-temp))
+             (setq top-height     (nth 8  toolbar-props-temp))
+             (setq right-width    (nth 9  toolbar-props-temp))
+             (setq bottom-height  (nth 10 toolbar-props-temp))
+             (setq left-width     (nth 11 toolbar-props-temp))))
+       ;; else, if normal button
+       (let* ((button-props (toolbarx-xemacs-button-properties button))
+              (toolbar (nth 0 button-props))
+              (height (nth 1 button-props))
+              (width (nth 2 button-props))
+              (button-description (nth 3 button-props)))
+         (when button-props
+           (cond
+            ;; default
+            ((eq toolbar 'default)
+             (setq default (cons button-description default))
+             (setq default-height (max default-height height))
+             (setq default-width (max default-width width)))
+            ;; top
+            ((eq toolbar 'top)
+             (setq top (cons button-description top))
+             (setq top-height (max top-height height)))
+            ;; right
+            ((eq toolbar 'right)
+             (setq right (cons button-description right))
+             (setq right-width (max right-width width)))
+            ;; bottom
+            ((eq toolbar 'bottom)
+             (setq bottom (cons button-description bottom))
+             (setq bottom-height (max bottom-height height)))
+            ;; left
+            ((eq toolbar 'left)
+             (setq left (cons button-description left))
+             (setq left-width (max left-width width))))))))
+    ;; return a list similar to toolbar-props
+    (list locale default top right bottom left default-height
+         default-width top-height right-width bottom-height left-width)))
+
+
+(defun toolbarx-xemacs-refresh (&optional global-flag)
+  "Refresh the toolbar in XEmacs."
+  (let* ((switches (if global-flag
+                      (if (default-boundp 'toolbarx-internal-button-switches)
+                          (default-value 'toolbarx-internal-button-switches)
+                        toolbarx-internal-button-switches)
+                    toolbarx-internal-button-switches))
+        (locale  (if global-flag 'global (current-buffer)))
+        (toolbar-init (list locale     ; locale
+                            nil        ; default
+                            nil        ; top
+                            nil        ; right
+                            nil        ; bottom
+                            nil        ; left
+                            0          ; default-height
+                            0          ; default-width
+                            0          ; top-height
+                            0          ; right-width
+                            0          ; bottom-height
+                            0))        ; left-width
+        (toolbar-props
+         (toolbarx-xemacs-refresh-process-button-or-insert-list switches
+                                                                toolbar-init))
+        ;; NB: Buttons (vectors) are inserted in front of the lists
+        ;; represented by `default', `top', `right', `bottom' and
+        ;; `left', so the lists are built reversed.
+        (default         (nreverse (nth 1  toolbar-props)))
+        (top             (nreverse (nth 2  toolbar-props)))
+        (right           (nreverse (nth 3  toolbar-props)))
+        (bottom          (nreverse (nth 4  toolbar-props)))
+        (left            (nreverse (nth 5  toolbar-props)))
+        (default-height  (nth 6  toolbar-props))
+        (default-width   (nth 7  toolbar-props))
+        (top-height      (nth 8  toolbar-props))
+        (right-width     (nth 9  toolbar-props))
+        (bottom-height   (nth 10 toolbar-props))
+        (left-width      (nth 11 toolbar-props))
+        (button-raised-border 2)
+        (default-border (specifier-instance default-toolbar-border-width))
+        (top-border (specifier-instance top-toolbar-border-width))
+        (right-border (specifier-instance right-toolbar-border-width))
+        (bottom-border (specifier-instance bottom-toolbar-border-width))
+        (left-border (specifier-instance left-toolbar-border-width)))
+    ;; adding borders
+    (when default
+      (setq default-height (+ (* 2 button-raised-border)
+                             (* 2 default-border)
+                             default-height))
+      (setq default-width (+ (* 2 button-raised-border)
+                            (* 2 default-border)
+                            default-width)))
+    (when top
+      (setq top-height (+ (* 2 button-raised-border)
+                         (* 2 top-border)
+                         top-height)))
+    (when right
+      (setq right-width (+ (* 2 button-raised-border)
+                          (* 2 right-border)
+                          right-width)))
+    (when bottom
+      (setq bottom-height (+ (* 2 button-raised-border)
+                            (* 2 bottom-border)
+                            bottom-height)))
+    (when left
+      (setq left-width (+ (* 2 button-raised-border)
+                         (* 2 left-border)
+                         left-width)))
+    ;; deal with specifiers
+    ;; - remove all specifiers for toolbars witout buttons
+    (if default
+       (progn
+         ;; Only activate the tool bar if it is already visible.
+         (when toolbar-visible-p
+           (set-specifier default-toolbar-visible-p (not (not default)) locale)
+           (if (memq (default-toolbar-position) '(top bottom))
+               (set-specifier default-toolbar-height default-height locale)
+             (set-specifier default-toolbar-width default-width locale)))
+         (set-specifier default-toolbar default locale))
+      (remove-specifier default-toolbar locale)
+      (remove-specifier default-toolbar-visible-p locale)
+      (remove-specifier default-toolbar-height locale)
+      (remove-specifier default-toolbar-width locale))
+    (if top
+       (progn
+         (set-specifier top-toolbar-visible-p (not (not top)) locale)
+         (set-specifier top-toolbar-height top-height locale)
+         (set-specifier top-toolbar top locale))
+      (remove-specifier top-toolbar locale)
+      (remove-specifier top-toolbar-visible-p locale)
+      (remove-specifier top-toolbar-height locale))
+    (if right
+       (progn
+         (set-specifier right-toolbar-visible-p (not (not right))
+                        locale)
+         (set-specifier right-toolbar-width right-width locale)
+         (set-specifier right-toolbar right locale))
+      (remove-specifier right-toolbar locale)
+      (remove-specifier right-toolbar-visible-p locale)
+      (remove-specifier right-toolbar-width locale))
+    (if bottom
+       (progn
+         (set-specifier bottom-toolbar-visible-p (not (not bottom)) locale)
+         (set-specifier bottom-toolbar-height bottom-height locale)
+         (set-specifier bottom-toolbar bottom locale))
+      (remove-specifier bottom-toolbar locale)
+      (remove-specifier bottom-toolbar-visible-p locale)
+      (remove-specifier bottom-toolbar-height locale))
+    (if left
+       (progn
+         (set-specifier left-toolbar-visible-p (not (not left)) locale)
+         (set-specifier left-toolbar-width left-width locale)
+         (set-specifier left-toolbar left locale))
+      (remove-specifier left-toolbar locale)
+      (remove-specifier left-toolbar-visible-p locale)
+      (remove-specifier left-toolbar-width locale))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; finishing parsing engine
+
+(defun toolbarx-refresh (&optional global-flag)
+  "Redraw the toolbar, peviously installed with `toolbarx'.
+Force global refresh if GLOBAL-FLAG is non-nil."
+  (interactive "P")
+  (if (featurep 'xemacs)
+      (toolbarx-xemacs-refresh global-flag)
+    (toolbarx-emacs-refresh global-flag)))
+
+;;;###autoload (autoload 'toolbarx-install-toolbar "toolbar-x")
+
+(defun toolbarx-install-toolbar (buttons &optional meaning-alist global-flag)
+  "Install toolbar buttons given in BUTTONS.
+Button properties are optionally given in MEANING-ALIST.  If
+GLOBAL-FLAG is non-nil, toolbar is installed globally (on every
+buffer that does not have a toolbar set locally).  BUTTONS is a
+list of format
+  (ELEM ... ELEM . PROPS),
+where each ELEM is either
+
+ - a list in the same format od BUTTONS, which is going to be
+   refered as a *group*; groups are used to distribute properties
+   recursively to its elements; there are groups with special
+   format for special purpose: *dropdown groups* and also *eval
+   groups*.
+
+ - a symbol, which could be associated in MEANING-ALIST with a
+   list of button properties (symbol + properties = a *button*)
+   or associated to a special kind of group (an *alias group*).
+
+ - a vector, which elements are on the previous formats (but not
+   another vector); this is useful to specify different
+   ingredients to the toolbar depending if editor is Emacs or
+   XEmacs; the first element will be used in Emacs; the second
+   element is going to be used in XEmacs.
+
+Meaning alist
+=============
+
+MEANING-ALIST is a list where each element is in one of the
+formats (SYMB . BUTTON-PROPS-LIST) or (SYMB .  ALIAS-GROUP).
+BUTTON-PROPS-LIST is a list in one of the formats
+  (IMAGE COMMAND PROP VAL PROP VAL ... PROP VAL)  or
+  (PROP VAL PROP VAL ... PROP VAL).
+The IMAGE is going to be used as the `:image' property of the
+button (see button properties bellow), and COMMAND shall be used
+as the `:command' property of the button.  Each PROP is one of
+the button properties, and VAL is its respective value.
+ALIAS-GROUP is a list which first element is the symbol `:alias'
+and the cdr shall be processed as a group.
+
+However, a symbol is not required to have an association in
+MEANING-ALIST, which is only a way to specify properties to a
+button.         One can use groups to specify properties.  Nil is a good
+MEANING-ALIST.
+
+Buttons
+=======
+
+A toolbar button in `toolbarx' is the set with a symbol and
+properties used to display the button, like a image and a command
+to call when the button is pressed (which are the minimal
+elements that a button should have.)  The supported properties
+for buttons and their `basic types' (see note on how values of
+properties are obtained!) are:
+
+ :image -- in Emacs, either a string or image descriptor (see
+   info for a definition), or a variable bound to a image
+   descriptor (like those defined with `defimage') or a list of 4
+   strings or image descriptors; in XEmacs, either a string or a
+   glyph, or a symbol bount to a glyph, or a list of at least 1
+   and at most 6 strings or glyphs or nil (not the first element
+   though); defines the image file displayed by the button.  If
+   it is a string, the image file found with that name (always
+   using the function `toolbarx-find-image' to make the
+   \`internal\' image descriptor) is used as button image.  For
+   the other formats, the button image is handled in the same way
+   as it is treated by the editors; see info nodes bellow for a
+   description of the capabilities of each editor
+      Emacs: info file \"elisp\", node \"Tool Bar\" (see `:image'
+             property);
+             PS: a *vector* of four strings is used in the Emacs
+             Lisp documentation as the `more ellaborated' image
+             property format, but here we reserve vectors to
+             provide editor-dependent values; this motivates our
+             choice for a list instead of vector (however,
+             internally the list becomes a vector when displaying
+             the button).
+     XEmacs: info file \"lispref\", node \"Toolbar Descriptor
+             Format\" (see GLYPH-LIST) or the documentation of
+             the variable `default-toolbar'; check the inheritage
+             in case of a ommited glyph or nil instead of glyph.
+
+ :command -- a form; if the form happens to be a command, it will
+   be called with `call-interactively'.
+
+ :append-command -- a form added to the end of the value of
+   `:command'.
+
+ :prepend-command -- a form added at the beginning of the value
+   of `:command'.
+
+ :help -- either a string or nil; defined the help string of the
+   button;
+
+ :enable -- a form, evaluated constantly by both editors to
+   determine if a button is active (enabled) or not.
+
+ :visible -- in Emacs, a form that is evaluated constantly to
+   determine if a button is visible; in XEmacs, this property is
+   ignored.
+
+ :button -- in Emacs, a cons cell (TYPE .  SELECTED) where the
+   TYPE should be `:toggle' or `:radio' and the cdr should be a
+   form.  SELECTED is evaluated to determine when the button is
+   selected.  This property is ignored in XEmacs.
+
+ :insert -- a form that is evaluated every time that the toolbar
+   is refresh (a call of `toolbarx-refresh') to determine if the
+   button is inserted or just ignored (until next refresh).
+
+ :toolbar -- in XEmacs, either one of the symbols `default',
+   `top', `bottom', `left', `right', or a cons cell
+   (POS . POS-AVOID-DEFAULT) where POS and POS-AVOID-DEFAULT
+   should be one of the symbols `top', `bottom', `left', `right';
+   if a symbol, the button will be inserted in one of these
+   toolbars; if a cons cell, button will be inserted in toolbar
+   POS unless the position of the default toolbar is POS (then,
+   the default toolbar would override the position-specific
+   toolbar), and in this case, button will be inserted in toolbar
+   POS-AVOID-DEFAULT; in Emacs, this property is meaningless, and
+   therefore ignored.  Hint of use of this property: in a
+   program, use or everything with `default' and the cons format
+   to avoid the default toolbar, or use only the position
+   specific buttons (symbols that are not `default'), because of
+   the `overriding' system in XEmacs, when a position-specific
+   toolbar overrides the default toolbar; for instance, if you
+   put a button in the default toolbar and another in the top
+   toolbar (and the default toolbar is in the top), then *only*
+   the ones in the top toolbar will be visible!
+
+How to specify a button
+=======================
+
+One can specify a button by its symbol or by a group to specify
+properties.  For example,
+  BUTTON =
+    ( foo
+      (bar :image [\"bar-Emacs\" \"bar-XEmacs\"]
+           :command bar-function :help \"Bar help string\")
+      :insert foo-bar )
+  MEANING-ALIST = ( (foo :image \"foo\" :command foo-function) )
+specifiy two buttons `foo' and `bar', each one with its necessary
+:image and :command properties, and both use the :insert property
+specified ate the end of BUTTONS (because groups distribute
+properties to all its elements).  `foo' and `bar' will be
+inserted only if `foo-bar' evaluation yields non-nil.  `bar' used
+a different :image property depending if editor is Emacs or
+XEmacs.
+
+Note on how values of properties are obtained
+=============================================
+
+For each property PROP, its value should be either:
+   i) a vector of 2 elements; then each element should be of the
+      basic type of PROP.
+  ii) an element on the basic type of PROP.
+ iii) a function (that does not need arguments); it is evaluated
+      and the return should be ot type i) or ii) above
+  iv) a symbol bound to a element of type i) or ii).
+
+The type is cheched in the order i), ii) iii) and iv). This
+evaluations are done every time that the oolbar is refresh.
+
+Ps.: in order to specify a vector as value of a property (like
+the :image in Emacs), it is necessary to provide the vector as
+element of another vector.
+
+Special groups
+==============
+
+Eval groups
+-----------
+
+If the first element of a group is the symbol `:eval-group', each
+element is evaluated (with `eval'), put inside a list and
+processed like a group.         Eval groups are useful to store
+definition of buttons in a variable.
+
+Dropdown groups
+---------------
+
+The idea is to specify a set of buttons that appear when a
+determined menu item of a dropdown menu is active.  The dropdown
+menu appears when a button (by default with a triangle pointing
+down) is clicked.  This button is called `dropdown button'.  The
+dropdown button appears on the left of the currently visible
+buttons of the dropdown group.
+
+A dropdown group is a list which first element is the symbol
+`:dropdown-group' and in one of the following formats
+  (:dropdown-group SYMBOL-1 ... SYMBOL-n  PROP-1 VAL-1 ... PROP-k VAL-k)
+or
+  (:dropdown-group
+     STRING-1 ITEM-11 ... ITEM-1n
+     STRING-2 ITEM-21 ... ITEM-2m
+          . . .
+     STRING-n ITEM-n1 ... ITEM-np
+       PROP-1 VAL-1 ... PROP-j VAL-j)
+where
+ SYMBOL-* is a symbol that defines a button in MEANING-ALIST;
+ STRING-* is a string that will appear in the dropdown menu;
+ ITEM-* is any format that define buttons or groups.
+
+\(a dropdown group of first format is internally converted to the
+second by making strings from the symbols and each symbol is the
+item)
+
+The same rules for obtaining property values, described above,
+apply here.  Properties are also distributed by groups.         The
+supported properties and their basic type are:
+
+ :type -- one of the symbols `radio' (default) or `toggle'; if
+   type is radio, only one of the itens may be active, and if
+   type is toggle, any item number of itens can be active.
+
+ :variable -- a symbol; it is the variable that govern the
+   dropdown button; every time the value should be an integer
+   starting from 1 (if type is radio) or a list of integers (if
+   type is toggle).  The Nth set of buttons is :insert'ed.
+
+ :default -- determines the default value when the menu is
+   installed; it is ignored if a value was saved with custom; it
+   defaults to 1 if type is radio or nil if type is toggle.  If
+   value is a integer and type is `toggle', value used is a list
+   with that integer.
+
+ :save -- one of the symbols nil (default), `offer' or
+   `always'; determined if it is possible for the user to save
+   the which menu itens are active, for a next session.         If value
+   is `offer', a item (offering to save) is added to the
+   popup menu. If the value is `always', every time that a item
+   is selected, the variable is saved. If value is nil, variable
+   shall not be saved. If value is non-nil then `:variable' is
+   mandatory.
+
+ :title -- a string or nil; if a string, the popup menu will show
+   is as menu title; if nil, no title is shown.
+
+ :dropdown-help -- a string or nil; the help string of the
+   dropdown button.
+
+ :dropdown-image -- in Emacs, either a string or a vector of 4
+   strings; in XEmacs, either a string or a glyph or a list of at
+   least 1 and at most 6 strings or glyphs; defines the image
+   file displayed by the dropdown button; by default, it is the
+   string \"dropdown\".
+
+ :dropdown-append-command,
+ :dropdownprepend-command -- a form; append or prepend forms to
+   the command that shows the dropdown menu, allowing extra code
+   to run before or after the menu appears (remember that every
+   menu item clicked refresh the toolbar.)
+
+ :dropdown-enable -- a form; evaluated constantly by both editors
+   to determine if the dropdown button is active (enabled) or
+   not.
+
+ :dropdown-visible -- a form; in Emacs, it is evaluated
+   constantly to determine if the dropdown button is visible; in
+   XEmacs, this property is ignored.
+
+ :dropdown-toolbar -- in XEmacs, one of the symbols `default',
+   `opposite', `top', `bottom', `left' or `right'; ignored in
+   Emacs; in XEmacs, the toolbar where the dropdown button will
+   appear.
+
+Also, if the symbol `dropdown' is associted in MEANING-ALIST
+with some properties, these properties override (or add) with
+higher precedence.
+
+Special buttons
+===============
+
+If the symbol of a button is `:new-line', it is inserted
+a (faked) return, and the next button will be displayed a next
+line of buttons.  The only property supported for this button is
+`:insert'.  This feature is available only in Emacs.  In XEmacs,
+this button is ignored."
+  (let ((switches (toolbarx-process-group buttons meaning-alist nil nil)))
+    (if global-flag
+       (setq-default toolbarx-internal-button-switches
+                     switches)
+      (set (make-local-variable 'toolbarx-internal-button-switches)
+          switches)
+      (unless (featurep 'xemacs)
+       (make-local-variable 'tool-bar-map))))
+  (toolbarx-refresh global-flag))
+
+
+(defconst toolbarx-default-toolbar-meaning-alist
+  `((separator :image "sep" :command t :enable nil :help "")
+
+    (,(if (and (not (featurep 'xemacs)) (>= emacs-major-version 22))
+         'new-file
+       'open-file)
+     :image ["new" toolbar-file-icon]
+     :command [find-file toolbar-open]
+     :enable [(not (window-minibuffer-p
+                   (frame-selected-window menu-updating-frame)))
+             t]
+     :help ["Specify a new file's name, to edit the file" "Visit new file"])
+
+    ,(when (and (not (featurep 'xemacs)) (>= emacs-major-version 22))
+       '(open-file :image ["open" toolbar-file-icon]
+                  :command [menu-find-file-existing toolbar-open]
+                  :enable [(not (window-minibuffer-p
+                                 (frame-selected-window menu-updating-frame)))
+                           t]
+                  :help ["Read a file into an Emacs buffer" "Open a file"]))
+
+    (dired :image [,(if (>= emacs-major-version 22)
+                       "diropen"
+                     "open")
+                  toolbar-folder-icon]
+          :command [dired toolbar-dired]
+          :help ["Read a directory, operate on its files" "Edit a directory"])
+
+    (save-buffer :image ["save" toolbar-disk-icon]
+                :command [save-buffer toolbar-save]
+                :enable [(and
+                          (buffer-modified-p)
+                          (buffer-file-name)
+                          (not (window-minibuffer-p
+                                (frame-selected-window menu-updating-frame))))
+                         t]
+                :help ["Save current buffer to its file"  "Save buffer"]
+                :visible (or buffer-file-name
+                             (not (eq 'special
+                                      (get major-mode 'mode-class)))))
+
+    ;; Emacs only
+    (write-file :image "saveas"
+               :command write-file
+               :enable (not
+                        (window-minibuffer-p
+                         (frame-selected-window menu-updating-frame)))
+               :insert [t nil]
+               :help "Write current buffer to another file"
+               :visible (or buffer-file-name
+                            (not (eq 'special (get major-mode 'mode-class)))))
+
+    (undo :image ["undo" toolbar-undo-icon]
+         :command [undo toolbar-undo]
+         :enable [(and (not buffer-read-only)
+                       (not (eq t buffer-undo-list))
+                       (if (eq last-command 'undo)
+                           pending-undo-list
+                         (consp buffer-undo-list)))
+                  t]
+         :help ["Undo last operation" "Undo edit"]
+         :visible (not (eq 'special (get major-mode 'mode-class))))
+
+    (cut :image ["cut" toolbar-cut-icon]
+        :help ["Delete text in region and copy it to the clipboard"
+               "Kill region"]
+        :command [clipboard-kill-region toolbar-cut]
+        :visible (not (eq 'special (get major-mode 'mode-class))))
+
+    (copy :image ["copy" toolbar-copy-icon]
+         :help ["Copy text in region to the clipboard" "Copy region"]
+         :command [clipboard-kill-ring-save toolbar-copy])
+
+    (paste :image ["paste" toolbar-paste-icon]
+          :help ["Paste text from clipboard" "Paste from clipboard"]
+          :command [clipboard-yank toolbar-paste]
+          :visible (not (eq 'special (get major-mode 'mode-class))))
+
+    ;; Emacs only
+    (search-forward :command nonincremental-search-forward
+                   :help "Search forward for a string"
+                   :image "search"
+                   :insert [t nil])
+
+    (search-replace
+     :image ["search-replace" toolbar-replace-icon]
+     :command [query-replace toolbar-replace]
+     :help ["Replace string interactively, ask about each occurrence"
+           "Search & Replace"])
+
+    (print-buffer :image ["print" toolbar-printer-icon]
+                 :command [print-buffer toolbar-print]
+                 :help ["Print current buffer with page headings"
+                        "Print buffer"])
+
+    ;; Emacs only
+    (customize :image "preferences"
+              :command customize
+              :help "Edit preferences (customize)"
+              :insert [t nil])
+
+    ;; Emacs only
+    (help :image "help"
+         :command (lambda () (interactive) (popup-menu menu-bar-help-menu))
+         :help "Pop up the Help menu"
+         :insert [t nil])
+
+    ;; Emacs only
+    (kill-buffer :command kill-this-buffer
+                :enable (kill-this-buffer-enabled-p)
+                :help "Discard current buffer"
+                :image "close"
+                :insert [t nil])
+
+    ;; Emacs only
+    (exit-emacs :image "exit"
+               :command save-buffers-kill-emacs
+               :help "Offer to save unsaved buffers, then exit Emacs"
+               :insert [t nil])
+
+    (spell-buffer :image ["spell" toolbar-spell-icon]
+                 :command [ispell-buffer toolbar-ispell]
+                 :help ["Check spelling of selected buffer" "Check spelling"])
+
+    (info :image ["info" toolbar-info-icon]
+         :command [info toolbar-info]
+         :help ["Enter Info, the documentation browser" "Info documentation"])
+
+    ;; XEmacs only
+    (mail :image toolbar-mail-icon
+         :command toolbar-mail
+         :help "Read mail"
+         :insert [nil t])
+
+    ;; XEmacs only
+    (compile :image toolbar-compile-icon
+            :command toolbar-compile
+            :help "Start a compilation"
+            :insert [nil t])
+
+    ;; XEmacs only
+    (debug :image toolbar-debug-icon
+          :command toolbar-debug
+          :help "Start a debugger"
+          :insert [nil t])
+
+    ;; XEmacs only
+    (news :image toolbar-news-icon
+         :command toolbar-news
+         :help "Read news"
+         :insert [nil t]))
+  "A meaning alist with definition of the default buttons.
+The following buttons are available:
+
+* Both Emacs and XEmacs: `open-file', `dired', `save-buffer',
+  `undo', `cut', `copy', `paste', `search-replace', `print-buffer',
+  `spell-buffer', `info'.
+
+* Emacs only: `new-file' (Emacs 22+) `write-file', `search-forward',
+  `customize', `help', `kill-buffer', `exit-emacs'.
+
+* XEmacs only: `mail', `compile', `debug', `news'.
+
+To reproduce the default toolbar in both editors with use as BUTTON
+in `toolbarx-install-toolbar':
+
+\(toolbarx-install-toolbar
+ '([(open-file dired kill-buffer save-buffer write-file undo cut
+               copy paste search-forward print-buffer customize help)
+    (open-file dired save-buffer print-buffer cut copy paste undo
+               spell-buffer search-replace mail info compile debug news)])
+ toolbarx-default-toolbar-meaning-alist)
+
+Ps.: there are more buttons available than suggested in the
+expression above.")
+
+(provide 'toolbar-x)
+
+;;; toolbar-x.el ends here