XEmacs 21.2.28 "Hermes".
[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, 2000 Free Software Foundation, Inc.
4 ;;
5 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6 ;; Maintainer: Hrvoje Niksic <hniksic@xemacs.org>
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   ;; Add 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                  :help-echo "\
1037 Make your editing in this buffer take effect for this session"
1038                  :action (lambda (widget &optional event)
1039                            (Custom-set)))
1040   (widget-insert " ")
1041   (widget-create 'push-button
1042                  :tag "Save"
1043                  :help-echo "\
1044 Make your editing in this buffer take effect for future Emacs sessions"
1045                  :action (lambda (widget &optional event)
1046                            (Custom-save)))
1047   (if custom-reset-button-menu
1048       (progn
1049         (widget-insert " ")
1050         (widget-create 'push-button
1051                        :tag "Reset"
1052                        :tag-glyph '("reset-up" "reset-down")
1053                        :help-echo "Show a menu with reset operations"
1054                        :mouse-down-action (lambda (&rest junk) t)
1055                        :action (lambda (widget &optional event)
1056                                  (custom-reset event))))
1057     (widget-insert " ")
1058     (widget-create 'push-button
1059                    :tag "Reset"
1060                    :help-echo "\
1061 Reset all edited text in this buffer to reflect current values"
1062                    :action 'Custom-reset-current)
1063     (widget-insert " ")
1064     (widget-create 'push-button
1065                    :tag "Reset to Saved"
1066                    :help-echo "\
1067 Reset all values in this buffer to their saved settings"
1068                    :action 'Custom-reset-saved)
1069     (widget-insert " ")
1070     (widget-create 'push-button
1071                    :tag "Reset to Standard"
1072                    :help-echo "\
1073 Reset all values in this buffer to their standard settings"
1074                    :action 'Custom-reset-standard))
1075   (widget-insert "  ")
1076   (widget-create 'push-button
1077                  :tag "Done"
1078                  :help-echo "Remove the buffer"
1079                  :action (lambda (widget &optional event)
1080                            (Custom-buffer-done)))
1081   (widget-insert "\n"))
1082
1083 (defcustom custom-novice t
1084   "If non-nil, show help message at top of customize buffers."
1085   :type 'boolean
1086   :group 'custom-buffer)
1087
1088 (defcustom custom-display-global-buttons 'top
1089   "If `nil' don't display the global buttons.  If `top' display at the
1090 beginning of custom buffers.  If `bottom', display at the end."
1091   :type '(choice (const top)
1092                  (const bottom)
1093                  (const :tag "don't" nil))
1094   :group 'custom-buffer)
1095
1096 (defun custom-buffer-create-internal (options &optional description)
1097   (message "Creating customization buffer...")
1098   (custom-mode)
1099   (widget-insert "This is a customization buffer")
1100   (if description
1101       (widget-insert description))
1102   (when custom-novice
1103       (widget-insert ".\n\
1104 Type RET or click button2 on an active field to invoke its action.
1105 Invoke ")
1106       (widget-create 'info-link
1107                      :tag "Help"
1108                      :help-echo "Read the online help"
1109                      "(XEmacs)Easy Customization")
1110       (widget-insert " for more information."))
1111   (widget-insert "\n")
1112   (if (equal custom-display-global-buttons 'top)
1113       (custom-buffer-create-buttons))
1114   (widget-insert "\n")
1115   (message "Creating customization items...")
1116   (setq custom-options
1117         (if (= (length options) 1)
1118             (mapcar (lambda (entry)
1119                       (widget-create (nth 1 entry)
1120                                      :documentation-shown t
1121                                      :custom-state 'unknown
1122                                      :tag (custom-unlispify-tag-name
1123                                            (nth 0 entry))
1124                                      :value (nth 0 entry)))
1125                     options)
1126           (let ((count 0)
1127                 (length (length options)))
1128             (mapcar (lambda (entry)
1129                       (prog2
1130                           (display-message
1131                            'progress
1132                            (format "Creating customization items %2d%%..."
1133                                    (/ (* 100.0 count) length)))
1134                           (widget-create (nth 1 entry)
1135                                          :tag (custom-unlispify-tag-name
1136                                                (nth 0 entry))
1137                                          :value (nth 0 entry))
1138                         (incf count)
1139                         (unless (eq (preceding-char) ?\n)
1140                           (widget-insert "\n"))
1141                         (widget-insert "\n")))
1142                     options))))
1143   (unless (eq (preceding-char) ?\n)
1144     (widget-insert "\n"))
1145   (if (equal custom-display-global-buttons 'bottom)
1146       (custom-buffer-create-buttons))
1147   (display-message 'progress
1148                    (format
1149                     "Creating customization items %2d%%...done" 100))
1150   (unless (eq custom-buffer-style 'tree)
1151     (mapc 'custom-magic-reset custom-options))
1152   (message "Creating customization setup...")
1153   (widget-setup)
1154   (goto-char (point-min))
1155   (message "Creating customization buffer...done"))
1156
1157 \f
1158 ;;; The Tree Browser.
1159
1160 ;;;###autoload
1161 (defun customize-browse (&optional group)
1162   "Create a tree browser for the customize hierarchy."
1163   (interactive)
1164   (unless group
1165     (setq group 'emacs))
1166   (let ((name "*Customize Browser*"))
1167     (kill-buffer (get-buffer-create name))
1168     (switch-to-buffer (get-buffer-create name)))
1169   (custom-mode)
1170   (widget-insert "\
1171 Square brackets show active fields; type RET or click button2
1172 on an active field to invoke its action.
1173 Invoke [+] below to expand a group, and [-] to collapse an expanded group.\n")
1174   (if custom-browse-only-groups
1175       (widget-insert "\
1176 Invoke the [Group] button below to edit that item in another window.\n\n")
1177     (widget-insert "Invoke the ")
1178     (widget-create 'item
1179                    :format "%t"
1180                    :tag "[Group]"
1181                    :tag-glyph "folder")
1182     (widget-insert ", ")
1183     (widget-create 'item
1184                    :format "%t"
1185                    :tag "[Face]"
1186                    :tag-glyph "face")
1187     (widget-insert ", and ")
1188     (widget-create 'item
1189                    :format "%t"
1190                    :tag "[Option]"
1191                    :tag-glyph "option")
1192     (widget-insert " buttons below to edit that
1193 item in another window.\n\n"))
1194   (let ((custom-buffer-style 'tree))
1195     (widget-create 'custom-group
1196                    :custom-last t
1197                    :custom-state 'unknown
1198                    :tag (custom-unlispify-tag-name group)
1199                    :value group))
1200   (widget-add-change)
1201   (goto-char (point-min)))
1202
1203 (define-widget 'custom-browse-visibility 'item
1204   "Control visibility of of items in the customize tree browser."
1205   :format "%[[%t]%]"
1206   :action 'custom-browse-visibility-action)
1207
1208 (defun custom-browse-visibility-action (widget &rest ignore)
1209   (let ((custom-buffer-style 'tree))
1210     (custom-toggle-parent widget)))
1211
1212 (define-widget 'custom-browse-group-tag 'push-button
1213   "Show parent in other window when activated."
1214   :tag "Group"
1215   :tag-glyph "folder"
1216   :action 'custom-browse-group-tag-action)
1217
1218 (defun custom-browse-group-tag-action (widget &rest ignore)
1219   (let ((parent (widget-get widget :parent)))
1220     (customize-group-other-window (widget-value parent))))
1221
1222 (define-widget 'custom-browse-variable-tag 'push-button
1223   "Show parent in other window when activated."
1224   :tag "Option"
1225   :tag-glyph "option"
1226   :action 'custom-browse-variable-tag-action)
1227
1228 (defun custom-browse-variable-tag-action (widget &rest ignore)
1229   (let ((parent (widget-get widget :parent)))
1230     (customize-variable-other-window (widget-value parent))))
1231
1232 (define-widget 'custom-browse-face-tag 'push-button
1233   "Show parent in other window when activated."
1234   :tag "Face"
1235   :tag-glyph "face"
1236   :action 'custom-browse-face-tag-action)
1237
1238 (defun custom-browse-face-tag-action (widget &rest ignore)
1239   (let ((parent (widget-get widget :parent)))
1240     (customize-face-other-window (widget-value parent))))
1241
1242 (defconst custom-browse-alist '(("   " "space")
1243                                 (" | " "vertical")
1244                                 ("-\\ " "top")
1245                                 (" |-" "middle")
1246                                 (" `-" "bottom")))
1247
1248 (defun custom-browse-insert-prefix (prefix)
1249   "Insert PREFIX.  On XEmacs convert it to line graphics."
1250   ;; #### Unfinished.
1251   (if nil ; (string-match "XEmacs" emacs-version)
1252       (progn
1253         (insert "*")
1254         (while (not (string-equal prefix ""))
1255           (let ((entry (substring prefix 0 3)))
1256             (setq prefix (substring prefix 3))
1257             (let ((overlay (make-overlay (1- (point)) (point) nil t nil))
1258                   (name (nth 1 (assoc entry custom-browse-alist))))
1259               (overlay-put overlay 'end-glyph (widget-glyph-find name entry))
1260               (overlay-put overlay 'start-open t)
1261               (overlay-put overlay 'end-open t)))))
1262     (insert prefix)))
1263
1264 \f
1265 ;;; Modification of Basic Widgets.
1266 ;;
1267 ;; We add extra properties to the basic widgets needed here.  This is
1268 ;; fine, as long as we are careful to stay within out own namespace.
1269 ;;
1270 ;; We want simple widgets to be displayed by default, but complex
1271 ;; widgets to be hidden.
1272
1273 (widget-put (get 'item 'widget-type) :custom-show t)
1274 (widget-put (get 'editable-field 'widget-type)
1275             :custom-show (lambda (widget value)
1276                            ;; This used to call pp-to-string
1277                            (let ((pp (widget-prettyprint-to-string value)))
1278                              (cond ((string-match "\n" pp)
1279                                     nil)
1280                                    ((> (length pp) 40)
1281                                     nil)
1282                                    (t t)))))
1283 (widget-put (get 'menu-choice 'widget-type) :custom-show t)
1284
1285 ;;; The `custom-manual' Widget.
1286
1287 (define-widget 'custom-manual 'info-link
1288   "Link to the manual entry for this customization option."
1289   :tag "Manual")
1290
1291 ;;; The `custom-magic' Widget.
1292
1293 (defgroup custom-magic-faces nil
1294   "Faces used by the magic button."
1295   :group 'custom-faces
1296   :group 'custom-buffer)
1297
1298 (defface custom-invalid-face '((((class color))
1299                                 (:foreground "yellow" :background "red"))
1300                                (t
1301                                 (:bold t :italic t :underline t)))
1302   "Face used when the customize item is invalid."
1303   :group 'custom-magic-faces)
1304
1305 (defface custom-rogue-face '((((class color))
1306                               (:foreground "pink" :background "black"))
1307                              (t
1308                               (:underline t)))
1309   "Face used when the customize item is not defined for customization."
1310   :group 'custom-magic-faces)
1311
1312 (defface custom-modified-face '((((class color))
1313                                  (:foreground "white" :background "blue"))
1314                                 (t
1315                                  (:italic t :bold)))
1316   "Face used when the customize item has been modified."
1317   :group 'custom-magic-faces)
1318
1319 (defface custom-set-face '((((class color))
1320                                 (:foreground "blue" :background "white"))
1321                                (t
1322                                 (:italic t)))
1323   "Face used when the customize item has been set."
1324   :group 'custom-magic-faces)
1325
1326 (defface custom-changed-face '((((class color))
1327                                 (:foreground "white" :background "blue"))
1328                                (t
1329                                 (:italic t)))
1330   "Face used when the customize item has been changed."
1331   :group 'custom-magic-faces)
1332
1333 (defface custom-saved-face '((t (:underline t)))
1334   "Face used when the customize item has been saved."
1335   :group 'custom-magic-faces)
1336
1337 (defconst custom-magic-alist '((nil "#" underline "\
1338 uninitialized, you should not see this.")
1339                                (unknown "?" italic "\
1340 unknown, you should not see this.")
1341                                (hidden "-" default "\
1342 hidden, invoke \"Show\" button in the previous line to show." "\
1343 group now hidden, invoke the above \"Show\" button to show contents.")
1344                                (invalid "x" custom-invalid-face "\
1345 the value displayed for this %c is invalid and cannot be set.")
1346                                (modified "*" custom-modified-face "\
1347 you have edited the value as text, but you have not set the %c." "\
1348 you have edited something in this group, but not set it.")
1349                                (set "+" custom-set-face "\
1350 you have set this %c, but not saved it for future sessions." "\
1351 something in this group has been set, but not saved.")
1352                                (changed ":" custom-changed-face "\
1353 this %c has been changed outside the customize buffer." "\
1354 something in this group has been changed outside customize.")
1355                                (saved "!" custom-saved-face "\
1356 this %c has been set and saved." "\
1357 something in this group has been set and saved.")
1358                                (rogue "@" custom-rogue-face "\
1359 this %c has not been changed with customize." "\
1360 something in this group is not prepared for customization.")
1361                                (standard " " nil "\
1362 this %c is unchanged from its standard setting." "\
1363 visible group members are all at standard settings."))
1364   "Alist of customize option states.
1365 Each entry is of the form (STATE MAGIC FACE ITEM-DESC [ GROUP-DESC ]), where
1366
1367 STATE is one of the following symbols:
1368
1369 `nil'
1370    For internal use, should never occur.
1371 `unknown'
1372    For internal use, should never occur.
1373 `hidden'
1374    This item is not being displayed.
1375 `invalid'
1376    This item is modified, but has an invalid form.
1377 `modified'
1378    This item is modified, and has a valid form.
1379 `set'
1380    This item has been set but not saved.
1381 `changed'
1382    The current value of this item has been changed temporarily.
1383 `saved'
1384    This item is marked for saving.
1385 `rogue'
1386    This item has no customization information.
1387 `standard'
1388    This item is unchanged from the standard setting.
1389
1390 MAGIC is a string used to present that state.
1391
1392 FACE is a face used to present the state.
1393
1394 ITEM-DESC is a string describing the state for options.
1395
1396 GROUP-DESC is a string describing the state for groups.  If this is
1397 left out, ITEM-DESC will be used.
1398
1399 The string %c in either description will be replaced with the
1400 category of the item.  These are `group'. `option', and `face'.
1401
1402 The list should be sorted most significant first.")
1403
1404 (defcustom custom-magic-show 'long
1405   "If non-nil, show textual description of the state.
1406 If `long', show a full-line description, not just one word."
1407   :type '(choice (const :tag "no" nil)
1408                  (const short)
1409                  (const long))
1410   :group 'custom-buffer)
1411
1412 (defcustom custom-magic-show-hidden '(option face)
1413   "Control whether the State button is shown for hidden items.
1414 The value should be a list with the custom categories where the State
1415 button should be visible.  Possible categories are `group', `option',
1416 and `face'."
1417   :type '(set (const group) (const option) (const face))
1418   :group 'custom-buffer)
1419
1420 (defcustom custom-magic-show-button nil
1421   "Show a \"magic\" button indicating the state of each customization option."
1422   :type 'boolean
1423   :group 'custom-buffer)
1424
1425 (define-widget 'custom-magic 'default
1426   "Show and manipulate state for a customization option."
1427   :format "%v"
1428   :action 'widget-parent-action
1429   :notify 'ignore
1430   :value-get 'ignore
1431   :value-create 'custom-magic-value-create
1432   :value-delete 'widget-children-value-delete)
1433
1434 (defun widget-magic-mouse-down-action (widget &optional event)
1435   ;; Non-nil unless hidden.
1436   (not (eq (widget-get (widget-get (widget-get widget :parent) :parent)
1437                        :custom-state)
1438            'hidden)))
1439
1440 (defun custom-magic-value-create (widget)
1441   ;; Create compact status report for WIDGET.
1442   (let* ((parent (widget-get widget :parent))
1443          (state (widget-get parent :custom-state))
1444          (hidden (eq state 'hidden))
1445          (entry (assq state custom-magic-alist))
1446          (magic (nth 1 entry))
1447          (face (nth 2 entry))
1448          (category (widget-get parent :custom-category))
1449          (text (or (and (eq category 'group)
1450                         (nth 4 entry))
1451                    (nth 3 entry)))
1452          (form (widget-get parent :custom-form))
1453          children)
1454     (while (string-match "\\`\\(.*\\)%c\\(.*\\)\\'" text)
1455       (setq text (concat (match-string 1 text)
1456                          (symbol-name category)
1457                          (match-string 2 text))))
1458     (when (and custom-magic-show
1459                (or (not hidden)
1460                    (memq category custom-magic-show-hidden)))
1461       (insert "   ")
1462       (when (and (eq category 'group)
1463                  (not (and (eq custom-buffer-style 'links)
1464                            (> (widget-get parent :custom-level) 1))))
1465         (insert-char ?\  (* custom-buffer-indent
1466                             (widget-get parent :custom-level))))
1467       (push (widget-create-child-and-convert
1468              widget 'choice-item
1469              :help-echo "Change the state of this item"
1470              :format (if hidden "%t" "%[%t%]")
1471              :button-prefix 'widget-push-button-prefix
1472              :button-suffix 'widget-push-button-suffix
1473              :mouse-down-action 'widget-magic-mouse-down-action
1474              :tag "State"
1475              ;;:tag-glyph (or hidden '("state-up" "state-down"))
1476              )
1477             children)
1478       (insert ": ")
1479       (let ((start (point)))
1480         (if (eq custom-magic-show 'long)
1481             (insert text)
1482           (insert (symbol-name state)))
1483         (cond ((eq form 'lisp)
1484                (insert " (lisp)"))
1485               ((eq form 'mismatch)
1486                (insert " (mismatch)")))
1487         (put-text-property start (point) 'face 'custom-state-face))
1488       (insert "\n"))
1489     (when (and (eq category 'group)
1490                (not (and (eq custom-buffer-style 'links)
1491                          (> (widget-get parent :custom-level) 1))))
1492       (insert-char ?\  (* custom-buffer-indent
1493                           (widget-get parent :custom-level))))
1494     (when custom-magic-show-button
1495       (when custom-magic-show
1496         (let ((indent (widget-get parent :indent)))
1497           (when indent
1498             (insert-char ?\  indent))))
1499       (push (widget-create-child-and-convert
1500              widget 'choice-item
1501              :mouse-down-action 'widget-magic-mouse-down-action
1502              :button-face face
1503              :button-prefix ""
1504              :button-suffix ""
1505              :help-echo "Change the state"
1506              :format (if hidden "%t" "%[%t%]")
1507              :tag (if (memq form '(lisp mismatch))
1508                       (concat "(" magic ")")
1509                     (concat "[" magic "]")))
1510             children)
1511       (insert " "))
1512     (widget-put widget :children children)))
1513
1514 (defun custom-magic-reset (widget)
1515   "Redraw the :custom-magic property of WIDGET."
1516   (let ((magic (widget-get widget :custom-magic)))
1517     (widget-value-set magic (widget-value magic))))
1518
1519 ;;; The `custom' Widget.
1520
1521 (defface custom-button-face '((t (:bold t)))
1522   "Face used for buttons in customization buffers."
1523   :group 'custom-faces)
1524
1525 (defface custom-documentation-face nil
1526   "Face used for documentation strings in customization buffers."
1527   :group 'custom-faces)
1528
1529 (defface custom-state-face '((((class color)
1530                                (background dark))
1531                               (:foreground "lime green"))
1532                              (((class color)
1533                                (background light))
1534                               (:foreground "dark green"))
1535                              (t nil))
1536   "Face used for State descriptions in the customize buffer."
1537   :group 'custom-faces)
1538
1539 (define-widget 'custom 'default
1540   "Customize a user option."
1541   :format "%v"
1542   :convert-widget 'custom-convert-widget
1543   :notify 'custom-notify
1544   :custom-prefix ""
1545   :custom-level 1
1546   :custom-state 'hidden
1547   :documentation-property 'widget-subclass-responsibility
1548   :value-create 'widget-subclass-responsibility
1549   :value-delete 'widget-children-value-delete
1550   :value-get 'widget-value-value-get
1551   :validate 'widget-children-validate
1552   :match (lambda (widget value) (symbolp value)))
1553
1554 (defun custom-convert-widget (widget)
1555   ;; Initialize :value and :tag from :args in WIDGET.
1556   (let ((args (widget-get widget :args)))
1557     (when args
1558       (widget-put widget :value (widget-apply widget
1559                                               :value-to-internal (car args)))
1560       (widget-put widget :tag (custom-unlispify-tag-name (car args)))
1561       (widget-put widget :args nil)))
1562   widget)
1563
1564 (defun custom-notify (widget &rest args)
1565   "Keep track of changes."
1566   (let ((state (widget-get widget :custom-state)))
1567     (unless (eq state 'modified)
1568       (unless (memq state '(nil unknown hidden))
1569         (widget-put widget :custom-state 'modified))
1570       (custom-magic-reset widget)
1571       (apply 'widget-default-notify widget args))))
1572
1573 (defun custom-redraw (widget)
1574   "Redraw WIDGET with current settings."
1575   (let ((line (count-lines (point-min) (point)))
1576         (column (current-column))
1577         (pos (point))
1578         (from (marker-position (widget-get widget :from)))
1579         (to (marker-position (widget-get widget :to))))
1580     (save-excursion
1581       (widget-value-set widget (widget-value widget))
1582       (custom-redraw-magic widget))
1583     (when (and (>= pos from) (<= pos to))
1584       (condition-case nil
1585           (progn
1586             (if (> column 0)
1587                 (goto-line line)
1588               (goto-line (1+ line)))
1589             (move-to-column column))
1590         (error nil)))))
1591
1592 (defun custom-redraw-magic (widget)
1593   "Redraw WIDGET state with current settings."
1594   (while widget
1595     (let ((magic (widget-get widget :custom-magic)))
1596       (cond (magic
1597              (widget-value-set magic (widget-value magic))
1598              (when (setq widget (widget-get widget :group))
1599                (custom-group-state-update widget)))
1600             (t
1601              (setq widget nil)))))
1602   (widget-setup))
1603
1604 (defun custom-show (widget value)
1605   "Non-nil if WIDGET should be shown with VALUE by default."
1606   (let ((show (widget-get widget :custom-show)))
1607     (cond ((null show)
1608            nil)
1609           ((eq t show)
1610            t)
1611           (t
1612            (funcall show widget value)))))
1613
1614 (defvar custom-load-recursion nil
1615   "Hack to avoid recursive dependencies.")
1616
1617 (defun custom-load-symbol (symbol)
1618   "Load all dependencies for SYMBOL."
1619   (unless custom-load-recursion
1620     (let ((custom-load-recursion t)
1621           (loads (get symbol 'custom-loads))
1622           load)
1623       (while loads
1624         (setq load (car loads)
1625               loads (cdr loads))
1626         (cond ((symbolp load)
1627                (condition-case nil
1628                    (require load)
1629                  (error nil)))
1630               ;; Don't reload a file already loaded.
1631               ((and (boundp 'preloaded-file-list)
1632                     (member load preloaded-file-list)))
1633               ((assoc load load-history))
1634               ((assoc (locate-library load) load-history))
1635               (t
1636                (condition-case nil
1637                    ;; Without this, we would load cus-edit recursively.
1638                    ;; We are still loading it when we call this,
1639                    ;; and it is not in load-history yet.
1640                    (or (equal load "cus-edit")
1641                        (load-library load))
1642                  (error nil))))))))
1643
1644 (defun custom-load-widget (widget)
1645   "Load all dependencies for WIDGET."
1646   (custom-load-symbol (widget-value widget)))
1647
1648 (defun custom-unloaded-symbol-p (symbol)
1649   "Return non-nil if the dependencies of SYMBOL has not yet been loaded."
1650   (let ((found nil)
1651         (loads (get symbol 'custom-loads))
1652         load)
1653     (while loads
1654       (setq load (car loads)
1655             loads (cdr loads))
1656       (cond ((symbolp load)
1657              (unless (featurep load)
1658                (setq found t)))
1659             ((assoc load load-history))
1660             ((assoc (locate-library load) load-history)
1661              ;; #### WTF???
1662              (message nil))
1663             (t
1664              (setq found t))))
1665     found))
1666
1667 (defun custom-unloaded-widget-p (widget)
1668   "Return non-nil if the dependencies of WIDGET has not yet been loaded."
1669   (custom-unloaded-symbol-p (widget-value widget)))
1670
1671 (defun custom-toggle-hide (widget)
1672   "Toggle visibility of WIDGET."
1673   (custom-load-widget widget)
1674   (let ((state (widget-get widget :custom-state)))
1675     (cond ((memq state '(invalid modified))
1676            (error "There are unset changes"))
1677           ((eq state 'hidden)
1678            (widget-put widget :custom-state 'unknown))
1679           (t
1680            (widget-put widget :documentation-shown nil)
1681            (widget-put widget :custom-state 'hidden)))
1682     (custom-redraw widget)
1683     (widget-setup)))
1684
1685 (defun custom-toggle-parent (widget &rest ignore)
1686   "Toggle visibility of parent of WIDGET."
1687   (custom-toggle-hide (widget-get widget :parent)))
1688
1689 (defun custom-add-see-also (widget &optional prefix)
1690   "Add `See also ...' to WIDGET if there are any links.
1691 Insert PREFIX first if non-nil."
1692   (let* ((symbol (widget-get widget :value))
1693          (links (get symbol 'custom-links))
1694          (many (> (length links) 2))
1695          (buttons (widget-get widget :buttons))
1696          (indent (widget-get widget :indent)))
1697     (when links
1698       (when indent
1699         (insert-char ?\  indent))
1700       (when prefix
1701         (insert prefix))
1702       (insert "See also ")
1703       (while links
1704         (push (widget-create-child-and-convert widget (car links))
1705               buttons)
1706         (setq links (cdr links))
1707         (cond ((null links)
1708                (insert ".\n"))
1709               ((null (cdr links))
1710                (if many
1711                    (insert ", and ")
1712                  (insert " and ")))
1713               (t
1714                (insert ", "))))
1715       (widget-put widget :buttons buttons))))
1716
1717 (defun custom-add-parent-links (widget &optional initial-string)
1718   "Add \"Parent groups: ...\" to WIDGET if the group has parents.
1719 The value if non-nil if any parents were found.
1720 If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
1721   (let ((name (widget-value widget))
1722         (type (widget-type widget))
1723         (buttons (widget-get widget :buttons))
1724         (start (point))
1725         found)
1726     (insert (or initial-string "Parent groups:"))
1727     (maphash (lambda (group ignore)
1728                (let ((entry (assq name (get group 'custom-group))))
1729                  (when (eq (nth 1 entry) type)
1730                    (insert " ")
1731                    (push (widget-create-child-and-convert
1732                           widget 'custom-group-link
1733                           :tag (custom-unlispify-tag-name group)
1734                           group)
1735                          buttons)
1736                    (setq found t))))
1737              custom-group-hash-table)
1738     (widget-put widget :buttons buttons)
1739     (if found
1740         (insert "\n")
1741       (delete-region start (point)))
1742     found))
1743
1744 ;;; The `custom-comment' Widget.
1745
1746 ;; like the editable field
1747 (defface custom-comment-face '((((class grayscale color)
1748                                  (background light))
1749                                 (:background "gray85"))
1750                                (((class grayscale color)
1751                                  (background dark))
1752                                 (:background "dim gray"))
1753                                (t
1754                                 (:italic t)))
1755   "Face used for comments on variables or faces"
1756   :group 'custom-faces)
1757
1758 ;; like font-lock-comment-face
1759 (defface custom-comment-tag-face
1760   '((((class color) (background dark)) (:foreground "gray80"))
1761     (((class color) (background light)) (:foreground "blue4"))
1762     (((class grayscale) (background light))
1763      (:foreground "DimGray" :bold t :italic t))
1764     (((class grayscale) (background dark))
1765      (:foreground "LightGray" :bold t :italic t))
1766     (t (:bold t)))
1767   "Face used for variables or faces comment tags"
1768   :group 'custom-faces)
1769
1770 (define-widget 'custom-comment 'string
1771   "User comment"
1772   :tag "Comment"
1773   :help-echo "Edit a comment here"
1774   :sample-face 'custom-comment-tag-face
1775   :value-face 'custom-comment-face
1776   :value-set 'custom-comment-value-set
1777   :create 'custom-comment-create
1778   :delete 'custom-comment-delete)
1779
1780 (defun custom-comment-create (widget)
1781   (let (ext)
1782     (widget-default-create widget)
1783     (widget-put widget :comment-extent
1784                 (setq ext (make-extent (widget-get widget :from)
1785                                        (widget-get widget :to))))
1786     (set-extent-property ext 'start-open t)
1787     (when (equal (widget-get widget :value) "")
1788       (set-extent-property ext 'invisible t))
1789     ))
1790
1791 (defun custom-comment-delete (widget)
1792   (widget-default-delete widget)
1793   (delete-extent (widget-get widget :comment-extent)))
1794
1795 (defun custom-comment-value-set (widget value)
1796   (widget-default-value-set widget value)
1797   (if (equal value "")
1798       (set-extent-property (widget-get widget :comment-extent)
1799                            'invisible t)
1800     (set-extent-property (widget-get widget :comment-extent)
1801                          'invisible nil)))
1802
1803 ;; Those functions are for the menu. WIDGET is NOT the comment widget. It's
1804 ;; the global custom one
1805 (defun custom-comment-show (widget)
1806   (set-extent-property
1807    (widget-get (widget-get widget :comment-widget) :comment-extent)
1808    'invisible nil))
1809
1810 (defun custom-comment-invisible-p (widget)
1811   (extent-property
1812    (widget-get (widget-get widget :comment-widget) :comment-extent)
1813    'invisible))
1814
1815 ;;; The `custom-variable' Widget.
1816
1817 (defface custom-variable-tag-face '((((class color)
1818                                       (background dark))
1819                                      (:foreground "light blue" :underline t))
1820                                     (((class color)
1821                                       (background light))
1822                                      (:foreground "blue" :underline t))
1823                                     (t (:underline t)))
1824   "Face used for unpushable variable tags."
1825   :group 'custom-faces)
1826
1827 (defface custom-variable-button-face '((t (:underline t :bold t)))
1828   "Face used for pushable variable tags."
1829   :group 'custom-faces)
1830
1831 (defcustom custom-variable-default-form 'edit
1832   "Default form of displaying variable values."
1833   :type '(choice (const edit)
1834                  (const lisp))
1835   :group 'custom-buffer)
1836
1837 (define-widget 'custom-variable 'custom
1838   "Customize variable."
1839   :format "%v"
1840   :help-echo "Set or reset this variable"
1841   :documentation-property 'variable-documentation
1842   :custom-category 'option
1843   :custom-state nil
1844   :custom-menu 'custom-variable-menu-create
1845   :custom-form nil ; defaults to value of `custom-variable-default-form'
1846   :value-create 'custom-variable-value-create
1847   :action 'custom-variable-action
1848   :custom-set 'custom-variable-set
1849   :custom-save 'custom-variable-save
1850   :custom-reset-current 'custom-redraw
1851   :custom-reset-saved 'custom-variable-reset-saved
1852   :custom-reset-standard 'custom-variable-reset-standard)
1853
1854 (defun custom-variable-type (symbol)
1855   "Return a widget suitable for editing the value of SYMBOL.
1856 If SYMBOL has a `custom-type' property, use that.
1857 Otherwise, look up symbol in `custom-guess-type-alist'."
1858   (let* ((type (or (get symbol 'custom-type)
1859                    (and (not (get symbol 'standard-value))
1860                         (custom-guess-type symbol))
1861                    'sexp))
1862          (options (get symbol 'custom-options))
1863          (tmp (if (listp type)
1864                   (copy-sequence type)
1865                 (list type))))
1866     (when options
1867       (widget-put tmp :options options))
1868     tmp))
1869
1870 (defun custom-variable-value-create (widget)
1871   "Here is where you edit the variables value."
1872   (custom-load-widget widget)
1873   (unless (widget-get widget :custom-form)
1874     (widget-put widget :custom-form custom-variable-default-form))
1875   (let* ((buttons (widget-get widget :buttons))
1876          (children (widget-get widget :children))
1877          (form (widget-get widget :custom-form))
1878          (state (widget-get widget :custom-state))
1879          (symbol (widget-get widget :value))
1880          (tag (widget-get widget :tag))
1881          (type (custom-variable-type symbol))
1882          (conv (widget-convert type))
1883          (get (or (get symbol 'custom-get) 'default-value))
1884          (prefix (widget-get widget :custom-prefix))
1885          (last (widget-get widget :custom-last))
1886          (value (if (default-boundp symbol)
1887                     (funcall get symbol)
1888                   (widget-get conv :value))))
1889     ;; If the widget is new, the child determine whether it is hidden.
1890     (cond (state)
1891           ((custom-show type value)
1892            (setq state 'unknown))
1893           (t
1894            (setq state 'hidden)))
1895     ;; If we don't know the state, see if we need to edit it in lisp form.
1896     (when (eq state 'unknown)
1897       (unless (widget-apply conv :match value)
1898         ;; (widget-apply (widget-convert type) :match value)
1899         (setq form 'mismatch)))
1900     ;; Now we can create the child widget.
1901     (cond ((eq custom-buffer-style 'tree)
1902            (insert prefix (if last " `--- " " |--- "))
1903            (push (widget-create-child-and-convert
1904                   widget 'custom-browse-variable-tag)
1905                  buttons)
1906            (insert " " tag "\n")
1907            (widget-put widget :buttons buttons))
1908           ((eq state 'hidden)
1909            ;; Indicate hidden value.
1910            (push (widget-create-child-and-convert
1911                   widget 'item
1912                   :format "%{%t%}: "
1913                   :sample-face 'custom-variable-tag-face
1914                   :tag tag
1915                   :parent widget)
1916                  buttons)
1917            (push (widget-create-child-and-convert
1918                   widget 'visibility
1919                   :help-echo "Show the value of this option"
1920                   :action 'custom-toggle-parent
1921                   nil)
1922                  buttons))
1923           ((memq form '(lisp mismatch))
1924            ;; In lisp mode edit the saved value when possible.
1925            (let* ((value (cond ((get symbol 'saved-value)
1926                                 (car (get symbol 'saved-value)))
1927                                ((get symbol 'standard-value)
1928                                 (car (get symbol 'standard-value)))
1929                                ((default-boundp symbol)
1930                                 (custom-quote (funcall get symbol)))
1931                                (t
1932                                 (custom-quote (widget-get conv :value))))))
1933              (insert (symbol-name symbol) ": ")
1934              (push (widget-create-child-and-convert
1935                     widget 'visibility
1936                     :help-echo "Hide the value of this option"
1937                     :action 'custom-toggle-parent
1938                     t)
1939                    buttons)
1940              (insert " ")
1941              (push (widget-create-child-and-convert
1942                     widget 'sexp
1943                     :button-face 'custom-variable-button-face
1944                     :format "%v"
1945                     :tag (symbol-name symbol)
1946                     :parent widget
1947                     :value value)
1948                    children)))
1949           (t
1950            ;; Edit mode.
1951            (let* ((format (widget-get type :format))
1952                   tag-format value-format)
1953              (while (not (string-match ":" format))
1954                (setq format (signal 'error (list "Bad format" format))))
1955              (setq tag-format (substring format 0 (match-end 0)))
1956              (setq value-format (substring format (match-end 0)))
1957              (push (widget-create-child-and-convert
1958                     widget 'item
1959                     :format tag-format
1960                     :action 'custom-tag-action
1961                     :help-echo "Change value of this option"
1962                     :mouse-down-action 'custom-tag-mouse-down-action
1963                     :button-face 'custom-variable-button-face
1964                     :sample-face 'custom-variable-tag-face
1965                     tag)
1966                    buttons)
1967              (insert " ")
1968              (push (widget-create-child-and-convert
1969                   widget 'visibility
1970                   :help-echo "Hide the value of this option"
1971                   :action 'custom-toggle-parent
1972                   t)
1973                  buttons)
1974              (push (widget-create-child-and-convert
1975                     widget type
1976                     :format value-format
1977                     :value value)
1978                    children))))
1979     (unless (eq custom-buffer-style 'tree)
1980       (unless (eq (preceding-char) ?\n)
1981         (widget-insert "\n"))
1982       ;; Create the magic button.
1983       (let ((magic (widget-create-child-and-convert
1984                     widget 'custom-magic nil)))
1985         (widget-put widget :custom-magic magic)
1986         (push magic buttons))
1987       ;; Insert documentation.
1988       ;; #### NOTE: this is ugly!!!! I need to do update the :buttons property
1989       ;; before the call to `widget-default-format-handler'. Otherwise, I
1990       ;; loose my current `buttons'. This function shouldn't be called like
1991       ;; this anyway. The doc string widget should be added like the others.
1992       ;; --dv
1993       (widget-put widget :buttons buttons)
1994       (widget-default-format-handler widget ?h)
1995       ;; The comment field
1996       (unless (eq state 'hidden)
1997         (let* ((comment (get symbol 'variable-comment))
1998                (comment-widget
1999                 (widget-create-child-and-convert
2000                  widget 'custom-comment
2001                  :parent widget
2002                  :value (or comment ""))))
2003           (widget-put widget :comment-widget comment-widget)
2004           ;; Don't push it !!! Custom assumes that the first child is the
2005           ;; value one.
2006           (setq children (append children (list comment-widget)))))
2007       ;; Update the rest of the properties properties.
2008       (widget-put widget :custom-form form)
2009       (widget-put widget :children children)
2010       ;; Now update the state.
2011       (if (eq state 'hidden)
2012           (widget-put widget :custom-state state)
2013         (custom-variable-state-set widget))
2014       ;; See also.
2015       (unless (eq state 'hidden)
2016         (when (eq (widget-get widget :custom-level) 1)
2017           (custom-add-parent-links widget))
2018         (custom-add-see-also widget)))))
2019
2020 (defun custom-tag-action (widget &rest args)
2021   "Pass :action to first child of WIDGET's parent."
2022   (apply 'widget-apply (car (widget-get (widget-get widget :parent) :children))
2023          :action args))
2024
2025 (defun custom-tag-mouse-down-action (widget &rest args)
2026   "Pass :mouse-down-action to first child of WIDGET's parent."
2027   (apply 'widget-apply (car (widget-get (widget-get widget :parent) :children))
2028          :mouse-down-action args))
2029
2030 (defun custom-variable-state-set (widget)
2031   "Set the state of WIDGET."
2032   (let* ((symbol (widget-value widget))
2033          (get (or (get symbol 'custom-get) 'default-value))
2034          (value (if (default-boundp symbol)
2035                     (funcall get symbol)
2036                   (widget-get widget :value)))
2037          (comment (get symbol 'variable-comment))
2038          tmp
2039          temp
2040          (state (cond ((progn (setq tmp (get symbol 'customized-value))
2041                               (setq temp
2042                                     (get symbol 'customized-variable-comment))
2043                               (or tmp temp))
2044                        (if (condition-case nil
2045                                (and (equal value (eval (car tmp)))
2046                                     (equal comment temp))
2047                              (error nil))
2048                            'set
2049                          'changed))
2050                       ((progn (setq tmp (get symbol 'saved-value))
2051                               (setq temp (get symbol 'saved-variable-comment))
2052                               (or tmp temp))
2053                        (if (condition-case nil
2054                                (and (equal value (eval (car tmp)))
2055                                     (equal comment temp))
2056                              (error nil))
2057                            'saved
2058                          'changed))
2059                       ((setq tmp (get symbol 'standard-value))
2060                        (if (condition-case nil
2061                                (and (equal value (eval (car tmp)))
2062                                     (equal comment nil))
2063                              (error nil))
2064                            'standard
2065                          'changed))
2066                       (t 'rogue))))
2067     (widget-put widget :custom-state state)))
2068
2069 (defvar custom-variable-menu
2070   '(("Set for Current Session" custom-variable-set
2071      (lambda (widget)
2072        (eq (widget-get widget :custom-state) 'modified)))
2073     ("Save for Future Sessions" custom-variable-save
2074      (lambda (widget)
2075        (memq (widget-get widget :custom-state) '(modified set changed rogue))))
2076     ("Reset to Current" custom-redraw
2077      (lambda (widget)
2078        (and (default-boundp (widget-value widget))
2079             (memq (widget-get widget :custom-state) '(modified changed)))))
2080     ("Reset to Saved" custom-variable-reset-saved
2081      (lambda (widget)
2082        (and (or (get (widget-value widget) 'saved-value)
2083                 (get (widget-value widget) 'saved-variable-comment))
2084             (memq (widget-get widget :custom-state)
2085                   '(modified set changed rogue)))))
2086     ("Reset to Standard Settings" custom-variable-reset-standard
2087      (lambda (widget)
2088        (and (get (widget-value widget) 'standard-value)
2089             (memq (widget-get widget :custom-state)
2090                   '(modified set changed saved rogue)))))
2091     ("---" ignore ignore)
2092     ("Add Comment" custom-comment-show custom-comment-invisible-p)
2093     ("---" ignore ignore)
2094     ("Don't show as Lisp expression" custom-variable-edit
2095      (lambda (widget)
2096        (eq (widget-get widget :custom-form) 'lisp)))
2097     ("Show as Lisp expression" custom-variable-edit-lisp
2098      (lambda (widget)
2099        (eq (widget-get widget :custom-form) 'edit))))
2100   "Alist of actions for the `custom-variable' widget.
2101 Each entry has the form (NAME ACTION FILTER) where NAME is the name of
2102 the menu entry, ACTION is the function to call on the widget when the
2103 menu is selected, and FILTER is a predicate which takes a `custom-variable'
2104 widget as an argument, and returns non-nil if ACTION is valid on that
2105 widget. If FILTER is nil, ACTION is always valid.")
2106
2107 (defun custom-variable-action (widget &optional event)
2108   "Show the menu for `custom-variable' WIDGET.
2109 Optional EVENT is the location for the menu."
2110   (if (eq (widget-get widget :custom-state) 'hidden)
2111       (custom-toggle-hide widget)
2112     (unless (eq (widget-get widget :custom-state) 'modified)
2113       (custom-variable-state-set widget))
2114     ;; Redrawing magic also depresses the state glyph.
2115     ;(custom-redraw-magic widget)
2116     (let* ((completion-ignore-case t)
2117            (answer (widget-choose (concat "Operation on "
2118                                           (custom-unlispify-tag-name
2119                                            (widget-get widget :value)))
2120                                   (custom-menu-filter custom-variable-menu
2121                                                       widget)
2122                                   event)))
2123       (if answer
2124           (funcall answer widget)))))
2125
2126 (defun custom-variable-edit (widget)
2127   "Edit value of WIDGET."
2128   (widget-put widget :custom-state 'unknown)
2129   (widget-put widget :custom-form 'edit)
2130   (custom-redraw widget))
2131
2132 (defun custom-variable-edit-lisp (widget)
2133   "Edit the lisp representation of the value of WIDGET."
2134   (widget-put widget :custom-state 'unknown)
2135   (widget-put widget :custom-form 'lisp)
2136   (custom-redraw widget))
2137
2138 (defun custom-variable-set (widget)
2139   "Set the current value for the variable being edited by WIDGET."
2140   (let* ((form (widget-get widget :custom-form))
2141          (state (widget-get widget :custom-state))
2142          (child (car (widget-get widget :children)))
2143          (symbol (widget-value widget))
2144          (set (or (get symbol 'custom-set) 'set-default))
2145          (comment-widget (widget-get widget :comment-widget))
2146          (comment (widget-value comment-widget))
2147          val)
2148     (cond ((eq state 'hidden)
2149            (error "Cannot set hidden variable"))
2150           ((setq val (widget-apply child :validate))
2151            (goto-char (widget-get val :from))
2152            (error "%s" (widget-get val :error)))
2153           ((memq form '(lisp mismatch))
2154            (when (equal comment "")
2155              (setq comment nil)
2156              ;; Make the comment invisible by hand if it's empty
2157              (set-extent-property (widget-get comment-widget :comment-extent)
2158                                   'invisible t))
2159            (funcall set symbol (eval (setq val (widget-value child))))
2160            (put symbol 'customized-value (list val))
2161            (put symbol 'variable-comment comment)
2162            (put symbol 'customized-variable-comment comment))
2163           (t
2164            (when (equal comment "")
2165              (setq comment nil)
2166              ;; Make the comment invisible by hand if it's empty
2167              (set-extent-property (widget-get comment-widget :comment-extent)
2168                                   'invisible t))
2169            (funcall set symbol (setq val (widget-value child)))
2170            (put symbol 'customized-value (list (custom-quote val)))
2171            (put symbol 'variable-comment comment)
2172            (put symbol 'customized-variable-comment comment)))
2173     (custom-variable-state-set widget)
2174     (custom-redraw-magic widget)))
2175
2176 (defun custom-variable-save (widget)
2177   "Set and save the value for the variable being edited by WIDGET."
2178   (let* ((form (widget-get widget :custom-form))
2179          (state (widget-get widget :custom-state))
2180          (child (car (widget-get widget :children)))
2181          (symbol (widget-value widget))
2182          (set (or (get symbol 'custom-set) 'set-default))
2183          (comment-widget (widget-get widget :comment-widget))
2184          (comment (widget-value comment-widget))
2185          val)
2186     (cond ((eq state 'hidden)
2187            (error "Cannot set hidden variable"))
2188           ((setq val (widget-apply child :validate))
2189            (goto-char (widget-get val :from))
2190            (error "%s" (widget-get val :error)))
2191           ((memq form '(lisp mismatch))
2192            (when (equal comment "")
2193              (setq comment nil)
2194              ;; Make the comment invisible by hand if it's empty
2195              (set-extent-property (widget-get comment-widget :comment-extent)
2196                                   'invisible t))
2197            (put symbol 'saved-value (list (widget-value child)))
2198            (custom-push-theme 'theme-value symbol 'user
2199                               'set (list (widget-value child)))
2200            (funcall set symbol (eval (widget-value child)))
2201            (put symbol 'variable-comment comment)
2202            (put symbol 'saved-variable-comment comment))
2203           (t
2204            (when (equal comment "")
2205              (setq comment nil)
2206              ;; Make the comment invisible by hand if it's empty
2207              (set-extent-property (widget-get comment-widget :comment-extent)
2208                                   'invisible t))
2209            (put symbol
2210                 'saved-value (list (custom-quote (widget-value
2211                                                   child))))
2212            (custom-push-theme 'theme-value symbol 'user
2213                               'set (list (custom-quote (widget-value
2214                                                   child))))
2215            (funcall set symbol (widget-value child))
2216            (put symbol 'variable-comment comment)
2217            (put symbol 'saved-variable-comment comment)))
2218     (put symbol 'customized-value nil)
2219     (put symbol 'customized-variable-comment nil)
2220     (custom-save-all)
2221     (custom-variable-state-set widget)
2222     (custom-redraw-magic widget)))
2223
2224 (defun custom-variable-reset-saved (widget)
2225   "Restore the saved value for the variable being edited by WIDGET."
2226   (let* ((symbol (widget-value widget))
2227          (set (or (get symbol 'custom-set) 'set-default))
2228          (comment-widget (widget-get widget :comment-widget))
2229          (value (get symbol 'saved-value))
2230          (comment (get symbol 'saved-variable-comment)))
2231     (cond ((or value comment)
2232            (put symbol 'variable-comment comment)
2233            (condition-case nil
2234                (funcall set symbol (eval (car value)))
2235              (error nil)))
2236           (t
2237            (signal 'error (list "No saved value for variable" symbol))))
2238     (put symbol 'customized-value nil)
2239     (put symbol 'customized-variable-comment nil)
2240     (widget-put widget :custom-state 'unknown)
2241     ;; This call will possibly make the comment invisible
2242     (custom-redraw widget)))
2243
2244 (defun custom-variable-reset-standard (widget)
2245   "Restore the standard setting for the variable being edited by WIDGET."
2246   (let* ((symbol (widget-value widget))
2247          (set (or (get symbol 'custom-set) 'set-default))
2248          (comment-widget (widget-get widget :comment-widget)))
2249     (if (get symbol 'standard-value)
2250         (funcall set symbol (eval (car (get symbol 'standard-value))))
2251       (signal 'error (list "No standard setting known for variable" symbol)))
2252     (put symbol 'variable-comment nil)
2253     (put symbol 'customized-value nil)
2254     (put symbol 'customized-variable-comment nil)
2255     (when (or (get symbol 'saved-value) (get symbol 'saved-variable-comment))
2256       (put symbol 'saved-value nil)
2257       (custom-push-theme 'theme-value symbol 'user 'reset 'standard)
2258       ;; As a special optimizations we do not (explictly)
2259       ;; save resets to standard when no theme set the value.
2260       (if (null (cdr (get symbol 'theme-value)))
2261           (put symbol 'theme-value nil))
2262       (put symbol 'saved-variable-comment nil)
2263       (custom-save-all))
2264     (widget-put widget :custom-state 'unknown)
2265     ;; This call will possibly make the comment invisible
2266     (custom-redraw widget)))
2267
2268 ;;; The `custom-face-edit' Widget.
2269
2270 (define-widget 'custom-face-edit 'checklist
2271   "Edit face attributes."
2272   :format "%t: %v"
2273   :tag "Attributes"
2274   :extra-offset 12
2275   :button-args '(:help-echo "Control whether this attribute has any effect")
2276   :args (mapcar (lambda (att)
2277                   (list 'group
2278                         :inline t
2279                         :sibling-args (widget-get (nth 1 att) :sibling-args)
2280                         (list 'const :format "" :value (nth 0 att))
2281                         (nth 1 att)))
2282                 custom-face-attributes))
2283
2284 ;;; The `custom-display' Widget.
2285
2286 (define-widget 'custom-display 'menu-choice
2287   "Select a display type."
2288   :tag "Display"
2289   :value t
2290   :help-echo "Specify frames where the face attributes should be used"
2291   :args '((const :tag "all" t)
2292           (checklist
2293            :offset 0
2294            :extra-offset 9
2295            :args ((group :sibling-args (:help-echo "\
2296 Only match the specified window systems")
2297                          (const :format "Type: "
2298                                 type)
2299                          (checklist :inline t
2300                                     :offset 0
2301                                     (const :format "X "
2302                                            :sibling-args (:help-echo "\
2303 The X11 Window System")
2304                                            x)
2305                                     (const :format "PM "
2306                                            :sibling-args (:help-echo "\
2307 OS/2 Presentation Manager")
2308                                            pm)
2309                                     (const :format "MSWindows "
2310                                            :sibling-args (:help-echo "\
2311 Microsoft Windows, displays")
2312                                            mswindows)
2313                                     (const :format "MSPrinter "
2314                                            :sibling-args (:help-echo "\
2315 Microsoft Windows, printers")
2316                                            msprinter)
2317                                     (const :format "TTY%n"
2318                                            :sibling-args (:help-echo "\
2319 Plain text terminals")
2320                                            tty)))
2321                   (group :sibling-args (:help-echo "\
2322 Only match display or printer devices")
2323                          (const :format "Output: "
2324                                 class)
2325                          (checklist :inline t
2326                                     :offset 0
2327                                     (const :format "Display "
2328                                            :sibling-args (:help-echo "\
2329 Match display devices")
2330                                            display)
2331                                     (const :format "Printer%n"
2332                                            :sibling-args (:help-echo "\
2333 Match printer devices")
2334                                            printer)))
2335                   (group :sibling-args (:help-echo "\
2336 Only match the frames with the specified color support")
2337                          (const :format "Color support: "
2338                                 class)
2339                          (checklist :inline t
2340                                     :offset 0
2341                                     (const :format "Color "
2342                                            :sibling-args (:help-echo "\
2343 Match color frames")
2344                                            color)
2345                                     (const :format "Grayscale "
2346                                            :sibling-args (:help-echo "\
2347 Match grayscale frames")
2348                                            grayscale)
2349                                     (const :format "Monochrome%n"
2350                                            :sibling-args (:help-echo "\
2351 Match frames with no color support")
2352                                            mono)))
2353                   (group :sibling-args (:help-echo "\
2354 Only match frames with the specified intensity")
2355                          (const :format "\
2356 Background brightness: "
2357                                 background)
2358                          (checklist :inline t
2359                                     :offset 0
2360                                     (const :format "Light "
2361                                            :sibling-args (:help-echo "\
2362 Match frames with light backgrounds")
2363                                            light)
2364                                     (const :format "Dark\n"
2365                                            :sibling-args (:help-echo "\
2366 Match frames with dark backgrounds")
2367                                            dark)))))))
2368
2369 ;;; The `custom-face' Widget.
2370
2371 (defface custom-face-tag-face '((t (:underline t)))
2372   "Face used for face tags."
2373   :group 'custom-faces)
2374
2375 (defcustom custom-face-default-form 'selected
2376   "Default form of displaying face definition."
2377   :type '(choice (const all)
2378                  (const selected)
2379                  (const lisp))
2380   :group 'custom-buffer)
2381
2382 (define-widget 'custom-face 'custom
2383   "Customize face."
2384   :sample-face 'custom-face-tag-face
2385   :help-echo "Set or reset this face"
2386   :documentation-property '(lambda (face)
2387                              (face-doc-string face))
2388   :value-create 'custom-face-value-create
2389   :action 'custom-face-action
2390   :custom-category 'face
2391   :custom-form nil ; defaults to value of `custom-face-default-form'
2392   :custom-set 'custom-face-set
2393   :custom-save 'custom-face-save
2394   :custom-reset-current 'custom-redraw
2395   :custom-reset-saved 'custom-face-reset-saved
2396   :custom-reset-standard 'custom-face-reset-standard
2397   :custom-menu 'custom-face-menu-create)
2398
2399 (define-widget 'custom-face-all 'editable-list
2400   "An editable list of display specifications and attributes."
2401   :entry-format "%i %d %v"
2402   :insert-button-args '(:help-echo "Insert new display specification here")
2403   :append-button-args '(:help-echo "Append new display specification here")
2404   :delete-button-args '(:help-echo "Delete this display specification")
2405   :args '((group :format "%v" custom-display custom-face-edit)))
2406
2407 (defconst custom-face-all (widget-convert 'custom-face-all)
2408   "Converted version of the `custom-face-all' widget.")
2409
2410 (define-widget 'custom-display-unselected 'item
2411   "A display specification that doesn't match the selected display."
2412   :match 'custom-display-unselected-match)
2413
2414 (defun custom-display-unselected-match (widget value)
2415   "Non-nil if VALUE is an unselected display specification."
2416   (not (face-spec-set-match-display value (selected-frame))))
2417
2418 (define-widget 'custom-face-selected 'group
2419   "Edit the attributes of the selected display in a face specification."
2420   :args '((repeat :format ""
2421                   :inline t
2422                   (group custom-display-unselected sexp))
2423           (group (sexp :format "") custom-face-edit)
2424           (repeat :format ""
2425                   :inline t
2426                   sexp)))
2427
2428 (defconst custom-face-selected (widget-convert 'custom-face-selected)
2429   "Converted version of the `custom-face-selected' widget.")
2430
2431 (defun custom-face-value-create (widget)
2432   "Create a list of the display specifications for WIDGET."
2433   (let ((buttons (widget-get widget :buttons))
2434         children
2435         (symbol (widget-get widget :value))
2436         (tag (widget-get widget :tag))
2437         (state (widget-get widget :custom-state))
2438         (begin (point))
2439         (is-last (widget-get widget :custom-last))
2440         (prefix (widget-get widget :custom-prefix)))
2441     (unless tag
2442       (setq tag (prin1-to-string symbol)))
2443     (cond ((eq custom-buffer-style 'tree)
2444            (insert prefix (if is-last " `--- " " |--- "))
2445            (push (widget-create-child-and-convert
2446                   widget 'custom-browse-face-tag)
2447                  buttons)
2448            (insert " " tag "\n")
2449            (widget-put widget :buttons buttons))
2450           (t
2451            ;; Create tag.
2452            (insert tag)
2453            (if (eq custom-buffer-style 'face)
2454                (insert " ")
2455              (widget-specify-sample widget begin (point))
2456              (insert ": "))
2457            ;; Sample.
2458            (and (not (find-face symbol))
2459                 ;; XEmacs cannot display uninitialized faces.
2460                 (make-face symbol))
2461            (push (widget-create-child-and-convert widget 'item
2462                                                   :format "(%{%t%})"
2463                                                   :sample-face symbol
2464                                                   :tag "sample")
2465                  buttons)
2466            ;; Visibility.
2467            (insert " ")
2468            (push (widget-create-child-and-convert
2469                   widget 'visibility
2470                   :help-echo "Hide or show this face"
2471                   :action 'custom-toggle-parent
2472                   (not (eq state 'hidden)))
2473                  buttons)
2474            ;; Magic.
2475            (insert "\n")
2476            (let ((magic (widget-create-child-and-convert
2477                          widget 'custom-magic nil)))
2478              (widget-put widget :custom-magic magic)
2479              (push magic buttons))
2480            ;; Update buttons.
2481            (widget-put widget :buttons buttons)
2482            ;; Insert documentation.
2483            (widget-default-format-handler widget ?h)
2484            ;; The comment field
2485            (unless (eq state 'hidden)
2486              (let* ((comment (get symbol 'face-comment))
2487                     (comment-widget
2488                      (widget-create-child-and-convert
2489                       widget 'custom-comment
2490                       :parent widget
2491                       :value (or comment ""))))
2492                (widget-put widget :comment-widget comment-widget)
2493                (push comment-widget children)))
2494            ;; See also.
2495            (unless (eq state 'hidden)
2496              (when (eq (widget-get widget :custom-level) 1)
2497                (custom-add-parent-links widget))
2498              (custom-add-see-also widget))
2499            ;; Editor.
2500            (unless (eq (preceding-char) ?\n)
2501              (insert "\n"))
2502            (unless (eq state 'hidden)
2503              (message "Creating face editor...")
2504              (custom-load-widget widget)
2505              (unless (widget-get widget :custom-form)
2506                  (widget-put widget :custom-form custom-face-default-form))
2507              (let* ((symbol (widget-value widget))
2508                     (spec (custom-face-get-spec symbol))
2509                     (form (widget-get widget :custom-form))
2510                     (indent (widget-get widget :indent))
2511                     (edit (widget-create-child-and-convert
2512                            widget
2513                            (cond ((and (eq form 'selected)
2514                                        (widget-apply custom-face-selected
2515                                                      :match spec))
2516                                   (when indent (insert-char ?\  indent))
2517                                   'custom-face-selected)
2518                                  ((and (not (eq form 'lisp))
2519                                        (widget-apply custom-face-all
2520                                                      :match spec))
2521                                   'custom-face-all)
2522                                  (t
2523                                   (when indent (insert-char ?\  indent))
2524                                   'sexp))
2525                            :value spec)))
2526                (custom-face-state-set widget)
2527                (push edit children)
2528                (widget-put widget :children children))
2529              (message "Creating face editor...done"))))))
2530
2531 (defvar custom-face-menu
2532   '(("Set for Current Session" custom-face-set)
2533     ("Save for Future Sessions" custom-face-save)
2534     ("Reset to Saved" custom-face-reset-saved
2535      (lambda (widget)
2536        (or (get (widget-value widget) 'saved-face)
2537            (get (widget-value widget) 'saved-face-comment))))
2538     ("Reset to Standard Setting" custom-face-reset-standard
2539      (lambda (widget)
2540        (get (widget-value widget) 'face-defface-spec)))
2541     ("---" ignore ignore)
2542     ("Add Comment" custom-comment-show custom-comment-invisible-p)
2543     ("---" ignore ignore)
2544     ("Show all display specs" custom-face-edit-all
2545      (lambda (widget)
2546        (not (eq (widget-get widget :custom-form) 'all))))
2547     ("Just current attributes" custom-face-edit-selected
2548      (lambda (widget)
2549        (not (eq (widget-get widget :custom-form) 'selected))))
2550     ("Show as Lisp expression" custom-face-edit-lisp
2551      (lambda (widget)
2552        (not (eq (widget-get widget :custom-form) 'lisp)))))
2553   "Alist of actions for the `custom-face' widget.
2554 Each entry has the form (NAME ACTION FILTER) where NAME is the name of
2555 the menu entry, ACTION is the function to call on the widget when the
2556 menu is selected, and FILTER is a predicate which takes a `custom-face'
2557 widget as an argument, and returns non-nil if ACTION is valid on that
2558 widget. If FILTER is nil, ACTION is always valid.")
2559
2560 (defun custom-face-edit-selected (widget)
2561   "Edit selected attributes of the value of WIDGET."
2562   (widget-put widget :custom-state 'unknown)
2563   (widget-put widget :custom-form 'selected)
2564   (custom-redraw widget))
2565
2566 (defun custom-face-edit-all (widget)
2567   "Edit all attributes of the value of WIDGET."
2568   (widget-put widget :custom-state 'unknown)
2569   (widget-put widget :custom-form 'all)
2570   (custom-redraw widget))
2571
2572 (defun custom-face-edit-lisp (widget)
2573   "Edit the lisp representation of the value of WIDGET."
2574   (widget-put widget :custom-state 'unknown)
2575   (widget-put widget :custom-form 'lisp)
2576   (custom-redraw widget))
2577
2578 (defun custom-face-state-set (widget)
2579   "Set the state of WIDGET."
2580   (let* ((symbol (widget-value widget))
2581          (comment (get symbol 'face-comment))
2582          tmp temp)
2583     (widget-put widget :custom-state
2584                 (cond ((progn
2585                          (setq tmp (get symbol 'customized-face))
2586                          (setq temp (get symbol 'customized-face-comment))
2587                          (or tmp temp))
2588                        (if (equal temp comment)
2589                            'set
2590                          'changed))
2591                       ((progn
2592                          (setq tmp (get symbol 'saved-face))
2593                          (setq temp (get symbol 'saved-face-comment))
2594                          (or tmp temp))
2595                        (if (equal temp comment)
2596                            'saved
2597                          'changed))
2598                       ((get symbol 'face-defface-spec)
2599                        (if (equal comment nil)
2600                            'standard
2601                          'changed))
2602                       (t
2603                        'rogue)))))
2604
2605 (defun custom-face-action (widget &optional event)
2606   "Show the menu for `custom-face' WIDGET.
2607 Optional EVENT is the location for the menu."
2608   (if (eq (widget-get widget :custom-state) 'hidden)
2609       (custom-toggle-hide widget)
2610     (let* ((completion-ignore-case t)
2611            (symbol (widget-get widget :value))
2612            (answer (widget-choose (concat "Operation on "
2613                                           (custom-unlispify-tag-name symbol))
2614                                   (custom-menu-filter custom-face-menu
2615                                                       widget)
2616                                   event)))
2617       (if answer
2618           (funcall answer widget)))))
2619
2620 (defun custom-face-set (widget)
2621   "Make the face attributes in WIDGET take effect."
2622   (let* ((symbol (widget-value widget))
2623          (child (car (widget-get widget :children)))
2624          (value (widget-value child))
2625          (comment-widget (widget-get widget :comment-widget))
2626          (comment (widget-value comment-widget)))
2627     (when (equal comment "")
2628       (setq comment nil)
2629       ;; Make the comment invisible by hand if it's empty
2630       (set-extent-property (widget-get comment-widget :comment-extent)
2631                            'invisible t))
2632     (put symbol 'customized-face value)
2633     (face-spec-set symbol value nil '(custom))
2634     (put symbol 'customized-face-comment comment)
2635     (put symbol 'face-comment comment)
2636     (custom-face-state-set widget)
2637     (custom-redraw-magic widget)))
2638
2639 (defun custom-face-save (widget)
2640   "Make the face attributes in WIDGET default."
2641   (let* ((symbol (widget-value widget))
2642          (child (car (widget-get widget :children)))
2643          (value (widget-value child))
2644          (comment-widget (widget-get widget :comment-widget))
2645          (comment (widget-value comment-widget)))
2646     (when (equal comment "")
2647       (setq comment nil)
2648       ;; Make the comment invisible by hand if it's empty
2649       (set-extent-property (widget-get comment-widget :comment-extent)
2650                            'invisible t))
2651     (face-spec-set symbol value nil '(custom))
2652     (put symbol 'saved-face value)
2653     (custom-push-theme 'theme-face symbol 'user 'set value)
2654     (put symbol 'customized-face nil)
2655     (put symbol 'face-comment comment)
2656     (put symbol 'customized-face-comment nil)
2657     (put symbol 'saved-face-comment comment)
2658     (custom-save-all)
2659     (custom-face-state-set widget)
2660     (custom-redraw-magic widget)))
2661
2662 (defun custom-face-reset-saved (widget)
2663   "Restore WIDGET to the face's default attributes."
2664   (let* ((symbol (widget-value widget))
2665          (child (car (widget-get widget :children)))
2666          (value (get symbol 'saved-face))
2667          (comment (get symbol 'saved-face-comment))
2668          (comment-widget (widget-get widget :comment-widget)))
2669     (unless (or value comment)
2670       (signal 'error (list "No saved value for this face" symbol)))
2671     (put symbol 'customized-face nil)
2672     (put symbol 'customized-face-comment nil)
2673     (face-spec-set symbol value nil '(custom))
2674     (put symbol 'face-comment comment)
2675     (widget-value-set child value)
2676     ;; This call manages the comment visibility
2677     (widget-value-set comment-widget (or comment ""))
2678     (custom-face-state-set widget)
2679     (custom-redraw-magic widget)))
2680
2681 (defun custom-face-reset-standard (widget)
2682   "Restore WIDGET to the face's standard settings."
2683   (let* ((symbol (widget-value widget))
2684          (child (car (widget-get widget :children)))
2685          (value (get symbol 'face-defface-spec))
2686          (comment-widget (widget-get widget :comment-widget)))
2687     (unless value
2688       (signal 'error (list "No standard setting for this face" symbol)))
2689     (put symbol 'customized-face nil)
2690     (put symbol 'customized-face-comment nil)
2691     (when (or (get symbol 'saved-face) (get symbol 'saved-face-comment))
2692       (put symbol 'saved-face nil)
2693       (custom-push-theme 'theme-face symbol 'user 'reset 'standard)
2694       ;; Do not explictly save resets to standards without themes.
2695       (if (null (cdr (get symbol 'theme-face)))
2696           (put symbol  'theme-face nil))
2697       (put symbol 'saved-face-comment nil)
2698       (custom-save-all))
2699     (face-spec-set symbol value nil '(custom))
2700     (put symbol 'face-comment nil)
2701     (widget-value-set child value)
2702     ;; This call manages the comment visibility
2703     (widget-value-set comment-widget "")
2704     (custom-face-state-set widget)
2705     (custom-redraw-magic widget)))
2706
2707 ;;; The `face' Widget.
2708
2709 (define-widget 'face 'default
2710   "Select and customize a face."
2711   :convert-widget 'widget-value-convert-widget
2712   :button-prefix 'widget-push-button-prefix
2713   :button-suffix 'widget-push-button-suffix
2714   :format "%t: %[select face%] %v"
2715   :tag "Face"
2716   :value 'default
2717   :value-create 'widget-face-value-create
2718   :value-delete 'widget-face-value-delete
2719   :value-get 'widget-value-value-get
2720   :validate 'widget-children-validate
2721   :action 'widget-face-action
2722   :match (lambda (widget value) (symbolp value)))
2723
2724 (defun widget-face-value-create (widget)
2725   ;; Create a `custom-face' child.
2726   (let* ((symbol (widget-value widget))
2727          (custom-buffer-style 'face)
2728          (child (widget-create-child-and-convert
2729                  widget 'custom-face
2730                  :custom-level nil
2731                  :value symbol)))
2732     (custom-magic-reset child)
2733     (setq custom-options (cons child custom-options))
2734     (widget-put widget :children (list child))))
2735
2736 (defun widget-face-value-delete (widget)
2737   ;; Remove the child from the options.
2738   (let ((child (car (widget-get widget :children))))
2739     (setq custom-options (delq child custom-options))
2740     (widget-children-value-delete widget)))
2741
2742 (defvar face-history nil
2743   "History of entered face names.")
2744
2745 (defun widget-face-action (widget &optional event)
2746   "Prompt for a face."
2747   (let ((answer (completing-read "Face: "
2748                                  (mapcar (lambda (face)
2749                                            (list (symbol-name face)))
2750                                          (face-list))
2751                                  nil nil nil
2752                                  'face-history)))
2753     (unless (zerop (length answer))
2754       (widget-value-set widget (intern answer))
2755       (widget-apply widget :notify widget event)
2756       (widget-setup))))
2757
2758 ;;; The `hook' Widget.
2759
2760 (define-widget 'hook 'list
2761   "A emacs lisp hook"
2762   :value-to-internal (lambda (widget value)
2763                        (if (symbolp value)
2764                            (list value)
2765                          value))
2766   :match (lambda (widget value)
2767            (or (symbolp value)
2768                (widget-group-match widget value)))
2769   :convert-widget 'custom-hook-convert-widget
2770   :tag "Hook")
2771
2772 (defun custom-hook-convert-widget (widget)
2773   ;; Handle `:options'.
2774   (let* ((options (widget-get widget :options))
2775          (other `(editable-list :inline t
2776                                 :entry-format "%i %d%v"
2777                                 (function :format " %v")))
2778          (args (if options
2779                    (list `(checklist :inline t
2780                                      ,@(mapcar (lambda (entry)
2781                                                  `(function-item ,entry))
2782                                                options))
2783                          other)
2784                  (list other))))
2785     (widget-put widget :args args)
2786     widget))
2787
2788 ;;; The `plist' Widget.
2789
2790 (define-widget 'plist 'list
2791   "A property list."
2792   :match (lambda (widget value)
2793            (valid-plist-p value))
2794   :convert-widget 'custom-plist-convert-widget
2795   :tag "Property List")
2796
2797 ;; #### Should handle options better.
2798 (defun custom-plist-convert-widget (widget)
2799   (let* ((options (widget-get widget :options))
2800          (other `(editable-list :inline t
2801                                 (group :inline t
2802                                        (symbol :format "%t: %v "
2803                                                :size 10
2804                                                :tag "Property")
2805                                        (sexp :tag "Value"))))
2806          (args
2807           (if options
2808               `((checklist :inline t
2809                            ,@(mapcar 'custom-plist-process-option options))
2810                 ,other)
2811             (list other))))
2812     (widget-put widget :args args)
2813     widget))
2814
2815 (defun custom-plist-process-option (entry)
2816   `(group :inline t
2817           (const :tag "Property"
2818                  :format "%t: %v "
2819                  :size 10
2820                  ,entry)
2821           (sexp :tag "Value")))
2822
2823 ;;; The `custom-group-link' Widget.
2824
2825 (define-widget 'custom-group-link 'link
2826   "Show parent in other window when activated."
2827   :help-echo 'custom-group-link-help-echo
2828   :action 'custom-group-link-action)
2829
2830 (defun custom-group-link-help-echo (widget)
2831   (concat "Create customization buffer for the `"
2832           (custom-unlispify-tag-name (widget-value widget))
2833           "' group"))
2834
2835 (defun custom-group-link-action (widget &rest ignore)
2836   (customize-group (widget-value widget)))
2837
2838 ;;; The `custom-group' Widget.
2839
2840 (defcustom custom-group-tag-faces nil
2841   ;; In XEmacs, this ought to play games with font size.
2842   "Face used for group tags.
2843 The first member is used for level 1 groups, the second for level 2,
2844 and so forth.  The remaining group tags are shown with
2845 `custom-group-tag-face'."
2846   :type '(repeat face)
2847   :group 'custom-faces)
2848
2849 (defface custom-group-tag-face-1 '((((class color)
2850                                      (background dark))
2851                                     (:foreground "pink" :underline t))
2852                                    (((class color)
2853                                      (background light))
2854                                     (:foreground "red" :underline t))
2855                                    (t (:underline t)))
2856   "Face used for group tags.")
2857
2858 (defface custom-group-tag-face '((((class color)
2859                                    (background dark))
2860                                   (:foreground "light blue" :underline t))
2861                                  (((class color)
2862                                    (background light))
2863                                   (:foreground "blue" :underline t))
2864                                  (t (:underline t)))
2865   "Face used for low level group tags."
2866   :group 'custom-faces)
2867
2868 (define-widget 'custom-group 'custom
2869   "Customize group."
2870   :format "%v"
2871   :sample-face-get 'custom-group-sample-face-get
2872   :documentation-property 'group-documentation
2873   :help-echo "Set or reset all members of this group"
2874   :value-create 'custom-group-value-create
2875   :action 'custom-group-action
2876   :custom-category 'group
2877   :custom-set 'custom-group-set
2878   :custom-save 'custom-group-save
2879   :custom-reset-current 'custom-group-reset-current
2880   :custom-reset-saved 'custom-group-reset-saved
2881   :custom-reset-standard 'custom-group-reset-standard
2882   :custom-menu 'custom-group-menu-create)
2883
2884 (defun custom-group-sample-face-get (widget)
2885   ;; Use :sample-face.
2886   (or (nth (1- (widget-get widget :custom-level)) custom-group-tag-faces)
2887       'custom-group-tag-face))
2888
2889 (define-widget 'custom-group-visibility 'visibility
2890   "An indicator and manipulator for hidden group contents."
2891   :create 'custom-group-visibility-create)
2892
2893 (defun custom-group-visibility-create (widget)
2894   (let ((visible (widget-value widget)))
2895     (if visible
2896         (insert "--------")))
2897   (widget-default-create widget))
2898
2899 (defun custom-group-members (symbol groups-only)
2900   "Return SYMBOL's custom group members.
2901 If GROUPS-ONLY non-nil, return only those members that are groups."
2902   (if (not groups-only)
2903       (get symbol 'custom-group)
2904     (let (members)
2905       (dolist (entry (get symbol 'custom-group) (nreverse members))
2906         (when (eq (nth 1 entry) 'custom-group)
2907           (push entry members))))))
2908
2909 (defun custom-group-value-create (widget)
2910   "Insert a customize group for WIDGET in the current buffer."
2911   (let* ((state (widget-get widget :custom-state))
2912          (level (widget-get widget :custom-level))
2913          ;; (indent (widget-get widget :indent))
2914          (prefix (widget-get widget :custom-prefix))
2915          (buttons (widget-get widget :buttons))
2916          (tag (widget-get widget :tag))
2917          (symbol (widget-value widget))
2918          (members (custom-group-members symbol
2919                                         (and (eq custom-buffer-style 'tree)
2920                                              custom-browse-only-groups))))
2921     (cond ((and (eq custom-buffer-style 'tree)
2922                 (eq state 'hidden)
2923                 (or members (custom-unloaded-widget-p widget)))
2924            (custom-browse-insert-prefix prefix)
2925            (push (widget-create-child-and-convert
2926                   widget 'custom-browse-visibility
2927                   ;; :tag-glyph "plus"
2928                   :tag "+")
2929                  buttons)
2930            (insert "-- ")
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           ((and (eq custom-buffer-style 'tree)
2938                 (zerop (length members)))
2939            (custom-browse-insert-prefix prefix)
2940            (insert "[ ]-- ")
2941            ;; (widget-glyph-insert nil "[ ]" "empty")
2942            ;; (widget-glyph-insert nil "-- " "horizontal")
2943            (push (widget-create-child-and-convert
2944                   widget 'custom-browse-group-tag)
2945                  buttons)
2946            (insert " " tag "\n")
2947            (widget-put widget :buttons buttons))
2948           ((eq custom-buffer-style 'tree)
2949            (custom-browse-insert-prefix prefix)
2950            (custom-load-widget widget)
2951            (if (zerop (length members))
2952                (progn
2953                  (custom-browse-insert-prefix prefix)
2954                  (insert "[ ]-- ")
2955                  ;; (widget-glyph-insert nil "[ ]" "empty")
2956                  ;; (widget-glyph-insert nil "-- " "horizontal")
2957                  (push (widget-create-child-and-convert
2958                         widget 'custom-browse-group-tag)
2959                        buttons)
2960                  (insert " " tag "\n")
2961                  (widget-put widget :buttons buttons))
2962              (push (widget-create-child-and-convert
2963                     widget 'custom-browse-visibility
2964                     ;; :tag-glyph "minus"
2965                     :tag "-")
2966                    buttons)
2967              (insert "-\\ ")
2968              ;; (widget-glyph-insert nil "-\\ " "top")
2969              (push (widget-create-child-and-convert
2970                     widget 'custom-browse-group-tag)
2971                    buttons)
2972              (insert " " tag "\n")
2973              (widget-put widget :buttons buttons)
2974              (message "Creating group...")
2975              (let* ((members (custom-sort-items members
2976                               custom-browse-sort-alphabetically
2977                               custom-browse-order-groups))
2978                     (prefixes (widget-get widget :custom-prefixes))
2979                     (custom-prefix-list (custom-prefix-add symbol prefixes))
2980                     (extra-prefix (if (widget-get widget :custom-last)
2981                                       "   "
2982                                     " | "))
2983                     (prefix (concat prefix extra-prefix))
2984                     children entry)
2985                (while members
2986                  (setq entry (car members)
2987                        members (cdr members))
2988                  (push (widget-create-child-and-convert
2989                         widget (nth 1 entry)
2990                         :group widget
2991                         :tag (custom-unlispify-tag-name (nth 0 entry))
2992                         :custom-prefixes custom-prefix-list
2993                         :custom-level (1+ level)
2994                         :custom-last (null members)
2995                         :value (nth 0 entry)
2996                         :custom-prefix prefix)
2997                        children))
2998                (widget-put widget :children (reverse children)))
2999              (message "Creating group...done")))
3000           ;; Nested style.
3001           ((eq state 'hidden)
3002            ;; Create level indicator.
3003            (unless (eq custom-buffer-style 'links)
3004              (insert-char ?\  (* custom-buffer-indent (1- level)))
3005              (insert "-- "))
3006            ;; Create link indicator.
3007            (when (eq custom-buffer-style 'links)
3008              (insert " ")
3009              (push (widget-create-child-and-convert
3010                     widget 'custom-group-link
3011                     :tag "Open"
3012                     :tag-glyph '("open-up" "open-down")
3013                     symbol)
3014                    buttons)
3015              (insert " "))
3016            ;; Create tag.
3017            (let ((begin (point)))
3018              (insert tag)
3019              (widget-specify-sample widget begin (point)))
3020            (insert " group")
3021            ;; Create visibility indicator.
3022            (unless (eq custom-buffer-style 'links)
3023              (insert ": ")
3024              (push (widget-create-child-and-convert
3025                     widget 'custom-group-visibility
3026                     :help-echo "Show members of this group"
3027                     :action 'custom-toggle-parent
3028                     (not (eq state 'hidden)))
3029                    buttons))
3030            (insert " \n")
3031            ;; Create magic button.
3032            (let ((magic (widget-create-child-and-convert
3033                          widget 'custom-magic nil)))
3034              (widget-put widget :custom-magic magic)
3035              (push magic buttons))
3036            ;; Update buttons.
3037            (widget-put widget :buttons buttons)
3038            ;; Insert documentation.
3039            (if (and (eq custom-buffer-style 'links) (> level 1))
3040                (widget-put widget :documentation-indent 0))
3041            (widget-default-format-handler widget ?h))
3042           ;; Nested style.
3043           (t                            ;Visible.
3044            (custom-load-widget widget)
3045            ;; Update members
3046            (setq members (custom-group-members
3047                           symbol (and (eq custom-buffer-style 'tree)
3048                                       custom-browse-only-groups)))
3049            ;; Add parent groups references above the group.
3050            (if t    ;;; This should test that the buffer
3051                     ;;; was made to display a group.
3052                (when (eq level 1)
3053                  (if (custom-add-parent-links widget
3054                                               "Go to parent group:")
3055                      (insert "\n"))))
3056            ;; Create level indicator.
3057            (insert-char ?\  (* custom-buffer-indent (1- level)))
3058            (insert "/- ")
3059            ;; Create tag.
3060            (let ((start (point)))
3061              (insert tag)
3062              (widget-specify-sample widget start (point)))
3063            (insert " group: ")
3064            ;; Create visibility indicator.
3065            (unless (eq custom-buffer-style 'links)
3066              (insert "--------")
3067              (push (widget-create-child-and-convert
3068                     widget 'visibility
3069                     :help-echo "Hide members of this group"
3070                     :action 'custom-toggle-parent
3071                     (not (eq state 'hidden)))
3072                    buttons)
3073              (insert " "))
3074            ;; Create more dashes.
3075            ;; Use 76 instead of 75 to compensate for the temporary "<"
3076            ;; added by `widget-insert'.
3077            (insert-char ?- (- 76 (current-column)
3078                               (* custom-buffer-indent level)))
3079            (insert "\\\n")
3080            ;; Create magic button.
3081            (let ((magic (widget-create-child-and-convert
3082                          widget 'custom-magic
3083                          :indent 0
3084                          nil)))
3085              (widget-put widget :custom-magic magic)
3086              (push magic buttons))
3087            ;; Update buttons.
3088            (widget-put widget :buttons buttons)
3089            ;; Insert documentation.
3090            (widget-default-format-handler widget ?h)
3091            ;; Parent groups.
3092            (if nil  ;;; This should test that the buffer
3093                     ;;; was not made to display a group.
3094                (when (eq level 1)
3095                  (insert-char ?\  custom-buffer-indent)
3096                  (custom-add-parent-links widget)))
3097            (custom-add-see-also widget
3098                                 (make-string (* custom-buffer-indent level)
3099                                              ?\ ))
3100            ;; Members.
3101            (message "Creating group...")
3102            (let* ((members (custom-sort-items members
3103                                               custom-buffer-sort-alphabetically
3104                                               custom-buffer-order-groups))
3105                   (prefixes (widget-get widget :custom-prefixes))
3106                   (custom-prefix-list (custom-prefix-add symbol prefixes))
3107                   (length (length members))
3108                   (count 0)
3109                   (children (mapcar
3110                              (lambda (entry)
3111                                (widget-insert "\n")
3112                                (when (zerop (% count custom-skip-messages))
3113                                  (display-message
3114                                   'progress
3115                                   (format "\
3116 Creating group members... %2d%%"
3117                                           (/ (* 100.0 count) length))))
3118                                (incf count)
3119                                (prog1
3120                                    (widget-create-child-and-convert
3121                                     widget (nth 1 entry)
3122                                     :group widget
3123                                     :tag (custom-unlispify-tag-name
3124                                           (nth 0 entry))
3125                                     :custom-prefixes custom-prefix-list
3126                                     :custom-level (1+ level)
3127                                     :value (nth 0 entry))
3128                                  (unless (eq (preceding-char) ?\n)
3129                                    (widget-insert "\n"))))
3130                              members)))
3131              (message "Creating group magic...")
3132              (mapc 'custom-magic-reset children)
3133              (message "Creating group state...")
3134              (widget-put widget :children children)
3135              (custom-group-state-update widget)
3136              (message "Creating group... done"))
3137            ;; End line
3138            (insert "\n")
3139            (insert-char ?\  (* custom-buffer-indent (1- level)))
3140            (insert "\\- " (widget-get widget :tag) " group end ")
3141            (insert-char ?- (- 75 (current-column) (* custom-buffer-indent level)))
3142            (insert "/\n")))))
3143
3144 (defvar custom-group-menu
3145   '(("Set for Current Session" custom-group-set
3146      (lambda (widget)
3147        (eq (widget-get widget :custom-state) 'modified)))
3148     ("Save for Future Sessions" custom-group-save
3149      (lambda (widget)
3150        (memq (widget-get widget :custom-state) '(modified set))))
3151     ("Reset to Current" custom-group-reset-current
3152      (lambda (widget)
3153        (memq (widget-get widget :custom-state) '(modified))))
3154     ("Reset to Saved" custom-group-reset-saved
3155      (lambda (widget)
3156        (memq (widget-get widget :custom-state) '(modified set))))
3157     ("Reset to standard setting" custom-group-reset-standard
3158      (lambda (widget)
3159        (memq (widget-get widget :custom-state) '(modified set saved)))))
3160   "Alist of actions for the `custom-group' widget.
3161 Each entry has the form (NAME ACTION FILTER) where NAME is the name of
3162 the menu entry, ACTION is the function to call on the widget when the
3163 menu is selected, and FILTER is a predicate which takes a `custom-group'
3164 widget as an argument, and returns non-nil if ACTION is valid on that
3165 widget. If FILTER is nil, ACTION is always valid.")
3166
3167 (defun custom-group-action (widget &optional event)
3168   "Show the menu for `custom-group' WIDGET.
3169 Optional EVENT is the location for the menu."
3170   (if (eq (widget-get widget :custom-state) 'hidden)
3171       (custom-toggle-hide widget)
3172     (let* ((completion-ignore-case t)
3173            (answer (widget-choose (concat "Operation on "
3174                                           (custom-unlispify-tag-name
3175                                            (widget-get widget :value)))
3176                                   (custom-menu-filter custom-group-menu
3177                                                       widget)
3178                                   event)))
3179       (if answer
3180           (funcall answer widget)))))
3181
3182 (defun custom-group-set (widget)
3183   "Set changes in all modified group members."
3184   (let ((children (widget-get widget :children)))
3185     (mapc (lambda (child)
3186             (when (eq (widget-get child :custom-state) 'modified)
3187               (widget-apply child :custom-set)))
3188           children)))
3189
3190 (defun custom-group-save (widget)
3191   "Save all modified group members."
3192   (let ((children (widget-get widget :children)))
3193     (mapc (lambda (child)
3194             (when (memq (widget-get child :custom-state) '(modified set))
3195               (widget-apply child :custom-save)))
3196           children)))
3197
3198 (defun custom-group-reset-current (widget)
3199   "Reset all modified group members."
3200   (let ((children (widget-get widget :children)))
3201     (mapc (lambda (child)
3202             (when (eq (widget-get child :custom-state) 'modified)
3203               (widget-apply child :custom-reset-current)))
3204           children)))
3205
3206 (defun custom-group-reset-saved (widget)
3207   "Reset all modified or set group members."
3208   (let ((children (widget-get widget :children)))
3209     (mapc (lambda (child)
3210             (when (memq (widget-get child :custom-state) '(modified set))
3211               (widget-apply child :custom-reset-saved)))
3212           children)))
3213
3214 (defun custom-group-reset-standard (widget)
3215   "Reset all modified, set, or saved group members."
3216   (let ((children (widget-get widget :children)))
3217     (mapc (lambda (child)
3218             (when (memq (widget-get child :custom-state)
3219                         '(modified set saved))
3220               (widget-apply child :custom-reset-standard)))
3221           children)))
3222
3223 (defun custom-group-state-update (widget)
3224   "Update magic."
3225   (unless (eq (widget-get widget :custom-state) 'hidden)
3226     (let* ((children (widget-get widget :children))
3227            (states (mapcar (lambda (child)
3228                              (widget-get child :custom-state))
3229                            children))
3230            (magics custom-magic-alist)
3231            (found 'standard))
3232       (while magics
3233         (let ((magic (car (car magics))))
3234           (if (and (not (eq magic 'hidden))
3235                    (memq magic states))
3236               (setq found magic
3237                     magics nil)
3238             (setq magics (cdr magics)))))
3239       (widget-put widget :custom-state found)))
3240   (custom-magic-reset widget))
3241
3242 ;;; The `custom-save-all' Function.
3243 ;;;###autoload
3244 (defcustom custom-file "~/.emacs"
3245   "File used for storing customization information.
3246 If you change this from the default \"~/.emacs\" you need to
3247 explicitly load that file for the settings to take effect."
3248   :type 'file
3249   :group 'customize)
3250
3251 (defun custom-save-delete (symbol)
3252   "Delete the call to SYMBOL form in `custom-file'.
3253 Leave point at the location of the call, or after the last expression."
3254   (let ((find-file-hooks nil)
3255         (auto-mode-alist nil))
3256     (set-buffer (find-file-noselect custom-file)))
3257   (goto-char (point-min))
3258   (catch 'found
3259     (while t
3260       (let ((sexp (condition-case nil
3261                       (read (current-buffer))
3262                     (end-of-file (throw 'found nil)))))
3263         (when (and (listp sexp)
3264                    (eq (car sexp) symbol))
3265           (delete-region (save-excursion
3266                            (backward-sexp)
3267                            (point))
3268                          (point))
3269           (throw 'found nil))))))
3270
3271 (defun custom-save-variables ()
3272    "Save all customized variables in `custom-file'."
3273    (save-excursion
3274      (custom-save-delete 'custom-load-themes)
3275      (custom-save-delete 'custom-reset-variables)
3276      (custom-save-delete 'custom-set-variables)
3277      (custom-save-loaded-themes)
3278      (custom-save-resets 'theme-value 'custom-reset-variables nil)
3279      (let ((standard-output (current-buffer)))
3280        (unless (bolp)
3281         (princ "\n"))
3282        (princ "(custom-set-variables")
3283        (mapatoms (lambda (symbol)                
3284                   (let ((spec (car-safe (get symbol 'theme-value)))
3285                         (requests (get symbol 'custom-requests))
3286                         (now (not (or (get symbol 'standard-value)
3287                                       (and (not (boundp symbol))
3288                                            (not (eq (get symbol 'force-value)
3289                                                     'rogue))))))
3290                         (comment (get symbol 'saved-variable-comment)))
3291                     (when (or (and spec (eq (car spec) 'user)
3292                                (eq (second spec) 'set)) comment)
3293                       (princ "\n '(")
3294                       (prin1 symbol)
3295                       (princ " ")
3296                       ;; This comment stuff is in the way ####
3297                       ;; Is (eq (third spec) (car saved-value)) ????
3298                       ;; (prin1 (third spec))
3299                       (prin1 (car (get symbol 'saved-value)))
3300                       (when (or now requests comment)
3301                         (princ (if now " t" " nil")))
3302                       (when (or comment requests)
3303                         (princ " ")
3304                         (prin1 requests))
3305                       (when comment
3306                         (princ " ")
3307                         (prin1 comment))
3308                       (princ ")")))))
3309       (princ ")")
3310       (unless (looking-at "\n")
3311         (princ "\n")))))
3312
3313 (defvar custom-save-face-ignoring nil)
3314
3315 (defun custom-save-face-internal (symbol)
3316   (let ((theme-spec (car-safe (get symbol 'theme-face)))
3317         (comment (get symbol 'saved-face-comment))
3318         (now (not (or (get symbol 'face-defface-spec)
3319               (and (not (find-face symbol))
3320                    (not (eq (get symbol 'force-face) 'rogue)))))))
3321     (when (or (and (not (memq symbol custom-save-face-ignoring))
3322                ;; Don't print default face here.
3323                theme-spec
3324                (eq (car theme-spec) 'user)
3325                (eq (second theme-spec) 'set)) comment)
3326       (princ "\n '(")
3327       (prin1 symbol)
3328       (princ " ")
3329       (prin1 (get symbol 'saved-face))
3330       (if (or comment now)
3331           (princ (if now " t" " nil")))
3332       (when comment
3333           (princ " ")
3334           (prin1 comment))
3335       (princ ")"))))
3336
3337 (defun custom-save-faces ()
3338   "Save all customized faces in `custom-file'."
3339   (save-excursion
3340     (custom-save-delete 'custom-reset-faces)
3341     (custom-save-delete 'custom-set-faces)
3342     (custom-save-resets 'theme-face 'custom-reset-faces '(default))
3343     (let ((standard-output (current-buffer)))
3344       (unless (bolp)
3345         (princ "\n"))
3346       (princ "(custom-set-faces")
3347         ;; The default face must be first, since it affects the others.
3348       (custom-save-face-internal 'default)
3349       (let ((custom-save-face-ignoring '(default)))
3350         (mapatoms #'custom-save-face-internal))
3351       (princ ")")
3352       (unless (looking-at "\n")
3353         (princ "\n")))))
3354
3355 (defun custom-save-resets (property setter special)
3356   (let (started-writing ignored-special)
3357     ;; (custom-save-delete setter) Done by caller 
3358     (let ((standard-output (current-buffer))
3359           (mapper `(lambda (object)
3360                     (let ((spec (car-safe (get object (quote ,property)))))
3361                       (when (and (not (memq object ignored-special))
3362                                  (eq (car spec) 'user)
3363                                  (eq (second spec) 'reset))
3364                         ;; Do not write reset statements unless necessary.
3365                         (unless started-writing
3366                           (setq started-writing t)
3367                           (unless (bolp)
3368                             (princ "\n"))
3369                         (princ "(")
3370                         (princ (quote ,setter))
3371                         (princ "\n '(")
3372                         (prin1 object)
3373                         (princ " ")
3374                         (prin1 (third spec))
3375                         (princ ")")))))))
3376       (mapc mapper special)
3377       (setq ignored-special special)
3378       (mapatoms mapper)
3379       (when started-writing
3380         (princ ")\n")))))
3381                         
3382
3383 (defun custom-save-loaded-themes ()
3384   (let ((themes (reverse (get 'user 'theme-loads-themes)))
3385         (standard-output (current-buffer)))
3386     (when themes
3387       (unless (bolp) (princ "\n"))
3388       (princ "(custom-load-themes")
3389       (mapc (lambda (theme)
3390               (princ "\n   '")
3391               (prin1 theme)) themes)
3392       (princ " )\n"))))  
3393
3394 ;;;###autoload
3395 (defun customize-save-customized ()
3396   "Save all user options which have been set in this session."
3397   (interactive)
3398   (mapatoms (lambda (symbol)
3399               (let ((face (get symbol 'customized-face))
3400                     (value (get symbol 'customized-value))
3401                     (face-comment (get symbol 'customized-face-comment))
3402                     (variable-comment
3403                      (get symbol 'customized-variable-comment)))
3404                 (when face
3405                   (put symbol 'saved-face face)
3406                   (custom-push-theme 'theme-face symbol 'user 'set value)
3407                   (put symbol 'customized-face nil))
3408                 (when value
3409                   (put symbol 'saved-value value)
3410                   (custom-push-theme 'theme-value symbol 'user 'set value)
3411                   (put symbol 'customized-value nil))
3412                 (when variable-comment
3413                   (put symbol 'saved-variable-comment variable-comment)
3414                   (put symbol 'customized-variable-comment nil))
3415                 (when face-comment
3416                   (put symbol 'saved-face-comment face-comment)
3417                   (put symbol 'customized-face-comment nil)))))
3418   ;; We really should update all custom buffers here.
3419   (custom-save-all))
3420
3421 ;;;###autoload
3422 (defun custom-save-all ()
3423   "Save all customizations in `custom-file'."
3424   (let ((inhibit-read-only t))
3425     (custom-save-variables)
3426     (custom-save-faces)
3427     (let ((find-file-hooks nil)
3428           (auto-mode-alist))
3429       (with-current-buffer (find-file-noselect custom-file)
3430         (save-buffer)))))
3431
3432 \f
3433 ;;; The Customize Menu.
3434
3435 ;;; Menu support
3436
3437 (defun custom-face-menu-create (widget symbol)
3438   "Ignoring WIDGET, create a menu entry for customization face SYMBOL."
3439   (vector (custom-unlispify-menu-entry symbol)
3440           `(customize-face ',symbol)
3441           t))
3442
3443 (defun custom-variable-menu-create (widget symbol)
3444   "Ignoring WIDGET, create a menu entry for customization variable SYMBOL."
3445   (let ((type (get symbol 'custom-type)))
3446     (unless (listp type)
3447       (setq type (list type)))
3448     (if (and type (widget-get type :custom-menu))
3449         (widget-apply type :custom-menu symbol)
3450       (vector (custom-unlispify-menu-entry symbol)
3451               `(customize-variable ',symbol)
3452               t))))
3453
3454 ;; Add checkboxes to boolean variable entries.
3455 (widget-put (get 'boolean 'widget-type)
3456             :custom-menu (lambda (widget symbol)
3457                            `[,(custom-unlispify-menu-entry symbol)
3458                              (customize-variable ',symbol)
3459                              :style toggle
3460                              :selected ,symbol]))
3461
3462 ;; XEmacs can create menus dynamically.
3463 (defun custom-group-menu-create (widget symbol)
3464   "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
3465   `( ,(custom-unlispify-menu-entry symbol t)
3466      :filter (lambda (&rest junk)
3467                (let ((item (custom-menu-create ',symbol)))
3468                  (if (listp item)
3469                      (cdr item)
3470                    (list item))))))
3471
3472 ;;;###autoload
3473 (defun custom-menu-create (symbol)
3474   "Create menu for customization group SYMBOL.
3475 The menu is in a format applicable to `easy-menu-define'."
3476   (let* ((item (vector (custom-unlispify-menu-entry symbol)
3477                        `(customize-group ',symbol)
3478                        t)))
3479     ;; Item is the entry for creating a menu buffer for SYMBOL.
3480     ;; We may nest, if the menu is not too big.
3481     (custom-load-symbol symbol)
3482     (if (< (length (get symbol 'custom-group)) widget-menu-max-size)
3483         ;; The menu is not too big.
3484         (let ((custom-prefix-list (custom-prefix-add symbol
3485                                                      custom-prefix-list))
3486               (members (custom-sort-items (get symbol 'custom-group)
3487                                           custom-menu-sort-alphabetically
3488                                           custom-menu-order-groups)))
3489           ;; Create the menu.
3490           `(,(custom-unlispify-menu-entry symbol t)
3491             ,item
3492             "--"
3493             ,@(mapcar (lambda (entry)
3494                         (widget-apply (if (listp (nth 1 entry))
3495                                           (nth 1 entry)
3496                                         (list (nth 1 entry)))
3497                                       :custom-menu (nth 0 entry)))
3498                       members)))
3499       ;; The menu was too big.
3500       item)))
3501
3502 ;;;###autoload
3503 (defun customize-menu-create (symbol &optional name)
3504   "Return a customize menu for customization group SYMBOL.
3505 If optional NAME is given, use that as the name of the menu.
3506 Otherwise the menu will be named `Customize'.
3507 The format is suitable for use with `easy-menu-define'."
3508   (unless name
3509     (setq name "Customize"))
3510   `(,name
3511     :filter (lambda (&rest junk)
3512               (cdr (custom-menu-create ',symbol)))))
3513
3514 ;;; The Custom Mode.
3515
3516 (defvar custom-mode-map nil
3517   "Keymap for `custom-mode'.")
3518
3519 (unless custom-mode-map
3520   (setq custom-mode-map (make-sparse-keymap))
3521   (set-keymap-parents custom-mode-map widget-keymap)
3522   (suppress-keymap custom-mode-map)
3523   (define-key custom-mode-map " " 'scroll-up)
3524   (define-key custom-mode-map [delete] 'scroll-down)
3525   (define-key custom-mode-map "q" 'Custom-buffer-done)
3526   (define-key custom-mode-map "u" 'Custom-goto-parent)
3527   (define-key custom-mode-map "n" 'widget-forward)
3528   (define-key custom-mode-map "p" 'widget-backward))
3529
3530 (easy-menu-define Custom-mode-menu
3531     custom-mode-map
3532   "Menu used in customization buffers."
3533   `("Custom"
3534     ,(customize-menu-create 'customize)
3535     ["Set" Custom-set t]
3536     ["Save" Custom-save t]
3537     ["Reset to Current" Custom-reset-current t]
3538     ["Reset to Saved" Custom-reset-saved t]
3539     ["Reset to Standard Settings" Custom-reset-standard t]
3540     ["Info" (Info-goto-node "(xemacs)Easy Customization") t]))
3541
3542 (defun Custom-goto-parent ()
3543   "Go to the parent group listed at the top of this buffer.
3544 If several parents are listed, go to the first of them."
3545   (interactive)
3546   (save-excursion
3547     (goto-char (point-min))
3548     (if (search-forward "\nGo to parent group: " nil t)
3549         (let* ((button (get-char-property (point) 'button))
3550                (parent (downcase (widget-get  button :tag))))
3551           (customize-group parent)))))
3552
3553 (defcustom custom-mode-hook nil
3554   "Hook called when entering custom-mode."
3555   :type 'hook
3556   :group 'custom-buffer )
3557
3558 (defun custom-state-buffer-message (widget)
3559   (if (eq (widget-get (widget-get widget :parent) :custom-state) 'modified)
3560       (message
3561        "To install your edits, invoke [State] and choose the Set operation")))
3562
3563 (defun custom-mode ()
3564   "Major mode for editing customization buffers.
3565
3566 The following commands are available:
3567
3568 Move to next button or editable field.     \\[widget-forward]
3569 Move to previous button or editable field. \\[widget-backward]
3570 \\<widget-field-keymap>\
3571 Complete content of editable text field.   \\[widget-complete]
3572 \\<custom-mode-map>\
3573 Invoke button under point.                 \\[widget-button-press]
3574 Set all modifications.                     \\[Custom-set]
3575 Make all modifications default.            \\[Custom-save]
3576 Reset all modified options.                \\[Custom-reset-current]
3577 Reset all modified or set options.         \\[Custom-reset-saved]
3578 Reset all options.                         \\[Custom-reset-standard]
3579
3580 Entry to this mode calls the value of `custom-mode-hook'
3581 if that value is non-nil."
3582   (kill-all-local-variables)
3583   (setq major-mode 'custom-mode
3584         mode-name "Custom")
3585   (use-local-map custom-mode-map)
3586   (easy-menu-add Custom-mode-menu)
3587   (make-local-variable 'custom-options)
3588   (make-local-variable 'widget-documentation-face)
3589   (setq widget-documentation-face 'custom-documentation-face)
3590   (make-local-variable 'widget-button-face)
3591   (setq widget-button-face 'custom-button-face)
3592   (make-local-hook 'widget-edit-functions)
3593   (add-hook 'widget-edit-functions 'custom-state-buffer-message nil t)
3594   (run-hooks 'custom-mode-hook))
3595
3596 \f
3597 ;;; The End.
3598
3599 (provide 'cus-edit)
3600
3601 ;; cus-edit.el ends here