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