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