Document problems with DEC C 5.9.x on Digital Unix 4.0f.
[bpt/emacs.git] / lisp / facemenu.el
CommitLineData
be010748 1;;; facemenu.el --- create a face menu for interactively adding fonts to text
b578f267 2
0e520006 3;; Copyright (c) 1994, 1995, 1996, 2001, 2002 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
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
RS
40;;
41;; Faces can be selected from the keyboard as well.
88d690a9
RS
42;; The standard keybindings are M-g (or ESC g) + letter:
43;; M-g i = "set italic", M-g 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
69;; `facemenu-new-faces-at-end'. List faces that you don't use in documents
70;; (eg, `region') in `facemenu-unlisted-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
89(provide 'facemenu)
90
0e520006
PA
91(eval-when-compile
92 (require 'help)
93 (require 'button))
772f363f 94(require 'wid-edit)
0e520006 95
9dc90430
BG
96;;; Provide some binding for startup:
97;;;###autoload (define-key global-map "\M-g" 'facemenu-keymap)
98;;;###autoload (autoload 'facemenu-keymap "facemenu" "Keymap for face-changing commands." t 'keymap)
9086c730
RS
99
100;; Global bindings:
101(define-key global-map [C-down-mouse-2] 'facemenu-menu)
102(define-key global-map "\M-g" 'facemenu-keymap)
4e8aa578 103
487e6fcb
RS
104(defgroup facemenu nil
105 "Create a face menu for interactively adding fonts to text"
106 :group 'faces
107 :prefix "facemenu-")
108
109(defcustom facemenu-keybindings
4e8aa578
RS
110 '((default . "d")
111 (bold . "b")
112 (italic . "i")
88d690a9 113 (bold-italic . "l") ; {bold} intersect {italic} = {l}
4e8aa578 114 (underline . "u"))
220c969f 115 "Alist of interesting faces and keybindings.
4e8aa578
RS
116Each element is itself a list: the car is the name of the face,
117the next element is the key to use as a keyboard equivalent of the menu item;
9086c730 118the binding is made in `facemenu-keymap'.
4e8aa578
RS
119
120The faces specifically mentioned in this list are put at the top of
121the menu, in the order specified. All other faces which are defined,
122except for those in `facemenu-unlisted-faces', are listed after them,
123but get no keyboard equivalents.
124
125If you change this variable after loading facemenu.el, you will need to call
487e6fcb
RS
126`facemenu-update' to make it take effect."
127 :type '(repeat (cons face string))
128 :group 'facemenu)
4e8aa578 129
487e6fcb 130(defcustom facemenu-new-faces-at-end t
9086c730 131 "*Where in the menu to insert newly-created faces.
88d690a9 132This should be nil to put them at the top of the menu, or t to put them
487e6fcb
RS
133just before \"Other\" at the end."
134 :type 'boolean
135 :group 'facemenu)
88d690a9 136
487e6fcb 137(defcustom facemenu-unlisted-faces
74a723de
DL
138 `(modeline region secondary-selection highlight scratch-face
139 ,(purecopy "^font-lock-") ,(purecopy "^gnus-") ,(purecopy "^message-")
140 ,(purecopy "^ediff-") ,(purecopy "^term-") ,(purecopy "^vc-")
141 ,(purecopy "^widget-") ,(purecopy "^custom-") ,(purecopy "^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 154when they are created."
7d8177cf
RS
155 :type '(choice (const :tag "Don't add faces" t)
156 (const :tag "None (do add any face)" 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 182 map)
7e6cb513 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")))
2d07ff84
DL
190 (define-key map [?s] (cons (purecopy "Remove Special")
191 'facemenu-remove-special))
192 (define-key map [?t] (cons (purecopy "Intangible")
193 'facemenu-set-intangible))
194 (define-key map [?v] (cons (purecopy "Invisible")
195 'facemenu-set-invisible))
196 (define-key map [?r] (cons (purecopy "Read-Only")
197 'facemenu-set-read-only))
bf7d4561
BG
198 map)
199 "Menu keymap for non-face text-properties.")
9dc90430 200;;;###autoload
88d690a9
RS
201(defalias 'facemenu-special-menu facemenu-special-menu)
202
9dc90430 203;;;###autoload
88d690a9
RS
204(defvar facemenu-justification-menu
205 (let ((map (make-sparse-keymap "Justification")))
2d07ff84
DL
206 (define-key map [?c] (cons (purecopy "Center") 'set-justification-center))
207 (define-key map [?b] (cons (purecopy "Full") 'set-justification-full))
208 (define-key map [?r] (cons (purecopy "Right") 'set-justification-right))
209 (define-key map [?l] (cons (purecopy "Left") 'set-justification-left))
210 (define-key map [?u] (cons (purecopy "Unfilled") 'set-justification-none))
88d690a9
RS
211 map)
212 "Submenu for text justification commands.")
9dc90430 213;;;###autoload
88d690a9
RS
214(defalias 'facemenu-justification-menu facemenu-justification-menu)
215
9dc90430 216;;;###autoload
88d690a9
RS
217(defvar facemenu-indentation-menu
218 (let ((map (make-sparse-keymap "Indentation")))
f34eaa2c 219 (define-key map [decrease-right-margin]
2d07ff84 220 (cons (purecopy "Indent Right Less") 'decrease-right-margin))
f34eaa2c 221 (define-key map [increase-right-margin]
2d07ff84 222 (cons (purecopy "Indent Right More") 'increase-right-margin))
f34eaa2c 223 (define-key map [decrease-left-margin]
2d07ff84 224 (cons (purecopy "Indent Less") 'decrease-left-margin))
f34eaa2c 225 (define-key map [increase-left-margin]
2d07ff84 226 (cons (purecopy "Indent More") 'increase-left-margin))
88d690a9
RS
227 map)
228 "Submenu for indentation commands.")
9dc90430 229;;;###autoload
88d690a9 230(defalias 'facemenu-indentation-menu facemenu-indentation-menu)
bf7d4561 231
f34eaa2c 232;; This is split up to avoid an overlong line in loaddefs.el.
9dc90430 233;;;###autoload
f34eaa2c 234(defvar facemenu-menu nil
535d2617 235 "Facemenu top-level menu keymap.")
9dc90430 236;;;###autoload
f34eaa2c
KH
237(setq facemenu-menu (make-sparse-keymap "Text Properties"))
238;;;###autoload
239(let ((map facemenu-menu))
2d07ff84
DL
240 (define-key map [dc] (cons (purecopy "Display Colors") 'list-colors-display))
241 (define-key map [df] (cons (purecopy "Display Faces") 'list-faces-display))
0e520006
PA
242 (define-key map [dp] (cons (purecopy "Describe Text")
243 'describe-text-at))
2d07ff84
DL
244 (define-key map [ra] (cons (purecopy "Remove Text Properties")
245 'facemenu-remove-all))
246 (define-key map [rm] (cons (purecopy "Remove Face Properties")
247 'facemenu-remove-face-props))
248 (define-key map [s1] (list (purecopy "--"))))
f34eaa2c
KH
249;;;###autoload
250(let ((map facemenu-menu))
2d07ff84
DL
251 (define-key map [in] (cons (purecopy "Indentation")
252 'facemenu-indentation-menu))
253 (define-key map [ju] (cons (purecopy "Justification")
254 'facemenu-justification-menu))
255 (define-key map [s2] (list (purecopy "--")))
256 (define-key map [sp] (cons (purecopy "Special Properties")
257 'facemenu-special-menu))
258 (define-key map [bg] (cons (purecopy "Background Color")
259 'facemenu-background-menu))
260 (define-key map [fg] (cons (purecopy "Foreground Color")
261 'facemenu-foreground-menu))
262 (define-key map [fc] (cons (purecopy "Face")
263 'facemenu-face-menu)))
f34eaa2c 264;;;###autoload
88d690a9 265(defalias 'facemenu-menu facemenu-menu)
bf7d4561 266
88d690a9
RS
267(defvar facemenu-keymap
268 (let ((map (make-sparse-keymap "Set face")))
2d07ff84 269 (define-key map "o" (cons (purecopy "Other...") 'facemenu-set-face))
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
306 ;; Don't initialize here; that doesn't work if preloaded.
307 "Alist of colors, used for completion.
308If null, `facemenu-read-color' will set it.")
4a24b314 309
4e8aa578 310(defun facemenu-update ()
bf7d4561
BG
311 "Add or update the \"Face\" menu in the menu bar.
312You can call this to update things if you change any of the menu configuration
313variables."
4e8aa578 314 (interactive)
4e8aa578 315
bf7d4561
BG
316 ;; Add each defined face to the menu.
317 (facemenu-iterate 'facemenu-add-new-face
318 (facemenu-complete-face-list facemenu-keybindings)))
4a24b314 319
4e8aa578
RS
320;;;###autoload
321(defun facemenu-set-face (face &optional start end)
4a24b314 322 "Add FACE to the region or next character typed.
7d8177cf 323This adds FACE to the top of the face list; any faces lower on the list that
4a24b314
RS
324will not show through at all will be removed.
325
7d8177cf 326Interactively, reads the face name with the minibuffer.
f34eaa2c 327
7d8177cf
RS
328If the region is active (normally true except in Transient Mark mode)
329and there is no prefix argument, this command sets the region to the
330requested face.
f34eaa2c
KH
331
332Otherwise, this command specifies the face for the next character
333inserted. Moving point or switching buffers before
334typing a character to insert cancels the specification."
7d8177cf
RS
335 (interactive (list (progn
336 (barf-if-buffer-read-only)
337 (read-face-name "Use face"))
338 (if (and mark-active (not current-prefix-arg))
339 (region-beginning))
340 (if (and mark-active (not current-prefix-arg))
341 (region-end))))
88d690a9 342 (facemenu-add-new-face face)
7d8177cf 343 (facemenu-add-face face start end))
4a24b314 344
bf7d4561 345;;;###autoload
4a24b314 346(defun facemenu-set-foreground (color &optional start end)
7e6cb513 347 "Set the foreground COLOR of the region or next character typed.
af1eab21 348This command reads the color in the minibuffer.
7d8177cf
RS
349
350If the region is active (normally true except in Transient Mark mode)
351and there is no prefix argument, this command sets the region to the
352requested face.
353
354Otherwise, this command specifies the face for the next character
355inserted. Moving point or switching buffers before
356typing a character to insert cancels the specification."
357 (interactive (list (progn
358 (barf-if-buffer-read-only)
359 (facemenu-read-color "Foreground color: "))
360 (if (and mark-active (not current-prefix-arg))
361 (region-beginning))
362 (if (and mark-active (not current-prefix-arg))
363 (region-end))))
364 (unless (color-defined-p color)
365 (message "Color `%s' undefined" color))
366 (facemenu-add-new-face color 'facemenu-foreground-menu)
367 (facemenu-add-face (list (list :foreground color)) start end))
4a24b314 368
bf7d4561 369;;;###autoload
4a24b314 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
380typing a character to insert cancels the specification."
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))))
388 (unless (color-defined-p color)
389 (message "Color `%s' undefined" color))
390 (facemenu-add-new-face color 'facemenu-background-menu)
391 (facemenu-add-face (list (list :background color)) start end))
4e8aa578 392
9dc90430 393;;;###autoload
4e8aa578 394(defun facemenu-set-face-from-menu (face start end)
7e6cb513 395 "Set the FACE of the region or next character typed.
4e8aa578
RS
396This function is designed to be called from a menu; the face to use
397is the menu item's name.
f34eaa2c 398
7d8177cf
RS
399If the region is active (normally true except in Transient Mark mode)
400and there is no prefix argument, this command sets the region to the
401requested face.
f34eaa2c
KH
402
403Otherwise, this command specifies the face for the next character
404inserted. Moving point or switching buffers before
405typing a character to insert cancels the specification."
4a24b314 406 (interactive (list last-command-event
f34eaa2c
KH
407 (if (and mark-active (not current-prefix-arg))
408 (region-beginning))
409 (if (and mark-active (not current-prefix-arg))
410 (region-end))))
88d690a9 411 (barf-if-buffer-read-only)
4a24b314 412 (facemenu-get-face face)
4e8aa578 413 (if start
4a24b314 414 (facemenu-add-face face start end)
cb5bec6e 415 (facemenu-add-face face)))
4e8aa578 416
9dc90430 417;;;###autoload
4e8aa578
RS
418(defun facemenu-set-invisible (start end)
419 "Make the region invisible.
420This sets the `invisible' text property; it can be undone with
f34eaa2c 421`facemenu-remove-special'."
4e8aa578 422 (interactive "r")
0e3edd7b 423 (add-text-properties start end '(invisible t)))
4e8aa578 424
9dc90430 425;;;###autoload
4e8aa578
RS
426(defun facemenu-set-intangible (start end)
427 "Make the region intangible: disallow moving into it.
428This sets the `intangible' text property; it can be undone with
f34eaa2c 429`facemenu-remove-special'."
4e8aa578 430 (interactive "r")
0e3edd7b 431 (add-text-properties start end '(intangible t)))
4e8aa578 432
9dc90430 433;;;###autoload
4e8aa578
RS
434(defun facemenu-set-read-only (start end)
435 "Make the region unmodifiable.
436This sets the `read-only' text property; it can be undone with
f34eaa2c 437`facemenu-remove-special'."
4e8aa578 438 (interactive "r")
0e3edd7b 439 (add-text-properties start end '(read-only t)))
4e8aa578 440
9dc90430 441;;;###autoload
a32d7856
KH
442(defun facemenu-remove-face-props (start end)
443 "Remove `face' and `mouse-face' text properties."
4e8aa578
RS
444 (interactive "*r") ; error if buffer is read-only despite the next line.
445 (let ((inhibit-read-only t))
446 (remove-text-properties
a32d7856 447 start end '(face nil mouse-face nil))))
4e8aa578 448
f34eaa2c
KH
449;;;###autoload
450(defun facemenu-remove-all (start end)
451 "Remove all text properties from the region."
452 (interactive "*r") ; error if buffer is read-only despite the next line.
453 (let ((inhibit-read-only t))
454 (set-text-properties start end nil)))
455
456;;;###autoload
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))
462 (remove-text-properties
463 start end '(invisible nil intangible nil read-only nil))))
464
0e520006
PA
465;;; Describe-Text Mode.
466
467(defun describe-text-done ()
468 "Delete the current window or bury the current buffer."
469 (interactive)
470 (if (> (count-windows) 1)
471 (delete-window)
472 (bury-buffer)))
473
474(defvar describe-text-mode-map
475 (let ((map (make-sparse-keymap)))
9b2d1d40 476 (set-keymap-parent map widget-keymap)
0e520006
PA
477 map)
478 "Keymap for `describe-text-mode'.")
479
480(defcustom describe-text-mode-hook nil
481 "List of hook functions ran by `describe-text-mode'."
482 :type 'hook)
483
484(defun describe-text-mode ()
485 "Major mode for buffers created by `describe-text-at'.
486
487\\{describe-text-mode-map}
488Entry to this mode calls the value of `describe-text-mode-hook'
489if that value is non-nil."
490 (kill-all-local-variables)
491 (setq major-mode 'describe-text-mode
492 mode-name "Describe-Text")
493 (use-local-map describe-text-mode-map)
494 (widget-setup)
495 (run-hooks 'describe-text-mode-hook))
496
497;;; Describe-Text Utilities.
498
499(defun describe-text-widget (widget)
500 "Insert text to describe WIDGET in the current buffer."
501 (widget-create 'link
502 :notify `(lambda (&rest ignore)
503 (widget-browse ',widget))
504 (format "%S" (if (symbolp widget)
505 widget
506 (car widget))))
507 (widget-insert " ")
508 (widget-create 'info-link :tag "widget" "(widget)Top"))
509
510(defun describe-text-sexp (sexp)
511 "Insert a short description of SEXP in the current buffer."
512 (let ((pp (condition-case signal
513 (pp-to-string sexp)
514 (error (prin1-to-string signal)))))
515 (when (string-match "\n\\'" pp)
516 (setq pp (substring pp 0 (1- (length pp)))))
517 (if (cond ((string-match "\n" pp)
518 nil)
519 ((> (length pp) (- (window-width) (current-column)))
520 nil)
521 (t t))
522 (widget-insert pp)
523 (widget-create 'push-button
524 :tag "show"
525 :action (lambda (widget &optional event)
526 (with-output-to-temp-buffer
527 "*Pp Eval Output*"
528 (princ (widget-get widget :value))))
529 pp))))
530
531
532(defun describe-text-properties (properties)
533 "Insert a description of PROPERTIES in the current buffer.
534PROPERTIES should be a list of overlay or text properties.
535The `category' property is made into a widget button that call
536`describe-text-category' when pushed."
537 (while properties
538 (widget-insert (format " %-20s " (car properties)))
539 (let ((key (nth 0 properties))
540 (value (nth 1 properties)))
541 (cond ((eq key 'category)
542 (widget-create 'link
543 :notify `(lambda (&rest ignore)
544 (describe-text-category ',value))
545 (format "%S" value)))
546 ((widgetp value)
547 (describe-text-widget value))
548 (t
549 (describe-text-sexp value))))
550 (widget-insert "\n")
551 (setq properties (cdr (cdr properties)))))
552
553;;; Describe-Text Commands.
554
555(defun describe-text-category (category)
556 "Describe a text property category."
557 (interactive "S")
558 (when (get-buffer "*Text Category*")
559 (kill-buffer "*Text Category*"))
560 (save-excursion
561 (with-output-to-temp-buffer "*Text Category*"
562 (set-buffer "*Text Category*")
563 (widget-insert "Category " (format "%S" category) ":\n\n")
564 (describe-text-properties (symbol-plist category))
565 (describe-text-mode)
566 (goto-char (point-min)))))
567
568;;;###autoload
569(defun describe-text-at (pos)
570 "Describe widgets, buttons, overlays and text properties at POS."
571 (interactive "d")
572 (when (eq (current-buffer) (get-buffer "*Text Description*"))
573 (error "Can't do self inspection"))
574 (let* ((properties (text-properties-at pos))
575 (overlays (overlays-at pos))
576 overlay
577 (wid-field (get-char-property pos 'field))
578 (wid-button (get-char-property pos 'button))
579 (wid-doc (get-char-property pos 'widget-doc))
580 ;; If button.el is not loaded, we have no buttons in the text.
581 (button (and (fboundp 'button-at) (button-at pos)))
582 (button-type (and button (button-type button)))
583 (button-label (and button (button-label button)))
584 (widget (or wid-field wid-button wid-doc)))
585 (if (not (or properties overlays))
586 (message "This is plain text.")
587 (when (get-buffer "*Text Description*")
588 (kill-buffer "*Text Description*"))
589 (save-excursion
590 (with-output-to-temp-buffer "*Text Description*"
591 (set-buffer "*Text Description*")
592 (widget-insert "Text content at position " (format "%d" pos) ":\n\n")
593 ;; Widgets
594 (when (widgetp widget)
595 (widget-insert (cond (wid-field "This is an editable text area")
596 (wid-button "This is an active area")
597 (wid-doc "This is documentation text")))
598 (widget-insert " of a ")
599 (describe-text-widget widget)
600 (widget-insert ".\n\n"))
601 ;; Buttons
602 (when (and button (not (widgetp wid-button)))
603 (widget-insert "Here is a " (format "%S" button-type)
604 " button labeled `" button-label "'.\n\n"))
605 ;; Overlays
606 (when overlays
607 (if (eq (length overlays) 1)
608 (widget-insert "There is an overlay here:\n")
609 (widget-insert "There are " (format "%d" (length overlays))
610 " overlays here:\n"))
611 (dolist (overlay overlays)
612 (widget-insert " From " (format "%d" (overlay-start overlay))
613 " to " (format "%d" (overlay-end overlay)) "\n")
614 (describe-text-properties (overlay-properties overlay)))
615 (widget-insert "\n"))
616 ;; Text properties
617 (when properties
618 (widget-insert "There are text properties here:\n")
619 (describe-text-properties properties))
620 (describe-text-mode)
621 (goto-char (point-min)))))))
622
623;;; List Text Properties
624
c0a7db84
BG
625;;;###autoload
626(defun list-text-properties-at (p)
627 "Pop up a buffer listing text-properties at LOCATION."
628 (interactive "d")
cb5bec6e 629 (let ((props (text-properties-at p))
25a4509f 630 category
cb5bec6e 631 str)
c0a7db84
BG
632 (if (null props)
633 (message "None")
cb5bec6e 634 (if (and (not (cdr (cdr props)))
25a4509f 635 (not (eq (car props) 'category))
cb5bec6e
RS
636 (< (length (setq str (format "Text property at %d: %s %S"
637 p (car props) (car (cdr props)))))
638 (frame-width)))
f2b7756c 639 (message "%s" str)
cb5bec6e
RS
640 (with-output-to-temp-buffer "*Text Properties*"
641 (princ (format "Text properties at %d:\n\n" p))
37d5af8a 642 (setq help-xref-stack nil)
cb5bec6e 643 (while props
25a4509f
RS
644 (if (eq (car props) 'category)
645 (setq category (car (cdr props))))
cb5bec6e
RS
646 (princ (format "%-20s %S\n"
647 (car props) (car (cdr props))))
25a4509f
RS
648 (setq props (cdr (cdr props))))
649 (if category
650 (progn
651 (setq props (symbol-plist category))
652 (princ (format "\nCategory %s:\n\n" category))
653 (while props
654 (princ (format "%-20s %S\n"
655 (car props) (car (cdr props))))
656 (if (eq (car props) 'category)
657 (setq category (car (cdr props))))
658 (setq props (cdr (cdr props)))))))))))
c0a7db84 659
bf7d4561 660;;;###autoload
da627a71 661(defun facemenu-read-color (&optional prompt)
bf7d4561 662 "Read a color using the minibuffer."
da627a71 663 (let ((col (completing-read (or prompt "Color: ")
bf7d4561 664 (or facemenu-color-alist
f795f633 665 (mapcar 'list (defined-colors)))
bf7d4561
BG
666 nil t)))
667 (if (equal "" col)
668 nil
669 col)))
4e8aa578 670
88d690a9
RS
671;;;###autoload
672(defun list-colors-display (&optional list)
7c49006b
RS
673 "Display names of defined colors, and show what they look like.
674If the optional argument LIST is non-nil, it should be a list of
675colors to display. Otherwise, this command computes a list
676of colors that the current display can handle."
88d690a9 677 (interactive)
6062889d 678 (when (and (null list) (> (display-color-cells) 0))
f795f633 679 (setq list (defined-colors))
16b6c966
DL
680 ;; Delete duplicate colors.
681 (let ((l list))
682 (while (cdr l)
683 (if (facemenu-color-equal (car l) (car (cdr l)))
684 (setcdr l (cdr (cdr l)))
6062889d
EZ
685 (setq l (cdr l)))))
686 ;; Don't show more than what the display can handle.
687 (let ((lc (nthcdr (1- (display-color-cells)) list)))
688 (if lc
689 (setcdr lc nil))))
88d690a9
RS
690 (with-output-to-temp-buffer "*Colors*"
691 (save-excursion
692 (set-buffer standard-output)
7dc30d5b 693 (let (s)
88d690a9
RS
694 (while list
695 (setq s (point))
696 (insert (car list))
697 (indent-to 20)
698 (put-text-property s (point) 'face
7dc30d5b 699 (cons 'background-color (car list)))
88d690a9
RS
700 (setq s (point))
701 (insert " " (car list) "\n")
702 (put-text-property s (point) 'face
7dc30d5b 703 (cons 'foreground-color (car list)))
88d690a9
RS
704 (setq list (cdr list)))))))
705
706(defun facemenu-color-equal (a b)
707 "Return t if colors A and B are the same color.
7c49006b 708A and B should be strings naming colors.
f795f633
EZ
709This function queries the display system to find out what the color
710names mean. It returns nil if the colors differ or if it can't
7c49006b 711determine the correct answer."
88d690a9 712 (cond ((equal a b) t)
f795f633 713 ((equal (color-values a) (color-values b)))))
88d690a9 714
cb5bec6e 715(defun facemenu-add-face (face &optional start end)
4a24b314 716 "Add FACE to text between START and END.
7e6cb513 717If START is nil or START to END is empty, add FACE to next typed character
cb5bec6e
RS
718instead. For each section of that region that has a different face property,
719FACE will be consed onto it, and other faces that are completely hidden by
720that will be removed from the list.
af1eab21 721If `facemenu-add-face-function' and maybe `facemenu-end-add-face' are non-nil,
cb5bec6e 722they are used to set the face information.
bf7d4561
BG
723
724As a special case, if FACE is `default', then the region is left with NO face
725text property. Otherwise, selecting the default face would not have any
cb5bec6e
RS
726effect. See `facemenu-remove-face-function'."
727 (interactive "*xFace: \nr")
728 (if (and (eq face 'default)
729 (not (eq facemenu-remove-face-function t)))
730 (if facemenu-remove-face-function
731 (funcall facemenu-remove-face-function start end)
682e437e
RS
732 (if (and start (< start end))
733 (remove-text-properties start end '(face default))
734 (setq self-insert-face 'default
735 self-insert-face-command this-command)))
cb5bec6e
RS
736 (if facemenu-add-face-function
737 (save-excursion
738 (if end (goto-char end))
739 (save-excursion
740 (if start (goto-char start))
741 (insert-before-markers
742 (funcall facemenu-add-face-function face end)))
743 (if facemenu-end-add-face
744 (insert (if (stringp facemenu-end-add-face)
745 facemenu-end-add-face
746 (funcall facemenu-end-add-face face)))))
747 (if (and start (< start end))
748 (let ((part-start start) part-end)
749 (while (not (= part-start end))
750 (setq part-end (next-single-property-change part-start 'face
751 nil end))
752 (let ((prev (get-text-property part-start 'face)))
753 (put-text-property part-start part-end 'face
754 (if (null prev)
755 face
756 (facemenu-active-faces
757 (cons face
758 (if (listp prev)
759 prev
760 (list prev)))))))
761 (setq part-start part-end)))
762 (setq self-insert-face (if (eq last-command self-insert-face-command)
763 (cons face (if (listp self-insert-face)
764 self-insert-face
765 (list self-insert-face)))
766 face)
767 self-insert-face-command this-command)))))
4a24b314 768
5a79ed26
KH
769(defun facemenu-active-faces (face-list &optional frame)
770 "Return from FACE-LIST those faces that would be used for display.
771This means each face attribute is not specified in a face earlier in FACE-LIST
772and such a face is therefore active when used to display text.
773If the optional argument FRAME is given, use the faces in that frame; otherwise
774use the selected frame. If t, then the global, non-frame faces are used."
7d8177cf
RS
775 (let* ((mask-atts (copy-sequence
776 (if (consp (car face-list))
f790dddf 777 (face-attributes-as-vector (car face-list))
7d8177cf
RS
778 (or (internal-lisp-face-p (car face-list) frame)
779 (check-face (car face-list))))))
5a79ed26
KH
780 (active-list (list (car face-list)))
781 (face-list (cdr face-list))
782 (mask-len (length mask-atts)))
783 (while face-list
7d8177cf
RS
784 (if (let ((face-atts
785 (if (consp (car face-list))
f790dddf 786 (face-attributes-as-vector (car face-list))
7d8177cf
RS
787 (or (internal-lisp-face-p (car face-list) frame)
788 (check-face (car face-list)))))
789 (i mask-len)
790 (useful nil))
5a79ed26 791 (while (> (setq i (1- i)) 1)
7d8177cf
RS
792 (and (not (memq (aref face-atts i) '(nil unspecified)))
793 (memq (aref mask-atts i) '(nil unspecified))
5a79ed26
KH
794 (aset mask-atts i (setq useful t))))
795 useful)
796 (setq active-list (cons (car face-list) active-list)))
797 (setq face-list (cdr face-list)))
798 (nreverse active-list)))
4a24b314 799
bf7d4561
BG
800(defun facemenu-get-face (symbol)
801 "Make sure FACE exists.
7d8177cf 802If not, create it and add it to the appropriate menu. Return the SYMBOL."
c7bce5f2 803 (let ((name (symbol-name symbol)))
7e6cb513 804 (cond ((facep symbol))
0351bce7
RS
805 (t (make-face symbol))))
806 symbol)
bf7d4561 807
7d8177cf
RS
808(defun facemenu-add-new-face (face-or-color &optional menu)
809 "Add FACE-OR-COLOR (a face or a color) to the appropriate Face menu.
810If MENU is nil, then FACE-OR-COLOR is a face to be added
811to `facemenu-face-menu'. If MENU is `facemenu-foreground-menu'
812or `facemenu-background-menu', FACE-OR-COLOR is a color
813to be added to the specified menu.
814
815This is called whenever you create a new face."
816 (let* (name
817 symbol
818 docstring
1d792b18 819 (key (cdr (assoc face-or-color facemenu-keybindings)))
88d690a9 820 function menu-val)
7d8177cf
RS
821 (if (symbolp face-or-color)
822 (setq name (symbol-name face-or-color)
823 symbol face-or-color)
824 (setq name face-or-color
1d792b18 825 symbol (intern name)))
7d8177cf 826 (cond ((eq menu 'facemenu-foreground-menu)
536f1a10
RS
827 (setq docstring
828 (format "Select foreground color %s for subsequent insertion."
7d8177cf
RS
829 name)))
830 ((eq menu 'facemenu-background-menu)
536f1a10
RS
831 (setq docstring
832 (format "Select background color %s for subsequent insertion."
7d8177cf 833 name)))
536f1a10 834 (t
7d8177cf 835 (setq menu 'facemenu-face-menu)
536f1a10
RS
836 (setq docstring
837 (format "Select face `%s' for subsequent insertion."
7d8177cf 838 name))))
88d690a9 839 (cond ((eq t facemenu-unlisted-faces))
7d8177cf 840 ((memq symbol facemenu-unlisted-faces))
7dc30d5b
RS
841 ;; test against regexps in facemenu-unlisted-faces
842 ((let ((unlisted facemenu-unlisted-faces)
843 (matched nil))
844 (while (and unlisted (not matched))
845 (if (and (stringp (car unlisted))
846 (string-match (car unlisted) name))
847 (setq matched t)
848 (setq unlisted (cdr unlisted))))
849 matched))
88d690a9
RS
850 (key ; has a keyboard equivalent. These go at the front.
851 (setq function (intern (concat "facemenu-set-" name)))
852 (fset function
536f1a10
RS
853 `(lambda ()
854 ,docstring
855 (interactive)
af1eab21
RS
856 (facemenu-set-face
857 (quote ,symbol)
858 (if (and mark-active (not current-prefix-arg))
859 (region-beginning))
860 (if (and mark-active (not current-prefix-arg))
861 (region-end)))))
88d690a9
RS
862 (define-key 'facemenu-keymap key (cons name function))
863 (define-key menu key (cons name function)))
864 ((facemenu-iterate ; check if equivalent face is already in the menu
865 (lambda (m) (and (listp m)
866 (symbolp (car m))
7d8177cf 867 (face-equal (car m) symbol)))
88d690a9
RS
868 (cdr (symbol-function menu))))
869 (t ; No keyboard equivalent. Figure out where to put it:
7d8177cf 870 (setq key (vector symbol)
88d690a9
RS
871 function 'facemenu-set-face-from-menu
872 menu-val (symbol-function menu))
873 (if (and facemenu-new-faces-at-end
874 (> (length menu-val) 3))
875 (define-key-after menu-val key (cons name function)
876 (car (nth (- (length menu-val) 3) menu-val)))
877 (define-key menu key (cons name function))))))
878 nil) ; Return nil for facemenu-iterate
bf7d4561 879
bf7d4561 880(defun facemenu-complete-face-list (&optional oldlist)
7cd49450 881 "Return list of all faces that look different.
bf7d4561
BG
882Starts with given ALIST of faces, and adds elements only if they display
883differently from any face already on the list.
884The faces on ALIST will end up at the end of the returned list, in reverse
885order."
886 (let ((list (nreverse (mapcar 'car oldlist))))
887 (facemenu-iterate
888 (lambda (new-face)
889 (if (not (memq new-face list))
890 (setq list (cons new-face list)))
891 nil)
892 (nreverse (face-list)))
893 list))
894
7e6cb513 895(defun facemenu-iterate (func list)
4e8aa578
RS
896 "Apply FUNC to each element of LIST until one returns non-nil.
897Returns the non-nil value it found, or nil if all were nil."
7e6cb513
SM
898 (while (and list (not (funcall func (car list))))
899 (setq list (cdr list)))
900 (car list))
4e8aa578
RS
901
902(facemenu-update)
4e8aa578
RS
903
904;;; facemenu.el ends here