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