(Info-build-node-completions, Info-search, Info-follow-reference)
[bpt/emacs.git] / lisp / facemenu.el
CommitLineData
be010748 1;;; facemenu.el --- create a face menu for interactively adding fonts to text
b578f267 2
682e437e 3;; Copyright (c) 1994, 1995, 1996 Free Software Foundation, Inc.
4e8aa578 4
5762abec 5;; Author: Boris Goldowsky <boris@gnu.org>
4e8aa578
RS
6;; Keywords: faces
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
b578f267
EN
21;; along with GNU Emacs; see the file COPYING. If not, write to the
22;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23;; Boston, MA 02111-1307, USA.
4e8aa578
RS
24
25;;; Commentary:
b578f267 26
bf7d4561
BG
27;; This file defines a menu of faces (bold, italic, etc) which allows you to
28;; set the face used for a region of the buffer. Some faces also have
29;; keybindings, which are shown in the menu. Faces with names beginning with
88d690a9 30;; "fg:" or "bg:", as in "fg:red", are treated specially.
bf7d4561
BG
31;; Such faces are assumed to consist only of a foreground (if "fg:") or
32;; background (if "bg:") color. They are thus put into the color submenus
88d690a9
RS
33;; rather than the general Face submenu. These faces can also be
34;; automatically created by selecting the "Other..." menu items in the
35;; "Foreground" and "Background" submenus.
36;;
37;; The menu also contains submenus for indentation and justification-changing
38;; commands.
4e8aa578 39
4e8aa578 40;;; Usage:
bf7d4561
BG
41;; Selecting a face from the menu or typing the keyboard equivalent will
42;; change the region to use that face. If you use transient-mark-mode and the
43;; region is not active, the face will be remembered and used for the next
44;; insertion. It will be forgotten if you move point or make other
45;; modifications before inserting or typing anything.
4e8aa578
RS
46;;
47;; Faces can be selected from the keyboard as well.
88d690a9
RS
48;; The standard keybindings are M-g (or ESC g) + letter:
49;; M-g i = "set italic", M-g b = "set bold", etc.
4e8aa578
RS
50
51;;; Customization:
52;; An alternative set of keybindings that may be easier to type can be set up
88d690a9
RS
53;; using "Alt" or "Hyper" keys. This requires that you either have or create
54;; an Alt or Hyper key on your keyboard. On my keyboard, there is a key
55;; labeled "Alt", but to make it act as an Alt key I have to put this command
56;; into my .xinitrc:
57;; xmodmap -e "add Mod3 = Alt_L"
58;; Or, I can make it into a Hyper key with this:
4e8aa578 59;; xmodmap -e "keysym Alt_L = Hyper_L" -e "add Mod2 = Hyper_L"
88d690a9
RS
60;; Check with local X-perts for how to do it on your system.
61;; Then you can define your keybindings with code like this in your .emacs:
4e8aa578
RS
62;; (setq facemenu-keybindings
63;; '((default . [?\H-d])
64;; (bold . [?\H-b])
65;; (italic . [?\H-i])
88d690a9 66;; (bold-italic . [?\H-l])
4e8aa578 67;; (underline . [?\H-u])))
9086c730 68;; (facemenu-update)
4e8aa578 69;; (setq facemenu-keymap global-map)
88d690a9
RS
70;; (define-key global-map [?\H-c] 'facemenu-set-foreground) ; set fg color
71;; (define-key global-map [?\H-C] 'facemenu-set-background) ; set bg color
4e8aa578 72;;
88d690a9
RS
73;; The order of the faces that appear in the menu and their keybindings can be
74;; controlled by setting the variables `facemenu-keybindings' and
75;; `facemenu-new-faces-at-end'. List faces that you don't use in documents
76;; (eg, `region') in `facemenu-unlisted-faces'.
4e8aa578
RS
77
78;;; Known Problems:
88d690a9
RS
79;; Bold and Italic do not combine to create bold-italic if you select them
80;; both, although most other combinations (eg bold + underline + some color)
81;; do the intuitive thing.
82;;
4e8aa578
RS
83;; There is at present no way to display what the faces look like in
84;; the menu itself.
85;;
86;; `list-faces-display' shows the faces in a different order than
87;; this menu, which could be confusing. I do /not/ sort the list
88;; alphabetically, because I like the default order: it puts the most
89;; basic, common fonts first.
90;;
91;; Please send me any other problems, comments or ideas.
92
93;;; Code:
94
95(provide 'facemenu)
96
9dc90430
BG
97;;; Provide some binding for startup:
98;;;###autoload (define-key global-map "\M-g" 'facemenu-keymap)
99;;;###autoload (autoload 'facemenu-keymap "facemenu" "Keymap for face-changing commands." t 'keymap)
9086c730
RS
100
101;; Global bindings:
102(define-key global-map [C-down-mouse-2] 'facemenu-menu)
103(define-key global-map "\M-g" 'facemenu-keymap)
4e8aa578 104
487e6fcb
RS
105(defgroup facemenu nil
106 "Create a face menu for interactively adding fonts to text"
107 :group 'faces
108 :prefix "facemenu-")
109
110(defcustom facemenu-keybindings
4e8aa578
RS
111 '((default . "d")
112 (bold . "b")
113 (italic . "i")
88d690a9 114 (bold-italic . "l") ; {bold} intersect {italic} = {l}
4e8aa578
RS
115 (underline . "u"))
116 "Alist of interesting faces and keybindings.
117Each element is itself a list: the car is the name of the face,
118the next element is the key to use as a keyboard equivalent of the menu item;
9086c730 119the binding is made in `facemenu-keymap'.
4e8aa578
RS
120
121The faces specifically mentioned in this list are put at the top of
122the menu, in the order specified. All other faces which are defined,
123except for those in `facemenu-unlisted-faces', are listed after them,
124but get no keyboard equivalents.
125
126If you change this variable after loading facemenu.el, you will need to call
487e6fcb
RS
127`facemenu-update' to make it take effect."
128 :type '(repeat (cons face string))
129 :group 'facemenu)
4e8aa578 130
487e6fcb 131(defcustom facemenu-new-faces-at-end t
9086c730 132 "*Where in the menu to insert newly-created faces.
88d690a9 133This should be nil to put them at the top of the menu, or t to put them
487e6fcb
RS
134just before \"Other\" at the end."
135 :type 'boolean
136 :group 'facemenu)
88d690a9 137
487e6fcb 138(defcustom facemenu-unlisted-faces
7dc30d5b
RS
139 '(modeline region secondary-selection highlight scratch-face
140 "^font-lock-" "^gnus-" "^message-" "^ediff-" "^term-" "^vc-"
141 "^widget-" "^custom-" "^vm-")
9086c730 142 "*List of faces not to include in the Face menu.
7dc30d5b
RS
143Each element may be either a symbol, which is the name of a face, or a string,
144which is a regular expression to be matched against face names. Matching
145faces will not be added to the menu.
146
5a79ed26
KH
147You can set this list before loading facemenu.el, or add a face to it before
148creating that face if you do not want it to be listed. If you change the
149variable so as to eliminate faces that have already been added to the menu,
150call `facemenu-update' to recalculate the menu contents.
4e8aa578 151
88d690a9
RS
152If this variable is t, no faces will be added to the menu. This is useful for
153temporarily turning off the feature that automatically adds faces to the menu
487e6fcb
RS
154when they are created."
155 :type '(choice (const :tag "Don't add" t)
156 (const :tag "None" nil)
7dc30d5b 157 (repeat (choice symbol regexp)))
487e6fcb 158 :group 'facemenu)
88d690a9 159
9dc90430 160;;;###autoload
88d690a9 161(defvar facemenu-face-menu
bf7d4561 162 (let ((map (make-sparse-keymap "Face")))
88d690a9 163 (define-key map "o" (cons "Other..." 'facemenu-set-face))
bf7d4561
BG
164 map)
165 "Menu keymap for faces.")
9dc90430 166;;;###autoload
88d690a9 167(defalias 'facemenu-face-menu facemenu-face-menu)
bf7d4561 168
9dc90430 169;;;###autoload
bf7d4561
BG
170(defvar facemenu-foreground-menu
171 (let ((map (make-sparse-keymap "Foreground Color")))
f34eaa2c 172 (define-key map "o" (cons "Other..." 'facemenu-set-foreground))
bf7d4561
BG
173 map)
174 "Menu keymap for foreground colors.")
9dc90430 175;;;###autoload
88d690a9 176(defalias 'facemenu-foreground-menu facemenu-foreground-menu)
bf7d4561 177
9dc90430 178;;;###autoload
bf7d4561
BG
179(defvar facemenu-background-menu
180 (let ((map (make-sparse-keymap "Background Color")))
f34eaa2c 181 (define-key map "o" (cons "Other..." 'facemenu-set-background))
bf7d4561
BG
182 map)
183 "Menu keymap for background colors")
9dc90430 184;;;###autoload
88d690a9 185(defalias 'facemenu-background-menu facemenu-background-menu)
bf7d4561 186
9dc90430 187;;;###autoload
bf7d4561
BG
188(defvar facemenu-special-menu
189 (let ((map (make-sparse-keymap "Special")))
f34eaa2c
KH
190 (define-key map [?s] (cons "Remove Special" 'facemenu-remove-special))
191 (define-key map [?t] (cons "Intangible" 'facemenu-set-intangible))
192 (define-key map [?v] (cons "Invisible" 'facemenu-set-invisible))
193 (define-key map [?r] (cons "Read-Only" 'facemenu-set-read-only))
bf7d4561
BG
194 map)
195 "Menu keymap for non-face text-properties.")
9dc90430 196;;;###autoload
88d690a9
RS
197(defalias 'facemenu-special-menu facemenu-special-menu)
198
9dc90430 199;;;###autoload
88d690a9
RS
200(defvar facemenu-justification-menu
201 (let ((map (make-sparse-keymap "Justification")))
9dc90430
BG
202 (define-key map [?c] (cons "Center" 'set-justification-center))
203 (define-key map [?b] (cons "Full" 'set-justification-full))
204 (define-key map [?r] (cons "Right" 'set-justification-right))
205 (define-key map [?l] (cons "Left" 'set-justification-left))
206 (define-key map [?u] (cons "Unfilled" 'set-justification-none))
88d690a9
RS
207 map)
208 "Submenu for text justification commands.")
9dc90430 209;;;###autoload
88d690a9
RS
210(defalias 'facemenu-justification-menu facemenu-justification-menu)
211
9dc90430 212;;;###autoload
88d690a9
RS
213(defvar facemenu-indentation-menu
214 (let ((map (make-sparse-keymap "Indentation")))
f34eaa2c
KH
215 (define-key map [decrease-right-margin]
216 (cons "Indent Right Less" 'decrease-right-margin))
217 (define-key map [increase-right-margin]
218 (cons "Indent Right More" 'increase-right-margin))
219 (define-key map [decrease-left-margin]
220 (cons "Indent Less" 'decrease-left-margin))
221 (define-key map [increase-left-margin]
222 (cons "Indent More" 'increase-left-margin))
88d690a9
RS
223 map)
224 "Submenu for indentation commands.")
9dc90430 225;;;###autoload
88d690a9 226(defalias 'facemenu-indentation-menu facemenu-indentation-menu)
bf7d4561 227
f34eaa2c 228;; This is split up to avoid an overlong line in loaddefs.el.
9dc90430 229;;;###autoload
f34eaa2c 230(defvar facemenu-menu nil
535d2617 231 "Facemenu top-level menu keymap.")
9dc90430 232;;;###autoload
f34eaa2c
KH
233(setq facemenu-menu (make-sparse-keymap "Text Properties"))
234;;;###autoload
235(let ((map facemenu-menu))
236 (define-key map [dc] (cons "Display Colors" 'list-colors-display))
237 (define-key map [df] (cons "Display Faces" 'list-faces-display))
238 (define-key map [dp] (cons "List Properties" 'list-text-properties-at))
a32d7856
KH
239 (define-key map [ra] (cons "Remove Text Properties" 'facemenu-remove-all))
240 (define-key map [rm] (cons "Remove Face Properties" 'facemenu-remove-face-props))
f34eaa2c
KH
241 (define-key map [s1] (list "-----------------")))
242;;;###autoload
243(let ((map facemenu-menu))
244 (define-key map [in] (cons "Indentation" 'facemenu-indentation-menu))
245 (define-key map [ju] (cons "Justification" 'facemenu-justification-menu))
246 (define-key map [s2] (list "-----------------"))
29008daa 247 (define-key map [sp] (cons "Special Properties" 'facemenu-special-menu))
f34eaa2c
KH
248 (define-key map [bg] (cons "Background Color" 'facemenu-background-menu))
249 (define-key map [fg] (cons "Foreground Color" 'facemenu-foreground-menu))
250 (define-key map [fc] (cons "Face" 'facemenu-face-menu)))
251;;;###autoload
88d690a9 252(defalias 'facemenu-menu facemenu-menu)
bf7d4561 253
88d690a9
RS
254(defvar facemenu-keymap
255 (let ((map (make-sparse-keymap "Set face")))
f34eaa2c 256 (define-key map "o" (cons "Other..." 'facemenu-set-face))
88d690a9 257 map)
9dc90430 258 "Keymap for face-changing commands.
bf7d4561 259`Facemenu-update' fills in the keymap according to the bindings
535d2617 260requested in `facemenu-keybindings'.")
88d690a9 261(defalias 'facemenu-keymap facemenu-keymap)
bf7d4561 262
cb5bec6e 263
487e6fcb 264(defcustom facemenu-add-face-function nil
cb5bec6e
RS
265 "Function called at beginning of text to change or `nil'.
266This function is passed the FACE to set and END of text to change, and must
487e6fcb
RS
267return a string which is inserted. It may set `facemenu-end-add-face'."
268 :type '(choice (const :tag "None" nil)
269 function)
270 :group 'facemenu)
cb5bec6e 271
487e6fcb 272(defcustom facemenu-end-add-face nil
cb5bec6e
RS
273 "String to insert or function called at end of text to change or `nil'.
274This function is passed the FACE to set, and must return a string which is
487e6fcb
RS
275inserted."
276 :type '(choice (const :tag "None" nil)
277 string
278 function)
279 :group 'facemenu)
cb5bec6e 280
487e6fcb 281(defcustom facemenu-remove-face-function nil
9086c730 282 "When non-nil, this is a function called to remove faces.
cb5bec6e 283This function is passed the START and END of text to change.
487e6fcb
RS
284May also be `t' meaning to use `facemenu-add-face-function'."
285 :type '(choice (const :tag "None" nil)
286 (const :tag "Use add-face" t)
287 function)
288 :group 'facemenu)
cb5bec6e 289
bf7d4561
BG
290;;; Internal Variables
291
292(defvar facemenu-color-alist nil
293 ;; Don't initialize here; that doesn't work if preloaded.
294 "Alist of colors, used for completion.
295If null, `facemenu-read-color' will set it.")
4a24b314 296
4e8aa578 297(defun facemenu-update ()
bf7d4561
BG
298 "Add or update the \"Face\" menu in the menu bar.
299You can call this to update things if you change any of the menu configuration
300variables."
4e8aa578 301 (interactive)
4e8aa578 302
bf7d4561
BG
303 ;; Add each defined face to the menu.
304 (facemenu-iterate 'facemenu-add-new-face
305 (facemenu-complete-face-list facemenu-keybindings)))
4a24b314 306
4e8aa578
RS
307;;;###autoload
308(defun facemenu-set-face (face &optional start end)
4a24b314
RS
309 "Add FACE to the region or next character typed.
310It will be added to the top of the face list; any faces lower on the list that
311will not show through at all will be removed.
312
f34eaa2c
KH
313Interactively, the face to be used is read with the minibuffer.
314
315If the region is active and there is no prefix argument,
316this command sets the region to the requested face.
317
318Otherwise, this command specifies the face for the next character
319inserted. Moving point or switching buffers before
320typing a character to insert cancels the specification."
4e8aa578 321 (interactive (list (read-face-name "Use face: ")))
88d690a9
RS
322 (barf-if-buffer-read-only)
323 (facemenu-add-new-face face)
f34eaa2c 324 (if (and mark-active (not current-prefix-arg))
4a24b314
RS
325 (let ((start (or start (region-beginning)))
326 (end (or end (region-end))))
327 (facemenu-add-face face start end))
cb5bec6e 328 (facemenu-add-face face)))
4a24b314 329
bf7d4561 330;;;###autoload
4a24b314
RS
331(defun facemenu-set-foreground (color &optional start end)
332 "Set the foreground color of the region or next character typed.
333The color is prompted for. A face named `fg:color' is used \(or created).
334If the region is active, it will be set to the requested face. If
335it is inactive \(even if mark-even-if-inactive is set) the next
336character that is typed \(via `self-insert-command') will be set to
f61deddc 337the selected face. Moving point or switching buffers before
4a24b314
RS
338typing a character cancels the request."
339 (interactive (list (facemenu-read-color "Foreground color: ")))
340 (let ((face (intern (concat "fg:" color))))
341 (or (facemenu-get-face face)
342 (error "Unknown color: %s" color))
343 (facemenu-set-face face start end)))
344
bf7d4561 345;;;###autoload
4a24b314
RS
346(defun facemenu-set-background (color &optional start end)
347 "Set the background color of the region or next character typed.
348The color is prompted for. A face named `bg:color' is used \(or created).
349If the region is active, it will be set to the requested face. If
350it is inactive \(even if mark-even-if-inactive is set) the next
351character that is typed \(via `self-insert-command') will be set to
f61deddc 352the selected face. Moving point or switching buffers before
4a24b314
RS
353typing a character cancels the request."
354 (interactive (list (facemenu-read-color "Background color: ")))
355 (let ((face (intern (concat "bg:" color))))
356 (or (facemenu-get-face face)
357 (error "Unknown color: %s" color))
358 (facemenu-set-face face start end)))
4e8aa578 359
9dc90430 360;;;###autoload
4e8aa578
RS
361(defun facemenu-set-face-from-menu (face start end)
362 "Set the face of the region or next character typed.
363This function is designed to be called from a menu; the face to use
364is the menu item's name.
f34eaa2c
KH
365
366If the region is active and there is no prefix argument,
367this command sets the region to the requested face.
368
369Otherwise, this command specifies the face for the next character
370inserted. Moving point or switching buffers before
371typing a character to insert cancels the specification."
4a24b314 372 (interactive (list last-command-event
f34eaa2c
KH
373 (if (and mark-active (not current-prefix-arg))
374 (region-beginning))
375 (if (and mark-active (not current-prefix-arg))
376 (region-end))))
88d690a9 377 (barf-if-buffer-read-only)
4a24b314 378 (facemenu-get-face face)
4e8aa578 379 (if start
4a24b314 380 (facemenu-add-face face start end)
cb5bec6e 381 (facemenu-add-face face)))
4e8aa578 382
9dc90430 383;;;###autoload
4e8aa578
RS
384(defun facemenu-set-invisible (start end)
385 "Make the region invisible.
386This sets the `invisible' text property; it can be undone with
f34eaa2c 387`facemenu-remove-special'."
4e8aa578 388 (interactive "r")
0e3edd7b 389 (add-text-properties start end '(invisible t)))
4e8aa578 390
9dc90430 391;;;###autoload
4e8aa578
RS
392(defun facemenu-set-intangible (start end)
393 "Make the region intangible: disallow moving into it.
394This sets the `intangible' text property; it can be undone with
f34eaa2c 395`facemenu-remove-special'."
4e8aa578 396 (interactive "r")
0e3edd7b 397 (add-text-properties start end '(intangible t)))
4e8aa578 398
9dc90430 399;;;###autoload
4e8aa578
RS
400(defun facemenu-set-read-only (start end)
401 "Make the region unmodifiable.
402This sets the `read-only' text property; it can be undone with
f34eaa2c 403`facemenu-remove-special'."
4e8aa578 404 (interactive "r")
0e3edd7b 405 (add-text-properties start end '(read-only t)))
4e8aa578 406
9dc90430 407;;;###autoload
a32d7856
KH
408(defun facemenu-remove-face-props (start end)
409 "Remove `face' and `mouse-face' text properties."
4e8aa578
RS
410 (interactive "*r") ; error if buffer is read-only despite the next line.
411 (let ((inhibit-read-only t))
412 (remove-text-properties
a32d7856 413 start end '(face nil mouse-face nil))))
4e8aa578 414
f34eaa2c
KH
415;;;###autoload
416(defun facemenu-remove-all (start end)
417 "Remove all text properties from the region."
418 (interactive "*r") ; error if buffer is read-only despite the next line.
419 (let ((inhibit-read-only t))
420 (set-text-properties start end nil)))
421
422;;;###autoload
423(defun facemenu-remove-special (start end)
424 "Remove all the \"special\" text properties from the region.
425These special properties include `invisible', `intangible' and `read-only'."
426 (interactive "*r") ; error if buffer is read-only despite the next line.
427 (let ((inhibit-read-only t))
428 (remove-text-properties
429 start end '(invisible nil intangible nil read-only nil))))
430
c0a7db84
BG
431;;;###autoload
432(defun list-text-properties-at (p)
433 "Pop up a buffer listing text-properties at LOCATION."
434 (interactive "d")
cb5bec6e 435 (let ((props (text-properties-at p))
25a4509f 436 category
cb5bec6e 437 str)
c0a7db84
BG
438 (if (null props)
439 (message "None")
cb5bec6e 440 (if (and (not (cdr (cdr props)))
25a4509f 441 (not (eq (car props) 'category))
cb5bec6e
RS
442 (< (length (setq str (format "Text property at %d: %s %S"
443 p (car props) (car (cdr props)))))
444 (frame-width)))
f2b7756c 445 (message "%s" str)
cb5bec6e
RS
446 (with-output-to-temp-buffer "*Text Properties*"
447 (princ (format "Text properties at %d:\n\n" p))
448 (while props
25a4509f
RS
449 (if (eq (car props) 'category)
450 (setq category (car (cdr props))))
cb5bec6e
RS
451 (princ (format "%-20s %S\n"
452 (car props) (car (cdr props))))
25a4509f
RS
453 (setq props (cdr (cdr props))))
454 (if category
455 (progn
456 (setq props (symbol-plist category))
457 (princ (format "\nCategory %s:\n\n" category))
458 (while props
459 (princ (format "%-20s %S\n"
460 (car props) (car (cdr props))))
461 (if (eq (car props) 'category)
462 (setq category (car (cdr props))))
463 (setq props (cdr (cdr props)))))))))))
c0a7db84 464
bf7d4561 465;;;###autoload
da627a71 466(defun facemenu-read-color (&optional prompt)
bf7d4561 467 "Read a color using the minibuffer."
da627a71 468 (let ((col (completing-read (or prompt "Color: ")
bf7d4561 469 (or facemenu-color-alist
cb5bec6e 470 (if window-system
f2711a0e
DL
471 (mapcar 'list (x-defined-colors))
472 (mapcar 'list (tty-defined-colors))))
bf7d4561
BG
473 nil t)))
474 (if (equal "" col)
475 nil
476 col)))
4e8aa578 477
88d690a9
RS
478;;;###autoload
479(defun list-colors-display (&optional list)
7c49006b
RS
480 "Display names of defined colors, and show what they look like.
481If the optional argument LIST is non-nil, it should be a list of
482colors to display. Otherwise, this command computes a list
483of colors that the current display can handle."
88d690a9 484 (interactive)
16b6c966
DL
485 (when (null list)
486 (setq list (if window-system
487 (x-defined-colors)
488 (tty-defined-colors)))
489 ;; Delete duplicate colors.
490 (let ((l list))
491 (while (cdr l)
492 (if (facemenu-color-equal (car l) (car (cdr l)))
493 (setcdr l (cdr (cdr l)))
494 (setq l (cdr l))))))
88d690a9
RS
495 (with-output-to-temp-buffer "*Colors*"
496 (save-excursion
497 (set-buffer standard-output)
7dc30d5b 498 (let (s)
88d690a9
RS
499 (while list
500 (setq s (point))
501 (insert (car list))
502 (indent-to 20)
503 (put-text-property s (point) 'face
7dc30d5b 504 (cons 'background-color (car list)))
88d690a9
RS
505 (setq s (point))
506 (insert " " (car list) "\n")
507 (put-text-property s (point) 'face
7dc30d5b 508 (cons 'foreground-color (car list)))
88d690a9
RS
509 (setq list (cdr list)))))))
510
511(defun facemenu-color-equal (a b)
512 "Return t if colors A and B are the same color.
7c49006b
RS
513A and B should be strings naming colors.
514This function queries the window-system server to find out what the
515color names mean. It returns nil if the colors differ or if it can't
516determine the correct answer."
88d690a9 517 (cond ((equal a b) t)
b86b9918 518 ((and (memq window-system '(x w32))
cb5bec6e
RS
519 (equal (x-color-values a) (x-color-values b))))
520 ((eq window-system 'pc)
521 (and (x-color-defined-p a) (x-color-defined-p b)
522 (eq (msdos-color-translate a) (msdos-color-translate b))))))
88d690a9 523
cb5bec6e 524(defun facemenu-add-face (face &optional start end)
4a24b314 525 "Add FACE to text between START and END.
cb5bec6e
RS
526If START is `nil' or START to END is empty, add FACE to next typed character
527instead. For each section of that region that has a different face property,
528FACE will be consed onto it, and other faces that are completely hidden by
529that will be removed from the list.
530If `facemenu-add-face-function' and maybe `facemenu-end-add-face' are non-`nil'
531they are used to set the face information.
bf7d4561
BG
532
533As a special case, if FACE is `default', then the region is left with NO face
534text property. Otherwise, selecting the default face would not have any
cb5bec6e
RS
535effect. See `facemenu-remove-face-function'."
536 (interactive "*xFace: \nr")
537 (if (and (eq face 'default)
538 (not (eq facemenu-remove-face-function t)))
539 (if facemenu-remove-face-function
540 (funcall facemenu-remove-face-function start end)
682e437e
RS
541 (if (and start (< start end))
542 (remove-text-properties start end '(face default))
543 (setq self-insert-face 'default
544 self-insert-face-command this-command)))
cb5bec6e
RS
545 (if facemenu-add-face-function
546 (save-excursion
547 (if end (goto-char end))
548 (save-excursion
549 (if start (goto-char start))
550 (insert-before-markers
551 (funcall facemenu-add-face-function face end)))
552 (if facemenu-end-add-face
553 (insert (if (stringp facemenu-end-add-face)
554 facemenu-end-add-face
555 (funcall facemenu-end-add-face face)))))
556 (if (and start (< start end))
557 (let ((part-start start) part-end)
558 (while (not (= part-start end))
559 (setq part-end (next-single-property-change part-start 'face
560 nil end))
561 (let ((prev (get-text-property part-start 'face)))
562 (put-text-property part-start part-end 'face
563 (if (null prev)
564 face
565 (facemenu-active-faces
566 (cons face
567 (if (listp prev)
568 prev
569 (list prev)))))))
570 (setq part-start part-end)))
571 (setq self-insert-face (if (eq last-command self-insert-face-command)
572 (cons face (if (listp self-insert-face)
573 self-insert-face
574 (list self-insert-face)))
575 face)
576 self-insert-face-command this-command)))))
4a24b314 577
5a79ed26
KH
578(defun facemenu-active-faces (face-list &optional frame)
579 "Return from FACE-LIST those faces that would be used for display.
580This means each face attribute is not specified in a face earlier in FACE-LIST
581and such a face is therefore active when used to display text.
582If the optional argument FRAME is given, use the faces in that frame; otherwise
583use the selected frame. If t, then the global, non-frame faces are used."
584 (let* ((mask-atts (copy-sequence (internal-get-face (car face-list) frame)))
585 (active-list (list (car face-list)))
586 (face-list (cdr face-list))
587 (mask-len (length mask-atts)))
588 (while face-list
589 (if (let ((face-atts (internal-get-face (car face-list) frame))
590 (i mask-len) (useful nil))
591 (while (> (setq i (1- i)) 1)
592 (and (aref face-atts i) (not (aref mask-atts i))
593 (aset mask-atts i (setq useful t))))
594 useful)
595 (setq active-list (cons (car face-list) active-list)))
596 (setq face-list (cdr face-list)))
597 (nreverse active-list)))
4a24b314 598
bf7d4561
BG
599(defun facemenu-get-face (symbol)
600 "Make sure FACE exists.
0351bce7
RS
601If not, create it and add it to the appropriate menu. Return the symbol.
602
603If a window system is in use, and this function creates a face named
604`fg:color', then it sets the foreground to that color. Likewise, `bg:color'
605means to set the background. In either case, if the color is undefined,
606no color is set and a warning is issued."
607 (let ((name (symbol-name symbol))
608 foreground)
609 (cond ((internal-find-face symbol))
610 ((and window-system
611 (or (setq foreground (string-match "^fg:" name))
612 (string-match "^bg:" name)))
613 (let ((face (make-face symbol))
88d690a9 614 (color (substring name 3)))
0351bce7
RS
615 (if (x-color-defined-p color)
616 (if foreground
617 (set-face-foreground face color)
618 (set-face-background face color))
619 (message "Color \"%s\" undefined" color))))
620 (t (make-face symbol))))
621 symbol)
bf7d4561
BG
622
623(defun facemenu-add-new-face (face)
624 "Add a FACE to the appropriate Face menu.
625Automatically called when a new face is created."
626 (let* ((name (symbol-name face))
536f1a10 627 menu docstring
88d690a9
RS
628 (key (cdr (assoc face facemenu-keybindings)))
629 function menu-val)
536f1a10
RS
630 (cond ((string-match "^fg:" name)
631 (setq name (substring name 3))
632 (setq docstring
633 (format "Select foreground color %s for subsequent insertion."
634 name))
635 (setq menu 'facemenu-foreground-menu))
636 ((string-match "^bg:" name)
637 (setq name (substring name 3))
638 (setq docstring
639 (format "Select background color %s for subsequent insertion."
640 name))
641 (setq menu 'facemenu-background-menu))
642 (t
643 (setq docstring
644 (format "Select face `%s' for subsequent insertion."
645 name))
646 (setq menu 'facemenu-face-menu)))
88d690a9
RS
647 (cond ((eq t facemenu-unlisted-faces))
648 ((memq face facemenu-unlisted-faces))
7dc30d5b
RS
649 ;; test against regexps in facemenu-unlisted-faces
650 ((let ((unlisted facemenu-unlisted-faces)
651 (matched nil))
652 (while (and unlisted (not matched))
653 (if (and (stringp (car unlisted))
654 (string-match (car unlisted) name))
655 (setq matched t)
656 (setq unlisted (cdr unlisted))))
657 matched))
88d690a9
RS
658 (key ; has a keyboard equivalent. These go at the front.
659 (setq function (intern (concat "facemenu-set-" name)))
660 (fset function
536f1a10
RS
661 `(lambda ()
662 ,docstring
663 (interactive)
b0383de2 664 (facemenu-set-face (quote ,face))))
88d690a9
RS
665 (define-key 'facemenu-keymap key (cons name function))
666 (define-key menu key (cons name function)))
667 ((facemenu-iterate ; check if equivalent face is already in the menu
668 (lambda (m) (and (listp m)
669 (symbolp (car m))
670 (face-equal (car m) face)))
671 (cdr (symbol-function menu))))
672 (t ; No keyboard equivalent. Figure out where to put it:
673 (setq key (vector face)
674 function 'facemenu-set-face-from-menu
675 menu-val (symbol-function menu))
676 (if (and facemenu-new-faces-at-end
677 (> (length menu-val) 3))
678 (define-key-after menu-val key (cons name function)
679 (car (nth (- (length menu-val) 3) menu-val)))
680 (define-key menu key (cons name function))))))
681 nil) ; Return nil for facemenu-iterate
bf7d4561 682
bf7d4561 683(defun facemenu-complete-face-list (&optional oldlist)
7cd49450 684 "Return list of all faces that look different.
bf7d4561
BG
685Starts with given ALIST of faces, and adds elements only if they display
686differently from any face already on the list.
687The faces on ALIST will end up at the end of the returned list, in reverse
688order."
689 (let ((list (nreverse (mapcar 'car oldlist))))
690 (facemenu-iterate
691 (lambda (new-face)
692 (if (not (memq new-face list))
693 (setq list (cons new-face list)))
694 nil)
695 (nreverse (face-list)))
696 list))
697
4e8aa578
RS
698(defun facemenu-iterate (func iterate-list)
699 "Apply FUNC to each element of LIST until one returns non-nil.
700Returns the non-nil value it found, or nil if all were nil."
701 (while (and iterate-list (not (funcall func (car iterate-list))))
702 (setq iterate-list (cdr iterate-list)))
703 (car iterate-list))
704
705(facemenu-update)
4e8aa578
RS
706
707;;; facemenu.el ends here