tool-bar.el 12.5 KB
Newer Older
1
;;; tool-bar.el --- setting up the tool bar
Glenn Morris's avatar
Glenn Morris committed
2

3
;; Copyright (C) 2000-2011  Free Software Foundation, Inc.
Glenn Morris's avatar
Glenn Morris committed
4

5 6
;; Author: Dave Love <fx@gnu.org>
;; Keywords: mouse frames
7
;; Package: emacs
8 9 10

;; This file is part of GNU Emacs.

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 14
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
15 16 17 18 19 20 21

;; GNU Emacs 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
22
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
23 24 25

;;; Commentary:

Richard M. Stallman's avatar
Richard M. Stallman committed
26
;; Provides `tool-bar-mode' to control display of the tool-bar and
27 28 29
;; bindings for the global tool bar with convenience functions
;; `tool-bar-add-item' and `tool-bar-add-item-from-menu'.

Dave Love's avatar
Dave Love committed
30
;; The normal global binding for [tool-bar] (below) uses the value of
Dave Love's avatar
Dave Love committed
31 32 33
;; `tool-bar-map' as the actual keymap to define the tool bar.  Modes
;; may either bind items under the [tool-bar] prefix key of the local
;; map to add to the global bar or may set `tool-bar-map'
Eli Zaretskii's avatar
Eli Zaretskii committed
34
;; buffer-locally to override it.  (Some items are removed from the
Dave Love's avatar
Dave Love committed
35
;; global bar in modes which have `special' as their `mode-class'
Juanma Barranquero's avatar
Juanma Barranquero committed
36
;; property.)
Dave Love's avatar
Dave Love committed
37

38 39
;; Todo: Somehow make tool bars easily customizable by the naive?

40 41
;;; Code:

