(custom-buffer-create-internal): Slightly reword text at top of Custom
[bpt/emacs.git] / lisp / cus-edit.el
CommitLineData
e8af40ee 1;;; cus-edit.el --- tools for customizing Emacs and Lisp packages
d543e20b 2;;
f7105ede
SM
3;; Copyright (C) 1996, 1997, 1999, 2000, 2001, 2002, 2003, 2004, 2005
4;; Free Software Foundation, Inc.
d543e20b
PA
5;;
6;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
c942535f 7;; Maintainer: FSF
d543e20b 8;; Keywords: help, faces
d543e20b 9
f2b98a56
RS
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
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
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
26
d543e20b
PA
27;;; Commentary:
28;;
6d528fc5 29;; This file implements the code to create and edit customize buffers.
c942535f 30;;
d543e20b
PA
31;; See `custom.el'.
32
ab678382
RS
33;; No commands should have names starting with `custom-' because
34;; that interferes with completion. Use `customize-' for commands
35;; that the user will run with M-x, and `Custom-' for interactive commands.
36
9f5d3236 37;; The identity of a customize option is represented by a Lisp symbol.
62bf2fd1 38;; The following values are associated with an option.
9f5d3236
RS
39
40;; 0. The current value.
41
42;; This is the value of the option as seen by "the rest of Emacs".
43
44;; Usually extracted by 'default-value', but can be extracted with
45;; different means if the option symbol has the 'custom-get'
46;; property. Similarly, set-default (or the 'custom-set' property)
47;; can set it.
48
49;; 1. The widget value.
50
62bf2fd1 51;; This is the value shown in the widget in a customize buffer.
9f5d3236
RS
52
53;; 2. The customized value.
54
55;; This is the last value given to the option through customize.
56
57;; It is stored in the 'customized-value' property of the option, in a
62bf2fd1 58;; cons-cell whose car evaluates to the customized value.
9f5d3236
RS
59
60;; 3. The saved value.
61
62;; This is last value saved from customize.
63
64;; It is stored in the 'saved-value' property of the option, in a
62bf2fd1 65;; cons-cell whose car evaluates to the saved value.
9f5d3236
RS
66
67;; 4. The standard value.
68
69;; This is the value given in the 'defcustom' declaration.
70
71;; It is stored in the 'standard-value' property of the option, in a
62bf2fd1 72;; cons-cell whose car evaluates to the standard value.
9f5d3236
RS
73
74;; 5. The "think" value.
62bf2fd1
LT
75
76;; This is what customize thinks the current value should be.
77
78;; This is the customized value, if any such value exists, otherwise
9f5d3236 79;; the saved value, if that exists, and as a last resort the standard
62bf2fd1 80;; value.
9f5d3236
RS
81
82;; The reason for storing values unevaluated: This is so you can have
83;; values that depend on the environment. For example, you can have a
62bf2fd1 84;; variable that has one value when Emacs is running under a window
9f5d3236 85;; system, and another value on a tty. Since the evaluation is only done
62bf2fd1 86;; when the variable is first initialized, this is only relevant for the
9f5d3236
RS
87;; saved (and standard) values, but affect others values for
88;; compatibility.
89
90;; You can see (and modify and save) this unevaluated value by selecting
91;; "Show initial Lisp expression" from the Lisp interface. This will
92;; give you the unevaluated saved value, if any, otherwise the
93;; unevaluated standard value.
94
95;; The possible states for a customize widget are:
96
97;; 0. unknown
98
99;; The state has not been determined yet.
100
101;; 1. modified
102
103;; The widget value is different from the current value.
104
105;; 2. changed
62bf2fd1
LT
106
107;; The current value is different from the "think" value.
9f5d3236
RS
108
109;; 3. set
110
111;; The "think" value is the customized value.
112
113;; 4. saved
114
115;; The "think" value is the saved value.
116
117;; 5. standard
118
119;; The "think" value is the standard value.
120
121;; 6. rogue
122
62bf2fd1
LT
123;; There is no standard value. This means that the variable was
124;; not defined with defcustom. You can not create a Custom buffer
125;; for such variables using the normal interactive Custom commands.
126;; However, such Custom buffers can be created in other ways, for
127;; instance, by calling `customize-option' non-interactively.
9f5d3236
RS
128
129;; 7. hidden
130
131;; There is no widget value.
132
133;; 8. mismatch
134
135;; The widget value is not valid member of the :type specified for the
62bf2fd1 136;; option.
9f5d3236 137
d543e20b
PA
138;;; Code:
139
140(require 'cus-face)
141(require 'wid-edit)
164cfaeb 142(eval-when-compile
164cfaeb 143 (defvar custom-versions-load-alist)) ; from cus-load
6d528fc5
PA
144
145(condition-case nil
146 (require 'cus-load)
147 (error nil))
d543e20b 148
9097aeb7
PA
149(condition-case nil
150 (require 'cus-start)
151 (error nil))
152
bd042c03 153(put 'custom-define-hook 'custom-type 'hook)
25ac13b5 154(put 'custom-define-hook 'standard-value '(nil))
bd042c03
PA
155(custom-add-to-group 'customize 'custom-define-hook 'custom-variable)
156
d543e20b
PA
157;;; Customization Groups.
158
159(defgroup emacs nil
160 "Customization of the One True Editor."
161 :link '(custom-manual "(emacs)Top"))
162
163;; Most of these groups are stolen from `finder.el',
164(defgroup editing nil
165 "Basic text editing facilities."
166 :group 'emacs)
167
168(defgroup abbrev nil
169 "Abbreviation handling, typing shortcuts, macros."
170 :tag "Abbreviations"
171 :group 'editing)
172
173(defgroup matching nil
174 "Various sorts of searching and matching."
175 :group 'editing)
176
177(defgroup emulations nil
178 "Emulations of other editors."
6ea2832e 179 :link '(custom-manual "(emacs)Emulation")
d543e20b
PA
180 :group 'editing)
181
182(defgroup mouse nil
183 "Mouse support."
184 :group 'editing)
185
186(defgroup outlines nil
187 "Support for hierarchical outlining."
188 :group 'editing)
189
190(defgroup external nil
191 "Interfacing to external utilities."
192 :group 'emacs)
193
d543e20b
PA
194(defgroup processes nil
195 "Process, subshell, compilation, and job control support."
196 :group 'external
197 :group 'development)
198
4599e8cd
RS
199(defgroup convenience nil
200 "Convenience features for faster editing."
201 :group 'emacs)
202
d543e20b
PA
203(defgroup programming nil
204 "Support for programming in other languages."
205 :group 'emacs)
206
207(defgroup languages nil
208 "Specialized modes for editing programming languages."
209 :group 'programming)
210
211(defgroup lisp nil
212 "Lisp support, including Emacs Lisp."
213 :group 'languages
214 :group 'development)
215
216(defgroup c nil
217 "Support for the C language and related languages."
6ea2832e 218 :link '(custom-manual "(ccmode)")
d543e20b
PA
219 :group 'languages)
220
221(defgroup tools nil
222 "Programming tools."
223 :group 'programming)
224
225(defgroup oop nil
226 "Support for object-oriented programming."
227 :group 'programming)
228
229(defgroup applications nil
230 "Applications written in Emacs."
231 :group 'emacs)
232
233(defgroup calendar nil
234 "Calendar and time management support."
235 :group 'applications)
236
237(defgroup mail nil
238 "Modes for electronic-mail handling."
239 :group 'applications)
240
241(defgroup news nil
242 "Support for netnews reading and posting."
6ea2832e 243 :link '(custom-manual "(gnus)")
d543e20b
PA
244 :group 'applications)
245
246(defgroup games nil
247 "Games, jokes and amusements."
248 :group 'applications)
249
250(defgroup development nil
251 "Support for further development of Emacs."
252 :group 'emacs)
253
254(defgroup docs nil
255 "Support for Emacs documentation."
256 :group 'development)
257
258(defgroup extensions nil
259 "Emacs Lisp language extensions."
260 :group 'development)
261
262(defgroup internal nil
263 "Code for Emacs internals, build process, defaults."
264 :group 'development)
265
266(defgroup maint nil
267 "Maintenance aids for the Emacs development group."
268 :tag "Maintenance"
269 :group 'development)
270
271(defgroup environment nil
272 "Fitting Emacs with its environment."
273 :group 'emacs)
274
275(defgroup comm nil
276 "Communications, networking, remote access to files."
277 :tag "Communication"
278 :group 'environment)
279
280(defgroup hardware nil
281 "Support for interfacing with exotic hardware."
282 :group 'environment)
283
284(defgroup terminals nil
285 "Support for terminal types."
286 :group 'environment)
287
288(defgroup unix nil
289 "Front-ends/assistants for, or emulators of, UNIX features."
290 :group 'environment)
291
292(defgroup vms nil
293 "Support code for vms."
294 :group 'environment)
295
296(defgroup i18n nil
297 "Internationalization and alternate character-set support."
6ea2832e 298 :link '(custom-manual "(emacs)International")
d543e20b
PA
299 :group 'environment
300 :group 'editing)
301
86bd10bc
PA
302(defgroup x nil
303 "The X Window system."
304 :group 'environment)
305
d543e20b
PA
306(defgroup frames nil
307 "Support for Emacs frames and window systems."
308 :group 'environment)
309
310(defgroup data nil
311 "Support editing files of data."
312 :group 'emacs)
313
482e54f3
RS
314(defgroup files nil
315 "Support editing files."
316 :group 'emacs)
317
d543e20b
PA
318(defgroup wp nil
319 "Word processing."
320 :group 'emacs)
321
322(defgroup tex nil
323 "Code related to the TeX formatter."
324 :group 'wp)
325
326(defgroup faces nil
327 "Support for multiple fonts."
328 :group 'emacs)
329
330(defgroup hypermedia nil
331 "Support for links between text or other media types."
332 :group 'emacs)
333
334(defgroup help nil
335 "Support for on-line help systems."
336 :group 'emacs)
337
d3d4df42
DL
338(defgroup multimedia nil
339 "Non-textual support, specifically images and sound."
340 :group 'emacs)
341
d543e20b
PA
342(defgroup local nil
343 "Code local to your site."
344 :group 'emacs)
345
346(defgroup customize '((widgets custom-group))
347 "Customization of the Customization support."
d543e20b 348 :prefix "custom-"
bd042c03
PA
349 :group 'help)
350
351(defgroup custom-faces nil
352 "Faces used by customize."
353 :group 'customize
d543e20b
PA
354 :group 'faces)
355
da5ec617
PA
356(defgroup custom-browse nil
357 "Control customize browser."
358 :prefix "custom-"
359 :group 'customize)
360
6aaedd12 361(defgroup custom-buffer nil
da5ec617 362 "Control customize buffers."
6aaedd12
PA
363 :prefix "custom-"
364 :group 'customize)
365
366(defgroup custom-menu nil
da5ec617 367 "Control customize menus."
6aaedd12
PA
368 :prefix "custom-"
369 :group 'customize)
370
bd042c03
PA
371(defgroup abbrev-mode nil
372 "Word abbreviations mode."
6ea2832e 373 :link '(custom-manual "(emacs)Abbrevs")
bd042c03
PA
374 :group 'abbrev)
375
376(defgroup alloc nil
377 "Storage allocation and gc for GNU Emacs Lisp interpreter."
378 :tag "Storage Allocation"
379 :group 'internal)
380
381(defgroup undo nil
382 "Undoing changes in buffers."
6ea2832e 383 :link '(custom-manual "(emacs)Undo")
bd042c03
PA
384 :group 'editing)
385
386(defgroup modeline nil
387 "Content of the modeline."
388 :group 'environment)
389
bd042c03
PA
390(defgroup editing-basics nil
391 "Most basic editing facilities."
392 :group 'editing)
393
394(defgroup display nil
395 "How characters are displayed in buffers."
396 :group 'environment)
397
398(defgroup execute nil
399 "Executing external commands."
400 :group 'processes)
401
402(defgroup installation nil
403 "The Emacs installation."
404 :group 'environment)
405
406(defgroup dired nil
407 "Directory editing."
408 :group 'environment)
409
410(defgroup limits nil
411 "Internal Emacs limits."
412 :group 'internal)
413
414(defgroup debug nil
415 "Debugging Emacs itself."
416 :group 'development)
417
418(defgroup minibuffer nil
419 "Controling the behaviour of the minibuffer."
6ea2832e 420 :link '(custom-manual "(emacs)Minibuffer")
bd042c03
PA
421 :group 'environment)
422
423(defgroup keyboard nil
424 "Input from the keyboard."
425 :group 'environment)
426
427(defgroup mouse nil
428 "Input from the mouse."
429 :group 'environment)
430
431(defgroup menu nil
432 "Input from the menus."
433 :group 'environment)
434
435(defgroup auto-save nil
436 "Preventing accidential loss of data."
482e54f3 437 :group 'files)
bd042c03
PA
438
439(defgroup processes-basics nil
440 "Basic stuff dealing with processes."
441 :group 'processes)
442
25ac13b5
PA
443(defgroup mule nil
444 "MULE Emacs internationalization."
70bc91bc 445 :group 'i18n)
25ac13b5 446
bd042c03
PA
447(defgroup windows nil
448 "Windows within a frame."
6ea2832e 449 :link '(custom-manual "(emacs)Windows")
86bd10bc 450 :group 'environment)
bd042c03 451
d543e20b
PA
452;;; Utilities.
453
454(defun custom-quote (sexp)
455 "Quote SEXP iff it is not self quoting."
456 (if (or (memq sexp '(t nil))
2365594b 457 (keywordp sexp)
d543e20b
PA
458 (and (listp sexp)
459 (memq (car sexp) '(lambda)))
460 (stringp sexp)
461 (numberp sexp)
d3d4df42
DL
462 (vectorp sexp)
463;;; (and (fboundp 'characterp)
464;;; (characterp sexp))
465 )
d543e20b
PA
466 sexp
467 (list 'quote sexp)))
468
469(defun custom-split-regexp-maybe (regexp)
470 "If REGEXP is a string, split it to a list at `\\|'.
d3d4df42 471You can get the original back with from the result with:
d543e20b
PA
472 (mapconcat 'identity result \"\\|\")
473
474IF REGEXP is not a string, return it unchanged."
475 (if (stringp regexp)
476 (let ((start 0)
477 all)
478 (while (string-match "\\\\|" regexp start)
479 (setq all (cons (substring regexp start (match-beginning 0)) all)
480 start (match-end 0)))
481 (nreverse (cons (substring regexp start) all)))
482 regexp))
483
bd042c03 484(defun custom-variable-prompt ()
3a495e15 485 "Prompt for a custom variable, defaulting to the variable at point.
bd042c03
PA
486Return a list suitable for use in `interactive'."
487 (let ((v (variable-at-point))
488 (enable-recursive-minibuffers t)
489 val)
d3d4df42 490 (setq val (completing-read
3a495e15 491 (if (and (symbolp v) (custom-variable-p v))
64dde95b 492 (format "Customize option: (default %s) " v)
3a495e15
MR
493 "Customize option: ")
494 obarray 'custom-variable-p t))
bd042c03 495 (list (if (equal val "")
5b5cdd97
RS
496 (if (symbolp v) v nil)
497 (intern val)))))
bd042c03 498
6d528fc5
PA
499(defun custom-menu-filter (menu widget)
500 "Convert MENU to the form used by `widget-choose'.
501MENU should be in the same format as `custom-variable-menu'.
502WIDGET is the widget to apply the filter entries of MENU on."
503 (let ((result nil)
504 current name action filter)
d3d4df42 505 (while menu
6d528fc5
PA
506 (setq current (car menu)
507 name (nth 0 current)
508 action (nth 1 current)
509 filter (nth 2 current)
510 menu (cdr menu))
511 (if (or (null filter) (funcall filter widget))
512 (push (cons name action) result)
513 (push name result)))
514 (nreverse result)))
515
bd042c03
PA
516;;; Unlispify.
517
d543e20b 518(defvar custom-prefix-list nil
2365594b 519 "List of prefixes that should be ignored by `custom-unlispify'.")
d543e20b
PA
520
521(defcustom custom-unlispify-menu-entries t
522 "Display menu entries as words instead of symbols if non nil."
6aaedd12 523 :group 'custom-menu
d543e20b
PA
524 :type 'boolean)
525
cda987f4
RS
526(defcustom custom-unlispify-remove-prefixes nil
527 "Non-nil means remove group prefixes from option names in buffer."
528 :group 'custom-menu
42b4edc5 529 :group 'custom-buffer
cda987f4
RS
530 :type 'boolean)
531
d543e20b 532(defun custom-unlispify-menu-entry (symbol &optional no-suffix)
2365594b 533 "Convert SYMBOL into a menu entry."
d543e20b
PA
534 (cond ((not custom-unlispify-menu-entries)
535 (symbol-name symbol))
536 ((get symbol 'custom-tag)
537 (if no-suffix
538 (get symbol 'custom-tag)
539 (concat (get symbol 'custom-tag) "...")))
540 (t
d9f67134 541 (with-current-buffer (get-buffer-create " *Custom-Work*")
d543e20b
PA
542 (erase-buffer)
543 (princ symbol (current-buffer))
544 (goto-char (point-min))
3b2f3d30
SM
545 ;; FIXME: Boolean variables are not predicates, so they shouldn't
546 ;; end with `-p'. -stef
547 ;; (when (and (eq (get symbol 'custom-type) 'boolean)
548 ;; (re-search-forward "-p\\'" nil t))
549 ;; (replace-match "" t t)
550 ;; (goto-char (point-min)))
cda987f4
RS
551 (if custom-unlispify-remove-prefixes
552 (let ((prefixes custom-prefix-list)
553 prefix)
554 (while prefixes
555 (setq prefix (car prefixes))
556 (if (search-forward prefix (+ (point) (length prefix)) t)
d3d4df42 557 (progn
cda987f4
RS
558 (setq prefixes nil)
559 (delete-region (point-min) (point)))
560 (setq prefixes (cdr prefixes))))))
d543e20b
PA
561 (subst-char-in-region (point-min) (point-max) ?- ?\ t)
562 (capitalize-region (point-min) (point-max))
d3d4df42 563 (unless no-suffix
d543e20b
PA
564 (goto-char (point-max))
565 (insert "..."))
566 (buffer-string)))))
567
568(defcustom custom-unlispify-tag-names t
569 "Display tag names as words instead of symbols if non nil."
6aaedd12 570 :group 'custom-buffer
d543e20b
PA
571 :type 'boolean)
572
573(defun custom-unlispify-tag-name (symbol)
2365594b 574 "Convert SYMBOL into a menu entry."
d543e20b
PA
575 (let ((custom-unlispify-menu-entries custom-unlispify-tag-names))
576 (custom-unlispify-menu-entry symbol t)))
577
578(defun custom-prefix-add (symbol prefixes)
2365594b 579 "Add SYMBOL to list of ignored PREFIXES."
d543e20b
PA
580 (cons (or (get symbol 'custom-prefix)
581 (concat (symbol-name symbol) "-"))
582 prefixes))
583
bd042c03
PA
584;;; Guess.
585
586(defcustom custom-guess-name-alist
587 '(("-p\\'" boolean)
588 ("-hook\\'" hook)
589 ("-face\\'" face)
590 ("-file\\'" file)
591 ("-function\\'" function)
592 ("-functions\\'" (repeat function))
593 ("-list\\'" (repeat sexp))
594 ("-alist\\'" (repeat (cons sexp sexp))))
595 "Alist of (MATCH TYPE).
596
d3d4df42 597MATCH should be a regexp matching the name of a symbol, and TYPE should
bd042c03
PA
598be a widget suitable for editing the value of that symbol. The TYPE
599of the first entry where MATCH matches the name of the symbol will be
d3d4df42 600used.
bd042c03
PA
601
602This is used for guessing the type of variables not declared with
603customize."
604 :type '(repeat (group (regexp :tag "Match") (sexp :tag "Type")))
d543e20b
PA
605 :group 'customize)
606
bd042c03
PA
607(defcustom custom-guess-doc-alist
608 '(("\\`\\*?Non-nil " boolean))
609 "Alist of (MATCH TYPE).
d543e20b 610
bd042c03
PA
611MATCH should be a regexp matching a documentation string, and TYPE
612should be a widget suitable for editing the value of a variable with
613that documentation string. The TYPE of the first entry where MATCH
614matches the name of the symbol will be used.
d543e20b 615
bd042c03
PA
616This is used for guessing the type of variables not declared with
617customize."
618 :type '(repeat (group (regexp :tag "Match") (sexp :tag "Type")))
619 :group 'customize)
d543e20b 620
bd042c03
PA
621(defun custom-guess-type (symbol)
622 "Guess a widget suitable for editing the value of SYMBOL.
d3d4df42 623This is done by matching SYMBOL with `custom-guess-name-alist' and
bd042c03
PA
624if that fails, the doc string with `custom-guess-doc-alist'."
625 (let ((name (symbol-name symbol))
626 (names custom-guess-name-alist)
627 current found)
628 (while names
629 (setq current (car names)
630 names (cdr names))
631 (when (string-match (nth 0 current) name)
632 (setq found (nth 1 current)
633 names nil)))
634 (unless found
635 (let ((doc (documentation-property symbol 'variable-documentation))
636 (docs custom-guess-doc-alist))
d3d4df42 637 (when doc
bd042c03
PA
638 (while docs
639 (setq current (car docs)
640 docs (cdr docs))
641 (when (string-match (nth 0 current) doc)
642 (setq found (nth 1 current)
643 docs nil))))))
644 found))
d543e20b 645
25ac13b5
PA
646;;; Sorting.
647
da5ec617
PA
648(defcustom custom-browse-sort-alphabetically nil
649 "If non-nil, sort members of each customization group alphabetically."
650 :type 'boolean
651 :group 'custom-browse)
652
653(defcustom custom-browse-order-groups nil
654 "If non-nil, order group members within each customization group.
655If `first', order groups before non-groups.
656If `last', order groups after non-groups."
657 :type '(choice (const first)
658 (const last)
659 (const :tag "none" nil))
660 :group 'custom-browse)
661
c953515e
PA
662(defcustom custom-browse-only-groups nil
663 "If non-nil, show group members only within each customization group."
664 :type 'boolean
665 :group 'custom-browse)
666
944c91b6 667(defcustom custom-buffer-sort-alphabetically nil
da5ec617 668 "If non-nil, sort members of each customization group alphabetically."
944c91b6 669 :type 'boolean
6aaedd12 670 :group 'custom-buffer)
25ac13b5 671
da5ec617
PA
672(defcustom custom-buffer-order-groups 'last
673 "If non-nil, order group members within each customization group.
674If `first', order groups before non-groups.
675If `last', order groups after non-groups."
676 :type '(choice (const first)
677 (const last)
678 (const :tag "none" nil))
6aaedd12 679 :group 'custom-buffer)
25ac13b5 680
944c91b6 681(defcustom custom-menu-sort-alphabetically nil
da5ec617 682 "If non-nil, sort members of each customization group alphabetically."
944c91b6 683 :type 'boolean
6aaedd12 684 :group 'custom-menu)
25ac13b5 685
da5ec617
PA
686(defcustom custom-menu-order-groups 'first
687 "If non-nil, order group members within each customization group.
688If `first', order groups before non-groups.
689If `last', order groups after non-groups."
690 :type '(choice (const first)
691 (const last)
692 (const :tag "none" nil))
6aaedd12
PA
693 :group 'custom-menu)
694
b4854a23
KH
695;;;###autoload (add-hook 'same-window-regexps "\\`\\*Customiz.*\\*\\'")
696
da5ec617
PA
697(defun custom-sort-items (items sort-alphabetically order-groups)
698 "Return a sorted copy of ITEMS.
699ITEMS should be a `custom-group' property.
700If SORT-ALPHABETICALLY non-nil, sort alphabetically.
701If ORDER-GROUPS is `first' order groups before non-groups, if `last' order
702groups after non-groups, if nil do not order groups at all."
703 (sort (copy-sequence items)
704 (lambda (a b)
705 (let ((typea (nth 1 a)) (typeb (nth 1 b))
359476e0 706 (namea (nth 0 a)) (nameb (nth 0 b)))
da5ec617
PA
707 (cond ((not order-groups)
708 ;; Since we don't care about A and B order, maybe sort.
709 (when sort-alphabetically
710 (string-lessp namea nameb)))
711 ((eq typea 'custom-group)
712 ;; If B is also a group, maybe sort. Otherwise, order A and B.
713 (if (eq typeb 'custom-group)
714 (when sort-alphabetically
715 (string-lessp namea nameb))
716 (eq order-groups 'first)))
717 ((eq typeb 'custom-group)
718 ;; Since A cannot be a group, order A and B.
719 (eq order-groups 'last))
720 (sort-alphabetically
721 ;; Since A and B cannot be groups, sort.
722 (string-lessp namea nameb)))))))
25ac13b5 723
d543e20b
PA
724;;; Custom Mode Commands.
725
bd042c03
PA
726(defvar custom-options nil
727 "Customization widgets in the current buffer.")
728
ab678382 729(defun Custom-set ()
d543e20b
PA
730 "Set changes in all modified options."
731 (interactive)
732 (let ((children custom-options))
fadbdfea
DL
733 (mapc (lambda (child)
734 (when (eq (widget-get child :custom-state) 'modified)
735 (widget-apply child :custom-set)))
d543e20b
PA
736 children)))
737
ab678382 738(defun Custom-save ()
d543e20b
PA
739 "Set all modified group members and save them."
740 (interactive)
741 (let ((children custom-options))
fadbdfea
DL
742 (mapc (lambda (child)
743 (when (memq (widget-get child :custom-state)
744 '(modified set changed rogue))
745 (widget-apply child :custom-save)))
d543e20b
PA
746 children))
747 (custom-save-all))
748
d3d4df42 749(defvar custom-reset-menu
ab678382
RS
750 '(("Current" . Custom-reset-current)
751 ("Saved" . Custom-reset-saved)
19d63704 752 ("Erase Customization (use standard settings)" . Custom-reset-standard))
d543e20b
PA
753 "Alist of actions for the `Reset' button.
754The key is a string containing the name of the action, the value is a
2365594b 755Lisp function taking the widget as an element which will be called
d543e20b
PA
756when the action is chosen.")
757
758(defun custom-reset (event)
759 "Select item from reset menu."
760 (let* ((completion-ignore-case t)
761 (answer (widget-choose "Reset to"
762 custom-reset-menu
763 event)))
764 (if answer
765 (funcall answer))))
766
ab678382 767(defun Custom-reset-current (&rest ignore)
d543e20b
PA
768 "Reset all modified group members to their current value."
769 (interactive)
770 (let ((children custom-options))
fadbdfea 771 (mapc (lambda (widget)
1d58631a
MR
772 (if (memq (widget-get widget :custom-state)
773 '(modified changed))
774 (widget-apply widget :custom-reset-current)))
775 children)))
d543e20b 776
ab678382 777(defun Custom-reset-saved (&rest ignore)
d543e20b
PA
778 "Reset all modified or set group members to their saved value."
779 (interactive)
780 (let ((children custom-options))
fadbdfea 781 (mapc (lambda (widget)
1d58631a
MR
782 (if (memq (widget-get widget :custom-state)
783 '(modified set changed rogue))
784 (widget-apply widget :custom-reset-saved)))
785 children)))
d543e20b 786
ab678382 787(defun Custom-reset-standard (&rest ignore)
19d63704
RS
788 "Erase all customization (either current or saved) for the group members.
789The immediate result is to restore them to their standard settings.
790This operation eliminates any saved settings for the group members,
791making them as if they had never been customized at all."
d543e20b
PA
792 (interactive)
793 (let ((children custom-options))
fadbdfea 794 (mapc (lambda (widget)
4f985043 795 (and (widget-apply widget :custom-standard-value)
fadbdfea
DL
796 (if (memq (widget-get widget :custom-state)
797 '(modified set changed saved rogue))
798 (widget-apply widget :custom-reset-standard))))
d543e20b
PA
799 children)))
800
801;;; The Customize Commands
802
d3d4df42 803(defun custom-prompt-variable (prompt-var prompt-val &optional comment)
6d528fc5
PA
804 "Prompt for a variable and a value and return them as a list.
805PROMPT-VAR is the prompt for the variable, and PROMPT-VAL is the
806prompt for the value. The %s escape in PROMPT-VAL is replaced with
807the name of the variable.
808
809If the variable has a `variable-interactive' property, that is used as if
810it were the arg to `interactive' (which see) to interactively read the value.
811
812If the variable has a `custom-type' property, it must be a widget and the
d3d4df42
DL
813`:prompt-value' property of that widget will be used for reading the value.
814
815If optional COMMENT argument is non nil, also prompt for a comment and return
816it as the third element in the list."
6d528fc5 817 (let* ((var (read-variable prompt-var))
d3d4df42
DL
818 (minibuffer-help-form '(describe-variable var))
819 (val
6d528fc5
PA
820 (let ((prop (get var 'variable-interactive))
821 (type (get var 'custom-type))
822 (prompt (format prompt-val var)))
823 (unless (listp type)
824 (setq type (list type)))
825 (cond (prop
826 ;; Use VAR's `variable-interactive' property
827 ;; as an interactive spec for prompting.
828 (call-interactively (list 'lambda '(arg)
829 (list 'interactive prop)
830 'arg)))
831 (type
832 (widget-prompt-value type
833 prompt
834 (if (boundp var)
835 (symbol-value var))
836 (not (boundp var))))
837 (t
d3d4df42
DL
838 (eval-minibuffer prompt))))))
839 (if comment
840 (list var val
841 (read-string "Comment: " (get var 'variable-comment)))
842 (list var val))))
6d528fc5
PA
843
844;;;###autoload
7ec8d2c6 845(defun customize-set-value (variable value &optional comment)
4f9b9060 846 "Set VARIABLE to VALUE, and return VALUE. VALUE is a Lisp object.
6d528fc5
PA
847
848If VARIABLE has a `variable-interactive' property, that is used as if
849it were the arg to `interactive' (which see) to interactively read the value.
850
851If VARIABLE has a `custom-type' property, it must be a widget and the
d3d4df42
DL
852`:prompt-value' property of that widget will be used for reading the value.
853
854If given a prefix (or a COMMENT argument), also prompt for a comment."
6d528fc5 855 (interactive (custom-prompt-variable "Set variable: "
d3d4df42
DL
856 "Set %s to value: "
857 current-prefix-arg))
ee1f522d 858
d3d4df42 859 (cond ((string= comment "")
7ec8d2c6 860 (put variable 'variable-comment nil))
d3d4df42 861 (comment
7ec8d2c6
PJ
862 (put variable 'variable-comment comment)))
863 (set variable value))
6d528fc5
PA
864
865;;;###autoload
2365594b 866(defun customize-set-variable (variable value &optional comment)
4f9b9060
PA
867 "Set the default for VARIABLE to VALUE, and return VALUE.
868VALUE is a Lisp object.
6d528fc5
PA
869
870If VARIABLE has a `custom-set' property, that is used for setting
871VARIABLE, otherwise `set-default' is used.
872
873The `customized-value' property of the VARIABLE will be set to a list
874with a quoted VALUE as its sole list member.
875
876If VARIABLE has a `variable-interactive' property, that is used as if
877it were the arg to `interactive' (which see) to interactively read the value.
878
879If VARIABLE has a `custom-type' property, it must be a widget and the
d3d4df42
DL
880`:prompt-value' property of that widget will be used for reading the value.
881
882If given a prefix (or a COMMENT argument), also prompt for a comment."
6d528fc5 883 (interactive (custom-prompt-variable "Set variable: "
d3d4df42
DL
884 "Set customized value for %s to: "
885 current-prefix-arg))
4f9b9060 886 (custom-load-symbol variable)
2365594b
DL
887 (funcall (or (get variable 'custom-set) 'set-default) variable value)
888 (put variable 'customized-value (list (custom-quote value)))
d3d4df42 889 (cond ((string= comment "")
2365594b
DL
890 (put variable 'variable-comment nil)
891 (put variable 'customized-variable-comment nil))
d3d4df42 892 (comment
2365594b 893 (put variable 'variable-comment comment)
4f9b9060
PA
894 (put variable 'customized-variable-comment comment)))
895 value)
6d528fc5 896
4ee1cf9f 897;;;###autoload
7ec8d2c6 898(defun customize-save-variable (variable value &optional comment)
4ee1cf9f 899 "Set the default for VARIABLE to VALUE, and save it for future sessions.
4f9b9060
PA
900Return VALUE.
901
4ee1cf9f
PA
902If VARIABLE has a `custom-set' property, that is used for setting
903VARIABLE, otherwise `set-default' is used.
904
905The `customized-value' property of the VARIABLE will be set to a list
906with a quoted VALUE as its sole list member.
907
908If VARIABLE has a `variable-interactive' property, that is used as if
909it were the arg to `interactive' (which see) to interactively read the value.
910
911If VARIABLE has a `custom-type' property, it must be a widget and the
d3d4df42
DL
912`:prompt-value' property of that widget will be used for reading the value.
913
914If given a prefix (or a COMMENT argument), also prompt for a comment."
901cd78b 915 (interactive (custom-prompt-variable "Set and save variable: "
d3d4df42
DL
916 "Set and save value for %s as: "
917 current-prefix-arg))
7ec8d2c6
PJ
918 (funcall (or (get variable 'custom-set) 'set-default) variable value)
919 (put variable 'saved-value (list (custom-quote value)))
c942535f 920 (custom-push-theme 'theme-value variable 'user 'set (list (custom-quote value)))
d3d4df42 921 (cond ((string= comment "")
7ec8d2c6
PJ
922 (put variable 'variable-comment nil)
923 (put variable 'saved-variable-comment nil))
d3d4df42 924 (comment
7ec8d2c6
PJ
925 (put variable 'variable-comment comment)
926 (put variable 'saved-variable-comment comment)))
4f9b9060
PA
927 (custom-save-all)
928 value)
4ee1cf9f 929
d543e20b 930;;;###autoload
5dd0cad0
RS
931(defun customize ()
932 "Select a customization buffer which you can use to set user options.
933User options are structured into \"groups\".
934Initially the top-level group `Emacs' and its immediate subgroups
935are shown; the contents of those subgroups are initially hidden."
936 (interactive)
dc2e979f 937 (customize-group 'emacs))
5dd0cad0 938
87434e7a
SM
939;;;###autoload
940(defun customize-mode (mode)
941 "Customize options related to the current major mode.
942If a prefix \\[universal-argument] was given (or if the current major mode has no known group),
943then prompt for the MODE to customize."
944 (interactive
945 (list
946 (let ((completion-regexp-list '("-mode\\'"))
947 (group (custom-group-of-mode major-mode)))
948 (if (and group (not current-prefix-arg))
949 major-mode
950 (intern
951 (completing-read (if group
952 (format "Major mode (default %s): " major-mode)
953 "Major mode: ")
954 obarray
955 'custom-group-of-mode
956 t nil nil (if group (symbol-name major-mode))))))))
957 (customize-group (custom-group-of-mode mode)))
958
959
5dd0cad0
RS
960;;;###autoload
961(defun customize-group (group)
962 "Customize GROUP, which must be a customization group."
07e694f8
RS
963 (interactive (list (let ((completion-ignore-case t))
964 (completing-read "Customize group: (default emacs) "
d3d4df42 965 obarray
07e694f8 966 (lambda (symbol)
5aa3f181
RS
967 (or (get symbol 'custom-loads)
968 (get symbol 'custom-group)))
07e694f8 969 t))))
5dd0cad0
RS
970 (when (stringp group)
971 (if (string-equal "" group)
972 (setq group 'emacs)
973 (setq group (intern group))))
241d3080
RS
974 (let ((name (format "*Customize Group: %s*"
975 (custom-unlispify-tag-name group))))
976 (if (get-buffer name)
b4854a23 977 (pop-to-buffer name)
241d3080 978 (custom-buffer-create (list (list group 'custom-group))
3aec85bf
RS
979 name
980 (concat " for group "
981 (custom-unlispify-tag-name group))))))
d543e20b 982
6d528fc5 983;;;###autoload
fd88fe73
RS
984(defun customize-group-other-window (group)
985 "Customize GROUP, which must be a customization group."
986 (interactive (list (let ((completion-ignore-case t))
987 (completing-read "Customize group: (default emacs) "
d3d4df42 988 obarray
fd88fe73
RS
989 (lambda (symbol)
990 (or (get symbol 'custom-loads)
991 (get symbol 'custom-group)))
992 t))))
993 (when (stringp group)
994 (if (string-equal "" group)
995 (setq group 'emacs)
996 (setq group (intern group))))
fd88fe73
RS
997 (let ((name (format "*Customize Group: %s*"
998 (custom-unlispify-tag-name group))))
999 (if (get-buffer name)
5f97a49d 1000 (let (
ffec8c5a
MR
1001 ;; Copied from `custom-buffer-create-other-window'.
1002 (pop-up-windows t)
ffec8c5a
MR
1003 (same-window-buffer-names nil)
1004 (same-window-regexps nil))
5f97a49d 1005 (pop-to-buffer name))
fd88fe73
RS
1006 (custom-buffer-create-other-window
1007 (list (list group 'custom-group))
1008 name
1009 (concat " for group "
1010 (custom-unlispify-tag-name group))))))
6d528fc5 1011
9097aeb7
PA
1012;;;###autoload
1013(defalias 'customize-variable 'customize-option)
38d58078 1014
d543e20b 1015;;;###autoload
38d58078
RS
1016(defun customize-option (symbol)
1017 "Customize SYMBOL, which must be a user option variable."
bd042c03 1018 (interactive (custom-variable-prompt))
86bd10bc 1019 (custom-buffer-create (list (list symbol 'custom-variable))
38d58078 1020 (format "*Customize Option: %s*"
86bd10bc 1021 (custom-unlispify-tag-name symbol))))
d543e20b 1022
ffec8c5a
MR
1023;;;###autoload
1024(defalias 'customize-variable-other-window 'customize-option-other-window)
1025
1026;;;###autoload
1027(defun customize-option-other-window (symbol)
1028 "Customize SYMBOL, which must be a user option variable.
1029Show the buffer in another window, but don't select it."
1030 (interactive (custom-variable-prompt))
1031 (custom-buffer-create-other-window
1032 (list (list symbol 'custom-variable))
1033 (format "*Customize Option: %s*" (custom-unlispify-tag-name symbol))))
1034
e418be26
KH
1035(defvar customize-changed-options-previous-release "20.2"
1036 "Version for `customize-changed-options' to refer back to by default.")
1037
c32de15e 1038;;;###autoload
f50dc5d2 1039(defun customize-changed-options (since-version)
e418be26
KH
1040 "Customize all user option variables changed in Emacs itself.
1041This includes new user option variables and faces, and new
1042customization groups, as well as older options and faces whose default
1043values have changed since the previous major Emacs release.
1044
1045With argument SINCE-VERSION (a string), customize all user option
1046variables that were added (or their meanings were changed) since that
2ee398c4 1047version."
e418be26 1048
f50dc5d2
KH
1049 (interactive "sCustomize options changed, since version (default all versions): ")
1050 (if (equal since-version "")
26c67de8
DL
1051 (setq since-version nil)
1052 (unless (condition-case nil
1053 (numberp (read since-version))
1054 (error nil))
1055 (signal 'wrong-type-argument (list 'numberp since-version))))
e418be26
KH
1056 (unless since-version
1057 (setq since-version customize-changed-options-previous-release))
cba752d0
MR
1058
1059 ;; Load the information for versions since since-version. We use
1060 ;; custom-load-symbol for this.
1061 (put 'custom-versions-load-alist 'custom-loads nil)
1062 (dolist (elt custom-versions-load-alist)
2ee398c4
MR
1063 (if (customize-version-lessp since-version (car elt))
1064 (dolist (load (cdr elt))
1065 (custom-add-load 'custom-versions-load-alist load))))
cba752d0
MR
1066 (custom-load-symbol 'custom-versions-load-alist)
1067 (put 'custom-versions-load-alist 'custom-loads nil)
1068
1069 (let (found)
1070 (mapatoms
1071 (lambda (symbol)
1072 (let ((version (get symbol 'custom-version)))
1073 (if version
1074 (when (customize-version-lessp since-version version)
1075 (if (or (get symbol 'custom-group)
1076 (get symbol 'group-documentation))
1077 (push (list symbol 'custom-group) found))
1078 (if (custom-variable-p symbol)
1079 (push (list symbol 'custom-variable) found))
1080 (if (custom-facep symbol)
1081 (push (list symbol 'custom-face) found)))))))
1082 (if found
1083 (custom-buffer-create (custom-sort-items found t 'first)
1084 "*Customize Changed Options*")
1085 (error "No user option defaults have been changed since Emacs %s"
1086 since-version))))
f50dc5d2
KH
1087
1088(defun customize-version-lessp (version1 version2)
26c67de8
DL
1089 ;; Why are the versions strings, and given that they are, why aren't
1090 ;; they converted to numbers and compared as such here? -- fx
1091
e418be26
KH
1092 ;; In case someone made a mistake and left out the quotes
1093 ;; in the :version value.
1094 (if (numberp version2)
1095 (setq version2 (prin1-to-string version2)))
f50dc5d2 1096 (let (major1 major2 minor1 minor2)
26c67de8
DL
1097 (string-match "\\([0-9]+\\)\\(\\.\\([0-9]+\\)\\)?" version1)
1098 (setq major1 (read (or (match-string 1 version1)
1099 "0")))
1100 (setq minor1 (read (or (match-string 3 version1)
1101 "0")))
1102 (string-match "\\([0-9]+\\)\\(\\.\\([0-9]+\\)\\)?" version2)
1103 (setq major2 (read (or (match-string 1 version2)
1104 "0")))
1105 (setq minor2 (read (or (match-string 3 version2)
1106 "0")))
f50dc5d2
KH
1107 (or (< major1 major2)
1108 (and (= major1 major2)
1109 (< minor1 minor2)))))
c942535f 1110
d543e20b 1111;;;###autoload
cdd50dea 1112(defun customize-face (&optional face)
3127aa13
JB
1113 "Customize FACE, which should be a face name or nil.
1114If FACE is nil, customize all faces.
d29a536a
GM
1115
1116Interactively, when point is on text which has a face specified,
3127aa13 1117suggest to customize that face, if it's customizable."
d29a536a 1118 (interactive
cdd50dea
RS
1119 (list (read-face-name "Customize face" "all faces" t)))
1120 (if (member face '(nil ""))
1121 (setq face (face-list)))
1122 (if (and (listp face) (null (cdr face)))
1123 (setq face (car face)))
1124 (if (listp face)
da5ec617 1125 (custom-buffer-create (custom-sort-items
cdd50dea
RS
1126 (mapcar (lambda (s)
1127 (list s 'custom-face))
1128 face)
da5ec617
PA
1129 t nil)
1130 "*Customize Faces*")
cdd50dea 1131 (unless (facep face)
b59b77f1 1132 (error "Invalid face %S" face))
cdd50dea 1133 (custom-buffer-create (list (list face 'custom-face))
86bd10bc 1134 (format "*Customize Face: %s*"
cdd50dea 1135 (custom-unlispify-tag-name face)))))
d543e20b 1136
bd042c03 1137;;;###autoload
701f49d2 1138(defun customize-face-other-window (&optional face)
3127aa13 1139 "Show customization buffer for face FACE in other window.
d29a536a
GM
1140
1141Interactively, when point is on text which has a face specified,
3127aa13 1142suggest to customize that face, if it's customizable."
d29a536a 1143 (interactive
501d8923
RS
1144 (list (read-face-name "Customize face" "all faces" t)))
1145 (if (member face '(nil ""))
1146 (setq face (face-list)))
1147 (if (and (listp face) (null (cdr face)))
1148 (setq face (car face)))
1149 (if (listp face)
1150 (custom-buffer-create-other-window
1151 (custom-sort-items
1152 (mapcar (lambda (s)
1153 (list s 'custom-face))
1154 face)
1155 t nil)
1156 "*Customize Faces*")
1157 (unless (facep face)
b59b77f1 1158 (error "Invalid face %S" face))
d3d4df42 1159 (custom-buffer-create-other-window
501d8923
RS
1160 (list (list face 'custom-face))
1161 (format "*Customize Face: %s*"
1162 (custom-unlispify-tag-name face)))))
bd042c03 1163
d543e20b
PA
1164;;;###autoload
1165(defun customize-customized ()
6d528fc5
PA
1166 "Customize all user options set since the last save in this session."
1167 (interactive)
1168 (let ((found nil))
1169 (mapatoms (lambda (symbol)
d3d4df42
DL
1170 (and (or (get symbol 'customized-face)
1171 (get symbol 'customized-face-comment))
6d528fc5 1172 (custom-facep symbol)
a1a4fa22 1173 (push (list symbol 'custom-face) found))
d3d4df42
DL
1174 (and (or (get symbol 'customized-value)
1175 (get symbol 'customized-variable-comment))
6d528fc5 1176 (boundp symbol)
a1a4fa22 1177 (push (list symbol 'custom-variable) found))))
da5ec617
PA
1178 (if (not found)
1179 (error "No customized user options")
1180 (custom-buffer-create (custom-sort-items found t nil)
1181 "*Customize Customized*"))))
6d528fc5
PA
1182
1183;;;###autoload
963c2e09
PA
1184(defun customize-rogue ()
1185 "Customize all user variable modified outside customize."
1186 (interactive)
1187 (let ((found nil))
1188 (mapatoms (lambda (symbol)
1189 (let ((cval (or (get symbol 'customized-value)
1190 (get symbol 'saved-value)
1191 (get symbol 'standard-value))))
1192 (when (and cval ;Declared with defcustom.
1193 (default-boundp symbol) ;Has a value.
3127aa13 1194 (not (equal (eval (car cval))
963c2e09
PA
1195 ;; Which does not match customize.
1196 (default-value symbol))))
1197 (push (list symbol 'custom-variable) found)))))
1198 (if (not found)
1199 (error "No rogue user options")
1200 (custom-buffer-create (custom-sort-items found t nil)
1201 "*Customize Rogue*"))))
1202;;;###autoload
6d528fc5
PA
1203(defun customize-saved ()
1204 "Customize all already saved user options."
d543e20b
PA
1205 (interactive)
1206 (let ((found nil))
1207 (mapatoms (lambda (symbol)
d3d4df42
DL
1208 (and (or (get symbol 'saved-face)
1209 (get symbol 'saved-face-comment))
d543e20b 1210 (custom-facep symbol)
a1a4fa22 1211 (push (list symbol 'custom-face) found))
d3d4df42
DL
1212 (and (or (get symbol 'saved-value)
1213 (get symbol 'saved-variable-comment))
d543e20b 1214 (boundp symbol)
a1a4fa22 1215 (push (list symbol 'custom-variable) found))))
da5ec617
PA
1216 (if (not found )
1217 (error "No saved user options")
1218 (custom-buffer-create (custom-sort-items found t nil)
1219 "*Customize Saved*"))))
d543e20b
PA
1220
1221;;;###autoload
1222(defun customize-apropos (regexp &optional all)
1223 "Customize all user options matching REGEXP.
a1a4fa22
PA
1224If ALL is `options', include only options.
1225If ALL is `faces', include only faces.
1226If ALL is `groups', include only groups.
1227If ALL is t (interactively, with prefix arg), include options which are not
1228user-settable, as well as faces and groups."
d543e20b
PA
1229 (interactive "sCustomize regexp: \nP")
1230 (let ((found nil))
1231 (mapatoms (lambda (symbol)
1232 (when (string-match regexp (symbol-name symbol))
a1a4fa22
PA
1233 (when (and (not (memq all '(faces options)))
1234 (get symbol 'custom-group))
1235 (push (list symbol 'custom-group) found))
1236 (when (and (not (memq all '(options groups)))
1237 (custom-facep symbol))
1238 (push (list symbol 'custom-face) found))
1239 (when (and (not (memq all '(groups faces)))
1240 (boundp symbol)
d543e20b 1241 (or (get symbol 'saved-value)
3a495e15 1242 (custom-variable-p symbol)
a1a4fa22
PA
1243 (if (memq all '(nil options))
1244 (user-variable-p symbol)
1245 (get symbol 'variable-documentation))))
1246 (push (list symbol 'custom-variable) found)))))
1247 (if (not found)
1248 (error "No matches")
da5ec617
PA
1249 (custom-buffer-create (custom-sort-items found t
1250 custom-buffer-order-groups)
1251 "*Customize Apropos*"))))
a1a4fa22
PA
1252
1253;;;###autoload
1254(defun customize-apropos-options (regexp &optional arg)
1255 "Customize all user options matching REGEXP.
1256With prefix arg, include options which are not user-settable."
1257 (interactive "sCustomize regexp: \nP")
1258 (customize-apropos regexp (or arg 'options)))
1259
1260;;;###autoload
1261(defun customize-apropos-faces (regexp)
1262 "Customize all user faces matching REGEXP."
1263 (interactive "sCustomize regexp: \n")
1264 (customize-apropos regexp 'faces))
1265
1266;;;###autoload
1267(defun customize-apropos-groups (regexp)
1268 "Customize all user groups matching REGEXP."
1269 (interactive "sCustomize regexp: \n")
1270 (customize-apropos regexp 'groups))
d543e20b 1271
6d528fc5
PA
1272;;; Buffer.
1273
944c91b6
PA
1274(defcustom custom-buffer-style 'links
1275 "Control the presentation style for customization buffers.
1276The value should be a symbol, one of:
1277
1278brackets: groups nest within each other with big horizontal brackets.
1279links: groups have links to subgroups."
1280 :type '(radio (const brackets)
1281 (const links))
1282 :group 'custom-buffer)
1283
97733c3d
RS
1284(defcustom custom-buffer-done-kill nil
1285 "*Non-nil means exiting a Custom buffer should kill it."
1286 :type 'boolean
bf247b6e 1287 :version "22.1"
d3d4df42
DL
1288 :group 'custom-buffer)
1289
944c91b6
PA
1290(defcustom custom-buffer-indent 3
1291 "Number of spaces to indent nested groups."
1292 :type 'integer
1293 :group 'custom-buffer)
1294
40627755
SM
1295(defun custom-get-fresh-buffer (name)
1296 "Get a fresh new buffer with name NAME.
a153acb7
SM
1297If the buffer already exist, clean it up to be like new.
1298Beware: it's not quite like new. Good enough for custom, but maybe
1299not for everybody."
1300 ;; To be more complete, we should also kill all permanent-local variables,
1301 ;; but it's not needed for custom.
40627755 1302 (let ((buf (get-buffer name)))
fd1c38f4 1303 (when (and buf (buffer-local-value 'buffer-file-name buf))
a153acb7
SM
1304 ;; This will check if the file is not saved.
1305 (kill-buffer buf)
1306 (setq buf nil))
40627755
SM
1307 (if (null buf)
1308 (get-buffer-create name)
1309 (with-current-buffer buf
1310 (kill-all-local-variables)
a153acb7 1311 (run-hooks 'kill-buffer-hook)
f5a9b180
SM
1312 ;; Delete overlays before erasing the buffer so the overlay hooks
1313 ;; don't get run spuriously when we erase the buffer.
40627755
SM
1314 (let ((ols (overlay-lists)))
1315 (dolist (ol (nconc (car ols) (cdr ols)))
1316 (delete-overlay ol)))
f5a9b180 1317 (erase-buffer)
40627755
SM
1318 buf))))
1319
d543e20b 1320;;;###autoload
3aec85bf 1321(defun custom-buffer-create (options &optional name description)
d543e20b 1322 "Create a buffer containing OPTIONS.
86bd10bc 1323Optional NAME is the name of the buffer.
d543e20b
PA
1324OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
1325SYMBOL is a customization option, and WIDGET is a widget for editing
1326that option."
40627755 1327 (pop-to-buffer (custom-get-fresh-buffer (or name "*Customization*")))
3aec85bf 1328 (custom-buffer-create-internal options description))
bd042c03 1329
6d528fc5 1330;;;###autoload
3aec85bf 1331(defun custom-buffer-create-other-window (options &optional name description)
5f97a49d
RS
1332 "Create a buffer containing OPTIONS, and display it in another window.
1333The result includes selecting that window.
86bd10bc 1334Optional NAME is the name of the buffer.
bd042c03
PA
1335OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
1336SYMBOL is a customization option, and WIDGET is a widget for editing
1337that option."
86bd10bc 1338 (unless name (setq name "*Customization*"))
5f97a49d 1339 (let ((pop-up-windows t)
b4854a23
KH
1340 (same-window-buffer-names nil)
1341 (same-window-regexps nil))
40627755 1342 (pop-to-buffer (custom-get-fresh-buffer name))
5f97a49d 1343 (custom-buffer-create-internal options description)))
9097aeb7
PA
1344
1345(defcustom custom-reset-button-menu nil
1346 "If non-nil, only show a single reset button in customize buffers.
1347This button will have a menu with all three reset operations."
1348 :type 'boolean
6aaedd12 1349 :group 'custom-buffer)
bd042c03 1350
c42c5c7c
KS
1351(defcustom custom-buffer-verbose-help t
1352 "If non-nil, include explanatory text in the customization buffer."
1353 :type 'boolean
1354 :group 'custom-buffer)
1355
d3d4df42 1356(defun Custom-buffer-done (&rest ignore)
97733c3d 1357 "Exit current Custom buffer according to `custom-buffer-done-kill'."
d3d4df42 1358 (interactive)
97733c3d 1359 (quit-window custom-buffer-done-kill))
d3d4df42
DL
1360
1361(defcustom custom-raised-buttons (not (equal (face-valid-attribute-values :box)
1362 '(("unspecified" . unspecified))))
1363 "If non-nil, indicate active buttons in a `raised-button' style.
1364Otherwise use brackets."
1365 :type 'boolean
1366 :version "21.1"
1367 :group 'custom-buffer)
1368
3aec85bf 1369(defun custom-buffer-create-internal (options &optional description)
d543e20b 1370 (custom-mode)
c42c5c7c
KS
1371 (if custom-buffer-verbose-help
1372 (progn
1373 (widget-insert "This is a customization buffer")
1374 (if description
1375 (widget-insert description))
1376 (widget-insert (format ".
d3d4df42 1377%s show active fields; type RET or click mouse-1
3aec85bf 1378on an active field to invoke its action. Editing an option value
167eefc5
LT
1379changes only the text in the buffer. Invoke the State button to set or
1380save the option value. Saving an option normally edits your init file.
1381Invoke "
1382 (if custom-raised-buttons
1383 "`Raised' buttons"
1384 "Square brackets")))
1385 (widget-create 'info-link
1386 :tag "Custom file"
1387 "(emacs)Saving Customizations")
1388 (widget-insert
1389 " for information on how to save in a different file.
1390Invoke ")
c42c5c7c
KS
1391 (widget-create 'info-link
1392 :tag "Help"
1393 :help-echo "Read the online help."
1394 "(emacs)Easy Customization")
167eefc5 1395 (widget-insert " for general information.\n\n")
c42c5c7c
KS
1396 (widget-insert "Operate on everything in this buffer:\n "))
1397 (widget-insert " "))
25ac13b5 1398 (widget-create 'push-button
0f3335c0 1399 :tag "Set for Current Session"
b62c92bb
RS
1400 :help-echo "\
1401Make your editing in this buffer take effect for this session."
25ac13b5 1402 :action (lambda (widget &optional event)
ab678382 1403 (Custom-set)))
25ac13b5
PA
1404 (widget-insert " ")
1405 (widget-create 'push-button
0f3335c0 1406 :tag "Save for Future Sessions"
25ac13b5 1407 :help-echo "\
9f5d3236 1408Make your editing in this buffer take effect for future Emacs sessions.
470cefd3 1409This updates your Emacs initialization file or creates a new one."
25ac13b5 1410 :action (lambda (widget &optional event)
ab678382 1411 (Custom-save)))
9097aeb7 1412 (if custom-reset-button-menu
0f3335c0
RS
1413 (progn
1414 (widget-insert " ")
1415 (widget-create 'push-button
1416 :tag "Reset"
1417 :help-echo "Show a menu with reset operations."
1418 :mouse-down-action (lambda (&rest junk) t)
1419 :action (lambda (widget &optional event)
1420 (custom-reset event))))
1421 (widget-insert "\n ")
9097aeb7
PA
1422 (widget-create 'push-button
1423 :tag "Reset"
c32de15e 1424 :help-echo "\
b62c92bb 1425Reset all edited text in this buffer to reflect current values."
ab678382 1426 :action 'Custom-reset-current)
9097aeb7
PA
1427 (widget-insert " ")
1428 (widget-create 'push-button
1429 :tag "Reset to Saved"
c32de15e 1430 :help-echo "\
b62c92bb 1431Reset all values in this buffer to their saved settings."
ab678382 1432 :action 'Custom-reset-saved)
9097aeb7
PA
1433 (widget-insert " ")
1434 (widget-create 'push-button
19d63704 1435 :tag "Erase Customization"
c32de15e 1436 :help-echo "\
19d63704 1437Un-customize all values in this buffer. They get their standard settings."
ab678382 1438 :action 'Custom-reset-standard))
c42c5c7c
KS
1439 (if (not custom-buffer-verbose-help)
1440 (progn
1441 (widget-insert " ")
1442 (widget-create 'info-link
1443 :tag "Help"
1444 :help-echo "Read the online help."
1445 "(emacs)Easy Customization")))
0eef62d5 1446 (widget-insert " ")
25ac13b5 1447 (widget-create 'push-button
d3d4df42 1448 :tag "Finish"
91a38db1
DL
1449 :help-echo
1450 (lambda (&rest ignore)
97733c3d
RS
1451 (if custom-buffer-done-kill
1452 "Kill this buffer"
1453 "Bury this buffer"))
d3d4df42 1454 :action #'Custom-buffer-done)
25ac13b5
PA
1455 (widget-insert "\n\n")
1456 (message "Creating customization items...")
fadbdfea 1457 (buffer-disable-undo)
d3d4df42 1458 (setq custom-options
d543e20b
PA
1459 (if (= (length options) 1)
1460 (mapcar (lambda (entry)
1461 (widget-create (nth 1 entry)
c32de15e 1462 :documentation-shown t
d543e20b
PA
1463 :custom-state 'unknown
1464 :tag (custom-unlispify-tag-name
1465 (nth 0 entry))
1466 :value (nth 0 entry)))
1467 options)
1468 (let ((count 0)
1469 (length (length options)))
1470 (mapcar (lambda (entry)
6b292312
DL
1471 (prog2
1472 (message "Creating customization items ...%2d%%"
1473 (/ (* 100.0 count) length))
1474 (widget-create (nth 1 entry)
d543e20b
PA
1475 :tag (custom-unlispify-tag-name
1476 (nth 0 entry))
1477 :value (nth 0 entry))
6b292312
DL
1478 (setq count (1+ count))
1479 (unless (eq (preceding-char) ?\n)
1480 (widget-insert "\n"))
1481 (widget-insert "\n")))
1482 options))))
d543e20b
PA
1483 (unless (eq (preceding-char) ?\n)
1484 (widget-insert "\n"))
a9bcbf3f 1485 (message "Creating customization items ...done")
1833b7b3 1486 (message "Resetting customization items...")
944c91b6 1487 (unless (eq custom-buffer-style 'tree)
fadbdfea 1488 (mapc 'custom-magic-reset custom-options))
1833b7b3 1489 (message "Resetting customization items...done")
d543e20b
PA
1490 (message "Creating customization setup...")
1491 (widget-setup)
fadbdfea 1492 (buffer-enable-undo)
d543e20b 1493 (goto-char (point-min))
1833b7b3 1494 (message "Creating customization setup...done"))
d543e20b 1495
944c91b6
PA
1496;;; The Tree Browser.
1497
1498;;;###autoload
4ee1cf9f 1499(defun customize-browse (&optional group)
944c91b6 1500 "Create a tree browser for the customize hierarchy."
cda987f4 1501 (interactive)
4ee1cf9f
PA
1502 (unless group
1503 (setq group 'emacs))
1504 (let ((name "*Customize Browser*"))
40627755 1505 (pop-to-buffer (custom-get-fresh-buffer name)))
4ee1cf9f
PA
1506 (custom-mode)
1507 (widget-insert "\
cda987f4
RS
1508Square brackets show active fields; type RET or click mouse-1
1509on an active field to invoke its action.
df816618 1510Invoke [+] below to expand a group, and [-] to collapse an expanded group.\n")
4ee1cf9f
PA
1511 (if custom-browse-only-groups
1512 (widget-insert "\
c953515e 1513Invoke the [Group] button below to edit that item in another window.\n\n")
d3d4df42
DL
1514 (widget-insert "Invoke the ")
1515 (widget-create 'item
4ee1cf9f
PA
1516 :format "%t"
1517 :tag "[Group]"
1518 :tag-glyph "folder")
1519 (widget-insert ", ")
d3d4df42 1520 (widget-create 'item
4ee1cf9f
PA
1521 :format "%t"
1522 :tag "[Face]"
1523 :tag-glyph "face")
1524 (widget-insert ", and ")
d3d4df42 1525 (widget-create 'item
4ee1cf9f
PA
1526 :format "%t"
1527 :tag "[Option]"
1528 :tag-glyph "option")
1529 (widget-insert " buttons below to edit that
c953515e 1530item in another window.\n\n"))
4ee1cf9f 1531 (let ((custom-buffer-style 'tree))
d3d4df42 1532 (widget-create 'custom-group
4ee1cf9f
PA
1533 :custom-last t
1534 :custom-state 'unknown
1535 :tag (custom-unlispify-tag-name group)
1536 :value group))
f134b461 1537 (widget-setup)
4ee1cf9f 1538 (goto-char (point-min)))
944c91b6 1539
c953515e 1540(define-widget 'custom-browse-visibility 'item
1edec9cf 1541 "Control visibility of items in the customize tree browser."
da5ec617 1542 :format "%[[%t]%]"
c953515e 1543 :action 'custom-browse-visibility-action)
944c91b6 1544
c953515e 1545(defun custom-browse-visibility-action (widget &rest ignore)
944c91b6
PA
1546 (let ((custom-buffer-style 'tree))
1547 (custom-toggle-parent widget)))
1548
c953515e 1549(define-widget 'custom-browse-group-tag 'push-button
944c91b6 1550 "Show parent in other window when activated."
cd6c0940 1551 :tag "Group"
da5ec617 1552 :tag-glyph "folder"
c953515e 1553 :action 'custom-browse-group-tag-action)
944c91b6 1554
c953515e 1555(defun custom-browse-group-tag-action (widget &rest ignore)
944c91b6
PA
1556 (let ((parent (widget-get widget :parent)))
1557 (customize-group-other-window (widget-value parent))))
1558
c953515e 1559(define-widget 'custom-browse-variable-tag 'push-button
944c91b6 1560 "Show parent in other window when activated."
cd6c0940 1561 :tag "Option"
da5ec617 1562 :tag-glyph "option"
c953515e 1563 :action 'custom-browse-variable-tag-action)
944c91b6 1564
c953515e 1565(defun custom-browse-variable-tag-action (widget &rest ignore)
944c91b6
PA
1566 (let ((parent (widget-get widget :parent)))
1567 (customize-variable-other-window (widget-value parent))))
1568
c953515e 1569(define-widget 'custom-browse-face-tag 'push-button
944c91b6 1570 "Show parent in other window when activated."
cd6c0940 1571 :tag "Face"
da5ec617 1572 :tag-glyph "face"
c953515e 1573 :action 'custom-browse-face-tag-action)
944c91b6 1574
c953515e 1575(defun custom-browse-face-tag-action (widget &rest ignore)
944c91b6
PA
1576 (let ((parent (widget-get widget :parent)))
1577 (customize-face-other-window (widget-value parent))))
1578
c953515e 1579(defconst custom-browse-alist '((" " "space")
da5ec617
PA
1580 (" | " "vertical")
1581 ("-\\ " "top")
1582 (" |-" "middle")
1583 (" `-" "bottom")))
1584
c953515e 1585(defun custom-browse-insert-prefix (prefix)
da5ec617 1586 "Insert PREFIX. On XEmacs convert it to line graphics."
d3d4df42 1587 ;; Fixme: do graphics.
da5ec617 1588 (if nil ; (string-match "XEmacs" emacs-version)
d3d4df42 1589 (progn
da5ec617
PA
1590 (insert "*")
1591 (while (not (string-equal prefix ""))
1592 (let ((entry (substring prefix 0 3)))
1593 (setq prefix (substring prefix 3))
1594 (let ((overlay (make-overlay (1- (point)) (point) nil t nil))
c953515e 1595 (name (nth 1 (assoc entry custom-browse-alist))))
da5ec617
PA
1596 (overlay-put overlay 'end-glyph (widget-glyph-find name entry))
1597 (overlay-put overlay 'start-open t)
1598 (overlay-put overlay 'end-open t)))))
1599 (insert prefix)))
1600
d543e20b
PA
1601;;; Modification of Basic Widgets.
1602;;
1603;; We add extra properties to the basic widgets needed here. This is
1604;; fine, as long as we are careful to stay within out own namespace.
1605;;
1606;; We want simple widgets to be displayed by default, but complex
1607;; widgets to be hidden.
1608
1609(widget-put (get 'item 'widget-type) :custom-show t)
1610(widget-put (get 'editable-field 'widget-type)
1611 :custom-show (lambda (widget value)
1612 (let ((pp (pp-to-string value)))
1613 (cond ((string-match "\n" pp)
1614 nil)
1615 ((> (length pp) 40)
1616 nil)
1617 (t t)))))
1618(widget-put (get 'menu-choice 'widget-type) :custom-show t)
1619
1620;;; The `custom-manual' Widget.
1621
1622(define-widget 'custom-manual 'info-link
1623 "Link to the manual entry for this customization option."
1624 :help-echo "Read the manual entry for this option."
1625 :tag "Manual")
1626
1627;;; The `custom-magic' Widget.
1628
6aaedd12
PA
1629(defgroup custom-magic-faces nil
1630 "Faces used by the magic button."
1631 :group 'custom-faces
1632 :group 'custom-buffer)
1633
d543e20b
PA
1634(defface custom-invalid-face '((((class color))
1635 (:foreground "yellow" :background "red"))
1636 (t
b5555381 1637 (:weight bold :slant italic :underline t)))
6aaedd12
PA
1638 "Face used when the customize item is invalid."
1639 :group 'custom-magic-faces)
d543e20b
PA
1640
1641(defface custom-rogue-face '((((class color))
1642 (:foreground "pink" :background "black"))
1643 (t
1644 (:underline t)))
6aaedd12
PA
1645 "Face used when the customize item is not defined for customization."
1646 :group 'custom-magic-faces)
d543e20b 1647
d3d4df42 1648(defface custom-modified-face '((((class color))
d543e20b
PA
1649 (:foreground "white" :background "blue"))
1650 (t
b5555381 1651 (:slant italic :bold)))
6aaedd12
PA
1652 "Face used when the customize item has been modified."
1653 :group 'custom-magic-faces)
d543e20b 1654
d3d4df42 1655(defface custom-set-face '((((class color))
2dfa4c57
RS
1656 (:foreground "blue" :background "white"))
1657 (t
1658 (:slant italic)))
6aaedd12
PA
1659 "Face used when the customize item has been set."
1660 :group 'custom-magic-faces)
d543e20b 1661
d3d4df42 1662(defface custom-changed-face '((((class color))
d543e20b
PA
1663 (:foreground "white" :background "blue"))
1664 (t
b5555381 1665 (:slant italic)))
6aaedd12
PA
1666 "Face used when the customize item has been changed."
1667 :group 'custom-magic-faces)
d543e20b
PA
1668
1669(defface custom-saved-face '((t (:underline t)))
6aaedd12
PA
1670 "Face used when the customize item has been saved."
1671 :group 'custom-magic-faces)
d543e20b 1672
2dfa4c57
RS
1673(defconst custom-magic-alist
1674 '((nil "#" underline "\
167eefc5 1675UNINITIALIZED, you should not see this.")
2dfa4c57 1676 (unknown "?" italic "\
167eefc5 1677UNKNOWN, you should not see this.")
2dfa4c57 1678 (hidden "-" default "\
167eefc5 1679HIDDEN, invoke \"Show\" in the previous line to show." "\
cbc7d892 1680group now hidden, invoke \"Show\", above, to show contents.")
2dfa4c57 1681 (invalid "x" custom-invalid-face "\
167eefc5 1682INVALID, the displayed value cannot be set.")
2dfa4c57 1683 (modified "*" custom-modified-face "\
167eefc5 1684EDITED, shown value does not take effect until you set or save it." "\
1833b7b3 1685something in this group has been edited but not set.")
2dfa4c57 1686 (set "+" custom-set-face "\
167eefc5 1687SET for current session only." "\
1833b7b3 1688something in this group has been set but not saved.")
2dfa4c57 1689 (changed ":" custom-changed-face "\
167eefc5 1690CHANGED outside Customize; operating on it here may be unreliable." "\
25ac13b5 1691something in this group has been changed outside customize.")
2dfa4c57 1692 (saved "!" custom-saved-face "\
167eefc5 1693SAVED and set." "\
5dd0cad0 1694something in this group has been set and saved.")
2dfa4c57 1695 (rogue "@" custom-rogue-face "\
167eefc5 1696NO CUSTOMIZATION DATA; you should not see this." "\
25ac13b5 1697something in this group is not prepared for customization.")
2dfa4c57 1698 (standard " " nil "\
167eefc5 1699STANDARD." "\
c32de15e 1700visible group members are all at standard settings."))
d543e20b 1701 "Alist of customize option states.
d3d4df42 1702Each entry is of the form (STATE MAGIC FACE ITEM-DESC [ GROUP-DESC ]), where
d543e20b
PA
1703
1704STATE is one of the following symbols:
1705
1706`nil'
1707 For internal use, should never occur.
1708`unknown'
1709 For internal use, should never occur.
1710`hidden'
d3d4df42 1711 This item is not being displayed.
d543e20b
PA
1712`invalid'
1713 This item is modified, but has an invalid form.
1714`modified'
1715 This item is modified, and has a valid form.
1716`set'
1717 This item has been set but not saved.
1718`changed'
167eefc5 1719 The current value of this item has been changed outside Customize.
d543e20b
PA
1720`saved'
1721 This item is marked for saving.
1722`rogue'
1723 This item has no customization information.
25ac13b5 1724`standard'
5dd0cad0 1725 This item is unchanged from the standard setting.
d543e20b
PA
1726
1727MAGIC is a string used to present that state.
1728
1729FACE is a face used to present the state.
1730
25ac13b5
PA
1731ITEM-DESC is a string describing the state for options.
1732
1733GROUP-DESC is a string describing the state for groups. If this is
1734left out, ITEM-DESC will be used.
1735
9097aeb7
PA
1736The string %c in either description will be replaced with the
1737category of the item. These are `group'. `option', and `face'.
1738
25ac13b5 1739The list should be sorted most significant first.")
d543e20b
PA
1740
1741(defcustom custom-magic-show 'long
3acab5ef 1742 "If non-nil, show textual description of the state.
b62c92bb 1743If `long', show a full-line description, not just one word."
d543e20b 1744 :type '(choice (const :tag "no" nil)
c992338c
AS
1745 (const long)
1746 (other :tag "short" short))
6aaedd12 1747 :group 'custom-buffer)
d543e20b 1748
9097aeb7 1749(defcustom custom-magic-show-hidden '(option face)
b62c92bb
RS
1750 "Control whether the State button is shown for hidden items.
1751The value should be a list with the custom categories where the State
9097aeb7
PA
1752button should be visible. Possible categories are `group', `option',
1753and `face'."
1754 :type '(set (const group) (const option) (const face))
6aaedd12 1755 :group 'custom-buffer)
3acab5ef 1756
25ac13b5 1757(defcustom custom-magic-show-button nil
b62c92bb 1758 "Show a \"magic\" button indicating the state of each customization option."
d543e20b 1759 :type 'boolean
6aaedd12 1760 :group 'custom-buffer)
d543e20b
PA
1761
1762(define-widget 'custom-magic 'default
1763 "Show and manipulate state for a customization option."
1764 :format "%v"
86bd10bc 1765 :action 'widget-parent-action
6d528fc5 1766 :notify 'ignore
d543e20b
PA
1767 :value-get 'ignore
1768 :value-create 'custom-magic-value-create
1769 :value-delete 'widget-children-value-delete)
1770
86bd10bc
PA
1771(defun widget-magic-mouse-down-action (widget &optional event)
1772 ;; Non-nil unless hidden.
d3d4df42 1773 (not (eq (widget-get (widget-get (widget-get widget :parent) :parent)
86bd10bc
PA
1774 :custom-state)
1775 'hidden)))
1776
d543e20b 1777(defun custom-magic-value-create (widget)
2365594b 1778 "Create compact status report for WIDGET."
d543e20b
PA
1779 (let* ((parent (widget-get widget :parent))
1780 (state (widget-get parent :custom-state))
3acab5ef 1781 (hidden (eq state 'hidden))
25ac13b5 1782 (entry (assq state custom-magic-alist))
d543e20b
PA
1783 (magic (nth 1 entry))
1784 (face (nth 2 entry))
9097aeb7
PA
1785 (category (widget-get parent :custom-category))
1786 (text (or (and (eq category 'group)
25ac13b5
PA
1787 (nth 4 entry))
1788 (nth 3 entry)))
f985c5f7 1789 (form (widget-get parent :custom-form))
d543e20b 1790 children)
9097aeb7 1791 (while (string-match "\\`\\(.*\\)%c\\(.*\\)\\'" text)
d3d4df42 1792 (setq text (concat (match-string 1 text)
9097aeb7
PA
1793 (symbol-name category)
1794 (match-string 2 text))))
3acab5ef 1795 (when (and custom-magic-show
9097aeb7
PA
1796 (or (not hidden)
1797 (memq category custom-magic-show-hidden)))
25ac13b5 1798 (insert " ")
26c7b3ef
RS
1799 (when (and (eq category 'group)
1800 (not (and (eq custom-buffer-style 'links)
1801 (> (widget-get parent :custom-level) 1))))
944c91b6
PA
1802 (insert-char ?\ (* custom-buffer-indent
1803 (widget-get parent :custom-level))))
d3d4df42
DL
1804 (push (widget-create-child-and-convert
1805 widget 'choice-item
d5c42d02 1806 :help-echo "Change the state of this item."
3acab5ef 1807 :format (if hidden "%t" "%[%t%]")
25ac13b5
PA
1808 :button-prefix 'widget-push-button-prefix
1809 :button-suffix 'widget-push-button-suffix
86bd10bc
PA
1810 :mouse-down-action 'widget-magic-mouse-down-action
1811 :tag "State")
d543e20b
PA
1812 children)
1813 (insert ": ")
b62c92bb
RS
1814 (let ((start (point)))
1815 (if (eq custom-magic-show 'long)
1816 (insert text)
1817 (insert (symbol-name state)))
f985c5f7
PA
1818 (cond ((eq form 'lisp)
1819 (insert " (lisp)"))
1820 ((eq form 'mismatch)
1821 (insert " (mismatch)")))
b62c92bb 1822 (put-text-property start (point) 'face 'custom-state-face))
d543e20b 1823 (insert "\n"))
26c7b3ef
RS
1824 (when (and (eq category 'group)
1825 (not (and (eq custom-buffer-style 'links)
1826 (> (widget-get parent :custom-level) 1))))
944c91b6
PA
1827 (insert-char ?\ (* custom-buffer-indent
1828 (widget-get parent :custom-level))))
d543e20b
PA
1829 (when custom-magic-show-button
1830 (when custom-magic-show
1831 (let ((indent (widget-get parent :indent)))
1832 (when indent
1833 (insert-char ? indent))))
d3d4df42
DL
1834 (push (widget-create-child-and-convert
1835 widget 'choice-item
86bd10bc
PA
1836 :mouse-down-action 'widget-magic-mouse-down-action
1837 :button-face face
3acab5ef
PA
1838 :button-prefix ""
1839 :button-suffix ""
86bd10bc 1840 :help-echo "Change the state."
3acab5ef 1841 :format (if hidden "%t" "%[%t%]")
f985c5f7 1842 :tag (if (memq form '(lisp mismatch))
86bd10bc
PA
1843 (concat "(" magic ")")
1844 (concat "[" magic "]")))
d543e20b
PA
1845 children)
1846 (insert " "))
1847 (widget-put widget :children children)))
1848
1849(defun custom-magic-reset (widget)
1850 "Redraw the :custom-magic property of WIDGET."
1851 (let ((magic (widget-get widget :custom-magic)))
1852 (widget-value-set magic (widget-value magic))))
1853
d543e20b
PA
1854;;; The `custom' Widget.
1855
d3d4df42 1856(defface custom-button-face
3060662f 1857 '((((type x w32 mac) (class color)) ; Like default modeline
1a578e9b
AC
1858 (:box (:line-width 2 :style released-button)
1859 :background "lightgrey" :foreground "black"))
d3d4df42
DL
1860 (t
1861 nil))
b62c92bb 1862 "Face used for buttons in customization buffers."
d3d4df42
DL
1863 :version "21.1"
1864 :group 'custom-faces)
1865
1866(defface custom-button-pressed-face
3060662f 1867 '((((type x w32 mac) (class color))
1a578e9b
AC
1868 (:box (:line-width 2 :style pressed-button)
1869 :background "lightgrey" :foreground "black"))
d3d4df42
DL
1870 (t
1871 (:inverse-video t)))
1872 "Face used for buttons in customization buffers."
1873 :version "21.1"
b62c92bb
RS
1874 :group 'custom-faces)
1875
1876(defface custom-documentation-face nil
1877 "Face used for documentation strings in customization buffers."
1878 :group 'custom-faces)
1879
1880(defface custom-state-face '((((class color)
1881 (background dark))
1882 (:foreground "lime green"))
1883 (((class color)
1884 (background light))
1885 (:foreground "dark green"))
1886 (t nil))
1887 "Face used for State descriptions in the customize buffer."
1888 :group 'custom-faces)
1889
d543e20b
PA
1890(define-widget 'custom 'default
1891 "Customize a user option."
944c91b6 1892 :format "%v"
d543e20b 1893 :convert-widget 'custom-convert-widget
d543e20b 1894 :notify 'custom-notify
944c91b6 1895 :custom-prefix ""
d543e20b
PA
1896 :custom-level 1
1897 :custom-state 'hidden
1898 :documentation-property 'widget-subclass-responsibility
1899 :value-create 'widget-subclass-responsibility
1900 :value-delete 'widget-children-value-delete
86bd10bc
PA
1901 :value-get 'widget-value-value-get
1902 :validate 'widget-children-validate
d543e20b
PA
1903 :match (lambda (widget value) (symbolp value)))
1904
1905(defun custom-convert-widget (widget)
2365594b 1906 "Initialize :value and :tag from :args in WIDGET."
d543e20b 1907 (let ((args (widget-get widget :args)))
d3d4df42 1908 (when args
d543e20b
PA
1909 (widget-put widget :value (widget-apply widget
1910 :value-to-internal (car args)))
1911 (widget-put widget :tag (custom-unlispify-tag-name (car args)))
1912 (widget-put widget :args nil)))
1913 widget)
1914
d543e20b
PA
1915(defun custom-notify (widget &rest args)
1916 "Keep track of changes."
0a3a0b56
PA
1917 (let ((state (widget-get widget :custom-state)))
1918 (unless (eq state 'modified)
1919 (unless (memq state '(nil unknown hidden))
1920 (widget-put widget :custom-state 'modified))
1921 (custom-magic-reset widget)
1922 (apply 'widget-default-notify widget args))))
d543e20b
PA
1923
1924(defun custom-redraw (widget)
1925 "Redraw WIDGET with current settings."
6d528fc5
PA
1926 (let ((line (count-lines (point-min) (point)))
1927 (column (current-column))
1928 (pos (point))
d543e20b
PA
1929 (from (marker-position (widget-get widget :from)))
1930 (to (marker-position (widget-get widget :to))))
1931 (save-excursion
1932 (widget-value-set widget (widget-value widget))
1933 (custom-redraw-magic widget))
1934 (when (and (>= pos from) (<= pos to))
6d528fc5 1935 (condition-case nil
d3d4df42 1936 (progn
86bd10bc
PA
1937 (if (> column 0)
1938 (goto-line line)
1939 (goto-line (1+ line)))
6d528fc5
PA
1940 (move-to-column column))
1941 (error nil)))))
d543e20b
PA
1942
1943(defun custom-redraw-magic (widget)
1944 "Redraw WIDGET state with current settings."
d3d4df42 1945 (while widget
d543e20b 1946 (let ((magic (widget-get widget :custom-magic)))
d3d4df42 1947 (cond (magic
944c91b6
PA
1948 (widget-value-set magic (widget-value magic))
1949 (when (setq widget (widget-get widget :group))
1950 (custom-group-state-update widget)))
1951 (t
1952 (setq widget nil)))))
d543e20b
PA
1953 (widget-setup))
1954
1955(defun custom-show (widget value)
1956 "Non-nil if WIDGET should be shown with VALUE by default."
1957 (let ((show (widget-get widget :custom-show)))
1958 (cond ((null show)
1959 nil)
1960 ((eq t show)
1961 t)
1962 (t
1963 (funcall show widget value)))))
1964
d543e20b
PA
1965(defun custom-load-widget (widget)
1966 "Load all dependencies for WIDGET."
1967 (custom-load-symbol (widget-value widget)))
1968
c953515e 1969(defun custom-unloaded-symbol-p (symbol)
3127aa13 1970 "Return non-nil if the dependencies of SYMBOL have not yet been loaded."
c953515e
PA
1971 (let ((found nil)
1972 (loads (get symbol 'custom-loads))
1973 load)
1974 (while loads
1975 (setq load (car loads)
1976 loads (cdr loads))
1977 (cond ((symbolp load)
1978 (unless (featurep load)
1979 (setq found t)))
1980 ((assoc load load-history))
1981 ((assoc (locate-library load) load-history)
1982 (message nil))
1983 (t
1984 (setq found t))))
1985 found))
1986
1987(defun custom-unloaded-widget-p (widget)
3127aa13 1988 "Return non-nil if the dependencies of WIDGET have not yet been loaded."
c953515e
PA
1989 (custom-unloaded-symbol-p (widget-value widget)))
1990
6d528fc5
PA
1991(defun custom-toggle-hide (widget)
1992 "Toggle visibility of WIDGET."
c953515e 1993 (custom-load-widget widget)
6d528fc5
PA
1994 (let ((state (widget-get widget :custom-state)))
1995 (cond ((memq state '(invalid modified))
1996 (error "There are unset changes"))
1997 ((eq state 'hidden)
1998 (widget-put widget :custom-state 'unknown))
d3d4df42 1999 (t
3acab5ef 2000 (widget-put widget :documentation-shown nil)
6d528fc5 2001 (widget-put widget :custom-state 'hidden)))
8697863a
PA
2002 (custom-redraw widget)
2003 (widget-setup)))
6d528fc5 2004
3acab5ef 2005(defun custom-toggle-parent (widget &rest ignore)
b62c92bb 2006 "Toggle visibility of parent of WIDGET."
3acab5ef
PA
2007 (custom-toggle-hide (widget-get widget :parent)))
2008
944c91b6
PA
2009(defun custom-add-see-also (widget &optional prefix)
2010 "Add `See also ...' to WIDGET if there are any links.
2011Insert PREFIX first if non-nil."
2012 (let* ((symbol (widget-get widget :value))
2013 (links (get symbol 'custom-links))
2014 (many (> (length links) 2))
2015 (buttons (widget-get widget :buttons))
2016 (indent (widget-get widget :indent)))
2017 (when links
2018 (when indent
2019 (insert-char ?\ indent))
2020 (when prefix
2021 (insert prefix))
2022 (insert "See also ")
2023 (while links
2024 (push (widget-create-child-and-convert widget (car links))
2025 buttons)
2026 (setq links (cdr links))
2027 (cond ((null links)
2028 (insert ".\n"))
2029 ((null (cdr links))
2030 (if many
2031 (insert ", and ")
2032 (insert " and ")))
d3d4df42 2033 (t
944c91b6
PA
2034 (insert ", "))))
2035 (widget-put widget :buttons buttons))))
2036
cd6c0940
RS
2037(defun custom-add-parent-links (widget &optional initial-string)
2038 "Add \"Parent groups: ...\" to WIDGET if the group has parents.
2039The value if non-nil if any parents were found.
2040If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
944c91b6
PA
2041 (let ((name (widget-value widget))
2042 (type (widget-type widget))
2043 (buttons (widget-get widget :buttons))
d377bee9 2044 (start (point))
ac00d71c 2045 (parents nil))
cd6c0940 2046 (insert (or initial-string "Parent groups:"))
944c91b6 2047 (mapatoms (lambda (symbol)
da5ec617
PA
2048 (let ((entry (assq name (get symbol 'custom-group))))
2049 (when (eq (nth 1 entry) type)
2050 (insert " ")
d3d4df42
DL
2051 (push (widget-create-child-and-convert
2052 widget 'custom-group-link
da5ec617
PA
2053 :tag (custom-unlispify-tag-name symbol)
2054 symbol)
2055 buttons)
ac00d71c 2056 (setq parents (cons symbol parents))))))
8e4d1662 2057 (and (null (get name 'custom-links)) ;No links of its own.
ac00d71c 2058 (= (length parents) 1) ;A single parent.
cfa921fd
PA
2059 (let* ((links (get (car parents) 'custom-links))
2060 (many (> (length links) 2)))
ac00d71c
PA
2061 (when links
2062 (insert "\nParent documentation: ")
2063 (while links
2064 (push (widget-create-child-and-convert widget (car links))
2065 buttons)
2066 (setq links (cdr links))
2067 (cond ((null links)
2068 (insert ".\n"))
2069 ((null (cdr links))
2070 (if many
2071 (insert ", and ")
2072 (insert " and ")))
2073 (t
2074 (insert ", ")))))))
2075 (if parents
2076 (insert "\n")
d377bee9 2077 (delete-region start (point)))
ac00d71c
PA
2078 (widget-put widget :buttons buttons)
2079 parents))
944c91b6 2080
d3d4df42
DL
2081;;; The `custom-comment' Widget.
2082
2083;; like the editable field
2084(defface custom-comment-face '((((class grayscale color)
2085 (background light))
2086 (:background "gray85"))
2087 (((class grayscale color)
2088 (background dark))
2089 (:background "dim gray"))
2090 (t
b5555381 2091 (:slant italic)))
d3d4df42
DL
2092 "Face used for comments on variables or faces"
2093 :version "21.1"
2094 :group 'custom-faces)
2095
2096;; like font-lock-comment-face
2097(defface custom-comment-tag-face
2098 '((((class color) (background dark)) (:foreground "gray80"))
2099 (((class color) (background light)) (:foreground "blue4"))
2100 (((class grayscale) (background light))
b5555381 2101 (:foreground "DimGray" :weight bold :slant italic))
d3d4df42 2102 (((class grayscale) (background dark))
b5555381
RS
2103 (:foreground "LightGray" :weight bold :slant italic))
2104 (t (:weight bold)))
d3d4df42
DL
2105 "Face used for variables or faces comment tags"
2106 :group 'custom-faces)
2107
2108(define-widget 'custom-comment 'string
164cfaeb 2109 "User comment."
d3d4df42 2110 :tag "Comment"
164cfaeb 2111 :help-echo "Edit a comment here."
d3d4df42
DL
2112 :sample-face 'custom-comment-tag-face
2113 :value-face 'custom-comment-face
164cfaeb
DL
2114 :shown nil
2115 :create 'custom-comment-create)
d3d4df42
DL
2116
2117(defun custom-comment-create (widget)
164cfaeb 2118 (let* ((null-comment (equal "" (widget-value widget))))
6171a945
DL
2119 (if (or (widget-get (widget-get widget :parent) :comment-shown)
2120 (not null-comment))
2121 (widget-default-create widget)
2122 ;; `widget-default-delete' expects markers in these slots --
2123 ;; maybe it shouldn't.
2124 (widget-put widget :from (point-marker))
2125 (widget-put widget :to (point-marker)))))
164cfaeb
DL
2126
2127(defun custom-comment-hide (widget)
2128 (widget-put (widget-get widget :parent) :comment-shown nil))
d3d4df42
DL
2129
2130;; Those functions are for the menu. WIDGET is NOT the comment widget. It's
2131;; the global custom one
2132(defun custom-comment-show (widget)
164cfaeb
DL
2133 (widget-put widget :comment-shown t)
2134 (custom-redraw widget)
2135 (widget-setup))
d3d4df42
DL
2136
2137(defun custom-comment-invisible-p (widget)
164cfaeb
DL
2138 (let ((val (widget-value (widget-get widget :comment-widget))))
2139 (and (equal "" val)
2140 (not (widget-get widget :comment-shown)))))
d3d4df42 2141
d543e20b
PA
2142;;; The `custom-variable' Widget.
2143
2365594b
DL
2144;; When this was underlined blue, users confused it with a
2145;; Mosaic-style hyperlink...
16b20ed9
GM
2146(defface custom-variable-tag-face
2147 `((((class color)
2148 (background dark))
b5555381 2149 (:foreground "light blue" :weight bold :height 1.2 :inherit variable-pitch))
16b20ed9
GM
2150 (((class color)
2151 (background light))
b5555381
RS
2152 (:foreground "blue" :weight bold :height 1.2 :inherit variable-pitch))
2153 (t (:weight bold)))
d543e20b 2154 "Face used for unpushable variable tags."
bd042c03 2155 :group 'custom-faces)
d543e20b 2156
b5555381 2157(defface custom-variable-button-face '((t (:underline t :weight bold)))
d543e20b 2158 "Face used for pushable variable tags."
bd042c03 2159 :group 'custom-faces)
d543e20b 2160
d64478da
KH
2161(defcustom custom-variable-default-form 'edit
2162 "Default form of displaying variable values."
2163 :type '(choice (const edit)
2164 (const lisp))
cd32a7ba
DN
2165 :group 'custom-buffer
2166 :version "20.3")
d64478da 2167
a075569a
LT
2168(defun custom-variable-documentation (variable)
2169 "Return documentation of VARIABLE for use in Custom buffer.
2170Normally just return the docstring. But if VARIABLE automatically
2171becomes buffer local when set, append a message to that effect."
2172 (if (and (local-variable-if-set-p variable)
2173 (or (not (local-variable-p variable))
2174 (with-temp-buffer
2175 (local-variable-if-set-p variable))))
2176 (concat (documentation-property variable 'variable-documentation)
2177 "\n
2178This variable automatically becomes buffer-local when set outside Custom.
2179However, setting it through Custom sets the default value.")
2180 (documentation-property variable 'variable-documentation)))
2181
d543e20b
PA
2182(define-widget 'custom-variable 'custom
2183 "Customize variable."
944c91b6 2184 :format "%v"
d543e20b 2185 :help-echo "Set or reset this variable."
a075569a 2186 :documentation-property #'custom-variable-documentation
9097aeb7 2187 :custom-category 'option
d543e20b
PA
2188 :custom-state nil
2189 :custom-menu 'custom-variable-menu-create
d64478da 2190 :custom-form nil ; defaults to value of `custom-variable-default-form'
d543e20b
PA
2191 :value-create 'custom-variable-value-create
2192 :action 'custom-variable-action
2193 :custom-set 'custom-variable-set
2194 :custom-save 'custom-variable-save
2195 :custom-reset-current 'custom-redraw
2196 :custom-reset-saved 'custom-variable-reset-saved
4f985043
RS
2197 :custom-reset-standard 'custom-variable-reset-standard
2198 :custom-standard-value 'custom-variable-standard-value)
d543e20b 2199
bd042c03
PA
2200(defun custom-variable-type (symbol)
2201 "Return a widget suitable for editing the value of SYMBOL.
d3d4df42 2202If SYMBOL has a `custom-type' property, use that.
bd042c03
PA
2203Otherwise, look up symbol in `custom-guess-type-alist'."
2204 (let* ((type (or (get symbol 'custom-type)
25ac13b5 2205 (and (not (get symbol 'standard-value))
bd042c03
PA
2206 (custom-guess-type symbol))
2207 'sexp))
2208 (options (get symbol 'custom-options))
2209 (tmp (if (listp type)
46fa5a83 2210 (copy-sequence type)
bd042c03
PA
2211 (list type))))
2212 (when options
2213 (widget-put tmp :options options))
2214 tmp))
2215
d543e20b 2216(defun custom-variable-value-create (widget)
164cfaeb 2217 "Here is where you edit the variable's value."
d543e20b 2218 (custom-load-widget widget)
d64478da
KH
2219 (unless (widget-get widget :custom-form)
2220 (widget-put widget :custom-form custom-variable-default-form))
d543e20b
PA
2221 (let* ((buttons (widget-get widget :buttons))
2222 (children (widget-get widget :children))
2223 (form (widget-get widget :custom-form))
2224 (state (widget-get widget :custom-state))
2225 (symbol (widget-get widget :value))
d543e20b 2226 (tag (widget-get widget :tag))
bd042c03 2227 (type (custom-variable-type symbol))
d543e20b 2228 (conv (widget-convert type))
6d528fc5 2229 (get (or (get symbol 'custom-get) 'default-value))
944c91b6
PA
2230 (prefix (widget-get widget :custom-prefix))
2231 (last (widget-get widget :custom-last))
d543e20b 2232 (value (if (default-boundp symbol)
6d528fc5 2233 (funcall get symbol)
d543e20b 2234 (widget-get conv :value))))
164cfaeb 2235 ;; If the widget is new, the child determines whether it is hidden.
d543e20b
PA
2236 (cond (state)
2237 ((custom-show type value)
2238 (setq state 'unknown))
2239 (t
2240 (setq state 'hidden)))
2241 ;; If we don't know the state, see if we need to edit it in lisp form.
2242 (when (eq state 'unknown)
2243 (unless (widget-apply conv :match value)
2244 ;; (widget-apply (widget-convert type) :match value)
f985c5f7 2245 (setq form 'mismatch)))
d543e20b 2246 ;; Now we can create the child widget.
944c91b6 2247 (cond ((eq custom-buffer-style 'tree)
da5ec617 2248 (insert prefix (if last " `--- " " |--- "))
944c91b6 2249 (push (widget-create-child-and-convert
c953515e 2250 widget 'custom-browse-variable-tag)
944c91b6
PA
2251 buttons)
2252 (insert " " tag "\n")
2253 (widget-put widget :buttons buttons))
2254 ((eq state 'hidden)
d543e20b 2255 ;; Indicate hidden value.
d3d4df42 2256 (push (widget-create-child-and-convert
d543e20b 2257 widget 'item
3acab5ef 2258 :format "%{%t%}: "
b62c92bb 2259 :sample-face 'custom-variable-tag-face
d543e20b
PA
2260 :tag tag
2261 :parent widget)
3acab5ef 2262 buttons)
d3d4df42 2263 (push (widget-create-child-and-convert
3acab5ef 2264 widget 'visibility
8697863a 2265 :help-echo "Show the value of this option."
7f663295 2266 :off "Show Value"
3acab5ef
PA
2267 :action 'custom-toggle-parent
2268 nil)
2269 buttons))
f985c5f7 2270 ((memq form '(lisp mismatch))
d543e20b
PA
2271 ;; In lisp mode edit the saved value when possible.
2272 (let* ((value (cond ((get symbol 'saved-value)
2273 (car (get symbol 'saved-value)))
25ac13b5
PA
2274 ((get symbol 'standard-value)
2275 (car (get symbol 'standard-value)))
d543e20b 2276 ((default-boundp symbol)
6d528fc5 2277 (custom-quote (funcall get symbol)))
d543e20b
PA
2278 (t
2279 (custom-quote (widget-get conv :value))))))
3acab5ef 2280 (insert (symbol-name symbol) ": ")
d3d4df42 2281 (push (widget-create-child-and-convert
944c91b6
PA
2282 widget 'visibility
2283 :help-echo "Hide the value of this option."
7f663295
RS
2284 :on "Hide Value"
2285 :off "Show Value"
944c91b6
PA
2286 :action 'custom-toggle-parent
2287 t)
2288 buttons)
3acab5ef 2289 (insert " ")
d3d4df42
DL
2290 (push (widget-create-child-and-convert
2291 widget 'sexp
d543e20b 2292 :button-face 'custom-variable-button-face
3acab5ef 2293 :format "%v"
d543e20b
PA
2294 :tag (symbol-name symbol)
2295 :parent widget
2296 :value value)
2297 children)))
2298 (t
2299 ;; Edit mode.
3acab5ef
PA
2300 (let* ((format (widget-get type :format))
2301 tag-format value-format)
2302 (unless (string-match ":" format)
896a6a5d 2303 (error "Bad format"))
3acab5ef
PA
2304 (setq tag-format (substring format 0 (match-end 0)))
2305 (setq value-format (substring format (match-end 0)))
2306 (push (widget-create-child-and-convert
d3d4df42 2307 widget 'item
3acab5ef
PA
2308 :format tag-format
2309 :action 'custom-tag-action
8697863a 2310 :help-echo "Change value of this option."
3acab5ef
PA
2311 :mouse-down-action 'custom-tag-mouse-down-action
2312 :button-face 'custom-variable-button-face
b62c92bb 2313 :sample-face 'custom-variable-tag-face
3acab5ef
PA
2314 tag)
2315 buttons)
2316 (insert " ")
d3d4df42 2317 (push (widget-create-child-and-convert
164cfaeb
DL
2318 widget 'visibility
2319 :help-echo "Hide the value of this option."
7f663295
RS
2320 :on "Hide Value"
2321 :off "Show Value"
164cfaeb
DL
2322 :action 'custom-toggle-parent
2323 t)
2324 buttons)
3acab5ef 2325 (push (widget-create-child-and-convert
d3d4df42 2326 widget type
3acab5ef
PA
2327 :format value-format
2328 :value value)
2329 children))))
944c91b6 2330 (unless (eq custom-buffer-style 'tree)
944c91b6
PA
2331 (unless (eq (preceding-char) ?\n)
2332 (widget-insert "\n"))
944c91b6
PA
2333 ;; Create the magic button.
2334 (let ((magic (widget-create-child-and-convert
2335 widget 'custom-magic nil)))
2336 (widget-put widget :custom-magic magic)
2337 (push magic buttons))
164cfaeb 2338 ;; ### NOTE: this is ugly!!!! I need to update the :buttons property
d3d4df42
DL
2339 ;; before the call to `widget-default-format-handler'. Otherwise, I
2340 ;; loose my current `buttons'. This function shouldn't be called like
2341 ;; this anyway. The doc string widget should be added like the others.
2342 ;; --dv
944c91b6 2343 (widget-put widget :buttons buttons)
7f663295 2344 (insert "\n")
944c91b6
PA
2345 ;; Insert documentation.
2346 (widget-default-format-handler widget ?h)
d3d4df42
DL
2347
2348 ;; The comment field
2349 (unless (eq state 'hidden)
2350 (let* ((comment (get symbol 'variable-comment))
2351 (comment-widget
2352 (widget-create-child-and-convert
2353 widget 'custom-comment
2354 :parent widget
2355 :value (or comment ""))))
2356 (widget-put widget :comment-widget comment-widget)
2357 ;; Don't push it !!! Custom assumes that the first child is the
2358 ;; value one.
2359 (setq children (append children (list comment-widget)))))
2360 ;; Update the rest of the properties properties.
2361 (widget-put widget :custom-form form)
2362 (widget-put widget :children children)
2363 ;; Now update the state.
2364 (if (eq state 'hidden)
2365 (widget-put widget :custom-state state)
2366 (custom-variable-state-set widget))
944c91b6
PA
2367 ;; See also.
2368 (unless (eq state 'hidden)
2369 (when (eq (widget-get widget :custom-level) 1)
2370 (custom-add-parent-links widget))
2371 (custom-add-see-also widget)))))
d543e20b 2372
3acab5ef
PA
2373(defun custom-tag-action (widget &rest args)
2374 "Pass :action to first child of WIDGET's parent."
2375 (apply 'widget-apply (car (widget-get (widget-get widget :parent) :children))
2376 :action args))
2377
2378(defun custom-tag-mouse-down-action (widget &rest args)
2379 "Pass :mouse-down-action to first child of WIDGET's parent."
2380 (apply 'widget-apply (car (widget-get (widget-get widget :parent) :children))
2381 :mouse-down-action args))
2382
d543e20b
PA
2383(defun custom-variable-state-set (widget)
2384 "Set the state of WIDGET."
2385 (let* ((symbol (widget-value widget))
6d528fc5 2386 (get (or (get symbol 'custom-get) 'default-value))
d543e20b 2387 (value (if (default-boundp symbol)
6d528fc5 2388 (funcall get symbol)
d543e20b 2389 (widget-get widget :value)))
d3d4df42 2390 (comment (get symbol 'variable-comment))
d543e20b 2391 tmp
d3d4df42
DL
2392 temp
2393 (state (cond ((progn (setq tmp (get symbol 'customized-value))
2394 (setq temp
2395 (get symbol 'customized-variable-comment))
2396 (or tmp temp))
d543e20b 2397 (if (condition-case nil
d3d4df42
DL
2398 (and (equal value (eval (car tmp)))
2399 (equal comment temp))
d543e20b
PA
2400 (error nil))
2401 'set
2402 'changed))
d3d4df42
DL
2403 ((progn (setq tmp (get symbol 'saved-value))
2404 (setq temp (get symbol 'saved-variable-comment))
2405 (or tmp temp))
d543e20b 2406 (if (condition-case nil
d3d4df42
DL
2407 (and (equal value (eval (car tmp)))
2408 (equal comment temp))
d543e20b
PA
2409 (error nil))
2410 'saved
2411 'changed))
25ac13b5 2412 ((setq tmp (get symbol 'standard-value))
d543e20b 2413 (if (condition-case nil
d3d4df42
DL
2414 (and (equal value (eval (car tmp)))
2415 (equal comment nil))
d543e20b 2416 (error nil))
25ac13b5 2417 'standard
d543e20b
PA
2418 'changed))
2419 (t 'rogue))))
2420 (widget-put widget :custom-state state)))
2421
4f985043
RS
2422(defun custom-variable-standard-value (widget)
2423 (get (widget-value widget) 'standard-value))
2424
d3d4df42 2425(defvar custom-variable-menu
0f3335c0 2426 '(("Set for Current Session" custom-variable-set
6d528fc5
PA
2427 (lambda (widget)
2428 (eq (widget-get widget :custom-state) 'modified)))
0f3335c0 2429 ("Save for Future Sessions" custom-variable-save
6d528fc5
PA
2430 (lambda (widget)
2431 (memq (widget-get widget :custom-state) '(modified set changed rogue))))
2432 ("Reset to Current" custom-redraw
2433 (lambda (widget)
2434 (and (default-boundp (widget-value widget))
86bd10bc 2435 (memq (widget-get widget :custom-state) '(modified changed)))))
6d528fc5
PA
2436 ("Reset to Saved" custom-variable-reset-saved
2437 (lambda (widget)
d3d4df42
DL
2438 (and (or (get (widget-value widget) 'saved-value)
2439 (get (widget-value widget) 'saved-variable-comment))
6d528fc5
PA
2440 (memq (widget-get widget :custom-state)
2441 '(modified set changed rogue)))))
19d63704 2442 ("Erase Customization" custom-variable-reset-standard
6d528fc5 2443 (lambda (widget)
25ac13b5 2444 (and (get (widget-value widget) 'standard-value)
6d528fc5 2445 (memq (widget-get widget :custom-state)
8697863a 2446 '(modified set changed saved rogue)))))
bde4f354
RS
2447 ("Use Backup Value" custom-variable-reset-backup
2448 (lambda (widget)
2449 (get (widget-value widget) 'backup-value)))
8697863a 2450 ("---" ignore ignore)
d3d4df42
DL
2451 ("Add Comment" custom-comment-show custom-comment-invisible-p)
2452 ("---" ignore ignore)
2453 ("Don't show as Lisp expression" custom-variable-edit
8697863a 2454 (lambda (widget)
f985c5f7 2455 (eq (widget-get widget :custom-form) 'lisp)))
0db1ff23 2456 ("Show initial Lisp expression" custom-variable-edit-lisp
8697863a 2457 (lambda (widget)
f985c5f7 2458 (eq (widget-get widget :custom-form) 'edit))))
d543e20b 2459 "Alist of actions for the `custom-variable' widget.
6d528fc5
PA
2460Each entry has the form (NAME ACTION FILTER) where NAME is the name of
2461the menu entry, ACTION is the function to call on the widget when the
2462menu is selected, and FILTER is a predicate which takes a `custom-variable'
2463widget as an argument, and returns non-nil if ACTION is valid on that
19d63704 2464widget. If FILTER is nil, ACTION is always valid.")
d543e20b
PA
2465
2466(defun custom-variable-action (widget &optional event)
2467 "Show the menu for `custom-variable' WIDGET.
2468Optional EVENT is the location for the menu."
2469 (if (eq (widget-get widget :custom-state) 'hidden)
6d528fc5 2470 (custom-toggle-hide widget)
86bd10bc
PA
2471 (unless (eq (widget-get widget :custom-state) 'modified)
2472 (custom-variable-state-set widget))
2473 (custom-redraw-magic widget)
d543e20b 2474 (let* ((completion-ignore-case t)
25ac13b5
PA
2475 (answer (widget-choose (concat "Operation on "
2476 (custom-unlispify-tag-name
2477 (widget-get widget :value)))
6d528fc5
PA
2478 (custom-menu-filter custom-variable-menu
2479 widget)
d543e20b
PA
2480 event)))
2481 (if answer
2482 (funcall answer widget)))))
2483
2484(defun custom-variable-edit (widget)
2485 "Edit value of WIDGET."
2486 (widget-put widget :custom-state 'unknown)
2487 (widget-put widget :custom-form 'edit)
2488 (custom-redraw widget))
2489
2490(defun custom-variable-edit-lisp (widget)
2365594b 2491 "Edit the Lisp representation of the value of WIDGET."
d543e20b
PA
2492 (widget-put widget :custom-state 'unknown)
2493 (widget-put widget :custom-form 'lisp)
2494 (custom-redraw widget))
2495
2496(defun custom-variable-set (widget)
2497 "Set the current value for the variable being edited by WIDGET."
6d528fc5
PA
2498 (let* ((form (widget-get widget :custom-form))
2499 (state (widget-get widget :custom-state))
2500 (child (car (widget-get widget :children)))
2501 (symbol (widget-value widget))
2502 (set (or (get symbol 'custom-set) 'set-default))
d3d4df42
DL
2503 (comment-widget (widget-get widget :comment-widget))
2504 (comment (widget-value comment-widget))
2505 val)
d543e20b 2506 (cond ((eq state 'hidden)
896a6a5d 2507 (error "Cannot set hidden variable"))
d543e20b
PA
2508 ((setq val (widget-apply child :validate))
2509 (goto-char (widget-get val :from))
2510 (error "%s" (widget-get val :error)))
f985c5f7 2511 ((memq form '(lisp mismatch))
d3d4df42
DL
2512 (when (equal comment "")
2513 (setq comment nil)
2514 ;; Make the comment invisible by hand if it's empty
164cfaeb 2515 (custom-comment-hide comment-widget))
bde4f354 2516 (custom-variable-backup-value widget)
6d528fc5 2517 (funcall set symbol (eval (setq val (widget-value child))))
d3d4df42
DL
2518 (put symbol 'customized-value (list val))
2519 (put symbol 'variable-comment comment)
2520 (put symbol 'customized-variable-comment comment))
d543e20b 2521 (t
d3d4df42
DL
2522 (when (equal comment "")
2523 (setq comment nil)
2524 ;; Make the comment invisible by hand if it's empty
164cfaeb 2525 (custom-comment-hide comment-widget))
bde4f354 2526 (custom-variable-backup-value widget)
6d528fc5 2527 (funcall set symbol (setq val (widget-value child)))
d3d4df42
DL
2528 (put symbol 'customized-value (list (custom-quote val)))
2529 (put symbol 'variable-comment comment)
2530 (put symbol 'customized-variable-comment comment)))
d543e20b
PA
2531 (custom-variable-state-set widget)
2532 (custom-redraw-magic widget)))
2533
2534(defun custom-variable-save (widget)
0db1ff23 2535 "Set and save the value for the variable being edited by WIDGET."
6d528fc5
PA
2536 (let* ((form (widget-get widget :custom-form))
2537 (state (widget-get widget :custom-state))
2538 (child (car (widget-get widget :children)))
2539 (symbol (widget-value widget))
2540 (set (or (get symbol 'custom-set) 'set-default))
d3d4df42
DL
2541 (comment-widget (widget-get widget :comment-widget))
2542 (comment (widget-value comment-widget))
6d528fc5 2543 val)
d543e20b 2544 (cond ((eq state 'hidden)
896a6a5d 2545 (error "Cannot set hidden variable"))
d543e20b
PA
2546 ((setq val (widget-apply child :validate))
2547 (goto-char (widget-get val :from))
6b292312 2548 (error "Saving %s: %s" symbol (widget-get val :error)))
f985c5f7 2549 ((memq form '(lisp mismatch))
d3d4df42
DL
2550 (when (equal comment "")
2551 (setq comment nil)
2552 ;; Make the comment invisible by hand if it's empty
164cfaeb 2553 (custom-comment-hide comment-widget))
d543e20b 2554 (put symbol 'saved-value (list (widget-value child)))
c942535f
RS
2555 (custom-push-theme 'theme-value symbol 'user
2556 'set (list (widget-value child)))
d3d4df42
DL
2557 (funcall set symbol (eval (widget-value child)))
2558 (put symbol 'variable-comment comment)
2559 (put symbol 'saved-variable-comment comment))
d543e20b 2560 (t
d3d4df42
DL
2561 (when (equal comment "")
2562 (setq comment nil)
2563 ;; Make the comment invisible by hand if it's empty
164cfaeb 2564 (custom-comment-hide comment-widget))
d3d4df42
DL
2565 (put symbol 'saved-value
2566 (list (custom-quote (widget-value child))))
c942535f
RS
2567 (custom-push-theme 'theme-value symbol 'user
2568 'set (list (custom-quote (widget-value
2569 child))))
d3d4df42
DL
2570 (funcall set symbol (widget-value child))
2571 (put symbol 'variable-comment comment)
2572 (put symbol 'saved-variable-comment comment)))
d543e20b 2573 (put symbol 'customized-value nil)
d3d4df42 2574 (put symbol 'customized-variable-comment nil)
d543e20b
PA
2575 (custom-save-all)
2576 (custom-variable-state-set widget)
2577 (custom-redraw-magic widget)))
2578
2579(defun custom-variable-reset-saved (widget)
bde4f354
RS
2580 "Restore the saved value for the variable being edited by WIDGET.
2581The value that was current before this operation
2582becomes the backup value, so you can get it again."
6d528fc5 2583 (let* ((symbol (widget-value widget))
d3d4df42 2584 (set (or (get symbol 'custom-set) 'set-default))
d3d4df42
DL
2585 (value (get symbol 'saved-value))
2586 (comment (get symbol 'saved-variable-comment)))
2587 (cond ((or value comment)
2588 (put symbol 'variable-comment comment)
bde4f354 2589 (custom-variable-backup-value widget)
d3d4df42
DL
2590 (condition-case nil
2591 (funcall set symbol (eval (car value)))
2592 (error nil)))
2593 (t
2594 (error "No saved value for %s" symbol)))
d543e20b 2595 (put symbol 'customized-value nil)
d3d4df42 2596 (put symbol 'customized-variable-comment nil)
d543e20b 2597 (widget-put widget :custom-state 'unknown)
d3d4df42 2598 ;; This call will possibly make the comment invisible
d543e20b
PA
2599 (custom-redraw widget)))
2600
25ac13b5 2601(defun custom-variable-reset-standard (widget)
19d63704
RS
2602 "Restore the standard setting for the variable being edited by WIDGET.
2603This operation eliminates any saved setting for the variable,
bde4f354
RS
2604restoring it to the state of a variable that has never been customized.
2605The value that was current before this operation
2606becomes the backup value, so you can get it again."
6d528fc5 2607 (let* ((symbol (widget-value widget))
40627755 2608 (set (or (get symbol 'custom-set) 'set-default)))
25ac13b5 2609 (if (get symbol 'standard-value)
bde4f354
RS
2610 (progn
2611 (custom-variable-backup-value widget)
2612 (funcall set symbol (eval (car (get symbol 'standard-value)))))
5dd0cad0 2613 (error "No standard setting known for %S" symbol))
164cfaeb 2614 (put symbol 'variable-comment nil)
d543e20b 2615 (put symbol 'customized-value nil)
d3d4df42
DL
2616 (put symbol 'customized-variable-comment nil)
2617 (when (or (get symbol 'saved-value) (get symbol 'saved-variable-comment))
d543e20b 2618 (put symbol 'saved-value nil)
c942535f
RS
2619 (custom-push-theme 'theme-value symbol 'user 'reset 'standard)
2620 ;; As a special optimizations we do not (explictly)
2621 ;; save resets to standard when no theme set the value.
2622 (if (null (cdr (get symbol 'theme-value)))
2623 (put symbol 'theme-value nil))
d3d4df42 2624 (put symbol 'saved-variable-comment nil)
d543e20b
PA
2625 (custom-save-all))
2626 (widget-put widget :custom-state 'unknown)
d3d4df42 2627 ;; This call will possibly make the comment invisible
d543e20b
PA
2628 (custom-redraw widget)))
2629
bde4f354
RS
2630(defun custom-variable-backup-value (widget)
2631 "Back up the current value for WIDGET's variable.
2632The backup value is kept in the car of the `backup-value' property."
2633 (let* ((symbol (widget-value widget))
2634 (get (or (get symbol 'custom-get) 'default-value))
2635 (type (custom-variable-type symbol))
2636 (conv (widget-convert type))
2637 (value (if (default-boundp symbol)
2638 (funcall get symbol)
2639 (widget-get conv :value))))
2640 (put symbol 'backup-value (list value))))
2641
2642(defun custom-variable-reset-backup (widget)
2643 "Restore the backup value for the variable being edited by WIDGET.
2644The value that was current before this operation
2645becomes the backup value, so you can use this operation repeatedly
2646to switch between two values."
2647 (let* ((symbol (widget-value widget))
2648 (set (or (get symbol 'custom-set) 'set-default))
2649 (value (get symbol 'backup-value))
2650 (comment-widget (widget-get widget :comment-widget))
2651 (comment (widget-value comment-widget)))
2652 (if value
2653 (progn
2654 (custom-variable-backup-value widget)
2655 (condition-case nil
2656 (funcall set symbol (car value))
2657 (error nil)))
2658 (error "No backup value for %s" symbol))
2659 (put symbol 'customized-value (list (car value)))
2660 (put symbol 'variable-comment comment)
2661 (put symbol 'customized-variable-comment comment)
2662 (custom-variable-state-set widget)
2663 ;; This call will possibly make the comment invisible
2664 (custom-redraw widget)))
2665
d543e20b
PA
2666;;; The `custom-face-edit' Widget.
2667
2668(define-widget 'custom-face-edit 'checklist
2669 "Edit face attributes."
2670 :format "%t: %v"
2671 :tag "Attributes"
2dfa4c57 2672 :extra-offset 13
d3d4df42 2673 :button-args '(:help-echo "Control whether this attribute has any effect.")
d75fa08f
RS
2674 :value-to-internal 'custom-face-edit-fix-value
2675 :match (lambda (widget value)
ee1f522d 2676 (widget-checklist-match widget
d75fa08f 2677 (custom-face-edit-fix-value widget value)))
3ea051cb 2678 :convert-widget 'custom-face-edit-convert-widget
d543e20b 2679 :args (mapcar (lambda (att)
d3d4df42 2680 (list 'group
d543e20b
PA
2681 :inline t
2682 :sibling-args (widget-get (nth 1 att) :sibling-args)
d3d4df42 2683 (list 'const :format "" :value (nth 0 att))
d543e20b
PA
2684 (nth 1 att)))
2685 custom-face-attributes))
2686
d75fa08f 2687(defun custom-face-edit-fix-value (widget value)
0bbe869a
AS
2688 "Ignoring WIDGET, convert :bold and :italic in VALUE to new form.
2689Also change :reverse-video to :inverse-video."
4f985043
RS
2690 (if (listp value)
2691 (let (result)
2692 (while value
2693 (let ((key (car value))
2694 (val (car (cdr value))))
2695 (cond ((eq key :italic)
2696 (push :slant result)
2697 (push (if val 'italic 'normal) result))
2698 ((eq key :bold)
2699 (push :weight result)
2700 (push (if val 'bold 'normal) result))
0bbe869a
AS
2701 ((eq key :reverse-video)
2702 (push :inverse-video result)
2703 (push val result))
ee1f522d 2704 (t
4f985043
RS
2705 (push key result)
2706 (push val result))))
2707 (setq value (cdr (cdr value))))
2708 (setq result (nreverse result))
2709 result)
2710 value))
d75fa08f 2711
3ea051cb
MB
2712(defun custom-face-edit-convert-widget (widget)
2713 "Convert :args as widget types in WIDGET."
2714 (widget-put
2715 widget
2716 :args (mapcar (lambda (arg)
2717 (widget-convert arg
2718 :deactivate 'custom-face-edit-deactivate
2719 :activate 'custom-face-edit-activate
2720 :delete 'custom-face-edit-delete))
2721 (widget-get widget :args)))
2722 widget)
2723
2724(defun custom-face-edit-deactivate (widget)
2725 "Make face widget WIDGET inactive for user modifications."
2726 (unless (widget-get widget :inactive)
2727 (let ((tag (custom-face-edit-attribute-tag widget))
2728 (from (copy-marker (widget-get widget :from)))
3ea051cb
MB
2729 (value (widget-value widget))
2730 (inhibit-read-only t)
2731 (inhibit-modification-hooks t))
2732 (save-excursion
2733 (goto-char from)
2734 (widget-default-delete widget)
2735 (insert tag ": *\n")
2736 (widget-put widget :inactive
2737 (cons value (cons from (- (point) from))))))))
2738
2739(defun custom-face-edit-activate (widget)
2740 "Make face widget WIDGET inactive for user modifications."
2741 (let ((inactive (widget-get widget :inactive))
2742 (inhibit-read-only t)
2743 (inhibit-modification-hooks t))
2744 (when (consp inactive)
2745 (save-excursion
2746 (goto-char (car (cdr inactive)))
2747 (delete-region (point) (+ (point) (cdr (cdr inactive))))
2748 (widget-put widget :inactive nil)
2749 (widget-apply widget :create)
2750 (widget-value-set widget (car inactive))
2751 (widget-setup)))))
2752
2753(defun custom-face-edit-delete (widget)
3127aa13 2754 "Remove WIDGET from the buffer."
3ea051cb
MB
2755 (let ((inactive (widget-get widget :inactive))
2756 (inhibit-read-only t)
2757 (inhibit-modification-hooks t))
2758 (if (not inactive)
2759 ;; Widget is alive, we don't have to do anything special
2760 (widget-default-delete widget)
2761 ;; WIDGET is already deleted because we did so to inactivate it;
2762 ;; now just get rid of the label we put in its place.
2763 (delete-region (car (cdr inactive))
2764 (+ (car (cdr inactive)) (cdr (cdr inactive))))
2765 (widget-put widget :inactive nil))))
ee1f522d 2766
3ea051cb
MB
2767
2768(defun custom-face-edit-attribute-tag (widget)
2769 "Returns the first :tag property in WIDGET or one of its children."
2770 (let ((tag (widget-get widget :tag)))
2771 (or (and (not (equal tag "")) tag)
2772 (let ((children (widget-get widget :children)))
2773 (while (and (null tag) children)
2774 (setq tag (custom-face-edit-attribute-tag (pop children))))
2775 tag))))
2776
d543e20b
PA
2777;;; The `custom-display' Widget.
2778
2779(define-widget 'custom-display 'menu-choice
2780 "Select a display type."
2781 :tag "Display"
2782 :value t
2783 :help-echo "Specify frames where the face attributes should be used."
2784 :args '((const :tag "all" t)
b9afd6dd 2785 (const :tag "defaults" default)
d543e20b
PA
2786 (checklist
2787 :offset 0
2788 :extra-offset 9
2789 :args ((group :sibling-args (:help-echo "\
2790Only match the specified window systems.")
2791 (const :format "Type: "
2792 type)
2793 (checklist :inline t
2794 :offset 0
2795 (const :format "X "
2796 :sibling-args (:help-echo "\
2797The X11 Window System.")
2798 x)
2799 (const :format "PM "
2800 :sibling-args (:help-echo "\
2801OS/2 Presentation Manager.")
2802 pm)
b97aca27 2803 (const :format "W32 "
d543e20b 2804 :sibling-args (:help-echo "\
b97aca27
GV
2805Windows NT/9X.")
2806 w32)
a0b1a022
AS
2807 (const :format "MAC "
2808 :sibling-args (:help-echo "\
2809Macintosh OS.")
2810 mac)
d543e20b
PA
2811 (const :format "DOS "
2812 :sibling-args (:help-echo "\
2813Plain MS-DOS.")
2814 pc)
2815 (const :format "TTY%n"
2816 :sibling-args (:help-echo "\
2817Plain text terminals.")
2818 tty)))
2819 (group :sibling-args (:help-echo "\
2820Only match the frames with the specified color support.")
2821 (const :format "Class: "
2822 class)
2823 (checklist :inline t
2824 :offset 0
2825 (const :format "Color "
2826 :sibling-args (:help-echo "\
2827Match color frames.")
2828 color)
2829 (const :format "Grayscale "
2830 :sibling-args (:help-echo "\
2831Match grayscale frames.")
2832 grayscale)
2833 (const :format "Monochrome%n"
2834 :sibling-args (:help-echo "\
2835Match frames with no color support.")
2836 mono)))
2837 (group :sibling-args (:help-echo "\
c4d9734c
JL
2838The minimum number of colors the frame should support.")
2839 (const :format "" min-colors)
2840 (integer :tag "Minimum number of colors" ))
2841 (group :sibling-args (:help-echo "\
d543e20b
PA
2842Only match frames with the specified intensity.")
2843 (const :format "\
2844Background brightness: "
2845 background)
2846 (checklist :inline t
2847 :offset 0
2848 (const :format "Light "
2849 :sibling-args (:help-echo "\
2850Match frames with light backgrounds.")
2851 light)
2852 (const :format "Dark\n"
2853 :sibling-args (:help-echo "\
2854Match frames with dark backgrounds.")
2b32d1a7
MB
2855 dark)))
2856 (group :sibling-args (:help-echo "\
2857Only match frames that support the specified face attributes.")
2858 (const :format "Supports attributes:" supports)
4abe16b5 2859 (custom-face-edit :inline t :format "%n%v"))))))
d543e20b
PA
2860
2861;;; The `custom-face' Widget.
2862
16b20ed9 2863(defface custom-face-tag-face
b5555381 2864 `((t (:weight bold :height 1.2 :inherit variable-pitch)))
d543e20b 2865 "Face used for face tags."
bd042c03 2866 :group 'custom-faces)
d543e20b 2867
d64478da
KH
2868(defcustom custom-face-default-form 'selected
2869 "Default form of displaying face definition."
2870 :type '(choice (const all)
2871 (const selected)
2872 (const lisp))
cd32a7ba
DN
2873 :group 'custom-buffer
2874 :version "20.3")
d64478da 2875
d543e20b
PA
2876(define-widget 'custom-face 'custom
2877 "Customize face."
d543e20b
PA
2878 :sample-face 'custom-face-tag-face
2879 :help-echo "Set or reset this face."
23c0fb21 2880 :documentation-property #'face-doc-string
d543e20b
PA
2881 :value-create 'custom-face-value-create
2882 :action 'custom-face-action
9097aeb7 2883 :custom-category 'face
d64478da 2884 :custom-form nil ; defaults to value of `custom-face-default-form'
d543e20b
PA
2885 :custom-set 'custom-face-set
2886 :custom-save 'custom-face-save
2887 :custom-reset-current 'custom-redraw
2888 :custom-reset-saved 'custom-face-reset-saved
25ac13b5 2889 :custom-reset-standard 'custom-face-reset-standard
4f985043 2890 :custom-standard-value 'custom-face-standard-value
d543e20b
PA
2891 :custom-menu 'custom-face-menu-create)
2892
d3d4df42 2893(define-widget 'custom-face-all 'editable-list
d543e20b
PA
2894 "An editable list of display specifications and attributes."
2895 :entry-format "%i %d %v"
2896 :insert-button-args '(:help-echo "Insert new display specification here.")
2897 :append-button-args '(:help-echo "Append new display specification here.")
2898 :delete-button-args '(:help-echo "Delete this display specification.")
2899 :args '((group :format "%v" custom-display custom-face-edit)))
2900
2901(defconst custom-face-all (widget-convert 'custom-face-all)
2902 "Converted version of the `custom-face-all' widget.")
2903
2904(define-widget 'custom-display-unselected 'item
2905 "A display specification that doesn't match the selected display."
2906 :match 'custom-display-unselected-match)
2907
2908(defun custom-display-unselected-match (widget value)
2909 "Non-nil if VALUE is an unselected display specification."
86bd10bc 2910 (not (face-spec-set-match-display value (selected-frame))))
d543e20b 2911
d3d4df42 2912(define-widget 'custom-face-selected 'group
d543e20b 2913 "Edit the attributes of the selected display in a face specification."
2dfa4c57
RS
2914 :args '((choice :inline t
2915 (group :tag "With Defaults" :inline t
2916 (group (const :tag "" default)
2917 (custom-face-edit :tag " Default\n Attributes"))
2918 (repeat :format ""
2919 :inline t
2920 (group custom-display-unselected sexp))
2921 (group (sexp :format "")
2922 (custom-face-edit :tag " Overriding\n Attributes"))
2923 (repeat :format ""
2924 :inline t
2925 sexp))
2926 (group :tag "No Defaults" :inline t
2927 (repeat :format ""
2928 :inline t
2929 (group custom-display-unselected sexp))
2930 (group (sexp :format "")
2931 (custom-face-edit :tag "\n Attributes"))
2932 (repeat :format ""
2933 :inline t
2934 sexp)))))
2935
2936
d543e20b
PA
2937
2938(defconst custom-face-selected (widget-convert 'custom-face-selected)
2939 "Converted version of the `custom-face-selected' widget.")
2940
3ea051cb 2941(defun custom-filter-face-spec (spec filter-index &optional default-filter)
f5b50baa
MB
2942 "Return a canonicalized version of SPEC using.
2943FILTER-INDEX is the index in the entry for each attribute in
2944`custom-face-attributes' at which the appropriate filter function can be
2945found, and DEFAULT-FILTER is the filter to apply for attributes that
2946don't specify one."
2947 (mapcar (lambda (entry)
2948 ;; Filter a single face-spec entry
2949 (let ((tests (car entry))
2950 (unfiltered-attrs
2951 ;; Handle both old- and new-style attribute syntax
2952 (if (listp (car (cdr entry)))
2953 (car (cdr entry))
2954 (cdr entry)))
2955 (filtered-attrs nil))
2956 ;; Filter each face attribute
2957 (while unfiltered-attrs
2958 (let* ((attr (pop unfiltered-attrs))
2959 (pre-filtered-value (pop unfiltered-attrs))
2960 (filter
2961 (or (nth filter-index (assq attr custom-face-attributes))
2962 default-filter))
2963 (filtered-value
2964 (if filter
2965 (funcall filter pre-filtered-value)
2966 pre-filtered-value)))
2967 (push filtered-value filtered-attrs)
2968 (push attr filtered-attrs)))
2969 ;;
2970 (list tests filtered-attrs)))
2971 spec))
2972
2973(defun custom-pre-filter-face-spec (spec)
2974 "Return SPEC changed as necessary for editing by the face customization widget.
2975SPEC must be a full face spec."
3ea051cb 2976 (custom-filter-face-spec spec 2))
f5b50baa
MB
2977
2978(defun custom-post-filter-face-spec (spec)
2979 "Return the customized SPEC in a form suitable for setting the face."
3ea051cb 2980 (custom-filter-face-spec spec 3))
f5b50baa 2981
d543e20b 2982(defun custom-face-value-create (widget)
944c91b6
PA
2983 "Create a list of the display specifications for WIDGET."
2984 (let ((buttons (widget-get widget :buttons))
d3d4df42 2985 children
944c91b6
PA
2986 (symbol (widget-get widget :value))
2987 (tag (widget-get widget :tag))
2988 (state (widget-get widget :custom-state))
2989 (begin (point))
2990 (is-last (widget-get widget :custom-last))
2991 (prefix (widget-get widget :custom-prefix)))
2992 (unless tag
2993 (setq tag (prin1-to-string symbol)))
2994 (cond ((eq custom-buffer-style 'tree)
da5ec617 2995 (insert prefix (if is-last " `--- " " |--- "))
944c91b6 2996 (push (widget-create-child-and-convert
c953515e 2997 widget 'custom-browse-face-tag)
944c91b6
PA
2998 buttons)
2999 (insert " " tag "\n")
3000 (widget-put widget :buttons buttons))
3001 (t
3002 ;; Create tag.
3003 (insert tag)
c069a9d3 3004 (widget-specify-sample widget begin (point))
944c91b6
PA
3005 (if (eq custom-buffer-style 'face)
3006 (insert " ")
a62ebc52
MB
3007 (if (string-match "face\\'" tag)
3008 (insert ":")
3009 (insert " face: ")))
944c91b6 3010 ;; Sample.
944c91b6
PA
3011 (push (widget-create-child-and-convert widget 'item
3012 :format "(%{%t%})"
3013 :sample-face symbol
3014 :tag "sample")
3015 buttons)
3016 ;; Visibility.
3017 (insert " ")
d3d4df42 3018 (push (widget-create-child-and-convert
944c91b6
PA
3019 widget 'visibility
3020 :help-echo "Hide or show this face."
7f663295
RS
3021 :on "Hide Face"
3022 :off "Show Face"
944c91b6
PA
3023 :action 'custom-toggle-parent
3024 (not (eq state 'hidden)))
3025 buttons)
3026 ;; Magic.
3027 (insert "\n")
3028 (let ((magic (widget-create-child-and-convert
3029 widget 'custom-magic nil)))
3030 (widget-put widget :custom-magic magic)
3031 (push magic buttons))
3032 ;; Update buttons.
3033 (widget-put widget :buttons buttons)
3034 ;; Insert documentation.
3035 (widget-default-format-handler widget ?h)
d3d4df42
DL
3036 ;; The comment field
3037 (unless (eq state 'hidden)
3038 (let* ((comment (get symbol 'face-comment))
3039 (comment-widget
3040 (widget-create-child-and-convert
3041 widget 'custom-comment
3042 :parent widget
3043 :value (or comment ""))))
3044 (widget-put widget :comment-widget comment-widget)
3045 (push comment-widget children)))
944c91b6
PA
3046 ;; See also.
3047 (unless (eq state 'hidden)
3048 (when (eq (widget-get widget :custom-level) 1)
3049 (custom-add-parent-links widget))
3050 (custom-add-see-also widget))
3051 ;; Editor.
3052 (unless (eq (preceding-char) ?\n)
3053 (insert "\n"))
3054 (unless (eq state 'hidden)
3055 (message "Creating face editor...")
3056 (custom-load-widget widget)
d64478da
KH
3057 (unless (widget-get widget :custom-form)
3058 (widget-put widget :custom-form custom-face-default-form))
944c91b6 3059 (let* ((symbol (widget-value widget))
61763509
PA
3060 (spec (or (get symbol 'customized-face)
3061 (get symbol 'saved-face)
944c91b6
PA
3062 (get symbol 'face-defface-spec)
3063 ;; Attempt to construct it.
d3d4df42 3064 (list (list t (custom-face-attributes-get
944c91b6
PA
3065 symbol (selected-frame))))))
3066 (form (widget-get widget :custom-form))
3067 (indent (widget-get widget :indent))
fa0b3d46
RS
3068 edit)
3069 ;; If the user has changed this face in some other way,
3070 ;; edit it as the user has specified it.
3071 (if (not (face-spec-match-p symbol spec (selected-frame)))
3072 (setq spec (list (list t (face-attr-construct symbol (selected-frame))))))
f5b50baa 3073 (setq spec (custom-pre-filter-face-spec spec))
fa0b3d46 3074 (setq edit (widget-create-child-and-convert
944c91b6
PA
3075 widget
3076 (cond ((and (eq form 'selected)
d3d4df42 3077 (widget-apply custom-face-selected
944c91b6
PA
3078 :match spec))
3079 (when indent (insert-char ?\ indent))
3080 'custom-face-selected)
3081 ((and (not (eq form 'lisp))
3082 (widget-apply custom-face-all
3083 :match spec))
3084 'custom-face-all)
d3d4df42 3085 (t
944c91b6
PA
3086 (when indent (insert-char ?\ indent))
3087 'sexp))
fa0b3d46 3088 :value spec))
944c91b6 3089 (custom-face-state-set widget)
d3d4df42
DL
3090 (push edit children)
3091 (widget-put widget :children children))
944c91b6 3092 (message "Creating face editor...done"))))))
d543e20b 3093
d3d4df42 3094(defvar custom-face-menu
3aec85bf 3095 '(("Set for Current Session" custom-face-set)
896a6a5d 3096 ("Save for Future Sessions" custom-face-save-command)
6d528fc5
PA
3097 ("Reset to Saved" custom-face-reset-saved
3098 (lambda (widget)
d3d4df42
DL
3099 (or (get (widget-value widget) 'saved-face)
3100 (get (widget-value widget) 'saved-face-comment))))
19d63704 3101 ("Erase Customization" custom-face-reset-standard
6d528fc5 3102 (lambda (widget)
8697863a
PA
3103 (get (widget-value widget) 'face-defface-spec)))
3104 ("---" ignore ignore)
d3d4df42
DL
3105 ("Add Comment" custom-comment-show custom-comment-invisible-p)
3106 ("---" ignore ignore)
8697863a
PA
3107 ("Show all display specs" custom-face-edit-all
3108 (lambda (widget)
3109 (not (eq (widget-get widget :custom-form) 'all))))
3110 ("Just current attributes" custom-face-edit-selected
3111 (lambda (widget)
3112 (not (eq (widget-get widget :custom-form) 'selected))))
a84ff57a 3113 ("Show as Lisp expression" custom-face-edit-lisp
8697863a
PA
3114 (lambda (widget)
3115 (not (eq (widget-get widget :custom-form) 'lisp)))))
d543e20b 3116 "Alist of actions for the `custom-face' widget.
6d528fc5
PA
3117Each entry has the form (NAME ACTION FILTER) where NAME is the name of
3118the menu entry, ACTION is the function to call on the widget when the
3119menu is selected, and FILTER is a predicate which takes a `custom-face'
3120widget as an argument, and returns non-nil if ACTION is valid on that
19d63704 3121widget. If FILTER is nil, ACTION is always valid.")
d543e20b
PA
3122
3123(defun custom-face-edit-selected (widget)
3124 "Edit selected attributes of the value of WIDGET."
3125 (widget-put widget :custom-state 'unknown)
3126 (widget-put widget :custom-form 'selected)
3127 (custom-redraw widget))
3128
3129(defun custom-face-edit-all (widget)
3130 "Edit all attributes of the value of WIDGET."
3131 (widget-put widget :custom-state 'unknown)
3132 (widget-put widget :custom-form 'all)
3133 (custom-redraw widget))
3134
3135(defun custom-face-edit-lisp (widget)
2365594b 3136 "Edit the Lisp representation of the value of WIDGET."
d543e20b
PA
3137 (widget-put widget :custom-state 'unknown)
3138 (widget-put widget :custom-form 'lisp)
3139 (custom-redraw widget))
3140
3141(defun custom-face-state-set (widget)
3142 "Set the state of WIDGET."
d3d4df42
DL
3143 (let* ((symbol (widget-value widget))
3144 (comment (get symbol 'face-comment))
a4992f73
RS
3145 tmp temp
3146 (state
3147 (cond ((progn
3148 (setq tmp (get symbol 'customized-face))
3149 (setq temp (get symbol 'customized-face-comment))
3150 (or tmp temp))
3151 (if (equal temp comment)
3152 'set
3153 'changed))
3154 ((progn
3155 (setq tmp (get symbol 'saved-face))
3156 (setq temp (get symbol 'saved-face-comment))
3157 (or tmp temp))
3158 (if (equal temp comment)
3159 'saved
3160 'changed))
3161 ((get symbol 'face-defface-spec)
3162 (if (equal comment nil)
3163 'standard
3164 'changed))
3165 (t
3166 'rogue))))
3167 ;; If the user called set-face-attribute to change the default
3168 ;; for new frames, this face is "set outside of Customize".
3169 (if (and (not (eq state 'rogue))
3170 (get symbol 'face-modified))
3171 (setq state 'changed))
3172 (widget-put widget :custom-state state)))
d543e20b
PA
3173
3174(defun custom-face-action (widget &optional event)
3175 "Show the menu for `custom-face' WIDGET.
3176Optional EVENT is the location for the menu."
3177 (if (eq (widget-get widget :custom-state) 'hidden)
6d528fc5 3178 (custom-toggle-hide widget)
d543e20b
PA
3179 (let* ((completion-ignore-case t)
3180 (symbol (widget-get widget :value))
25ac13b5
PA
3181 (answer (widget-choose (concat "Operation on "
3182 (custom-unlispify-tag-name symbol))
6d528fc5
PA
3183 (custom-menu-filter custom-face-menu
3184 widget)
3185 event)))
d543e20b
PA
3186 (if answer
3187 (funcall answer widget)))))
3188
3189(defun custom-face-set (widget)
3190 "Make the face attributes in WIDGET take effect."
3191 (let* ((symbol (widget-value widget))
3192 (child (car (widget-get widget :children)))
f5b50baa 3193 (value (custom-post-filter-face-spec (widget-value child)))
d3d4df42
DL
3194 (comment-widget (widget-get widget :comment-widget))
3195 (comment (widget-value comment-widget)))
3196 (when (equal comment "")
3197 (setq comment nil)
3198 ;; Make the comment invisible by hand if it's empty
164cfaeb 3199 (custom-comment-hide comment-widget))
d543e20b 3200 (put symbol 'customized-face value)
f5b50baa
MB
3201 (if (face-spec-choose value)
3202 (face-spec-set symbol value)
3203 ;; face-set-spec ignores empty attribute lists, so just give it
3204 ;; something harmless instead.
3205 (face-spec-set symbol '((t :foreground unspecified))))
d3d4df42
DL
3206 (put symbol 'customized-face-comment comment)
3207 (put symbol 'face-comment comment)
d543e20b
PA
3208 (custom-face-state-set widget)
3209 (custom-redraw-magic widget)))
3210
896a6a5d
RS
3211(defun custom-face-save-command (widget)
3212 "Save in `.emacs' the face attributes in WIDGET."
3213 (custom-face-save widget)
3214 (custom-save-all))
3215
d543e20b 3216(defun custom-face-save (widget)
896a6a5d 3217 "Prepare for saving WIDGET's face attributes, but don't write `.emacs'."
d543e20b
PA
3218 (let* ((symbol (widget-value widget))
3219 (child (car (widget-get widget :children)))
e475612a 3220 (value (custom-post-filter-face-spec (widget-value child)))
d3d4df42
DL
3221 (comment-widget (widget-get widget :comment-widget))
3222 (comment (widget-value comment-widget)))
3223 (when (equal comment "")
3224 (setq comment nil)
3225 ;; Make the comment invisible by hand if it's empty
164cfaeb 3226 (custom-comment-hide comment-widget))
e475612a
MB
3227 (if (face-spec-choose value)
3228 (face-spec-set symbol value)
3229 ;; face-set-spec ignores empty attribute lists, so just give it
3230 ;; something harmless instead.
3231 (face-spec-set symbol '((t :foreground unspecified))))
4f985043
RS
3232 (unless (eq (widget-get widget :custom-state) 'standard)
3233 (put symbol 'saved-face value))
c942535f 3234 (custom-push-theme 'theme-face symbol 'user 'set value)
d543e20b 3235 (put symbol 'customized-face nil)
d3d4df42
DL
3236 (put symbol 'face-comment comment)
3237 (put symbol 'customized-face-comment nil)
3238 (put symbol 'saved-face-comment comment)
6321bddd 3239 (custom-save-all)
d543e20b
PA
3240 (custom-face-state-set widget)
3241 (custom-redraw-magic widget)))
3242
3243(defun custom-face-reset-saved (widget)
3244 "Restore WIDGET to the face's default attributes."
3245 (let* ((symbol (widget-value widget))
3246 (child (car (widget-get widget :children)))
d3d4df42
DL
3247 (value (get symbol 'saved-face))
3248 (comment (get symbol 'saved-face-comment))
3249 (comment-widget (widget-get widget :comment-widget)))
3250 (unless (or value comment)
d543e20b
PA
3251 (error "No saved value for this face"))
3252 (put symbol 'customized-face nil)
d3d4df42 3253 (put symbol 'customized-face-comment nil)
25ac13b5 3254 (face-spec-set symbol value)
d3d4df42 3255 (put symbol 'face-comment comment)
d543e20b 3256 (widget-value-set child value)
d3d4df42
DL
3257 ;; This call manages the comment visibility
3258 (widget-value-set comment-widget (or comment ""))
d543e20b
PA
3259 (custom-face-state-set widget)
3260 (custom-redraw-magic widget)))
3261
4f985043
RS
3262(defun custom-face-standard-value (widget)
3263 (get (widget-value widget) 'face-defface-spec))
3264
25ac13b5 3265(defun custom-face-reset-standard (widget)
19d63704
RS
3266 "Restore WIDGET to the face's standard settings.
3267This operation eliminates any saved setting for the face,
3268restoring it to the state of a face that has never been customized."
d543e20b
PA
3269 (let* ((symbol (widget-value widget))
3270 (child (car (widget-get widget :children)))
d3d4df42
DL
3271 (value (get symbol 'face-defface-spec))
3272 (comment-widget (widget-get widget :comment-widget)))
d543e20b 3273 (unless value
5dd0cad0 3274 (error "No standard setting for this face"))
d543e20b 3275 (put symbol 'customized-face nil)
d3d4df42
DL
3276 (put symbol 'customized-face-comment nil)
3277 (when (or (get symbol 'saved-face) (get symbol 'saved-face-comment))
d543e20b 3278 (put symbol 'saved-face nil)
c942535f
RS
3279 (custom-push-theme 'theme-face symbol 'user 'reset 'standard)
3280 ;; Do not explictly save resets to standards without themes.
3281 (if (null (cdr (get symbol 'theme-face)))
3282 (put symbol 'theme-face nil))
d3d4df42 3283 (put symbol 'saved-face-comment nil)
d543e20b 3284 (custom-save-all))
25ac13b5 3285 (face-spec-set symbol value)
d3d4df42 3286 (put symbol 'face-comment nil)
d543e20b 3287 (widget-value-set child value)
d3d4df42
DL
3288 ;; This call manages the comment visibility
3289 (widget-value-set comment-widget "")
d543e20b
PA
3290 (custom-face-state-set widget)
3291 (custom-redraw-magic widget)))
3292
3293;;; The `face' Widget.
3294
1833b7b3
RS
3295(defvar widget-face-prompt-value-history nil
3296 "History of input to `widget-face-prompt-value'.")
3297
3298(define-widget 'face 'restricted-sexp
3299 "A Lisp face name."
3300 :complete-function (lambda ()
3301 (interactive)
3302 (lisp-complete-symbol 'facep))
3303 :prompt-value 'widget-field-prompt-value
3304 :prompt-internal 'widget-symbol-prompt-internal
3305 :prompt-match 'facep
3306 :prompt-history 'widget-face-prompt-value-history
d543e20b 3307 :value-create 'widget-face-value-create
1833b7b3
RS
3308 :action 'widget-field-action
3309 :match-alternatives '(facep)
3310 :validate (lambda (widget)
3311 (unless (facep (widget-value widget))
3312 (widget-put widget :error (format "Invalid face: %S"
3313 (widget-value widget)))
3314 widget))
3315 :value 'ignore
3316 :tag "Function")
3317
3318
3319;;; There is a bug here: the sample doesn't get redisplayed
3320;;; in the new font when you specify one. Does anyone know how to
3321;;; make that work? -- rms.
d543e20b
PA
3322
3323(defun widget-face-value-create (widget)
1833b7b3
RS
3324 "Create an editable face name field."
3325 (let ((buttons (widget-get widget :buttons))
3326 (symbol (widget-get widget :value)))
3327 ;; Sample.
3328 (push (widget-create-child-and-convert widget 'item
3329 :format "(%{%t%})"
3330 :sample-face symbol
3331 :tag "sample")
3332 buttons)
3333 (insert " ")
3334 ;; Update buttons.
3335 (widget-put widget :buttons buttons))
3336
3337 (let ((size (widget-get widget :size))
3338 (value (widget-get widget :value))
3339 (from (point))
3340 ;; This is changed to a real overlay in `widget-setup'. We
3341 ;; need the end points to behave differently until
3342 ;; `widget-setup' is called.
3343 (overlay (cons (make-marker) (make-marker))))
3344 (widget-put widget :field-overlay overlay)
3345 (insert value)
3346 (and size
3347 (< (length value) size)
3348 (insert-char ?\ (- size (length value))))
3349 (unless (memq widget widget-field-list)
3350 (setq widget-field-new (cons widget widget-field-new)))
3351 (move-marker (cdr overlay) (point))
3352 (set-marker-insertion-type (cdr overlay) nil)
3353 (when (null size)
3354 (insert ?\n))
3355 (move-marker (car overlay) from)
3356 (set-marker-insertion-type (car overlay) t)))
3357
d543e20b
PA
3358
3359;;; The `hook' Widget.
3360
3361(define-widget 'hook 'list
3362 "A emacs lisp hook"
f985c5f7 3363 :value-to-internal (lambda (widget value)
5aa3f181 3364 (if (and value (symbolp value))
f985c5f7
PA
3365 (list value)
3366 value))
3367 :match (lambda (widget value)
3368 (or (symbolp value)
4743fc91 3369 (widget-group-match widget value)))
2365594b
DL
3370 ;; Avoid adding undefined functions to the hook, especially for
3371 ;; things like `find-file-hook' or even more basic ones, to avoid
3372 ;; chaos.
3373 :set (lambda (symbol value)
d4881668
SM
3374 (dolist (elt value)
3375 (if (fboundp elt)
3376 (add-hook symbol elt))))
d543e20b
PA
3377 :convert-widget 'custom-hook-convert-widget
3378 :tag "Hook")
3379
3380(defun custom-hook-convert-widget (widget)
3c708e98 3381 ;; Handle `:options'.
d543e20b 3382 (let* ((options (widget-get widget :options))
d3d4df42 3383 (other `(editable-list :inline t
d543e20b
PA
3384 :entry-format "%i %d%v"
3385 (function :format " %v")))
3386 (args (if options
3387 (list `(checklist :inline t
3388 ,@(mapcar (lambda (entry)
3389 `(function-item ,entry))
3390 options))
3391 other)
3392 (list other))))
3393 (widget-put widget :args args)
3394 widget))
3395
944c91b6
PA
3396;;; The `custom-group-link' Widget.
3397
3398(define-widget 'custom-group-link 'link
3399 "Show parent in other window when activated."
b62c92bb 3400 :help-echo "Create customization buffer for this group."
944c91b6
PA
3401 :action 'custom-group-link-action)
3402
3403(defun custom-group-link-action (widget &rest ignore)
3404 (customize-group (widget-value widget)))
3405
d543e20b
PA
3406;;; The `custom-group' Widget.
3407
b62c92bb 3408(defcustom custom-group-tag-faces nil
d543e20b 3409 ;; In XEmacs, this ought to play games with font size.
d3d4df42 3410 ;; Fixme: make it do so in Emacs.
d543e20b
PA
3411 "Face used for group tags.
3412The first member is used for level 1 groups, the second for level 2,
3413and so forth. The remaining group tags are shown with
3414`custom-group-tag-face'."
3415 :type '(repeat face)
bd042c03 3416 :group 'custom-faces)
d543e20b 3417
16b20ed9
GM
3418(defface custom-group-tag-face-1
3419 `((((class color)
3420 (background dark))
b5555381 3421 (:foreground "pink" :weight bold :height 1.2 :inherit variable-pitch))
16b20ed9
GM
3422 (((class color)
3423 (background light))
b5555381
RS
3424 (:foreground "red" :weight bold :height 1.2 :inherit variable-pitch))
3425 (t (:weight bold)))
16b20ed9
GM
3426 "Face used for group tags."
3427 :group 'custom-faces)
3428
3429(defface custom-group-tag-face
3430 `((((class color)
3431 (background dark))
b5555381 3432 (:foreground "light blue" :weight bold :height 1.2))
16b20ed9
GM
3433 (((class color)
3434 (background light))
b5555381
RS
3435 (:foreground "blue" :weight bold :height 1.2))
3436 (t (:weight bold)))
d543e20b 3437 "Face used for low level group tags."
bd042c03 3438 :group 'custom-faces)
d543e20b
PA
3439
3440(define-widget 'custom-group 'custom
3441 "Customize group."
944c91b6 3442 :format "%v"
d543e20b
PA
3443 :sample-face-get 'custom-group-sample-face-get
3444 :documentation-property 'group-documentation
3445 :help-echo "Set or reset all members of this group."
3446 :value-create 'custom-group-value-create
3447 :action 'custom-group-action
9097aeb7 3448 :custom-category 'group
d543e20b
PA
3449 :custom-set 'custom-group-set
3450 :custom-save 'custom-group-save
3451 :custom-reset-current 'custom-group-reset-current
3452 :custom-reset-saved 'custom-group-reset-saved
25ac13b5 3453 :custom-reset-standard 'custom-group-reset-standard
d543e20b
PA
3454 :custom-menu 'custom-group-menu-create)
3455
3456(defun custom-group-sample-face-get (widget)
3457 ;; Use :sample-face.
3458 (or (nth (1- (widget-get widget :custom-level)) custom-group-tag-faces)
3459 'custom-group-tag-face))
3460
8691cfa7
RS
3461(define-widget 'custom-group-visibility 'visibility
3462 "An indicator and manipulator for hidden group contents."
3463 :create 'custom-group-visibility-create)
3464
3465(defun custom-group-visibility-create (widget)
3466 (let ((visible (widget-value widget)))
3467 (if visible
3468 (insert "--------")))
3469 (widget-default-create widget))
3470
4ee1cf9f
PA
3471(defun custom-group-members (symbol groups-only)
3472 "Return SYMBOL's custom group members.
3473If GROUPS-ONLY non-nil, return only those members that are groups."
3474 (if (not groups-only)
3475 (get symbol 'custom-group)
3476 (let (members)
3477 (dolist (entry (get symbol 'custom-group))
3478 (when (eq (nth 1 entry) 'custom-group)
3479 (push entry members)))
3480 (nreverse members))))
3481
d543e20b 3482(defun custom-group-value-create (widget)
944c91b6 3483 "Insert a customize group for WIDGET in the current buffer."
2de2cb02 3484 (unless (eq (widget-get widget :custom-state) 'hidden)
2ee398c4 3485 (custom-load-widget widget))
4ee1cf9f
PA
3486 (let* ((state (widget-get widget :custom-state))
3487 (level (widget-get widget :custom-level))
f985c5f7 3488 ;; (indent (widget-get widget :indent))
4ee1cf9f
PA
3489 (prefix (widget-get widget :custom-prefix))
3490 (buttons (widget-get widget :buttons))
3491 (tag (widget-get widget :tag))
3492 (symbol (widget-value widget))
3493 (members (custom-group-members symbol
3494 (and (eq custom-buffer-style 'tree)
3495 custom-browse-only-groups))))
944c91b6 3496 (cond ((and (eq custom-buffer-style 'tree)
c953515e 3497 (eq state 'hidden)
4ee1cf9f 3498 (or members (custom-unloaded-widget-p widget)))
c953515e 3499 (custom-browse-insert-prefix prefix)
944c91b6 3500 (push (widget-create-child-and-convert
d3d4df42 3501 widget 'custom-browse-visibility
da5ec617 3502 ;; :tag-glyph "plus"
df816618 3503 :tag "+")
944c91b6
PA
3504 buttons)
3505 (insert "-- ")
da5ec617 3506 ;; (widget-glyph-insert nil "-- " "horizontal")
944c91b6 3507 (push (widget-create-child-and-convert
c953515e 3508 widget 'custom-browse-group-tag)
944c91b6
PA
3509 buttons)
3510 (insert " " tag "\n")
3511 (widget-put widget :buttons buttons))
3512 ((and (eq custom-buffer-style 'tree)
4ee1cf9f 3513 (zerop (length members)))
c953515e 3514 (custom-browse-insert-prefix prefix)
da5ec617
PA
3515 (insert "[ ]-- ")
3516 ;; (widget-glyph-insert nil "[ ]" "empty")
3517 ;; (widget-glyph-insert nil "-- " "horizontal")
d3d4df42 3518 (push (widget-create-child-and-convert
c953515e 3519 widget 'custom-browse-group-tag)
944c91b6
PA
3520 buttons)
3521 (insert " " tag "\n")
3522 (widget-put widget :buttons buttons))
3523 ((eq custom-buffer-style 'tree)
c953515e 3524 (custom-browse-insert-prefix prefix)
4ee1cf9f 3525 (if (zerop (length members))
d3d4df42 3526 (progn
c953515e 3527 (custom-browse-insert-prefix prefix)
da5ec617
PA
3528 (insert "[ ]-- ")
3529 ;; (widget-glyph-insert nil "[ ]" "empty")
3530 ;; (widget-glyph-insert nil "-- " "horizontal")
d3d4df42 3531 (push (widget-create-child-and-convert
c953515e 3532 widget 'custom-browse-group-tag)
944c91b6
PA
3533 buttons)
3534 (insert " " tag "\n")
3535 (widget-put widget :buttons buttons))
d3d4df42
DL
3536 (push (widget-create-child-and-convert
3537 widget 'custom-browse-visibility
da5ec617
PA
3538 ;; :tag-glyph "minus"
3539 :tag "-")
944c91b6 3540 buttons)
da5ec617
PA
3541 (insert "-\\ ")
3542 ;; (widget-glyph-insert nil "-\\ " "top")
d3d4df42 3543 (push (widget-create-child-and-convert
c953515e 3544 widget 'custom-browse-group-tag)
944c91b6
PA
3545 buttons)
3546 (insert " " tag "\n")
3547 (widget-put widget :buttons buttons)
3548 (message "Creating group...")
4ee1cf9f 3549 (let* ((members (custom-sort-items members
da5ec617
PA
3550 custom-browse-sort-alphabetically
3551 custom-browse-order-groups))
944c91b6
PA
3552 (prefixes (widget-get widget :custom-prefixes))
3553 (custom-prefix-list (custom-prefix-add symbol prefixes))
944c91b6
PA
3554 (extra-prefix (if (widget-get widget :custom-last)
3555 " "
3556 " | "))
3557 (prefix (concat prefix extra-prefix))
3558 children entry)
3559 (while members
3560 (setq entry (car members)
3561 members (cdr members))
4ee1cf9f
PA
3562 (push (widget-create-child-and-convert
3563 widget (nth 1 entry)
3564 :group widget
3565 :tag (custom-unlispify-tag-name (nth 0 entry))
3566 :custom-prefixes custom-prefix-list
3567 :custom-level (1+ level)
3568 :custom-last (null members)
3569 :value (nth 0 entry)
3570 :custom-prefix prefix)
3571 children))
944c91b6
PA
3572 (widget-put widget :children (reverse children)))
3573 (message "Creating group...done")))
3574 ;; Nested style.
3575 ((eq state 'hidden)
3576 ;; Create level indicator.
26c7b3ef
RS
3577 (unless (eq custom-buffer-style 'links)
3578 (insert-char ?\ (* custom-buffer-indent (1- level)))
3579 (insert "-- "))
944c91b6
PA
3580 ;; Create tag.
3581 (let ((begin (point)))
3582 (insert tag)
3583 (widget-specify-sample widget begin (point)))
3584 (insert " group: ")
3585 ;; Create link/visibility indicator.
3586 (if (eq custom-buffer-style 'links)
3587 (push (widget-create-child-and-convert
d3d4df42 3588 widget 'custom-group-link
b62c92bb 3589 :tag "Go to Group"
944c91b6
PA
3590 symbol)
3591 buttons)
d3d4df42 3592 (push (widget-create-child-and-convert
98d5aafe 3593 widget 'custom-group-visibility
944c91b6
PA
3594 :help-echo "Show members of this group."
3595 :action 'custom-toggle-parent
3596 (not (eq state 'hidden)))
3597 buttons))
3598 (insert " \n")
3599 ;; Create magic button.
3600 (let ((magic (widget-create-child-and-convert
3601 widget 'custom-magic nil)))
3602 (widget-put widget :custom-magic magic)
3603 (push magic buttons))
3604 ;; Update buttons.
3605 (widget-put widget :buttons buttons)
3606 ;; Insert documentation.
26c7b3ef
RS
3607 (if (and (eq custom-buffer-style 'links) (> level 1))
3608 (widget-put widget :documentation-indent 0))
944c91b6
PA
3609 (widget-default-format-handler widget ?h))
3610 ;; Nested style.
3611 (t ;Visible.
d377bee9
RS
3612 ;; Add parent groups references above the group.
3613 (if t ;;; This should test that the buffer
3614 ;;; was made to display a group.
3615 (when (eq level 1)
cd6c0940
RS
3616 (if (custom-add-parent-links widget
3617 "Go to parent group:")
d377bee9 3618 (insert "\n"))))
944c91b6
PA
3619 ;; Create level indicator.
3620 (insert-char ?\ (* custom-buffer-indent (1- level)))
3621 (insert "/- ")
3622 ;; Create tag.
3623 (let ((start (point)))
3624 (insert tag)
3625 (widget-specify-sample widget start (point)))
3626 (insert " group: ")
3627 ;; Create visibility indicator.
3628 (unless (eq custom-buffer-style 'links)
3629 (insert "--------")
d3d4df42 3630 (push (widget-create-child-and-convert
944c91b6
PA
3631 widget 'visibility
3632 :help-echo "Hide members of this group."
3633 :action 'custom-toggle-parent
3634 (not (eq state 'hidden)))
3635 buttons)
3636 (insert " "))
3637 ;; Create more dashes.
3638 ;; Use 76 instead of 75 to compensate for the temporary "<"
d3d4df42 3639 ;; added by `widget-insert'.
944c91b6
PA
3640 (insert-char ?- (- 76 (current-column)
3641 (* custom-buffer-indent level)))
3642 (insert "\\\n")
3643 ;; Create magic button.
3644 (let ((magic (widget-create-child-and-convert
d3d4df42 3645 widget 'custom-magic
944c91b6
PA
3646 :indent 0
3647 nil)))
3648 (widget-put widget :custom-magic magic)
3649 (push magic buttons))
3650 ;; Update buttons.
3651 (widget-put widget :buttons buttons)
3652 ;; Insert documentation.
3653 (widget-default-format-handler widget ?h)
d377bee9
RS
3654 ;; Parent groups.
3655 (if nil ;;; This should test that the buffer
3656 ;;; was not made to display a group.
3657 (when (eq level 1)
3658 (insert-char ?\ custom-buffer-indent)
3659 (custom-add-parent-links widget)))
d3d4df42 3660 (custom-add-see-also widget
944c91b6
PA
3661 (make-string (* custom-buffer-indent level)
3662 ?\ ))
3663 ;; Members.
3664 (message "Creating group...")
4ee1cf9f 3665 (let* ((members (custom-sort-items members
da5ec617
PA
3666 custom-buffer-sort-alphabetically
3667 custom-buffer-order-groups))
944c91b6
PA
3668 (prefixes (widget-get widget :custom-prefixes))
3669 (custom-prefix-list (custom-prefix-add symbol prefixes))
3670 (length (length members))
3671 (count 0)
3672 (children (mapcar (lambda (entry)
3673 (widget-insert "\n")
3674 (message "\
3675Creating group members... %2d%%"
3676 (/ (* 100.0 count) length))
3677 (setq count (1+ count))
3678 (prog1
3679 (widget-create-child-and-convert
3680 widget (nth 1 entry)
3681 :group widget
3682 :tag (custom-unlispify-tag-name
3683 (nth 0 entry))
3684 :custom-prefixes custom-prefix-list
3685 :custom-level (1+ level)
3686 :value (nth 0 entry))
3687 (unless (eq (preceding-char) ?\n)
3688 (widget-insert "\n"))))
3689 members)))
3690 (message "Creating group magic...")
fadbdfea 3691 (mapc 'custom-magic-reset children)
944c91b6
PA
3692 (message "Creating group state...")
3693 (widget-put widget :children children)
3694 (custom-group-state-update widget)
3695 (message "Creating group... done"))
3696 ;; End line
3697 (insert "\n")
3698 (insert-char ?\ (* custom-buffer-indent (1- level)))
3699 (insert "\\- " (widget-get widget :tag) " group end ")
3700 (insert-char ?- (- 75 (current-column) (* custom-buffer-indent level)))
3701 (insert "/\n")))))
d543e20b 3702
d3d4df42 3703(defvar custom-group-menu
3aec85bf 3704 '(("Set for Current Session" custom-group-set
6d528fc5
PA
3705 (lambda (widget)
3706 (eq (widget-get widget :custom-state) 'modified)))
3aec85bf 3707 ("Save for Future Sessions" custom-group-save
6d528fc5
PA
3708 (lambda (widget)
3709 (memq (widget-get widget :custom-state) '(modified set))))
3710 ("Reset to Current" custom-group-reset-current
3711 (lambda (widget)
86bd10bc 3712 (memq (widget-get widget :custom-state) '(modified))))
6d528fc5
PA
3713 ("Reset to Saved" custom-group-reset-saved
3714 (lambda (widget)
86bd10bc 3715 (memq (widget-get widget :custom-state) '(modified set))))
25ac13b5 3716 ("Reset to standard setting" custom-group-reset-standard
6d528fc5 3717 (lambda (widget)
86bd10bc 3718 (memq (widget-get widget :custom-state) '(modified set saved)))))
d543e20b 3719 "Alist of actions for the `custom-group' widget.
6d528fc5
PA
3720Each entry has the form (NAME ACTION FILTER) where NAME is the name of
3721the menu entry, ACTION is the function to call on the widget when the
3722menu is selected, and FILTER is a predicate which takes a `custom-group'
3723widget as an argument, and returns non-nil if ACTION is valid on that
d3d4df42 3724widget. If FILTER is nil, ACTION is always valid.")
d543e20b
PA
3725
3726(defun custom-group-action (widget &optional event)
3727 "Show the menu for `custom-group' WIDGET.
3728Optional EVENT is the location for the menu."
3729 (if (eq (widget-get widget :custom-state) 'hidden)
6d528fc5 3730 (custom-toggle-hide widget)
d543e20b 3731 (let* ((completion-ignore-case t)
25ac13b5
PA
3732 (answer (widget-choose (concat "Operation on "
3733 (custom-unlispify-tag-name
3734 (widget-get widget :value)))
6d528fc5
PA
3735 (custom-menu-filter custom-group-menu
3736 widget)
d543e20b
PA
3737 event)))
3738 (if answer
3739 (funcall answer widget)))))
3740
3741(defun custom-group-set (widget)
3742 "Set changes in all modified group members."
3743 (let ((children (widget-get widget :children)))
fadbdfea
DL
3744 (mapc (lambda (child)
3745 (when (eq (widget-get child :custom-state) 'modified)
3746 (widget-apply child :custom-set)))
d543e20b
PA
3747 children )))
3748
3749(defun custom-group-save (widget)
3750 "Save all modified group members."
3751 (let ((children (widget-get widget :children)))
fadbdfea
DL
3752 (mapc (lambda (child)
3753 (when (memq (widget-get child :custom-state) '(modified set))
3754 (widget-apply child :custom-save)))
d543e20b
PA
3755 children )))
3756
3757(defun custom-group-reset-current (widget)
3758 "Reset all modified group members."
3759 (let ((children (widget-get widget :children)))
fadbdfea
DL
3760 (mapc (lambda (child)
3761 (when (eq (widget-get child :custom-state) 'modified)
3762 (widget-apply child :custom-reset-current)))
d543e20b
PA
3763 children )))
3764
3765(defun custom-group-reset-saved (widget)
3766 "Reset all modified or set group members."
3767 (let ((children (widget-get widget :children)))
fadbdfea
DL
3768 (mapc (lambda (child)
3769 (when (memq (widget-get child :custom-state) '(modified set))
3770 (widget-apply child :custom-reset-saved)))
d543e20b
PA
3771 children )))
3772
25ac13b5 3773(defun custom-group-reset-standard (widget)
d543e20b
PA
3774 "Reset all modified, set, or saved group members."
3775 (let ((children (widget-get widget :children)))
fadbdfea
DL
3776 (mapc (lambda (child)
3777 (when (memq (widget-get child :custom-state)
3778 '(modified set saved))
3779 (widget-apply child :custom-reset-standard)))
d543e20b
PA
3780 children )))
3781
3782(defun custom-group-state-update (widget)
3783 "Update magic."
3784 (unless (eq (widget-get widget :custom-state) 'hidden)
3785 (let* ((children (widget-get widget :children))
3786 (states (mapcar (lambda (child)
3787 (widget-get child :custom-state))
3788 children))
25ac13b5
PA
3789 (magics custom-magic-alist)
3790 (found 'standard))
d543e20b
PA
3791 (while magics
3792 (let ((magic (car (car magics))))
3793 (if (and (not (eq magic 'hidden))
3794 (memq magic states))
3795 (setq found magic
3796 magics nil)
3797 (setq magics (cdr magics)))))
3798 (widget-put widget :custom-state found)))
3799 (custom-magic-reset widget))
3800
3801;;; The `custom-save-all' Function.
a1a4fa22 3802;;;###autoload
1e4ed6df 3803(defcustom custom-file nil
d543e20b 3804 "File used for storing customization information.
1e4ed6df 3805The default is nil, which means to use your init file
09b73f49
RS
3806as specified by `user-init-file'. If the value is not nil,
3807it should be an absolute file name.
3808
5aa971e3
LT
3809You can set this option through Custom, if you carefully read the
3810last paragraph below. However, usually it is simpler to write
3811something like the following in your init file:
3812
3813\(setq custom-file \"~/.emacs-custom.el\")
3814\(load custom-file)
3815
3816Note that both lines are necessary: the first line tells Custom to
3817save all customizations in this file, but does not load it.
3818
3819When you change this variable outside Custom, look in the
3820previous custom file \(usually your init file) for the
3821forms `(custom-set-variables ...)' and `(custom-set-faces ...)',
3822and copy them (whichever ones you find) to the new custom file.
3823This will preserve your existing customizations.
3824
3825If you save this option using Custom, Custom will write all
3826currently saved customizations, including the new one for this
3827option itself, into the file you specify, overwriting any
3828`custom-set-variables' and `custom-set-faces' forms already
3829present in that file. It will not delete any customizations from
3830the old custom file. You should do that manually if that is what you
3831want. You also have to put something like `\(load \"CUSTOM-FILE\")
3832in your init file, where CUSTOM-FILE is the actual name of the
3833file. Otherwise, Emacs will not load the file when it starts up,
3834and hence will not set `custom-file' to that file either."
3835 :type '(choice (const :tag "Your Emacs init file" nil)
3836 (file :format "%t:%v%d"
3837 :doc
3838 "Please read entire docstring below before setting \
3839this through Custom.
3840Click om \"More\" \(or position point there and press RETURN)
3841if only the first line of the docstring is shown."))
d543e20b
PA
3842 :group 'customize)
3843
176eb8cb
KH
3844(defun custom-file ()
3845 "Return the file name for saving customizations."
5aa971e3
LT
3846 (or custom-file
3847 (let ((user-init-file user-init-file)
3848 (default-init-file
3849 (if (eq system-type 'ms-dos) "~/_emacs" "~/.emacs")))
3850 (when (null user-init-file)
3851 (if (or (file-exists-p default-init-file)
3852 (and (eq system-type 'windows-nt)
3853 (file-exists-p "~/_emacs")))
3854 ;; Started with -q, i.e. the file containing
3855 ;; Custom settings hasn't been read. Saving
3856 ;; settings there would overwrite other settings.
3857 (error "Saving settings from \"emacs -q\" would overwrite existing customizations"))
3858 (setq user-init-file default-init-file))
3859 user-init-file)))
176eb8cb 3860
d543e20b 3861(defun custom-save-delete (symbol)
a34511a1
RS
3862 "Visit `custom-file' and delete all calls to SYMBOL from it.
3863Leave point at the old location of the first such call,
3864or (if there were none) at the end of the buffer."
2302ee34 3865 (let ((default-major-mode 'emacs-lisp-mode))
176eb8cb 3866 (set-buffer (find-file-noselect (custom-file))))
d543e20b 3867 (goto-char (point-min))
cbe8bb8e
KH
3868 ;; Skip all whitespace and comments.
3869 (while (forward-comment 1))
3870 (or (eobp)
3871 (save-excursion (forward-sexp (buffer-size)))) ; Test for scan errors.
a34511a1
RS
3872 (let (first)
3873 (catch 'found
3874 (while t ;; We exit this loop only via throw.
3875 ;; Skip all whitespace and comments.
3876 (while (forward-comment 1))
3877 (let ((start (point))
3878 (sexp (condition-case nil
3879 (read (current-buffer))
3880 (end-of-file (throw 'found nil)))))
3881 (when (and (listp sexp)
3882 (eq (car sexp) symbol))
3883 (delete-region start (point))
3884 (unless first
3885 (setq first (point)))))))
3886 (if first
3887 (goto-char first)
189638d5
GM
3888 ;; Move in front of local variables, otherwise long Custom
3889 ;; entries would make them ineffective.
3890 (let ((pos (point-max))
3891 (case-fold-search t))
3892 (save-excursion
3893 (goto-char (point-max))
3894 (search-backward "\n\^L" (max (- (point-max) 3000) (point-min))
3895 'move)
3896 (when (search-forward "Local Variables:" nil t)
3897 (setq pos (line-beginning-position))))
3898 (goto-char pos)))))
d543e20b
PA
3899
3900(defun custom-save-variables ()
3901 "Save all customized variables in `custom-file'."
3902 (save-excursion
c942535f
RS
3903 (custom-save-delete 'custom-load-themes)
3904 (custom-save-delete 'custom-reset-variables)
d543e20b 3905 (custom-save-delete 'custom-set-variables)
c942535f
RS
3906 (custom-save-loaded-themes)
3907 (custom-save-resets 'theme-value 'custom-reset-variables nil)
d151422c
MR
3908 (let ((standard-output (current-buffer))
3909 (saved-list (make-list 1 0))
3910 sort-fold-case)
3911 ;; First create a sorted list of saved variables.
3912 (mapatoms
3913 (lambda (symbol)
3914 (if (get symbol 'saved-value)
3915 (nconc saved-list (list symbol)))))
3916 (setq saved-list (sort (cdr saved-list) 'string<))
d543e20b
PA
3917 (unless (bolp)
3918 (princ "\n"))
aec2bb63 3919 (princ "(custom-set-variables
99b398e0
RS
3920 ;; custom-set-variables was added by Custom.
3921 ;; If you edit it by hand, you could mess it up, so be careful.
3922 ;; Your init file should contain only one such instance.
3923 ;; If there is more than one, they won't work right.\n")
d9f67134
SM
3924 (dolist (symbol saved-list)
3925 (let ((spec (car-safe (get symbol 'theme-value)))
3926 (value (get symbol 'saved-value))
3927 (requests (get symbol 'custom-requests))
3928 (now (not (or (custom-variable-p symbol)
3929 (and (not (boundp symbol))
3930 (not (eq (get symbol 'force-value)
3931 'rogue))))))
40627755 3932 (comment (get symbol 'saved-variable-comment)))
d9f67134
SM
3933 ;; Check `requests'.
3934 (dolist (request requests)
3935 (when (and (symbolp request) (not (featurep request)))
3936 (message "Unknown requested feature: %s" request)
3937 (setq requests (delq request requests))))
3938 (when (or (and spec
3939 (eq (nth 0 spec) 'user)
3940 (eq (nth 1 spec) 'set))
3941 comment
3942 (and (null spec) (get symbol 'saved-value)))
3943 (unless (bolp)
3944 (princ "\n"))
3945 (princ " '(")
3946 (prin1 symbol)
3947 (princ " ")
3948 (prin1 (car value))
3949 (when (or now requests comment)
3950 (princ " ")
3951 (prin1 now)
3952 (when (or requests comment)
3953 (princ " ")
3954 (prin1 requests)
3955 (when comment
3956 (princ " ")
3957 (prin1 comment))))
3958 (princ ")"))))
a34511a1
RS
3959 (if (bolp)
3960 (princ " "))
d543e20b
PA
3961 (princ ")")
3962 (unless (looking-at "\n")
3963 (princ "\n")))))
3964
3965(defun custom-save-faces ()
3966 "Save all customized faces in `custom-file'."
3967 (save-excursion
c942535f 3968 (custom-save-delete 'custom-reset-faces)
d543e20b 3969 (custom-save-delete 'custom-set-faces)
c942535f 3970 (custom-save-resets 'theme-face 'custom-reset-faces '(default))
d151422c
MR
3971 (let ((standard-output (current-buffer))
3972 (saved-list (make-list 1 0))
3973 sort-fold-case)
3974 ;; First create a sorted list of saved faces.
3975 (mapatoms
3976 (lambda (symbol)
3977 (if (get symbol 'saved-face)
3978 (nconc saved-list (list symbol)))))
3979 (setq saved-list (sort (cdr saved-list) 'string<))
3980 ;; The default face must be first, since it affects the others.
3981 (if (memq 'default saved-list)
3982 (setq saved-list (cons 'default (delq 'default saved-list))))
d543e20b
PA
3983 (unless (bolp)
3984 (princ "\n"))
aec2bb63 3985 (princ "(custom-set-faces
99b398e0
RS
3986 ;; custom-set-faces was added by Custom.
3987 ;; If you edit it by hand, you could mess it up, so be careful.
3988 ;; Your init file should contain only one such instance.
3989 ;; If there is more than one, they won't work right.\n")
d9f67134
SM
3990 (dolist (symbol saved-list)
3991 (let ((spec (car-safe (get symbol 'theme-face)))
3992 (value (get symbol 'saved-face))
3993 (now (not (or (get symbol 'face-defface-spec)
3994 (and (not (custom-facep symbol))
3995 (not (get symbol 'force-face))))))
3996 (comment (get symbol 'saved-face-comment)))
3997 (when (or (and spec
3998 (eq (nth 0 spec) 'user)
3999 (eq (nth 1 spec) 'set))
4000 comment
4001 (and (null spec) (get symbol 'saved-face)))
4002 ;; Don't print default face here.
4003 (unless (bolp)
4004 (princ "\n"))
4005 (princ " '(")
4006 (prin1 symbol)
4007 (princ " ")
4008 (prin1 value)
4009 (when (or now comment)
4010 (princ " ")
4011 (prin1 now)
4012 (when comment
4013 (princ " ")
4014 (prin1 comment)))
4015 (princ ")"))))
a34511a1
RS
4016 (if (bolp)
4017 (princ " "))
d543e20b
PA
4018 (princ ")")
4019 (unless (looking-at "\n")
08b4ae6c 4020 (princ "\n")))))
c942535f
RS
4021
4022(defun custom-save-resets (property setter special)
4023 (let (started-writing ignored-special)
4024 ;; (custom-save-delete setter) Done by caller
4025 (let ((standard-output (current-buffer))
4026 (mapper `(lambda (object)
4027 (let ((spec (car-safe (get object (quote ,property)))))
4028 (when (and (not (memq object ignored-special))
4029 (eq (nth 0 spec) 'user)
4030 (eq (nth 1 spec) 'reset))
4031 ;; Do not write reset statements unless necessary.
4032 (unless started-writing
4033 (setq started-writing t)
4034 (unless (bolp)
4035 (princ "\n"))
4036 (princ "(")
4037 (princ (quote ,setter))
4038 (princ "\n '(")
4039 (prin1 object)
4040 (princ " ")
4041 (prin1 (nth 3 spec))
4042 (princ ")")))))))
4043 (mapc mapper special)
4044 (setq ignored-special special)
4045 (mapatoms mapper)
4046 (when started-writing
4047 (princ ")\n")))))
ee1f522d 4048
c942535f
RS
4049(defun custom-save-loaded-themes ()
4050 (let ((themes (reverse (get 'user 'theme-loads-themes)))
4051 (standard-output (current-buffer)))
4052 (when themes
4053 (unless (bolp) (princ "\n"))
4054 (princ "(custom-load-themes")
4055 (mapc (lambda (theme)
4056 (princ "\n '")
4057 (prin1 theme)) themes)
ee1f522d 4058 (princ " )\n"))))
d543e20b 4059
6d528fc5 4060;;;###autoload
f9dd586e 4061(defun customize-save-customized ()
6d528fc5
PA
4062 "Save all user options which have been set in this session."
4063 (interactive)
4064 (mapatoms (lambda (symbol)
4065 (let ((face (get symbol 'customized-face))
d3d4df42
DL
4066 (value (get symbol 'customized-value))
4067 (face-comment (get symbol 'customized-face-comment))
4068 (variable-comment
4069 (get symbol 'customized-variable-comment)))
4070 (when face
6d528fc5 4071 (put symbol 'saved-face face)
c942535f 4072 (custom-push-theme 'theme-face symbol 'user 'set value)
6d528fc5 4073 (put symbol 'customized-face nil))
d3d4df42 4074 (when value
6d528fc5 4075 (put symbol 'saved-value value)
c942535f 4076 (custom-push-theme 'theme-value symbol 'user 'set value)
d3d4df42
DL
4077 (put symbol 'customized-value nil))
4078 (when variable-comment
4079 (put symbol 'saved-variable-comment variable-comment)
4080 (put symbol 'customized-variable-comment nil))
4081 (when face-comment
4082 (put symbol 'saved-face-comment face-comment)
4083 (put symbol 'customized-face-comment nil)))))
6d528fc5
PA
4084 ;; We really should update all custom buffers here.
4085 (custom-save-all))
4086
d543e20b
PA
4087;;;###autoload
4088(defun custom-save-all ()
4089 "Save all customizations in `custom-file'."
4ee1cf9f
PA
4090 (let ((inhibit-read-only t))
4091 (custom-save-variables)
4092 (custom-save-faces)
4093 (save-excursion
fc4d62fe 4094 (let ((default-major-mode nil))
176eb8cb 4095 (set-buffer (find-file-noselect (custom-file))))
82d3d694
RS
4096 (let ((file-precious-flag t))
4097 (save-buffer)))))
d543e20b
PA
4098
4099;;; The Customize Menu.
4100
bd042c03
PA
4101;;; Menu support
4102
25ac13b5
PA
4103(defcustom custom-menu-nesting 2
4104 "Maximum nesting in custom menus."
4105 :type 'integer
6aaedd12 4106 :group 'custom-menu)
d543e20b
PA
4107
4108(defun custom-face-menu-create (widget symbol)
4109 "Ignoring WIDGET, create a menu entry for customization face SYMBOL."
4110 (vector (custom-unlispify-menu-entry symbol)
86bd10bc 4111 `(customize-face ',symbol)
d543e20b
PA
4112 t))
4113
4114(defun custom-variable-menu-create (widget symbol)
4115 "Ignoring WIDGET, create a menu entry for customization variable SYMBOL."
4116 (let ((type (get symbol 'custom-type)))
4117 (unless (listp type)
4118 (setq type (list type)))
4119 (if (and type (widget-get type :custom-menu))
4120 (widget-apply type :custom-menu symbol)
4121 (vector (custom-unlispify-menu-entry symbol)
86bd10bc 4122 `(customize-variable ',symbol)
d543e20b
PA
4123 t))))
4124
bd042c03 4125;; Add checkboxes to boolean variable entries.
d543e20b
PA
4126(widget-put (get 'boolean 'widget-type)
4127 :custom-menu (lambda (widget symbol)
4128 (vector (custom-unlispify-menu-entry symbol)
86bd10bc 4129 `(customize-variable ',symbol)
d543e20b
PA
4130 ':style 'toggle
4131 ':selected symbol)))
4132
d04a3972
DL
4133(defun custom-group-menu-create (widget symbol)
4134 "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
4135 `( ,(custom-unlispify-menu-entry symbol t)
4136 :filter (lambda (&rest junk)
3b2f3d30
SM
4137 (let ((menu (custom-menu-create ',symbol)))
4138 (if (consp menu) (cdr menu) menu)))))
d543e20b 4139
bd042c03
PA
4140;;;###autoload
4141(defun custom-menu-create (symbol)
d543e20b 4142 "Create menu for customization group SYMBOL.
d543e20b 4143The menu is in a format applicable to `easy-menu-define'."
bd042c03 4144 (let* ((item (vector (custom-unlispify-menu-entry symbol)
86bd10bc 4145 `(customize-group ',symbol)
bd042c03
PA
4146 t)))
4147 (if (and (or (not (boundp 'custom-menu-nesting))
4148 (>= custom-menu-nesting 0))
2de2cb02
MR
4149 (progn
4150 (custom-load-symbol symbol)
4151 (< (length (get symbol 'custom-group)) widget-menu-max-size)))
d543e20b 4152 (let ((custom-prefix-list (custom-prefix-add symbol
25ac13b5 4153 custom-prefix-list))
da5ec617
PA
4154 (members (custom-sort-items (get symbol 'custom-group)
4155 custom-menu-sort-alphabetically
4156 custom-menu-order-groups)))
d543e20b
PA
4157 `(,(custom-unlispify-menu-entry symbol t)
4158 ,item
4159 "--"
4160 ,@(mapcar (lambda (entry)
4161 (widget-apply (if (listp (nth 1 entry))
4162 (nth 1 entry)
4163 (list (nth 1 entry)))
4164 :custom-menu (nth 0 entry)))
25ac13b5 4165 members)))
d543e20b
PA
4166 item)))
4167
4168;;;###autoload
bd042c03
PA
4169(defun customize-menu-create (symbol &optional name)
4170 "Return a customize menu for customization group SYMBOL.
d3d4df42 4171If optional NAME is given, use that as the name of the menu.
bd042c03
PA
4172Otherwise the menu will be named `Customize'.
4173The format is suitable for use with `easy-menu-define'."
4174 (unless name
4175 (setq name "Customize"))
d04a3972
DL
4176 `(,name
4177 :filter (lambda (&rest junk)
3b2f3d30
SM
4178 (let ((menu (custom-menu-create ',symbol)))
4179 (if (consp menu) (cdr menu) menu)))))
d543e20b 4180
bd042c03
PA
4181;;; The Custom Mode.
4182
e4105348 4183(defvar custom-mode-map
b92aaee0
SM
4184 ;; This keymap should be dense, but a dense keymap would prevent inheriting
4185 ;; "\r" bindings from the parent map.
e4105348
SM
4186 ;; Actually, this misfeature of dense keymaps was fixed on 2001-11-26.
4187 (let ((map (make-keymap)))
4188 (set-keymap-parent map widget-keymap)
4189 (suppress-keymap map)
4190 (define-key map " " 'scroll-up)
4191 (define-key map "\177" 'scroll-down)
4192 (define-key map "\C-x\C-s" 'Custom-save)
4193 (define-key map "q" 'Custom-buffer-done)
4194 (define-key map "u" 'Custom-goto-parent)
4195 (define-key map "n" 'widget-forward)
4196 (define-key map "p" 'widget-backward)
4197 (define-key map [mouse-1] 'Custom-move-and-invoke)
4198 map)
4199 "Keymap for `custom-mode'.")
0f3335c0
RS
4200
4201(defun Custom-move-and-invoke (event)
4202 "Move to where you click, and if it is an active field, invoke it."
4203 (interactive "e")
4204 (mouse-set-point event)
4205 (if (widget-event-point event)
4206 (let* ((pos (widget-event-point event))
4207 (button (get-char-property pos 'button)))
4208 (if button
4209 (widget-button-click event)))))
bd042c03 4210
d3d4df42 4211(easy-menu-define Custom-mode-menu
bd042c03
PA
4212 custom-mode-map
4213 "Menu used in customization buffers."
4214 `("Custom"
944c91b6 4215 ,(customize-menu-create 'customize)
ab678382
RS
4216 ["Set" Custom-set t]
4217 ["Save" Custom-save t]
4218 ["Reset to Current" Custom-reset-current t]
4219 ["Reset to Saved" Custom-reset-saved t]
4220 ["Reset to Standard Settings" Custom-reset-standard t]
2a1c4b90 4221 ["Info" (Info-goto-node "(emacs)Easy Customization") t]))
bd042c03 4222
b62c92bb
RS
4223(defun Custom-goto-parent ()
4224 "Go to the parent group listed at the top of this buffer.
4225If several parents are listed, go to the first of them."
4226 (interactive)
4227 (save-excursion
4228 (goto-char (point-min))
4229 (if (search-forward "\nGo to parent group: " nil t)
4230 (let* ((button (get-char-property (point) 'button))
4231 (parent (downcase (widget-get button :tag))))
4232 (customize-group parent)))))
4233
bd042c03 4234(defcustom custom-mode-hook nil
d3d4df42 4235 "Hook called when entering Custom mode."
bd042c03 4236 :type 'hook
6aaedd12 4237 :group 'custom-buffer )
bd042c03 4238
b62c92bb
RS
4239(defun custom-state-buffer-message (widget)
4240 (if (eq (widget-get (widget-get widget :parent) :custom-state) 'modified)
4241 (message "To install your edits, invoke [State] and choose the Set operation")))
8691cfa7 4242
bd042c03
PA
4243(defun custom-mode ()
4244 "Major mode for editing customization buffers.
4245
4246The following commands are available:
4247
4248Move to next button or editable field. \\[widget-forward]
4249Move to previous button or editable field. \\[widget-backward]
4ee1cf9f
PA
4250\\<widget-field-keymap>\
4251Complete content of editable text field. \\[widget-complete]
4252\\<custom-mode-map>\
0f3335c0 4253Invoke button under the mouse pointer. \\[Custom-move-and-invoke]
25ac13b5 4254Invoke button under point. \\[widget-button-press]
ab678382
RS
4255Set all modifications. \\[Custom-set]
4256Make all modifications default. \\[Custom-save]
4257Reset all modified options. \\[Custom-reset-current]
4258Reset all modified or set options. \\[Custom-reset-saved]
4259Reset all options. \\[Custom-reset-standard]
bd042c03
PA
4260
4261Entry to this mode calls the value of `custom-mode-hook'
4262if that value is non-nil."
4263 (kill-all-local-variables)
4264 (setq major-mode 'custom-mode
4265 mode-name "Custom")
4266 (use-local-map custom-mode-map)
ab678382 4267 (easy-menu-add Custom-mode-menu)
bd042c03 4268 (make-local-variable 'custom-options)
9b7826f4 4269 (make-local-variable 'custom-local-buffer)
b62c92bb
RS
4270 (make-local-variable 'widget-documentation-face)
4271 (setq widget-documentation-face 'custom-documentation-face)
3aec85bf
RS
4272 (make-local-variable 'widget-button-face)
4273 (setq widget-button-face 'custom-button-face)
d3d4df42
DL
4274 (set (make-local-variable 'widget-button-pressed-face)
4275 'custom-button-pressed-face)
4276 (set (make-local-variable 'widget-mouse-face)
4277 'custom-button-pressed-face) ; buttons `depress' when moused
4278 ;; When possible, use relief for buttons, not bracketing. This test
4279 ;; may not be optimal.
4280 (when custom-raised-buttons
4281 (set (make-local-variable 'widget-push-button-prefix) "")
4282 (set (make-local-variable 'widget-push-button-suffix) "")
4283 (set (make-local-variable 'widget-link-prefix) "")
4284 (set (make-local-variable 'widget-link-suffix) ""))
b62c92bb 4285 (add-hook 'widget-edit-functions 'custom-state-buffer-message nil t)
bd042c03 4286 (run-hooks 'custom-mode-hook))
d543e20b 4287
7f352f86
DL
4288(put 'custom-mode 'mode-class 'special)
4289
2365594b
DL
4290(add-to-list
4291 'debug-ignored-errors
4292 "^No user options have changed defaults in recent Emacs versions$")
4293
d543e20b
PA
4294;;; The End.
4295
4296(provide 'cus-edit)
4297
f7105ede 4298;; arch-tag: 64533aa4-1b1a-48c3-8812-f9dc718e8a6f
d3d4df42 4299;;; cus-edit.el ends here