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