]> code.delx.au - gnu-emacs/blob - lisp/facemenu.el
(menu-bar-file-menu): Changed "Emerge" to "Merge" to agree with the new ediff
[gnu-emacs] / lisp / facemenu.el
1 ;;; facemenu.el -- Create a face menu for interactively adding fonts to text
2 ;; Copyright (c) 1994 Free Software Foundation, Inc.
3
4 ;; Author: Boris Goldowsky <boris@cs.rochester.edu>
5 ;; Keywords: faces
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING. If not, write to
21 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22
23 ;;; Commentary:
24 ;; This file defines a menu of faces (bold, italic, etc) which allows you to
25 ;; set the face used for a region of the buffer. Some faces also have
26 ;; keybindings, which are shown in the menu. Faces with names beginning with
27 ;; "fg:" or "bg:", as in "fg:red", are treated specially.
28 ;; Such faces are assumed to consist only of a foreground (if "fg:") or
29 ;; background (if "bg:") color. They are thus put into the color submenus
30 ;; rather than the general Face submenu. These faces can also be
31 ;; automatically created by selecting the "Other..." menu items in the
32 ;; "Foreground" and "Background" submenus.
33 ;;
34 ;; The menu also contains submenus for indentation and justification-changing
35 ;; commands.
36
37 ;;; Usage:
38 ;; Selecting a face from the menu or typing the keyboard equivalent will
39 ;; change the region to use that face. If you use transient-mark-mode and the
40 ;; region is not active, the face will be remembered and used for the next
41 ;; insertion. It will be forgotten if you move point or make other
42 ;; modifications before inserting or typing anything.
43 ;;
44 ;; Faces can be selected from the keyboard as well.
45 ;; The standard keybindings are M-g (or ESC g) + letter:
46 ;; M-g i = "set italic", M-g b = "set bold", etc.
47
48 ;;; Customization:
49 ;; An alternative set of keybindings that may be easier to type can be set up
50 ;; using "Alt" or "Hyper" keys. This requires that you either have or create
51 ;; an Alt or Hyper key on your keyboard. On my keyboard, there is a key
52 ;; labeled "Alt", but to make it act as an Alt key I have to put this command
53 ;; into my .xinitrc:
54 ;; xmodmap -e "add Mod3 = Alt_L"
55 ;; Or, I can make it into a Hyper key with this:
56 ;; xmodmap -e "keysym Alt_L = Hyper_L" -e "add Mod2 = Hyper_L"
57 ;; Check with local X-perts for how to do it on your system.
58 ;; Then you can define your keybindings with code like this in your .emacs:
59 ;; (setq facemenu-keybindings
60 ;; '((default . [?\H-d])
61 ;; (bold . [?\H-b])
62 ;; (italic . [?\H-i])
63 ;; (bold-italic . [?\H-l])
64 ;; (underline . [?\H-u])))
65 ;; (setq facemenu-keymap global-map)
66 ;; (setq facemenu-key nil)
67 ;; (define-key global-map [?\H-c] 'facemenu-set-foreground) ; set fg color
68 ;; (define-key global-map [?\H-C] 'facemenu-set-background) ; set bg color
69 ;; (require 'facemenu)
70 ;;
71 ;; The order of the faces that appear in the menu and their keybindings can be
72 ;; controlled by setting the variables `facemenu-keybindings' and
73 ;; `facemenu-new-faces-at-end'. List faces that you don't use in documents
74 ;; (eg, `region') in `facemenu-unlisted-faces'.
75
76 ;;; Known Problems:
77 ;; Bold and Italic do not combine to create bold-italic if you select them
78 ;; both, although most other combinations (eg bold + underline + some color)
79 ;; do the intuitive thing.
80 ;;
81 ;; There is at present no way to display what the faces look like in
82 ;; the menu itself.
83 ;;
84 ;; `list-faces-display' shows the faces in a different order than
85 ;; this menu, which could be confusing. I do /not/ sort the list
86 ;; alphabetically, because I like the default order: it puts the most
87 ;; basic, common fonts first.
88 ;;
89 ;; Please send me any other problems, comments or ideas.
90
91 ;;; Code:
92
93 (provide 'facemenu)
94
95 (defvar facemenu-key "\M-g"
96 "Prefix to use for facemenu commands.")
97
98 (defvar facemenu-keybindings
99 '((default . "d")
100 (bold . "b")
101 (italic . "i")
102 (bold-italic . "l") ; {bold} intersect {italic} = {l}
103 (underline . "u"))
104 "Alist of interesting faces and keybindings.
105 Each element is itself a list: the car is the name of the face,
106 the next element is the key to use as a keyboard equivalent of the menu item;
107 the binding is made in facemenu-keymap.
108
109 The faces specifically mentioned in this list are put at the top of
110 the menu, in the order specified. All other faces which are defined,
111 except for those in `facemenu-unlisted-faces', are listed after them,
112 but get no keyboard equivalents.
113
114 If you change this variable after loading facemenu.el, you will need to call
115 `facemenu-update' to make it take effect.")
116
117 (defvar facemenu-new-faces-at-end t
118 "Where in the menu to insert newly-created faces.
119 This should be nil to put them at the top of the menu, or t to put them
120 just before \"Other\" at the end.")
121
122 (defvar facemenu-unlisted-faces
123 '(modeline region secondary-selection highlight scratch-face)
124 "List of faces not to include in the Face menu.
125 Set this before loading facemenu.el, or call `facemenu-update' after
126 changing it.
127
128 If this variable is t, no faces will be added to the menu. This is useful for
129 temporarily turning off the feature that automatically adds faces to the menu
130 when they are created.")
131
132 (defvar facemenu-face-menu
133 (let ((map (make-sparse-keymap "Face")))
134 (define-key map "o" (cons "Other..." 'facemenu-set-face))
135 map)
136 "Menu keymap for faces.")
137 (defalias 'facemenu-face-menu facemenu-face-menu)
138
139 (defvar facemenu-foreground-menu
140 (let ((map (make-sparse-keymap "Foreground Color")))
141 (define-key map "o" (cons "Other" 'facemenu-set-foreground))
142 map)
143 "Menu keymap for foreground colors.")
144 (defalias 'facemenu-foreground-menu facemenu-foreground-menu)
145
146 (defvar facemenu-background-menu
147 (let ((map (make-sparse-keymap "Background Color")))
148 (define-key map "o" (cons "Other" 'facemenu-set-background))
149 map)
150 "Menu keymap for background colors")
151 (defalias 'facemenu-background-menu facemenu-background-menu)
152
153 (defvar facemenu-special-menu
154 (let ((map (make-sparse-keymap "Special")))
155 (define-key map [read-only] (cons "Read-Only" 'facemenu-set-read-only))
156 (define-key map [invisible] (cons "Invisible" 'facemenu-set-invisible))
157 map)
158 "Menu keymap for non-face text-properties.")
159 (defalias 'facemenu-special-menu facemenu-special-menu)
160
161 (defvar facemenu-justification-menu
162 (let ((map (make-sparse-keymap "Justification")))
163 (define-key map "c" (cons "Center" 'set-justification-center))
164 (define-key map "f" (cons "Full" 'set-justification-full))
165 (define-key map "r" (cons "Right" 'set-justification-right))
166 (define-key map "l" (cons "Left" 'set-justification-left))
167 (define-key map "n" (cons "Unfilled" 'set-justification-none))
168 map)
169 "Submenu for text justification commands.")
170 (defalias 'facemenu-justification-menu facemenu-justification-menu)
171
172 (defvar facemenu-indentation-menu
173 (let ((map (make-sparse-keymap "Indentation")))
174 (define-key map [UnIndentRight]
175 (cons "UnIndentRight" 'decrease-right-margin))
176 (define-key map [IndentRight]
177 (cons "IndentRight" 'increase-right-margin))
178 (define-key map [Unindent]
179 (cons "UnIndent" 'decrease-left-margin))
180 (define-key map [Indent]
181 (cons "Indent" 'increase-left-margin))
182 map)
183 "Submenu for indentation commands.")
184 (defalias 'facemenu-indentation-menu facemenu-indentation-menu)
185
186 (defvar facemenu-menu
187 (let ((map (make-sparse-keymap "Face")))
188 (define-key map [dc] (cons "Display Colors" 'list-colors-display))
189 (define-key map [df] (cons "Display Faces" 'list-faces-display))
190 (define-key map [rm] (cons "Remove Props" 'facemenu-remove-all))
191 (define-key map [s1] (list "-----------------"))
192 (define-key map [in] (cons "Indentation" 'facemenu-indentation-menu))
193 (define-key map [ju] (cons "Justification" 'facemenu-justification-menu))
194 (define-key map [s2] (list "-----------------"))
195 (define-key map [sp] (cons "Special Props" 'facemenu-special-menu))
196 (define-key map [bg] (cons "Background Color" 'facemenu-background-menu))
197 (define-key map [fg] (cons "Foreground Color" 'facemenu-foreground-menu))
198 (define-key map [fc] (cons "Face" 'facemenu-face-menu))
199 map)
200 "Facemenu top-level menu keymap.")
201 (defalias 'facemenu-menu facemenu-menu)
202
203 (defvar facemenu-keymap
204 (let ((map (make-sparse-keymap "Set face")))
205 (define-key map "o" (cons "Other" 'facemenu-set-face))
206 map)
207 "Map for keyboard face-changing commands.
208 `Facemenu-update' fills in the keymap according to the bindings
209 requested in `facemenu-keybindings'.")
210 (defalias 'facemenu-keymap facemenu-keymap)
211
212 ;;; Internal Variables
213
214 (defvar facemenu-color-alist nil
215 ;; Don't initialize here; that doesn't work if preloaded.
216 "Alist of colors, used for completion.
217 If null, `facemenu-read-color' will set it.")
218
219 (defun facemenu-update ()
220 "Add or update the \"Face\" menu in the menu bar.
221 You can call this to update things if you change any of the menu configuration
222 variables."
223 (interactive)
224
225 ;; Global bindings:
226 (define-key global-map [C-down-mouse-2] 'facemenu-menu)
227 (if facemenu-key (define-key global-map facemenu-key 'facemenu-keymap))
228
229 ;; Add each defined face to the menu.
230 (facemenu-iterate 'facemenu-add-new-face
231 (facemenu-complete-face-list facemenu-keybindings)))
232
233 ;;;###autoload
234 (defun facemenu-set-face (face &optional start end)
235 "Add FACE to the region or next character typed.
236 It will be added to the top of the face list; any faces lower on the list that
237 will not show through at all will be removed.
238
239 Interactively, the face to be used is prompted for.
240 If the region is active, it will be set to the requested face. If
241 it is inactive \(even if mark-even-if-inactive is set) the next
242 character that is typed \(or otherwise inserted) will be set to
243 the the selected face. Moving point or switching buffers before
244 typing a character cancels the request."
245 (interactive (list (read-face-name "Use face: ")))
246 (barf-if-buffer-read-only)
247 (facemenu-add-new-face face)
248 (if mark-active
249 (let ((start (or start (region-beginning)))
250 (end (or end (region-end))))
251 (facemenu-add-face face start end))
252 (facemenu-self-insert-face face)))
253
254 ;;;###autoload
255 (defun facemenu-set-foreground (color &optional start end)
256 "Set the foreground color of the region or next character typed.
257 The color is prompted for. A face named `fg:color' is used \(or created).
258 If the region is active, it will be set to the requested face. If
259 it is inactive \(even if mark-even-if-inactive is set) the next
260 character that is typed \(via `self-insert-command') will be set to
261 the the selected face. Moving point or switching buffers before
262 typing a character cancels the request."
263 (interactive (list (facemenu-read-color "Foreground color: ")))
264 (let ((face (intern (concat "fg:" color))))
265 (or (facemenu-get-face face)
266 (error "Unknown color: %s" color))
267 (facemenu-set-face face start end)))
268
269 ;;;###autoload
270 (defun facemenu-set-background (color &optional start end)
271 "Set the background color of the region or next character typed.
272 The color is prompted for. A face named `bg:color' is used \(or created).
273 If the region is active, it will be set to the requested face. If
274 it is inactive \(even if mark-even-if-inactive is set) the next
275 character that is typed \(via `self-insert-command') will be set to
276 the the selected face. Moving point or switching buffers before
277 typing a character cancels the request."
278 (interactive (list (facemenu-read-color "Background color: ")))
279 (let ((face (intern (concat "bg:" color))))
280 (or (facemenu-get-face face)
281 (error "Unknown color: %s" color))
282 (facemenu-set-face face start end)))
283
284 (defun facemenu-set-face-from-menu (face start end)
285 "Set the face of the region or next character typed.
286 This function is designed to be called from a menu; the face to use
287 is the menu item's name.
288 If the region is active, it will be set to the requested face. If
289 it is inactive \(even if mark-even-if-inactive is set) the next
290 character that is typed \(or otherwise inserted) will be set to
291 the the selected face. Moving point or switching buffers before
292 typing a character cancels the request."
293 (interactive (list last-command-event
294 (if mark-active (region-beginning))
295 (if mark-active (region-end))))
296 (barf-if-buffer-read-only)
297 (facemenu-get-face face)
298 (if start
299 (facemenu-add-face face start end)
300 (facemenu-self-insert-face face)))
301
302 (defun facemenu-self-insert-face (face)
303 (setq self-insert-face (if (eq last-command self-insert-face-command)
304 (cons face (if (listp self-insert-face)
305 self-insert-face
306 (list self-insert-face)))
307 face)
308 self-insert-face-command this-command))
309
310 (defun facemenu-set-invisible (start end)
311 "Make the region invisible.
312 This sets the `invisible' text property; it can be undone with
313 `facemenu-remove-all'."
314 (interactive "r")
315 (put-text-property start end 'invisible t))
316
317 (defun facemenu-set-intangible (start end)
318 "Make the region intangible: disallow moving into it.
319 This sets the `intangible' text property; it can be undone with
320 `facemenu-remove-all'."
321 (interactive "r")
322 (put-text-property start end 'intangible t))
323
324 (defun facemenu-set-read-only (start end)
325 "Make the region unmodifiable.
326 This sets the `read-only' text property; it can be undone with
327 `facemenu-remove-all'."
328 (interactive "r")
329 (put-text-property start end 'read-only t))
330
331 (defun facemenu-remove-all (start end)
332 "Remove all text properties that facemenu added to region."
333 (interactive "*r") ; error if buffer is read-only despite the next line.
334 (let ((inhibit-read-only t))
335 (remove-text-properties
336 start end '(face nil invisible nil intangible nil
337 read-only nil category nil))))
338
339 ;;;###autoload
340 (defun facemenu-read-color (prompt)
341 "Read a color using the minibuffer."
342 (let ((col (completing-read (or "Color: ")
343 (or facemenu-color-alist
344 (if (eq 'x window-system)
345 (mapcar 'list (x-defined-colors))))
346 nil t)))
347 (if (equal "" col)
348 nil
349 col)))
350
351 ;;;###autoload
352 (defun list-colors-display (&optional list)
353 "Display colors.
354 You can optionally supply a LIST of colors to display, or this function will
355 get a list for the current display, removing alternate names for the same
356 color."
357 (interactive)
358 (if (and (null list) (eq 'x window-system))
359 (let ((l (setq list (x-defined-colors))))
360 (while (cdr l)
361 (if (facemenu-color-equal (car l) (car (cdr l)))
362 (setcdr l (cdr (cdr l)))
363 (setq l (cdr l))))))
364 (with-output-to-temp-buffer "*Colors*"
365 (save-excursion
366 (set-buffer standard-output)
367 (let ((facemenu-unlisted-faces t)
368 s)
369 (while list
370 (setq s (point))
371 (insert (car list))
372 (indent-to 20)
373 (put-text-property s (point) 'face
374 (facemenu-get-face
375 (intern (concat "bg:" (car list)))))
376 (setq s (point))
377 (insert " " (car list) "\n")
378 (put-text-property s (point) 'face
379 (facemenu-get-face
380 (intern (concat "fg:" (car list)))))
381 (setq list (cdr list)))))))
382
383 (defun facemenu-color-equal (a b)
384 "Return t if colors A and B are the same color.
385 A and B should be strings naming colors. The window-system server is queried
386 to find how they would actually be displayed. Nil is always returned if the
387 correct answer cannot be determined."
388 (cond ((equal a b) t)
389 ((and (eq 'x window-system)
390 (equal (x-color-values a) (x-color-values b))))))
391
392 (defun facemenu-add-face (face start end)
393 "Add FACE to text between START and END.
394 For each section of that region that has a different face property, FACE will
395 be consed onto it, and other faces that are completely hidden by that will be
396 removed from the list.
397
398 As a special case, if FACE is `default', then the region is left with NO face
399 text property. Otherwise, selecting the default face would not have any
400 effect."
401 (interactive "*xFace:\nr")
402 (if (eq face 'default)
403 (remove-text-properties start end '(face default))
404 (let ((part-start start) part-end)
405 (while (not (= part-start end))
406 (setq part-end (next-single-property-change part-start 'face nil end))
407 (let ((prev (get-text-property part-start 'face)))
408 (put-text-property part-start part-end 'face
409 (if (null prev)
410 face
411 (facemenu-discard-redundant-faces
412 (cons face
413 (if (listp prev) prev (list prev)))))))
414 (setq part-start part-end)))))
415
416 (defun facemenu-discard-redundant-faces (face-list &optional mask)
417 "Remove from FACE-LIST any faces that won't show at all.
418 This means they have no non-nil elements that aren't also non-nil in an
419 earlier face."
420 (let ((useful nil))
421 (cond ((null face-list) nil)
422 ((null mask)
423 (cons (car face-list)
424 (facemenu-discard-redundant-faces
425 (cdr face-list)
426 (copy-sequence (internal-get-face (car face-list))))))
427 ((let ((i (length mask))
428 (face (internal-get-face (car face-list))))
429 (while (>= (setq i (1- i)) 0)
430 (if (and (aref face i)
431 (not (aref mask i)))
432 (progn (setq useful t)
433 (aset mask i t))))
434 useful)
435 (cons (car face-list)
436 (facemenu-discard-redundant-faces (cdr face-list) mask)))
437 (t (facemenu-discard-redundant-faces (cdr face-list) mask)))))
438
439 (defun facemenu-get-face (symbol)
440 "Make sure FACE exists.
441 If not, it is created. If it is created and is of the form `fg:color', then
442 set the foreground to that color. If of the form `bg:color', set the
443 background. In any case, add it to the appropriate menu. Returns the face,
444 or nil if given a bad color."
445 (if (or (internal-find-face symbol)
446 (let* ((face (make-face symbol))
447 (name (symbol-name symbol))
448 (color (substring name 3)))
449 (cond ((string-match "^fg:" name)
450 (set-face-foreground face color)
451 (and (eq 'x window-system) (x-color-defined-p color)))
452 ((string-match "^bg:" name)
453 (set-face-background face color)
454 (and (eq 'x window-system) (x-color-defined-p color)))
455 (t))))
456 symbol))
457
458 (defun facemenu-add-new-face (face)
459 "Add a FACE to the appropriate Face menu.
460 Automatically called when a new face is created."
461 (let* ((name (symbol-name face))
462 (menu (cond ((string-match "^fg:" name)
463 (setq name (substring name 3))
464 'facemenu-foreground-menu)
465 ((string-match "^bg:" name)
466 (setq name (substring name 3))
467 'facemenu-background-menu)
468 (t 'facemenu-face-menu)))
469 (key (cdr (assoc face facemenu-keybindings)))
470 function menu-val)
471 (cond ((eq t facemenu-unlisted-faces))
472 ((memq face facemenu-unlisted-faces))
473 (key ; has a keyboard equivalent. These go at the front.
474 (setq function (intern (concat "facemenu-set-" name)))
475 (fset function
476 (` (lambda () (interactive)
477 (facemenu-set-face (quote (, face))))))
478 (define-key 'facemenu-keymap key (cons name function))
479 (define-key menu key (cons name function)))
480 ((facemenu-iterate ; check if equivalent face is already in the menu
481 (lambda (m) (and (listp m)
482 (symbolp (car m))
483 (face-equal (car m) face)))
484 (cdr (symbol-function menu))))
485 (t ; No keyboard equivalent. Figure out where to put it:
486 (setq key (vector face)
487 function 'facemenu-set-face-from-menu
488 menu-val (symbol-function menu))
489 (if (and facemenu-new-faces-at-end
490 (> (length menu-val) 3))
491 (define-key-after menu-val key (cons name function)
492 (car (nth (- (length menu-val) 3) menu-val)))
493 (define-key menu key (cons name function))))))
494 nil) ; Return nil for facemenu-iterate
495
496 (defun facemenu-complete-face-list (&optional oldlist)
497 "Return list of all faces that are look different.
498 Starts with given ALIST of faces, and adds elements only if they display
499 differently from any face already on the list.
500 The faces on ALIST will end up at the end of the returned list, in reverse
501 order."
502 (let ((list (nreverse (mapcar 'car oldlist))))
503 (facemenu-iterate
504 (lambda (new-face)
505 (if (not (memq new-face list))
506 (setq list (cons new-face list)))
507 nil)
508 (nreverse (face-list)))
509 list))
510
511 (defun facemenu-iterate (func iterate-list)
512 "Apply FUNC to each element of LIST until one returns non-nil.
513 Returns the non-nil value it found, or nil if all were nil."
514 (while (and iterate-list (not (funcall func (car iterate-list))))
515 (setq iterate-list (cdr iterate-list)))
516 (car iterate-list))
517
518 (facemenu-update)
519
520 ;;; facemenu.el ends here