XEmacs 21.2.27 "Hera".
[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   ;; Addd SYMBOL to list of ignored PREFIXES.
403   (cons (or (get symbol 'custom-prefix)
404             (concat (symbol-name symbol) "-"))
405         prefixes))
406
407 \f
408 ;;; Guess.
409
410 (defcustom custom-guess-name-alist
411   '(("-p\\'" boolean)
412     ("-hooks?\\'" hook)
413     ("-face\\'" face)
414     ("-file\\'" file)
415     ("-function\\'" function)
416     ("-functions\\'" (repeat function))
417     ("-list\\'" (repeat sexp))
418     ("-alist\\'" (repeat (cons sexp sexp))))
419   "Alist of (MATCH TYPE).
420
421 MATCH should be a regexp matching the name of a symbol, and TYPE should
422 be a widget suitable for editing the value of that symbol.  The TYPE
423 of the first entry where MATCH matches the name of the symbol will be
424 used.
425
426 This is used for guessing the type of variables not declared with
427 customize."
428   :type '(repeat (group (regexp :tag "Match") (sexp :tag "Type")))
429   :group 'customize)
430
431 (defcustom custom-guess-doc-alist
432   '(("\\`\\*?Non-nil " boolean))
433   "Alist of (MATCH TYPE).
434
435 MATCH should be a regexp matching a documentation string, and TYPE
436 should be a widget suitable for editing the value of a variable with
437 that documentation string.  The TYPE of the first entry where MATCH
438 matches the name of the symbol will be used.
439
440 This is used for guessing the type of variables not declared with
441 customize."
442   :type '(repeat (group (regexp :tag "Match") (sexp :tag "Type")))
443   :group 'customize)
444
445 (defun custom-guess-type (symbol)
446   "Guess a widget suitable for editing the value of SYMBOL.
447 This is done by matching SYMBOL with `custom-guess-name-alist' and
448 if that fails, the doc string with `custom-guess-doc-alist'."
449   (let ((name (symbol-name symbol))
450         (names custom-guess-name-alist)
451         current found)
452     (while names
453       (setq current (car names)
454             names (cdr names))
455       (when (string-match (nth 0 current) name)
456         (setq found (nth 1 current)
457               names nil)))
458     (unless found
459       (let ((doc (documentation-property symbol 'variable-documentation))
460             (docs custom-guess-doc-alist))
461         (when doc
462           (while docs
463             (setq current (car docs)
464                   docs (cdr docs))
465             (when (string-match (nth 0 current) doc)
466               (setq found (nth 1 current)
467                     docs nil))))))
468     found))
469
470 \f
471 ;;; Sorting.
472
473 (defcustom custom-browse-sort-alphabetically nil
474   "If non-nil, sort members of each customization group alphabetically."
475   :type 'boolean
476   :group 'custom-browse)
477
478 (defcustom custom-browse-order-groups nil
479   "If non-nil, order group members within each customization group.
480 If `first', order groups before non-groups.
481 If `last', order groups after non-groups."
482   :type '(choice (const first)
483                  (const last)
484                  (const :tag "none" nil))
485   :group 'custom-browse)
486
487 (defcustom custom-browse-only-groups nil
488   "If non-nil, show group members only within each customization group."
489   :type 'boolean
490   :group 'custom-browse)
491
492 (defcustom custom-buffer-sort-alphabetically nil
493   "If non-nil, sort members of each customization group alphabetically."
494   :type 'boolean
495   :group 'custom-buffer)
496
497 (defcustom custom-buffer-order-groups 'last
498   "If non-nil, order group members within each customization group.
499 If `first', order groups before non-groups.
500 If `last', order groups after non-groups."
501   :type '(choice (const first)
502                  (const last)
503                  (const :tag "none" nil))
504   :group 'custom-buffer)
505
506 (defcustom custom-menu-sort-alphabetically nil
507   "If non-nil, sort members of each customization group alphabetically."
508   :type 'boolean
509   :group 'custom-menu)
510
511 (defcustom custom-menu-order-groups 'first
512   "If non-nil, order group members within each customization group.
513 If `first', order groups before non-groups.
514 If `last', order groups after non-groups."
515   :type '(choice (const first)
516                  (const last)
517                  (const :tag "none" nil))
518   :group 'custom-menu)
519
520 (defun custom-sort-items (items sort-alphabetically order-groups)
521   "Return a sorted copy of ITEMS.
522 ITEMS should be a `custom-group' property.
523 If SORT-ALPHABETICALLY non-nil, sort alphabetically.
524 If ORDER-GROUPS is `first' order groups before non-groups, if `last' order
525 groups after non-groups, if nil do not order groups at all."
526   (sort (copy-sequence items)
527    (lambda (a b)
528      (let ((typea (nth 1 a)) (typeb (nth 1 b))
529            (namea (symbol-name (nth 0 a))) (nameb (symbol-name (nth 0 b))))
530        (cond ((not order-groups)
531               ;; Since we don't care about A and B order, maybe sort.
532               (when sort-alphabetically
533                 (string-lessp namea nameb)))
534              ((eq typea 'custom-group)
535               ;; If B is also a group, maybe sort.  Otherwise, order A and B.
536               (if (eq typeb 'custom-group)
537                   (when sort-alphabetically
538                     (string-lessp namea nameb))
539                 (eq order-groups 'first)))
540              ((eq typeb 'custom-group)
541               ;; Since A cannot be a group, order A and B.
542               (eq order-groups 'last))
543              (sort-alphabetically
544               ;; Since A and B cannot be groups, sort.
545               (string-lessp namea nameb)))))))
546
547 \f
548 ;;; Custom Mode Commands.
549
550 (defvar custom-options nil
551   "Customization widgets in the current buffer.")
552
553 (defun Custom-set ()
554   "Set changes in all modified options."
555   (interactive)
556   (let ((children custom-options))
557     (mapc (lambda (child)
558             (when (eq (widget-get child :custom-state) 'modified)
559               (widget-apply child :custom-set)))
560           children)))
561
562 (defun Custom-save ()
563   "Set all modified group members and save them."
564   (interactive)
565   (let ((children custom-options))
566     (mapc (lambda (child)
567             (when (memq (widget-get child :custom-state) '(modified set))
568               (widget-apply child :custom-save)))
569           children))
570   (custom-save-all))
571
572 (defvar custom-reset-menu
573   '(("Current" . Custom-reset-current)
574     ("Saved" . Custom-reset-saved)
575     ("Standard Settings" . Custom-reset-standard))
576   "Alist of actions for the `Reset' button.
577 The key is a string containing the name of the action, the value is a
578 lisp function taking the widget as an element which will be called
579 when the action is chosen.")
580
581 (defun custom-reset (event)
582   "Select item from reset menu."
583   (let* ((completion-ignore-case t)
584          (answer (widget-choose "Reset to"
585                                 custom-reset-menu
586                                 event)))
587     (if answer
588         (funcall answer))))
589
590 (defun Custom-reset-current (&rest ignore)
591   "Reset all modified group members to their current value."
592   (interactive)
593   (let ((children custom-options))
594     (mapc (lambda (child)
595             (when (eq (widget-get child :custom-state) 'modified)
596               (widget-apply child :custom-reset-current)))
597           children)))
598
599 (defun Custom-reset-saved (&rest ignore)
600   "Reset all modified or set group members to their saved value."
601   (interactive)
602   (let ((children custom-options))
603     (mapc (lambda (child)
604             (when (eq (widget-get child :custom-state) 'modified)
605               (widget-apply child :custom-reset-saved)))
606           children)))
607
608 (defun Custom-reset-standard (&rest ignore)
609   "Reset all modified, set, or saved group members to their standard settings."
610   (interactive)
611   (let ((children custom-options))
612     (mapc (lambda (child)
613             (when (eq (widget-get child :custom-state) 'modified)
614               (widget-apply child :custom-reset-standard)))
615           children)))
616
617 \f
618 ;;; The Customize Commands
619
620 (defun custom-prompt-variable (prompt-var prompt-val &optional comment)
621   "Prompt for a variable and a value and return them as a list.
622 PROMPT-VAR is the prompt for the variable, and PROMPT-VAL is the
623 prompt for the value.  The %s escape in PROMPT-VAL is replaced with
624 the name of the variable.
625
626 If the variable has a `variable-interactive' property, that is used as if
627 it were the arg to `interactive' (which see) to interactively read the value.
628
629 If the variable has a `custom-type' property, it must be a widget and the
630 `:prompt-value' property of that widget will be used for reading the value.
631
632 If optional COMMENT argument is non nil, also prompt for a comment and return
633 it as the third element in the list."
634   (let* ((var (read-variable prompt-var))
635          (minibuffer-help-form '(describe-variable var))
636          (val
637           (let ((prop (get var 'variable-interactive))
638                 (type (get var 'custom-type))
639                 (prompt (format prompt-val var)))
640             (unless (listp type)
641               (setq type (list type)))
642             (cond (prop
643                    ;; Use VAR's `variable-interactive' property
644                    ;; as an interactive spec for prompting.
645                    (call-interactively (list 'lambda '(arg)
646                                              (list 'interactive prop)
647                                              'arg)))
648                   (type
649                    (widget-prompt-value type
650                                         prompt
651                                         (if (boundp var)
652                                             (symbol-value var))
653                                         (not (boundp var))))
654                   (t
655                    (eval-minibuffer prompt))))))
656     (if comment
657         (list var val
658               (read-string "Comment: " (get var 'variable-comment)))
659       (list var val))
660     ))
661
662 ;;;###autoload
663 (defun customize-set-value (var val &optional comment)
664   "Set VARIABLE to VALUE.  VALUE is a Lisp object.
665
666 If VARIABLE has a `variable-interactive' property, that is used as if
667 it were the arg to `interactive' (which see) to interactively read the value.
668
669 If VARIABLE has a `custom-type' property, it must be a widget and the
670 `:prompt-value' property of that widget will be used for reading the value.
671
672 If given a prefix (or a COMMENT argument), also prompt for a comment."
673   (interactive (custom-prompt-variable "Set variable: "
674                                        "Set %s to value: "
675                                        current-prefix-arg))
676
677   (set var val)
678   (cond ((string= comment "")
679          (put var 'variable-comment nil))
680         (comment
681          (put var 'variable-comment comment))))
682
683 ;;;###autoload
684 (defun customize-set-variable (var val &optional comment)
685   "Set the default for VARIABLE to VALUE.  VALUE is a Lisp object.
686
687 If VARIABLE has a `custom-set' property, that is used for setting
688 VARIABLE, otherwise `set-default' is used.
689
690 The `customized-value' property of the VARIABLE will be set to a list
691 with a quoted VALUE as its sole list member.
692
693 If VARIABLE has a `variable-interactive' property, that is used as if
694 it were the arg to `interactive' (which see) to interactively read the value.
695
696 If VARIABLE has a `custom-type' property, it must be a widget and the
697 `:prompt-value' property of that widget will be used for reading the value.
698
699 If given a prefix (or a COMMENT argument), also prompt for a comment."
700   (interactive (custom-prompt-variable "Set variable: "
701                                        "Set customized value for %s to: "
702                                        current-prefix-arg))
703   (funcall (or (get var 'custom-set) 'set-default) var val)
704   (put var 'customized-value (list (custom-quote val)))
705   (cond ((string= comment "")
706          (put var 'variable-comment nil)
707          (put var 'customized-variable-comment nil))
708         (comment
709          (put var 'variable-comment comment)
710          (put var 'customized-variable-comment comment))))
711
712
713 ;;;###autoload
714 (defun customize-save-variable (var val &optional comment)
715   "Set the default for VARIABLE to VALUE, and save it for future sessions.
716 If VARIABLE has a `custom-set' property, that is used for setting
717 VARIABLE, otherwise `set-default' is used.
718
719 The `customized-value' property of the VARIABLE will be set to a list
720 with a quoted VALUE as its sole list member.
721
722 If VARIABLE has a `variable-interactive' property, that is used as if
723 it were the arg to `interactive' (which see) to interactively read the value.
724
725 If VARIABLE has a `custom-type' property, it must be a widget and the
726 `:prompt-value' property of that widget will be used for reading the value.
727
728 If given a prefix (or a COMMENT argument), also prompt for a comment."
729   (interactive (custom-prompt-variable "Set and ave variable: "
730                                        "Set and save value for %s as: "
731                                        current-prefix-arg))
732   (funcall (or (get var 'custom-set) 'set-default) var val)
733   (put var 'saved-value (list (custom-quote val)))
734   (custom-push-theme 'theme-value var 'user 'set (list (custom-quote val)))
735   (cond ((string= comment "")
736          (put var 'variable-comment nil)
737          (put var 'saved-variable-comment nil))
738         (comment
739          (put var 'variable-comment comment)
740          (put var 'saved-variable-comment comment)))
741   (custom-save-all))
742
743 ;;;###autoload
744 (defun customize (group)
745   "Select a customization buffer which you can use to set user options.
746 User options are structured into \"groups\".
747 The default group is `Emacs'."
748   (interactive (custom-group-prompt
749                 "Customize group: (default emacs) "))
750   (when (stringp group)
751     (if (string-equal "" group)
752         (setq group 'emacs)
753       (setq group (intern group))))
754   (let ((name (format "*Customize Group: %s*"
755                       (custom-unlispify-tag-name group))))
756     (if (get-buffer name)
757         (switch-to-buffer name)
758       (custom-buffer-create (list (list group 'custom-group))
759                             name
760                             (concat " for group "
761                                     (custom-unlispify-tag-name group))))))
762
763 ;;;###autoload
764 (defalias 'customize-group 'customize)
765
766 ;;;###autoload
767 (defun customize-other-window (symbol)
768   "Customize SYMBOL, which must be a customization group."
769   (interactive (custom-group-prompt
770                 "Customize group: (default emacs) "))
771   (when (stringp symbol)
772     (if (string-equal "" symbol)
773         (setq symbol 'emacs)
774       (setq symbol (intern symbol))))
775   (custom-buffer-create-other-window
776    (list (list symbol 'custom-group))
777    (format "*Customize Group: %s*" (custom-unlispify-tag-name symbol))))
778
779 ;;;###autoload
780 (defalias 'customize-group-other-window 'customize-other-window)
781
782 ;;;###autoload
783 (defalias 'customize-option 'customize-variable)
784
785 ;;;###autoload
786 (defun customize-variable (symbol)
787   "Customize SYMBOL, which must be a user option variable."
788   (interactive (custom-variable-prompt))
789   (custom-buffer-create (list (list symbol 'custom-variable))
790                         (format "*Customize Variable: %s*"
791                                 (custom-unlispify-tag-name symbol))))
792
793 ;;;###autoload
794 (defun customize-changed-options (since-version)
795   "Customize all user option variables whose default values changed recently.
796 This means, in other words, variables defined with a `:version' keyword."
797   (interactive "sCustomize options changed, since version (default all versions): ")
798   (if (equal since-version "")
799       (setq since-version nil))
800   (let ((found nil))
801     (mapatoms (lambda (symbol)
802                 (and (boundp symbol)
803                      (let ((version (get symbol 'custom-version)))
804                        (and version
805                             (or (null since-version)
806                                 (customize-version-lessp since-version version))))
807                      (push (list symbol 'custom-variable) found))))
808     (unless found
809       (error "No user options have changed defaults %s"
810              (if since-version
811                  (format "since XEmacs %s" since-version)
812                "in recent Emacs versions")))
813     (custom-buffer-create (custom-sort-items found t nil)
814                           "*Customize Changed Options*")))
815
816 (defun customize-version-lessp (version1 version2)
817   (let (major1 major2 minor1 minor2)
818     (string-match "\\([0-9]+\\)[.]\\([0-9]+\\)" version1)
819     (setq major1 (read (match-string 1 version1)))
820     (setq minor1 (read (match-string 2 version1)))
821     (string-match "\\([0-9]+\\)[.]\\([0-9]+\\)" version2)
822     (setq major2 (read (match-string 1 version2)))
823     (setq minor2 (read (match-string 2 version2)))
824     (or (< major1 major2)
825         (and (= major1 major2)
826              (< minor1 minor2)))))
827
828 ;;;###autoload
829 (defalias 'customize-variable-other-window 'customize-option-other-window)
830
831 ;;;###autoload
832 (defun customize-option-other-window (symbol)
833   "Customize SYMBOL, which must be a user option variable.
834 Show the buffer in another window, but don't select it."
835   (interactive (custom-variable-prompt))
836   (custom-buffer-create-other-window
837    (list (list symbol 'custom-variable))
838    (format "*Customize Option: %s*" (custom-unlispify-tag-name symbol))))
839
840 ;;;###autoload
841 (defun customize-face (&optional symbol)
842   "Customize SYMBOL, which should be a face name or nil.
843 If SYMBOL is nil, customize all faces."
844   (interactive (list (completing-read "Customize face: (default all) "
845                                       obarray 'find-face)))
846   (if (or (null symbol) (and (stringp symbol) (zerop (length symbol))))
847       (custom-buffer-create (custom-sort-items
848                              (mapcar (lambda (symbol)
849                                        (list symbol 'custom-face))
850                                      (face-list))
851                              t nil)
852                             "*Customize Faces*")
853     (when (stringp symbol)
854       (setq symbol (intern symbol)))
855     (check-argument-type 'symbolp symbol)
856     (custom-buffer-create (list (list symbol 'custom-face))
857                           (format "*Customize Face: %s*"
858                                   (custom-unlispify-tag-name symbol)))))
859
860 ;;;###autoload
861 (defun customize-face-other-window (&optional symbol)
862   "Show customization buffer for FACE in other window."
863   (interactive (list (completing-read "Customize face: "
864                                       obarray 'find-face)))
865   (if (or (null symbol) (and (stringp symbol) (zerop (length symbol))))
866       ()
867     (if (stringp symbol)
868         (setq symbol (intern symbol)))
869     (check-argument-type 'symbolp symbol)
870     (custom-buffer-create-other-window
871      (list (list symbol 'custom-face))
872      (format "*Customize Face: %s*" (custom-unlispify-tag-name symbol)))))
873
874 ;;;###autoload
875 (defun customize-customized ()
876   "Customize all user options set since the last save in this session."
877   (interactive)
878   (let ((found nil))
879     (mapatoms (lambda (symbol)
880                 (and (or (get symbol 'customized-face)
881                          (get symbol 'customized-face-comment))
882                      (find-face symbol)
883                      (push (list symbol 'custom-face) found))
884                 (and (or (get symbol 'customized-value)
885                          (get symbol 'customized-variable-comment))
886                      (boundp symbol)
887                      (push (list symbol 'custom-variable) found))))
888     (if (not found)
889         (error "No customized user options")
890       (custom-buffer-create (custom-sort-items found t nil)
891                             "*Customize Customized*"))))
892
893 ;;;###autoload
894 (defun customize-saved ()
895   "Customize all already saved user options."
896   (interactive)
897   (let ((found nil))
898     (mapatoms (lambda (symbol)
899                 (and (or (get symbol 'saved-face)
900                          (get symbol 'saved-face-comment))
901                      (find-face symbol)
902                      (push (list symbol 'custom-face) found))
903                 (and (or (get symbol 'saved-value)
904                          (get symbol 'saved-variable-comment))
905                      (boundp symbol)
906                      (push (list symbol 'custom-variable) found))))
907     (if (not found )
908         (error "No saved user options")
909       (custom-buffer-create (custom-sort-items found t nil)
910                             "*Customize Saved*"))))
911
912 ;;;###autoload
913 (defun customize-apropos (regexp &optional all)
914   "Customize all user options matching REGEXP.
915 If ALL is `options', include only options.
916 If ALL is `faces', include only faces.
917 If ALL is `groups', include only groups.
918 If ALL is t (interactively, with prefix arg), include options which are not
919 user-settable, as well as faces and groups."
920   (interactive "sCustomize regexp: \nP")
921   (let ((found nil))
922     (mapatoms (lambda (symbol)
923                 (when (string-match regexp (symbol-name symbol))
924                   (when (and (not (memq all '(faces options)))
925                              (get symbol 'custom-group))
926                     (push (list symbol 'custom-group) found))
927                   (when (and (not (memq all '(options groups)))
928                              (find-face symbol))
929                     (push (list symbol 'custom-face) found))
930                   (when (and (not (memq all '(groups faces)))
931                              (boundp symbol)
932                              (or (get symbol 'saved-value)
933                                  (get symbol 'standard-value)
934                                  (if (memq all '(nil options))
935                                      (user-variable-p symbol)
936                                    (get symbol 'variable-documentation))))
937                     (push (list symbol 'custom-variable) found)))))
938     (if (not found)
939         (error "No matches")
940       (custom-buffer-create (custom-sort-items found t
941                                                custom-buffer-order-groups)
942                             "*Customize Apropos*"))))
943
944 ;;;###autoload
945 (defun customize-apropos-options (regexp &optional arg)
946   "Customize all user options matching REGEXP.
947 With prefix arg, include options which are not user-settable."
948   (interactive "sCustomize regexp: \nP")
949   (customize-apropos regexp (or arg 'options)))
950
951 ;;;###autoload
952 (defun customize-apropos-faces (regexp)
953   "Customize all user faces matching REGEXP."
954   (interactive "sCustomize regexp: \n")
955   (customize-apropos regexp 'faces))
956
957 ;;;###autoload
958 (defun customize-apropos-groups (regexp)
959   "Customize all user groups matching REGEXP."
960   (interactive "sCustomize regexp: \n")
961   (customize-apropos regexp 'groups))
962
963 \f
964 ;;; Buffer.
965
966 (defcustom custom-buffer-style 'links
967   "*Control the presentation style for customization buffers.
968 The value should be a symbol, one of:
969
970 brackets: groups nest within each other with big horizontal brackets.
971 links: groups have links to subgroups."
972   :type '(radio (const :tag "brackets: Groups nest within each others" brackets)
973                 (const :tag "links: Group have links to subgroups" links))
974   :group 'custom-buffer)
975
976 (defcustom custom-buffer-done-function 'kill-buffer
977   "*Function to be used to remove the buffer when the user is done with it.
978 Choices include `kill-buffer' (the default) and `bury-buffer'.
979 The function will be called with one argument, the buffer to remove."
980   :type '(radio (function-item kill-buffer)
981                 (function-item bury-buffer)
982                 (function :tag "Other" nil))
983   :group 'custom-buffer)
984
985 (defcustom custom-buffer-indent 3
986   "Number of spaces to indent nested groups."
987   :type 'integer
988   :group 'custom-buffer)
989
990 ;;;###autoload
991 (defun custom-buffer-create (options &optional name description)
992   "Create a buffer containing OPTIONS.
993 Optional NAME is the name of the buffer.
994 OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
995 SYMBOL is a customization option, and WIDGET is a widget for editing
996 that option."
997   (unless name (setq name "*Customization*"))
998   (kill-buffer (get-buffer-create name))
999   (switch-to-buffer (get-buffer-create name))
1000   (custom-buffer-create-internal options description))
1001
1002 ;;;###autoload
1003 (defun custom-buffer-create-other-window (options &optional name description)
1004   "Create a buffer containing OPTIONS.
1005 Optional NAME is the name of the buffer.
1006 OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
1007 SYMBOL is a customization option, and WIDGET is a widget for editing
1008 that option."
1009   (unless name (setq name "*Customization*"))
1010   (kill-buffer (get-buffer-create name))
1011   (let ((window (selected-window)))
1012     (switch-to-buffer-other-window (get-buffer-create name))
1013     (custom-buffer-create-internal options description)
1014     (select-window window)))
1015
1016 (defcustom custom-reset-button-menu t
1017   "If non-nil, only show a single reset button in customize buffers.
1018 This button will have a menu with all three reset operations."
1019   :type 'boolean
1020   :group 'custom-buffer)
1021
1022 (defconst custom-skip-messages 5)
1023
1024 (defun Custom-buffer-done ()
1025   "Remove current buffer.
1026 This works by calling the function specified by
1027  `custom-buffer-done-function'."
1028   (interactive)
1029   (funcall custom-buffer-done-function (current-buffer)))
1030
1031 (defun custom-buffer-create-buttons ()
1032   (message "Creating customization buttons...")
1033   (widget-insert "\nOperate on everything in this buffer:\n ")
1034   (widget-create 'push-button
1035                  :tag "Set"
1036                  :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 Windows NT/95/97")
2312                                            mswindows)
2313                                     (const :format "DOS "
2314                                            :sibling-args (:help-echo "\
2315 Plain MS-DOS")
2316                                            pc)
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 the frames with the specified color support")
2323                          (const :format "Class: "
2324                                 class)
2325                          (checklist :inline t
2326                                     :offset 0
2327                                     (const :format "Color "
2328                                            :sibling-args (:help-echo "\
2329 Match color frames")
2330                                            color)
2331                                     (const :format "Grayscale "
2332                                            :sibling-args (:help-echo "\
2333 Match grayscale frames")
2334                                            grayscale)
2335                                     (const :format "Monochrome%n"
2336                                            :sibling-args (:help-echo "\
2337 Match frames with no color support")
2338                                            mono)))
2339                   (group :sibling-args (:help-echo "\
2340 Only match frames with the specified intensity")
2341                          (const :format "\
2342 Background brightness: "
2343                                 background)
2344                          (checklist :inline t
2345                                     :offset 0
2346                                     (const :format "Light "
2347                                            :sibling-args (:help-echo "\
2348 Match frames with light backgrounds")
2349                                            light)
2350                                     (const :format "Dark\n"
2351                                            :sibling-args (:help-echo "\
2352 Match frames with dark backgrounds")
2353                                            dark)))))))
2354
2355 ;;; The `custom-face' Widget.
2356
2357 (defface custom-face-tag-face '((t (:underline t)))
2358   "Face used for face tags."
2359   :group 'custom-faces)
2360
2361 (defcustom custom-face-default-form 'selected
2362   "Default form of displaying face definition."
2363   :type '(choice (const all)
2364                  (const selected)
2365                  (const lisp))
2366   :group 'custom-buffer)
2367
2368 (define-widget 'custom-face 'custom
2369   "Customize face."
2370   :sample-face 'custom-face-tag-face
2371   :help-echo "Set or reset this face"
2372   :documentation-property '(lambda (face)
2373                              (face-doc-string face))
2374   :value-create 'custom-face-value-create
2375   :action 'custom-face-action
2376   :custom-category 'face
2377   :custom-form nil ; defaults to value of `custom-face-default-form'
2378   :custom-set 'custom-face-set
2379   :custom-save 'custom-face-save
2380   :custom-reset-current 'custom-redraw
2381   :custom-reset-saved 'custom-face-reset-saved
2382   :custom-reset-standard 'custom-face-reset-standard
2383   :custom-menu 'custom-face-menu-create)
2384
2385 (define-widget 'custom-face-all 'editable-list
2386   "An editable list of display specifications and attributes."
2387   :entry-format "%i %d %v"
2388   :insert-button-args '(:help-echo "Insert new display specification here")
2389   :append-button-args '(:help-echo "Append new display specification here")
2390   :delete-button-args '(:help-echo "Delete this display specification")
2391   :args '((group :format "%v" custom-display custom-face-edit)))
2392
2393 (defconst custom-face-all (widget-convert 'custom-face-all)
2394   "Converted version of the `custom-face-all' widget.")
2395
2396 (define-widget 'custom-display-unselected 'item
2397   "A display specification that doesn't match the selected display."
2398   :match 'custom-display-unselected-match)
2399
2400 (defun custom-display-unselected-match (widget value)
2401   "Non-nil if VALUE is an unselected display specification."
2402   (not (face-spec-set-match-display value (selected-frame))))
2403
2404 (define-widget 'custom-face-selected 'group
2405   "Edit the attributes of the selected display in a face specification."
2406   :args '((repeat :format ""
2407                   :inline t
2408                   (group custom-display-unselected sexp))
2409           (group (sexp :format "") custom-face-edit)
2410           (repeat :format ""
2411                   :inline t
2412                   sexp)))
2413
2414 (defconst custom-face-selected (widget-convert 'custom-face-selected)
2415   "Converted version of the `custom-face-selected' widget.")
2416
2417 (defun custom-face-value-create (widget)
2418   "Create a list of the display specifications for WIDGET."
2419   (let ((buttons (widget-get widget :buttons))
2420         children
2421         (symbol (widget-get widget :value))
2422         (tag (widget-get widget :tag))
2423         (state (widget-get widget :custom-state))
2424         (begin (point))
2425         (is-last (widget-get widget :custom-last))
2426         (prefix (widget-get widget :custom-prefix)))
2427     (unless tag
2428       (setq tag (prin1-to-string symbol)))
2429     (cond ((eq custom-buffer-style 'tree)
2430            (insert prefix (if is-last " `--- " " |--- "))
2431            (push (widget-create-child-and-convert
2432                   widget 'custom-browse-face-tag)
2433                  buttons)
2434            (insert " " tag "\n")
2435            (widget-put widget :buttons buttons))
2436           (t
2437            ;; Create tag.
2438            (insert tag)
2439            (if (eq custom-buffer-style 'face)
2440                (insert " ")
2441              (widget-specify-sample widget begin (point))
2442              (insert ": "))
2443            ;; Sample.
2444            (and (not (find-face symbol))
2445                 ;; XEmacs cannot display uninitialized faces.
2446                 (make-face symbol))
2447            (push (widget-create-child-and-convert widget 'item
2448                                                   :format "(%{%t%})"
2449                                                   :sample-face symbol
2450                                                   :tag "sample")
2451                  buttons)
2452            ;; Visibility.
2453            (insert " ")
2454            (push (widget-create-child-and-convert
2455                   widget 'visibility
2456                   :help-echo "Hide or show this face"
2457                   :action 'custom-toggle-parent
2458                   (not (eq state 'hidden)))
2459                  buttons)
2460            ;; Magic.
2461            (insert "\n")
2462            (let ((magic (widget-create-child-and-convert
2463                          widget 'custom-magic nil)))
2464              (widget-put widget :custom-magic magic)
2465              (push magic buttons))
2466            ;; Update buttons.
2467            (widget-put widget :buttons buttons)
2468            ;; Insert documentation.
2469            (widget-default-format-handler widget ?h)
2470            ;; The comment field
2471            (unless (eq state 'hidden)
2472              (let* ((comment (get symbol 'face-comment))
2473                     (comment-widget
2474                      (widget-create-child-and-convert
2475                       widget 'custom-comment
2476                       :parent widget
2477                       :value (or comment ""))))
2478                (widget-put widget :comment-widget comment-widget)
2479                (push comment-widget children)))
2480            ;; See also.
2481            (unless (eq state 'hidden)
2482              (when (eq (widget-get widget :custom-level) 1)
2483                (custom-add-parent-links widget))
2484              (custom-add-see-also widget))
2485            ;; Editor.
2486            (unless (eq (preceding-char) ?\n)
2487              (insert "\n"))
2488            (unless (eq state 'hidden)
2489              (message "Creating face editor...")
2490              (custom-load-widget widget)
2491              (unless (widget-get widget :custom-form)
2492                  (widget-put widget :custom-form custom-face-default-form))
2493              (let* ((symbol (widget-value widget))
2494                     (spec (custom-face-get-spec symbol))
2495                     (form (widget-get widget :custom-form))
2496                     (indent (widget-get widget :indent))
2497                     (edit (widget-create-child-and-convert
2498                            widget
2499                            (cond ((and (eq form 'selected)
2500                                        (widget-apply custom-face-selected
2501                                                      :match spec))
2502                                   (when indent (insert-char ?\  indent))
2503                                   'custom-face-selected)
2504                                  ((and (not (eq form 'lisp))
2505                                        (widget-apply custom-face-all
2506                                                      :match spec))
2507                                   'custom-face-all)
2508                                  (t
2509                                   (when indent (insert-char ?\  indent))
2510                                   'sexp))
2511                            :value spec)))
2512                (custom-face-state-set widget)
2513                (push edit children)
2514                (widget-put widget :children children))
2515              (message "Creating face editor...done"))))))
2516
2517 (defvar custom-face-menu
2518   '(("Set for Current Session" custom-face-set)
2519     ("Save for Future Sessions" custom-face-save)
2520     ("Reset to Saved" custom-face-reset-saved
2521      (lambda (widget)
2522        (or (get (widget-value widget) 'saved-face)
2523            (get (widget-value widget) 'saved-face-comment))))
2524     ("Reset to Standard Setting" custom-face-reset-standard
2525      (lambda (widget)
2526        (get (widget-value widget) 'face-defface-spec)))
2527     ("---" ignore ignore)
2528     ("Add Comment" custom-comment-show custom-comment-invisible-p)
2529     ("---" ignore ignore)
2530     ("Show all display specs" custom-face-edit-all
2531      (lambda (widget)
2532        (not (eq (widget-get widget :custom-form) 'all))))
2533     ("Just current attributes" custom-face-edit-selected
2534      (lambda (widget)
2535        (not (eq (widget-get widget :custom-form) 'selected))))
2536     ("Show as Lisp expression" custom-face-edit-lisp
2537      (lambda (widget)
2538        (not (eq (widget-get widget :custom-form) 'lisp)))))
2539   "Alist of actions for the `custom-face' widget.
2540 Each entry has the form (NAME ACTION FILTER) where NAME is the name of
2541 the menu entry, ACTION is the function to call on the widget when the
2542 menu is selected, and FILTER is a predicate which takes a `custom-face'
2543 widget as an argument, and returns non-nil if ACTION is valid on that
2544 widget. If FILTER is nil, ACTION is always valid.")
2545
2546 (defun custom-face-edit-selected (widget)
2547   "Edit selected attributes of the value of WIDGET."
2548   (widget-put widget :custom-state 'unknown)
2549   (widget-put widget :custom-form 'selected)
2550   (custom-redraw widget))
2551
2552 (defun custom-face-edit-all (widget)
2553   "Edit all attributes of the value of WIDGET."
2554   (widget-put widget :custom-state 'unknown)
2555   (widget-put widget :custom-form 'all)
2556   (custom-redraw widget))
2557
2558 (defun custom-face-edit-lisp (widget)
2559   "Edit the lisp representation of the value of WIDGET."
2560   (widget-put widget :custom-state 'unknown)
2561   (widget-put widget :custom-form 'lisp)
2562   (custom-redraw widget))
2563
2564 (defun custom-face-state-set (widget)
2565   "Set the state of WIDGET."
2566   (let* ((symbol (widget-value widget))
2567          (comment (get symbol 'face-comment))
2568          tmp temp)
2569     (widget-put widget :custom-state
2570                 (cond ((progn
2571                          (setq tmp (get symbol 'customized-face))
2572                          (setq temp (get symbol 'customized-face-comment))
2573                          (or tmp temp))
2574                        (if (equal temp comment)
2575                            'set
2576                          'changed))
2577                       ((progn
2578                          (setq tmp (get symbol 'saved-face))
2579                          (setq temp (get symbol 'saved-face-comment))
2580                          (or tmp temp))
2581                        (if (equal temp comment)
2582                            'saved
2583                          'changed))
2584                       ((get symbol 'face-defface-spec)
2585                        (if (equal comment nil)
2586                            'standard
2587                          'changed))
2588                       (t
2589                        'rogue)))))
2590
2591 (defun custom-face-action (widget &optional event)
2592   "Show the menu for `custom-face' WIDGET.
2593 Optional EVENT is the location for the menu."
2594   (if (eq (widget-get widget :custom-state) 'hidden)
2595       (custom-toggle-hide widget)
2596     (let* ((completion-ignore-case t)
2597            (symbol (widget-get widget :value))
2598            (answer (widget-choose (concat "Operation on "
2599                                           (custom-unlispify-tag-name symbol))
2600                                   (custom-menu-filter custom-face-menu
2601                                                       widget)
2602                                   event)))
2603       (if answer
2604           (funcall answer widget)))))
2605
2606 (defun custom-face-set (widget)
2607   "Make the face attributes in WIDGET take effect."
2608   (let* ((symbol (widget-value widget))
2609          (child (car (widget-get widget :children)))
2610          (value (widget-value child))
2611          (comment-widget (widget-get widget :comment-widget))
2612          (comment (widget-value comment-widget)))
2613     (when (equal comment "")
2614       (setq comment nil)
2615       ;; Make the comment invisible by hand if it's empty
2616       (set-extent-property (widget-get comment-widget :comment-extent)
2617                            'invisible t))
2618     (put symbol 'customized-face value)
2619     (face-spec-set symbol value nil '(custom))
2620     (put symbol 'customized-face-comment comment)
2621     (put symbol 'face-comment comment)
2622     (custom-face-state-set widget)
2623     (custom-redraw-magic widget)))
2624
2625 (defun custom-face-save (widget)
2626   "Make the face attributes in WIDGET default."
2627   (let* ((symbol (widget-value widget))
2628          (child (car (widget-get widget :children)))
2629          (value (widget-value child))
2630          (comment-widget (widget-get widget :comment-widget))
2631          (comment (widget-value comment-widget)))
2632     (when (equal comment "")
2633       (setq comment nil)
2634       ;; Make the comment invisible by hand if it's empty
2635       (set-extent-property (widget-get comment-widget :comment-extent)
2636                            'invisible t))
2637     (face-spec-set symbol value nil '(custom))
2638     (put symbol 'saved-face value)
2639     (custom-push-theme 'theme-face symbol 'user 'set value)
2640     (put symbol 'customized-face nil)
2641     (put symbol 'face-comment comment)
2642     (put symbol 'customized-face-comment nil)
2643     (put symbol 'saved-face-comment comment)
2644     (custom-save-all)
2645     (custom-face-state-set widget)
2646     (custom-redraw-magic widget)))
2647
2648 (defun custom-face-reset-saved (widget)
2649   "Restore WIDGET to the face's default attributes."
2650   (let* ((symbol (widget-value widget))
2651          (child (car (widget-get widget :children)))
2652          (value (get symbol 'saved-face))
2653          (comment (get symbol 'saved-face-comment))
2654          (comment-widget (widget-get widget :comment-widget)))
2655     (unless (or value comment)
2656       (signal 'error (list "No saved value for this face" symbol)))
2657     (put symbol 'customized-face nil)
2658     (put symbol 'customized-face-comment nil)
2659     (face-spec-set symbol value nil '(custom))
2660     (put symbol 'face-comment comment)
2661     (widget-value-set child value)
2662     ;; This call manages the comment visibility
2663     (widget-value-set comment-widget (or comment ""))
2664     (custom-face-state-set widget)
2665     (custom-redraw-magic widget)))
2666
2667 (defun custom-face-reset-standard (widget)
2668   "Restore WIDGET to the face's standard settings."
2669   (let* ((symbol (widget-value widget))
2670          (child (car (widget-get widget :children)))
2671          (value (get symbol 'face-defface-spec))
2672          (comment-widget (widget-get widget :comment-widget)))
2673     (unless value
2674       (signal 'error (list "No standard setting for this face" symbol)))
2675     (put symbol 'customized-face nil)
2676     (put symbol 'customized-face-comment nil)
2677     (when (or (get symbol 'saved-face) (get symbol 'saved-face-comment))
2678       (put symbol 'saved-face nil)
2679       (custom-push-theme 'theme-face symbol 'user 'reset 'standard)
2680       ;; Do not explictly save resets to standards without themes.
2681       (if (null (cdr (get symbol 'theme-face)))
2682           (put symbol  'theme-face nil))
2683       (put symbol 'saved-face-comment nil)
2684       (custom-save-all))
2685     (face-spec-set symbol value nil '(custom))
2686     (put symbol 'face-comment nil)
2687     (widget-value-set child value)
2688     ;; This call manages the comment visibility
2689     (widget-value-set comment-widget "")
2690     (custom-face-state-set widget)
2691     (custom-redraw-magic widget)))
2692
2693 ;;; The `face' Widget.
2694
2695 (define-widget 'face 'default
2696   "Select and customize a face."
2697   :convert-widget 'widget-value-convert-widget
2698   :button-prefix 'widget-push-button-prefix
2699   :button-suffix 'widget-push-button-suffix
2700   :format "%t: %[select face%] %v"
2701   :tag "Face"
2702   :value 'default
2703   :value-create 'widget-face-value-create
2704   :value-delete 'widget-face-value-delete
2705   :value-get 'widget-value-value-get
2706   :validate 'widget-children-validate
2707   :action 'widget-face-action
2708   :match (lambda (widget value) (symbolp value)))
2709
2710 (defun widget-face-value-create (widget)
2711   ;; Create a `custom-face' child.
2712   (let* ((symbol (widget-value widget))
2713          (custom-buffer-style 'face)
2714          (child (widget-create-child-and-convert
2715                  widget 'custom-face
2716                  :custom-level nil
2717                  :value symbol)))
2718     (custom-magic-reset child)
2719     (setq custom-options (cons child custom-options))
2720     (widget-put widget :children (list child))))
2721
2722 (defun widget-face-value-delete (widget)
2723   ;; Remove the child from the options.
2724   (let ((child (car (widget-get widget :children))))
2725     (setq custom-options (delq child custom-options))
2726     (widget-children-value-delete widget)))
2727
2728 (defvar face-history nil
2729   "History of entered face names.")
2730
2731 (defun widget-face-action (widget &optional event)
2732   "Prompt for a face."
2733   (let ((answer (completing-read "Face: "
2734                                  (mapcar (lambda (face)
2735                                            (list (symbol-name face)))
2736                                          (face-list))
2737                                  nil nil nil
2738                                  'face-history)))
2739     (unless (zerop (length answer))
2740       (widget-value-set widget (intern answer))
2741       (widget-apply widget :notify widget event)
2742       (widget-setup))))
2743
2744 ;;; The `hook' Widget.
2745
2746 (define-widget 'hook 'list
2747   "A emacs lisp hook"
2748   :value-to-internal (lambda (widget value)
2749                        (if (symbolp value)
2750                            (list value)
2751                          value))
2752   :match (lambda (widget value)
2753            (or (symbolp value)
2754                (widget-group-match widget value)))
2755   :convert-widget 'custom-hook-convert-widget
2756   :tag "Hook")
2757
2758 (defun custom-hook-convert-widget (widget)
2759   ;; Handle `:options'.
2760   (let* ((options (widget-get widget :options))
2761          (other `(editable-list :inline t
2762                                 :entry-format "%i %d%v"
2763                                 (function :format " %v")))
2764          (args (if options
2765                    (list `(checklist :inline t
2766                                      ,@(mapcar (lambda (entry)
2767                                                  `(function-item ,entry))
2768                                                options))
2769                          other)
2770                  (list other))))
2771     (widget-put widget :args args)
2772     widget))
2773
2774 ;;; The `plist' Widget.
2775
2776 (define-widget 'plist 'list
2777   "A property list."
2778   :match (lambda (widget value)
2779            (valid-plist-p value))
2780   :convert-widget 'custom-plist-convert-widget
2781   :tag "Property List")
2782
2783 ;; #### Should handle options better.
2784 (defun custom-plist-convert-widget (widget)
2785   (let* ((options (widget-get widget :options))
2786          (other `(editable-list :inline t
2787                                 (group :inline t
2788                                        (symbol :format "%t: %v "
2789                                                :size 10
2790                                                :tag "Property")
2791                                        (sexp :tag "Value"))))
2792          (args
2793           (if options
2794               `((checklist :inline t
2795                            ,@(mapcar 'custom-plist-process-option options))
2796                 ,other)
2797             (list other))))
2798     (widget-put widget :args args)
2799     widget))
2800
2801 (defun custom-plist-process-option (entry)
2802   `(group :inline t
2803           (const :tag "Property"
2804                  :format "%t: %v "
2805                  :size 10
2806                  ,entry)
2807           (sexp :tag "Value")))
2808
2809 ;;; The `custom-group-link' Widget.
2810
2811 (define-widget 'custom-group-link 'link
2812   "Show parent in other window when activated."
2813   :help-echo 'custom-group-link-help-echo
2814   :action 'custom-group-link-action)
2815
2816 (defun custom-group-link-help-echo (widget)
2817   (concat "Create customization buffer for the `"
2818           (custom-unlispify-tag-name (widget-value widget))
2819           "' group"))
2820
2821 (defun custom-group-link-action (widget &rest ignore)
2822   (customize-group (widget-value widget)))
2823
2824 ;;; The `custom-group' Widget.
2825
2826 (defcustom custom-group-tag-faces nil
2827   ;; In XEmacs, this ought to play games with font size.
2828   "Face used for group tags.
2829 The first member is used for level 1 groups, the second for level 2,
2830 and so forth.  The remaining group tags are shown with
2831 `custom-group-tag-face'."
2832   :type '(repeat face)
2833   :group 'custom-faces)
2834
2835 (defface custom-group-tag-face-1 '((((class color)
2836                                      (background dark))
2837                                     (:foreground "pink" :underline t))
2838                                    (((class color)
2839                                      (background light))
2840                                     (:foreground "red" :underline t))
2841                                    (t (:underline t)))
2842   "Face used for group tags.")
2843
2844 (defface custom-group-tag-face '((((class color)
2845                                    (background dark))
2846                                   (:foreground "light blue" :underline t))
2847                                  (((class color)
2848                                    (background light))
2849                                   (:foreground "blue" :underline t))
2850                                  (t (:underline t)))
2851   "Face used for low level group tags."
2852   :group 'custom-faces)
2853
2854 (define-widget 'custom-group 'custom
2855   "Customize group."
2856   :format "%v"
2857   :sample-face-get 'custom-group-sample-face-get
2858   :documentation-property 'group-documentation
2859   :help-echo "Set or reset all members of this group"
2860   :value-create 'custom-group-value-create
2861   :action 'custom-group-action
2862   :custom-category 'group
2863   :custom-set 'custom-group-set
2864   :custom-save 'custom-group-save
2865   :custom-reset-current 'custom-group-reset-current
2866   :custom-reset-saved 'custom-group-reset-saved
2867   :custom-reset-standard 'custom-group-reset-standard
2868   :custom-menu 'custom-group-menu-create)
2869
2870 (defun custom-group-sample-face-get (widget)
2871   ;; Use :sample-face.
2872   (or (nth (1- (widget-get widget :custom-level)) custom-group-tag-faces)
2873       'custom-group-tag-face))
2874
2875 (define-widget 'custom-group-visibility 'visibility
2876   "An indicator and manipulator for hidden group contents."
2877   :create 'custom-group-visibility-create)
2878
2879 (defun custom-group-visibility-create (widget)
2880   (let ((visible (widget-value widget)))
2881     (if visible
2882         (insert "--------")))
2883   (widget-default-create widget))
2884
2885 (defun custom-group-members (symbol groups-only)
2886   "Return SYMBOL's custom group members.
2887 If GROUPS-ONLY non-nil, return only those members that are groups."
2888   (if (not groups-only)
2889       (get symbol 'custom-group)
2890     (let (members)
2891       (dolist (entry (get symbol 'custom-group) (nreverse members))
2892         (when (eq (nth 1 entry) 'custom-group)
2893           (push entry members))))))
2894
2895 (defun custom-group-value-create (widget)
2896   "Insert a customize group for WIDGET in the current buffer."
2897   (let* ((state (widget-get widget :custom-state))
2898          (level (widget-get widget :custom-level))
2899          ;; (indent (widget-get widget :indent))
2900          (prefix (widget-get widget :custom-prefix))
2901          (buttons (widget-get widget :buttons))
2902          (tag (widget-get widget :tag))
2903          (symbol (widget-value widget))
2904          (members (custom-group-members symbol
2905                                         (and (eq custom-buffer-style 'tree)
2906                                              custom-browse-only-groups))))
2907     (cond ((and (eq custom-buffer-style 'tree)
2908                 (eq state 'hidden)
2909                 (or members (custom-unloaded-widget-p widget)))
2910            (custom-browse-insert-prefix prefix)
2911            (push (widget-create-child-and-convert
2912                   widget 'custom-browse-visibility
2913                   ;; :tag-glyph "plus"
2914                   :tag "+")
2915                  buttons)
2916            (insert "-- ")
2917            ;; (widget-glyph-insert nil "-- " "horizontal")
2918            (push (widget-create-child-and-convert
2919                   widget 'custom-browse-group-tag)
2920                  buttons)
2921            (insert " " tag "\n")
2922            (widget-put widget :buttons buttons))
2923           ((and (eq custom-buffer-style 'tree)
2924                 (zerop (length members)))
2925            (custom-browse-insert-prefix prefix)
2926            (insert "[ ]-- ")
2927            ;; (widget-glyph-insert nil "[ ]" "empty")
2928            ;; (widget-glyph-insert nil "-- " "horizontal")
2929            (push (widget-create-child-and-convert
2930                   widget 'custom-browse-group-tag)
2931                  buttons)
2932            (insert " " tag "\n")
2933            (widget-put widget :buttons buttons))
2934           ((eq custom-buffer-style 'tree)
2935            (custom-browse-insert-prefix prefix)
2936            (custom-load-widget widget)
2937            (if (zerop (length members))
2938                (progn
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              (push (widget-create-child-and-convert
2949                     widget 'custom-browse-visibility
2950                     ;; :tag-glyph "minus"
2951                     :tag "-")
2952                    buttons)
2953              (insert "-\\ ")
2954              ;; (widget-glyph-insert nil "-\\ " "top")
2955              (push (widget-create-child-and-convert
2956                     widget 'custom-browse-group-tag)
2957                    buttons)
2958              (insert " " tag "\n")
2959              (widget-put widget :buttons buttons)
2960              (message "Creating group...")
2961              (let* ((members (custom-sort-items members
2962                               custom-browse-sort-alphabetically
2963                               custom-browse-order-groups))
2964                     (prefixes (widget-get widget :custom-prefixes))
2965                     (custom-prefix-list (custom-prefix-add symbol prefixes))
2966                     (extra-prefix (if (widget-get widget :custom-last)
2967                                       "   "
2968                                     " | "))
2969                     (prefix (concat prefix extra-prefix))
2970                     children entry)
2971                (while members
2972                  (setq entry (car members)
2973                        members (cdr members))
2974                  (push (widget-create-child-and-convert
2975                         widget (nth 1 entry)
2976                         :group widget
2977                         :tag (custom-unlispify-tag-name (nth 0 entry))
2978                         :custom-prefixes custom-prefix-list
2979                         :custom-level (1+ level)
2980                         :custom-last (null members)
2981                         :value (nth 0 entry)
2982                         :custom-prefix prefix)
2983                        children))
2984                (widget-put widget :children (reverse children)))
2985              (message "Creating group...done")))
2986           ;; Nested style.
2987           ((eq state 'hidden)
2988            ;; Create level indicator.
2989            (unless (eq custom-buffer-style 'links)
2990              (insert-char ?\  (* custom-buffer-indent (1- level)))
2991              (insert "-- "))
2992            ;; Create link indicator.
2993            (when (eq custom-buffer-style 'links)
2994              (insert " ")
2995              (push (widget-create-child-and-convert
2996                     widget 'custom-group-link
2997                     :tag "Open"
2998                     :tag-glyph '("open-up" "open-down")
2999                     symbol)
3000                    buttons)
3001              (insert " "))
3002            ;; Create tag.
3003            (let ((begin (point)))
3004              (insert tag)
3005              (widget-specify-sample widget begin (point)))
3006            (insert " group")
3007            ;; Create visibility indicator.
3008            (unless (eq custom-buffer-style 'links)
3009              (insert ": ")
3010              (push (widget-create-child-and-convert
3011                     widget 'custom-group-visibility
3012                     :help-echo "Show members of this group"
3013                     :action 'custom-toggle-parent
3014                     (not (eq state 'hidden)))
3015                    buttons))
3016            (insert " \n")
3017            ;; Create magic button.
3018            (let ((magic (widget-create-child-and-convert
3019                          widget 'custom-magic nil)))
3020              (widget-put widget :custom-magic magic)
3021              (push magic buttons))
3022            ;; Update buttons.
3023            (widget-put widget :buttons buttons)
3024            ;; Insert documentation.
3025            (if (and (eq custom-buffer-style 'links) (> level 1))
3026                (widget-put widget :documentation-indent 0))
3027            (widget-default-format-handler widget ?h))
3028           ;; Nested style.
3029           (t                            ;Visible.
3030            (custom-load-widget widget)
3031            ;; Update members
3032            (setq members (custom-group-members
3033                           symbol (and (eq custom-buffer-style 'tree)
3034                                       custom-browse-only-groups)))
3035            ;; Add parent groups references above the group.
3036            (if t    ;;; This should test that the buffer
3037                     ;;; was made to display a group.
3038                (when (eq level 1)
3039                  (if (custom-add-parent-links widget
3040                                               "Go to parent group:")
3041                      (insert "\n"))))
3042            ;; Create level indicator.
3043            (insert-char ?\  (* custom-buffer-indent (1- level)))
3044            (insert "/- ")
3045            ;; Create tag.
3046            (let ((start (point)))
3047              (insert tag)
3048              (widget-specify-sample widget start (point)))
3049            (insert " group: ")
3050            ;; Create visibility indicator.
3051            (unless (eq custom-buffer-style 'links)
3052              (insert "--------")
3053              (push (widget-create-child-and-convert
3054                     widget 'visibility
3055                     :help-echo "Hide members of this group"
3056                     :action 'custom-toggle-parent
3057                     (not (eq state 'hidden)))
3058                    buttons)
3059              (insert " "))
3060            ;; Create more dashes.
3061            ;; Use 76 instead of 75 to compensate for the temporary "<"
3062            ;; added by `widget-insert'.
3063            (insert-char ?- (- 76 (current-column)
3064                               (* custom-buffer-indent level)))
3065            (insert "\\\n")
3066            ;; Create magic button.
3067            (let ((magic (widget-create-child-and-convert
3068                          widget 'custom-magic
3069                          :indent 0
3070                          nil)))
3071              (widget-put widget :custom-magic magic)
3072              (push magic buttons))
3073            ;; Update buttons.
3074            (widget-put widget :buttons buttons)
3075            ;; Insert documentation.
3076            (widget-default-format-handler widget ?h)
3077            ;; Parent groups.
3078            (if nil  ;;; This should test that the buffer
3079                     ;;; was not made to display a group.
3080                (when (eq level 1)
3081                  (insert-char ?\  custom-buffer-indent)
3082                  (custom-add-parent-links widget)))
3083            (custom-add-see-also widget
3084                                 (make-string (* custom-buffer-indent level)
3085                                              ?\ ))
3086            ;; Members.
3087            (message "Creating group...")
3088            (let* ((members (custom-sort-items members
3089                                               custom-buffer-sort-alphabetically
3090                                               custom-buffer-order-groups))
3091                   (prefixes (widget-get widget :custom-prefixes))
3092                   (custom-prefix-list (custom-prefix-add symbol prefixes))
3093                   (length (length members))
3094                   (count 0)
3095                   (children (mapcar
3096                              (lambda (entry)
3097                                (widget-insert "\n")
3098                                (when (zerop (% count custom-skip-messages))
3099                                  (display-message
3100                                   'progress
3101                                   (format "\
3102 Creating group members... %2d%%"
3103                                           (/ (* 100.0 count) length))))
3104                                (incf count)
3105                                (prog1
3106                                    (widget-create-child-and-convert
3107                                     widget (nth 1 entry)
3108                                     :group widget
3109                                     :tag (custom-unlispify-tag-name
3110                                           (nth 0 entry))
3111                                     :custom-prefixes custom-prefix-list
3112                                     :custom-level (1+ level)
3113                                     :value (nth 0 entry))
3114                                  (unless (eq (preceding-char) ?\n)
3115                                    (widget-insert "\n"))))
3116                              members)))
3117              (message "Creating group magic...")
3118              (mapc 'custom-magic-reset children)
3119              (message "Creating group state...")
3120              (widget-put widget :children children)
3121              (custom-group-state-update widget)
3122              (message "Creating group... done"))
3123            ;; End line
3124            (insert "\n")
3125            (insert-char ?\  (* custom-buffer-indent (1- level)))
3126            (insert "\\- " (widget-get widget :tag) " group end ")
3127            (insert-char ?- (- 75 (current-column) (* custom-buffer-indent level)))
3128            (insert "/\n")))))
3129
3130 (defvar custom-group-menu
3131   '(("Set for Current Session" custom-group-set
3132      (lambda (widget)
3133        (eq (widget-get widget :custom-state) 'modified)))
3134     ("Save for Future Sessions" custom-group-save
3135      (lambda (widget)
3136        (memq (widget-get widget :custom-state) '(modified set))))
3137     ("Reset to Current" custom-group-reset-current
3138      (lambda (widget)
3139        (memq (widget-get widget :custom-state) '(modified))))
3140     ("Reset to Saved" custom-group-reset-saved
3141      (lambda (widget)
3142        (memq (widget-get widget :custom-state) '(modified set))))
3143     ("Reset to standard setting" custom-group-reset-standard
3144      (lambda (widget)
3145        (memq (widget-get widget :custom-state) '(modified set saved)))))
3146   "Alist of actions for the `custom-group' widget.
3147 Each entry has the form (NAME ACTION FILTER) where NAME is the name of
3148 the menu entry, ACTION is the function to call on the widget when the
3149 menu is selected, and FILTER is a predicate which takes a `custom-group'
3150 widget as an argument, and returns non-nil if ACTION is valid on that
3151 widget. If FILTER is nil, ACTION is always valid.")
3152
3153 (defun custom-group-action (widget &optional event)
3154   "Show the menu for `custom-group' WIDGET.
3155 Optional EVENT is the location for the menu."
3156   (if (eq (widget-get widget :custom-state) 'hidden)
3157       (custom-toggle-hide widget)
3158     (let* ((completion-ignore-case t)
3159            (answer (widget-choose (concat "Operation on "
3160                                           (custom-unlispify-tag-name
3161                                            (widget-get widget :value)))
3162                                   (custom-menu-filter custom-group-menu
3163                                                       widget)
3164                                   event)))
3165       (if answer
3166           (funcall answer widget)))))
3167
3168 (defun custom-group-set (widget)
3169   "Set changes in all modified group members."
3170   (let ((children (widget-get widget :children)))
3171     (mapc (lambda (child)
3172             (when (eq (widget-get child :custom-state) 'modified)
3173               (widget-apply child :custom-set)))
3174           children)))
3175
3176 (defun custom-group-save (widget)
3177   "Save all modified group members."
3178   (let ((children (widget-get widget :children)))
3179     (mapc (lambda (child)
3180             (when (memq (widget-get child :custom-state) '(modified set))
3181               (widget-apply child :custom-save)))
3182           children)))
3183
3184 (defun custom-group-reset-current (widget)
3185   "Reset all modified group members."
3186   (let ((children (widget-get widget :children)))
3187     (mapc (lambda (child)
3188             (when (eq (widget-get child :custom-state) 'modified)
3189               (widget-apply child :custom-reset-current)))
3190           children)))
3191
3192 (defun custom-group-reset-saved (widget)
3193   "Reset all modified or set group members."
3194   (let ((children (widget-get widget :children)))
3195     (mapc (lambda (child)
3196             (when (memq (widget-get child :custom-state) '(modified set))
3197               (widget-apply child :custom-reset-saved)))
3198           children)))
3199
3200 (defun custom-group-reset-standard (widget)
3201   "Reset all modified, set, or saved group members."
3202   (let ((children (widget-get widget :children)))
3203     (mapc (lambda (child)
3204             (when (memq (widget-get child :custom-state)
3205                         '(modified set saved))
3206               (widget-apply child :custom-reset-standard)))
3207           children)))
3208
3209 (defun custom-group-state-update (widget)
3210   "Update magic."
3211   (unless (eq (widget-get widget :custom-state) 'hidden)
3212     (let* ((children (widget-get widget :children))
3213            (states (mapcar (lambda (child)
3214                              (widget-get child :custom-state))
3215                            children))
3216            (magics custom-magic-alist)
3217            (found 'standard))
3218       (while magics
3219         (let ((magic (car (car magics))))
3220           (if (and (not (eq magic 'hidden))
3221                    (memq magic states))
3222               (setq found magic
3223                     magics nil)
3224             (setq magics (cdr magics)))))
3225       (widget-put widget :custom-state found)))
3226   (custom-magic-reset widget))
3227
3228 ;;; The `custom-save-all' Function.
3229 ;;;###autoload
3230 (defcustom custom-file "~/.emacs"
3231   "File used for storing customization information.
3232 If you change this from the default \"~/.emacs\" you need to
3233 explicitly load that file for the settings to take effect."
3234   :type 'file
3235   :group 'customize)
3236
3237 (defun custom-save-delete (symbol)
3238   "Delete the call to SYMBOL form in `custom-file'.
3239 Leave point at the location of the call, or after the last expression."
3240   (let ((find-file-hooks nil)
3241         (auto-mode-alist nil))
3242     (set-buffer (find-file-noselect custom-file)))
3243   (goto-char (point-min))
3244   (catch 'found
3245     (while t
3246       (let ((sexp (condition-case nil
3247                       (read (current-buffer))
3248                     (end-of-file (throw 'found nil)))))
3249         (when (and (listp sexp)
3250                    (eq (car sexp) symbol))
3251           (delete-region (save-excursion
3252                            (backward-sexp)
3253                            (point))
3254                          (point))
3255           (throw 'found nil))))))
3256
3257 (defun custom-save-variables ()
3258    "Save all customized variables in `custom-file'."
3259    (save-excursion
3260      (custom-save-delete 'custom-load-themes)
3261      (custom-save-delete 'custom-reset-variables)
3262      (custom-save-delete 'custom-set-variables)
3263      (custom-save-loaded-themes)
3264      (custom-save-resets 'theme-value 'custom-reset-variables nil)
3265      (let ((standard-output (current-buffer)))
3266        (unless (bolp)
3267         (princ "\n"))
3268        (princ "(custom-set-variables")
3269        (mapatoms (lambda (symbol)                
3270                   (let ((spec (car-safe (get symbol 'theme-value)))
3271                         (requests (get symbol 'custom-requests))
3272                         (now (not (or (get symbol 'standard-value)
3273                                       (and (not (boundp symbol))
3274                                            (not (eq (get symbol 'force-value)
3275                                                     'rogue))))))
3276                         (comment (get symbol 'saved-variable-comment)))
3277                     (when (or (and spec (eq (car spec) 'user)
3278                                (eq (second spec) 'set)) comment)
3279                       (princ "\n '(")
3280                       (prin1 symbol)
3281                       (princ " ")
3282                       ;; This comment stuf is in the way ####
3283                       ;; Is (eq (third spec) (car saved-value)) ????
3284                       ;; (prin1 (third spec))
3285                       (prin1 (car (get symbol 'saved-value)))
3286                       (when (or now requests comment)
3287                         (princ (if now " t" " nil")))
3288                       (when (or comment requests)
3289                         (princ " ")
3290                         (prin1 requests))
3291                       (when comment
3292                         (princ " ")
3293                         (prin1 comment))
3294                       (princ ")")))))
3295       (princ ")")
3296       (unless (looking-at "\n")
3297         (princ "\n")))))
3298
3299 (defvar custom-save-face-ignoring nil)
3300
3301 (defun custom-save-face-internal (symbol)
3302   (let ((theme-spec (car-safe (get symbol 'theme-face)))
3303         (comment (get symbol 'saved-face-comment))
3304         (now (not (or (get symbol 'face-defface-spec)
3305               (and (not (find-face symbol))
3306                    (not (eq (get symbol 'force-face) 'rogue)))))))
3307     (when (or (and (not (memq symbol custom-save-face-ignoring))
3308                ;; Don't print default face here.
3309                theme-spec
3310                (eq (car theme-spec) 'user)
3311                (eq (second theme-spec) 'set)) comment)
3312       (princ "\n '(")
3313       (prin1 symbol)
3314       (princ " ")
3315       (prin1 (get symbol 'saved-face))
3316       (if (or comment now)
3317           (princ (if now " t" " nil")))
3318       (when comment
3319           (princ " ")
3320           (prin1 comment))
3321       (princ ")"))))
3322
3323 (defun custom-save-faces ()
3324   "Save all customized faces in `custom-file'."
3325   (save-excursion
3326     (custom-save-delete 'custom-reset-faces)
3327     (custom-save-delete 'custom-set-faces)
3328     (custom-save-resets 'theme-face 'custom-reset-faces '(default))
3329     (let ((standard-output (current-buffer)))
3330       (unless (bolp)
3331         (princ "\n"))
3332       (princ "(custom-set-faces")
3333         ;; The default face must be first, since it affects the others.
3334       (custom-save-face-internal 'default)
3335       (let ((custom-save-face-ignoring '(default)))
3336         (mapatoms #'custom-save-face-internal))
3337       (princ ")")
3338       (unless (looking-at "\n")
3339         (princ "\n")))))
3340
3341 (defun custom-save-resets (property setter special)
3342   (let (started-writing ignored-special)
3343     ;; (custom-save-delete setter) Done by caller 
3344     (let ((standard-output (current-buffer))
3345           (mapper `(lambda (object)
3346                     (let ((spec (car-safe (get object (quote ,property)))))
3347                       (when (and (not (memq object ignored-special))
3348                                  (eq (car spec) 'user)
3349                                  (eq (second spec) 'reset))
3350                         ;; Do not write reset statements unless necessary.
3351                         (unless started-writing
3352                           (setq started-writing t)
3353                           (unless (bolp)
3354                             (princ "\n"))
3355                         (princ "(")
3356                         (princ (quote ,setter))
3357                         (princ "\n '(")
3358                         (prin1 object)
3359                         (princ " ")
3360                         (prin1 (third spec))
3361                         (princ ")")))))))
3362       (mapc mapper special)
3363       (setq ignored-special special)
3364       (mapatoms mapper)
3365       (when started-writing
3366         (princ ")\n")))))
3367                         
3368
3369 (defun custom-save-loaded-themes ()
3370   (let ((themes (reverse (get 'user 'theme-loads-themes)))
3371         (standard-output (current-buffer)))
3372     (when themes
3373       (unless (bolp) (princ "\n"))
3374       (princ "(custom-load-themes")
3375       (mapc (lambda (theme)
3376               (princ "\n   '")
3377               (prin1 theme)) themes)
3378       (princ " )\n"))))  
3379
3380 ;;;###autoload
3381 (defun customize-save-customized ()
3382   "Save all user options which have been set in this session."
3383   (interactive)
3384   (mapatoms (lambda (symbol)
3385               (let ((face (get symbol 'customized-face))
3386                     (value (get symbol 'customized-value))
3387                     (face-comment (get symbol 'customized-face-comment))
3388                     (variable-comment
3389                      (get symbol 'customized-variable-comment)))
3390                 (when face
3391                   (put symbol 'saved-face face)
3392                   (custom-push-theme 'theme-face symbol 'user 'set value)
3393                   (put symbol 'customized-face nil))
3394                 (when value
3395                   (put symbol 'saved-value value)
3396                   (custom-push-theme 'theme-value symbol 'user 'set value)
3397                   (put symbol 'customized-value nil))
3398                 (when variable-comment
3399                   (put symbol 'saved-variable-comment variable-comment)
3400                   (put symbol 'customized-variable-comment nil))
3401                 (when face-comment
3402                   (put symbol 'saved-face-comment face-comment)
3403                   (put symbol 'customized-face-comment nil)))))
3404   ;; We really should update all custom buffers here.
3405   (custom-save-all))
3406
3407 ;;;###autoload
3408 (defun custom-save-all ()
3409   "Save all customizations in `custom-file'."
3410   (let ((inhibit-read-only t))
3411     (custom-save-variables)
3412     (custom-save-faces)
3413     (let ((find-file-hooks nil)
3414           (auto-mode-alist))
3415       (with-current-buffer (find-file-noselect custom-file)
3416         (save-buffer)))))
3417
3418 \f
3419 ;;; The Customize Menu.
3420
3421 ;;; Menu support
3422
3423 (defun custom-face-menu-create (widget symbol)
3424   "Ignoring WIDGET, create a menu entry for customization face SYMBOL."
3425   (vector (custom-unlispify-menu-entry symbol)
3426           `(customize-face ',symbol)
3427           t))
3428
3429 (defun custom-variable-menu-create (widget symbol)
3430   "Ignoring WIDGET, create a menu entry for customization variable SYMBOL."
3431   (let ((type (get symbol 'custom-type)))
3432     (unless (listp type)
3433       (setq type (list type)))
3434     (if (and type (widget-get type :custom-menu))
3435         (widget-apply type :custom-menu symbol)
3436       (vector (custom-unlispify-menu-entry symbol)
3437               `(customize-variable ',symbol)
3438               t))))
3439
3440 ;; Add checkboxes to boolean variable entries.
3441 (widget-put (get 'boolean 'widget-type)
3442             :custom-menu (lambda (widget symbol)
3443                            `[,(custom-unlispify-menu-entry symbol)
3444                              (customize-variable ',symbol)
3445                              :style toggle
3446                              :selected ,symbol]))
3447
3448 ;; XEmacs can create menus dynamically.
3449 (defun custom-group-menu-create (widget symbol)
3450   "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
3451   `( ,(custom-unlispify-menu-entry symbol t)
3452      :filter (lambda (&rest junk)
3453                (let ((item (custom-menu-create ',symbol)))
3454                  (if (listp item)
3455                      (cdr item)
3456                    (list item))))))
3457
3458 ;;;###autoload
3459 (defun custom-menu-create (symbol)
3460   "Create menu for customization group SYMBOL.
3461 The menu is in a format applicable to `easy-menu-define'."
3462   (let* ((item (vector (custom-unlispify-menu-entry symbol)
3463                        `(customize-group ',symbol)
3464                        t)))
3465     ;; Item is the entry for creating a menu buffer for SYMBOL.
3466     ;; We may nest, if the menu is not too big.
3467     (custom-load-symbol symbol)
3468     (if (< (length (get symbol 'custom-group)) widget-menu-max-size)
3469         ;; The menu is not too big.
3470         (let ((custom-prefix-list (custom-prefix-add symbol
3471                                                      custom-prefix-list))
3472               (members (custom-sort-items (get symbol 'custom-group)
3473                                           custom-menu-sort-alphabetically
3474                                           custom-menu-order-groups)))
3475           ;; Create the menu.
3476           `(,(custom-unlispify-menu-entry symbol t)
3477             ,item
3478             "--"
3479             ,@(mapcar (lambda (entry)
3480                         (widget-apply (if (listp (nth 1 entry))
3481                                           (nth 1 entry)
3482                                         (list (nth 1 entry)))
3483                                       :custom-menu (nth 0 entry)))
3484                       members)))
3485       ;; The menu was too big.
3486       item)))
3487
3488 ;;;###autoload
3489 (defun customize-menu-create (symbol &optional name)
3490   "Return a customize menu for customization group SYMBOL.
3491 If optional NAME is given, use that as the name of the menu.
3492 Otherwise the menu will be named `Customize'.
3493 The format is suitable for use with `easy-menu-define'."
3494   (unless name
3495     (setq name "Customize"))
3496   `(,name
3497     :filter (lambda (&rest junk)
3498               (cdr (custom-menu-create ',symbol)))))
3499
3500 ;;; The Custom Mode.
3501
3502 (defvar custom-mode-map nil
3503   "Keymap for `custom-mode'.")
3504
3505 (unless custom-mode-map
3506   (setq custom-mode-map (make-sparse-keymap))
3507   (set-keymap-parents custom-mode-map widget-keymap)
3508   (suppress-keymap custom-mode-map)
3509   (define-key custom-mode-map " " 'scroll-up)
3510   (define-key custom-mode-map [delete] 'scroll-down)
3511   (define-key custom-mode-map "q" 'Custom-buffer-done)
3512   (define-key custom-mode-map "u" 'Custom-goto-parent)
3513   (define-key custom-mode-map "n" 'widget-forward)
3514   (define-key custom-mode-map "p" 'widget-backward))
3515
3516 (easy-menu-define Custom-mode-menu
3517     custom-mode-map
3518   "Menu used in customization buffers."
3519   `("Custom"
3520     ,(customize-menu-create 'customize)
3521     ["Set" Custom-set t]
3522     ["Save" Custom-save t]
3523     ["Reset to Current" Custom-reset-current t]
3524     ["Reset to Saved" Custom-reset-saved t]
3525     ["Reset to Standard Settings" Custom-reset-standard t]
3526     ["Info" (Info-goto-node "(xemacs)Easy Customization") t]))
3527
3528 (defun Custom-goto-parent ()
3529   "Go to the parent group listed at the top of this buffer.
3530 If several parents are listed, go to the first of them."
3531   (interactive)
3532   (save-excursion
3533     (goto-char (point-min))
3534     (if (search-forward "\nGo to parent group: " nil t)
3535         (let* ((button (get-char-property (point) 'button))
3536                (parent (downcase (widget-get  button :tag))))
3537           (customize-group parent)))))
3538
3539 (defcustom custom-mode-hook nil
3540   "Hook called when entering custom-mode."
3541   :type 'hook
3542   :group 'custom-buffer )
3543
3544 (defun custom-state-buffer-message (widget)
3545   (if (eq (widget-get (widget-get widget :parent) :custom-state) 'modified)
3546       (message
3547        "To install your edits, invoke [State] and choose the Set operation")))
3548
3549 (defun custom-mode ()
3550   "Major mode for editing customization buffers.
3551
3552 The following commands are available:
3553
3554 Move to next button or editable field.     \\[widget-forward]
3555 Move to previous button or editable field. \\[widget-backward]
3556 \\<widget-field-keymap>\
3557 Complete content of editable text field.   \\[widget-complete]
3558 \\<custom-mode-map>\
3559 Invoke button under point.                 \\[widget-button-press]
3560 Set all modifications.                     \\[Custom-set]
3561 Make all modifications default.            \\[Custom-save]
3562 Reset all modified options.                \\[Custom-reset-current]
3563 Reset all modified or set options.         \\[Custom-reset-saved]
3564 Reset all options.                         \\[Custom-reset-standard]
3565
3566 Entry to this mode calls the value of `custom-mode-hook'
3567 if that value is non-nil."
3568   (kill-all-local-variables)
3569   (setq major-mode 'custom-mode
3570         mode-name "Custom")
3571   (use-local-map custom-mode-map)
3572   (easy-menu-add Custom-mode-menu)
3573   (make-local-variable 'custom-options)
3574   (make-local-variable 'widget-documentation-face)
3575   (setq widget-documentation-face 'custom-documentation-face)
3576   (make-local-variable 'widget-button-face)
3577   (setq widget-button-face 'custom-button-face)
3578   (make-local-hook 'widget-edit-functions)
3579   (add-hook 'widget-edit-functions 'custom-state-buffer-message nil t)
3580   (run-hooks 'custom-mode-hook))
3581
3582 \f
3583 ;;; The End.
3584
3585 (provide 'cus-edit)
3586
3587 ;; cus-edit.el ends here