1 ;;; toolbar-utils.el --- Toolbar utility functions for XEmacs
3 ;; Copyright (C) 1997, 2002 by Free Software Foundation, Inc.
5 ;; Author: Stephen J. Turnbull <stephen@xemacs.org>
6 ;; Jeff Miller <jmiller@smart.net>
8 ;; Last-Modified: 03 December 2002
9 ;; Keywords: gui, services
11 ;; This file is part of XEmacs.
13 ;; XEmacs is free software; you can redistribute it and/or modify it
14 ;; under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
18 ;; XEmacs is distributed in the hope that it will be useful, but
19 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 ;; General Public License for more details.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with XEmacs; see the file COPYING. If not, write to the Free
25 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 ;;; Synched up with: Not in FSF
32 ;; This file contains utilities for managing XEmacs toolbars.
33 ;; It doesn't seem to make sense to provide GNU compatibility here.
34 ;; Instead, it seems much more reliable to reimplement the XEmacs
37 ;; This code is inspired by that written by Jeff Miller, and retains
38 ;; his APIs. However, it is a thorough rewrite.
39 ;; See also edit-toolbar.el by Peter D. Pezaris <pez@dwwc.com>
41 ;;; Change Log: see package ChangeLog.
45 ;; 1. Probably should convert all these to use &keywords?
46 ;; 2. Behavior-like implementation of "mode locales".
47 ;; 3. Implement toolbar-replace-button (by position or content).
48 ;; 5. Make an xemacs-toolbar version to do something sane in GNU.
53 (defun restore-initial-toolbar ()
54 "Restores the default toolbar defined by initial-toolbar-spec.
56 There is also a cache of killed buttons in `button-palette'."
58 (set-specifier default-toolbar initial-toolbar-spec))
62 ;; #### need parent group
63 (defgroup edit-toolbar nil
64 "Tools for interactive editing and non-interactive management of toolbars.")
66 (defcustom toolbar-button-default-position 'right
67 "Default position for adding toolbar buttons on the fly.
69 See `toolbar-add-button-on-the-fly' for possible values and meanings."
73 ;; This needs to be a toolbar descriptor. We should make a function to
74 ;; stick it in a non-default toolbar, with surgery done on the function
75 ;; so that pressing the palette buttons moves them to another toolbar.
76 (defvar button-palette nil
77 "List of buttons cut from toolbars.
79 Note this is actually a toolbar descriptor.")
81 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
85 ;; toolbar-add-button-on-the-fly
87 ;; #### This really belongs in edit-toolbar.el, except that it requires
88 ;; functions from this file and edit-toolbar.el otherwise would not.
90 (defun toolbar-add-button-on-the-fly (description command label
91 &optional position locale)
92 "Add an button at POSITION to the default toolbar of the selected window.
95 #### The return value may change. Tell stephen@xemacs.org what value
96 you think would be (most) useful.
98 DESCRIPTION is a string describing the action, and displayed as help.
99 COMMAND is an interactive command (ie, a symbol with an interactive function
100 definition) implementing the action.
101 LABEL is a string used to label the button.
102 POSITION is an optional position (a non-negative integer, or one of the
103 symbols 'left, 'right, or 'extreme-right, see `toolbar-add-item').
104 LOCALE is an optional locale, defaulting to the current buffer. If current-
105 buffer-only is not what you want, and you don't understand specifier locales,
106 use 'global. It's safe and probably does what you want.
108 #### No error checking, use at your own risk."
110 (interactive (list (read-string "Description: ")
111 (read-command "Command: ")
112 (read-string "Button label: ")
113 (read (completing-read
115 (lambda (x junk morejunk)
116 (cond ((let ((y (read x)))
120 ((try-completion x '(("left")
123 nil nil nil nil "right"))
126 (let ((domain (selected-window))
127 (button (toolbar-new-button label command description)))
128 (set-specifier default-toolbar
129 (toolbar-add-item (specifier-instance default-toolbar
133 (or locale (current-buffer))))
136 ;; toolbar-add-kbd-macro
139 (defun toolbar-add-kbd-macro (mac icon is-file)
140 "Add a button invoking a keyboard macro to the toolbar of the current buffer.
141 The button is added at the end of the left group.
143 MAC is a keyboard macro name, or the empty string or nil to use a copy of
144 the last keyboard macro defined.
145 ICON is a string specifying the icon to be used. If IS-FILE is non-nil,
146 it is interpreted as the name of an image file, and searched for using
147 `locate-data-file'. Otherwise it is used verbatim as a label.
149 Interactively, prompts for the macro name MAC and an ICON. IS-FILE is
150 non-nil if a prefix argument was used.
152 Warning: the interpretation of the prefix argument is likely to change."
154 (list (read-string "Macro name (RET to use last defined macro): ")
155 (read-string (format "Icon %s: "
156 (if current-prefix-arg "image" "label")))
158 (let* ((locale (current-buffer))
160 (mac (if (or (null mac) (= 0 (length mac)))
161 ;; is there an argument pro or con to copy-sequence?
164 ;; #### this actually probably just works, and we don't even need
165 ;; the IS-FILE argument at all
167 (error 'unimplemented "We don't do image icons yet")
169 ;; this is actually the help string
170 (label (cond ((and is-file mac))
171 ;; #### should truncate image file extension if any
172 ((and (stringp icon) (< 0 (length icon))) icon)
174 (button (toolbar-new-button icon
177 (execute-kbd-macro ,mac))
179 ;; #### need to abstract this
180 ;; something like (toolbar-update-toolbar TOOLBAR ITEM POS LOCALE)
181 (set-specifier default-toolbar
182 (toolbar-add-item (cdadar
183 (specifier-spec-list default-toolbar
190 ;; #### this should be a behavior
192 (defun toolbar-add-execute-macro-button ()
193 "Add a button to the global toolbar to execute the last keyboard macro.
195 Unlike `toolbar-add-kbd-macro', this does not copy the macro. The macro
196 executed will change with redefinitions.
198 Due to a simple implementation, this button will not appear in buffers with
199 local toolbars if invoked after the toolbar is installed. If you like this
200 button, it's probably best to invoke this function in your init file."
202 (let* ((locale 'global)
203 (spec (cdadar (specifier-spec-list default-toolbar locale)))
204 (button (toolbar-new-button "LastMac"
205 #'toolbar-execute-last-kbd-macro
206 "Execute last defined keyboard macro")))
207 (set-specifier default-toolbar (toolbar-add-item spec button) locale)))
209 ;; if this is defsubst, XEmacs 21.4.10 crashes?
210 (defun toolbar-execute-last-kbd-macro ()
212 (execute-kbd-macro last-kbd-macro))
215 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
217 ;; Lisp API functions:
219 ;; adding items to a toolbar, with convenience functions for constructing
220 ;; valid buttons, spacers, and toolbar descriptors
223 (defun toolbar-update-toolbar (item &optional toolbar position locale)
224 "Use ITEM to update TOOLBAR at POSITION in LOCALE.
226 ITEM is a toolbar button or spacer specification (eg, from
227 `toolbar-new-button' or `toolbar-new-spacer').
228 Optional TOOLBAR is a toolbar specifier object. It defaults to the value
229 of `default-toolbar'.
230 Optional POSITION is a non-negative integer or a symbol (for valid values,
231 see `toolbar-add-item'). The default is 'right.
232 Optional LOCALE is a specifier locale. The default is 'global. (This
233 default is not yet set in stone; it's best not to depend on it.)
235 This is a convenience function for helper applications or minor modes that
236 would like to add a small number of buttons to an existing toolbar. For
237 constructing toolbars from scratch, use list and vector primitives, or
239 (setq toolbar (or toolbar default-toolbar))
240 (setq position (or position 'right))
241 (setq locale (or locale 'global))
242 (let ((spec (cdadar (specifier-spec-list toolbar locale))))
243 (set-specifier toolbar (toolbar-add-item spec item position) locale)))
247 (defun toolbar-add-item (toolbar-spec item &optional position)
248 "Add ITEM to TOOLBAR-SPEC at POSITION, and return TOOLBAR-SPEC.
249 Uses functions that alter list structure.
251 TOOLBAR-SPEC is a toolbar descriptor (eg, from `toolbar-new-toolbar').
252 ITEM is a toolbar button or spacer specification (eg, from
253 `toolbar-new-button' or `toolbar-new-spacer').
254 Optional POSITION is a non-negative integer, with 0 being the extreme left and
255 \(length TOOLBAR-SPEC) the extreme right. The symbols 'left, 'right, and
256 'extreme-right are also accepted. 'left is synonymous with 0. 'right places
257 ITEM at the right end of the left group of buttons. 'extreme-right places
258 ITEM at the extreme right of the toolbar, creating a right group if one
261 #### This function does not yet support inserting the group delimiter nil
264 POSITION may be greater than (length TOOLBAR-SPEC), in which case it is
265 truncated to (length TOOLBAR-SPEC). Note that (length TOOLBAR-SPEC) is not
266 synonymous with either 'right or 'extreme-right."
267 (check-valid-instantiator toolbar-spec 'toolbar)
268 (check-toolbar-button-syntax item)
269 (unless position (setq position toolbar-button-default-position))
270 (cond ((eq position 'right)
271 (let ((tail (memq nil toolbar-spec)))
273 (nconc toolbar-spec (list item))
274 ;; is this overly tricky?
276 (setcdr tail (cons nil (cdr tail)))
278 ((or (eq position 'left) (eq position 0))
279 (cons item toolbar-spec))
280 ((eq position 'extreme-right)
281 (nconc toolbar-spec (if (memq nil toolbar-spec)
284 ((and (integerp position) (< 0 position))
285 (let ((tail (nthcdr 1 toolbar-spec)))
287 (nconc toolbar-spec (list item))
288 ;; is this overly tricky?
289 (setcdr tail (cons (car tail) (cdr tail)))
292 (t (error 'wrong-type-argument
297 (defun toolbar-new-button (icon-spec command help-string
298 &optional initially-disabled)
299 "Return a checked toolbar button specification.
301 ICON-SPEC should be a list of glyphs (from `make-glyph'), a glyph, or a
302 string to use as the button's icon. If a string or single glyph, it will
303 be used for the button-up glyph. If a list, it may contain 1 to 6 glyphs,
304 which XEmacs will use for button up, button down, button disabled, button
305 up with caption, button down with caption, and button disabled with caption,
306 in that order. Missing or nil glyphs will be defaulted. (#### Strings as
307 list elements are not supported but could be.)
308 COMMAND is the (interactive) command to invoke when the button is pressed.
309 HELP-STRING is a string briefly describing the command, displayed in the
310 echo area or as balloon help when the pointer enters the button.
311 Optional argument INITIALLY-DISABLED, if non-nil, specifies that the button
312 should initially be disabled.
314 See `default-toolbar' or the Lispref (node toolbars) for more information."
315 (setq icon-spec (cond ((glyphp icon-spec) (list icon-spec))
316 ((stringp icon-spec) (list (make-glyph icon-spec)))
317 ((listp icon-spec) icon-spec)))
319 (vector icon-spec command (not initially-disabled) help-string)))
320 (check-toolbar-button-syntax button-spec)
324 (defun toolbar-new-spacer (style &optional size)
325 "Returns a checked toolbar spacer \"button\".
327 STYLE is one of the symbols '2d or '3d, indicating whether the area is
328 displayed without shadows (giving it a flat appearance), or with shadows
329 \(giving it a raised, 3-d appearance). There is no default.
330 #### We may set a default style. Tell stephen@xemacs.org which you use.
331 SIZE specifies the length, in pixels, of the blank area. If omitted,
332 it defaults to a device-specific value (8 pixels for X devices)."
333 (let* ((style (or style '2d))
335 (vector :style style :size size)
336 (vector :style style))))
337 (check-toolbar-button-syntax button)
341 (defun make-toolbar-instantiator (&optional toolbar-spec domain)
342 "Return a checked toolbar instantiator, a list of vectors.
344 TOOLBAR-SPEC may be a list of buttons (ie, a toolbar descriptor, see
345 `default-toolbar'), a toolbar specifier object, or a symbol whose value is
346 a toolbar specifier object. If a toolbar specifier object or variable
347 containing one, the specification for DOMAIN is used. If non-nil, DOMAIN
348 must be a window, a frame, or a device, otherwise it defaults to the selected
349 window (see `specifier-instance'). The list thus generated is checked and
352 If TOOLBAR-SPEC is a list, it is copied; it is safe to pass other packages'
353 toolbar initializers to this function. However, you probably do not want
354 to change any of the objects in the returned specification. They are
355 returned as is, not copied.
357 See `default-toolbar' or the Lispref (node toolbars) for more information."
359 (cond ((null toolbar-spec) nil)
361 ((symbolp toolbar-spec)
362 (specifier-instance (symbol-value toolbar-spec) domain))
363 ((specifierp toolbar-spec)
364 (specifier-instance toolbar-spec domain))
365 ((listp toolbar-spec) (copy-sequence toolbar-spec))
366 (t toolbar-spec))) ; errors by check-valid-instantiator
367 (check-valid-instantiator toolbar-spec 'toolbar)
370 ;; removing buttons from a toolbar
373 (defun toolbar-kill-item-pos (pos &optional toolbar locale)
374 "Kill the item at position POS of TOOLBAR in LOCALE.
375 Killed buttons are prepended to `button-palette'.
377 LOCALE defaults to 'global. If there are multiple specs for LOCALE, take
380 This function currently does not accept symbolic positions a la
381 `toolbar-add-item'. Use `toolbar-find-item' to locate whole buttons and
382 spacers, and `toolbar-find-button' to locate buttons by characteristics.
383 See also `toolbar-find-button-by-icon', `toolbar-find-button-by-command',
384 and `toolbar-find-button-by-help-string'."
385 (setq locale (or locale 'global))
386 (setq toolbar (or toolbar default-toolbar))
387 (let ((spec (cdadar (specifier-spec-list toolbar locale))))
388 (setq button-palette (cons (nth pos spec) button-palette))
390 (setq spec (cdr spec))
391 (setcdr (nthcdr (1- pos) spec)
392 (nthcdr (1+ pos) spec)))
393 (set-specifier toolbar spec locale)))
395 ;; locating buttons by their content, returning a position
398 (defun toolbar-find-button (item &optional toolbar locale)
399 "Return the position of a button containing ITEM in its specification.
401 ITEM may specify a button, spacer, icon, command, help string, or nil.
402 If ITEM is nil, find the separator between the group of buttons to be left
403 justified, and the group to be right justified. (Distinctions among the
404 various \"search key types\" are somewhat heuristic but are probably
405 reliable enough to use in library code.)
407 If TOOLBAR is non-nil, search it; otherwise search the default toolbar.
408 If LOCALE is non-nil, get TOOLBAR's descriptor in that locale, otherwise
409 use the global locale."
410 (setq toolbar (or toolbar default-toolbar))
411 (setq locale (or locale 'global))
412 (cond ((or (null item) (vectorp item)) (toolbar-find-item item))
413 ((commandp item) (toolbar-find-button-by-command item toolbar locale))
415 (toolbar-find-button-by-help-string item toolbar locale))
416 (t (toolbar-find-button-by-icon item toolbar locale))))
419 (defun toolbar-find-item (item &optional toolbar locale)
420 "Return the position of ITEM, a button, spacer, or nil.
421 TOOLBAR and LOCALE determine the descriptor to be searched.
423 If ITEM is nil, find the separator between the group of buttons to be left
424 justified, and the group to be right justified.
425 If there are several matching items, the first is returned. If none is
430 (if (equal (car toolbar) item) (throw 'found pos))
432 (setq toolbar (cdr toolbar)))
435 ;; internals -- if you think these should be autoloaded, let me know
436 (defun toolbar-find-button-by-icon (icon &optional toolbar locale)
437 "Return the position of a button with icon ICON.
438 ICON must be a list of glyphs or a symbols whose value is a list of glyphs.
439 TOOLBAR and LOCALE determine the descriptor to be searched.
441 If there are several matching buttons, the first is returned."
442 (flet ((thunk (x) (if (symbolp x) (symbol-value x) x)))
443 (toolbar-find-button-by-element icon 0 toolbar locale #'thunk)))
445 (defun toolbar-find-button-by-command (cmd &optional toolbar locale)
446 "Return the position of a button invoking command CMD.
447 TOOLBAR and LOCALE determine the descriptor to be searched.
449 If there are several matching buttons, the first is returned."
450 (toolbar-find-button-by-element cmd 2 toolbar locale))
452 (defun toolbar-find-button-by-help-string (str &optional toolbar locale)
453 "Return the position of a button with help-string STR.
454 TOOLBAR and LOCALE determine the descriptor to be searched.
456 If there are several matching buttons, the first is returned.
457 This function will not find spacers."
458 (toolbar-find-button-by-element str 3 toolbar locale))
460 (defun toolbar-find-button-by-element (object index toolbar locale
462 "Return the position of a button containing OBJECT in element INDEX.
463 TOOLBAR and LOCALE determine the descriptor to be searched.
465 Optional argument THUNK is a function of one argument which is used to
466 normalize OBJECT for comparison.
468 If there are several matching buttons, the first is returned.
469 This function will not find spacers."
470 (setq toolbar (or toolbar default-toolbar))
471 (setq locale (or locale 'global))
472 (if thunk (setq object (funcall thunk object)))
473 (let ((desc (cdadar (specifier-spec-list toolbar locale)))
475 ;; #### rewrite this as a catch ... throw
476 (while (not (equal object (let ((el (aref (car desc) index)))
477 (if thunk (funcall thunk el) el))))
478 (setq desc (cdr desc))
480 (if toolbar pos nil)))
482 (provide 'toolbar-utils)
484 ;;; toolbar-utils.el ends here