42 43 44
;; The autoload cookie doesn't work when preloading.
;; Deleting it means invoking this command won't work
;; when you are on a tty.  I hope that won't cause too much trouble -- rms.
45 46
(define-minor-mode tool-bar-mode
  "Toggle use of the tool bar.
Gerd Moellmann's avatar
Gerd Moellmann committed
47
With numeric ARG, display the tool bar if and only if ARG is positive.
48 49 50

See `tool-bar-add-item' and `tool-bar-add-item-from-menu' for
conveniently adding tool bar items."
51
  :init-value t
52
  :global t
53 54
  ;; It's defined in C/cus-start, this stops the d-m-m macro defining it again.
  :variable tool-bar-mode
55 56
  (let ((val (if tool-bar-mode 1 0)))
    (dolist (frame (frame-list))
57 58 59 60 61 62 63 64 65 66 67
      (set-frame-parameter frame 'tool-bar-lines val))
    ;; If the user has given `default-frame-alist' a `tool-bar-lines'
    ;; parameter, replace it.
    (if (assq 'tool-bar-lines default-frame-alist)
	(setq default-frame-alist
	      (cons (cons 'tool-bar-lines val)
		    (assq-delete-all 'tool-bar-lines
				     default-frame-alist)))))
  (and tool-bar-mode
       (= 1 (length (default-value 'tool-bar-map))) ; not yet setup
       (tool-bar-setup)))
68

69
;;;###autoload
70 71 72 73 74 75 76 77 78
;; Used in the Show/Hide menu, to have the toggle reflect the current frame.
(defun toggle-tool-bar-mode-from-frame (&optional arg)
  "Toggle tool bar on or off, based on the status of the current frame.
See `tool-bar-mode' for more information."
  (interactive (list (or current-prefix-arg 'toggle)))
  (if (eq arg 'toggle)
      (tool-bar-mode (if (> (frame-parameter nil 'tool-bar-lines) 0) 0 1))
    (tool-bar-mode arg)))

Dave Love's avatar
Dave Love committed
79 80 81 82 83
(defvar tool-bar-map (make-sparse-keymap)
  "Keymap for the tool bar.
Define this locally to override the global tool bar.")

(global-set-key [tool-bar]
84
		`(menu-item ,(purecopy "tool bar") ignore
Stefan Monnier's avatar
Stefan Monnier committed
85 86
			    :filter tool-bar-make-keymap))

87 88
(declare-function image-mask-p "image.c" (spec &optional frame))

89 90
(defconst tool-bar-keymap-cache (make-hash-table :weakness t :test 'equal))

Stefan Monnier's avatar
Stefan Monnier committed
91 92 93 94
(defun tool-bar-make-keymap (&optional ignore)
  "Generate an actual keymap from `tool-bar-map'.
Its main job is to figure out which images to use based on the display's
color capability and based on the available image libraries."
95 96 97 98 99 100
  (let ((key (cons (frame-terminal) tool-bar-map)))
    (or (gethash key tool-bar-keymap-cache)
	(puthash key (tool-bar-make-keymap-1) tool-bar-keymap-cache))))

(defun tool-bar-make-keymap-1 ()
  "Generate an actual keymap from `tool-bar-map', without caching."
Stefan Monnier's avatar
Stefan Monnier committed
101
  (mapcar (lambda (bind)
102
            (let (image-exp plist)
Stefan Monnier's avatar
Stefan Monnier committed
103
              (when (and (eq (car-safe (cdr-safe bind)) 'menu-item)
104 105 106 107 108 109 110 111 112 113 114
			 ;; For the format of menu-items, see node
			 ;; `Extended Menu Items' in the Elisp manual.
			 (setq plist (nthcdr (if (consp (nth 4 bind)) 5 4)
					     bind))
			 (setq image-exp (plist-get plist :image))
			 (consp image-exp)
			 (not (eq (car image-exp) 'image))
			 (fboundp (car image-exp)))
		(if (not (display-images-p))
		    (setq bind nil)
		  (let ((image (eval image-exp)))
115
		    (unless (and image (image-mask-p image))
116 117 118 119 120 121
		      (setq image (append image '(:mask heuristic))))
		    (setq bind (copy-sequence bind)
			  plist (nthcdr (if (consp (nth 4 bind)) 5 4)
					bind))
		    (plist-put plist :image image))))
	      bind))
Stefan Monnier's avatar
Stefan Monnier committed
122 123
	  tool-bar-map))

124
;;;###autoload
Dave Love's avatar
Dave Love committed
125
(defun tool-bar-add-item (icon def key &rest props)
126 127
  "Add an item to the tool bar.
ICON names the image, DEF is the key definition and KEY is a symbol
Dave Love's avatar
Dave Love committed
128 129 130
for the fake function key in the menu keymap.  Remaining arguments
PROPS are additional items to add to the menu item specification.  See
Info node `(elisp)Tool Bar'.  Items are added from left to right.
131

Eli Zaretskii's avatar
Eli Zaretskii committed
132
ICON is the base name of a file containing the image to use.  The
133
function will first try to use low-color/ICON.xpm if `display-color-cells'
Jan Djärv's avatar
Jan Djärv committed
134
is less or equal to 256, then ICON.xpm, then ICON.pbm, and finally
Eli Zaretskii's avatar
Eli Zaretskii committed
135
ICON.xbm, using `find-image'.
Dave Love's avatar
Dave Love committed
136

137 138 139 140
Use this function only to make bindings in the global value of `tool-bar-map'.
To define items in any other map, use `tool-bar-local-item'."
  (apply 'tool-bar-local-item icon def key tool-bar-map props))

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
(defun tool-bar--image-expression (icon)
  "Return an expression that evaluates to an image spec for ICON."
  (let* ((fg (face-attribute 'tool-bar :foreground))
	 (bg (face-attribute 'tool-bar :background))
	 (colors (nconc (if (eq fg 'unspecified) nil (list :foreground fg))
			(if (eq bg 'unspecified) nil (list :background bg))))
	 (xpm-spec (list :type 'xpm :file (concat icon ".xpm")))
	 (xpm-lo-spec (list :type 'xpm :file
			    (concat "low-color/" icon ".xpm")))
	 (pbm-spec (append (list :type 'pbm :file
                                 (concat icon ".pbm")) colors))
	 (xbm-spec (append (list :type 'xbm :file
                                 (concat icon ".xbm")) colors)))
    `(find-image (cond ((not (display-color-p))
			',(list pbm-spec xbm-spec xpm-lo-spec xpm-spec))
		       ((< (display-color-cells) 256)
			',(list xpm-lo-spec xpm-spec pbm-spec xbm-spec))
		       (t
			',(list xpm-spec pbm-spec xbm-spec))))))

161 162 163 164 165 166 167 168 169
;;;###autoload
(defun tool-bar-local-item (icon def key map &rest props)
  "Add an item to the tool bar in map MAP.
ICON names the image, DEF is the key definition and KEY is a symbol
for the fake function key in the menu keymap.  Remaining arguments
PROPS are additional items to add to the menu item specification.  See
Info node `(elisp)Tool Bar'.  Items are added from left to right.

ICON is the base name of a file containing the image to use.  The
Stefan Monnier's avatar
Stefan Monnier committed
170
function will first try to use low-color/ICON.xpm if `display-color-cells'
Jan Djärv's avatar
Jan Djärv committed
171
is less or equal to 256, then ICON.xpm, then ICON.pbm, and finally
172
ICON.xbm, using `find-image'."
173
  (let* ((image-exp (tool-bar--image-expression icon)))
Stefan Monnier's avatar
Stefan Monnier committed
174 175
    (define-key-after map (vector key)
      `(menu-item ,(symbol-name key) ,def :image ,image-exp ,@props))))
176

177
;;;###autoload
178
(defun tool-bar-add-item-from-menu (command icon &optional map &rest props)
Nick Roberts's avatar
Nick Roberts committed
179
  "Define tool bar binding for COMMAND in keymap MAP using the given ICON.
180 181 182 183 184 185 186 187 188
This makes a binding for COMMAND in `tool-bar-map', copying its
binding from the menu bar in MAP (which defaults to `global-map'), but
modifies the binding by adding an image specification for ICON.  It
finds ICON just like `tool-bar-add-item'.  PROPS are additional
properties to add to the binding.

MAP must contain appropriate binding for `[menu-bar]' which holds a keymap.

Use this function only to make bindings in the global value of `tool-bar-map'.
Nick Roberts's avatar
Nick Roberts committed
189
To define items in any other map, use `tool-bar-local-item-from-menu'."
190 191
  (apply 'tool-bar-local-item-from-menu command icon
	 (default-value 'tool-bar-map) map props))
192 193 194

;;;###autoload
(defun tool-bar-local-item-from-menu (command icon in-map &optional from-map &rest props)
Nick Roberts's avatar
Nick Roberts committed
195
  "Define local tool bar binding for COMMAND using the given ICON.
196 197 198 199 200 201
This makes a binding for COMMAND in IN-MAP, copying its binding from
the menu bar in FROM-MAP (which defaults to `global-map'), but
modifies the binding by adding an image specification for ICON.  It
finds ICON just like `tool-bar-add-item'.  PROPS are additional
properties to add to the binding.

Nick Roberts's avatar
Nick Roberts committed
202 203
FROM-MAP must contain appropriate binding for `[menu-bar]' which
holds a keymap."
204 205 206
  (unless from-map
    (setq from-map global-map))
  (let* ((menu-bar-map (lookup-key from-map [menu-bar]))
207
	 (keys (where-is-internal command menu-bar-map))
208
	 (image-exp (tool-bar--image-expression icon))
209
	 submap key)
Stefan Monnier's avatar
Stefan Monnier committed
210 211
    ;; We'll pick up the last valid entry in the list of keys if
    ;; there's more than one.
212
    ;; FIXME: Aren't they *all* "valid"??  --Stef
Stefan Monnier's avatar
Stefan Monnier committed
213 214 215 216 217 218 219 220 221 222
    (dolist (k keys)
      ;; We're looking for a binding of the command in a submap of
      ;; the menu bar map, so the key sequence must be two or more
      ;; long.
      (if (and (vectorp k)
               (> (length k) 1))
          (let ((m (lookup-key menu-bar-map (substring k 0 -1)))
                ;; Last element in the bound key sequence:
                (kk (aref k (1- (length k)))))
            (if (and (keymapp m)
223
                     (symbolp kk))
Stefan Monnier's avatar
Stefan Monnier committed
224 225
                (setq submap m
                      key kk)))))
226 227 228 229 230 231 232
    (when (and (symbolp submap) (boundp submap))
      (setq submap (eval submap)))
    (let ((defn (assq key (cdr submap))))
      (if (eq (cadr defn) 'menu-item)
          (define-key-after in-map (vector key)
            (append (cdr defn) (list :image image-exp) props))
        (setq defn (cdr defn))
Stefan Monnier's avatar
Stefan Monnier committed
233
        (define-key-after in-map (vector key)
234 235 236 237 238 239 240
          (let ((rest (cdr defn)))
            ;; If the rest of the definition starts
            ;; with a list of menu cache info, get rid of that.
            (if (and (consp rest) (consp (car rest)))
                (setq rest (cdr rest)))
            (append `(menu-item ,(car defn) ,rest)
                    (list :image image-exp) props)))))))
241 242 243

;;; Set up some global items.  Additions/deletions up for grabs.

244
(defun tool-bar-setup ()
245 246
  (setq tool-bar-separator-image-expression
	(tool-bar--image-expression "separator"))
247 248 249
  (tool-bar-add-item-from-menu 'find-file "new" nil :label "New File"
			       :vert-only t)
  (tool-bar-add-item-from-menu 'menu-find-file-existing "open" nil
250
			       :label "Open" :vert-only t)
251 252
  (tool-bar-add-item-from-menu 'dired "diropen" nil :vert-only t)
  (tool-bar-add-item-from-menu 'kill-this-buffer "close" nil :vert-only t)
253
  (tool-bar-add-item-from-menu 'save-buffer "save" nil
254
			       :label "Save")
255
  (define-key-after (default-value 'tool-bar-map) [separator-1] menu-bar-separator)
256
  (tool-bar-add-item-from-menu 'undo "undo" nil)
257
  (define-key-after (default-value 'tool-bar-map) [separator-2] menu-bar-separator)
258
  (tool-bar-add-item-from-menu (lookup-key menu-bar-edit-menu [cut])
259
			       "cut" nil :vert-only t)
260
  (tool-bar-add-item-from-menu (lookup-key menu-bar-edit-menu [copy])
261
			       "copy" nil :vert-only t)
262
  (tool-bar-add-item-from-menu (lookup-key menu-bar-edit-menu [paste])
263
			       "paste" nil :vert-only t)
264
  (define-key-after (default-value 'tool-bar-map) [separator-3] menu-bar-separator)
265 266
  (tool-bar-add-item-from-menu 'isearch-forward "search"
			       nil :label "Search" :vert-only t)
267 268 269 270 271 272
  ;;(tool-bar-add-item-from-menu 'ispell-buffer "spell")

  ;; There's no icon appropriate for News and we need a command rather
  ;; than a lambda for Read Mail.
  ;;(tool-bar-add-item-from-menu 'compose-mail "mail/compose")

273 274 275 276 277 278 279 280
  ;; Help button on a tool bar is rather non-standard...
  ;; (let ((tool-bar-map (default-value 'tool-bar-map)))
  ;;   (tool-bar-add-item "help" (lambda ()
  ;; 				(interactive)
  ;; 				(popup-menu menu-bar-help-menu))
  ;; 		       'help
  ;; 		       :help "Pop up the Help menu"))
)
281

282 283 284 285 286 287 288 289 290 291 292 293 294 295
(if (featurep 'move-toolbar)
    (defcustom tool-bar-position 'top
      "Specify on which side the tool bar shall be.
Possible values are `top' (tool bar on top), `bottom' (tool bar at bottom),
`left' (tool bar on left) and `right' (tool bar on right).
Customize `tool-bar-mode' if you want to show or hide the tool bar."
      :type '(choice (const top)
		     (const bottom)
		     (const left)
		     (const right))
      :group 'frames
      :initialize 'custom-initialize-default
      :set (lambda (sym val)
	     (set-default sym val)
296
	     (modify-all-frames-parameters
297 298
	      (list (cons 'tool-bar-position val))))))

299

Karoly Lorentey's avatar
Karoly Lorentey committed
300
(provide 'tool-bar)
301

302
;;; tool-bar.el ends here