(replace_buffer_in_all_windows):
[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
7865eac6 5;; Author: Boris Goldowsky <boris@gnu.ai.mit.edu>
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))
239 (define-key map [ra] (cons "Remove All" 'facemenu-remove-all))
240 (define-key map [rm] (cons "Remove Properties" 'facemenu-remove-props))
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
f34eaa2c 408(defun facemenu-remove-props (start end)
4e8aa578
RS
409 "Remove all text properties that facemenu added to region."
410 (interactive "*r") ; error if buffer is read-only despite the next line.
411 (let ((inhibit-read-only t))
412 (remove-text-properties
413 start end '(face nil invisible nil intangible nil
414 read-only nil category nil))))
415
f34eaa2c
KH
416;;;###autoload
417(defun facemenu-remove-all (start end)
418 "Remove all text properties from the region."
419 (interactive "*r") ; error if buffer is read-only despite the next line.
420 (let ((inhibit-read-only t))
421 (set-text-properties start end nil)))
422
423;;;###autoload
424(defun facemenu-remove-special (start end)
425 "Remove all the \"special\" text properties from the region.
426These special properties include `invisible', `intangible' and `read-only'."
427 (interactive "*r") ; error if buffer is read-only despite the next line.
428 (let ((inhibit-read-only t))
429 (remove-text-properties
430 start end '(invisible nil intangible nil read-only nil))))
431
c0a7db84
BG
432;;;###autoload
433(defun list-text-properties-at (p)
434 "Pop up a buffer listing text-properties at LOCATION."
435 (interactive "d")
cb5bec6e 436 (let ((props (text-properties-at p))
25a4509f 437 category
cb5bec6e 438 str)
c0a7db84
BG
439 (if (null props)
440 (message "None")
cb5bec6e 441 (if (and (not (cdr (cdr props)))
25a4509f 442 (not (eq (car props) 'category))
cb5bec6e
RS
443 (< (length (setq str (format "Text property at %d: %s %S"
444 p (car props) (car (cdr props)))))
445 (frame-width)))
f2b7756c 446 (message "%s" str)
cb5bec6e
RS
447 (with-output-to-temp-buffer "*Text Properties*"
448 (princ (format "Text properties at %d:\n\n" p))
449 (while props
25a4509f
RS
450 (if (eq (car props) 'category)
451 (setq category (car (cdr props))))
cb5bec6e
RS
452 (princ (format "%-20s %S\n"
453 (car props) (car (cdr props))))
25a4509f
RS
454 (setq props (cdr (cdr props))))
455 (if category
456 (progn
457 (setq props (symbol-plist category))
458 (princ (format "\nCategory %s:\n\n" category))
459 (while props
460 (princ (format "%-20s %S\n"
461 (car props) (car (cdr props))))
462 (if (eq (car props) 'category)
463 (setq category (car (cdr props))))
464 (setq props (cdr (cdr props)))))))))))
c0a7db84 465
bf7d4561 466;;;###autoload
da627a71 467(defun facemenu-read-color (&optional prompt)
bf7d4561 468 "Read a color using the minibuffer."
da627a71 469 (let ((col (completing-read (or prompt "Color: ")
bf7d4561 470 (or facemenu-color-alist
cb5bec6e 471 (if window-system
bf7d4561
BG
472 (mapcar 'list (x-defined-colors))))
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)
cb5bec6e 485 (if (and (null list) window-system)
7c49006b
RS
486 (progn
487 (setq list (x-defined-colors))
488 ;; Delete duplicate colors.
489 (let ((l list))
490 (while (cdr l)
491 (if (facemenu-color-equal (car l) (car (cdr l)))
492 (setcdr l (cdr (cdr l)))
493 (setq l (cdr l)))))))
88d690a9
RS
494 (with-output-to-temp-buffer "*Colors*"
495 (save-excursion
496 (set-buffer standard-output)
7dc30d5b 497 (let (s)
88d690a9
RS
498 (while list
499 (setq s (point))
500 (insert (car list))
501 (indent-to 20)
502 (put-text-property s (point) 'face
7dc30d5b 503 (cons 'background-color (car list)))
88d690a9
RS
504 (setq s (point))
505 (insert " " (car list) "\n")
506 (put-text-property s (point) 'face
7dc30d5b 507 (cons 'foreground-color (car list)))
88d690a9
RS
508 (setq list (cdr list)))))))
509
510(defun facemenu-color-equal (a b)
511 "Return t if colors A and B are the same color.
7c49006b
RS
512A and B should be strings naming colors.
513This function queries the window-system server to find out what the
514color names mean. It returns nil if the colors differ or if it can't
515determine the correct answer."
88d690a9 516 (cond ((equal a b) t)
b86b9918 517 ((and (memq window-system '(x w32))
cb5bec6e
RS
518 (equal (x-color-values a) (x-color-values b))))
519 ((eq window-system 'pc)
520 (and (x-color-defined-p a) (x-color-defined-p b)
521 (eq (msdos-color-translate a) (msdos-color-translate b))))))
88d690a9 522
cb5bec6e 523(defun facemenu-add-face (face &optional start end)
4a24b314 524 "Add FACE to text between START and END.
cb5bec6e
RS
525If START is `nil' or START to END is empty, add FACE to next typed character
526instead. For each section of that region that has a different face property,
527FACE will be consed onto it, and other faces that are completely hidden by
528that will be removed from the list.
529If `facemenu-add-face-function' and maybe `facemenu-end-add-face' are non-`nil'
530they are used to set the face information.
bf7d4561
BG
531
532As a special case, if FACE is `default', then the region is left with NO face
533text property. Otherwise, selecting the default face would not have any
cb5bec6e
RS
534effect. See `facemenu-remove-face-function'."
535 (interactive "*xFace: \nr")
536 (if (and (eq face 'default)
537 (not (eq facemenu-remove-face-function t)))
538 (if facemenu-remove-face-function
539 (funcall facemenu-remove-face-function start end)
682e437e
RS
540 (if (and start (< start end))
541 (remove-text-properties start end '(face default))
542 (setq self-insert-face 'default
543 self-insert-face-command this-command)))
cb5bec6e
RS
544 (if facemenu-add-face-function
545 (save-excursion
546 (if end (goto-char end))
547 (save-excursion
548 (if start (goto-char start))
549 (insert-before-markers
550 (funcall facemenu-add-face-function face end)))
551 (if facemenu-end-add-face
552 (insert (if (stringp facemenu-end-add-face)
553 facemenu-end-add-face
554 (funcall facemenu-end-add-face face)))))
555 (if (and start (< start end))
556 (let ((part-start start) part-end)
557 (while (not (= part-start end))
558 (setq part-end (next-single-property-change part-start 'face
559 nil end))
560 (let ((prev (get-text-property part-start 'face)))
561 (put-text-property part-start part-end 'face
562 (if (null prev)
563 face
564 (facemenu-active-faces
565 (cons face
566 (if (listp prev)
567 prev
568 (list prev)))))))
569 (setq part-start part-end)))
570 (setq self-insert-face (if (eq last-command self-insert-face-command)
571 (cons face (if (listp self-insert-face)
572 self-insert-face
573 (list self-insert-face)))
574 face)
575 self-insert-face-command this-command)))))
4a24b314 576
5a79ed26
KH
577(defun facemenu-active-faces (face-list &optional frame)
578 "Return from FACE-LIST those faces that would be used for display.
579This means each face attribute is not specified in a face earlier in FACE-LIST
580and such a face is therefore active when used to display text.
581If the optional argument FRAME is given, use the faces in that frame; otherwise
582use the selected frame. If t, then the global, non-frame faces are used."
583 (let* ((mask-atts (copy-sequence (internal-get-face (car face-list) frame)))
584 (active-list (list (car face-list)))
585 (face-list (cdr face-list))
586 (mask-len (length mask-atts)))
587 (while face-list
588 (if (let ((face-atts (internal-get-face (car face-list) frame))
589 (i mask-len) (useful nil))
590 (while (> (setq i (1- i)) 1)
591 (and (aref face-atts i) (not (aref mask-atts i))
592 (aset mask-atts i (setq useful t))))
593 useful)
594 (setq active-list (cons (car face-list) active-list)))
595 (setq face-list (cdr face-list)))
596 (nreverse active-list)))
4a24b314 597
bf7d4561
BG
598(defun facemenu-get-face (symbol)
599 "Make sure FACE exists.
0351bce7
RS
600If not, create it and add it to the appropriate menu. Return the symbol.
601
602If a window system is in use, and this function creates a face named
603`fg:color', then it sets the foreground to that color. Likewise, `bg:color'
604means to set the background. In either case, if the color is undefined,
605no color is set and a warning is issued."
606 (let ((name (symbol-name symbol))
607 foreground)
608 (cond ((internal-find-face symbol))
609 ((and window-system
610 (or (setq foreground (string-match "^fg:" name))
611 (string-match "^bg:" name)))
612 (let ((face (make-face symbol))
88d690a9 613 (color (substring name 3)))
0351bce7
RS
614 (if (x-color-defined-p color)
615 (if foreground
616 (set-face-foreground face color)
617 (set-face-background face color))
618 (message "Color \"%s\" undefined" color))))
619 (t (make-face symbol))))
620 symbol)
bf7d4561
BG
621
622(defun facemenu-add-new-face (face)
623 "Add a FACE to the appropriate Face menu.
624Automatically called when a new face is created."
625 (let* ((name (symbol-name face))
536f1a10 626 menu docstring
88d690a9
RS
627 (key (cdr (assoc face facemenu-keybindings)))
628 function menu-val)
536f1a10
RS
629 (cond ((string-match "^fg:" name)
630 (setq name (substring name 3))
631 (setq docstring
632 (format "Select foreground color %s for subsequent insertion."
633 name))
634 (setq menu 'facemenu-foreground-menu))
635 ((string-match "^bg:" name)
636 (setq name (substring name 3))
637 (setq docstring
638 (format "Select background color %s for subsequent insertion."
639 name))
640 (setq menu 'facemenu-background-menu))
641 (t
642 (setq docstring
643 (format "Select face `%s' for subsequent insertion."
644 name))
645 (setq menu 'facemenu-face-menu)))
88d690a9
RS
646 (cond ((eq t facemenu-unlisted-faces))
647 ((memq face facemenu-unlisted-faces))
7dc30d5b
RS
648 ;; test against regexps in facemenu-unlisted-faces
649 ((let ((unlisted facemenu-unlisted-faces)
650 (matched nil))
651 (while (and unlisted (not matched))
652 (if (and (stringp (car unlisted))
653 (string-match (car unlisted) name))
654 (setq matched t)
655 (setq unlisted (cdr unlisted))))
656 matched))
88d690a9
RS
657 (key ; has a keyboard equivalent. These go at the front.
658 (setq function (intern (concat "facemenu-set-" name)))
659 (fset function
536f1a10
RS
660 `(lambda ()
661 ,docstring
662 (interactive)
b0383de2 663 (facemenu-set-face (quote ,face))))
88d690a9
RS
664 (define-key 'facemenu-keymap key (cons name function))
665 (define-key menu key (cons name function)))
666 ((facemenu-iterate ; check if equivalent face is already in the menu
667 (lambda (m) (and (listp m)
668 (symbolp (car m))
669 (face-equal (car m) face)))
670 (cdr (symbol-function menu))))
671 (t ; No keyboard equivalent. Figure out where to put it:
672 (setq key (vector face)
673 function 'facemenu-set-face-from-menu
674 menu-val (symbol-function menu))
675 (if (and facemenu-new-faces-at-end
676 (> (length menu-val) 3))
677 (define-key-after menu-val key (cons name function)
678 (car (nth (- (length menu-val) 3) menu-val)))
679 (define-key menu key (cons name function))))))
680 nil) ; Return nil for facemenu-iterate
bf7d4561 681
bf7d4561 682(defun facemenu-complete-face-list (&optional oldlist)
7cd49450 683 "Return list of all faces that look different.
bf7d4561
BG
684Starts with given ALIST of faces, and adds elements only if they display
685differently from any face already on the list.
686The faces on ALIST will end up at the end of the returned list, in reverse
687order."
688 (let ((list (nreverse (mapcar 'car oldlist))))
689 (facemenu-iterate
690 (lambda (new-face)
691 (if (not (memq new-face list))
692 (setq list (cons new-face list)))
693 nil)
694 (nreverse (face-list)))
695 list))
696
4e8aa578
RS
697(defun facemenu-iterate (func iterate-list)
698 "Apply FUNC to each element of LIST until one returns non-nil.
699Returns the non-nil value it found, or nil if all were nil."
700 (while (and iterate-list (not (funcall func (car iterate-list))))
701 (setq iterate-list (cdr iterate-list)))
702 (car iterate-list))
703
704(facemenu-update)
4e8aa578
RS
705
706;;; facemenu.el ends here