Add "Package:" file headers to denote built-in packages.
[bpt/emacs.git] / lisp / facemenu.el
CommitLineData
be010748 1;;; facemenu.el --- create a face menu for interactively adding fonts to text
b578f267 2
0d30b337 3;; Copyright (C) 1994, 1995, 1996, 2001, 2002, 2003, 2004,
114f9c96 4;; 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
4e8aa578 5
5762abec 6;; Author: Boris Goldowsky <boris@gnu.org>
4e8aa578 7;; Keywords: faces
bd78fa1d 8;; Package: emacs
4e8aa578
RS
9
10;; This file is part of GNU Emacs.
11
eb3fa2cf 12;; GNU Emacs is free software: you can redistribute it and/or modify
4e8aa578 13;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
4e8aa578
RS
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
eb3fa2cf 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
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
af1eab21 29;; keybindings, which are shown in the menu.
88d690a9
RS
30;;
31;; The menu also contains submenus for indentation and justification-changing
32;; commands.
4e8aa578 33
4e8aa578 34;;; Usage:
bf7d4561
BG
35;; Selecting a face from the menu or typing the keyboard equivalent will
36;; change the region to use that face. If you use transient-mark-mode and the
37;; region is not active, the face will be remembered and used for the next
38;; insertion. It will be forgotten if you move point or make other
39;; modifications before inserting or typing anything.
4e8aa578 40;;
71296446 41;; Faces can be selected from the keyboard as well.
6be7d8db
RS
42;; The standard keybindings are M-o (or ESC o) + letter:
43;; M-o i = "set italic", M-o b = "set bold", etc.
4e8aa578
RS
44
45;;; Customization:
46;; An alternative set of keybindings that may be easier to type can be set up
88d690a9
RS
47;; using "Alt" or "Hyper" keys. This requires that you either have or create
48;; an Alt or Hyper key on your keyboard. On my keyboard, there is a key
49;; labeled "Alt", but to make it act as an Alt key I have to put this command
50;; into my .xinitrc:
51;; xmodmap -e "add Mod3 = Alt_L"
52;; Or, I can make it into a Hyper key with this:
4e8aa578 53;; xmodmap -e "keysym Alt_L = Hyper_L" -e "add Mod2 = Hyper_L"
88d690a9
RS
54;; Check with local X-perts for how to do it on your system.
55;; Then you can define your keybindings with code like this in your .emacs:
4e8aa578
RS
56;; (setq facemenu-keybindings
57;; '((default . [?\H-d])
58;; (bold . [?\H-b])
59;; (italic . [?\H-i])
88d690a9 60;; (bold-italic . [?\H-l])
4e8aa578 61;; (underline . [?\H-u])))
9086c730 62;; (facemenu-update)
4e8aa578 63;; (setq facemenu-keymap global-map)
88d690a9
RS
64;; (define-key global-map [?\H-c] 'facemenu-set-foreground) ; set fg color
65;; (define-key global-map [?\H-C] 'facemenu-set-background) ; set bg color
4e8aa578 66;;
88d690a9
RS
67;; The order of the faces that appear in the menu and their keybindings can be
68;; controlled by setting the variables `facemenu-keybindings' and
b6a67507
CY
69;; `facemenu-new-faces-at-end'. List faces that you want to use in documents
70;; in `facemenu-listed-faces'.
4e8aa578
RS
71
72;;; Known Problems:
88d690a9
RS
73;; Bold and Italic do not combine to create bold-italic if you select them
74;; both, although most other combinations (eg bold + underline + some color)
75;; do the intuitive thing.
76;;
4e8aa578
RS
77;; There is at present no way to display what the faces look like in
78;; the menu itself.
79;;
80;; `list-faces-display' shows the faces in a different order than
81;; this menu, which could be confusing. I do /not/ sort the list
82;; alphabetically, because I like the default order: it puts the most
83;; basic, common fonts first.
84;;
85;; Please send me any other problems, comments or ideas.
86
87;;; Code:
88
71296446 89(eval-when-compile
0e520006
PA
90 (require 'help)
91 (require 'button))
92
9086c730
RS
93;; Global bindings:
94(define-key global-map [C-down-mouse-2] 'facemenu-menu)
6be7d8db 95(define-key global-map "\M-o" 'facemenu-keymap)
4e8aa578 96
487e6fcb 97(defgroup facemenu nil
8e51619c 98 "Create a face menu for interactively adding fonts to text."
487e6fcb
RS
99 :group 'faces
100 :prefix "facemenu-")
101
102(defcustom facemenu-keybindings
6bdad9ae 103 (mapcar 'purecopy
4e8aa578
RS
104 '((default . "d")
105 (bold . "b")
106 (italic . "i")
88d690a9 107 (bold-italic . "l") ; {bold} intersect {italic} = {l}
6bdad9ae 108 (underline . "u")))
220c969f 109 "Alist of interesting faces and keybindings.
4e8aa578
RS
110Each element is itself a list: the car is the name of the face,
111the next element is the key to use as a keyboard equivalent of the menu item;
9086c730 112the binding is made in `facemenu-keymap'.
4e8aa578
RS
113
114The faces specifically mentioned in this list are put at the top of
b6a67507
CY
115the menu, in the order specified. All other faces which are defined
116in `facemenu-listed-faces' are listed after them, but get no
117keyboard equivalents.
4e8aa578
RS
118
119If you change this variable after loading facemenu.el, you will need to call
487e6fcb
RS
120`facemenu-update' to make it take effect."
121 :type '(repeat (cons face string))
122 :group 'facemenu)
4e8aa578 123
487e6fcb 124(defcustom facemenu-new-faces-at-end t
9201cc28 125 "Where in the menu to insert newly-created faces.
88d690a9 126This should be nil to put them at the top of the menu, or t to put them
487e6fcb
RS
127just before \"Other\" at the end."
128 :type 'boolean
129 :group 'facemenu)
88d690a9 130
ed50f0d2
CY
131(defvar facemenu-unlisted-faces
132 `(modeline region secondary-selection highlight scratch-face
133 ,(purecopy "^font-lock-") ,(purecopy "^gnus-") ,(purecopy "^message-")
134 ,(purecopy "^ediff-") ,(purecopy "^term-") ,(purecopy "^vc-")
135 ,(purecopy "^widget-") ,(purecopy "^custom-") ,(purecopy "^vm-"))
d9f2959e
JB
136 "*List of faces that are of no interest to the user.")
137(make-obsolete-variable 'facemenu-unlisted-faces 'facemenu-listed-faces
b8a2c14a 138 "22.1,\n and has no effect on the Face menu")
ed50f0d2 139
b6a67507 140(defcustom facemenu-listed-faces nil
9201cc28 141 "List of faces to include in the Face menu.
d7beaf53 142Each element should be a symbol, the name of a face.
b6a67507 143The \"basic \" faces in `facemenu-keybindings' are automatically
d7beaf53
RS
144added to the Face menu, and need not be in this list.
145
146This value takes effect when you load facemenu.el. If the
147list includes symbols which are not defined as faces, they
148are ignored; however, subsequently defining or creating
149those faces adds them to the menu then. You can call
150`facemenu-update' to recalculate the menu contents, such as
151if you change the value of this variable,
152
153If this variable is t, all faces that you apply to text
154using the face menu commands (even by name), and all faces
155that you define or create, are added to the menu. You may
156find it useful to set this variable to t temporarily while
157you define some faces, so that they will be added. However,
158if the value is no longer t and you call `facemenu-update',
159it will remove any faces not explicitly in the list."
b6a67507
CY
160 :type '(choice (const :tag "List all faces" t)
161 (const :tag "None" nil)
162 (repeat symbol))
163 :group 'facemenu
164 :version "22.1")
88d690a9
RS
165
166(defvar facemenu-face-menu
bf7d4561 167 (let ((map (make-sparse-keymap "Face")))
1e8780b1 168 (define-key map "o" (cons (purecopy "Other...") 'facemenu-set-face))
bf7d4561
BG
169 map)
170 "Menu keymap for faces.")
88d690a9 171(defalias 'facemenu-face-menu facemenu-face-menu)
6c763f36 172(put 'facemenu-face-menu 'menu-enable '(facemenu-enable-faces-p))
bf7d4561 173
71296446 174(defvar facemenu-foreground-menu
bf7d4561 175 (let ((map (make-sparse-keymap "Foreground Color")))
1e8780b1 176 (define-key map "o" (cons (purecopy "Other...") 'facemenu-set-foreground))
bf7d4561
BG
177 map)
178 "Menu keymap for foreground colors.")
88d690a9 179(defalias 'facemenu-foreground-menu facemenu-foreground-menu)
6c763f36 180(put 'facemenu-foreground-menu 'menu-enable '(facemenu-enable-faces-p))
bf7d4561
BG
181
182(defvar facemenu-background-menu
183 (let ((map (make-sparse-keymap "Background Color")))
1e8780b1 184 (define-key map "o" (cons (purecopy "Other...") 'facemenu-set-background))
bf7d4561 185 map)
7e6cb513 186 "Menu keymap for background colors.")
88d690a9 187(defalias 'facemenu-background-menu facemenu-background-menu)
6c763f36
RS
188(put 'facemenu-background-menu 'menu-enable '(facemenu-enable-faces-p))
189
190;;; Condition for enabling menu items that set faces.
191(defun facemenu-enable-faces-p ()
673c1168
CY
192 ;; Enable the facemenu if facemenu-add-face-function is defined
193 ;; (e.g. in Tex-mode and SGML mode), or if font-lock is off.
194 (or (not (and font-lock-mode font-lock-defaults))
195 facemenu-add-face-function))
bf7d4561 196
71296446 197(defvar facemenu-special-menu
bf7d4561 198 (let ((map (make-sparse-keymap "Special")))
2d07ff84
DL
199 (define-key map [?s] (cons (purecopy "Remove Special")
200 'facemenu-remove-special))
201 (define-key map [?t] (cons (purecopy "Intangible")
202 'facemenu-set-intangible))
203 (define-key map [?v] (cons (purecopy "Invisible")
204 'facemenu-set-invisible))
205 (define-key map [?r] (cons (purecopy "Read-Only")
206 'facemenu-set-read-only))
bf7d4561
BG
207 map)
208 "Menu keymap for non-face text-properties.")
88d690a9
RS
209(defalias 'facemenu-special-menu facemenu-special-menu)
210
211(defvar facemenu-justification-menu
212 (let ((map (make-sparse-keymap "Justification")))
2d07ff84
DL
213 (define-key map [?c] (cons (purecopy "Center") 'set-justification-center))
214 (define-key map [?b] (cons (purecopy "Full") 'set-justification-full))
215 (define-key map [?r] (cons (purecopy "Right") 'set-justification-right))
216 (define-key map [?l] (cons (purecopy "Left") 'set-justification-left))
217 (define-key map [?u] (cons (purecopy "Unfilled") 'set-justification-none))
88d690a9
RS
218 map)
219 "Submenu for text justification commands.")
220(defalias 'facemenu-justification-menu facemenu-justification-menu)
221
222(defvar facemenu-indentation-menu
223 (let ((map (make-sparse-keymap "Indentation")))
71296446 224 (define-key map [decrease-right-margin]
2d07ff84 225 (cons (purecopy "Indent Right Less") 'decrease-right-margin))
f34eaa2c 226 (define-key map [increase-right-margin]
2d07ff84 227 (cons (purecopy "Indent Right More") 'increase-right-margin))
f34eaa2c 228 (define-key map [decrease-left-margin]
2d07ff84 229 (cons (purecopy "Indent Less") 'decrease-left-margin))
f34eaa2c 230 (define-key map [increase-left-margin]
2d07ff84 231 (cons (purecopy "Indent More") 'increase-left-margin))
88d690a9
RS
232 map)
233 "Submenu for indentation commands.")
234(defalias 'facemenu-indentation-menu facemenu-indentation-menu)
bf7d4561 235
f34eaa2c 236;; This is split up to avoid an overlong line in loaddefs.el.
f34eaa2c 237(defvar facemenu-menu nil
535d2617 238 "Facemenu top-level menu keymap.")
f34eaa2c 239(setq facemenu-menu (make-sparse-keymap "Text Properties"))
f34eaa2c 240(let ((map facemenu-menu))
2d07ff84
DL
241 (define-key map [dc] (cons (purecopy "Display Colors") 'list-colors-display))
242 (define-key map [df] (cons (purecopy "Display Faces") 'list-faces-display))
cea5ec30
RS
243 (define-key map [dp] (cons (purecopy "Describe Properties")
244 'describe-text-properties))
2d07ff84
DL
245 (define-key map [ra] (cons (purecopy "Remove Text Properties")
246 'facemenu-remove-all))
247 (define-key map [rm] (cons (purecopy "Remove Face Properties")
248 'facemenu-remove-face-props))
249 (define-key map [s1] (list (purecopy "--"))))
f34eaa2c 250(let ((map facemenu-menu))
71296446 251 (define-key map [in] (cons (purecopy "Indentation")
2d07ff84
DL
252 'facemenu-indentation-menu))
253 (define-key map [ju] (cons (purecopy "Justification")
254 'facemenu-justification-menu))
255 (define-key map [s2] (list (purecopy "--")))
71296446 256 (define-key map [sp] (cons (purecopy "Special Properties")
2d07ff84 257 'facemenu-special-menu))
71296446 258 (define-key map [bg] (cons (purecopy "Background Color")
2d07ff84 259 'facemenu-background-menu))
71296446 260 (define-key map [fg] (cons (purecopy "Foreground Color")
2d07ff84 261 'facemenu-foreground-menu))
71296446 262 (define-key map [fc] (cons (purecopy "Face")
2d07ff84 263 'facemenu-face-menu)))
88d690a9 264(defalias 'facemenu-menu facemenu-menu)
bf7d4561 265
71296446 266(defvar facemenu-keymap
88d690a9 267 (let ((map (make-sparse-keymap "Set face")))
2d07ff84 268 (define-key map "o" (cons (purecopy "Other...") 'facemenu-set-face))
ef49d20f 269 (define-key map "\M-o" 'font-lock-fontify-block)
88d690a9 270 map)
9dc90430 271 "Keymap for face-changing commands.
bf7d4561 272`Facemenu-update' fills in the keymap according to the bindings
535d2617 273requested in `facemenu-keybindings'.")
88d690a9 274(defalias 'facemenu-keymap facemenu-keymap)
bf7d4561 275
cb5bec6e 276
487e6fcb 277(defcustom facemenu-add-face-function nil
7e6cb513 278 "Function called at beginning of text to change or nil.
cb5bec6e 279This function is passed the FACE to set and END of text to change, and must
487e6fcb
RS
280return a string which is inserted. It may set `facemenu-end-add-face'."
281 :type '(choice (const :tag "None" nil)
282 function)
283 :group 'facemenu)
cb5bec6e 284
487e6fcb 285(defcustom facemenu-end-add-face nil
7e6cb513 286 "String to insert or function called at end of text to change or nil.
cb5bec6e 287This function is passed the FACE to set, and must return a string which is
487e6fcb
RS
288inserted."
289 :type '(choice (const :tag "None" nil)
290 string
291 function)
292 :group 'facemenu)
cb5bec6e 293
487e6fcb 294(defcustom facemenu-remove-face-function nil
9086c730 295 "When non-nil, this is a function called to remove faces.
cb5bec6e 296This function is passed the START and END of text to change.
7e6cb513 297May also be t meaning to use `facemenu-add-face-function'."
487e6fcb
RS
298 :type '(choice (const :tag "None" nil)
299 (const :tag "Use add-face" t)
300 function)
301 :group 'facemenu)
cb5bec6e 302
bf7d4561
BG
303;;; Internal Variables
304
305(defvar facemenu-color-alist nil
bf7d4561 306 "Alist of colors, used for completion.
a926c9ce 307If this is nil, then the value of (defined-colors) is used.")
4a24b314 308
4e8aa578 309(defun facemenu-update ()
bf7d4561
BG
310 "Add or update the \"Face\" menu in the menu bar.
311You can call this to update things if you change any of the menu configuration
312variables."
4e8aa578 313 (interactive)
4e8aa578 314
bf7d4561
BG
315 ;; Add each defined face to the menu.
316 (facemenu-iterate 'facemenu-add-new-face
317 (facemenu-complete-face-list facemenu-keybindings)))
4a24b314 318
4e8aa578 319(defun facemenu-set-face (face &optional start end)
cd7890bd
RS
320 "Apply FACE to the region or next character typed.
321
322If the region is active (normally true except in Transient
323Mark mode) and nonempty, and there is no prefix argument,
324this command applies FACE to the region. Otherwise, it applies FACE
325to the faces to use for the next character
326inserted. (Moving point or switching buffers before typing
327a character to insert cancels the specification.)
328
329If FACE is `default', to \"apply\" it means clearing
330the list of faces to be used. For any other value of FACE,
331to \"apply\" it means putting FACE at the front of the list
332of faces to be used, and removing any faces further
333along in the list that would be completely overridden by
334preceding faces (including FACE).
335
336This command can also add FACE to the menu of faces,
337if `facemenu-listed-faces' says to do that."
7d8177cf
RS
338 (interactive (list (progn
339 (barf-if-buffer-read-only)
340 (read-face-name "Use face"))
341 (if (and mark-active (not current-prefix-arg))
342 (region-beginning))
343 (if (and mark-active (not current-prefix-arg))
344 (region-end))))
88d690a9 345 (facemenu-add-new-face face)
7d8177cf 346 (facemenu-add-face face start end))
4a24b314
RS
347
348(defun facemenu-set-foreground (color &optional start end)
7e6cb513 349 "Set the foreground COLOR of the region or next character typed.
af1eab21 350This command reads the color in the minibuffer.
7d8177cf
RS
351
352If the region is active (normally true except in Transient Mark mode)
353and there is no prefix argument, this command sets the region to the
354requested face.
355
356Otherwise, this command specifies the face for the next character
357inserted. Moving point or switching buffers before
71296446 358typing a character to insert cancels the specification."
7d8177cf
RS
359 (interactive (list (progn
360 (barf-if-buffer-read-only)
361 (facemenu-read-color "Foreground color: "))
362 (if (and mark-active (not current-prefix-arg))
363 (region-beginning))
364 (if (and mark-active (not current-prefix-arg))
365 (region-end))))
b97c98ad
LK
366 (facemenu-set-face-from-menu
367 (facemenu-add-new-color color 'facemenu-foreground-menu)
368 start end))
4a24b314
RS
369
370(defun facemenu-set-background (color &optional start end)
7e6cb513 371 "Set the background COLOR of the region or next character typed.
af1eab21 372This command reads the color in the minibuffer.
7d8177cf
RS
373
374If the region is active (normally true except in Transient Mark mode)
375and there is no prefix argument, this command sets the region to the
376requested face.
377
378Otherwise, this command specifies the face for the next character
379inserted. Moving point or switching buffers before
71296446 380typing a character to insert cancels the specification."
7d8177cf
RS
381 (interactive (list (progn
382 (barf-if-buffer-read-only)
383 (facemenu-read-color "Background color: "))
384 (if (and mark-active (not current-prefix-arg))
385 (region-beginning))
386 (if (and mark-active (not current-prefix-arg))
387 (region-end))))
b97c98ad
LK
388 (facemenu-set-face-from-menu
389 (facemenu-add-new-color color 'facemenu-background-menu)
390 start end))
4e8aa578
RS
391
392(defun facemenu-set-face-from-menu (face start end)
7e6cb513 393 "Set the FACE of the region or next character typed.
b97c98ad
LK
394This function is designed to be called from a menu; FACE is determined
395using the event type of the menu entry. If FACE is a symbol whose
396name starts with \"fg:\" or \"bg:\", then this functions sets the
397foreground or background to the color specified by the rest of the
398symbol's name. Any other symbol is considered the name of a face.
f34eaa2c 399
7d8177cf
RS
400If the region is active (normally true except in Transient Mark mode)
401and there is no prefix argument, this command sets the region to the
402requested face.
f34eaa2c
KH
403
404Otherwise, this command specifies the face for the next character
b97c98ad
LK
405inserted. Moving point or switching buffers before typing a character
406to insert cancels the specification."
4a24b314 407 (interactive (list last-command-event
f34eaa2c
KH
408 (if (and mark-active (not current-prefix-arg))
409 (region-beginning))
410 (if (and mark-active (not current-prefix-arg))
411 (region-end))))
88d690a9 412 (barf-if-buffer-read-only)
b97c98ad
LK
413 (facemenu-add-face
414 (let ((fn (symbol-name face)))
415 (if (string-match "\\`\\([fb]\\)g:\\(.+\\)" fn)
416 (list (list (if (string= (match-string 1 fn) "f")
417 :foreground
418 :background)
419 (match-string 2 fn)))
420 face))
421 start end))
4e8aa578
RS
422
423(defun facemenu-set-invisible (start end)
424 "Make the region invisible.
425This sets the `invisible' text property; it can be undone with
f34eaa2c 426`facemenu-remove-special'."
4e8aa578 427 (interactive "r")
0e3edd7b 428 (add-text-properties start end '(invisible t)))
4e8aa578
RS
429
430(defun facemenu-set-intangible (start end)
431 "Make the region intangible: disallow moving into it.
432This sets the `intangible' text property; it can be undone with
f34eaa2c 433`facemenu-remove-special'."
4e8aa578 434 (interactive "r")
0e3edd7b 435 (add-text-properties start end '(intangible t)))
4e8aa578
RS
436
437(defun facemenu-set-read-only (start end)
438 "Make the region unmodifiable.
439This sets the `read-only' text property; it can be undone with
f34eaa2c 440`facemenu-remove-special'."
4e8aa578 441 (interactive "r")
0e3edd7b 442 (add-text-properties start end '(read-only t)))
4e8aa578 443
a32d7856
KH
444(defun facemenu-remove-face-props (start end)
445 "Remove `face' and `mouse-face' text properties."
4e8aa578
RS
446 (interactive "*r") ; error if buffer is read-only despite the next line.
447 (let ((inhibit-read-only t))
71296446 448 (remove-text-properties
a32d7856 449 start end '(face nil mouse-face nil))))
4e8aa578 450
f34eaa2c
KH
451(defun facemenu-remove-all (start end)
452 "Remove all text properties from the region."
453 (interactive "*r") ; error if buffer is read-only despite the next line.
454 (let ((inhibit-read-only t))
455 (set-text-properties start end nil)))
456
f34eaa2c
KH
457(defun facemenu-remove-special (start end)
458 "Remove all the \"special\" text properties from the region.
459These special properties include `invisible', `intangible' and `read-only'."
460 (interactive "*r") ; error if buffer is read-only despite the next line.
461 (let ((inhibit-read-only t))
71296446 462 (remove-text-properties
f34eaa2c 463 start end '(invisible nil intangible nil read-only nil))))
0af1db42 464\f
da627a71 465(defun facemenu-read-color (&optional prompt)
bf7d4561 466 "Read a color using the minibuffer."
5bcc074b 467 (let* ((completion-ignore-case t)
daad00fc
CY
468 (color-list (or facemenu-color-alist (defined-colors)))
469 (completer
470 (lambda (string pred all-completions)
471 (if all-completions
472 (or (all-completions string color-list pred)
473 (if (color-defined-p string)
474 (list string)))
475 (or (try-completion string color-list pred)
476 (if (color-defined-p string)
477 string)))))
478 (col (completing-read (or prompt "Color: ") completer nil t)))
bf7d4561
BG
479 (if (equal "" col)
480 nil
481 col)))
4e8aa578 482
f0bf7c8e
JL
483(defun color-rgb-to-hsv (r g b)
484 "For R, G, B color components return a list of hue, saturation, value.
485R, G, B input values should be in [0..65535] range.
486Output values for hue are integers in [0..360] range.
487Output values for saturation and value are integers in [0..100] range."
488 (let* ((r (/ r 65535.0))
489 (g (/ g 65535.0))
490 (b (/ b 65535.0))
491 (max (max r g b))
492 (min (min r g b))
493 (h (cond ((= max min) 0)
494 ((= max r) (mod (+ (* 60 (/ (- g b) (- max min))) 360) 360))
495 ((= max g) (+ (* 60 (/ (- b r) (- max min))) 120))
496 ((= max b) (+ (* 60 (/ (- r g) (- max min))) 240))))
497 (s (cond ((= max 0) 0)
498 (t (- 1 (/ min max)))))
499 (v max))
500 (list (round h) (round s 0.01) (round v 0.01))))
501
502(defcustom list-colors-sort nil
503 "Color sort order for `list-colors-display'.
504`nil' means default implementation-dependent order (defined in `x-colors').
505`name' sorts by color name.
506`rgb' sorts by red, green, blue components.
8fd02581 507`(rgb-dist . COLOR)' sorts by the RGB distance to the specified color.
f0bf7c8e 508`hsv' sorts by hue, saturation, value.
8fd02581 509`(hsv-dist . COLOR)' sorts by the HSV distance to the specified color
f0bf7c8e
JL
510and excludes grayscale colors."
511 :type '(choice (const :tag "Unsorted" nil)
512 (const :tag "Color Name" name)
513 (const :tag "Red-Green-Blue" rgb)
514 (cons :tag "Distance on RGB cube"
515 (const :tag "Distance from Color" rgb-dist)
516 (color :tag "Source Color Name"))
517 (const :tag "Hue-Saturation-Value" hsv)
518 (cons :tag "Distance on HSV cylinder"
519 (const :tag "Distance from Color" hsv-dist)
520 (color :tag "Source Color Name")))
521 :group 'facemenu
522 :version "24.1")
523
524(defun list-colors-sort-key (color)
525 "Return a list of keys for sorting colors depending on `list-colors-sort'.
526COLOR is the name of the color. When return value is nil,
527filter out the color from the output."
528 (cond
529 ((null list-colors-sort) color)
530 ((eq list-colors-sort 'name)
531 (downcase color))
532 ((eq list-colors-sort 'rgb)
533 (color-values color))
534 ((eq (car-safe list-colors-sort) 'rgb-dist)
535 (color-distance color (cdr list-colors-sort)))
536 ((eq list-colors-sort 'hsv)
537 (apply 'color-rgb-to-hsv (color-values color)))
538 ((eq (car-safe list-colors-sort) 'hsv-dist)
539 (let* ((c-rgb (color-values color))
540 (c-hsv (apply 'color-rgb-to-hsv c-rgb))
541 (o-hsv (apply 'color-rgb-to-hsv
542 (color-values (cdr list-colors-sort)))))
543 (unless (and (eq (nth 0 c-rgb) (nth 1 c-rgb)) ; exclude grayscale
544 (eq (nth 1 c-rgb) (nth 2 c-rgb)))
545 ;; 3D Euclidean distance (sqrt is not needed for sorting)
546 (+ (expt (- 180 (abs (- 180 (abs (- (nth 0 c-hsv) ; wrap hue
547 (nth 0 o-hsv)))))) 2)
548 (expt (- (nth 1 c-hsv) (nth 1 o-hsv)) 2)
549 (expt (- (nth 2 c-hsv) (nth 2 o-hsv)) 2)))))))
6f320937
CY
550
551(defun list-colors-display (&optional list buffer-name callback)
7c49006b
RS
552 "Display names of defined colors, and show what they look like.
553If the optional argument LIST is non-nil, it should be a list of
066a23af 554colors to display. Otherwise, this command computes a list of
6f320937
CY
555colors that the current display can handle.
556
557If the optional argument BUFFER-NAME is nil, it defaults to
558*Colors*.
559
560If the optional argument CALLBACK is non-nil, it should be a
561function to call each time the user types RET or clicks on a
562color. The function should accept a single argument, the color
f0bf7c8e
JL
563name.
564
565You can change the color sort order by customizing `list-colors-sort'."
88d690a9 566 (interactive)
6062889d 567 (when (and (null list) (> (display-color-cells) 0))
066a23af 568 (setq list (list-colors-duplicates (defined-colors)))
f0bf7c8e
JL
569 (when list-colors-sort
570 ;; Schwartzian transform with `(color key1 key2 key3 ...)'.
571 (setq list (mapcar
572 'car
573 (sort (delq nil (mapcar
574 (lambda (c)
575 (let ((key (list-colors-sort-key
576 (car c))))
577 (when key
578 (cons c (if (consp key) key
579 (list key))))))
580 list))
581 (lambda (a b)
582 (let* ((a-keys (cdr a))
583 (b-keys (cdr b))
584 (a-key (car a-keys))
585 (b-key (car b-keys)))
586 ;; Skip common keys at the beginning of key lists.
587 (while (and a-key b-key (equal a-key b-key))
588 (setq a-keys (cdr a-keys) a-key (car a-keys)
589 b-keys (cdr b-keys) b-key (car b-keys)))
590 (cond
591 ((and (numberp a-key) (numberp b-key))
592 (< a-key b-key))
593 ((and (stringp a-key) (stringp b-key))
594 (string< a-key b-key)))))))))
d2596700
MB
595 (when (memq (display-visual-class) '(gray-scale pseudo-color direct-color))
596 ;; Don't show more than what the display can handle.
597 (let ((lc (nthcdr (1- (display-color-cells)) list)))
598 (if lc
599 (setcdr lc nil)))))
6f320937
CY
600 (let ((buf (get-buffer-create "*Colors*")))
601 (with-current-buffer buf
602 (erase-buffer)
066a23af 603 (setq truncate-lines t)
e020fb59
JL
604 ;; Display buffer before generating content to allow
605 ;; `list-colors-print' to get the right window-width.
606 (pop-to-buffer buf)
6f320937 607 (list-colors-print list callback)
e020fb59 608 (set-buffer-modified-p nil)))
6f320937
CY
609 (if callback
610 (message "Click on a color to select it.")))
611
612(defun list-colors-print (list &optional callback)
613 (let ((callback-fn
614 (if callback
615 `(lambda (button)
616 (funcall ,callback (button-get button 'color-name))))))
617 (dolist (color list)
618 (if (consp color)
619 (if (cdr color)
620 (setq color (sort color (lambda (a b)
621 (string< (downcase a)
622 (downcase b))))))
623 (setq color (list color)))
624 (let* ((opoint (point))
625 (color-values (color-values (car color)))
626 (light-p (>= (apply 'max color-values)
89877f5f
CY
627 (* (car (color-values "white")) .5)))
628 (max-len (max (- (window-width) 33) 20)))
6f320937
CY
629 (insert (car color))
630 (indent-to 22)
631 (put-text-property opoint (point) 'face `(:background ,(car color)))
632 (put-text-property
633 (prog1 (point)
89877f5f
CY
634 (insert " ")
635 (if (cdr color)
636 ;; Insert as many color names as possible, fitting max-len.
637 (let ((names (list (car color)))
638 (others (cdr color))
639 (len (length (car color)))
640 newlen)
641 (while (and others
642 (< (setq newlen (+ len 2 (length (car others))))
643 max-len))
644 (setq len newlen)
645 (push (pop others) names))
646 (insert (mapconcat 'identity (nreverse names) ", ")))
647 (insert (car color))))
6f320937
CY
648 (point)
649 'face (list :foreground (car color)))
650 (indent-to (max (- (window-width) 8) 44))
f0bf7c8e
JL
651 (insert (propertize
652 (apply 'format "#%02x%02x%02x"
653 (mapcar (lambda (c) (lsh c -8))
654 color-values))
655 'mouse-face 'highlight
656 'help-echo
657 (let ((hsv (apply 'color-rgb-to-hsv
658 (color-values (car color)))))
659 (format "H:%d S:%d V:%d"
660 (nth 0 hsv) (nth 1 hsv) (nth 2 hsv)))))
6f320937
CY
661 (when callback
662 (make-text-button
663 opoint (point)
664 'follow-link t
665 'mouse-face (list :background (car color)
666 :foreground (if light-p "black" "white"))
667 'color-name (car color)
668 'action callback-fn)))
669 (insert "\n"))
670 (goto-char (point-min))))
671
066a23af
JL
672
673(defun list-colors-duplicates (&optional list)
674 "Return a list of colors with grouped duplicate colors.
675If a color has no duplicates, then the element of the returned list
676has the form '(COLOR-NAME). The element of the returned list with
677duplicate colors has the form '(COLOR-NAME DUPLICATE-COLOR-NAME ...).
678This function uses the predicate `facemenu-color-equal' to compare
679color names. If the optional argument LIST is non-nil, it should
680be a list of colors to display. Otherwise, this function uses
681a list of colors that the current display can handle."
682 (let* ((list (mapcar 'list (or list (defined-colors))))
683 (l list))
684 (while (cdr l)
685 (if (and (facemenu-color-equal (car (car l)) (car (car (cdr l))))
c3f9cd46
JR
686 (not (if (fboundp 'w32-default-color-map)
687 (not (assoc (car (car l)) (w32-default-color-map))))))
066a23af
JL
688 (progn
689 (setcdr (car l) (cons (car (car (cdr l))) (cdr (car l))))
690 (setcdr l (cdr (cdr l))))
691 (setq l (cdr l))))
692 list))
88d690a9
RS
693
694(defun facemenu-color-equal (a b)
695 "Return t if colors A and B are the same color.
7c49006b 696A and B should be strings naming colors.
f795f633
EZ
697This function queries the display system to find out what the color
698names mean. It returns nil if the colors differ or if it can't
7c49006b 699determine the correct answer."
88d690a9 700 (cond ((equal a b) t)
f795f633 701 ((equal (color-values a) (color-values b)))))
88d690a9 702
a2e5caf7
SM
703
704(defvar facemenu-self-insert-data nil)
705
706(defun facemenu-post-self-insert-function ()
707 (when (and (car facemenu-self-insert-data)
708 (eq last-command (cdr facemenu-self-insert-data)))
709 (put-text-property (1- (point)) (point)
710 'face (car facemenu-self-insert-data))
711 (setq facemenu-self-insert-data nil))
712 (remove-hook 'post-self-insert-hook 'facemenu-post-self-insert-function))
713
714(defun facemenu-set-self-insert-face (face)
715 "Arrange for the next self-inserted char to have face `face'."
716 (setq facemenu-self-insert-data (cons face this-command))
717 (add-hook 'post-self-insert-hook 'facemenu-post-self-insert-function))
718
cb5bec6e 719(defun facemenu-add-face (face &optional start end)
4a24b314 720 "Add FACE to text between START and END.
7e6cb513 721If START is nil or START to END is empty, add FACE to next typed character
cb5bec6e
RS
722instead. For each section of that region that has a different face property,
723FACE will be consed onto it, and other faces that are completely hidden by
724that will be removed from the list.
af1eab21 725If `facemenu-add-face-function' and maybe `facemenu-end-add-face' are non-nil,
cb5bec6e 726they are used to set the face information.
bf7d4561
BG
727
728As a special case, if FACE is `default', then the region is left with NO face
729text property. Otherwise, selecting the default face would not have any
cb5bec6e
RS
730effect. See `facemenu-remove-face-function'."
731 (interactive "*xFace: \nr")
a2e5caf7
SM
732 (cond
733 ((and (eq face 'default)
734 (not (eq facemenu-remove-face-function t)))
735 (if facemenu-remove-face-function
736 (funcall facemenu-remove-face-function start end)
cb5bec6e 737 (if (and start (< start end))
a2e5caf7
SM
738 (remove-text-properties start end '(face default))
739 (facemenu-set-self-insert-face 'default))))
740 (facemenu-add-face-function
741 (save-excursion
742 (if end (goto-char end))
743 (save-excursion
744 (if start (goto-char start))
745 (insert-before-markers
746 (funcall facemenu-add-face-function face end)))
747 (if facemenu-end-add-face
748 (insert (if (stringp facemenu-end-add-face)
749 facemenu-end-add-face
750 (funcall facemenu-end-add-face face))))))
751 ((and start (< start end))
752 (let ((part-start start) part-end)
753 (while (not (= part-start end))
754 (setq part-end (next-single-property-change part-start 'face
755 nil end))
756 (let ((prev (get-text-property part-start 'face)))
757 (put-text-property part-start part-end 'face
758 (if (null prev)
759 face
760 (facemenu-active-faces
761 (cons face
762 (if (listp prev)
763 prev
764 (list prev)))
765 ;; Specify the selected frame
766 ;; because nil would mean to use
767 ;; the new-frame default settings,
768 ;; and those are usually nil.
769 (selected-frame)))))
770 (setq part-start part-end))))
771 (t
772 (facemenu-set-self-insert-face
773 (if (eq last-command (cdr facemenu-self-insert-data))
774 (cons face (if (listp (car facemenu-self-insert-data))
775 (car facemenu-self-insert-data)
776 (list (car facemenu-self-insert-data))))
777 face))))
97a7aa7b
RS
778 (unless (facemenu-enable-faces-p)
779 (message "Font-lock mode will override any faces you set in this buffer")))
4a24b314 780
5a79ed26
KH
781(defun facemenu-active-faces (face-list &optional frame)
782 "Return from FACE-LIST those faces that would be used for display.
783This means each face attribute is not specified in a face earlier in FACE-LIST
784and such a face is therefore active when used to display text.
785If the optional argument FRAME is given, use the faces in that frame; otherwise
786use the selected frame. If t, then the global, non-frame faces are used."
7d8177cf
RS
787 (let* ((mask-atts (copy-sequence
788 (if (consp (car face-list))
f790dddf 789 (face-attributes-as-vector (car face-list))
7d8177cf
RS
790 (or (internal-lisp-face-p (car face-list) frame)
791 (check-face (car face-list))))))
5a79ed26
KH
792 (active-list (list (car face-list)))
793 (face-list (cdr face-list))
794 (mask-len (length mask-atts)))
795 (while face-list
7d8177cf
RS
796 (if (let ((face-atts
797 (if (consp (car face-list))
f790dddf 798 (face-attributes-as-vector (car face-list))
7d8177cf
RS
799 (or (internal-lisp-face-p (car face-list) frame)
800 (check-face (car face-list)))))
801 (i mask-len)
802 (useful nil))
240c0c90 803 (while (>= (setq i (1- i)) 0)
7d8177cf
RS
804 (and (not (memq (aref face-atts i) '(nil unspecified)))
805 (memq (aref mask-atts i) '(nil unspecified))
5a79ed26
KH
806 (aset mask-atts i (setq useful t))))
807 useful)
808 (setq active-list (cons (car face-list) active-list)))
809 (setq face-list (cdr face-list)))
810 (nreverse active-list)))
4a24b314 811
9bf4c4e5 812(defun facemenu-add-new-face (face)
cd7890bd
RS
813 "Add FACE (a face) to the Face menu if `facemenu-listed-faces' says so.
814This is called whenever you create a new face, and at other times."
7d8177cf
RS
815 (let* (name
816 symbol
9bf4c4e5
RS
817 menu docstring
818 (key (cdr (assoc face facemenu-keybindings)))
88d690a9 819 function menu-val)
9bf4c4e5
RS
820 (if (symbolp face)
821 (setq name (symbol-name face)
822 symbol face)
823 (setq name face
1d792b18 824 symbol (intern name)))
9bf4c4e5
RS
825 (setq menu 'facemenu-face-menu)
826 (setq docstring
1e8780b1 827 (purecopy (format "Select face `%s' for subsequent insertion.
3187841b
RS
828If the mark is active and there is no prefix argument,
829apply face `%s' to the region instead.
830This command was defined by `facemenu-add-new-face'."
1e8780b1 831 name name)))
b6a67507
CY
832 (cond ((facemenu-iterate ; check if equivalent face is already in the menu
833 (lambda (m) (and (listp m)
834 (symbolp (car m))
f3359de1
RS
835 ;; Avoid error in face-equal
836 ;; when a non-face is erroneously present.
837 (facep (car m))
b6a67507
CY
838 (face-equal (car m) symbol)))
839 (cdr (symbol-function menu))))
840 ;; Faces with a keyboard equivalent. These go at the front.
841 (key
88d690a9
RS
842 (setq function (intern (concat "facemenu-set-" name)))
843 (fset function
536f1a10
RS
844 `(lambda ()
845 ,docstring
846 (interactive)
af1eab21
RS
847 (facemenu-set-face
848 (quote ,symbol)
849 (if (and mark-active (not current-prefix-arg))
850 (region-beginning))
851 (if (and mark-active (not current-prefix-arg))
852 (region-end)))))
88d690a9
RS
853 (define-key 'facemenu-keymap key (cons name function))
854 (define-key menu key (cons name function)))
b6a67507
CY
855 ;; Faces with no keyboard equivalent. Figure out where to put it:
856 ((or (eq t facemenu-listed-faces)
857 (memq symbol facemenu-listed-faces))
7d8177cf 858 (setq key (vector symbol)
88d690a9
RS
859 function 'facemenu-set-face-from-menu
860 menu-val (symbol-function menu))
861 (if (and facemenu-new-faces-at-end
b6a67507 862 (> (length menu-val) 3))
88d690a9 863 (define-key-after menu-val key (cons name function)
9bf4c4e5
RS
864 (car (nth (- (length menu-val) 3) menu-val)))
865 (define-key menu key (cons name function))))))
866 nil) ; Return nil for facemenu-iterate
867
019b1899 868(defun facemenu-add-new-color (color menu)
9bf4c4e5 869 "Add COLOR (a color name string) to the appropriate Face menu.
019b1899 870MENU should be `facemenu-foreground-menu' or `facemenu-background-menu'.
b97c98ad 871Return the event type (a symbol) of the added menu entry.
9bf4c4e5
RS
872
873This is called whenever you use a new color."
019b1899
LK
874 (let (symbol docstring)
875 (unless (color-defined-p color)
876 (error "Color `%s' undefined" color))
9bf4c4e5
RS
877 (cond ((eq menu 'facemenu-foreground-menu)
878 (setq docstring
879 (format "Select foreground color %s for subsequent insertion."
019b1899 880 color)
b97c98ad 881 symbol (intern (concat "fg:" color))))
9bf4c4e5
RS
882 ((eq menu 'facemenu-background-menu)
883 (setq docstring
884 (format "Select background color %s for subsequent insertion."
019b1899 885 color)
b97c98ad 886 symbol (intern (concat "bg:" color))))
019b1899 887 (t (error "MENU should be `facemenu-foreground-menu' or `facemenu-background-menu'")))
b97c98ad
LK
888 (unless (facemenu-iterate ; Check if color is already in the menu.
889 (lambda (m) (and (listp m)
890 (eq (car m) symbol)))
891 (cdr (symbol-function menu)))
892 ;; Color is not in the menu. Figure out where to put it.
893 (let ((key (vector symbol))
894 (function 'facemenu-set-face-from-menu)
895 (menu-val (symbol-function menu)))
896 (if (and facemenu-new-faces-at-end
897 (> (length menu-val) 3))
898 (define-key-after menu-val key (cons color function)
899 (car (nth (- (length menu-val) 3) menu-val)))
900 (define-key menu key (cons color function)))))
019b1899 901 symbol))
bf7d4561 902
bf7d4561 903(defun facemenu-complete-face-list (&optional oldlist)
7cd49450 904 "Return list of all faces that look different.
71296446 905Starts with given ALIST of faces, and adds elements only if they display
bf7d4561 906differently from any face already on the list.
71296446 907The faces on ALIST will end up at the end of the returned list, in reverse
bf7d4561
BG
908order."
909 (let ((list (nreverse (mapcar 'car oldlist))))
71296446
JB
910 (facemenu-iterate
911 (lambda (new-face)
bf7d4561
BG
912 (if (not (memq new-face list))
913 (setq list (cons new-face list)))
914 nil)
915 (nreverse (face-list)))
916 list))
917
7e6cb513 918(defun facemenu-iterate (func list)
4e8aa578
RS
919 "Apply FUNC to each element of LIST until one returns non-nil.
920Returns the non-nil value it found, or nil if all were nil."
7e6cb513
SM
921 (while (and list (not (funcall func (car list))))
922 (setq list (cdr list)))
923 (car list))
4e8aa578
RS
924
925(facemenu-update)
4e8aa578 926
1ff4ace5 927(provide 'facemenu)
ab5796a9 928
cbee283d 929;; arch-tag: 85f6d02b-9085-420e-b651-0678f0e9c7eb
4e8aa578 930;;; facemenu.el ends here