]> code.delx.au - gnu-emacs/blob - lisp/tool-bar.el
Choose images dynamically.
[gnu-emacs] / lisp / tool-bar.el
1 ;;; tool-bar.el --- setting up the tool bar
2 ;;
3 ;; Copyright (C) 2000, 2001, 2002, 2003, 2004,
4 ;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5 ;;
6 ;; Author: Dave Love <fx@gnu.org>
7 ;; Keywords: mouse frames
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software: you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation, either version 3 of the License, or
14 ;; (at your option) any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23
24 ;;; Commentary:
25
26 ;; Provides `tool-bar-mode' to control display of the tool-bar and
27 ;; bindings for the global tool bar with convenience functions
28 ;; `tool-bar-add-item' and `tool-bar-add-item-from-menu'.
29
30 ;; The normal global binding for [tool-bar] (below) uses the value of
31 ;; `tool-bar-map' as the actual keymap to define the tool bar. Modes
32 ;; may either bind items under the [tool-bar] prefix key of the local
33 ;; map to add to the global bar or may set `tool-bar-map'
34 ;; buffer-locally to override it. (Some items are removed from the
35 ;; global bar in modes which have `special' as their `mode-class'
36 ;; property.)
37
38 ;; Todo: Somehow make tool bars easily customizable by the naive?
39
40 ;;; Code:
41
42 ;; The autoload cookie doesn't work when preloading.
43 ;; Deleting it means invoking this command won't work
44 ;; when you are on a tty. I hope that won't cause too much trouble -- rms.
45 (define-minor-mode tool-bar-mode
46 "Toggle use of the tool bar.
47 With numeric ARG, display the tool bar if and only if ARG is positive.
48
49 See `tool-bar-add-item' and `tool-bar-add-item-from-menu' for
50 conveniently adding tool bar items."
51 :init-value nil
52 :global t
53 :group 'mouse
54 :group 'frames
55 (and (display-images-p)
56 (modify-all-frames-parameters (list (cons 'tool-bar-lines
57 (if tool-bar-mode 1 0))))
58 (if (and tool-bar-mode
59 (display-graphic-p))
60 (tool-bar-setup))))
61
62 ;;;###autoload
63 ;; Used in the Show/Hide menu, to have the toggle reflect the current frame.
64 (defun toggle-tool-bar-mode-from-frame (&optional arg)
65 "Toggle tool bar on or off, based on the status of the current frame.
66 See `tool-bar-mode' for more information."
67 (interactive (list (or current-prefix-arg 'toggle)))
68 (if (eq arg 'toggle)
69 (tool-bar-mode (if (> (frame-parameter nil 'tool-bar-lines) 0) 0 1))
70 (tool-bar-mode arg)))
71
72 ;;;###autoload
73 ;; We want to pretend the toolbar by standard is on, as this will make
74 ;; customize consider disabling the toolbar a customization, and save
75 ;; that. We could do this for real by setting :init-value above, but
76 ;; that would turn on the toolbar in MS Windows where it is currently
77 ;; useless, and it would overwrite disabling the tool bar from X
78 ;; resources. If anyone want to implement this in a cleaner way,
79 ;; please do so.
80 ;; -- Per Abrahamsen <abraham@dina.kvl.dk> 2002-02-21.
81 (put 'tool-bar-mode 'standard-value '(t))
82
83 (defvar tool-bar-map (make-sparse-keymap)
84 "Keymap for the tool bar.
85 Define this locally to override the global tool bar.")
86
87 (global-set-key [tool-bar]
88 '(menu-item "tool bar" ignore
89 :filter tool-bar-make-keymap))
90
91 (defun tool-bar-make-keymap (&optional ignore)
92 "Generate an actual keymap from `tool-bar-map'.
93 Its main job is to figure out which images to use based on the display's
94 color capability and based on the available image libraries."
95 (mapcar (lambda (bind)
96 (let (image-exp)
97 (when (and (eq (car-safe (cdr-safe bind)) 'menu-item)
98 (setq image-exp (plist-get bind :image))
99 (consp image-exp)
100 (not (eq (car image-exp) 'image))
101 (fboundp (car image-exp)))
102 (if (not (display-images-p))
103 (setq bind nil)
104 (let ((image (eval image-exp)))
105 (unless (image-mask-p image)
106 (setq image (append image '(:mask heuristic))))
107 (setq bind (copy-sequence bind))
108 (plist-put bind :image image))))
109 bind))
110 tool-bar-map))
111
112 (defconst tool-bar-find-image-cache (make-hash-table :weakness t :test 'equal))
113
114 (defun tool-bar-find-image (specs)
115 "Like `find-image' but with caching."
116 (or (gethash specs tool-bar-find-image-cache)
117 (puthash specs (find-image specs) tool-bar-find-image-cache)))
118
119 ;;;###autoload
120 (defun tool-bar-add-item (icon def key &rest props)
121 "Add an item to the tool bar.
122 ICON names the image, DEF is the key definition and KEY is a symbol
123 for the fake function key in the menu keymap. Remaining arguments
124 PROPS are additional items to add to the menu item specification. See
125 Info node `(elisp)Tool Bar'. Items are added from left to right.
126
127 ICON is the base name of a file containing the image to use. The
128 function will first try to use low-color/ICON.xpm if display-color-cells
129 is less or equal to 256, then ICON.xpm, then ICON.pbm, and finally
130 ICON.xbm, using `find-image'.
131
132 Use this function only to make bindings in the global value of `tool-bar-map'.
133 To define items in any other map, use `tool-bar-local-item'."
134 (apply 'tool-bar-local-item icon def key tool-bar-map props))
135
136 ;;;###autoload
137 (defun tool-bar-local-item (icon def key map &rest props)
138 "Add an item to the tool bar in map MAP.
139 ICON names the image, DEF is the key definition and KEY is a symbol
140 for the fake function key in the menu keymap. Remaining arguments
141 PROPS are additional items to add to the menu item specification. See
142 Info node `(elisp)Tool Bar'. Items are added from left to right.
143
144 ICON is the base name of a file containing the image to use. The
145 function will first try to use low-color/ICON.xpm if `display-color-cells'
146 is less or equal to 256, then ICON.xpm, then ICON.pbm, and finally
147 ICON.xbm, using `find-image'."
148 (let* ((fg (face-attribute 'tool-bar :foreground))
149 (bg (face-attribute 'tool-bar :background))
150 (colors (nconc (if (eq fg 'unspecified) nil (list :foreground fg))
151 (if (eq bg 'unspecified) nil (list :background bg))))
152 (xpm-spec (list :type 'xpm :file (concat icon ".xpm")))
153 (xpm-lo-spec (if (> (display-color-cells) 256)
154 nil
155 (list :type 'xpm :file
156 (concat "low-color/" icon ".xpm"))))
157 (pbm-spec (append (list :type 'pbm :file
158 (concat icon ".pbm")) colors))
159 (xbm-spec (append (list :type 'xbm :file
160 (concat icon ".xbm")) colors))
161 (image-exp `(tool-bar-find-image
162 (if (display-color-p)
163 ',(list xpm-lo-spec xpm-spec pbm-spec xbm-spec)
164 ',(list pbm-spec xbm-spec xpm-lo-spec xpm-spec)))))
165
166 (define-key-after map (vector key)
167 `(menu-item ,(symbol-name key) ,def :image ,image-exp ,@props))))
168
169 ;;;###autoload
170 (defun tool-bar-add-item-from-menu (command icon &optional map &rest props)
171 "Define tool bar binding for COMMAND in keymap MAP using the given ICON.
172 This makes a binding for COMMAND in `tool-bar-map', copying its
173 binding from the menu bar in MAP (which defaults to `global-map'), but
174 modifies the binding by adding an image specification for ICON. It
175 finds ICON just like `tool-bar-add-item'. PROPS are additional
176 properties to add to the binding.
177
178 MAP must contain appropriate binding for `[menu-bar]' which holds a keymap.
179
180 Use this function only to make bindings in the global value of `tool-bar-map'.
181 To define items in any other map, use `tool-bar-local-item-from-menu'."
182 (apply 'tool-bar-local-item-from-menu command icon
183 (default-value 'tool-bar-map) map props))
184
185 ;;;###autoload
186 (defun tool-bar-local-item-from-menu (command icon in-map &optional from-map &rest props)
187 "Define local tool bar binding for COMMAND using the given ICON.
188 This makes a binding for COMMAND in IN-MAP, copying its binding from
189 the menu bar in FROM-MAP (which defaults to `global-map'), but
190 modifies the binding by adding an image specification for ICON. It
191 finds ICON just like `tool-bar-add-item'. PROPS are additional
192 properties to add to the binding.
193
194 FROM-MAP must contain appropriate binding for `[menu-bar]' which
195 holds a keymap."
196 (unless from-map
197 (setq from-map global-map))
198 (let* ((menu-bar-map (lookup-key from-map [menu-bar]))
199 (keys (where-is-internal command menu-bar-map))
200 (fg (face-attribute 'tool-bar :foreground))
201 (bg (face-attribute 'tool-bar :background))
202 (colors (nconc (if (eq fg 'unspecified) nil (list :foreground fg))
203 (if (eq bg 'unspecified) nil (list :background bg))))
204 (xpm-spec (list :type 'xpm :file (concat icon ".xpm")))
205 (xpm-lo-spec (if (> (display-color-cells) 256)
206 nil
207 (list :type 'xpm :file
208 (concat "low-color/" icon ".xpm"))))
209 (pbm-spec (append (list :type 'pbm :file
210 (concat icon ".pbm")) colors))
211 (xbm-spec (append (list :type 'xbm :file
212 (concat icon ".xbm")) colors))
213 (image-exp `(tool-bar-find-image
214 (if (display-color-p)
215 ',(list xpm-lo-spec xpm-spec pbm-spec xbm-spec)
216 ',(list pbm-spec xbm-spec xpm-lo-spec xpm-spec))))
217 submap key)
218 ;; We'll pick up the last valid entry in the list of keys if
219 ;; there's more than one.
220 (dolist (k keys)
221 ;; We're looking for a binding of the command in a submap of
222 ;; the menu bar map, so the key sequence must be two or more
223 ;; long.
224 (if (and (vectorp k)
225 (> (length k) 1))
226 (let ((m (lookup-key menu-bar-map (substring k 0 -1)))
227 ;; Last element in the bound key sequence:
228 (kk (aref k (1- (length k)))))
229 (if (and (keymapp m)
230 (symbolp kk))
231 (setq submap m
232 key kk)))))
233 (when (and (symbolp submap) (boundp submap))
234 (setq submap (eval submap)))
235 (let ((defn (assq key (cdr submap))))
236 (if (eq (cadr defn) 'menu-item)
237 (define-key-after in-map (vector key)
238 (append (cdr defn) (list :image image-exp) props))
239 (setq defn (cdr defn))
240 (define-key-after in-map (vector key)
241 (let ((rest (cdr defn)))
242 ;; If the rest of the definition starts
243 ;; with a list of menu cache info, get rid of that.
244 (if (and (consp rest) (consp (car rest)))
245 (setq rest (cdr rest)))
246 (append `(menu-item ,(car defn) ,rest)
247 (list :image image-exp) props)))))))
248
249 ;;; Set up some global items. Additions/deletions up for grabs.
250
251 (defvar tool-bar-setup nil
252 "Set to t if the tool-bar has been set up by `tool-bar-setup'.")
253
254 (defun tool-bar-setup (&optional frame)
255 (unless tool-bar-setup
256 (with-selected-frame (or frame (selected-frame))
257 ;; People say it's bad to have EXIT on the tool bar, since users
258 ;; might inadvertently click that button.
259 ;;(tool-bar-add-item-from-menu 'save-buffers-kill-emacs "exit")
260 (tool-bar-add-item-from-menu 'find-file "new")
261 (tool-bar-add-item-from-menu 'menu-find-file-existing "open")
262 (tool-bar-add-item-from-menu 'dired "diropen")
263 (tool-bar-add-item-from-menu 'kill-this-buffer "close")
264 (tool-bar-add-item-from-menu 'save-buffer "save" nil
265 :visible '(or buffer-file-name
266 (not (eq 'special
267 (get major-mode
268 'mode-class)))))
269 (tool-bar-add-item-from-menu 'write-file "saveas" nil
270 :visible '(or buffer-file-name
271 (not (eq 'special
272 (get major-mode
273 'mode-class)))))
274 (tool-bar-add-item-from-menu 'undo "undo" nil
275 :visible '(not (eq 'special (get major-mode
276 'mode-class))))
277 (tool-bar-add-item-from-menu (lookup-key menu-bar-edit-menu [cut])
278 "cut" nil
279 :visible '(not (eq 'special (get major-mode
280 'mode-class))))
281 (tool-bar-add-item-from-menu (lookup-key menu-bar-edit-menu [copy])
282 "copy")
283 (tool-bar-add-item-from-menu (lookup-key menu-bar-edit-menu [paste])
284 "paste" nil
285 :visible '(not (eq 'special (get major-mode
286 'mode-class))))
287 (tool-bar-add-item-from-menu 'nonincremental-search-forward "search")
288 ;;(tool-bar-add-item-from-menu 'ispell-buffer "spell")
289
290 ;; There's no icon appropriate for News and we need a command rather
291 ;; than a lambda for Read Mail.
292 ;;(tool-bar-add-item-from-menu 'compose-mail "mail/compose")
293
294 (tool-bar-add-item-from-menu 'print-buffer "print")
295
296 ;; tool-bar-add-item-from-menu itself operates on
297 ;; (default-value 'tool-bar-map), but when we don't use that function,
298 ;; we must explicitly operate on the default value.
299
300 (let ((tool-bar-map (default-value 'tool-bar-map)))
301 (tool-bar-add-item "preferences" 'customize 'customize
302 :help "Edit preferences (customize)")
303
304 (tool-bar-add-item "help" (lambda ()
305 (interactive)
306 (popup-menu menu-bar-help-menu))
307 'help
308 :help "Pop up the Help menu"))
309 (setq tool-bar-setup t))))
310
311
312 (provide 'tool-bar)
313 ;; arch-tag: 15f30f0a-d0d7-4d50-bbb7-f48fd0c8582f
314 ;;; tool-bar.el ends here