(syms_of_fileio): Fix missing \n\.
[bpt/emacs.git] / lisp / facemenu.el
CommitLineData
4e8aa578
RS
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:
bf7d4561
BG
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
88d690a9 27;; "fg:" or "bg:", as in "fg:red", are treated specially.
bf7d4561
BG
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
88d690a9
RS
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.
4e8aa578 36
4e8aa578 37;;; Usage:
bf7d4561
BG
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.
4e8aa578
RS
43;;
44;; Faces can be selected from the keyboard as well.
88d690a9
RS
45;; The standard keybindings are M-g (or ESC g) + letter:
46;; M-g i = "set italic", M-g b = "set bold", etc.
4e8aa578
RS
47
48;;; Customization:
49;; An alternative set of keybindings that may be easier to type can be set up
88d690a9
RS
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:
4e8aa578 56;; xmodmap -e "keysym Alt_L = Hyper_L" -e "add Mod2 = Hyper_L"
88d690a9
RS
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:
4e8aa578
RS
59;; (setq facemenu-keybindings
60;; '((default . [?\H-d])
61;; (bold . [?\H-b])
62;; (italic . [?\H-i])
88d690a9 63;; (bold-italic . [?\H-l])
4e8aa578
RS
64;; (underline . [?\H-u])))
65;; (setq facemenu-keymap global-map)
66;; (setq facemenu-key nil)
88d690a9
RS
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)
4e8aa578 70;;
88d690a9
RS
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'.
4e8aa578
RS
75
76;;; Known Problems:
88d690a9
RS
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;;
4e8aa578
RS
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
d2eafd88 95(defvar facemenu-key "\M-g"
4e8aa578
RS
96 "Prefix to use for facemenu commands.")
97
4e8aa578
RS
98(defvar facemenu-keybindings
99 '((default . "d")
100 (bold . "b")
101 (italic . "i")
88d690a9 102 (bold-italic . "l") ; {bold} intersect {italic} = {l}
4e8aa578
RS
103 (underline . "u"))
104 "Alist of interesting faces and keybindings.
105Each element is itself a list: the car is the name of the face,
106the next element is the key to use as a keyboard equivalent of the menu item;
107the binding is made in facemenu-keymap.
108
109The faces specifically mentioned in this list are put at the top of
110the menu, in the order specified. All other faces which are defined,
111except for those in `facemenu-unlisted-faces', are listed after them,
112but get no keyboard equivalents.
113
114If you change this variable after loading facemenu.el, you will need to call
115`facemenu-update' to make it take effect.")
116
88d690a9
RS
117(defvar facemenu-new-faces-at-end t
118 "Where in the menu to insert newly-created faces.
119This should be nil to put them at the top of the menu, or t to put them
120just before \"Other\" at the end.")
121
4e8aa578
RS
122(defvar facemenu-unlisted-faces
123 '(modeline region secondary-selection highlight scratch-face)
88d690a9 124 "List of faces not to include in the Face menu.
4e8aa578 125Set this before loading facemenu.el, or call `facemenu-update' after
88d690a9 126changing it.
4e8aa578 127
88d690a9
RS
128If this variable is t, no faces will be added to the menu. This is useful for
129temporarily turning off the feature that automatically adds faces to the menu
130when they are created.")
131
132(defvar facemenu-face-menu
bf7d4561 133 (let ((map (make-sparse-keymap "Face")))
88d690a9 134 (define-key map "o" (cons "Other..." 'facemenu-set-face))
bf7d4561
BG
135 map)
136 "Menu keymap for faces.")
88d690a9 137(defalias 'facemenu-face-menu facemenu-face-menu)
bf7d4561
BG
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.")
88d690a9 144(defalias 'facemenu-foreground-menu facemenu-foreground-menu)
bf7d4561
BG
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")
88d690a9 151(defalias 'facemenu-background-menu facemenu-background-menu)
bf7d4561
BG
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.")
88d690a9
RS
159(defalias 'facemenu-special-menu facemenu-special-menu)
160
161(defvar facemenu-justification-menu
162 (let ((map (make-sparse-keymap "Justification")))
6c1fd142
BG
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))
88d690a9
RS
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)
bf7d4561
BG
185
186(defvar facemenu-menu
187 (let ((map (make-sparse-keymap "Face")))
88d690a9
RS
188 (define-key map [dc] (cons "Display Colors" 'list-colors-display))
189 (define-key map [df] (cons "Display Faces" 'list-faces-display))
c0a7db84
BG
190 (define-key map [dp] (cons "List Properties" 'list-text-properties-at))
191 (define-key map [rm] (cons "Remove Properties" 'facemenu-remove-all))
88d690a9
RS
192 (define-key map [s1] (list "-----------------"))
193 (define-key map [in] (cons "Indentation" 'facemenu-indentation-menu))
194 (define-key map [ju] (cons "Justification" 'facemenu-justification-menu))
195 (define-key map [s2] (list "-----------------"))
196 (define-key map [sp] (cons "Special Props" 'facemenu-special-menu))
197 (define-key map [bg] (cons "Background Color" 'facemenu-background-menu))
198 (define-key map [fg] (cons "Foreground Color" 'facemenu-foreground-menu))
199 (define-key map [fc] (cons "Face" 'facemenu-face-menu))
bf7d4561 200 map)
535d2617 201 "Facemenu top-level menu keymap.")
88d690a9 202(defalias 'facemenu-menu facemenu-menu)
bf7d4561 203
88d690a9
RS
204(defvar facemenu-keymap
205 (let ((map (make-sparse-keymap "Set face")))
206 (define-key map "o" (cons "Other" 'facemenu-set-face))
207 map)
bf7d4561
BG
208 "Map for keyboard face-changing commands.
209`Facemenu-update' fills in the keymap according to the bindings
535d2617 210requested in `facemenu-keybindings'.")
88d690a9 211(defalias 'facemenu-keymap facemenu-keymap)
bf7d4561
BG
212
213;;; Internal Variables
214
215(defvar facemenu-color-alist nil
216 ;; Don't initialize here; that doesn't work if preloaded.
217 "Alist of colors, used for completion.
218If null, `facemenu-read-color' will set it.")
4a24b314 219
4e8aa578 220(defun facemenu-update ()
bf7d4561
BG
221 "Add or update the \"Face\" menu in the menu bar.
222You can call this to update things if you change any of the menu configuration
223variables."
4e8aa578
RS
224 (interactive)
225
bf7d4561 226 ;; Global bindings:
88d690a9
RS
227 (define-key global-map [C-down-mouse-2] 'facemenu-menu)
228 (if facemenu-key (define-key global-map facemenu-key 'facemenu-keymap))
4e8aa578 229
bf7d4561
BG
230 ;; Add each defined face to the menu.
231 (facemenu-iterate 'facemenu-add-new-face
232 (facemenu-complete-face-list facemenu-keybindings)))
4a24b314 233
4e8aa578
RS
234;;;###autoload
235(defun facemenu-set-face (face &optional start end)
4a24b314
RS
236 "Add FACE to the region or next character typed.
237It will be added to the top of the face list; any faces lower on the list that
238will not show through at all will be removed.
239
240Interactively, the face to be used is prompted for.
241If the region is active, it will be set to the requested face. If
4e8aa578 242it is inactive \(even if mark-even-if-inactive is set) the next
88d690a9 243character that is typed \(or otherwise inserted) will be set to
4e8aa578
RS
244the the selected face. Moving point or switching buffers before
245typing a character cancels the request."
246 (interactive (list (read-face-name "Use face: ")))
88d690a9
RS
247 (barf-if-buffer-read-only)
248 (facemenu-add-new-face face)
4e8aa578 249 (if mark-active
4a24b314
RS
250 (let ((start (or start (region-beginning)))
251 (end (or end (region-end))))
252 (facemenu-add-face face start end))
7fce8c91 253 (facemenu-self-insert-face face)))
4a24b314 254
bf7d4561 255;;;###autoload
4a24b314
RS
256(defun facemenu-set-foreground (color &optional start end)
257 "Set the foreground color of the region or next character typed.
258The color is prompted for. A face named `fg:color' is used \(or created).
259If the region is active, it will be set to the requested face. If
260it is inactive \(even if mark-even-if-inactive is set) the next
261character that is typed \(via `self-insert-command') will be set to
262the the selected face. Moving point or switching buffers before
263typing a character cancels the request."
264 (interactive (list (facemenu-read-color "Foreground color: ")))
265 (let ((face (intern (concat "fg:" color))))
266 (or (facemenu-get-face face)
267 (error "Unknown color: %s" color))
268 (facemenu-set-face face start end)))
269
bf7d4561 270;;;###autoload
4a24b314
RS
271(defun facemenu-set-background (color &optional start end)
272 "Set the background color of the region or next character typed.
273The color is prompted for. A face named `bg:color' is used \(or created).
274If the region is active, it will be set to the requested face. If
275it is inactive \(even if mark-even-if-inactive is set) the next
276character that is typed \(via `self-insert-command') will be set to
277the the selected face. Moving point or switching buffers before
278typing a character cancels the request."
279 (interactive (list (facemenu-read-color "Background color: ")))
280 (let ((face (intern (concat "bg:" color))))
281 (or (facemenu-get-face face)
282 (error "Unknown color: %s" color))
283 (facemenu-set-face face start end)))
4e8aa578
RS
284
285(defun facemenu-set-face-from-menu (face start end)
286 "Set the face of the region or next character typed.
287This function is designed to be called from a menu; the face to use
288is the menu item's name.
289If the region is active, it will be set to the requested face. If
290it is inactive \(even if mark-even-if-inactive is set) the next
88d690a9 291character that is typed \(or otherwise inserted) will be set to
4e8aa578
RS
292the the selected face. Moving point or switching buffers before
293typing a character cancels the request."
4a24b314
RS
294 (interactive (list last-command-event
295 (if mark-active (region-beginning))
296 (if mark-active (region-end))))
88d690a9 297 (barf-if-buffer-read-only)
4a24b314 298 (facemenu-get-face face)
4e8aa578 299 (if start
4a24b314 300 (facemenu-add-face face start end)
7fce8c91
RS
301 (facemenu-self-insert-face face)))
302
303(defun facemenu-self-insert-face (face)
41e5bf66
BG
304 (setq self-insert-face (if (eq last-command self-insert-face-command)
305 (cons face (if (listp self-insert-face)
306 self-insert-face
307 (list self-insert-face)))
308 face)
7fce8c91 309 self-insert-face-command this-command))
4e8aa578
RS
310
311(defun facemenu-set-invisible (start end)
312 "Make the region invisible.
313This sets the `invisible' text property; it can be undone with
314`facemenu-remove-all'."
315 (interactive "r")
316 (put-text-property start end 'invisible t))
317
318(defun facemenu-set-intangible (start end)
319 "Make the region intangible: disallow moving into it.
320This sets the `intangible' text property; it can be undone with
321`facemenu-remove-all'."
322 (interactive "r")
323 (put-text-property start end 'intangible t))
324
325(defun facemenu-set-read-only (start end)
326 "Make the region unmodifiable.
327This sets the `read-only' text property; it can be undone with
328`facemenu-remove-all'."
329 (interactive "r")
330 (put-text-property start end 'read-only t))
331
332(defun facemenu-remove-all (start end)
333 "Remove all text properties that facemenu added to region."
334 (interactive "*r") ; error if buffer is read-only despite the next line.
335 (let ((inhibit-read-only t))
336 (remove-text-properties
337 start end '(face nil invisible nil intangible nil
338 read-only nil category nil))))
339
c0a7db84
BG
340;;;###autoload
341(defun list-text-properties-at (p)
342 "Pop up a buffer listing text-properties at LOCATION."
343 (interactive "d")
344 (let ((props (text-properties-at p)))
345 (if (null props)
346 (message "None")
347 (with-output-to-temp-buffer "*Text Properties*"
348 (princ (format "Text properties at %d:\n\n" p))
349 (while props
350 (princ (format "%-20s %S\n"
351 (car props) (car (cdr props))))
352 (setq props (cdr (cdr props))))))))
353
bf7d4561
BG
354;;;###autoload
355(defun facemenu-read-color (prompt)
356 "Read a color using the minibuffer."
357 (let ((col (completing-read (or "Color: ")
358 (or facemenu-color-alist
359 (if (eq 'x window-system)
360 (mapcar 'list (x-defined-colors))))
361 nil t)))
362 (if (equal "" col)
363 nil
364 col)))
4e8aa578 365
88d690a9
RS
366;;;###autoload
367(defun list-colors-display (&optional list)
368 "Display colors.
369You can optionally supply a LIST of colors to display, or this function will
370get a list for the current display, removing alternate names for the same
371color."
372 (interactive)
373 (if (and (null list) (eq 'x window-system))
374 (let ((l (setq list (x-defined-colors))))
375 (while (cdr l)
376 (if (facemenu-color-equal (car l) (car (cdr l)))
377 (setcdr l (cdr (cdr l)))
378 (setq l (cdr l))))))
379 (with-output-to-temp-buffer "*Colors*"
380 (save-excursion
381 (set-buffer standard-output)
382 (let ((facemenu-unlisted-faces t)
383 s)
384 (while list
385 (setq s (point))
386 (insert (car list))
387 (indent-to 20)
388 (put-text-property s (point) 'face
389 (facemenu-get-face
390 (intern (concat "bg:" (car list)))))
391 (setq s (point))
392 (insert " " (car list) "\n")
393 (put-text-property s (point) 'face
394 (facemenu-get-face
395 (intern (concat "fg:" (car list)))))
396 (setq list (cdr list)))))))
397
398(defun facemenu-color-equal (a b)
399 "Return t if colors A and B are the same color.
400A and B should be strings naming colors. The window-system server is queried
401to find how they would actually be displayed. Nil is always returned if the
402correct answer cannot be determined."
403 (cond ((equal a b) t)
404 ((and (eq 'x window-system)
405 (equal (x-color-values a) (x-color-values b))))))
406
4a24b314
RS
407(defun facemenu-add-face (face start end)
408 "Add FACE to text between START and END.
409For each section of that region that has a different face property, FACE will
410be consed onto it, and other faces that are completely hidden by that will be
bf7d4561
BG
411removed from the list.
412
413As a special case, if FACE is `default', then the region is left with NO face
414text property. Otherwise, selecting the default face would not have any
415effect."
4a24b314 416 (interactive "*xFace:\nr")
bf7d4561
BG
417 (if (eq face 'default)
418 (remove-text-properties start end '(face default))
419 (let ((part-start start) part-end)
420 (while (not (= part-start end))
421 (setq part-end (next-single-property-change part-start 'face nil end))
422 (let ((prev (get-text-property part-start 'face)))
423 (put-text-property part-start part-end 'face
424 (if (null prev)
425 face
426 (facemenu-discard-redundant-faces
427 (cons face
428 (if (listp prev) prev (list prev)))))))
429 (setq part-start part-end)))))
4a24b314
RS
430
431(defun facemenu-discard-redundant-faces (face-list &optional mask)
432 "Remove from FACE-LIST any faces that won't show at all.
433This means they have no non-nil elements that aren't also non-nil in an
434earlier face."
435 (let ((useful nil))
436 (cond ((null face-list) nil)
437 ((null mask)
438 (cons (car face-list)
439 (facemenu-discard-redundant-faces
440 (cdr face-list)
441 (copy-sequence (internal-get-face (car face-list))))))
442 ((let ((i (length mask))
443 (face (internal-get-face (car face-list))))
444 (while (>= (setq i (1- i)) 0)
445 (if (and (aref face i)
446 (not (aref mask i)))
447 (progn (setq useful t)
448 (aset mask i t))))
449 useful)
450 (cons (car face-list)
451 (facemenu-discard-redundant-faces (cdr face-list) mask)))
452 (t (facemenu-discard-redundant-faces (cdr face-list) mask)))))
453
bf7d4561
BG
454(defun facemenu-get-face (symbol)
455 "Make sure FACE exists.
456If not, it is created. If it is created and is of the form `fg:color', then
457set the foreground to that color. If of the form `bg:color', set the
88d690a9
RS
458background. In any case, add it to the appropriate menu. Returns the face,
459or nil if given a bad color."
460 (if (or (internal-find-face symbol)
461 (let* ((face (make-face symbol))
462 (name (symbol-name symbol))
463 (color (substring name 3)))
464 (cond ((string-match "^fg:" name)
465 (set-face-foreground face color)
466 (and (eq 'x window-system) (x-color-defined-p color)))
467 ((string-match "^bg:" name)
468 (set-face-background face color)
469 (and (eq 'x window-system) (x-color-defined-p color)))
470 (t))))
471 symbol))
bf7d4561
BG
472
473(defun facemenu-add-new-face (face)
474 "Add a FACE to the appropriate Face menu.
475Automatically called when a new face is created."
476 (let* ((name (symbol-name face))
477 (menu (cond ((string-match "^fg:" name)
478 (setq name (substring name 3))
88d690a9 479 'facemenu-foreground-menu)
bf7d4561
BG
480 ((string-match "^bg:" name)
481 (setq name (substring name 3))
88d690a9
RS
482 'facemenu-background-menu)
483 (t 'facemenu-face-menu)))
484 (key (cdr (assoc face facemenu-keybindings)))
485 function menu-val)
486 (cond ((eq t facemenu-unlisted-faces))
487 ((memq face facemenu-unlisted-faces))
488 (key ; has a keyboard equivalent. These go at the front.
489 (setq function (intern (concat "facemenu-set-" name)))
490 (fset function
491 (` (lambda () (interactive)
492 (facemenu-set-face (quote (, face))))))
493 (define-key 'facemenu-keymap key (cons name function))
494 (define-key menu key (cons name function)))
495 ((facemenu-iterate ; check if equivalent face is already in the menu
496 (lambda (m) (and (listp m)
497 (symbolp (car m))
498 (face-equal (car m) face)))
499 (cdr (symbol-function menu))))
500 (t ; No keyboard equivalent. Figure out where to put it:
501 (setq key (vector face)
502 function 'facemenu-set-face-from-menu
503 menu-val (symbol-function menu))
504 (if (and facemenu-new-faces-at-end
505 (> (length menu-val) 3))
506 (define-key-after menu-val key (cons name function)
507 (car (nth (- (length menu-val) 3) menu-val)))
508 (define-key menu key (cons name function))))))
509 nil) ; Return nil for facemenu-iterate
bf7d4561 510
bf7d4561
BG
511(defun facemenu-complete-face-list (&optional oldlist)
512 "Return list of all faces that are look different.
513Starts with given ALIST of faces, and adds elements only if they display
514differently from any face already on the list.
515The faces on ALIST will end up at the end of the returned list, in reverse
516order."
517 (let ((list (nreverse (mapcar 'car oldlist))))
518 (facemenu-iterate
519 (lambda (new-face)
520 (if (not (memq new-face list))
521 (setq list (cons new-face list)))
522 nil)
523 (nreverse (face-list)))
524 list))
525
4e8aa578
RS
526(defun facemenu-iterate (func iterate-list)
527 "Apply FUNC to each element of LIST until one returns non-nil.
528Returns the non-nil value it found, or nil if all were nil."
529 (while (and iterate-list (not (funcall func (car iterate-list))))
530 (setq iterate-list (cdr iterate-list)))
531 (car iterate-list))
532
533(facemenu-update)
4e8aa578
RS
534
535;;; facemenu.el ends here