merging Emacs.app (NeXTstep port)
[bpt/emacs.git] / lisp / cus-face.el
CommitLineData
e8af40ee 1;;; cus-face.el --- customization support for faces
d543e20b 2;;
0d30b337 3;; Copyright (C) 1996, 1997, 1999, 2000, 2001, 2002, 2003, 2004,
409cc4a3 4;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
d543e20b
PA
5;;
6;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
7;; Keywords: help, faces
d543e20b 8
6c283d44 9;; This file is part of GNU Emacs.
d543e20b 10
eb3fa2cf 11;; GNU Emacs is free software: you can redistribute it and/or modify
6c283d44 12;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
d543e20b 15
6c283d44
RS
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
d543e20b 20
6c283d44 21;; You should have received a copy of the GNU General Public License
eb3fa2cf 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
d543e20b 23
6c283d44
RS
24;;; Commentary:
25;;
26;; See `custom.el'.
d543e20b 27
6c283d44 28;;; Code:
d543e20b 29
da0b1f56 30(defalias 'custom-facep 'facep)
ccd0d40c 31
d543e20b
PA
32;;; Declaring a face.
33
d543e20b
PA
34(defun custom-declare-face (face spec doc &rest args)
35 "Like `defface', but FACE is evaluated as a normal argument."
6c283d44 36 (unless (get face 'face-defface-spec)
d543e20b 37 (when (fboundp 'facep)
6c283d44 38 (unless (facep face)
d543e20b
PA
39 ;; If the user has already created the face, respect that.
40 (let ((value (or (get face 'saved-face) spec))
987a3489 41 (have-window-system (memq initial-window-system '(x w32))))
d543e20b
PA
42 ;; Create global face.
43 (make-empty-face face)
b5188bcd 44 ;; Create frame-local faces
f4db5253 45 (dolist (frame (frame-list))
af4bbb69 46 (face-spec-set-2 face frame value)
edfda783 47 (when (memq (window-system frame) '(x w32 mac ns))
2246281f
KL
48 (setq have-window-system t)))
49 ;; When making a face after frames already exist
50 (if have-window-system
51 (make-face-x-resource-internal face)))))
b5188bcd
RS
52 ;; Don't record SPEC until we see it causes no errors.
53 (put face 'face-defface-spec spec)
4829145a 54 (push (cons 'defface face) current-load-list)
209ddde3 55 (when (and doc (null (face-documentation face)))
08449ec1 56 (set-face-documentation face (purecopy doc)))
d543e20b
PA
57 (custom-handle-all-keywords face args 'custom-face)
58 (run-hooks 'custom-define-hook))
59 face)
60
da0b1f56
GM
61;;; Face attributes.
62
d543e20b 63(defconst custom-face-attributes
da0b1f56 64 '((:family
c165ad40
MB
65 (string :tag "Font Family"
66 :help-echo "Font family or fontset alias name."))
a3997907 67
b8a9467c
KH
68 (:foundry
69 (string :tag "Font Foundry"
70 :help-echo "Font foundry name."))
71
da0b1f56
GM
72 (:width
73 (choice :tag "Width"
74 :help-echo "Font width."
c165ad40 75 :value normal ; default
da0b1f56
GM
76 (const :tag "compressed" condensed)
77 (const :tag "condensed" condensed)
78 (const :tag "demiexpanded" semi-expanded)
79 (const :tag "expanded" expanded)
80 (const :tag "extracondensed" extra-condensed)
81 (const :tag "extraexpanded" extra-expanded)
82 (const :tag "medium" normal)
83 (const :tag "narrow" condensed)
84 (const :tag "normal" normal)
85 (const :tag "regular" normal)
86 (const :tag "semicondensed" semi-condensed)
87 (const :tag "semiexpanded" semi-expanded)
88 (const :tag "ultracondensed" ultra-condensed)
89 (const :tag "ultraexpanded" ultra-expanded)
51a1edab 90 (const :tag "wide" extra-expanded)))
71296446 91
da0b1f56
GM
92 (:height
93 (choice :tag "Height"
94 :help-echo "Face's font height."
c165ad40 95 :value 1.0 ; default
6bca3005 96 (integer :tag "Height in 1/10 pt")
51a1edab 97 (number :tag "Scale" 1.0)))
6bca3005 98
da0b1f56
GM
99 (:weight
100 (choice :tag "Weight"
101 :help-echo "Font weight."
c165ad40 102 :value normal ; default
992f7c96 103 (const :tag "black" ultra-bold)
da0b1f56
GM
104 (const :tag "bold" bold)
105 (const :tag "book" semi-light)
106 (const :tag "demibold" semi-bold)
107 (const :tag "extralight" extra-light)
108 (const :tag "extrabold" extra-bold)
109 (const :tag "heavy" extra-bold)
110 (const :tag "light" light)
111 (const :tag "medium" normal)
112 (const :tag "normal" normal)
113 (const :tag "regular" normal)
114 (const :tag "semibold" semi-bold)
115 (const :tag "semilight" semi-light)
116 (const :tag "ultralight" ultra-light)
131b3b11
KH
117 (const :tag "ultrabold" ultra-bold)
118 (const :tag "thin" thin)))
71296446 119
da0b1f56
GM
120 (:slant
121 (choice :tag "Slant"
122 :help-echo "Font slant."
c165ad40 123 :value normal ; default
da0b1f56
GM
124 (const :tag "italic" italic)
125 (const :tag "oblique" oblique)
43240958
CY
126 (const :tag "normal" normal)
127 (const :tag "roman" roman)))
71296446 128
da0b1f56
GM
129 (:underline
130 (choice :tag "Underline"
131 :help-echo "Control text underlining."
c165ad40 132 (const :tag "Off" nil)
da0b1f56 133 (const :tag "On" t)
51a1edab 134 (color :tag "Colored")))
71296446 135
da0b1f56
GM
136 (:overline
137 (choice :tag "Overline"
138 :help-echo "Control text overlining."
c165ad40 139 (const :tag "Off" nil)
da0b1f56 140 (const :tag "On" t)
51a1edab 141 (color :tag "Colored")))
71296446 142
da0b1f56
GM
143 (:strike-through
144 (choice :tag "Strike-through"
145 :help-echo "Control text strike-through."
c165ad40 146 (const :tag "Off" nil)
da0b1f56 147 (const :tag "On" t)
51a1edab 148 (color :tag "Colored")))
71296446 149
da0b1f56 150 (:box
1743c17a 151 ;; Fixme: this can probably be done better.
da0b1f56
GM
152 (choice :tag "Box around text"
153 :help-echo "Control box around text."
c165ad40 154 (const :tag "Off" nil)
da0b1f56 155 (list :tag "Box"
36b80a0d 156 :value (:line-width 2 :color "grey75" :style released-button)
1743c17a 157 (const :format "" :value :line-width)
da0b1f56 158 (integer :tag "Width")
1743c17a
DL
159 (const :format "" :value :color)
160 (choice :tag "Color" (const :tag "*" nil) color)
161 (const :format "" :value :style)
162 (choice :tag "Style"
163 (const :tag "Raised" released-button)
164 (const :tag "Sunken" pressed-button)
165 (const :tag "None" nil))))
51a1edab
MB
166 ;; filter to make value suitable for customize
167 (lambda (real-value)
c165ad40
MB
168 (and real-value
169 (let ((lwidth
170 (or (and (consp real-value)
171 (plist-get real-value :line-width))
172 (and (integerp real-value) real-value)
173 1))
174 (color
175 (or (and (consp real-value) (plist-get real-value :color))
176 (and (stringp real-value) real-value)
177 nil))
178 (style
179 (and (consp real-value) (plist-get real-value :style))))
180 (list :line-width lwidth :color color :style style))))
36b80a0d
MB
181 ;; filter to make customized-value suitable for storing
182 (lambda (cus-value)
c165ad40
MB
183 (and cus-value
184 (let ((lwidth (plist-get cus-value :line-width))
185 (color (plist-get cus-value :color))
186 (style (plist-get cus-value :style)))
187 (cond ((and (null color) (null style))
188 lwidth)
189 ((and (null lwidth) (null style))
190 ;; actually can't happen, because LWIDTH is always an int
191 color)
192 (t
193 ;; Keep as a plist, but remove null entries
194 (nconc (and lwidth `(:line-width ,lwidth))
195 (and color `(:color ,color))
196 (and style `(:style ,style)))))))))
71296446 197
da0b1f56
GM
198 (:inverse-video
199 (choice :tag "Inverse-video"
200 :help-echo "Control whether text should be in inverse-video."
c165ad40
MB
201 (const :tag "Off" nil)
202 (const :tag "On" t)))
71296446 203
da0b1f56 204 (:foreground
c165ad40 205 (color :tag "Foreground"
4bbe6176 206 :help-echo "Set foreground color (name or #RRGGBB hex spec)."))
71296446 207
da0b1f56 208 (:background
c165ad40 209 (color :tag "Background"
4bbe6176 210 :help-echo "Set background color (name or #RRGGBB hex spec)."))
71296446 211
da0b1f56
GM
212 (:stipple
213 (choice :tag "Stipple"
675f1fcb 214 :help-echo "Background bit-mask"
c165ad40 215 (const :tag "None" nil)
675f1fcb
MB
216 (file :tag "File"
217 :help-echo "Name of bitmap file."
218 :must-match t)))
6bca3005
MB
219
220 (:inherit
221 (repeat :tag "Inherit"
222 :help-echo "List of faces to inherit attributes from."
223 (face :Tag "Face" default))
51a1edab
MB
224 ;; filter to make value suitable for customize
225 (lambda (real-value)
226 (cond ((or (null real-value) (eq real-value 'unspecified))
227 nil)
228 ((symbolp real-value)
229 (list real-value))
230 (t
231 real-value)))
232 ;; filter to make customized-value suitable for storing
233 (lambda (cus-value)
234 (if (and (consp cus-value) (null (cdr cus-value)))
235 (car cus-value)
236 cus-value))))
71296446 237
da0b1f56
GM
238 "Alist of face attributes.
239
51a1edab
MB
240The elements are of the form (KEY TYPE PRE-FILTER POST-FILTER),
241where KEY is the name of the attribute, TYPE is a widget type for
242editing the attribute, PRE-FILTER is a function to make the attribute's
243value suitable for the customization widget, and POST-FILTER is a
244function to make the customized value suitable for storing. PRE-FILTER
245and POST-FILTER are optional.
d543e20b 246
51a1edab
MB
247The PRE-FILTER should take a single argument, the attribute value as
248stored, and should return a value for customization (using the
249customization type TYPE).
d543e20b 250
51a1edab
MB
251The POST-FILTER should also take a single argument, the value after
252being customized, and should return a value suitable for setting the
253given face attribute.")
d543e20b
PA
254
255(defun custom-face-attributes-get (face frame)
6c283d44
RS
256 "For FACE on FRAME, return an alternating list describing its attributes.
257The list has the form (KEYWORD VALUE KEYWORD VALUE...).
d543e20b
PA
258Each keyword should be listed in `custom-face-attributes'.
259
6c283d44 260If FRAME is nil, use the global defaults for FACE."
da0b1f56
GM
261 (let ((attrs custom-face-attributes)
262 plist)
263 (while attrs
264 (let* ((attribute (car (car attrs)))
265 (value (face-attribute face attribute frame)))
266 (setq attrs (cdr attrs))
6bca3005
MB
267 (unless (or (eq value 'unspecified)
268 (and (null value) (memq attribute '(:inherit))))
da0b1f56
GM
269 (setq plist (cons attribute (cons value plist))))))
270 plist))
d543e20b 271
d543e20b
PA
272;;; Initializing.
273
d543e20b
PA
274(defun custom-set-faces (&rest args)
275 "Initialize faces according to user preferences.
a3997907
RS
276This associates the settings with the `user' theme.
277The arguments should be a list where each entry has the form:
278
279 (FACE SPEC [NOW [COMMENT]])
280
281SPEC is stored as the saved value for FACE, as well as the value for the
282`user' theme. The `user' theme is one of the default themes known to Emacs.
283See `custom-known-themes' for more information on the known themes.
284See `custom-theme-set-faces' for more information on the interplay
285between themes and faces.
286See `defface' for the format of SPEC.
287
288If NOW is present and non-nil, FACE is created now, according to SPEC.
289COMMENT is a string comment about FACE."
290 (apply 'custom-theme-set-faces 'user args))
291
292(defun custom-theme-set-faces (theme &rest args)
293 "Initialize faces for theme THEME.
d543e20b
PA
294The arguments should be a list where each entry has the form:
295
1743c17a 296 (FACE SPEC [NOW [COMMENT]])
d543e20b 297
a3997907
RS
298SPEC is stored as the saved value for FACE, as well as the value for the
299`user' theme. The `user' theme is one of the default themes known to Emacs.
300See `custom-known-themes' for more information on the known themes.
301See `custom-theme-set-faces' for more information on the interplay
302between themes and faces.
303See `defface' for the format of SPEC.
304
6c283d44 305If NOW is present and non-nil, FACE is created now, according to SPEC.
1743c17a 306COMMENT is a string comment about FACE.
d543e20b 307
a3997907
RS
308Several properties of THEME and FACE are used in the process:
309
310If THEME property `theme-immediate' is non-nil, this is equivalent of
311providing the NOW argument to all faces in the argument list: FACE is
312created now. The only difference is FACE property `force-face': if NOW
313is non-nil, FACE property `force-face' is set to the symbol `rogue', else
314if THEME property `theme-immediate' is non-nil, FACE property `force-face'
315is set to the symbol `immediate'.
316
317SPEC itself is saved in FACE property `saved-face' and it is stored in
318FACE's list property `theme-face' \(using `custom-push-theme')."
319 (custom-check-theme theme)
320 (let ((immediate (get theme 'theme-immediate)))
321 (while args
322 (let ((entry (car args)))
323 (if (listp entry)
324 (let ((face (nth 0 entry))
325 (spec (nth 1 entry))
326 (now (nth 2 entry))
e335f09e
CY
327 (comment (nth 3 entry))
328 oldspec)
403546cb
LT
329 ;; If FACE is actually an alias, customize the face it
330 ;; is aliased to.
331 (if (get face 'face-alias)
332 (setq face (get face 'face-alias)))
e335f09e
CY
333
334 (setq oldspec (get face 'theme-face))
335 (when (not (and oldspec (eq 'user (caar oldspec))))
336 (put face 'saved-face spec)
337 (put face 'saved-face-comment comment))
338
a3997907
RS
339 (custom-push-theme 'theme-face face theme 'set spec)
340 (when (or now immediate)
341 (put face 'force-face (if now 'rogue 'immediate)))
342 (when (or now immediate (facep face))
343 (unless (facep face)
344 (make-empty-face face))
345 (put face 'face-comment comment)
af4bbb69
RS
346 (put face 'face-override-spec nil)
347 (face-spec-set face spec t))
578b4e9d
RS
348 (setq args (cdr args)))
349 ;; Old format, a plist of FACE SPEC pairs.
350 (let ((face (nth 0 args))
351 (spec (nth 1 args)))
352 (if (get face 'face-alias)
353 (setq face (get face 'face-alias)))
354 (put face 'saved-face spec)
355 (custom-push-theme 'theme-face face theme 'set spec))
356 (setq args (cdr (cdr args))))))))
a3997907 357
d358aa10
CY
358;; XEmacs compability function. In XEmacs, when you reset a Custom
359;; Theme, you have to specify the theme to reset it to. We just apply
360;; the next theme.
a3997907 361(defun custom-theme-reset-faces (theme &rest args)
2c9e973f
RS
362 "Reset the specs in THEME of some faces to their specs in other themes.
363Each of the arguments ARGS has this form:
a3997907 364
d358aa10 365 (FACE IGNORED)
a3997907 366
d358aa10 367This means reset FACE. The argument IGNORED is ignored."
a3997907 368 (custom-check-theme theme)
2c9e973f 369 (dolist (arg args)
d358aa10 370 (custom-push-theme 'theme-face (car arg) theme 'reset)))
a3997907 371
a3997907 372(defun custom-reset-faces (&rest args)
2c9e973f
RS
373 "Reset the specs of some faces to their specs in specified themes.
374This creates settings in the `user' theme.
375
376Each of the arguments ARGS has this form:
377
378 (FACE FROM-THEME)
a3997907 379
2c9e973f 380This means reset FACE to its value in FROM-THEME."
a3997907 381 (apply 'custom-theme-reset-faces 'user args))
d543e20b
PA
382
383;;; The End.
384
385(provide 'cus-face)
386
cbee283d 387;; arch-tag: 9a5c4b63-0d27-4c92-a5af-f2c7ed764c2b
1743c17a 388;;; cus-face.el ends here