--- /dev/null
+;;; 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