1 ;;; facemenu.el -- Create a face menu for interactively adding fonts to text
2 ;; Copyright (c) 1994 Free Software Foundation, Inc.
4 ;; Author: Boris Goldowsky <boris@cs.rochester.edu>
7 ;; This file is part of GNU Emacs.
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)
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.
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.
24 ;; This file defines a menu of faces (bold, italic, etc) which
25 ;; allows you to set the face used for a region of the buffer.
26 ;; Some faces also have keybindings, which are shown in the menu.
29 ;; Put this file somewhere on emacs's load-path, and put
30 ;; (require 'facemenu)
31 ;; in your .emacs file.
34 ;; Selecting a face from the menu or typing the keyboard equivalent
35 ;; will change the region to use that face.
36 ;; If you use transient-mark-mode and the region is not active, the
37 ;; face will be remembered and used for the next insertion. It will
38 ;; be forgotten if you move point or make other modifications before
39 ;; inserting or typing anything.
41 ;; Faces can be selected from the keyboard as well.
42 ;; The standard keybindings are M-s (or ESC s) + letter:
43 ;; M-s i = "set italic", M-s b = "set bold", etc.
46 ;; An alternative set of keybindings that may be easier to type can be set up
47 ;; using "Hyper" keys. This requires that you set up a hyper-key on your
48 ;; keyboard. On my system, putting the following command in my .xinitrc:
49 ;; xmodmap -e "keysym Alt_L = Hyper_L" -e "add Mod2 = Hyper_L"
50 ;; makes the key labelled "Alt" act as a hyper key, but check with local
51 ;; X-perts for how to do it on your system. If you do this, then put the
52 ;; following in your .emacs before the (require 'facemenu):
53 ;; (setq facemenu-keybindings
54 ;; '((default . [?\H-d])
57 ;; (bold-italic . [?\H-o])
58 ;; (underline . [?\H-u])))
59 ;; (setq facemenu-keymap global-map)
60 ;; (setq facemenu-key nil)
62 ;; In general, the order of the faces that appear in the menu and their
63 ;; keybindings can be controlled by setting the variable
64 ;; `facemenu-keybindings'. Faces that you never want to add to your
65 ;; document (e.g., `region') are listed in `facemenu-unlisted-faces'.
68 ;; Only works with Emacs 19.23 and later.
70 ;; There is at present no way to display what the faces look like in
73 ;; `list-faces-display' shows the faces in a different order than
74 ;; this menu, which could be confusing. I do /not/ sort the list
75 ;; alphabetically, because I like the default order: it puts the most
76 ;; basic, common fonts first.
78 ;; Please send me any other problems, comments or ideas.
84 (defvar facemenu-key
"\M-s"
85 "Prefix to use for facemenu commands.")
87 (defvar facemenu-keymap nil
88 "Map for keybindings of face commands.
89 If nil, `facemenu-update' will create one.
90 `Facemenu-update' also fills in the keymap according to the bindings
91 requested in facemenu-keybindings.")
93 (defvar facemenu-keybindings
97 (bold-italic .
"o") ; O for "Oblique" or "bOld"...
99 "Alist of interesting faces and keybindings.
100 Each element is itself a list: the car is the name of the face,
101 the next element is the key to use as a keyboard equivalent of the menu item;
102 the binding is made in facemenu-keymap.
104 The faces specifically mentioned in this list are put at the top of
105 the menu, in the order specified. All other faces which are defined,
106 except for those in `facemenu-unlisted-faces', are listed after them,
107 but get no keyboard equivalents.
109 If you change this variable after loading facemenu.el, you will need to call
110 `facemenu-update' to make it take effect.")
112 (defvar facemenu-unlisted-faces
113 '(modeline region secondary-selection highlight scratch-face
)
114 "Faces that are not included in the Face menu.
115 Set this before loading facemenu.el, or call `facemenu-update' after
118 (defvar facemenu-next nil
) ; set when we are going to set a face on next char.
119 (defvar facemenu-loc nil
)
121 (defun facemenu-update ()
122 "Add or update the \"Face\" menu in the menu bar."
126 (fset 'facemenu-menu
(setq facemenu-menu
(make-sparse-keymap "Face")))
127 (if (null facemenu-keymap
)
128 (fset 'facemenu-keymap
129 (setq facemenu-keymap
(make-sparse-keymap "Set face"))))
131 (define-key global-map facemenu-key facemenu-keymap
))
134 ;; We construct this list structure explicitly because a quoted constant
136 (define-key facemenu-menu
[other] (cons "Other..." 'facemenu-set-face))
137 (define-key facemenu-menu [sep2] (list "---Special---"))
138 (define-key facemenu-menu [invisible] (cons "Invisible"
139 'facemenu-set-invisible))
140 (define-key facemenu-menu [read-only] (cons "Read-Only"
141 'facemenu-set-read-only))
142 (define-key facemenu-menu [remove] (cons "Remove Properties"
143 'facemenu-remove-all))
144 (define-key facemenu-menu [sep1] (list "-------------"))
145 (define-key facemenu-menu [display] (cons "Display" 'list-faces-display))
146 (define-key facemenu-menu [update] (cons "Update Menu" 'facemenu-update))
148 ;; Define commands for face-changing
153 (name (symbol-name (car f)))
155 (cond ((memq face facemenu-unlisted-faces)
157 ((null key) (define-key facemenu-menu (vector face)
158 (cons name 'facemenu-set-face-from-menu)))
159 (t (let ((function (intern (concat "facemenu-set-" name))))
161 (` (lambda () (interactive)
162 (facemenu-set-face (quote (, face))))))
163 (define-key facemenu-keymap key (cons name function))
164 (define-key facemenu-menu key (cons name function))))))
166 (facemenu-complete-face-list facemenu-keybindings))
168 (define-key global-map (vector 'menu-bar 'Face)
169 (cons "Face" facemenu-menu)))
171 ; We'd really like to name the menu items as follows,
172 ; but we can't since menu entries don't display text properties (yet?)
173 ; (let ((s (copy-sequence (symbol-name face))))
174 ; (put-text-property 0 (1- (length s))
179 (defun facemenu-set-face (face &optional start end)
180 "Set the face of the region or next character typed.
181 The face to be used is prompted for.
182 If the region is active, it will be set to the requested face. If
183 it is inactive \(even if mark-even-if-inactive is set) the next
184 character that is typed \(via `self-insert-command') will be set to
185 the the selected face. Moving point or switching buffers before
186 typing a character cancels the request."
187 (interactive (list (read-face-name "Use face: ")))
189 (put-text-property (or start (region-beginning))
190 (or end (region-end))
192 (setq facemenu-next face facemenu-loc (point))))
194 (defun facemenu-set-face-from-menu (face start end)
195 "Set the face of the region or next character typed.
196 This function is designed to be called from a menu; the face to use
197 is the menu item's name.
198 If the region is active, it will be set to the requested face. If
199 it is inactive \(even if mark-even-if-inactive is set) the next
200 character that is typed \(via `self-insert-command') will be set to
201 the the selected face. Moving point or switching buffers before
202 typing a character cancels the request."
203 (interactive (let ((keys (this-command-keys)))
204 (list (elt keys (1- (length keys)))
205 (if mark-active (region-beginning))
206 (if mark-active (region-end)))))
208 (put-text-property start end 'face face)
209 (setq facemenu-next face facemenu-loc (point))))
211 (defun facemenu-set-invisible (start end)
212 "Make the region invisible.
213 This sets the `invisible' text property; it can be undone with
214 `facemenu-remove-all'."
216 (put-text-property start end 'invisible t))
218 (defun facemenu-set-intangible (start end)
219 "Make the region intangible: disallow moving into it.
220 This sets the `intangible' text property; it can be undone with
221 `facemenu-remove-all'."
223 (put-text-property start end 'intangible t))
225 (defun facemenu-set-read-only (start end)
226 "Make the region unmodifiable.
227 This sets the `read-only' text property; it can be undone with
228 `facemenu-remove-all'."
230 (put-text-property start end 'read-only t))
232 (defun facemenu-remove-all (start end)
233 "Remove all text properties that facemenu added to region."
234 (interactive "*r") ; error if buffer is read-only despite the next line.
235 (let ((inhibit-read-only t))
236 (remove-text-properties
237 start end '(face nil invisible nil intangible nil
238 read-only nil category nil))))
240 (defun facemenu-after-change (begin end old-length)
241 "May set the face of just-inserted text to user's request.
242 This only happens if the change is an insertion, and
243 `facemenu-set-face[-from-menu]' was called with point at the
244 beginning of the insertion."
245 (if (null facemenu-next) ; exit immediately if no work
247 (if (and (= 0 old-length) ; insertion
248 (= facemenu-loc begin)) ; point wasn't moved in between
249 (put-text-property begin end 'face facemenu-next))
250 (setq facemenu-next nil)))
253 (defun facemenu-complete-face-list (&optional oldlist)
254 "Return alist of all faces that are look different.
255 Starts with given LIST of faces, and adds elements only if they display
256 differently from any face already on the list.
257 The original LIST will end up at the end of the returned list, in reverse
258 order. The elements added will have null cdrs."
263 (if (internal-find-face (car item))
264 (setq list (cons item list)))
270 (if (not (facemenu-iterate
272 (lambda (item) (face-equal (car item) new-face t)))
274 (setq list (cons (cons new-face nil) list)))
276 (nreverse (face-list)))
279 (defun facemenu-iterate (func iterate-list)
280 "Apply FUNC to each element of LIST until one returns non-nil.
281 Returns the non-nil value it found, or nil if all were nil."
282 (while (and iterate-list (not (funcall func (car iterate-list))))
283 (setq iterate-list (cdr iterate-list)))
287 (add-hook 'menu-bar-final-items 'Face)
288 (add-hook 'after-change-functions 'facemenu-after-change)
290 ;;; facemenu.el ends here