facemenu.el 27.3 KB
Newer Older
Richard M. Stallman's avatar
Richard M. Stallman committed
1
;;; facemenu.el --- create a face menu for interactively adding fonts to text
Erik Naggum's avatar
Erik Naggum committed
2

3
;; Copyright (c) 1994, 1995, 1996, 2001, 2002 Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
4

Karl Heuer's avatar
Karl Heuer committed
5
;; Author: Boris Goldowsky <boris@gnu.org>
Richard M. Stallman's avatar
Richard M. Stallman committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
;; Keywords: faces

;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
Erik Naggum's avatar
Erik Naggum committed
21 22 23
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
Richard M. Stallman's avatar
Richard M. Stallman committed
24 25

;;; Commentary:
Erik Naggum's avatar
Erik Naggum committed
26

27 28
;; This file defines a menu of faces (bold, italic, etc) which allows you to
;; set the face used for a region of the buffer.  Some faces also have
Richard M. Stallman's avatar
Richard M. Stallman committed
29
;; keybindings, which are shown in the menu.
30 31 32
;;
;; The menu also contains submenus for indentation and justification-changing
;; commands.
Richard M. Stallman's avatar
Richard M. Stallman committed
33 34

;;; Usage:
35 36 37 38 39
;; Selecting a face from the menu or typing the keyboard equivalent will
;; change the region to use that face.  If you use transient-mark-mode and the
;; region is not active, the face will be remembered and used for the next
;; insertion.  It will be forgotten if you move point or make other
;; modifications before inserting or typing anything.
Richard M. Stallman's avatar
Richard M. Stallman committed
40 41
;;
;; Faces can be selected from the keyboard as well.  
42 43
;; The standard keybindings are M-g (or ESC g) + letter:
;; M-g i = "set italic",  M-g b = "set bold", etc.
Richard M. Stallman's avatar
Richard M. Stallman committed
44 45 46

