merging Emacs.app (NeXTstep port)
[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
edfda783 2071 '((((type x w32 mac ns) (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 2082(defface custom-button-mouse
edfda783 2083 '((((type x w32 mac ns) (class color))
85a5eb0e
CY
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
edfda783 2105 '((((type x w32 mac ns) (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 "\
edfda783 3166Macintosh OS (Carbon interface).")
a0b1a022 3167 mac)
edfda783
AR
3168 (const :format "NS "
3169 :sibling-args (:help-echo "\
3170GNUstep or Macintosh OS Cocoa interface.")
3171 ns)
d543e20b
PA
3172 (const :format "DOS "
3173 :sibling-args (:help-echo "\
3174Plain MS-DOS.")
3175 pc)
3176 (const :format "TTY%n"
3177 :sibling-args (:help-echo "\
3178Plain text terminals.")
3179 tty)))
3180 (group :sibling-args (:help-echo "\
3181Only match the frames with the specified color support.")
3182 (const :format "Class: "
3183 class)
3184 (checklist :inline t
3185 :offset 0
3186 (const :format "Color "
3187 :sibling-args (:help-echo "\
3188Match color frames.")
3189 color)
3190 (const :format "Grayscale "
3191 :sibling-args (:help-echo "\
3192Match grayscale frames.")
3193 grayscale)
3194 (const :format "Monochrome%n"
3195 :sibling-args (:help-echo "\
3196Match frames with no color support.")
3197 mono)))
3198 (group :sibling-args (:help-echo "\
c4d9734c
JL
3199The minimum number of colors the frame should support.")
3200 (const :format "" min-colors)
3201 (integer :tag "Minimum number of colors" ))
3202 (group :sibling-args (:help-echo "\
d543e20b
PA
3203Only match frames with the specified intensity.")
3204 (const :format "\
3205Background brightness: "
3206 background)
3207 (checklist :inline t
3208 :offset 0
3209 (const :format "Light "
3210 :sibling-args (:help-echo "\
3211Match frames with light backgrounds.")
3212 light)
3213 (const :format "Dark\n"
3214 :sibling-args (:help-echo "\
3215Match frames with dark backgrounds.")
2b32d1a7
MB
3216 dark)))
3217 (group :sibling-args (:help-echo "\
3218Only match frames that support the specified face attributes.")
3219 (const :format "Supports attributes:" supports)
4abe16b5 3220 (custom-face-edit :inline t :format "%n%v"))))))
d543e20b
PA
3221
3222;;; The `custom-face' Widget.
3223
d478e69d 3224(defface custom-face-tag
b5555381 3225 `((t (:weight bold :height 1.2 :inherit variable-pitch)))
d543e20b 3226 "Face used for face tags."
bd042c03 3227 :group 'custom-faces)
d478e69d
MB
3228;; backward-compatibility alias
3229(put 'custom-face-tag-face 'face-alias 'custom-face-tag)
d543e20b 3230
d64478da
KH
3231(defcustom custom-face-default-form 'selected
3232 "Default form of displaying face definition."
3233 :type '(choice (const all)
3234 (const selected)
3235 (const lisp))
cd32a7ba
DN
3236 :group 'custom-buffer
3237 :version "20.3")
d64478da 3238
d543e20b
PA
3239(define-widget 'custom-face 'custom
3240 "Customize face."
d543e20b
PA
3241 :sample-face 'custom-face-tag-face
3242 :help-echo "Set or reset this face."
23c0fb21 3243 :documentation-property #'face-doc-string
d543e20b
PA
3244 :value-create 'custom-face-value-create
3245 :action 'custom-face-action
9097aeb7 3246 :custom-category 'face
d64478da 3247 :custom-form nil ; defaults to value of `custom-face-default-form'
d543e20b 3248 :custom-set 'custom-face-set
5ae87ed4 3249 :custom-mark-to-save 'custom-face-mark-to-save
d543e20b
PA
3250 :custom-reset-current 'custom-redraw
3251 :custom-reset-saved 'custom-face-reset-saved
25ac13b5 3252 :custom-reset-standard 'custom-face-reset-standard
5ae87ed4 3253 :custom-mark-to-reset-standard 'custom-face-mark-to-reset-standard
4f985043 3254 :custom-standard-value 'custom-face-standard-value
5ae87ed4 3255 :custom-state-set-and-redraw 'custom-face-state-set-and-redraw
d543e20b
PA
3256 :custom-menu 'custom-face-menu-create)
3257
d3d4df42 3258(define-widget 'custom-face-all 'editable-list
d543e20b
PA
3259 "An editable list of display specifications and attributes."
3260 :entry-format "%i %d %v"
3261 :insert-button-args '(:help-echo "Insert new display specification here.")
3262 :append-button-args '(:help-echo "Append new display specification here.")
3263 :delete-button-args '(:help-echo "Delete this display specification.")
3264 :args '((group :format "%v" custom-display custom-face-edit)))
3265
3266(defconst custom-face-all (widget-convert 'custom-face-all)
3267 "Converted version of the `custom-face-all' widget.")
3268
3269(define-widget 'custom-display-unselected 'item
3270 "A display specification that doesn't match the selected display."
3271 :match 'custom-display-unselected-match)
3272
3273(defun custom-display-unselected-match (widget value)
3274 "Non-nil if VALUE is an unselected display specification."
86bd10bc 3275 (not (face-spec-set-match-display value (selected-frame))))
d543e20b 3276
d3d4df42 3277(define-widget 'custom-face-selected 'group
d543e20b 3278 "Edit the attributes of the selected display in a face specification."
2dfa4c57
RS
3279 :args '((choice :inline t
3280 (group :tag "With Defaults" :inline t
3281 (group (const :tag "" default)
3282 (custom-face-edit :tag " Default\n Attributes"))
3283 (repeat :format ""
3284 :inline t
3285 (group custom-display-unselected sexp))
3286 (group (sexp :format "")
3287 (custom-face-edit :tag " Overriding\n Attributes"))
3288 (repeat :format ""
3289 :inline t
3290 sexp))
3291 (group :tag "No Defaults" :inline t
3292 (repeat :format ""
3293 :inline t
3294 (group custom-display-unselected sexp))
3295 (group (sexp :format "")
3296 (custom-face-edit :tag "\n Attributes"))
3297 (repeat :format ""
3298 :inline t
3299 sexp)))))
3300
3301
d543e20b
PA
3302
3303(defconst custom-face-selected (widget-convert 'custom-face-selected)
3304 "Converted version of the `custom-face-selected' widget.")
3305
3ea051cb 3306(defun custom-filter-face-spec (spec filter-index &optional default-filter)
f5b50baa
MB
3307 "Return a canonicalized version of SPEC using.
3308FILTER-INDEX is the index in the entry for each attribute in
3309`custom-face-attributes' at which the appropriate filter function can be
3310found, and DEFAULT-FILTER is the filter to apply for attributes that
3311don't specify one."
3312 (mapcar (lambda (entry)
3313 ;; Filter a single face-spec entry
3314 (let ((tests (car entry))
3315 (unfiltered-attrs
3316 ;; Handle both old- and new-style attribute syntax
3317 (if (listp (car (cdr entry)))
3318 (car (cdr entry))
3319 (cdr entry)))
3320 (filtered-attrs nil))
3321 ;; Filter each face attribute
3322 (while unfiltered-attrs
3323 (let* ((attr (pop unfiltered-attrs))
3324 (pre-filtered-value (pop unfiltered-attrs))
3325 (filter
3326 (or (nth filter-index (assq attr custom-face-attributes))
3327 default-filter))
3328 (filtered-value
3329 (if filter
3330 (funcall filter pre-filtered-value)
3331 pre-filtered-value)))
3332 (push filtered-value filtered-attrs)
3333 (push attr filtered-attrs)))
3334 ;;
3335 (list tests filtered-attrs)))
3336 spec))
3337
3338(defun custom-pre-filter-face-spec (spec)
3339 "Return SPEC changed as necessary for editing by the face customization widget.
3340SPEC must be a full face spec."
3ea051cb 3341 (custom-filter-face-spec spec 2))
f5b50baa
MB
3342
3343(defun custom-post-filter-face-spec (spec)
3344 "Return the customized SPEC in a form suitable for setting the face."
3ea051cb 3345 (custom-filter-face-spec spec 3))
f5b50baa 3346
d543e20b 3347(defun custom-face-value-create (widget)
944c91b6
PA
3348 "Create a list of the display specifications for WIDGET."
3349 (let ((buttons (widget-get widget :buttons))
d3d4df42 3350 children
944c91b6
PA
3351 (symbol (widget-get widget :value))
3352 (tag (widget-get widget :tag))
3353 (state (widget-get widget :custom-state))
3354 (begin (point))
3355 (is-last (widget-get widget :custom-last))
3356 (prefix (widget-get widget :custom-prefix)))
3357 (unless tag
3358 (setq tag (prin1-to-string symbol)))
3359 (cond ((eq custom-buffer-style 'tree)
da5ec617 3360 (insert prefix (if is-last " `--- " " |--- "))
944c91b6 3361 (push (widget-create-child-and-convert
c953515e 3362 widget 'custom-browse-face-tag)
944c91b6
PA
3363 buttons)
3364 (insert " " tag "\n")
3365 (widget-put widget :buttons buttons))
3366 (t
3367 ;; Create tag.
3368 (insert tag)
c069a9d3 3369 (widget-specify-sample widget begin (point))
944c91b6
PA
3370 (if (eq custom-buffer-style 'face)
3371 (insert " ")
a62ebc52
MB
3372 (if (string-match "face\\'" tag)
3373 (insert ":")
3374 (insert " face: ")))
944c91b6 3375 ;; Sample.
944c91b6
PA
3376 (push (widget-create-child-and-convert widget 'item
3377 :format "(%{%t%})"
3378 :sample-face symbol
3379 :tag "sample")
3380 buttons)
3381 ;; Visibility.
3382 (insert " ")
d3d4df42 3383 (push (widget-create-child-and-convert
944c91b6
PA
3384 widget 'visibility
3385 :help-echo "Hide or show this face."
7f663295
RS
3386 :on "Hide Face"
3387 :off "Show Face"
944c91b6
PA
3388 :action 'custom-toggle-parent
3389 (not (eq state 'hidden)))
3390 buttons)
3391 ;; Magic.
3392 (insert "\n")
3393 (let ((magic (widget-create-child-and-convert
3394 widget 'custom-magic nil)))
3395 (widget-put widget :custom-magic magic)
3396 (push magic buttons))
3397 ;; Update buttons.
3398 (widget-put widget :buttons buttons)
3399 ;; Insert documentation.
5ae87ed4 3400 (widget-put widget :documentation-indent 3)
12bafdaa
CY
3401 (widget-add-documentation-string-button
3402 widget :visibility-widget 'custom-visibility)
3403
d3d4df42
DL
3404 ;; The comment field
3405 (unless (eq state 'hidden)
3406 (let* ((comment (get symbol 'face-comment))
3407 (comment-widget
3408 (widget-create-child-and-convert
3409 widget 'custom-comment
3410 :parent widget
3411 :value (or comment ""))))
3412 (widget-put widget :comment-widget comment-widget)
3413 (push comment-widget children)))
944c91b6
PA
3414 ;; See also.
3415 (unless (eq state 'hidden)
3416 (when (eq (widget-get widget :custom-level) 1)
3417 (custom-add-parent-links widget))
3418 (custom-add-see-also widget))
3419 ;; Editor.
3420 (unless (eq (preceding-char) ?\n)
3421 (insert "\n"))
3422 (unless (eq state 'hidden)
3423 (message "Creating face editor...")
3424 (custom-load-widget widget)
d64478da
KH
3425 (unless (widget-get widget :custom-form)
3426 (widget-put widget :custom-form custom-face-default-form))
944c91b6 3427 (let* ((symbol (widget-value widget))
61763509
PA
3428 (spec (or (get symbol 'customized-face)
3429 (get symbol 'saved-face)
944c91b6
PA
3430 (get symbol 'face-defface-spec)
3431 ;; Attempt to construct it.
d3d4df42 3432 (list (list t (custom-face-attributes-get
944c91b6
PA
3433 symbol (selected-frame))))))
3434 (form (widget-get widget :custom-form))
3435 (indent (widget-get widget :indent))
fa0b3d46
RS
3436 edit)
3437 ;; If the user has changed this face in some other way,
3438 ;; edit it as the user has specified it.
3439 (if (not (face-spec-match-p symbol spec (selected-frame)))
3440 (setq spec (list (list t (face-attr-construct symbol (selected-frame))))))
f5b50baa 3441 (setq spec (custom-pre-filter-face-spec spec))
fa0b3d46 3442 (setq edit (widget-create-child-and-convert
944c91b6
PA
3443 widget
3444 (cond ((and (eq form 'selected)
d3d4df42 3445 (widget-apply custom-face-selected
944c91b6
PA
3446 :match spec))
3447 (when indent (insert-char ?\ indent))
3448 'custom-face-selected)
3449 ((and (not (eq form 'lisp))
3450 (widget-apply custom-face-all
3451 :match spec))
3452 'custom-face-all)
d3d4df42 3453 (t
944c91b6
PA
3454 (when indent (insert-char ?\ indent))
3455 'sexp))
fa0b3d46 3456 :value spec))
944c91b6 3457 (custom-face-state-set widget)
d3d4df42
DL
3458 (push edit children)
3459 (widget-put widget :children children))
944c91b6 3460 (message "Creating face editor...done"))))))
d543e20b 3461
d3d4df42 3462(defvar custom-face-menu
70482877 3463 `(("Set for Current Session" custom-face-set)
9b5007e7 3464 ,@(when (or custom-file init-file-user)
633bb8a9 3465 '(("Save for Future Sessions" custom-face-save)))
70482877
LT
3466 ("Undo Edits" custom-redraw
3467 (lambda (widget)
3468 (memq (widget-get widget :custom-state) '(modified changed))))
3469 ("Reset to Saved" custom-face-reset-saved
0c731046
CY
3470 (lambda (widget)
3471 (or (get (widget-value widget) 'saved-face)
3472 (get (widget-value widget) 'saved-face-comment))))
9b5007e7 3473 ,@(when (or custom-file init-file-user)
70482877 3474 '(("Erase Customization" custom-face-reset-standard
73e60f53
CY
3475 (lambda (widget)
3476 (get (widget-value widget) 'face-defface-spec)))))
eaa99205 3477 ("---" ignore ignore)
70482877
LT
3478 ("Add Comment" custom-comment-show custom-comment-invisible-p)
3479 ("---" ignore ignore)
3480 ("For Current Display" custom-face-edit-selected
eaa99205
CY
3481 (lambda (widget)
3482 (not (eq (widget-get widget :custom-form) 'selected))))
70482877
LT
3483 ("For All Kinds of Displays" custom-face-edit-all
3484 (lambda (widget)
3485 (not (eq (widget-get widget :custom-form) 'all))))
3486 ("Show Lisp Expression" custom-face-edit-lisp
eaa99205
CY
3487 (lambda (widget)
3488 (not (eq (widget-get widget :custom-form) 'lisp)))))
d543e20b 3489 "Alist of actions for the `custom-face' widget.
6d528fc5
PA
3490Each entry has the form (NAME ACTION FILTER) where NAME is the name of
3491the menu entry, ACTION is the function to call on the widget when the
3492menu is selected, and FILTER is a predicate which takes a `custom-face'
3493widget as an argument, and returns non-nil if ACTION is valid on that
19d63704 3494widget. If FILTER is nil, ACTION is always valid.")
d543e20b
PA
3495
3496(defun custom-face-edit-selected (widget)
3497 "Edit selected attributes of the value of WIDGET."
3498 (widget-put widget :custom-state 'unknown)
3499 (widget-put widget :custom-form 'selected)
3500 (custom-redraw widget))
3501
3502(defun custom-face-edit-all (widget)
3503 "Edit all attributes of the value of WIDGET."
3504 (widget-put widget :custom-state 'unknown)
3505 (widget-put widget :custom-form 'all)
3506 (custom-redraw widget))
3507
3508(defun custom-face-edit-lisp (widget)
2365594b 3509 "Edit the Lisp representation of the value of WIDGET."
d543e20b
PA
3510 (widget-put widget :custom-state 'unknown)
3511 (widget-put widget :custom-form 'lisp)
3512 (custom-redraw widget))
3513
3514(defun custom-face-state-set (widget)
3515 "Set the state of WIDGET."
d3d4df42
DL
3516 (let* ((symbol (widget-value widget))
3517 (comment (get symbol 'face-comment))
a4992f73
RS
3518 tmp temp
3519 (state
3520 (cond ((progn
3521 (setq tmp (get symbol 'customized-face))
3522 (setq temp (get symbol 'customized-face-comment))
3523 (or tmp temp))
3524 (if (equal temp comment)
3525 'set
3526 'changed))
3527 ((progn
3528 (setq tmp (get symbol 'saved-face))
3529 (setq temp (get symbol 'saved-face-comment))
3530 (or tmp temp))
3531 (if (equal temp comment)
70128967
CY
3532 (cond
3533 ((eq 'user (caar (get symbol 'theme-face)))
3534 'saved)
d358aa10 3535 ((eq 'changed (caar (get symbol 'theme-face)))
70128967
CY
3536 'changed)
3537 (t 'themed))
a4992f73
RS
3538 'changed))
3539 ((get symbol 'face-defface-spec)
3540 (if (equal comment nil)
3541 'standard
3542 'changed))
3543 (t
3544 'rogue))))
3545 ;; If the user called set-face-attribute to change the default
3546 ;; for new frames, this face is "set outside of Customize".
3547 (if (and (not (eq state 'rogue))
3548 (get symbol 'face-modified))
3549 (setq state 'changed))
3550 (widget-put widget :custom-state state)))
d543e20b
PA
3551
3552(defun custom-face-action (widget &optional event)
3553 "Show the menu for `custom-face' WIDGET.
3554Optional EVENT is the location for the menu."
3555 (if (eq (widget-get widget :custom-state) 'hidden)
6d528fc5 3556 (custom-toggle-hide widget)
d543e20b
PA
3557 (let* ((completion-ignore-case t)
3558 (symbol (widget-get widget :value))
25ac13b5
PA
3559 (answer (widget-choose (concat "Operation on "
3560 (custom-unlispify-tag-name symbol))
6d528fc5
PA
3561 (custom-menu-filter custom-face-menu
3562 widget)
3563 event)))
d543e20b
PA
3564 (if answer
3565 (funcall answer widget)))))
3566
3567(defun custom-face-set (widget)
3568 "Make the face attributes in WIDGET take effect."
3569 (let* ((symbol (widget-value widget))
3570 (child (car (widget-get widget :children)))
f5b50baa 3571 (value (custom-post-filter-face-spec (widget-value child)))
d3d4df42
DL
3572 (comment-widget (widget-get widget :comment-widget))
3573 (comment (widget-value comment-widget)))
3574 (when (equal comment "")
3575 (setq comment nil)
3576 ;; Make the comment invisible by hand if it's empty
164cfaeb 3577 (custom-comment-hide comment-widget))
d543e20b 3578 (put symbol 'customized-face value)
e02577b7 3579 (custom-push-theme 'theme-face symbol 'user 'set value)
f5b50baa 3580 (if (face-spec-choose value)
d50e9d5b 3581 (face-spec-set symbol value t)
f5b50baa
MB
3582 ;; face-set-spec ignores empty attribute lists, so just give it
3583 ;; something harmless instead.
d50e9d5b 3584 (face-spec-set symbol '((t :foreground unspecified)) t))
d3d4df42
DL
3585 (put symbol 'customized-face-comment comment)
3586 (put symbol 'face-comment comment)
d543e20b
PA
3587 (custom-face-state-set widget)
3588 (custom-redraw-magic widget)))
3589
5ae87ed4
MR
3590(defun custom-face-mark-to-save (widget)
3591 "Mark for saving the face edited by WIDGET."
d543e20b
PA
3592 (let* ((symbol (widget-value widget))
3593 (child (car (widget-get widget :children)))
e475612a 3594 (value (custom-post-filter-face-spec (widget-value child)))
d3d4df42
DL
3595 (comment-widget (widget-get widget :comment-widget))
3596 (comment (widget-value comment-widget)))
3597 (when (equal comment "")
3598 (setq comment nil)
3599 ;; Make the comment invisible by hand if it's empty
164cfaeb 3600 (custom-comment-hide comment-widget))
b76747af 3601 (custom-push-theme 'theme-face symbol 'user 'set value)
e475612a 3602 (if (face-spec-choose value)
d50e9d5b 3603 (face-spec-set symbol value t)
e475612a
MB
3604 ;; face-set-spec ignores empty attribute lists, so just give it
3605 ;; something harmless instead.
d50e9d5b 3606 (face-spec-set symbol '((t :foreground unspecified)) t))
4f985043
RS
3607 (unless (eq (widget-get widget :custom-state) 'standard)
3608 (put symbol 'saved-face value))
d543e20b 3609 (put symbol 'customized-face nil)
d3d4df42
DL
3610 (put symbol 'face-comment comment)
3611 (put symbol 'customized-face-comment nil)
5ae87ed4
MR
3612 (put symbol 'saved-face-comment comment)))
3613
3614(defsubst custom-face-state-set-and-redraw (widget)
3615 "Set state of face widget WIDGET and redraw with current settings."
3616 (custom-face-state-set widget)
3617 (custom-redraw-magic widget))
3618
3619(defun custom-face-save (widget)
3620 "Save the face edited by WIDGET."
3621 (custom-face-mark-to-save widget)
3622 (custom-save-all)
3623 (custom-face-state-set-and-redraw widget))
d543e20b 3624
633bb8a9
LT
3625;; For backward compatibility.
3626(define-obsolete-function-alias 'custom-face-save-command 'custom-face-save
3627 "22.1")
3628
d543e20b
PA
3629(defun custom-face-reset-saved (widget)
3630 "Restore WIDGET to the face's default attributes."
3631 (let* ((symbol (widget-value widget))
3632 (child (car (widget-get widget :children)))
d3d4df42
DL
3633 (value (get symbol 'saved-face))
3634 (comment (get symbol 'saved-face-comment))
3635 (comment-widget (widget-get widget :comment-widget)))
3636 (unless (or value comment)
d543e20b
PA
3637 (error "No saved value for this face"))
3638 (put symbol 'customized-face nil)
d3d4df42 3639 (put symbol 'customized-face-comment nil)
25c6d447 3640 (custom-push-theme 'theme-face symbol 'user 'set value)
d50e9d5b 3641 (face-spec-set symbol value t)
d3d4df42 3642 (put symbol 'face-comment comment)
d543e20b 3643 (widget-value-set child value)
d3d4df42
DL
3644 ;; This call manages the comment visibility
3645 (widget-value-set comment-widget (or comment ""))
d543e20b
PA
3646 (custom-face-state-set widget)
3647 (custom-redraw-magic widget)))
3648
4f985043
RS
3649(defun custom-face-standard-value (widget)
3650 (get (widget-value widget) 'face-defface-spec))
3651
5ae87ed4
MR
3652(defun custom-face-mark-to-reset-standard (widget)
3653 "Restore widget WIDGET to the face's standard attribute values.
3654If `custom-reset-standard-faces-list' is nil, save, reset and
3655redraw the widget immediately."
d543e20b
PA
3656 (let* ((symbol (widget-value widget))
3657 (child (car (widget-get widget :children)))
d3d4df42
DL
3658 (value (get symbol 'face-defface-spec))
3659 (comment-widget (widget-get widget :comment-widget)))
d543e20b 3660 (unless value
5dd0cad0 3661 (error "No standard setting for this face"))
d543e20b 3662 (put symbol 'customized-face nil)
d3d4df42 3663 (put symbol 'customized-face-comment nil)
d358aa10 3664 (custom-push-theme 'theme-face symbol 'user 'reset)
d50e9d5b 3665 (face-spec-set symbol value t)
25c6d447 3666 (custom-theme-recalc-face symbol)
5ae87ed4
MR
3667 (if (and custom-reset-standard-faces-list
3668 (or (get symbol 'saved-face) (get symbol 'saved-face-comment)))
3669 ;; Do this later.
3670 (progn
3671 (put symbol 'saved-face nil)
3672 (put symbol 'saved-face-comment nil)
3673 ;; Append this to `custom-reset-standard-faces-list' and have
3674 ;; `custom-reset-standard-save-and-update' save setting to the
3675 ;; file, update the widget's state, and redraw it.
3676 (setq custom-reset-standard-faces-list
3677 (cons widget custom-reset-standard-faces-list)))
3678 (when (or (get symbol 'saved-face) (get symbol 'saved-face-comment))
3679 (put symbol 'saved-face nil)
3680 (put symbol 'saved-face-comment nil)
3681 (custom-save-all))
3682 (put symbol 'face-comment nil)
3683 (widget-value-set child
3684 (custom-pre-filter-face-spec
3685 (list (list t (custom-face-attributes-get
3686 symbol nil)))))
3687 ;; This call manages the comment visibility
3688 (widget-value-set comment-widget "")
3689 (custom-face-state-set widget)
3690 (custom-redraw-magic widget))))
3691
3692(defun custom-face-reset-standard (widget)
3693 "Restore WIDGET to the face's standard attribute values.
3694This operation eliminates any saved attributes for the face,
3695restoring it to the state of a face that has never been customized."
3696 (let (custom-reset-standard-faces-list)
3697 (custom-face-mark-to-reset-standard widget)))
d543e20b
PA
3698
3699;;; The `face' Widget.
3700
1833b7b3
RS
3701(defvar widget-face-prompt-value-history nil
3702 "History of input to `widget-face-prompt-value'.")
3703
0e739597
DP
3704(define-widget 'face 'symbol
3705 "A Lisp face name (with sample)."
49698bb7 3706 :format "%{%t%}: (%{sample%}) %v"
0e739597
DP
3707 :tag "Face"
3708 :value 'default
3709 :sample-face-get 'widget-face-sample-face-get
3710 :notify 'widget-face-notify
3711 :match (lambda (widget value) (facep value))
1833b7b3
RS
3712 :complete-function (lambda ()
3713 (interactive)
3714 (lisp-complete-symbol 'facep))
1833b7b3
RS
3715 :prompt-match 'facep
3716 :prompt-history 'widget-face-prompt-value-history
1833b7b3
RS
3717 :validate (lambda (widget)
3718 (unless (facep (widget-value widget))
0e739597
DP
3719 (widget-put widget
3720 :error (format "Invalid face: %S"
3721 (widget-value widget)))
3722 widget)))
3723
3724(defun widget-face-sample-face-get (widget)
3725 (let ((value (widget-value widget)))
3726 (if (facep value)
3727 value
3728 'default)))
3729
3730(defun widget-face-notify (widget child &optional event)
3731 "Update the sample, and notify the parent."
3732 (overlay-put (widget-get widget :sample-overlay)
3733 'face (widget-apply widget :sample-face-get))
3734 (widget-default-notify widget child event))
1833b7b3 3735
d543e20b
PA
3736
3737;;; The `hook' Widget.
3738
3739(define-widget 'hook 'list
c8c9333c 3740 "An Emacs Lisp hook."
f985c5f7 3741 :value-to-internal (lambda (widget value)
5aa3f181 3742 (if (and value (symbolp value))
f985c5f7
PA
3743 (list value)
3744 value))
3745 :match (lambda (widget value)
3746 (or (symbolp value)
4743fc91 3747 (widget-group-match widget value)))
2365594b
DL
3748 ;; Avoid adding undefined functions to the hook, especially for
3749 ;; things like `find-file-hook' or even more basic ones, to avoid
3750 ;; chaos.
3751 :set (lambda (symbol value)
d4881668
SM
3752 (dolist (elt value)
3753 (if (fboundp elt)
3754 (add-hook symbol elt))))
d543e20b
PA
3755 :convert-widget 'custom-hook-convert-widget
3756 :tag "Hook")
3757
3758(defun custom-hook-convert-widget (widget)
3c708e98 3759 ;; Handle `:options'.
d543e20b 3760 (let* ((options (widget-get widget :options))
d3d4df42 3761 (other `(editable-list :inline t
d543e20b
PA
3762 :entry-format "%i %d%v"
3763 (function :format " %v")))
3764 (args (if options
3765 (list `(checklist :inline t
3766 ,@(mapcar (lambda (entry)
3767 `(function-item ,entry))
3768 options))
3769 other)
3770 (list other))))
3771 (widget-put widget :args args)
3772 widget))
3773
944c91b6
PA
3774;;; The `custom-group-link' Widget.
3775
3776(define-widget 'custom-group-link 'link
3777 "Show parent in other window when activated."
192e44fc
JL
3778 :button-face 'custom-link
3779 :mouse-face 'highlight
e1ec62a5 3780 :pressed-face 'highlight
b62c92bb 3781 :help-echo "Create customization buffer for this group."
f3093f77
CY
3782 :keymap custom-mode-link-map
3783 :follow-link 'mouse-face
944c91b6
PA
3784 :action 'custom-group-link-action)
3785
3786(defun custom-group-link-action (widget &rest ignore)
3787 (customize-group (widget-value widget)))
3788
d543e20b
PA
3789;;; The `custom-group' Widget.
3790
b62c92bb 3791(defcustom custom-group-tag-faces nil
d543e20b 3792 ;; In XEmacs, this ought to play games with font size.
d3d4df42 3793 ;; Fixme: make it do so in Emacs.
d543e20b
PA
3794 "Face used for group tags.
3795The first member is used for level 1 groups, the second for level 2,
d478e69d 3796and so forth. The remaining group tags are shown with `custom-group-tag'."
d543e20b 3797 :type '(repeat face)
bd042c03 3798 :group 'custom-faces)
d543e20b 3799
d478e69d 3800(defface custom-group-tag-1
16b20ed9
GM
3801 `((((class color)
3802 (background dark))
b5555381 3803 (:foreground "pink" :weight bold :height 1.2 :inherit variable-pitch))
ea81d57e
DN
3804 (((min-colors 88) (class color)
3805 (background light))
3806 (:foreground "red1" :weight bold :height 1.2 :inherit variable-pitch))
16b20ed9
GM
3807 (((class color)
3808 (background light))
b5555381
RS
3809 (:foreground "red" :weight bold :height 1.2 :inherit variable-pitch))
3810 (t (:weight bold)))
16b20ed9
GM
3811 "Face used for group tags."
3812 :group 'custom-faces)
d478e69d
MB
3813;; backward-compatibility alias
3814(put 'custom-group-tag-face-1 'face-alias 'custom-group-tag-1)
16b20ed9 3815
d478e69d 3816(defface custom-group-tag
16b20ed9
GM
3817 `((((class color)
3818 (background dark))
9db1942d 3819 (:foreground "light blue" :weight bold :height 1.2 :inherit variable-pitch))
ea81d57e
DN
3820 (((min-colors 88) (class color)
3821 (background light))
9db1942d 3822 (:foreground "blue1" :weight bold :height 1.2 :inherit variable-pitch))
16b20ed9
GM
3823 (((class color)
3824 (background light))
9db1942d 3825 (:foreground "blue" :weight bold :height 1.2 :inherit variable-pitch))
b5555381 3826 (t (:weight bold)))
d543e20b 3827 "Face used for low level group tags."
bd042c03 3828 :group 'custom-faces)
d478e69d
MB
3829;; backward-compatibility alias
3830(put 'custom-group-tag-face 'face-alias 'custom-group-tag)
d543e20b
PA
3831
3832(define-widget 'custom-group 'custom
3833 "Customize group."
944c91b6 3834 :format "%v"
d543e20b
PA
3835 :sample-face-get 'custom-group-sample-face-get
3836 :documentation-property 'group-documentation
3837 :help-echo "Set or reset all members of this group."
3838 :value-create 'custom-group-value-create
3839 :action 'custom-group-action
9097aeb7 3840 :custom-category 'group
d543e20b 3841 :custom-set 'custom-group-set
5ae87ed4 3842 :custom-mark-to-save 'custom-group-mark-to-save
d543e20b
PA
3843 :custom-reset-current 'custom-group-reset-current
3844 :custom-reset-saved 'custom-group-reset-saved
25ac13b5 3845 :custom-reset-standard 'custom-group-reset-standard
5ae87ed4
MR
3846 :custom-mark-to-reset-standard 'custom-group-mark-to-reset-standard
3847 :custom-state-set-and-redraw 'custom-group-state-set-and-redraw
d543e20b
PA
3848 :custom-menu 'custom-group-menu-create)
3849
3850(defun custom-group-sample-face-get (widget)
3851 ;; Use :sample-face.
3852 (or (nth (1- (widget-get widget :custom-level)) custom-group-tag-faces)
d478e69d 3853 'custom-group-tag))
d543e20b 3854
8691cfa7
RS
3855(define-widget 'custom-group-visibility 'visibility
3856 "An indicator and manipulator for hidden group contents."
3857 :create 'custom-group-visibility-create)
3858
3859(defun custom-group-visibility-create (widget)
3860 (let ((visible (widget-value widget)))
3861 (if visible
3862 (insert "--------")))
3863 (widget-default-create widget))
3864
4ee1cf9f
PA
3865(defun custom-group-members (symbol groups-only)
3866 "Return SYMBOL's custom group members.
3867If GROUPS-ONLY non-nil, return only those members that are groups."
3868 (if (not groups-only)
3869 (get symbol 'custom-group)
3870 (let (members)
3871 (dolist (entry (get symbol 'custom-group))
3872 (when (eq (nth 1 entry) 'custom-group)
3873 (push entry members)))
3874 (nreverse members))))
3875
d543e20b 3876(defun custom-group-value-create (widget)
944c91b6 3877 "Insert a customize group for WIDGET in the current buffer."
2de2cb02 3878 (unless (eq (widget-get widget :custom-state) 'hidden)
2ee398c4 3879 (custom-load-widget widget))
4ee1cf9f
PA
3880 (let* ((state (widget-get widget :custom-state))
3881 (level (widget-get widget :custom-level))
f985c5f7 3882 ;; (indent (widget-get widget :indent))
4ee1cf9f
PA
3883 (prefix (widget-get widget :custom-prefix))
3884 (buttons (widget-get widget :buttons))
3885 (tag (widget-get widget :tag))
3886 (symbol (widget-value widget))
3887 (members (custom-group-members symbol
3888 (and (eq custom-buffer-style 'tree)
3889 custom-browse-only-groups))))
944c91b6 3890 (cond ((and (eq custom-buffer-style 'tree)
c953515e 3891 (eq state 'hidden)
4ee1cf9f 3892 (or members (custom-unloaded-widget-p widget)))
c953515e 3893 (custom-browse-insert-prefix prefix)
944c91b6 3894 (push (widget-create-child-and-convert
d3d4df42 3895 widget 'custom-browse-visibility
da5ec617 3896 ;; :tag-glyph "plus"
df816618 3897 :tag "+")
944c91b6
PA
3898 buttons)
3899 (insert "-- ")
da5ec617 3900 ;; (widget-glyph-insert nil "-- " "horizontal")
944c91b6 3901 (push (widget-create-child-and-convert
c953515e 3902 widget 'custom-browse-group-tag)
944c91b6
PA
3903 buttons)
3904 (insert " " tag "\n")
3905 (widget-put widget :buttons buttons))
3906 ((and (eq custom-buffer-style 'tree)
4ee1cf9f 3907 (zerop (length members)))
c953515e 3908 (custom-browse-insert-prefix prefix)
da5ec617
PA
3909 (insert "[ ]-- ")
3910 ;; (widget-glyph-insert nil "[ ]" "empty")
3911 ;; (widget-glyph-insert nil "-- " "horizontal")
d3d4df42 3912 (push (widget-create-child-and-convert
c953515e 3913 widget 'custom-browse-group-tag)
944c91b6
PA
3914 buttons)
3915 (insert " " tag "\n")
3916 (widget-put widget :buttons buttons))
3917 ((eq custom-buffer-style 'tree)
c953515e 3918 (custom-browse-insert-prefix prefix)
4ee1cf9f 3919 (if (zerop (length members))
d3d4df42 3920 (progn
c953515e 3921 (custom-browse-insert-prefix prefix)
da5ec617
PA
3922 (insert "[ ]-- ")
3923 ;; (widget-glyph-insert nil "[ ]" "empty")
3924 ;; (widget-glyph-insert nil "-- " "horizontal")
d3d4df42 3925 (push (widget-create-child-and-convert
c953515e 3926 widget 'custom-browse-group-tag)
944c91b6
PA
3927 buttons)
3928 (insert " " tag "\n")
3929 (widget-put widget :buttons buttons))
d3d4df42
DL
3930 (push (widget-create-child-and-convert
3931 widget 'custom-browse-visibility
da5ec617
PA
3932 ;; :tag-glyph "minus"
3933 :tag "-")
944c91b6 3934 buttons)
da5ec617
PA
3935 (insert "-\\ ")
3936 ;; (widget-glyph-insert nil "-\\ " "top")
d3d4df42 3937 (push (widget-create-child-and-convert
c953515e 3938 widget 'custom-browse-group-tag)
944c91b6
PA
3939 buttons)
3940 (insert " " tag "\n")
3941 (widget-put widget :buttons buttons)
3942 (message "Creating group...")
4ee1cf9f 3943 (let* ((members (custom-sort-items members
da5ec617
PA
3944 custom-browse-sort-alphabetically
3945 custom-browse-order-groups))
944c91b6
PA
3946 (prefixes (widget-get widget :custom-prefixes))
3947 (custom-prefix-list (custom-prefix-add symbol prefixes))
944c91b6
PA
3948 (extra-prefix (if (widget-get widget :custom-last)
3949 " "
3950 " | "))
3951 (prefix (concat prefix extra-prefix))
3952 children entry)
3953 (while members
3954 (setq entry (car members)
3955 members (cdr members))
4ee1cf9f
PA
3956 (push (widget-create-child-and-convert
3957 widget (nth 1 entry)
3958 :group widget
3959 :tag (custom-unlispify-tag-name (nth 0 entry))
3960 :custom-prefixes custom-prefix-list
3961 :custom-level (1+ level)
3962 :custom-last (null members)
3963 :value (nth 0 entry)
3964 :custom-prefix prefix)
3965 children))
944c91b6
PA
3966 (widget-put widget :children (reverse children)))
3967 (message "Creating group...done")))
3968 ;; Nested style.
3969 ((eq state 'hidden)
3970 ;; Create level indicator.
944c91b6 3971 ;; Create tag.
944c91b6
PA
3972 (if (eq custom-buffer-style 'links)
3973 (push (widget-create-child-and-convert
d3d4df42 3974 widget 'custom-group-link
9db1942d 3975 :tag tag
944c91b6
PA
3976 symbol)
3977 buttons)
9db1942d
CY
3978 (insert-char ?\ (* custom-buffer-indent (1- level)))
3979 (insert "-- ")
d3d4df42 3980 (push (widget-create-child-and-convert
98d5aafe 3981 widget 'custom-group-visibility
944c91b6
PA
3982 :help-echo "Show members of this group."
3983 :action 'custom-toggle-parent
3984 (not (eq state 'hidden)))
3985 buttons))
9db1942d 3986 (insert " : ")
944c91b6
PA
3987 ;; Create magic button.
3988 (let ((magic (widget-create-child-and-convert
3989 widget 'custom-magic nil)))
3990 (widget-put widget :custom-magic magic)
3991 (push magic buttons))
3992 ;; Update buttons.
3993 (widget-put widget :buttons buttons)
3994 ;; Insert documentation.
26c7b3ef
RS
3995 (if (and (eq custom-buffer-style 'links) (> level 1))
3996 (widget-put widget :documentation-indent 0))
12bafdaa
CY
3997 (widget-add-documentation-string-button
3998 widget :visibility-widget 'custom-visibility))
3999
944c91b6
PA
4000 ;; Nested style.
4001 (t ;Visible.
d377bee9
RS
4002 ;; Add parent groups references above the group.
4003 (if t ;;; This should test that the buffer
4004 ;;; was made to display a group.
4005 (when (eq level 1)
cd6c0940 4006 (if (custom-add-parent-links widget
f1201e3a
RS
4007 "Parent groups:"
4008 "Parent group documentation:")
d377bee9 4009 (insert "\n"))))
944c91b6
PA
4010 ;; Create level indicator.
4011 (insert-char ?\ (* custom-buffer-indent (1- level)))
4012 (insert "/- ")
4013 ;; Create tag.
4014 (let ((start (point)))
9db1942d 4015 (insert tag " group: ")
944c91b6 4016 (widget-specify-sample widget start (point)))
9db1942d 4017 (insert (widget-docstring widget))
944c91b6
PA
4018 ;; Create visibility indicator.
4019 (unless (eq custom-buffer-style 'links)
4020 (insert "--------")
d3d4df42 4021 (push (widget-create-child-and-convert
944c91b6
PA
4022 widget 'visibility
4023 :help-echo "Hide members of this group."
4024 :action 'custom-toggle-parent
4025 (not (eq state 'hidden)))
4026 buttons)
4027 (insert " "))
4028 ;; Create more dashes.
4029 ;; Use 76 instead of 75 to compensate for the temporary "<"
d3d4df42 4030 ;; added by `widget-insert'.
944c91b6
PA
4031 (insert-char ?- (- 76 (current-column)
4032 (* custom-buffer-indent level)))
4033 (insert "\\\n")
4034 ;; Create magic button.
4035 (let ((magic (widget-create-child-and-convert
d3d4df42 4036 widget 'custom-magic
944c91b6
PA
4037 :indent 0
4038 nil)))
4039 (widget-put widget :custom-magic magic)
4040 (push magic buttons))
4041 ;; Update buttons.
4042 (widget-put widget :buttons buttons)
4043 ;; Insert documentation.
12bafdaa
CY
4044 (widget-add-documentation-string-button
4045 widget :visibility-widget 'custom-visibility)
4046
d377bee9
RS
4047 ;; Parent groups.
4048 (if nil ;;; This should test that the buffer
4049 ;;; was not made to display a group.
4050 (when (eq level 1)
4051 (insert-char ?\ custom-buffer-indent)
4052 (custom-add-parent-links widget)))
d3d4df42 4053 (custom-add-see-also widget
944c91b6
PA
4054 (make-string (* custom-buffer-indent level)
4055 ?\ ))
4056 ;; Members.
4057 (message "Creating group...")
4ee1cf9f 4058 (let* ((members (custom-sort-items members
da5ec617
PA
4059 custom-buffer-sort-alphabetically
4060 custom-buffer-order-groups))
944c91b6
PA
4061 (prefixes (widget-get widget :custom-prefixes))
4062 (custom-prefix-list (custom-prefix-add symbol prefixes))
4063 (length (length members))
4064 (count 0)
4065 (children (mapcar (lambda (entry)
4066 (widget-insert "\n")
4067 (message "\
4068Creating group members... %2d%%"
4069 (/ (* 100.0 count) length))
4070 (setq count (1+ count))
4071 (prog1
4072 (widget-create-child-and-convert
4073 widget (nth 1 entry)
4074 :group widget
4075 :tag (custom-unlispify-tag-name
4076 (nth 0 entry))
4077 :custom-prefixes custom-prefix-list
4078 :custom-level (1+ level)
4079 :value (nth 0 entry))
4080 (unless (eq (preceding-char) ?\n)
4081 (widget-insert "\n"))))
4082 members)))
4083 (message "Creating group magic...")
fadbdfea 4084 (mapc 'custom-magic-reset children)
944c91b6
PA
4085 (message "Creating group state...")
4086 (widget-put widget :children children)
4087 (custom-group-state-update widget)
4088 (message "Creating group... done"))
4089 ;; End line
4090 (insert "\n")
4091 (insert-char ?\ (* custom-buffer-indent (1- level)))
4092 (insert "\\- " (widget-get widget :tag) " group end ")
4093 (insert-char ?- (- 75 (current-column) (* custom-buffer-indent level)))
4094 (insert "/\n")))))
d543e20b 4095
d3d4df42 4096(defvar custom-group-menu
70482877 4097 `(("Set for Current Session" custom-group-set
eaa99205
CY
4098 (lambda (widget)
4099 (eq (widget-get widget :custom-state) 'modified)))
9b5007e7 4100 ,@(when (or custom-file init-file-user)
70482877 4101 '(("Save for Future Sessions" custom-group-save
eaa99205
CY
4102 (lambda (widget)
4103 (memq (widget-get widget :custom-state) '(modified set))))))
70482877 4104 ("Undo Edits" custom-group-reset-current
6d528fc5 4105 (lambda (widget)
86bd10bc 4106 (memq (widget-get widget :custom-state) '(modified))))
70482877 4107 ("Reset to Saved" custom-group-reset-saved
6d528fc5 4108 (lambda (widget)
86bd10bc 4109 (memq (widget-get widget :custom-state) '(modified set))))
9b5007e7 4110 ,@(when (or custom-file init-file-user)
70482877 4111 '(("Erase Customization" custom-group-reset-standard
73e60f53 4112 (lambda (widget)
eaa99205 4113 (memq (widget-get widget :custom-state) '(modified set saved)))))))
d543e20b 4114 "Alist of actions for the `custom-group' widget.
6d528fc5
PA
4115Each entry has the form (NAME ACTION FILTER) where NAME is the name of
4116the menu entry, ACTION is the function to call on the widget when the
4117menu is selected, and FILTER is a predicate which takes a `custom-group'
4118widget as an argument, and returns non-nil if ACTION is valid on that
d3d4df42 4119widget. If FILTER is nil, ACTION is always valid.")
d543e20b
PA
4120
4121(defun custom-group-action (widget &optional event)
4122 "Show the menu for `custom-group' WIDGET.
4123Optional EVENT is the location for the menu."
4124 (if (eq (widget-get widget :custom-state) 'hidden)
6d528fc5 4125 (custom-toggle-hide widget)
d543e20b 4126 (let* ((completion-ignore-case t)
25ac13b5
PA
4127 (answer (widget-choose (concat "Operation on "
4128 (custom-unlispify-tag-name
4129 (widget-get widget :value)))
6d528fc5
PA
4130 (custom-menu-filter custom-group-menu
4131 widget)
d543e20b
PA
4132 event)))
4133 (if answer
4134 (funcall answer widget)))))
4135
4136(defun custom-group-set (widget)
4137 "Set changes in all modified group members."
9db1942d
CY
4138 (dolist (child (widget-get widget :children))
4139 (when (eq (widget-get child :custom-state) 'modified)
4140 (widget-apply child :custom-set))))
d543e20b 4141
5ae87ed4
MR
4142(defun custom-group-mark-to-save (widget)
4143 "Mark all modified group members for saving."
ed8c9cf1 4144 (dolist (child (widget-get widget :children))
9db1942d 4145 (when (memq (widget-get child :custom-state) '(modified set))
5ae87ed4
MR
4146 (widget-apply child :custom-mark-to-save))))
4147
4148(defsubst custom-group-state-set-and-redraw (widget)
4149 "Set state of group widget WIDGET and redraw with current settings."
4150 (dolist (child (widget-get widget :children))
4151 (when (memq (widget-get child :custom-state) '(modified set))
4152 (widget-apply child :custom-state-set-and-redraw))))
4153
4154(defun custom-group-save (widget)
4155 "Save all modified group members."
4156 (custom-group-mark-to-save widget)
4157 (custom-save-all)
4158 (custom-group-state-set-and-redraw widget))
d543e20b
PA
4159
4160(defun custom-group-reset-current (widget)
4161 "Reset all modified group members."
9db1942d
CY
4162 (dolist (child (widget-get widget :children))
4163 (when (eq (widget-get child :custom-state) 'modified)
4164 (widget-apply child :custom-reset-current))))
d543e20b
PA
4165
4166(defun custom-group-reset-saved (widget)
4167 "Reset all modified or set group members."
9db1942d
CY
4168 (dolist (child (widget-get widget :children))
4169 (when (memq (widget-get child :custom-state) '(modified set))
4170 (widget-apply child :custom-reset-saved))))
d543e20b 4171
25ac13b5 4172(defun custom-group-reset-standard (widget)
d543e20b 4173 "Reset all modified, set, or saved group members."
5ae87ed4
MR
4174 (let ((custom-reset-standard-variables-list '(t))
4175 (custom-reset-standard-faces-list '(t)))
4176 (custom-group-mark-to-reset-standard widget)
4177 (custom-reset-standard-save-and-update)))
4178
4179(defun custom-group-mark-to-reset-standard (widget)
4180 "Mark to reset all modified, set, or saved group members."
9db1942d
CY
4181 (dolist (child (widget-get widget :children))
4182 (when (memq (widget-get child :custom-state)
4183 '(modified set saved))
5ae87ed4 4184 (widget-apply child :custom-mark-to-reset-standard))))
d543e20b
PA
4185
4186(defun custom-group-state-update (widget)
4187 "Update magic."
4188 (unless (eq (widget-get widget :custom-state) 'hidden)
4189 (let* ((children (widget-get widget :children))
4190 (states (mapcar (lambda (child)
4191 (widget-get child :custom-state))
4192 children))
25ac13b5
PA
4193 (magics custom-magic-alist)
4194 (found 'standard))
d543e20b
PA
4195 (while magics
4196 (let ((magic (car (car magics))))
4197 (if (and (not (eq magic 'hidden))
4198 (memq magic states))
4199 (setq found magic
4200 magics nil)
4201 (setq magics (cdr magics)))))
4202 (widget-put widget :custom-state found)))
4203 (custom-magic-reset widget))
f8d869d1
RS
4204\f
4205;;; Reading and writing the custom file.
d543e20b 4206
a1a4fa22 4207;;;###autoload
1e4ed6df 4208(defcustom custom-file nil
d543e20b 4209 "File used for storing customization information.
1e4ed6df 4210The default is nil, which means to use your init file
09b73f49
RS
4211as specified by `user-init-file'. If the value is not nil,
4212it should be an absolute file name.
4213
5aa971e3
LT
4214You can set this option through Custom, if you carefully read the
4215last paragraph below. However, usually it is simpler to write
4216something like the following in your init file:
4217
4218\(setq custom-file \"~/.emacs-custom.el\")
4219\(load custom-file)
4220
4221Note that both lines are necessary: the first line tells Custom to
4222save all customizations in this file, but does not load it.
4223
4224When you change this variable outside Custom, look in the
4225previous custom file \(usually your init file) for the
4226forms `(custom-set-variables ...)' and `(custom-set-faces ...)',
4227and copy them (whichever ones you find) to the new custom file.
4228This will preserve your existing customizations.
4229
4230If you save this option using Custom, Custom will write all
4231currently saved customizations, including the new one for this
4232option itself, into the file you specify, overwriting any
4233`custom-set-variables' and `custom-set-faces' forms already
4234present in that file. It will not delete any customizations from
4235the old custom file. You should do that manually if that is what you
4236want. You also have to put something like `\(load \"CUSTOM-FILE\")
4237in your init file, where CUSTOM-FILE is the actual name of the
4238file. Otherwise, Emacs will not load the file when it starts up,
4239and hence will not set `custom-file' to that file either."
4240 :type '(choice (const :tag "Your Emacs init file" nil)
4241 (file :format "%t:%v%d"
4242 :doc
4243 "Please read entire docstring below before setting \
4244this through Custom.
4245Click om \"More\" \(or position point there and press RETURN)
4246if only the first line of the docstring is shown."))
d543e20b
PA
4247 :group 'customize)
4248
176eb8cb
KH
4249(defun custom-file ()
4250 "Return the file name for saving customizations."
541044b0
RS
4251 (file-chase-links
4252 (or custom-file
4253 (let ((user-init-file user-init-file)
4254 (default-init-file
4255 (if (eq system-type 'ms-dos) "~/_emacs" "~/.emacs")))
4256 (when (null user-init-file)
4257 (if (or (file-exists-p default-init-file)
4258 (and (eq system-type 'windows-nt)
4259 (file-exists-p "~/_emacs")))
4260 ;; Started with -q, i.e. the file containing
4261 ;; Custom settings hasn't been read. Saving
4262 ;; settings there would overwrite other settings.
4263 (error "Saving settings from \"emacs -q\" would overwrite existing customizations"))
4264 (setq user-init-file default-init-file))
4265 user-init-file))))
176eb8cb 4266
b581d2ea
GM
4267;; If recentf-mode is non-nil, this is defined.
4268(declare-function recentf-expand-file-name "recentf" (name))
4269
f8d869d1
RS
4270;;;###autoload
4271(defun custom-save-all ()
4272 "Save all customizations in `custom-file'."
e29e9065
RS
4273 (when (and (null custom-file) init-file-had-error)
4274 (error "Cannot save customizations; init file was not fully loaded"))
f8d869d1 4275 (let* ((filename (custom-file))
c34a8a87
EZ
4276 (recentf-exclude
4277 (if recentf-mode
4278 (cons (concat "\\`"
4279 (regexp-quote
4280 (recentf-expand-file-name (custom-file)))
4281 "\\'")
4282 recentf-exclude)))
f8d869d1 4283 (old-buffer (find-buffer-visiting filename)))
9e2f5050
RS
4284 (with-current-buffer (let ((find-file-visit-truename t))
4285 (or old-buffer (find-file-noselect filename)))
31b7fa51
CY
4286 (unless (eq major-mode 'emacs-lisp-mode)
4287 (emacs-lisp-mode))
f8d869d1
RS
4288 (let ((inhibit-read-only t))
4289 (custom-save-variables)
4290 (custom-save-faces))
4291 (let ((file-precious-flag t))
4292 (save-buffer))
4293 (unless old-buffer
4294 (kill-buffer (current-buffer))))))
16ecd4c5
RS
4295
4296;;;###autoload
4297(defun customize-save-customized ()
4298 "Save all user options which have been set in this session."
4299 (interactive)
4300 (mapatoms (lambda (symbol)
4301 (let ((face (get symbol 'customized-face))
4302 (value (get symbol 'customized-value))
4303 (face-comment (get symbol 'customized-face-comment))
4304 (variable-comment
4305 (get symbol 'customized-variable-comment)))
4306 (when face
4307 (put symbol 'saved-face face)
4308 (custom-push-theme 'theme-face symbol 'user 'set value)
4309 (put symbol 'customized-face nil))
4310 (when value
4311 (put symbol 'saved-value value)
4312 (custom-push-theme 'theme-value symbol 'user 'set value)
4313 (put symbol 'customized-value nil))
4314 (when variable-comment
4315 (put symbol 'saved-variable-comment variable-comment)
4316 (put symbol 'customized-variable-comment nil))
4317 (when face-comment
4318 (put symbol 'saved-face-comment face-comment)
4319 (put symbol 'customized-face-comment nil)))))
4320 ;; We really should update all custom buffers here.
4321 (custom-save-all))
f8d869d1
RS
4322\f
4323;; Editing the custom file contents in a buffer.
4324
d543e20b 4325(defun custom-save-delete (symbol)
f8d869d1 4326 "Delete all calls to SYMBOL from the contents of the current buffer.
a34511a1 4327Leave point at the old location of the first such call,
f8d869d1
RS
4328or (if there were none) at the end of the buffer.
4329
4330This function does not save the buffer."
d543e20b 4331 (goto-char (point-min))
cbe8bb8e
KH
4332 ;; Skip all whitespace and comments.
4333 (while (forward-comment 1))
4334 (or (eobp)
4335 (save-excursion (forward-sexp (buffer-size)))) ; Test for scan errors.
a34511a1
RS
4336 (let (first)
4337 (catch 'found
4338 (while t ;; We exit this loop only via throw.
4339 ;; Skip all whitespace and comments.
4340 (while (forward-comment 1))
4341 (let ((start (point))
4342 (sexp (condition-case nil
4343 (read (current-buffer))
4344 (end-of-file (throw 'found nil)))))
4345 (when (and (listp sexp)
4346 (eq (car sexp) symbol))
4347 (delete-region start (point))
4348 (unless first
4349 (setq first (point)))))))
4350 (if first
4351 (goto-char first)
189638d5
GM
4352 ;; Move in front of local variables, otherwise long Custom
4353 ;; entries would make them ineffective.
4354 (let ((pos (point-max))
4355 (case-fold-search t))
4356 (save-excursion
4357 (goto-char (point-max))
4358 (search-backward "\n\^L" (max (- (point-max) 3000) (point-min))
4359 'move)
4360 (when (search-forward "Local Variables:" nil t)
4361 (setq pos (line-beginning-position))))
4362 (goto-char pos)))))
d543e20b
PA
4363
4364(defun custom-save-variables ()
4365 "Save all customized variables in `custom-file'."
4366 (save-excursion
4367 (custom-save-delete 'custom-set-variables)
d151422c
MR
4368 (let ((standard-output (current-buffer))
4369 (saved-list (make-list 1 0))
4370 sort-fold-case)
4371 ;; First create a sorted list of saved variables.
4372 (mapatoms
4373 (lambda (symbol)
d358aa10 4374 (if (and (get symbol 'saved-value)
2d656942
CY
4375 ;; ignore theme values
4376 (or (null (get symbol 'theme-value))
4377 (eq 'user (caar (get symbol 'theme-value)))))
d151422c
MR
4378 (nconc saved-list (list symbol)))))
4379 (setq saved-list (sort (cdr saved-list) 'string<))
d543e20b
PA
4380 (unless (bolp)
4381 (princ "\n"))
aec2bb63 4382 (princ "(custom-set-variables
99b398e0
RS
4383 ;; custom-set-variables was added by Custom.
4384 ;; If you edit it by hand, you could mess it up, so be careful.
4385 ;; Your init file should contain only one such instance.
4386 ;; If there is more than one, they won't work right.\n")
d9f67134
SM
4387 (dolist (symbol saved-list)
4388 (let ((spec (car-safe (get symbol 'theme-value)))
4389 (value (get symbol 'saved-value))
4390 (requests (get symbol 'custom-requests))
adcc7a37
RS
4391 (now (and (not (custom-variable-p symbol))
4392 (or (boundp symbol)
4393 (eq (get symbol 'force-value)
4394 'rogue))))
40627755 4395 (comment (get symbol 'saved-variable-comment)))
c8c9333c 4396 ;; Check REQUESTS for validity.
d9f67134
SM
4397 (dolist (request requests)
4398 (when (and (symbolp request) (not (featurep request)))
4399 (message "Unknown requested feature: %s" request)
4400 (setq requests (delq request requests))))
adcc7a37 4401 ;; Is there anything customized about this variable?
d358aa10 4402 (when (or (and spec (eq (car spec) 'user))
d9f67134
SM
4403 comment
4404 (and (null spec) (get symbol 'saved-value)))
adcc7a37
RS
4405 ;; Output an element for this variable.
4406 ;; It has the form (SYMBOL VALUE-FORM NOW REQUESTS COMMENT).
4407 ;; SYMBOL is the variable name.
4408 ;; VALUE-FORM is an expression to return the customized value.
4409 ;; NOW if non-nil means always set the variable immediately
4410 ;; when the customizations are reloaded. This is used
4411 ;; for rogue variables
4412 ;; REQUESTS is a list of packages to load before setting the
4413 ;; variable. Each element of it will be passed to `require'.
4414 ;; COMMENT is whatever comment the user has specified
4415 ;; with the customize facility.
d9f67134
SM
4416 (unless (bolp)
4417 (princ "\n"))
4418 (princ " '(")
4419 (prin1 symbol)
4420 (princ " ")
4421 (prin1 (car value))
4422 (when (or now requests comment)
4423 (princ " ")
4424 (prin1 now)
4425 (when (or requests comment)
4426 (princ " ")
4427 (prin1 requests)
4428 (when comment
4429 (princ " ")
4430 (prin1 comment))))
4431 (princ ")"))))
a34511a1
RS
4432 (if (bolp)
4433 (princ " "))
d543e20b
PA
4434 (princ ")")
4435 (unless (looking-at "\n")
4436 (princ "\n")))))
4437
4438(defun custom-save-faces ()
4439 "Save all customized faces in `custom-file'."
4440 (save-excursion
c942535f 4441 (custom-save-delete 'custom-reset-faces)
d543e20b 4442 (custom-save-delete 'custom-set-faces)
d151422c
MR
4443 (let ((standard-output (current-buffer))
4444 (saved-list (make-list 1 0))
4445 sort-fold-case)
4446 ;; First create a sorted list of saved faces.
4447 (mapatoms
4448 (lambda (symbol)
d358aa10
CY
4449 (if (and (get symbol 'saved-face)
4450 (eq 'user (car (car-safe (get symbol 'theme-face)))))
d151422c
MR
4451 (nconc saved-list (list symbol)))))
4452 (setq saved-list (sort (cdr saved-list) 'string<))
4453 ;; The default face must be first, since it affects the others.
4454 (if (memq 'default saved-list)
4455 (setq saved-list (cons 'default (delq 'default saved-list))))
d543e20b
PA
4456 (unless (bolp)
4457 (princ "\n"))
aec2bb63 4458 (princ "(custom-set-faces
99b398e0
RS
4459 ;; custom-set-faces was added by Custom.
4460 ;; If you edit it by hand, you could mess it up, so be careful.
4461 ;; Your init file should contain only one such instance.
4462 ;; If there is more than one, they won't work right.\n")
d9f67134
SM
4463 (dolist (symbol saved-list)
4464 (let ((spec (car-safe (get symbol 'theme-face)))
4465 (value (get symbol 'saved-face))
4466 (now (not (or (get symbol 'face-defface-spec)
4467 (and (not (custom-facep symbol))
4468 (not (get symbol 'force-face))))))
4469 (comment (get symbol 'saved-face-comment)))
d358aa10 4470 (when (or (and spec (eq (nth 0 spec) 'user))
d9f67134
SM
4471 comment
4472 (and (null spec) (get symbol 'saved-face)))
4473 ;; Don't print default face here.
4474 (unless (bolp)
4475 (princ "\n"))
4476 (princ " '(")
4477 (prin1 symbol)
4478 (princ " ")
4479 (prin1 value)
4480 (when (or now comment)
4481 (princ " ")
4482 (prin1 now)
4483 (when comment
4484 (princ " ")
4485 (prin1 comment)))
4486 (princ ")"))))
a34511a1
RS
4487 (if (bolp)
4488 (princ " "))
d543e20b
PA
4489 (princ ")")
4490 (unless (looking-at "\n")
08b4ae6c 4491 (princ "\n")))))
f8d869d1 4492\f
d543e20b
PA
4493;;; The Customize Menu.
4494
bd042c03
PA
4495;;; Menu support
4496
25ac13b5
PA
4497(defcustom custom-menu-nesting 2
4498 "Maximum nesting in custom menus."
4499 :type 'integer
6aaedd12 4500 :group 'custom-menu)
d543e20b
PA
4501
4502(defun custom-face-menu-create (widget symbol)
4503 "Ignoring WIDGET, create a menu entry for customization face SYMBOL."
4504 (vector (custom-unlispify-menu-entry symbol)
86bd10bc 4505 `(customize-face ',symbol)
d543e20b
PA
4506 t))
4507
4508(defun custom-variable-menu-create (widget symbol)
4509 "Ignoring WIDGET, create a menu entry for customization variable SYMBOL."
4510 (let ((type (get symbol 'custom-type)))
4511 (unless (listp type)
4512 (setq type (list type)))
4513 (if (and type (widget-get type :custom-menu))
4514 (widget-apply type :custom-menu symbol)
4515 (vector (custom-unlispify-menu-entry symbol)
86bd10bc 4516 `(customize-variable ',symbol)
d543e20b
PA
4517 t))))
4518
bd042c03 4519;; Add checkboxes to boolean variable entries.
d543e20b
PA
4520(widget-put (get 'boolean 'widget-type)
4521 :custom-menu (lambda (widget symbol)
4522 (vector (custom-unlispify-menu-entry symbol)
86bd10bc 4523 `(customize-variable ',symbol)
d543e20b
PA
4524 ':style 'toggle
4525 ':selected symbol)))
4526
d04a3972
DL
4527(defun custom-group-menu-create (widget symbol)
4528 "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
4529 `( ,(custom-unlispify-menu-entry symbol t)
4530 :filter (lambda (&rest junk)
809f0c35 4531 (let* ((menu (custom-menu-create ',symbol)))
3b2f3d30 4532 (if (consp menu) (cdr menu) menu)))))
d543e20b 4533
bd042c03
PA
4534;;;###autoload
4535(defun custom-menu-create (symbol)
d543e20b 4536 "Create menu for customization group SYMBOL.
d543e20b 4537The menu is in a format applicable to `easy-menu-define'."
809f0c35
RS
4538 (let* ((deactivate-mark nil)
4539 (item (vector (custom-unlispify-menu-entry symbol)
86bd10bc 4540 `(customize-group ',symbol)
bd042c03
PA
4541 t)))
4542 (if (and (or (not (boundp 'custom-menu-nesting))
4543 (>= custom-menu-nesting 0))
2de2cb02
MR
4544 (progn
4545 (custom-load-symbol symbol)
4546 (< (length (get symbol 'custom-group)) widget-menu-max-size)))
d543e20b 4547 (let ((custom-prefix-list (custom-prefix-add symbol
25ac13b5 4548 custom-prefix-list))
da5ec617
PA
4549 (members (custom-sort-items (get symbol 'custom-group)
4550 custom-menu-sort-alphabetically
4551 custom-menu-order-groups)))
d543e20b
PA
4552 `(,(custom-unlispify-menu-entry symbol t)
4553 ,item
4554 "--"
4555 ,@(mapcar (lambda (entry)
4556 (widget-apply (if (listp (nth 1 entry))
4557 (nth 1 entry)
4558 (list (nth 1 entry)))
4559 :custom-menu (nth 0 entry)))
25ac13b5 4560 members)))
d543e20b
PA
4561 item)))
4562
4563;;;###autoload
bd042c03
PA
4564(defun customize-menu-create (symbol &optional name)
4565 "Return a customize menu for customization group SYMBOL.
d3d4df42 4566If optional NAME is given, use that as the name of the menu.
bd042c03
PA
4567Otherwise the menu will be named `Customize'.
4568The format is suitable for use with `easy-menu-define'."
4569 (unless name
4570 (setq name "Customize"))
d04a3972
DL
4571 `(,name
4572 :filter (lambda (&rest junk)
3b2f3d30
SM
4573 (let ((menu (custom-menu-create ',symbol)))
4574 (if (consp menu) (cdr menu) menu)))))
d543e20b 4575
9db1942d
CY
4576;;; Toolbar and menubar support
4577
4578(easy-menu-define
34cf517c 4579 Custom-mode-menu (list custom-mode-map custom-field-keymap)
9db1942d
CY
4580 "Menu used in customization buffers."
4581 (nconc (list "Custom"
4582 (customize-menu-create 'customize))
4583 (mapcar (lambda (arg)
4584 (let ((tag (nth 0 arg))
4585 (command (nth 1 arg))
4586 (active (nth 2 arg))
4587 (help (nth 3 arg)))
4588 (vector tag command :active (eval active) :help help)))
4589 custom-commands)))
4590
4591(defvar tool-bar-map)
2614ccc3
MR
4592
4593;;; `custom-tool-bar-map' used to be set up here. This will fail to
4594;;; DTRT when `display-graphic-p' returns nil during compilation. Hence
6aec3b9d 4595;;; we set this up lazily in `Custom-mode'.
2614ccc3
MR
4596(defvar custom-tool-bar-map nil
4597 "Keymap for toolbar in Custom mode.")
9db1942d 4598
bd042c03
PA
4599;;; The Custom Mode.
4600
adcc7a37 4601(defun Custom-no-edit (pos &optional event)
7eb944cf
RS
4602 "Invoke button at POS, or refuse to allow editing of Custom buffer."
4603 (interactive "@d")
a8da2abd 4604 (error "You can't edit this part of the Custom buffer"))
0ce026b1 4605
adcc7a37 4606(defun Custom-newline (pos &optional event)
19f0515a
RS
4607 "Invoke button at POS, or refuse to allow editing of Custom buffer."
4608 (interactive "@d")
4609 (let ((button (get-char-property pos 'button)))
4610 (if button
4611 (widget-apply-action button event)
4612 (error "You can't edit this part of the Custom buffer"))))
4613
b62c92bb
RS
4614(defun Custom-goto-parent ()
4615 "Go to the parent group listed at the top of this buffer.
4616If several parents are listed, go to the first of them."
4617 (interactive)
4618 (save-excursion
4619 (goto-char (point-min))
d84fcc30 4620 (if (search-forward "\nParent groups: " nil t)
b62c92bb
RS
4621 (let* ((button (get-char-property (point) 'button))
4622 (parent (downcase (widget-get button :tag))))
4623 (customize-group parent)))))
4624
6aec3b9d 4625(defcustom Custom-mode-hook nil
d3d4df42 4626 "Hook called when entering Custom mode."
bd042c03 4627 :type 'hook
6aec3b9d 4628 :group 'custom-buffer)
bd042c03 4629
b62c92bb
RS
4630(defun custom-state-buffer-message (widget)
4631 (if (eq (widget-get (widget-get widget :parent) :custom-state) 'modified)
4632 (message "To install your edits, invoke [State] and choose the Set operation")))
8691cfa7 4633
6aec3b9d 4634(define-derived-mode Custom-mode nil "Custom"
bd042c03
PA
4635 "Major mode for editing customization buffers.
4636
4637The following commands are available:
4638
192e44fc
JL
4639\\<widget-keymap>\
4640Move to next button, link or editable field. \\[widget-forward]
61e80285 4641Move to previous button, link or editable field. \\[advertised-widget-backward]
192e44fc 4642\\<custom-field-keymap>\
4ee1cf9f
PA
4643Complete content of editable text field. \\[widget-complete]
4644\\<custom-mode-map>\
9ca66103 4645Invoke button under the mouse pointer. \\[widget-button-click]
192e44fc 4646Invoke button under point. \\[widget-button-press]
107736a2
RS
4647Set all options from current text. \\[Custom-set]
4648Make values in current text permanent. \\[Custom-save]
192e44fc
JL
4649Make text match actual option values. \\[Custom-reset-current]
4650Reset options to permanent settings. \\[Custom-reset-saved]
107736a2
RS
4651Erase customizations; set options
4652 and buffer text to the standard values. \\[Custom-reset-standard]
bd042c03 4653
6aec3b9d 4654Entry to this mode calls the value of `Custom-mode-hook'
bd042c03 4655if that value is non-nil."
bd042c03 4656 (use-local-map custom-mode-map)
ab678382 4657 (easy-menu-add Custom-mode-menu)
2614ccc3
MR
4658 (when (display-graphic-p)
4659 (set (make-local-variable 'tool-bar-map)
4660 (or custom-tool-bar-map
4661 ;; Set up `custom-tool-bar-map'.
4662 (let ((map (make-sparse-keymap)))
4663 (mapc
4664 (lambda (arg)
4665 (tool-bar-local-item-from-menu
4666 (nth 1 arg) (nth 4 arg) map custom-mode-map))
4667 custom-commands)
4668 (setq custom-tool-bar-map map)))))
bd042c03 4669 (make-local-variable 'custom-options)
9b7826f4 4670 (make-local-variable 'custom-local-buffer)
b62c92bb 4671 (make-local-variable 'widget-documentation-face)
d478e69d 4672 (setq widget-documentation-face 'custom-documentation)
3aec85bf 4673 (make-local-variable 'widget-button-face)
87911bdb 4674 (setq widget-button-face custom-button)
d1a3873f 4675 (setq show-trailing-whitespace nil)
b0c4ae71
CY
4676
4677 ;; We need this because of the "More" button on docstrings.
4678 ;; Otherwise clicking on "More" can push point offscreen, which
4679 ;; causes the window to recenter on point, which pushes the
4680 ;; newly-revealed docstring offscreen; which is annoying. -- cyd.
4681 (set (make-local-variable 'widget-button-click-moves-point) t)
4682
87911bdb 4683 (set (make-local-variable 'widget-button-pressed-face) custom-button-pressed)
85a5eb0e 4684 (set (make-local-variable 'widget-mouse-face) custom-button-mouse)
87911bdb 4685
d3d4df42
DL
4686 ;; When possible, use relief for buttons, not bracketing. This test
4687 ;; may not be optimal.
4688 (when custom-raised-buttons
4689 (set (make-local-variable 'widget-push-button-prefix) "")
4690 (set (make-local-variable 'widget-push-button-suffix) "")
4691 (set (make-local-variable 'widget-link-prefix) "")
4692 (set (make-local-variable 'widget-link-suffix) ""))
d1a3873f 4693 (add-hook 'widget-edit-functions 'custom-state-buffer-message nil t))
d543e20b 4694
6aec3b9d
JL
4695(put 'Custom-mode 'mode-class 'special)
4696
4697;; backward-compatibility
4698(defun custom-mode ()
4699 "Non-interactive variant of `Custom-mode'."
4700 (Custom-mode))
82c02591 4701(make-obsolete 'custom-mode 'Custom-mode "23.1")
7f352f86 4702(put 'custom-mode 'mode-class 'special)
82c02591 4703(define-obsolete-variable-alias 'custom-mode-hook 'Custom-mode-hook "23.1")
7f352f86 4704
81711dba
EZ
4705(dolist (regexp
4706 '("^No user option defaults have been changed since Emacs "
4707 "^Invalid face:? "
4708 "^No \\(?:customized\\|rogue\\|saved\\) user options"
4709 "^No customizable items matching "
4710 "^There are unset changes"
4711 "^Cannot set hidden variable"
4712 "^No \\(?:saved\\|backup\\) value for "
4713 "^No standard setting known for "
4714 "^No standard setting for this face"
4715 "^Saving settings from \"emacs -q\" would overwrite existing customizations"))
4716 (add-to-list 'debug-ignored-errors regexp))
2365594b 4717
d543e20b
PA
4718;;; The End.
4719
4720(provide 'cus-edit)
4721
f7105ede 4722;; arch-tag: 64533aa4-1b1a-48c3-8812-f9dc718e8a6f
d3d4df42 4723;;; cus-edit.el ends here