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