;;; Customization:
;; An alternative set of keybindings that may be easier to type can be set up
47 48 49 50 51 52
;; using "Alt" or "Hyper" keys.  This requires that you either have or create
;; an Alt or Hyper key on your keyboard.  On my keyboard, there is a key
;; labeled "Alt", but to make it act as an Alt key I have to put this command
;; into my .xinitrc:
;;    xmodmap -e "add Mod3 = Alt_L"
;; Or, I can make it into a Hyper key with this:
Richard M. Stallman's avatar
Richard M. Stallman committed
53
;;    xmodmap -e "keysym Alt_L = Hyper_L" -e "add Mod2 = Hyper_L"
54 55
;; Check with local X-perts for how to do it on your system.
;; Then you can define your keybindings with code like this in your .emacs:
Richard M. Stallman's avatar
Richard M. Stallman committed
56 57 58 59
;;   (setq facemenu-keybindings
;;    '((default     . [?\H-d])
;;      (bold        . [?\H-b])
;;      (italic      . [?\H-i])
60
;;      (bold-italic . [?\H-l])
Richard M. Stallman's avatar
Richard M. Stallman committed
61
;;      (underline   . [?\H-u])))
62
;;   (facemenu-update)
Richard M. Stallman's avatar
Richard M. Stallman committed
63
;;   (setq facemenu-keymap global-map)
64 65
;;   (define-key global-map [?\H-c] 'facemenu-set-foreground) ; set fg color
;;   (define-key global-map [?\H-C] 'facemenu-set-background) ; set bg color
Richard M. Stallman's avatar
Richard M. Stallman committed
66
;;
67 68 69 70
;; The order of the faces that appear in the menu and their keybindings can be
;; controlled by setting the variables `facemenu-keybindings' and
;; `facemenu-new-faces-at-end'.  List faces that you don't use in documents
;; (eg, `region') in `facemenu-unlisted-faces'.
Richard M. Stallman's avatar
Richard M. Stallman committed
71 72

;;; Known Problems:
73 74 75 76
;; Bold and Italic do not combine to create bold-italic if you select them
;; both, although most other combinations (eg bold + underline + some color)
;; do the intuitive thing.
;;
Richard M. Stallman's avatar
Richard M. Stallman committed
77 78 79 80 81 82 83 84 85 86 87 88
;; There is at present no way to display what the faces look like in
;; the menu itself.
;;
;; `list-faces-display' shows the faces in a different order than
;; this menu, which could be confusing.  I do /not/ sort the list
;; alphabetically, because I like the default order: it puts the most
;; basic, common fonts first.
;;
;; Please send me any other problems, comments or ideas.

;;; Code:

89 90 91 92
(eval-when-compile 
  (require 'help)
  (require 'button))

Boris Goldowsky's avatar
Boris Goldowsky committed
93 94 95
;;; Provide some binding for startup:
;;;###autoload (define-key global-map "\M-g" 'facemenu-keymap)
;;;###autoload (autoload 'facemenu-keymap "facemenu" "Keymap for face-changing commands." t 'keymap)
96 97 98 99
  
;; Global bindings:
(define-key global-map [C-down-mouse-2] 'facemenu-menu)
(define-key global-map "\M-g" 'facemenu-keymap)
Richard M. Stallman's avatar
Richard M. Stallman committed
100

Richard M. Stallman's avatar
Richard M. Stallman committed
101 102 103 104 105 106
(defgroup facemenu nil
  "Create a face menu for interactively adding fonts to text"
  :group 'faces
  :prefix "facemenu-")

(defcustom facemenu-keybindings
Richard M. Stallman's avatar
Richard M. Stallman committed
107 108 109
  '((default     . "d")
    (bold        . "b")
    (italic      . "i")
110
    (bold-italic . "l") ; {bold} intersect {italic} = {l}
Richard M. Stallman's avatar
Richard M. Stallman committed
111
    (underline   . "u"))
Pavel Janík's avatar
Pavel Janík committed
112
  "Alist of interesting faces and keybindings.
Richard M. Stallman's avatar
Richard M. Stallman committed
113 114
Each element is itself a list: the car is the name of the face,
the next element is the key to use as a keyboard equivalent of the menu item;
115
the binding is made in `facemenu-keymap'.
Richard M. Stallman's avatar
Richard M. Stallman committed
116 117 118 119 120 121 122

The faces specifically mentioned in this list are put at the top of
the menu, in the order specified.  All other faces which are defined,
except for those in `facemenu-unlisted-faces', are listed after them, 
but get no keyboard equivalents.

If you change this variable after loading facemenu.el, you will need to call
Richard M. Stallman's avatar
Richard M. Stallman committed
123 124 125
`facemenu-update' to make it take effect."
  :type '(repeat (cons face string))
  :group 'facemenu)
Richard M. Stallman's avatar
Richard M. Stallman committed
126

Richard M. Stallman's avatar
Richard M. Stallman committed
127
(defcustom facemenu-new-faces-at-end t
128
  "*Where in the menu to insert newly-created faces.
129
This should be nil to put them at the top of the menu, or t to put them
Richard M. Stallman's avatar
Richard M. Stallman committed
130 131 132
just before \"Other\" at the end."
  :type 'boolean
  :group 'facemenu)
133

Richard M. Stallman's avatar
Richard M. Stallman committed
134
(defcustom facemenu-unlisted-faces
135 136 137 138
  `(modeline region secondary-selection highlight scratch-face
    ,(purecopy "^font-lock-") ,(purecopy "^gnus-") ,(purecopy "^message-")
    ,(purecopy "^ediff-") ,(purecopy "^term-") ,(purecopy "^vc-")
    ,(purecopy "^widget-") ,(purecopy "^custom-") ,(purecopy "^vm-"))
139
  "*List of faces not to include in the Face menu.
140 141 142 143
Each element may be either a symbol, which is the name of a face, or a string,
which is a regular expression to be matched against face names.  Matching
faces will not be added to the menu.

144 145 146 147
You can set this list before loading facemenu.el, or add a face to it before
creating that face if you do not want it to be listed.  If you change the
variable so as to eliminate faces that have already been added to the menu,
call `facemenu-update' to recalculate the menu contents.
Richard M. Stallman's avatar
Richard M. Stallman committed
148

149 150
If this variable is t, no faces will be added to the menu.  This is useful for
temporarily turning off the feature that automatically adds faces to the menu
Richard M. Stallman's avatar
Richard M. Stallman committed
151
when they are created."
152 153
  :type '(choice (const :tag "Don't add faces" t)
		 (const :tag "None (do add any face)" nil)
154
		 (repeat (choice symbol regexp)))
Richard M. Stallman's avatar
Richard M. Stallman committed
155
  :group 'facemenu)
156

Boris Goldowsky's avatar
Boris Goldowsky committed
157
;;;###autoload
158
(defvar facemenu-face-menu
159
  (let ((map (make-sparse-keymap "Face")))
160
    (define-key map "o" (cons "Other..." 'facemenu-set-face))
161 162
    map)
  "Menu keymap for faces.")
Boris Goldowsky's avatar
Boris Goldowsky committed
163
;;;###autoload
164
(defalias 'facemenu-face-menu facemenu-face-menu)
165

Boris Goldowsky's avatar
Boris Goldowsky committed
166
;;;###autoload
167 168
(defvar facemenu-foreground-menu 
  (let ((map (make-sparse-keymap "Foreground Color")))
169
    (define-key map "o" (cons "Other..." 'facemenu-set-foreground))
170 171
    map)
  "Menu keymap for foreground colors.")
Boris Goldowsky's avatar
Boris Goldowsky committed
172
;;;###autoload
173
(defalias 'facemenu-foreground-menu facemenu-foreground-menu)
174

Boris Goldowsky's avatar
Boris Goldowsky committed
175
;;;###autoload
176 177
(defvar facemenu-background-menu
  (let ((map (make-sparse-keymap "Background Color")))
178
    (define-key map "o" (cons "Other..." 'facemenu-set-background))
179
    map)
Stefan Monnier's avatar
Stefan Monnier committed
180
  "Menu keymap for background colors.")
Boris Goldowsky's avatar
Boris Goldowsky committed
181
;;;###autoload
182
(defalias 'facemenu-background-menu facemenu-background-menu)
183

Boris Goldowsky's avatar
Boris Goldowsky committed
184
;;;###autoload
185 186
(defvar facemenu-special-menu 
  (let ((map (make-sparse-keymap "Special")))
Dave Love's avatar
Dave Love committed
187 188 189 190 191 192 193 194
    (define-key map [?s] (cons (purecopy "Remove Special")
			       'facemenu-remove-special))
    (define-key map [?t] (cons (purecopy "Intangible")
			       'facemenu-set-intangible))
    (define-key map [?v] (cons (purecopy "Invisible")
			       'facemenu-set-invisible))
    (define-key map [?r] (cons (purecopy "Read-Only")
			       'facemenu-set-read-only))
195 196
    map)
  "Menu keymap for non-face text-properties.")
Boris Goldowsky's avatar
Boris Goldowsky committed
197
;;;###autoload
198 199
(defalias 'facemenu-special-menu facemenu-special-menu)

Boris Goldowsky's avatar
Boris Goldowsky committed
200
;;;###autoload
201 202
(defvar facemenu-justification-menu
  (let ((map (make-sparse-keymap "Justification")))
Dave Love's avatar
Dave Love committed
203 204 205 206 207
    (define-key map [?c] (cons (purecopy "Center") 'set-justification-center))
    (define-key map [?b] (cons (purecopy "Full") 'set-justification-full))
    (define-key map [?r] (cons (purecopy "Right") 'set-justification-right))
    (define-key map [?l] (cons (purecopy "Left") 'set-justification-left))
    (define-key map [?u] (cons (purecopy "Unfilled") 'set-justification-none))
208 209
    map)
  "Submenu for text justification commands.")
Boris Goldowsky's avatar
Boris Goldowsky committed
210
;;;###autoload
211 212
(defalias 'facemenu-justification-menu facemenu-justification-menu)

Boris Goldowsky's avatar
Boris Goldowsky committed
213
;;;###autoload
214 215
(defvar facemenu-indentation-menu
  (let ((map (make-sparse-keymap "Indentation")))
216
    (define-key map [decrease-right-margin] 
Dave Love's avatar
Dave Love committed
217
      (cons (purecopy "Indent Right Less") 'decrease-right-margin))
218
    (define-key map [increase-right-margin]
Dave Love's avatar
Dave Love committed
219
      (cons (purecopy "Indent Right More") 'increase-right-margin))
220
    (define-key map [decrease-left-margin]
Dave Love's avatar
Dave Love committed
221
      (cons (purecopy "Indent Less") 'decrease-left-margin))
222
    (define-key map [increase-left-margin]
Dave Love's avatar
Dave Love committed
223
      (cons (purecopy "Indent More") 'increase-left-margin))
224 225
    map)
  "Submenu for indentation commands.")
Boris Goldowsky's avatar
Boris Goldowsky committed
226
;;;###autoload
227
(defalias 'facemenu-indentation-menu facemenu-indentation-menu)
228

229
;; This is split up to avoid an overlong line in loaddefs.el.
Boris Goldowsky's avatar
Boris Goldowsky committed
230
;;;###autoload
231
(defvar facemenu-menu nil
232
  "Facemenu top-level menu keymap.")
Boris Goldowsky's avatar
Boris Goldowsky committed
233
;;;###autoload
234 235 236
(setq facemenu-menu (make-sparse-keymap "Text Properties"))
;;;###autoload
(let ((map facemenu-menu))
Dave Love's avatar
Dave Love committed
237 238
  (define-key map [dc] (cons (purecopy "Display Colors") 'list-colors-display))
  (define-key map [df] (cons (purecopy "Display Faces") 'list-faces-display))
239 240
  (define-key map [dp] (cons (purecopy "Describe Properties")
			     'describe-text-properties))
Dave Love's avatar
Dave Love committed
241 242 243 244 245
  (define-key map [ra] (cons (purecopy "Remove Text Properties")
			     'facemenu-remove-all))
  (define-key map [rm] (cons (purecopy "Remove Face Properties")
			     'facemenu-remove-face-props))
  (define-key map [s1] (list (purecopy "--"))))
246 247
;;;###autoload
(let ((map facemenu-menu))
Dave Love's avatar
Dave Love committed
248 249 250 251 252 253 254 255 256 257 258 259 260
  (define-key map [in] (cons (purecopy "Indentation") 
			     'facemenu-indentation-menu))
  (define-key map [ju] (cons (purecopy "Justification")
			     'facemenu-justification-menu))
  (define-key map [s2] (list (purecopy "--")))
  (define-key map [sp] (cons (purecopy "Special Properties") 
			     'facemenu-special-menu))
  (define-key map [bg] (cons (purecopy "Background Color") 
			     'facemenu-background-menu))
  (define-key map [fg] (cons (purecopy "Foreground Color") 
			     'facemenu-foreground-menu))
  (define-key map [fc] (cons (purecopy "Face") 
			     'facemenu-face-menu)))
261
;;;###autoload
262
(defalias 'facemenu-menu facemenu-menu)
263

264 265
(defvar facemenu-keymap 
  (let ((map (make-sparse-keymap "Set face")))
Dave Love's avatar
Dave Love committed
266
    (define-key map "o" (cons (purecopy "Other...") 'facemenu-set-face))
267
    map)
Boris Goldowsky's avatar
Boris Goldowsky committed
268
  "Keymap for face-changing commands.
269
`Facemenu-update' fills in the keymap according to the bindings
270
requested in `facemenu-keybindings'.")
271
(defalias 'facemenu-keymap facemenu-keymap)
272

273

Richard M. Stallman's avatar
Richard M. Stallman committed
274
(defcustom facemenu-add-face-function nil
Stefan Monnier's avatar
Stefan Monnier committed
275
  "Function called at beginning of text to change or nil.
276
This function is passed the FACE to set and END of text to change, and must
Richard M. Stallman's avatar
Richard M. Stallman committed
277 278 279 280
return a string which is inserted.  It may set `facemenu-end-add-face'."
  :type '(choice (const :tag "None" nil)
		 function)
  :group 'facemenu)
281

Richard M. Stallman's avatar
Richard M. Stallman committed
282
(defcustom facemenu-end-add-face nil
Stefan Monnier's avatar
Stefan Monnier committed
283
  "String to insert or function called at end of text to change or nil.
284
This function is passed the FACE to set, and must return a string which is
Richard M. Stallman's avatar
Richard M. Stallman committed
285 286 287 288 289
inserted."
  :type '(choice (const :tag "None" nil)
		 string
		 function)
  :group 'facemenu)
290

Richard M. Stallman's avatar
Richard M. Stallman committed
291
(defcustom facemenu-remove-face-function nil
292
  "When non-nil, this is a function called to remove faces.
293
This function is passed the START and END of text to change.
Stefan Monnier's avatar
Stefan Monnier committed
294
May also be t meaning to use `facemenu-add-face-function'."
Richard M. Stallman's avatar
Richard M. Stallman committed
295 296 297 298
  :type '(choice (const :tag "None" nil)
		 (const :tag "Use add-face" t)
		 function)
  :group 'facemenu)
299

300 301 302 303 304 305
;;; Internal Variables

(defvar facemenu-color-alist nil
  ;; Don't initialize here; that doesn't work if preloaded.
  "Alist of colors, used for completion.
If null, `facemenu-read-color' will set it.")
306

Richard M. Stallman's avatar
Richard M. Stallman committed
307
(defun facemenu-update ()
308 309 310
  "Add or update the \"Face\" menu in the menu bar.
You can call this to update things if you change any of the menu configuration
variables."
Richard M. Stallman's avatar
Richard M. Stallman committed
311 312
  (interactive)

313 314 315
  ;; Add each defined face to the menu.
  (facemenu-iterate 'facemenu-add-new-face
		    (facemenu-complete-face-list facemenu-keybindings)))
316

Richard M. Stallman's avatar
Richard M. Stallman committed
317 318
;;;###autoload
(defun facemenu-set-face (face &optional start end)
319
  "Add FACE to the region or next character typed.
320
This adds FACE to the top of the face list; any faces lower on the list that
321 322
will not show through at all will be removed.

323
Interactively, reads the face name with the minibuffer.
324

325 326 327
If the region is active (normally true except in Transient Mark mode)
and there is no prefix argument, this command sets the region to the
requested face.
328 329 330 331

Otherwise, this command specifies the face for the next character
inserted.  Moving point or switching buffers before
typing a character to insert cancels the specification." 
332 333 334 335 336 337 338
  (interactive (list (progn
		       (barf-if-buffer-read-only)
		       (read-face-name "Use face"))
		     (if (and mark-active (not current-prefix-arg))
			 (region-beginning))
		     (if (and mark-active (not current-prefix-arg))
			 (region-end))))
339
  (facemenu-add-new-face face)
340
  (facemenu-add-face face start end))
341

342
;;;###autoload
343
(defun facemenu-set-foreground (color &optional start end)
Stefan Monnier's avatar
Stefan Monnier committed
344
  "Set the foreground COLOR of the region or next character typed.
Richard M. Stallman's avatar
Richard M. Stallman committed
345
This command reads the color in the minibuffer.
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362

If the region is active (normally true except in Transient Mark mode)
and there is no prefix argument, this command sets the region to the
requested face.

Otherwise, this command specifies the face for the next character
inserted.  Moving point or switching buffers before
typing a character to insert cancels the specification." 
  (interactive (list (progn
		       (barf-if-buffer-read-only)
		       (facemenu-read-color "Foreground color: "))
		     (if (and mark-active (not current-prefix-arg))
			 (region-beginning))
		     (if (and mark-active (not current-prefix-arg))
			 (region-end))))
  (unless (color-defined-p color)
    (message "Color `%s' undefined" color))
363
  (facemenu-add-new-color color 'facemenu-foreground-menu)
364
  (facemenu-add-face (list (list :foreground color)) start end))
365

366
;;;###autoload
367
(defun facemenu-set-background (color &optional start end)
Stefan Monnier's avatar
Stefan Monnier committed
368
  "Set the background COLOR of the region or next character typed.
Richard M. Stallman's avatar
Richard M. Stallman committed
369
This command reads the color in the minibuffer.
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386

If the region is active (normally true except in Transient Mark mode)
and there is no prefix argument, this command sets the region to the
requested face.

Otherwise, this command specifies the face for the next character
inserted.  Moving point or switching buffers before
typing a character to insert cancels the specification." 
  (interactive (list (progn
		       (barf-if-buffer-read-only)
		       (facemenu-read-color "Background color: "))
		     (if (and mark-active (not current-prefix-arg))
			 (region-beginning))
		     (if (and mark-active (not current-prefix-arg))
			 (region-end))))
  (unless (color-defined-p color)
    (message "Color `%s' undefined" color))
387
  (facemenu-add-new-color color 'facemenu-background-menu)
388
  (facemenu-add-face (list (list :background color)) start end))
Richard M. Stallman's avatar
Richard M. Stallman committed
389

Boris Goldowsky's avatar
Boris Goldowsky committed
390
;;;###autoload
Richard M. Stallman's avatar
Richard M. Stallman committed
391
(defun facemenu-set-face-from-menu (face start end)
Stefan Monnier's avatar
Stefan Monnier committed
392
  "Set the FACE of the region or next character typed.
Richard M. Stallman's avatar
Richard M. Stallman committed
393 394
This function is designed to be called from a menu; the face to use
is the menu item's name.
395

396 397 398
If the region is active (normally true except in Transient Mark mode)
and there is no prefix argument, this command sets the region to the
requested face.
399 400 401 402

Otherwise, this command specifies the face for the next character
inserted.  Moving point or switching buffers before
typing a character to insert cancels the specification." 
403
  (interactive (list last-command-event
404 405 406 407
		     (if (and mark-active (not current-prefix-arg))
			 (region-beginning))
		     (if (and mark-active (not current-prefix-arg))
			 (region-end))))
408
  (barf-if-buffer-read-only)
409
  (facemenu-get-face face)
Richard M. Stallman's avatar
Richard M. Stallman committed
410
  (if start 
411
      (facemenu-add-face face start end)
412
    (facemenu-add-face face)))
Richard M. Stallman's avatar
Richard M. Stallman committed
413

Boris Goldowsky's avatar
Boris Goldowsky committed
414
;;;###autoload
Richard M. Stallman's avatar
Richard M. Stallman committed
415 416 417
(defun facemenu-set-invisible (start end)
  "Make the region invisible.
This sets the `invisible' text property; it can be undone with
418
`facemenu-remove-special'."
Richard M. Stallman's avatar
Richard M. Stallman committed
419
  (interactive "r")
420
  (add-text-properties start end '(invisible t)))
Richard M. Stallman's avatar
Richard M. Stallman committed
421

Boris Goldowsky's avatar
Boris Goldowsky committed
422
;;;###autoload
Richard M. Stallman's avatar
Richard M. Stallman committed
423 424 425
(defun facemenu-set-intangible (start end)
  "Make the region intangible: disallow moving into it.
This sets the `intangible' text property; it can be undone with
426
`facemenu-remove-special'."
Richard M. Stallman's avatar
Richard M. Stallman committed
427
  (interactive "r")
428
  (add-text-properties start end '(intangible t)))
Richard M. Stallman's avatar
Richard M. Stallman committed
429

Boris Goldowsky's avatar
Boris Goldowsky committed
430
;;;###autoload
Richard M. Stallman's avatar
Richard M. Stallman committed
431 432 433
(defun facemenu-set-read-only (start end)
  "Make the region unmodifiable.
This sets the `read-only' text property; it can be undone with
434
`facemenu-remove-special'."
Richard M. Stallman's avatar
Richard M. Stallman committed
435
  (interactive "r")
436
  (add-text-properties start end '(read-only t)))
Richard M. Stallman's avatar
Richard M. Stallman committed
437

Boris Goldowsky's avatar
Boris Goldowsky committed
438
;;;###autoload
439 440
(defun facemenu-remove-face-props (start end)
  "Remove `face' and `mouse-face' text properties."
Richard M. Stallman's avatar
Richard M. Stallman committed
441 442 443
  (interactive "*r") ; error if buffer is read-only despite the next line.
  (let ((inhibit-read-only t))
    (remove-text-properties 
444
     start end '(face nil mouse-face nil))))
Richard M. Stallman's avatar
Richard M. Stallman committed
445

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
;;;###autoload
(defun facemenu-remove-all (start end)
  "Remove all text properties from the region."
  (interactive "*r") ; error if buffer is read-only despite the next line.
  (let ((inhibit-read-only t))
    (set-text-properties start end nil)))

;;;###autoload
(defun facemenu-remove-special (start end)
  "Remove all the \"special\" text properties from the region.
These special properties include `invisible', `intangible' and `read-only'."
  (interactive "*r") ; error if buffer is read-only despite the next line.
  (let ((inhibit-read-only t))
    (remove-text-properties 
     start end '(invisible nil intangible nil read-only nil))))
461

462
;;;###autoload
463
(defun facemenu-read-color (&optional prompt)
464
  "Read a color using the minibuffer."
465
  (let ((col (completing-read (or prompt "Color: ") 
466
			      (or facemenu-color-alist
Stefan Monnier's avatar
Stefan Monnier committed
467
				  (defined-colors))
468 469 470 471
			      nil t)))
    (if (equal "" col)
	nil
      col)))
Richard M. Stallman's avatar
Richard M. Stallman committed
472

473 474
;;;###autoload
(defun list-colors-display (&optional list)
475 476 477 478
  "Display names of defined colors, and show what they look like.
If the optional argument LIST is non-nil, it should be a list of
colors to display.  Otherwise, this command computes a list
of colors that the current display can handle."
479
  (interactive)
480
  (when (and (null list) (> (display-color-cells) 0))
481
    (setq list (defined-colors))
482 483 484 485 486
    ;; Delete duplicate colors.
    (let ((l list))
      (while (cdr l)
	(if (facemenu-color-equal (car l) (car (cdr l)))
	    (setcdr l (cdr (cdr l)))
487
	  (setq l (cdr l)))))
488 489 490 491 492
    (when (memq (display-visual-class) '(gray-scale pseudo-color direct-color))
      ;; Don't show more than what the display can handle.
      (let ((lc (nthcdr (1- (display-color-cells)) list)))
	(if lc
	    (setcdr lc nil)))))
493 494 495
  (with-output-to-temp-buffer "*Colors*"
    (save-excursion
      (set-buffer standard-output)
496
      (let (s)
497 498 499 500 501
	(while list
	  (setq s (point))
	  (insert (car list))
	  (indent-to 20)
	  (put-text-property s (point) 'face 
502
			     (cons 'background-color (car list)))
503 504 505
	  (setq s (point))
	  (insert "  " (car list) "\n")
	  (put-text-property s (point) 'face 
506
			     (cons 'foreground-color (car list)))
507 508 509 510
	  (setq list (cdr list)))))))

(defun facemenu-color-equal (a b)
  "Return t if colors A and B are the same color.
511
A and B should be strings naming colors.
512 513
This function queries the display system to find out what the color
names mean.  It returns nil if the colors differ or if it can't
514
determine the correct answer."
515
  (cond ((equal a b) t)
516
	((equal (color-values a) (color-values b)))))
517

518
(defun facemenu-add-face (face &optional start end)
519
  "Add FACE to text between START and END.
Stefan Monnier's avatar
Stefan Monnier committed
520
If START is nil or START to END is empty, add FACE to next typed character
521 522 523
instead.  For each section of that region that has a different face property,
FACE will be consed onto it, and other faces that are completely hidden by
that will be removed from the list.
Richard M. Stallman's avatar
Richard M. Stallman committed
524
If `facemenu-add-face-function' and maybe `facemenu-end-add-face' are non-nil,
525
they are used to set the face information.
526 527 528

As a special case, if FACE is `default', then the region is left with NO face
text property.  Otherwise, selecting the default face would not have any
529 530 531 532 533 534
effect.  See `facemenu-remove-face-function'."
  (interactive "*xFace: \nr")
  (if (and (eq face 'default)
	   (not (eq facemenu-remove-face-function t)))
      (if facemenu-remove-face-function
	  (funcall facemenu-remove-face-function start end)
535 536 537 538
	(if (and start (< start end))
	    (remove-text-properties start end '(face default))
	  (setq self-insert-face 'default
		self-insert-face-command this-command)))
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
    (if facemenu-add-face-function
	(save-excursion
	  (if end (goto-char end))
	  (save-excursion
	    (if start (goto-char start))
	    (insert-before-markers
	     (funcall facemenu-add-face-function face end)))
	  (if facemenu-end-add-face
	      (insert (if (stringp facemenu-end-add-face)
			  facemenu-end-add-face
			(funcall facemenu-end-add-face face)))))
      (if (and start (< start end))
	  (let ((part-start start) part-end)
	    (while (not (= part-start end))
	      (setq part-end (next-single-property-change part-start 'face
							  nil end))
	      (let ((prev (get-text-property part-start 'face)))
		(put-text-property part-start part-end 'face
				   (if (null prev)
				       face
				     (facemenu-active-faces
				      (cons face
					    (if (listp prev)
						prev
					      (list prev)))))))
	      (setq part-start part-end)))
	(setq self-insert-face (if (eq last-command self-insert-face-command)
				   (cons face (if (listp self-insert-face)
						  self-insert-face
						(list self-insert-face)))
				 face)
	      self-insert-face-command this-command)))))
571

572 573 574 575 576 577
(defun facemenu-active-faces (face-list &optional frame)
  "Return from FACE-LIST those faces that would be used for display.
This means each face attribute is not specified in a face earlier in FACE-LIST
and such a face is therefore active when used to display text.
If the optional argument FRAME is given, use the faces in that frame; otherwise
use the selected frame.  If t, then the global, non-frame faces are used."
578 579
  (let* ((mask-atts (copy-sequence
		     (if (consp (car face-list))
Richard M. Stallman's avatar
Richard M. Stallman committed
580
			 (face-attributes-as-vector (car face-list))
581 582
		       (or (internal-lisp-face-p (car face-list) frame)
			   (check-face (car face-list))))))
583 584 585 586
	 (active-list (list (car face-list)))
	 (face-list (cdr face-list))
	 (mask-len (length mask-atts)))
    (while face-list
587 588
      (if (let ((face-atts
		 (if (consp (car face-list))
Richard M. Stallman's avatar
Richard M. Stallman committed
589
		     (face-attributes-as-vector (car face-list))
590 591 592 593
		   (or (internal-lisp-face-p (car face-list) frame)
		       (check-face (car face-list)))))
		(i mask-len)
		(useful nil))
594
	    (while (> (setq i (1- i)) 1)
595 596
	      (and (not (memq (aref face-atts i) '(nil unspecified)))
		   (memq (aref mask-atts i) '(nil unspecified))
597 598 599 600 601
		   (aset mask-atts i (setq useful t))))
	    useful)
	  (setq active-list (cons (car face-list) active-list)))
      (setq face-list (cdr face-list)))
    (nreverse active-list)))
602

603 604
(defun facemenu-get-face (symbol)
  "Make sure FACE exists.
605
If not, create it and add it to the appropriate menu.  Return the SYMBOL."
606
  (let ((name (symbol-name symbol)))
Stefan Monnier's avatar
Stefan Monnier committed
607
    (cond ((facep symbol))
608 609
	  (t (make-face symbol))))
  symbol)
610

611 612
(defun facemenu-add-new-face (face)
  "Add FACE (a face) to the Face menu.
613 614 615 616

This is called whenever you create a new face."
  (let* (name
	 symbol
617 618
	 menu docstring
	 (key (cdr (assoc face facemenu-keybindings)))
619
	 function menu-val)
620 621 622 623
    (if (symbolp face)
	(setq name (symbol-name face)
	      symbol face)
      (setq name face
624
	    symbol (intern name)))
625 626 627 628
    (setq menu 'facemenu-face-menu)
    (setq docstring
	  (format "Select face `%s' for subsequent insertion."
		  name))
629
    (cond ((eq t facemenu-unlisted-faces))
630
	  ((memq symbol facemenu-unlisted-faces))
631 632 633 634 635 636 637 638 639
	  ;; test against regexps in facemenu-unlisted-faces
	  ((let ((unlisted facemenu-unlisted-faces)
		 (matched nil))
	     (while (and unlisted (not matched))
	       (if (and (stringp (car unlisted))
			(string-match (car unlisted) name))
		   (setq matched t)
		 (setq unlisted (cdr unlisted))))
	     matched))
640 641 642
	  (key ; has a keyboard equivalent.  These go at the front.
	   (setq function (intern (concat "facemenu-set-" name)))
	   (fset function
643 644 645
		 `(lambda ()
		    ,docstring
		    (interactive)
Richard M. Stallman's avatar
Richard M. Stallman committed
646 647 648 649 650 651
		    (facemenu-set-face
		     (quote ,symbol)
		     (if (and mark-active (not current-prefix-arg))
			 (region-beginning))
		     (if (and mark-active (not current-prefix-arg))
			 (region-end)))))
652 653 654 655 656
	   (define-key 'facemenu-keymap key (cons name function))
	   (define-key menu key (cons name function)))
	  ((facemenu-iterate ; check if equivalent face is already in the menu
	    (lambda (m) (and (listp m) 
			     (symbolp (car m))
657
			     (face-equal (car m) symbol)))
658 659
	    (cdr (symbol-function menu))))
	  (t   ; No keyboard equivalent.  Figure out where to put it:
660
	   (setq key (vector symbol)
661 662 663 664 665
		 function 'facemenu-set-face-from-menu
		 menu-val (symbol-function menu))
	   (if (and facemenu-new-faces-at-end
		   (> (length menu-val) 3))
	       (define-key-after menu-val key (cons name function)
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
		 (car (nth (- (length menu-val) 3) menu-val)))
	     (define-key menu key (cons name function))))))
  nil) ; Return nil for facemenu-iterate

(defun facemenu-add-new-color (color &optional menu)
  "Add COLOR (a color name string) to the appropriate Face menu.
MENU should be `facemenu-foreground-menu' or
`facemenu-background-menu'.

This is called whenever you use a new color."
  (let* (name
	 symbol
	 docstring
	 function menu-val key
	 (color-p (memq menu '(facemenu-foreground-menu
			       facemenu-background-menu))))
    (unless (stringp color)
      (error "%s is not a color" color))
    (setq name color
	  symbol (intern name))

    (cond ((eq menu 'facemenu-foreground-menu)
	   (setq docstring
		 (format "Select foreground color %s for subsequent insertion."
			 name)))
	  ((eq menu 'facemenu-background-menu)
	   (setq docstring
		 (format "Select background color %s for subsequent insertion."
			 name))))
    (cond ((facemenu-iterate ; check if equivalent face is already in the menu
	    (lambda (m) (and (listp m) 
			     (symbolp (car m))
			     (stringp (cadr m))
			     (string-equal (cadr m) color)))
	    (cdr (symbol-function menu))))
	  (t   ; No keyboard equivalent.  Figure out where to put it:
	   (setq key (vector symbol)
		 function 'facemenu-set-face-from-menu
		 menu-val (symbol-function menu))
	   (if (and facemenu-new-faces-at-end
		   (> (length menu-val) 3))
	       (define-key-after menu-val key (cons name function)
708 709 710
		 (car (nth (- (length menu-val) 3) menu-val)))
	     (define-key menu key (cons name function))))))
  nil) ; Return nil for facemenu-iterate
711 712

(defun facemenu-complete-face-list (&optional oldlist)
713
  "Return list of all faces that look different.
714 715 716 717 718 719 720 721 722 723 724 725 726
Starts with given ALIST of faces, and adds elements only if they display 
differently from any face already on the list.
The faces on ALIST will end up at the end of the returned list, in reverse 
order."
  (let ((list (nreverse (mapcar 'car oldlist))))
    (facemenu-iterate 
     (lambda (new-face) 
       (if (not (memq new-face list))
	   (setq list (cons new-face list)))
       nil)
     (nreverse (face-list)))
    list))

Stefan Monnier's avatar
Stefan Monnier committed
727
(defun facemenu-iterate (func list)
Richard M. Stallman's avatar
Richard M. Stallman committed
728 729
  "Apply FUNC to each element of LIST until one returns non-nil.
Returns the non-nil value it found, or nil if all were nil."
Stefan Monnier's avatar
Stefan Monnier committed
730 731 732
  (while (and list (not (funcall func (car list))))
    (setq list (cdr list)))
  (car list))
Richard M. Stallman's avatar
Richard M. Stallman committed
733 734 735

(facemenu-update)

Stefan Monnier's avatar
Stefan Monnier committed
736
(provide 'facemenu)
Richard M. Stallman's avatar
Richard M. Stallman committed
737
;;; facemenu.el ends here