*** empty log message ***
[gnus] / lisp / cus-edit.el
1 ;;; cus-edit.el --- Tools for customization Emacs.
2 ;;
3 ;; Copyright (C) 1996, 1997 Free Software Foundation, Inc.
4 ;;
5 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6 ;; Keywords: help, faces
7 ;; Version: 1.65
8 ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
9
10 ;;; Commentary:
11 ;;
12 ;; See `custom.el'.
13
14 ;;; Code:
15
16 (require 'cus-face)
17 (require 'wid-edit)
18 (require 'easymenu)
19
20 (define-widget-keywords :custom-prefixes :custom-menu :custom-show
21   :custom-magic :custom-state :custom-level :custom-form
22   :custom-set :custom-save :custom-reset-current :custom-reset-saved 
23   :custom-reset-factory)
24
25 ;;; Customization Groups.
26
27 (defgroup emacs nil
28   "Customization of the One True Editor."
29   :link '(custom-manual "(emacs)Top"))
30
31 ;; Most of these groups are stolen from `finder.el',
32 (defgroup editing nil
33   "Basic text editing facilities."
34   :group 'emacs)
35
36 (defgroup abbrev nil
37   "Abbreviation handling, typing shortcuts, macros."
38   :tag "Abbreviations"
39   :group 'editing)
40
41 (defgroup matching nil
42   "Various sorts of searching and matching."
43   :group 'editing)
44
45 (defgroup emulations nil
46   "Emulations of other editors."
47   :group 'editing)
48
49 (defgroup mouse nil
50   "Mouse support."
51   :group 'editing)
52
53 (defgroup outlines nil
54   "Support for hierarchical outlining."
55   :group 'editing)
56
57 (defgroup external nil
58   "Interfacing to external utilities."
59   :group 'emacs)
60
61 (defgroup bib nil
62   "Code related to the `bib' bibliography processor."
63   :tag "Bibliography"
64   :group 'external)
65
66 (defgroup processes nil
67   "Process, subshell, compilation, and job control support."
68   :group 'external
69   :group 'development)
70
71 (defgroup programming nil
72   "Support for programming in other languages."
73   :group 'emacs)
74
75 (defgroup languages nil
76   "Specialized modes for editing programming languages."
77   :group 'programming)
78
79 (defgroup lisp nil
80   "Lisp support, including Emacs Lisp."
81   :group 'languages
82   :group 'development)
83
84 (defgroup c nil
85   "Support for the C language and related languages."
86   :group 'languages)
87
88 (defgroup tools nil
89   "Programming tools."
90   :group 'programming)
91
92 (defgroup oop nil
93   "Support for object-oriented programming."
94   :group 'programming)
95
96 (defgroup applications nil
97   "Applications written in Emacs."
98   :group 'emacs)
99
100 (defgroup calendar nil
101   "Calendar and time management support."
102   :group 'applications)
103
104 (defgroup mail nil
105   "Modes for electronic-mail handling."
106   :group 'applications)
107
108 (defgroup news nil
109   "Support for netnews reading and posting."
110   :group 'applications)
111
112 (defgroup games nil
113   "Games, jokes and amusements."
114   :group 'applications)
115
116 (defgroup development nil
117   "Support for further development of Emacs."
118   :group 'emacs)
119
120 (defgroup docs nil
121   "Support for Emacs documentation."
122   :group 'development)
123
124 (defgroup extensions nil
125   "Emacs Lisp language extensions."
126   :group 'development)
127
128 (defgroup internal nil
129   "Code for Emacs internals, build process, defaults."
130   :group 'development)
131
132 (defgroup maint nil
133   "Maintenance aids for the Emacs development group."
134   :tag "Maintenance"
135   :group 'development)
136
137 (defgroup environment nil
138   "Fitting Emacs with its environment."
139   :group 'emacs)
140
141 (defgroup comm nil
142   "Communications, networking, remote access to files."
143   :tag "Communication"
144   :group 'environment)
145
146 (defgroup hardware nil
147   "Support for interfacing with exotic hardware."
148   :group 'environment)
149
150 (defgroup terminals nil
151   "Support for terminal types."
152   :group 'environment)
153
154 (defgroup unix nil
155   "Front-ends/assistants for, or emulators of, UNIX features."
156   :group 'environment)
157
158 (defgroup vms nil
159   "Support code for vms."
160   :group 'environment)
161
162 (defgroup i18n nil
163   "Internationalization and alternate character-set support."
164   :group 'environment
165   :group 'editing)
166
167 (defgroup frames nil
168   "Support for Emacs frames and window systems."
169   :group 'environment)
170
171 (defgroup data nil
172   "Support editing files of data."
173   :group 'emacs)
174
175 (defgroup wp nil
176   "Word processing."
177   :group 'emacs)
178
179 (defgroup tex nil
180   "Code related to the TeX formatter."
181   :group 'wp)
182
183 (defgroup faces nil
184   "Support for multiple fonts."
185   :group 'emacs)
186
187 (defgroup hypermedia nil
188   "Support for links between text or other media types."
189   :group 'emacs)
190
191 (defgroup help nil
192   "Support for on-line help systems."
193   :group 'emacs)
194
195 (defgroup local nil
196   "Code local to your site."
197   :group 'emacs)
198
199 (defgroup customize '((widgets custom-group))
200   "Customization of the Customization support."
201   :link '(custom-manual "(custom)Top")
202   :link '(url-link :tag "Development Page" 
203                    "http://www.dina.kvl.dk/~abraham/custom/")
204   :prefix "custom-"
205   :group 'help
206   :group 'faces)
207
208 ;;; Utilities.
209
210 (defun custom-quote (sexp)
211   "Quote SEXP iff it is not self quoting."
212   (if (or (memq sexp '(t nil))
213           (and (symbolp sexp)
214                (eq (aref (symbol-name sexp) 0) ?:))
215           (and (listp sexp)
216                (memq (car sexp) '(lambda)))
217           (stringp sexp)
218           (numberp sexp)
219           (and (fboundp 'characterp)
220                (characterp sexp)))
221       sexp
222     (list 'quote sexp)))
223
224 (defun custom-split-regexp-maybe (regexp)
225   "If REGEXP is a string, split it to a list at `\\|'.
226 You can get the original back with from the result with: 
227   (mapconcat 'identity result \"\\|\")
228
229 IF REGEXP is not a string, return it unchanged."
230   (if (stringp regexp)
231       (let ((start 0)
232             all)
233         (while (string-match "\\\\|" regexp start)
234           (setq all (cons (substring regexp start (match-beginning 0)) all)
235                 start (match-end 0)))
236         (nreverse (cons (substring regexp start) all)))
237     regexp))
238
239 (defvar custom-prefix-list nil
240   "List of prefixes that should be ignored by `custom-unlispify'")
241
242 (defcustom custom-unlispify-menu-entries t
243   "Display menu entries as words instead of symbols if non nil."
244   :group 'customize
245   :type 'boolean)
246
247 (defun custom-unlispify-menu-entry (symbol &optional no-suffix)
248   "Convert symbol into a menu entry."
249   (cond ((not custom-unlispify-menu-entries)
250          (symbol-name symbol))
251         ((get symbol 'custom-tag)
252          (if no-suffix
253              (get symbol 'custom-tag)
254            (concat (get symbol 'custom-tag) "...")))
255         (t
256          (save-excursion
257            (set-buffer (get-buffer-create " *Custom-Work*"))
258            (erase-buffer)
259            (princ symbol (current-buffer))
260            (goto-char (point-min))
261            (let ((prefixes custom-prefix-list)
262                  prefix)
263              (while prefixes
264                (setq prefix (car prefixes))
265                (if (search-forward prefix (+ (point) (length prefix)) t)
266                    (progn 
267                      (setq prefixes nil)
268                      (delete-region (point-min) (point)))
269                  (setq prefixes (cdr prefixes)))))
270            (subst-char-in-region (point-min) (point-max) ?- ?\  t)
271            (capitalize-region (point-min) (point-max))
272            (unless no-suffix 
273              (goto-char (point-max))
274              (insert "..."))
275            (buffer-string)))))
276
277 (defcustom custom-unlispify-tag-names t
278   "Display tag names as words instead of symbols if non nil."
279   :group 'customize
280   :type 'boolean)
281
282 (defun custom-unlispify-tag-name (symbol)
283   "Convert symbol into a menu entry."
284   (let ((custom-unlispify-menu-entries custom-unlispify-tag-names))
285     (custom-unlispify-menu-entry symbol t)))
286
287 (defun custom-prefix-add (symbol prefixes)
288   ;; Addd SYMBOL to list of ignored PREFIXES.
289   (cons (or (get symbol 'custom-prefix)
290             (concat (symbol-name symbol) "-"))
291         prefixes))
292
293 ;;; The Custom Mode.
294
295 (defvar custom-options nil
296   "Customization widgets in the current buffer.")
297
298 (defvar custom-mode-map nil
299   "Keymap for `custom-mode'.")
300   
301 (unless custom-mode-map
302   (setq custom-mode-map (make-sparse-keymap))
303   (set-keymap-parent custom-mode-map widget-keymap)
304   (define-key custom-mode-map "q" 'bury-buffer))
305
306 (easy-menu-define custom-mode-menu 
307     custom-mode-map
308   "Menu used in customization buffers."
309     '("Custom"
310       ["Set" custom-set t]
311       ["Save" custom-save t]
312       ["Reset to Current" custom-reset-current t]
313       ["Reset to Saved" custom-reset-saved t]
314       ["Reset to Factory Settings" custom-reset-factory t]
315       ["Info" (Info-goto-node "(custom)The Customization Buffer") t]))
316
317 (defcustom custom-mode-hook nil
318   "Hook called when entering custom-mode."
319   :type 'hook
320   :group 'customize)
321
322 (defun custom-mode ()
323   "Major mode for editing customization buffers.
324
325 The following commands are available:
326
327 \\[widget-forward]              Move to next button or editable field.
328 \\[widget-backward]             Move to previous button or editable field.
329 \\[widget-button-click]         Activate button under the mouse pointer.
330 \\[widget-button-press]         Activate button under point.
331 \\[custom-set]                  Set all modifications.
332 \\[custom-save]         Make all modifications default.
333 \\[custom-reset-current]        Reset all modified options. 
334 \\[custom-reset-saved]          Reset all modified or set options.
335 \\[custom-reset-factory]        Reset all options.
336
337 Entry to this mode calls the value of `custom-mode-hook'
338 if that value is non-nil."
339   (kill-all-local-variables)
340   (setq major-mode 'custom-mode
341         mode-name "Custom")
342   (use-local-map custom-mode-map)
343   (easy-menu-add custom-mode-menu)
344   (make-local-variable 'custom-options)
345   (run-hooks 'custom-mode-hook))
346
347 ;;; Custom Mode Commands.
348
349 (defun custom-set ()
350   "Set changes in all modified options."
351   (interactive)
352   (let ((children custom-options))
353     (mapcar (lambda (child)
354               (when (eq (widget-get child :custom-state) 'modified)
355                 (widget-apply child :custom-set)))
356             children)))
357
358 (defun custom-save ()
359   "Set all modified group members and save them."
360   (interactive)
361   (let ((children custom-options))
362     (mapcar (lambda (child)
363               (when (memq (widget-get child :custom-state) '(modified set))
364                 (widget-apply child :custom-save)))
365             children))
366   (custom-save-all))
367
368 (defvar custom-reset-menu 
369   '(("Current" . custom-reset-current)
370     ("Saved" . custom-reset-saved)
371     ("Factory Settings" . custom-reset-factory))
372   "Alist of actions for the `Reset' button.
373 The key is a string containing the name of the action, the value is a
374 lisp function taking the widget as an element which will be called
375 when the action is chosen.")
376
377 (defun custom-reset (event)
378   "Select item from reset menu."
379   (let* ((completion-ignore-case t)
380          (answer (widget-choose "Reset to"
381                                 custom-reset-menu
382                                 event)))
383     (if answer
384         (funcall answer))))
385
386 (defun custom-reset-current ()
387   "Reset all modified group members to their current value."
388   (interactive)
389   (let ((children custom-options))
390     (mapcar (lambda (child)
391               (when (eq (widget-get child :custom-state) 'modified)
392                 (widget-apply child :custom-reset-current)))
393             children)))
394
395 (defun custom-reset-saved ()
396   "Reset all modified or set group members to their saved value."
397   (interactive)
398   (let ((children custom-options))
399     (mapcar (lambda (child)
400               (when (eq (widget-get child :custom-state) 'modified)
401                 (widget-apply child :custom-reset-current)))
402             children)))
403
404 (defun custom-reset-factory ()
405   "Reset all modified, set, or saved group members to their factory settings."
406   (interactive)
407   (let ((children custom-options))
408     (mapcar (lambda (child)
409               (when (eq (widget-get child :custom-state) 'modified)
410                 (widget-apply child :custom-reset-current)))
411             children)))
412
413 ;;; The Customize Commands
414
415 ;;;###autoload
416 (defun customize (symbol)
417   "Customize SYMBOL, which must be a customization group."
418   (interactive (list (completing-read "Customize group: (default emacs) "
419                                       obarray 
420                                       (lambda (symbol)
421                                         (get symbol 'custom-group))
422                                       t)))
423
424   (when (stringp symbol)
425     (if (string-equal "" symbol)
426         (setq symbol 'emacs)
427       (setq symbol (intern symbol))))
428   (custom-buffer-create (list (list symbol 'custom-group))))
429
430 ;;;###autoload
431 (defun customize-variable (symbol)
432   "Customize SYMBOL, which must be a variable."
433   (interactive
434    ;; Code stolen from `help.el'.
435    (let ((v (variable-at-point))
436          (enable-recursive-minibuffers t)
437          val)
438      (setq val (completing-read 
439                 (if v
440                     (format "Customize variable (default %s): " v)
441                   "Customize variable: ")
442                 obarray 'boundp t))
443      (list (if (equal val "")
444                v (intern val)))))
445   (custom-buffer-create (list (list symbol 'custom-variable))))
446
447 ;;;###autoload
448 (defun customize-face (&optional symbol)
449   "Customize SYMBOL, which should be a face name or nil.
450 If SYMBOL is nil, customize all faces."
451   (interactive (list (completing-read "Customize face: (default all) " 
452                                       obarray 'custom-facep)))
453   (if (or (null symbol) (and (stringp symbol) (zerop (length symbol))))
454       (let ((found nil))
455         (message "Looking for faces...")
456         (mapcar (lambda (symbol)
457                   (setq found (cons (list symbol 'custom-face) found)))
458                 (face-list))
459         (message "Creating customization buffer...")
460         (custom-buffer-create found))
461     (if (stringp symbol)
462         (setq symbol (intern symbol)))
463     (unless (symbolp symbol)
464       (error "Should be a symbol %S" symbol))
465     (custom-buffer-create (list (list symbol 'custom-face)))))
466
467 ;;;###autoload
468 (defun customize-customized ()
469   "Customize all already customized user options."
470   (interactive)
471   (let ((found nil))
472     (mapatoms (lambda (symbol)
473                 (and (get symbol 'saved-face)
474                      (custom-facep symbol)
475                      (setq found (cons (list symbol 'custom-face) found)))
476                 (and (get symbol 'saved-value)
477                      (boundp symbol)
478                      (setq found
479                            (cons (list symbol 'custom-variable) found)))))
480     (if found 
481         (custom-buffer-create found)
482       (error "No customized user options"))))
483
484 ;;;###autoload
485 (defun customize-apropos (regexp &optional all)
486   "Customize all user options matching REGEXP.
487 If ALL (e.g., started with a prefix key), include options which are not
488 user-settable."
489   (interactive "sCustomize regexp: \nP")
490   (let ((found nil))
491     (mapatoms (lambda (symbol)
492                 (when (string-match regexp (symbol-name symbol))
493                   (when (get symbol 'custom-group)
494                     (setq found (cons (list symbol 'custom-group) found)))
495                   (when (custom-facep symbol)
496                     (setq found (cons (list symbol 'custom-face) found)))
497                   (when (and (boundp symbol)
498                              (or (get symbol 'saved-value)
499                                  (get symbol 'factory-value)
500                                  (if all
501                                      (get symbol 'variable-documentation)
502                                    (user-variable-p symbol))))
503                     (setq found
504                           (cons (list symbol 'custom-variable) found))))))
505     (if found 
506         (custom-buffer-create found)
507       (error "No matches"))))
508
509 ;;;###autoload
510 (defun custom-buffer-create (options)
511   "Create a buffer containing OPTIONS.
512 OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
513 SYMBOL is a customization option, and WIDGET is a widget for editing
514 that option."
515   (kill-buffer (get-buffer-create "*Customization*"))
516   (switch-to-buffer (get-buffer-create "*Customization*"))
517   (custom-mode)
518   (widget-insert "This is a customization buffer.
519 Push RET or click mouse-2 on the word ")
520   (widget-create 'info-link 
521                  :tag "help"
522                  :help-echo "Read the online help."
523                  "(custom)The Customization Buffer")
524   (widget-insert " for more information.\n\n")
525   (setq custom-options 
526         (mapcar (lambda (entry)
527                   (prog1 
528                       (if (> (length options) 1)
529                           (widget-create (nth 1 entry)
530                                          :tag (custom-unlispify-tag-name
531                                                (nth 0 entry))
532                                          :value (nth 0 entry))
533                         ;; If there is only one entry, don't hide it!
534                         (widget-create (nth 1 entry)
535                                        :custom-state 'unknown
536                                        :tag (custom-unlispify-tag-name
537                                                (nth 0 entry))
538                                        :value (nth 0 entry)))
539                     (unless (eq (preceding-char) ?\n)
540                       (widget-insert "\n"))
541                     (widget-insert "\n")))
542                 options))
543   (mapcar 'custom-magic-reset custom-options)
544   (widget-create 'push-button
545                  :tag "Set"
546                  :help-echo "Set all modifications for this session."
547                  :action (lambda (widget &optional event)
548                            (custom-set)))
549   (widget-insert " ")
550   (widget-create 'push-button
551                  :tag "Save"
552                  :help-echo "\
553 Make the modifications default for future sessions."
554                  :action (lambda (widget &optional event)
555                            (custom-save)))
556   (widget-insert " ")
557   (widget-create 'push-button
558                  :tag "Reset"
559                  :help-echo "Undo all modifications."
560                  :action (lambda (widget &optional event)
561                            (custom-reset event)))
562   (widget-insert " ")
563   (widget-create 'push-button
564                  :tag "Done"
565                  :help-echo "Bury the buffer."
566                  :action (lambda (widget &optional event)
567                            (bury-buffer)
568                            ;; Steal button release event.
569                            (if (and (fboundp 'button-press-event-p)
570                                     (fboundp 'next-command-event))
571                                ;; XEmacs
572                                (and event
573                                     (button-press-event-p event)
574                                     (next-command-event))
575                              ;; Emacs
576                              (when (memq 'down (event-modifiers event))
577                                (read-event)))))
578   (widget-insert "\n")
579   (widget-setup)
580   (goto-char (point-min)))
581
582 ;;; Modification of Basic Widgets.
583 ;;
584 ;; We add extra properties to the basic widgets needed here.  This is
585 ;; fine, as long as we are careful to stay within out own namespace.
586 ;;
587 ;; We want simple widgets to be displayed by default, but complex
588 ;; widgets to be hidden.
589
590 (widget-put (get 'item 'widget-type) :custom-show t)
591 (widget-put (get 'editable-field 'widget-type)
592             :custom-show (lambda (widget value)
593                            (let ((pp (pp-to-string value)))
594                              (cond ((string-match "\n" pp)
595                                     nil)
596                                    ((> (length pp) 40)
597                                     nil)
598                                    (t t)))))
599 (widget-put (get 'menu-choice 'widget-type) :custom-show t)
600
601 ;;; The `custom-manual' Widget.
602
603 (define-widget 'custom-manual 'info-link
604   "Link to the manual entry for this customization option."
605   :help-echo "Read the manual entry for this option."
606   :tag "Manual")
607
608 ;;; The `custom-magic' Widget.
609
610 (defface custom-invalid-face '((((class color))
611                                 (:foreground "yellow" :background "red"))
612                                (t
613                                 (:bold t :italic t :underline t)))
614   "Face used when the customize item is invalid.")
615
616 (defface custom-rogue-face '((((class color))
617                               (:foreground "pink" :background "black"))
618                              (t
619                               (:underline t)))
620   "Face used when the customize item is not defined for customization.")
621
622 (defface custom-modified-face '((((class color)) 
623                                  (:foreground "white" :background "blue"))
624                                 (t
625                                  (:italic t :bold)))
626   "Face used when the customize item has been modified.")
627
628 (defface custom-set-face '((((class color)) 
629                                 (:foreground "blue" :background "white"))
630                                (t
631                                 (:italic t)))
632   "Face used when the customize item has been set.")
633
634 (defface custom-changed-face '((((class color)) 
635                                 (:foreground "white" :background "blue"))
636                                (t
637                                 (:italic t)))
638   "Face used when the customize item has been changed.")
639
640 (defface custom-saved-face '((t (:underline t)))
641   "Face used when the customize item has been saved.")
642
643 (defcustom custom-magic-alist '((nil "#" underline "\
644 uninitialized, you should not see this.")
645                                 (unknown "?" italic "\
646 unknown, you should not see this.")
647                                 (hidden "-" default "\
648 hidden, press the state button to show.")
649                                 (invalid "x" custom-invalid-face "\
650 the value displayed for this item is invalid and cannot be set.")
651                                 (modified "*" custom-modified-face "\
652 you have edited the item, and can now set it.")
653                                 (set "+" custom-set-face "\
654 you have set this item, but not saved it.")
655                                 (changed ":" custom-changed-face "\
656 this item has been changed outside customize.")
657                                 (saved "!" custom-saved-face "\
658 this item has been saved.")
659                                 (rogue "@" custom-rogue-face "\
660 this item is not prepared for customization.")
661                                 (factory " " nil "\
662 this item is unchanged from its factory setting."))
663   "Alist of customize option states.
664 Each entry is of the form (STATE MAGIC FACE DESCRIPTION), where 
665
666 STATE is one of the following symbols:
667
668 `nil'
669    For internal use, should never occur.
670 `unknown'
671    For internal use, should never occur.
672 `hidden'
673    This item is not being displayed. 
674 `invalid'
675    This item is modified, but has an invalid form.
676 `modified'
677    This item is modified, and has a valid form.
678 `set'
679    This item has been set but not saved.
680 `changed'
681    The current value of this item has been changed temporarily.
682 `saved'
683    This item is marked for saving.
684 `rogue'
685    This item has no customization information.
686 `factory'
687    This item is unchanged from the factory default.
688
689 MAGIC is a string used to present that state.
690
691 FACE is a face used to present the state.
692
693 DESCRIPTION is a string describing the state.
694
695 The list should be sorted most significant first."
696   :type '(list (checklist :inline t
697                           (group (const nil)
698                                  (string :tag "Magic")
699                                  face 
700                                  (string :tag "Description"))
701                           (group (const unknown)
702                                  (string :tag "Magic")
703                                  face 
704                                  (string :tag "Description"))
705                           (group (const hidden)
706                                  (string :tag "Magic")
707                                  face 
708                                  (string :tag "Description"))
709                           (group (const invalid)
710                                  (string :tag "Magic")
711                                  face 
712                                  (string :tag "Description"))
713                           (group (const modified)
714                                  (string :tag "Magic")
715                                  face 
716                                  (string :tag "Description"))
717                           (group (const set)
718                                  (string :tag "Magic")
719                                  face 
720                                  (string :tag "Description"))
721                           (group (const changed)
722                                  (string :tag "Magic")
723                                  face 
724                                  (string :tag "Description"))
725                           (group (const saved)
726                                  (string :tag "Magic")
727                                  face 
728                                  (string :tag "Description"))
729                           (group (const rogue)
730                                  (string :tag "Magic")
731                                  face 
732                                  (string :tag "Description"))
733                           (group (const factory)
734                                  (string :tag "Magic")
735                                  face 
736                                  (string :tag "Description")))
737                (editable-list :inline t
738                               (group symbol
739                                      (string :tag "Magic")
740                                      face
741                                      (string :tag "Description"))))
742   :group 'customize)
743
744 (defcustom custom-magic-show 'long
745   "Show long description of the state of each customization option."
746   :type '(choice (const :tag "no" nil)
747                  (const short)
748                  (const long))
749   :group 'customize)
750
751 (defcustom custom-magic-show-button t
752   "Show a magic button indicating the state of each customization option."
753   :type 'boolean
754   :group 'customize)
755
756 (define-widget 'custom-magic 'default
757   "Show and manipulate state for a customization option."
758   :format "%v"
759   :action 'widget-choice-item-action
760   :value-get 'ignore
761   :value-create 'custom-magic-value-create
762   :value-delete 'widget-children-value-delete)
763
764 (defun custom-magic-value-create (widget)
765   ;; Create compact status report for WIDGET.
766   (let* ((parent (widget-get widget :parent))
767          (state (widget-get parent :custom-state))
768          (entry (assq state custom-magic-alist))
769          (magic (nth 1 entry))
770          (face (nth 2 entry))
771          (text (nth 3 entry))
772          (lisp (eq (widget-get parent :custom-form) 'lisp))
773          children)
774     (when custom-magic-show
775       (push (widget-create-child-and-convert widget 'choice-item 
776                                              :help-echo "\
777 Change the state of this item."
778                                              :format "%[%t%]"
779                                              :tag "State")
780             children)
781       (insert ": ")
782       (if (eq custom-magic-show 'long)
783           (insert text)
784         (insert (symbol-name state)))
785       (when lisp 
786         (insert " (lisp)"))
787       (insert "\n"))
788     (when custom-magic-show-button
789       (when custom-magic-show
790         (let ((indent (widget-get parent :indent)))
791           (when indent
792             (insert-char ?  indent))))
793       (push (widget-create-child-and-convert widget 'choice-item 
794                                              :button-face face
795                                              :help-echo "Change the state."
796                                              :format "%[%t%]"
797                                              :tag (if lisp 
798                                                       (concat "(" magic ")")
799                                                     (concat "[" magic "]")))
800             children)
801       (insert " "))
802     (widget-put widget :children children)))
803
804 (defun custom-magic-reset (widget)
805   "Redraw the :custom-magic property of WIDGET."
806   (let ((magic (widget-get widget :custom-magic)))
807     (widget-value-set magic (widget-value magic))))
808
809 ;;; The `custom-level' Widget.
810
811 (define-widget 'custom-level 'item
812   "The custom level buttons."
813   :format "%[%t%]"
814   :help-echo "Expand or collapse this item."
815   :action 'custom-level-action)
816
817 (defun custom-level-action (widget &optional event)
818   "Toggle visibility for parent to WIDGET."
819   (let* ((parent (widget-get widget :parent))
820          (state (widget-get parent :custom-state)))
821     (cond ((memq state '(invalid modified))
822            (error "There are unset changes"))
823           ((eq state 'hidden)
824            (widget-put parent :custom-state 'unknown))
825           (t
826            (widget-put parent :custom-state 'hidden)))
827     (custom-redraw parent)))
828
829 ;;; The `custom' Widget.
830
831 (define-widget 'custom 'default
832   "Customize a user option."
833   :convert-widget 'custom-convert-widget
834   :format "%l%[%t%]: %v%m%h%a"
835   :format-handler 'custom-format-handler
836   :notify 'custom-notify
837   :custom-level 1
838   :custom-state 'hidden
839   :documentation-property 'widget-subclass-responsibility
840   :value-create 'widget-subclass-responsibility
841   :value-delete 'widget-children-value-delete
842   :value-get 'widget-item-value-get
843   :validate 'widget-editable-list-validate
844   :match (lambda (widget value) (symbolp value)))
845
846 (defun custom-convert-widget (widget)
847   ;; Initialize :value and :tag from :args in WIDGET.
848   (let ((args (widget-get widget :args)))
849     (when args 
850       (widget-put widget :value (widget-apply widget
851                                               :value-to-internal (car args)))
852       (widget-put widget :tag (custom-unlispify-tag-name (car args)))
853       (widget-put widget :args nil)))
854   widget)
855
856 (defun custom-format-handler (widget escape)
857   ;; We recognize extra escape sequences.
858   (let* ((buttons (widget-get widget :buttons))
859          (state (widget-get widget :custom-state))
860          (level (widget-get widget :custom-level)))
861     (cond ((eq escape ?l)
862            (when level 
863              (push (widget-create-child-and-convert
864                     widget 'custom-level (make-string level ?*))
865                    buttons)
866              (widget-insert " ")
867              (widget-put widget :buttons buttons)))
868           ((eq escape ?L)
869            (when (eq state 'hidden)
870              (widget-insert " ...")))
871           ((eq escape ?m)
872            (and (eq (preceding-char) ?\n)
873                 (widget-get widget :indent)
874                 (insert-char ?  (widget-get widget :indent)))
875            (let ((magic (widget-create-child-and-convert
876                          widget 'custom-magic nil)))
877              (widget-put widget :custom-magic magic)
878              (push magic buttons)
879              (widget-put widget :buttons buttons)))
880           ((eq escape ?a)
881            (let* ((symbol (widget-get widget :value))
882                   (links (get symbol 'custom-links))
883                   (many (> (length links) 2)))
884              (when links
885                (and (eq (preceding-char) ?\n)
886                     (widget-get widget :indent)
887                     (insert-char ?  (widget-get widget :indent)))
888                (insert "See also ")
889                (while links
890                  (push (widget-create-child-and-convert widget (car links))
891                        buttons)
892                  (setq links (cdr links))
893                  (cond ((null links)
894                         (insert ".\n"))
895                        ((null (cdr links))
896                         (if many
897                             (insert ", and ")
898                           (insert " and ")))
899                        (t 
900                         (insert ", "))))
901                (widget-put widget :buttons buttons))))
902           (t 
903            (widget-default-format-handler widget escape)))))
904
905 (defun custom-notify (widget &rest args)
906   "Keep track of changes."
907   (unless (memq (widget-get widget :custom-state) '(nil unknown hidden))
908     (widget-put widget :custom-state 'modified))
909   (let ((buffer-undo-list t))
910     (custom-magic-reset widget))
911   (apply 'widget-default-notify widget args))
912
913 (defun custom-redraw (widget)
914   "Redraw WIDGET with current settings."
915   (let ((pos (point))
916         (from (marker-position (widget-get widget :from)))
917         (to (marker-position (widget-get widget :to))))
918     (save-excursion
919       (widget-value-set widget (widget-value widget))
920       (custom-redraw-magic widget))
921     (when (and (>= pos from) (<= pos to))
922       (goto-char pos))))
923
924 (defun custom-redraw-magic (widget)
925   "Redraw WIDGET state with current settings."
926   (while widget 
927     (let ((magic (widget-get widget :custom-magic)))
928       (unless magic 
929         (debug))
930       (widget-value-set magic (widget-value magic))
931       (when (setq widget (widget-get widget :group))
932         (custom-group-state-update widget))))
933   (widget-setup))
934
935 (defun custom-show (widget value)
936   "Non-nil if WIDGET should be shown with VALUE by default."
937   (let ((show (widget-get widget :custom-show)))
938     (cond ((null show)
939            nil)
940           ((eq t show)
941            t)
942           (t
943            (funcall show widget value)))))
944
945 (defun custom-load-symbol (symbol)
946   "Load all dependencies for SYMBOL."
947   (let ((loads (get symbol 'custom-loads))
948         load)
949     (while loads
950       (setq load (car loads)
951             loads (cdr loads))
952       (cond ((symbolp load)
953              (condition-case nil
954                  (require load)
955                (error nil)))
956             ((assoc load load-history))
957             (t
958              (condition-case nil
959                  (load-library load)
960                (error nil)))))))
961
962 (defun custom-load-widget (widget)
963   "Load all dependencies for WIDGET."
964   (custom-load-symbol (widget-value widget)))
965
966 ;;; The `custom-variable' Widget.
967
968 (defface custom-variable-sample-face '((t (:underline t)))
969   "Face used for unpushable variable tags."
970   :group 'customize)
971
972 (defface custom-variable-button-face '((t (:underline t :bold t)))
973   "Face used for pushable variable tags."
974   :group 'customize)
975
976 (define-widget 'custom-variable 'custom
977   "Customize variable."
978   :format "%l%v%m%h%a"
979   :help-echo "Set or reset this variable."
980   :documentation-property 'variable-documentation
981   :custom-state nil
982   :custom-menu 'custom-variable-menu-create
983   :custom-form 'edit
984   :value-create 'custom-variable-value-create
985   :action 'custom-variable-action
986   :custom-set 'custom-variable-set
987   :custom-save 'custom-variable-save
988   :custom-reset-current 'custom-redraw
989   :custom-reset-saved 'custom-variable-reset-saved
990   :custom-reset-factory 'custom-variable-reset-factory)
991
992 (defun custom-variable-value-create (widget)
993   "Here is where you edit the variables value."
994   (custom-load-widget widget)
995   (let* ((buttons (widget-get widget :buttons))
996          (children (widget-get widget :children))
997          (form (widget-get widget :custom-form))
998          (state (widget-get widget :custom-state))
999          (symbol (widget-get widget :value))
1000          (options (get symbol 'custom-options))
1001          (child-type (or (get symbol 'custom-type) 'sexp))
1002          (tag (widget-get widget :tag))
1003          (type (let ((tmp (if (listp child-type)
1004                               (copy-list child-type)
1005                             (list child-type))))
1006                  (when options
1007                    (widget-put tmp :options options))
1008                  tmp))
1009          (conv (widget-convert type))
1010          (value (if (default-boundp symbol)
1011                     (default-value symbol)
1012                   (widget-get conv :value))))
1013     ;; If the widget is new, the child determine whether it is hidden.
1014     (cond (state)
1015           ((custom-show type value)
1016            (setq state 'unknown))
1017           (t
1018            (setq state 'hidden)))
1019     ;; If we don't know the state, see if we need to edit it in lisp form.
1020     (when (eq state 'unknown)
1021       (unless (widget-apply conv :match value)
1022         ;; (widget-apply (widget-convert type) :match value)
1023         (setq form 'lisp)))
1024     ;; Now we can create the child widget.
1025     (cond ((eq state 'hidden)
1026            ;; Indicate hidden value.
1027            (push (widget-create-child-and-convert 
1028                   widget 'item
1029                   :format "%{%t%}: ..."
1030                   :sample-face 'custom-variable-sample-face
1031                   :tag tag
1032                   :parent widget)
1033                  children))
1034           ((eq form 'lisp)
1035            ;; In lisp mode edit the saved value when possible.
1036            (let* ((value (cond ((get symbol 'saved-value)
1037                                 (car (get symbol 'saved-value)))
1038                                ((get symbol 'factory-value)
1039                                 (car (get symbol 'factory-value)))
1040                                ((default-boundp symbol)
1041                                 (custom-quote (default-value symbol)))
1042                                (t
1043                                 (custom-quote (widget-get conv :value))))))
1044              (push (widget-create-child-and-convert 
1045                     widget 'sexp 
1046                     :button-face 'custom-variable-button-face
1047                     :tag (symbol-name symbol)
1048                     :parent widget
1049                     :value value)
1050                    children)))
1051           (t
1052            ;; Edit mode.
1053            (push (widget-create-child-and-convert
1054                   widget type 
1055                   :tag tag
1056                   :button-face 'custom-variable-button-face
1057                   :sample-face 'custom-variable-sample-face
1058                   :value value)
1059                  children)))
1060     ;; Now update the state.
1061     (unless (eq (preceding-char) ?\n)
1062       (widget-insert "\n"))
1063     (if (eq state 'hidden)
1064         (widget-put widget :custom-state state)
1065       (custom-variable-state-set widget))
1066     (widget-put widget :custom-form form)            
1067     (widget-put widget :buttons buttons)
1068     (widget-put widget :children children)))
1069
1070 (defun custom-variable-state-set (widget)
1071   "Set the state of WIDGET."
1072   (let* ((symbol (widget-value widget))
1073          (value (if (default-boundp symbol)
1074                     (default-value symbol)
1075                   (widget-get widget :value)))
1076          tmp
1077          (state (cond ((setq tmp (get symbol 'customized-value))
1078                        (if (condition-case nil
1079                                (equal value (eval (car tmp)))
1080                              (error nil))
1081                            'set
1082                          'changed))
1083                       ((setq tmp (get symbol 'saved-value))
1084                        (if (condition-case nil
1085                                (equal value (eval (car tmp)))
1086                              (error nil))
1087                            'saved
1088                          'changed))
1089                       ((setq tmp (get symbol 'factory-value))
1090                        (if (condition-case nil
1091                                (equal value (eval (car tmp)))
1092                              (error nil))
1093                            'factory
1094                          'changed))
1095                       (t 'rogue))))
1096     (widget-put widget :custom-state state)))
1097
1098 (defvar custom-variable-menu 
1099   '(("Edit" . custom-variable-edit)
1100     ("Edit Lisp" . custom-variable-edit-lisp)
1101     ("Set" . custom-variable-set)
1102     ("Save" . custom-variable-save)
1103     ("Reset to Current" . custom-redraw)
1104     ("Reset to Saved" . custom-variable-reset-saved)
1105     ("Reset to Factory Settings" . custom-variable-reset-factory))
1106   "Alist of actions for the `custom-variable' widget.
1107 The key is a string containing the name of the action, the value is a
1108 lisp function taking the widget as an element which will be called
1109 when the action is chosen.")
1110
1111 (defun custom-variable-action (widget &optional event)
1112   "Show the menu for `custom-variable' WIDGET.
1113 Optional EVENT is the location for the menu."
1114   (if (eq (widget-get widget :custom-state) 'hidden)
1115       (progn 
1116         (widget-put widget :custom-state 'unknown)
1117         (custom-redraw widget))
1118     (let* ((completion-ignore-case t)
1119            (answer (widget-choose (custom-unlispify-tag-name
1120                                    (widget-get widget :value))
1121                                   custom-variable-menu
1122                                   event)))
1123       (if answer
1124           (funcall answer widget)))))
1125
1126 (defun custom-variable-edit (widget)
1127   "Edit value of WIDGET."
1128   (widget-put widget :custom-state 'unknown)
1129   (widget-put widget :custom-form 'edit)
1130   (custom-redraw widget))
1131
1132 (defun custom-variable-edit-lisp (widget)
1133   "Edit the lisp representation of the value of WIDGET."
1134   (widget-put widget :custom-state 'unknown)
1135   (widget-put widget :custom-form 'lisp)
1136   (custom-redraw widget))
1137
1138 (defun custom-variable-set (widget)
1139   "Set the current value for the variable being edited by WIDGET."
1140   (let ((form (widget-get widget :custom-form))
1141         (state (widget-get widget :custom-state))
1142         (child (car (widget-get widget :children)))
1143         (symbol (widget-value widget))
1144         val)
1145     (cond ((eq state 'hidden)
1146            (error "Cannot set hidden variable."))
1147           ((setq val (widget-apply child :validate))
1148            (goto-char (widget-get val :from))
1149            (error "%s" (widget-get val :error)))
1150           ((eq form 'lisp)
1151            (set symbol (eval (setq val (widget-value child))))
1152            (put symbol 'customized-value (list val)))
1153           (t
1154            (set symbol (setq val (widget-value child)))
1155            (put symbol 'customized-value (list (custom-quote val)))))
1156     (custom-variable-state-set widget)
1157     (custom-redraw-magic widget)))
1158
1159 (defun custom-variable-save (widget)
1160   "Set the default value for the variable being edited by WIDGET."
1161   (let ((form (widget-get widget :custom-form))
1162         (state (widget-get widget :custom-state))
1163         (child (car (widget-get widget :children)))
1164         (symbol (widget-value widget))
1165         val)
1166     (cond ((eq state 'hidden)
1167            (error "Cannot set hidden variable."))
1168           ((setq val (widget-apply child :validate))
1169            (goto-char (widget-get val :from))
1170            (error "%s" (widget-get val :error)))
1171           ((eq form 'lisp)
1172            (put symbol 'saved-value (list (widget-value child)))
1173            (set symbol (eval (widget-value child))))
1174           (t
1175            (put symbol
1176                 'saved-value (list (custom-quote (widget-value
1177                                                   child))))
1178            (set symbol (widget-value child))))
1179     (put symbol 'customized-value nil)
1180     (custom-save-all)
1181     (custom-variable-state-set widget)
1182     (custom-redraw-magic widget)))
1183
1184 (defun custom-variable-reset-saved (widget)
1185   "Restore the saved value for the variable being edited by WIDGET."
1186   (let ((symbol (widget-value widget)))
1187     (if (get symbol 'saved-value)
1188         (condition-case nil
1189             (set symbol (eval (car (get symbol 'saved-value))))
1190           (error nil))
1191       (error "No saved value for %s" symbol))
1192     (put symbol 'customized-value nil)
1193     (widget-put widget :custom-state 'unknown)
1194     (custom-redraw widget)))
1195
1196 (defun custom-variable-reset-factory (widget)
1197   "Restore the factory setting for the variable being edited by WIDGET."
1198   (let ((symbol (widget-value widget)))
1199     (if (get symbol 'factory-value)
1200         (set symbol (eval (car (get symbol 'factory-value))))
1201       (error "No factory default for %S" symbol))
1202     (put symbol 'customized-value nil)
1203     (when (get symbol 'saved-value)
1204       (put symbol 'saved-value nil)
1205       (custom-save-all))
1206     (widget-put widget :custom-state 'unknown)
1207     (custom-redraw widget)))
1208
1209 ;;; The `custom-face-edit' Widget.
1210
1211 (define-widget 'custom-face-edit 'checklist
1212   "Edit face attributes."
1213   :format "%t: %v"
1214   :tag "Attributes"
1215   :extra-offset 12
1216   :button-args '(:help-echo "Control whether this attribute have any effect.")
1217   :args (mapcar (lambda (att)
1218                   (list 'group 
1219                         :inline t
1220                         :sibling-args (widget-get (nth 1 att) :sibling-args)
1221                         (list 'const :format "" :value (nth 0 att)) 
1222                         (nth 1 att)))
1223                 custom-face-attributes))
1224
1225 ;;; The `custom-display' Widget.
1226
1227 (define-widget 'custom-display 'menu-choice
1228   "Select a display type."
1229   :tag "Display"
1230   :value t
1231   :help-echo "Specify frames where the face attributes should be used."
1232   :args '((const :tag "all" t)
1233           (checklist
1234            :offset 0
1235            :extra-offset 9
1236            :args ((group :sibling-args (:help-echo "\
1237 Only match the specified window systems.")
1238                          (const :format "Type: "
1239                                 type)
1240                          (checklist :inline t
1241                                     :offset 0
1242                                     (const :format "X "
1243                                            :sibling-args (:help-echo "\
1244 The X11 Window System.")
1245                                            x)
1246                                     (const :format "PM "
1247                                            :sibling-args (:help-echo "\
1248 OS/2 Presentation Manager.")
1249                                            pm)
1250                                     (const :format "Win32 "
1251                                            :sibling-args (:help-echo "\
1252 Windows NT/95/97.")
1253                                            win32)
1254                                     (const :format "DOS "
1255                                            :sibling-args (:help-echo "\
1256 Plain MS-DOS.")
1257                                            pc)
1258                                     (const :format "TTY%n"
1259                                            :sibling-args (:help-echo "\
1260 Plain text terminals.")
1261                                            tty)))
1262                   (group :sibling-args (:help-echo "\
1263 Only match the frames with the specified color support.")
1264                          (const :format "Class: "
1265                                 class)
1266                          (checklist :inline t
1267                                     :offset 0
1268                                     (const :format "Color "
1269                                            :sibling-args (:help-echo "\
1270 Match color frames.")
1271                                            color)
1272                                     (const :format "Grayscale "
1273                                            :sibling-args (:help-echo "\
1274 Match grayscale frames.")
1275                                            grayscale)
1276                                     (const :format "Monochrome%n"
1277                                            :sibling-args (:help-echo "\
1278 Match frames with no color support.")
1279                                            mono)))
1280                   (group :sibling-args (:help-echo "\
1281 Only match frames with the specified intensity.")
1282                          (const :format "\
1283 Background brightness: "
1284                                 background)
1285                          (checklist :inline t
1286                                     :offset 0
1287                                     (const :format "Light "
1288                                            :sibling-args (:help-echo "\
1289 Match frames with light backgrounds.")
1290                                            light)
1291                                     (const :format "Dark\n"
1292                                            :sibling-args (:help-echo "\
1293 Match frames with dark backgrounds.")
1294                                            dark)))))))
1295
1296 ;;; The `custom-face' Widget.
1297
1298 (defface custom-face-tag-face '((t (:underline t)))
1299   "Face used for face tags."
1300   :group 'customize)
1301
1302 (define-widget 'custom-face 'custom
1303   "Customize face."
1304   :format "%l%{%t%}: %s%m%h%a%v"
1305   :format-handler 'custom-face-format-handler
1306   :sample-face 'custom-face-tag-face
1307   :help-echo "Set or reset this face."
1308   :documentation-property '(lambda (face)
1309                              (face-doc-string face))
1310   :value-create 'custom-face-value-create
1311   :action 'custom-face-action
1312   :custom-set 'custom-face-set
1313   :custom-save 'custom-face-save
1314   :custom-reset-current 'custom-redraw
1315   :custom-reset-saved 'custom-face-reset-saved
1316   :custom-reset-factory 'custom-face-reset-factory
1317   :custom-menu 'custom-face-menu-create)
1318
1319 (defun custom-face-format-handler (widget escape)
1320   ;; We recognize extra escape sequences.
1321   (let (child
1322         (symbol (widget-get widget :value)))
1323     (cond ((eq escape ?s)
1324            (and (string-match "XEmacs" emacs-version)
1325                 ;; XEmacs cannot display initialized faces.
1326                 (not (custom-facep symbol))
1327                 (copy-face 'custom-face-empty symbol))
1328            (setq child (widget-create-child-and-convert 
1329                         widget 'item
1330                         :format "(%{%t%})\n"
1331                         :sample-face symbol
1332                         :tag "sample")))
1333           (t 
1334            (custom-format-handler widget escape)))
1335     (when child
1336       (widget-put widget
1337                   :buttons (cons child (widget-get widget :buttons))))))
1338
1339 (defun custom-face-value-create (widget)
1340   ;; Create a list of the display specifications.
1341   (unless (eq (preceding-char) ?\n)
1342     (insert "\n"))
1343   (when (not (eq (widget-get widget :custom-state) 'hidden))
1344     (custom-load-widget widget)
1345     (let* ((symbol (widget-value widget))
1346            (edit (widget-create-child-and-convert
1347                   widget 'editable-list
1348                   :entry-format "%i %d %v"
1349                   :value (or (get symbol 'saved-face)
1350                              (get symbol 'factory-face))
1351                   :insert-button-args '(:help-echo "\
1352 Insert new display specification here.")
1353                   :append-button-args '(:help-echo "\
1354 Append new display specification here.")
1355                   :delete-button-args '(:help-echo "\
1356 Delete this display specification.")
1357                   '(group :format "%v"
1358                           custom-display custom-face-edit))))
1359       (custom-face-state-set widget)
1360       (widget-put widget :children (list edit)))))
1361
1362 (defvar custom-face-menu 
1363   '(("Set" . custom-face-set)
1364     ("Save" . custom-face-save)
1365     ("Reset to Saved" . custom-face-reset-saved)
1366     ("Reset to Factory Setting" . custom-face-reset-factory))
1367   "Alist of actions for the `custom-face' widget.
1368 The key is a string containing the name of the action, the value is a
1369 lisp function taking the widget as an element which will be called
1370 when the action is chosen.")
1371
1372 (defun custom-face-state-set (widget)
1373   "Set the state of WIDGET."
1374   (let ((symbol (widget-value widget)))
1375     (widget-put widget :custom-state (cond ((get symbol 'customized-face)
1376                                             'set)
1377                                            ((get symbol 'saved-face)
1378                                             'saved)
1379                                            ((get symbol 'factory-face)
1380                                             'factory)
1381                                            (t 
1382                                             'rogue)))))
1383
1384 (defun custom-face-action (widget &optional event)
1385   "Show the menu for `custom-face' WIDGET.
1386 Optional EVENT is the location for the menu."
1387   (if (eq (widget-get widget :custom-state) 'hidden)
1388       (progn 
1389         (widget-put widget :custom-state 'unknown)
1390         (custom-redraw widget))
1391     (let* ((completion-ignore-case t)
1392            (symbol (widget-get widget :value))
1393            (answer (widget-choose (custom-unlispify-tag-name symbol)
1394                                   custom-face-menu event)))
1395       (if answer
1396           (funcall answer widget)))))
1397
1398 (defun custom-face-set (widget)
1399   "Make the face attributes in WIDGET take effect."
1400   (let* ((symbol (widget-value widget))
1401          (child (car (widget-get widget :children)))
1402          (value (widget-value child)))
1403     (put symbol 'customized-face value)
1404     (when (fboundp 'copy-face)
1405       (copy-face 'custom-face-empty symbol))
1406     (custom-face-display-set symbol value)
1407     (custom-face-state-set widget)
1408     (custom-redraw-magic widget)))
1409
1410 (defun custom-face-save (widget)
1411   "Make the face attributes in WIDGET default."
1412   (let* ((symbol (widget-value widget))
1413          (child (car (widget-get widget :children)))
1414          (value (widget-value child)))
1415     (when (fboundp 'copy-face)
1416       (copy-face 'custom-face-empty symbol))
1417     (custom-face-display-set symbol value)
1418     (put symbol 'saved-face value)
1419     (put symbol 'customized-face nil)
1420     (custom-face-state-set widget)
1421     (custom-redraw-magic widget)))
1422
1423 (defun custom-face-reset-saved (widget)
1424   "Restore WIDGET to the face's default attributes."
1425   (let* ((symbol (widget-value widget))
1426          (child (car (widget-get widget :children)))
1427          (value (get symbol 'saved-face)))
1428     (unless value
1429       (error "No saved value for this face"))
1430     (put symbol 'customized-face nil)
1431     (when (fboundp 'copy-face)
1432       (copy-face 'custom-face-empty symbol))
1433     (custom-face-display-set symbol value)
1434     (widget-value-set child value)
1435     (custom-face-state-set widget)
1436     (custom-redraw-magic widget)))
1437
1438 (defun custom-face-reset-factory (widget)
1439   "Restore WIDGET to the face's factory settings."
1440   (let* ((symbol (widget-value widget))
1441          (child (car (widget-get widget :children)))
1442          (value (get symbol 'factory-face)))
1443     (unless value
1444       (error "No factory default for this face"))
1445     (put symbol 'customized-face nil)
1446     (when (get symbol 'saved-face)
1447       (put symbol 'saved-face nil)
1448       (custom-save-all))
1449     (when (fboundp 'copy-face)
1450       (copy-face 'custom-face-empty symbol))
1451     (custom-face-display-set symbol value)
1452     (widget-value-set child value)
1453     (custom-face-state-set widget)
1454     (custom-redraw-magic widget)))
1455
1456 ;;; The `face' Widget.
1457
1458 (define-widget 'face 'default
1459   "Select and customize a face."
1460   :convert-widget 'widget-item-convert-widget
1461   :format "%[%t%]: %v"
1462   :tag "Face"
1463   :value 'default
1464   :value-create 'widget-face-value-create
1465   :value-delete 'widget-face-value-delete
1466   :value-get 'widget-item-value-get
1467   :validate 'widget-editable-list-validate
1468   :action 'widget-face-action
1469   :match '(lambda (widget value) (symbolp value)))
1470
1471 (defun widget-face-value-create (widget)
1472   ;; Create a `custom-face' child.
1473   (let* ((symbol (widget-value widget))
1474          (child (widget-create-child-and-convert
1475                  widget 'custom-face
1476                  :format "%t %s%m%h%v"
1477                  :custom-level nil
1478                  :value symbol)))
1479     (custom-magic-reset child)
1480     (setq custom-options (cons child custom-options))
1481     (widget-put widget :children (list child))))
1482
1483 (defun widget-face-value-delete (widget)
1484   ;; Remove the child from the options.
1485   (let ((child (car (widget-get widget :children))))
1486     (setq custom-options (delq child custom-options))
1487     (widget-children-value-delete widget)))
1488
1489 (defvar face-history nil
1490   "History of entered face names.")
1491
1492 (defun widget-face-action (widget &optional event)
1493   "Prompt for a face."
1494   (let ((answer (completing-read "Face: "
1495                                  (mapcar (lambda (face)
1496                                            (list (symbol-name face)))
1497                                          (face-list))
1498                                  nil nil nil                             
1499                                  'face-history)))
1500     (unless (zerop (length answer))
1501       (widget-value-set widget (intern answer))
1502       (widget-apply widget :notify widget event)
1503       (widget-setup))))
1504
1505 ;;; The `hook' Widget.
1506
1507 (define-widget 'hook 'list
1508   "A emacs lisp hook"
1509   :convert-widget 'custom-hook-convert-widget
1510   :tag "Hook")
1511
1512 (defun custom-hook-convert-widget (widget)
1513   ;; Handle `:custom-options'.
1514   (let* ((options (widget-get widget :options))
1515          (other `(editable-list :inline t 
1516                                 :entry-format "%i %d%v"
1517                                 (function :format " %v")))
1518          (args (if options
1519                    (list `(checklist :inline t
1520                                      ,@(mapcar (lambda (entry)
1521                                                  `(function-item ,entry))
1522                                                options))
1523                          other)
1524                  (list other))))
1525     (widget-put widget :args args)
1526     widget))
1527
1528 ;;; The `custom-group' Widget.
1529
1530 (defcustom custom-group-tag-faces '(custom-group-tag-face-1)
1531   ;; In XEmacs, this ought to play games with font size.
1532   "Face used for group tags.
1533 The first member is used for level 1 groups, the second for level 2,
1534 and so forth.  The remaining group tags are shown with
1535 `custom-group-tag-face'."
1536   :type '(repeat face)
1537   :group 'customize)
1538
1539 (defface custom-group-tag-face-1 '((((class color)
1540                                    (background dark))
1541                                   (:foreground "pink" :underline t))
1542                                  (((class color)
1543                                    (background light))
1544                                   (:foreground "red" :underline t))
1545                                  (t (:underline t)))
1546   "Face used for group tags.")
1547
1548 (defface custom-group-tag-face '((((class color)
1549                                    (background dark))
1550                                   (:foreground "light blue" :underline t))
1551                                  (((class color)
1552                                    (background light))
1553                                   (:foreground "blue" :underline t))
1554                                  (t (:underline t)))
1555   "Face used for low level group tags."
1556   :group 'customize)
1557
1558 (define-widget 'custom-group 'custom
1559   "Customize group."
1560   :format "%l%{%t%}:%L\n%m%h%a%v"
1561   :sample-face-get 'custom-group-sample-face-get
1562   :documentation-property 'group-documentation
1563   :help-echo "Set or reset all members of this group."
1564   :value-create 'custom-group-value-create
1565   :action 'custom-group-action
1566   :custom-set 'custom-group-set
1567   :custom-save 'custom-group-save
1568   :custom-reset-current 'custom-group-reset-current
1569   :custom-reset-saved 'custom-group-reset-saved
1570   :custom-reset-factory 'custom-group-reset-factory
1571   :custom-menu 'custom-group-menu-create)
1572
1573 (defun custom-group-sample-face-get (widget)
1574   ;; Use :sample-face.
1575   (or (nth (1- (widget-get widget :custom-level)) custom-group-tag-faces)
1576       'custom-group-tag-face))
1577
1578 (defun custom-group-value-create (widget)
1579   (let ((state (widget-get widget :custom-state)))
1580     (unless (eq state 'hidden)
1581       (custom-load-widget widget)
1582       (let* ((level (widget-get widget :custom-level))
1583              (symbol (widget-value widget))
1584              (members (get symbol 'custom-group))
1585              (prefixes (widget-get widget :custom-prefixes))
1586              (custom-prefix-list (custom-prefix-add symbol prefixes))
1587              (children (mapcar (lambda (entry)
1588                                  (widget-insert "\n")
1589                                  (prog1
1590                                      (widget-create-child-and-convert
1591                                       widget (nth 1 entry)
1592                                       :group widget
1593                                       :tag (custom-unlispify-tag-name
1594                                             (nth 0 entry))
1595                                       :custom-prefixes custom-prefix-list
1596                                       :custom-level (1+ level)
1597                                       :value (nth 0 entry))
1598                                    (unless (eq (preceding-char) ?\n)
1599                                      (widget-insert "\n"))))
1600                                members)))
1601         (mapcar 'custom-magic-reset children)
1602         (widget-put widget :children children)
1603         (custom-group-state-update widget)))))
1604
1605 (defvar custom-group-menu 
1606   '(("Set" . custom-group-set)
1607     ("Save" . custom-group-save)
1608     ("Reset to Current" . custom-group-reset-current)
1609     ("Reset to Saved" . custom-group-reset-saved)
1610     ("Reset to Factory" . custom-group-reset-factory))
1611   "Alist of actions for the `custom-group' widget.
1612 The key is a string containing the name of the action, the value is a
1613 lisp function taking the widget as an element which will be called
1614 when the action is chosen.")
1615
1616 (defun custom-group-action (widget &optional event)
1617   "Show the menu for `custom-group' WIDGET.
1618 Optional EVENT is the location for the menu."
1619   (if (eq (widget-get widget :custom-state) 'hidden)
1620       (progn 
1621         (widget-put widget :custom-state 'unknown)
1622         (custom-redraw widget))
1623     (let* ((completion-ignore-case t)
1624            (answer (widget-choose (custom-unlispify-tag-name
1625                                    (widget-get widget :value))
1626                                   custom-group-menu
1627                                   event)))
1628       (if answer
1629           (funcall answer widget)))))
1630
1631 (defun custom-group-set (widget)
1632   "Set changes in all modified group members."
1633   (let ((children (widget-get widget :children)))
1634     (mapcar (lambda (child)
1635               (when (eq (widget-get child :custom-state) 'modified)
1636                 (widget-apply child :custom-set)))
1637             children )))
1638
1639 (defun custom-group-save (widget)
1640   "Save all modified group members."
1641   (let ((children (widget-get widget :children)))
1642     (mapcar (lambda (child)
1643               (when (memq (widget-get child :custom-state) '(modified set))
1644                 (widget-apply child :custom-save)))
1645             children )))
1646
1647 (defun custom-group-reset-current (widget)
1648   "Reset all modified group members."
1649   (let ((children (widget-get widget :children)))
1650     (mapcar (lambda (child)
1651               (when (eq (widget-get child :custom-state) 'modified)
1652                 (widget-apply child :custom-reset-current)))
1653             children )))
1654
1655 (defun custom-group-reset-saved (widget)
1656   "Reset all modified or set group members."
1657   (let ((children (widget-get widget :children)))
1658     (mapcar (lambda (child)
1659               (when (memq (widget-get child :custom-state) '(modified set))
1660                 (widget-apply child :custom-reset-saved)))
1661             children )))
1662
1663 (defun custom-group-reset-factory (widget)
1664   "Reset all modified, set, or saved group members."
1665   (let ((children (widget-get widget :children)))
1666     (mapcar (lambda (child)
1667               (when (memq (widget-get child :custom-state)
1668                           '(modified set saved))
1669                 (widget-apply child :custom-reset-factory)))
1670             children )))
1671
1672 (defun custom-group-state-update (widget)
1673   "Update magic."
1674   (unless (eq (widget-get widget :custom-state) 'hidden)
1675     (let* ((children (widget-get widget :children))
1676            (states (mapcar (lambda (child)
1677                              (widget-get child :custom-state))
1678                            children))
1679            (magics custom-magic-alist)
1680            (found 'factory))
1681       (while magics
1682         (let ((magic (car (car magics))))
1683           (if (and (not (eq magic 'hidden))
1684                    (memq magic states))
1685               (setq found magic
1686                     magics nil)
1687             (setq magics (cdr magics)))))
1688       (widget-put widget :custom-state found)))
1689   (custom-magic-reset widget))
1690
1691 ;;; The `custom-save-all' Function.
1692
1693 (defcustom custom-file "~/.emacs"
1694   "File used for storing customization information.
1695 If you change this from the default \"~/.emacs\" you need to
1696 explicitly load that file for the settings to take effect."
1697   :type 'file
1698   :group 'customize)
1699
1700 (defun custom-save-delete (symbol)
1701   "Delete the call to SYMBOL form `custom-file'.
1702 Leave point at the location of the call, or after the last expression."
1703   (set-buffer (find-file-noselect custom-file))
1704   (goto-char (point-min))
1705   (catch 'found
1706     (while t
1707       (let ((sexp (condition-case nil
1708                       (read (current-buffer))
1709                     (end-of-file (throw 'found nil)))))
1710         (when (and (listp sexp)
1711                    (eq (car sexp) symbol))
1712           (delete-region (save-excursion
1713                            (backward-sexp)
1714                            (point))
1715                          (point))
1716           (throw 'found nil))))))
1717
1718 (defun custom-save-variables ()
1719   "Save all customized variables in `custom-file'."
1720   (save-excursion
1721     (custom-save-delete 'custom-set-variables)
1722     (let ((standard-output (current-buffer)))
1723       (unless (bolp)
1724         (princ "\n"))
1725       (princ "(custom-set-variables")
1726       (mapatoms (lambda (symbol)
1727                   (let ((value (get symbol 'saved-value)))
1728                     (when value
1729                       (princ "\n '(")
1730                       (princ symbol)
1731                       (princ " ")
1732                       (prin1 (car value))
1733                       (if (or (get symbol 'factory-value)
1734                               (and (not (boundp symbol))
1735                                    (not (get symbol 'force-value))))
1736                           (princ ")")
1737                         (princ " t)"))))))
1738       (princ ")")
1739       (unless (eolp)
1740         (princ "\n")))))
1741
1742 (defun custom-save-faces ()
1743   "Save all customized faces in `custom-file'."
1744   (save-excursion
1745     (custom-save-delete 'custom-set-faces)
1746     (let ((standard-output (current-buffer)))
1747       (unless (bolp)
1748         (princ "\n"))
1749       (princ "(custom-set-faces")
1750       (mapatoms (lambda (symbol)
1751                   (let ((value (get symbol 'saved-face)))
1752                     (when value
1753                       (princ "\n '(")
1754                       (princ symbol)
1755                       (princ " ")
1756                       (prin1 value)
1757                       (if (or (get symbol 'factory-face)
1758                               (and (not (custom-facep symbol))
1759                                    (not (get symbol 'force-face))))
1760                           (princ ")")
1761                         (princ " t)"))))))
1762       (princ ")")
1763       (unless (eolp)
1764         (princ "\n")))))
1765
1766 (defun custom-save-all ()
1767   "Save all customizations in `custom-file'."
1768   (custom-save-variables)
1769   (custom-save-faces)
1770   (save-excursion
1771     (set-buffer (find-file-noselect custom-file))
1772     (save-buffer)))
1773
1774 ;;; The Customize Menu.
1775
1776 (defcustom custom-menu-nesting 2
1777   "Maximum nesting in custom menus."
1778   :type 'integer
1779   :group 'customize)
1780
1781 (defun custom-face-menu-create (widget symbol)
1782   "Ignoring WIDGET, create a menu entry for customization face SYMBOL."
1783   (vector (custom-unlispify-menu-entry symbol)
1784           `(custom-buffer-create '((,symbol custom-face)))
1785           t))
1786
1787 (defun custom-variable-menu-create (widget symbol)
1788   "Ignoring WIDGET, create a menu entry for customization variable SYMBOL."
1789   (let ((type (get symbol 'custom-type)))
1790     (unless (listp type)
1791       (setq type (list type)))
1792     (if (and type (widget-get type :custom-menu))
1793         (widget-apply type :custom-menu symbol)
1794       (vector (custom-unlispify-menu-entry symbol)
1795               `(custom-buffer-create '((,symbol custom-variable)))
1796               t))))
1797
1798 (widget-put (get 'boolean 'widget-type)
1799             :custom-menu (lambda (widget symbol)
1800                            (vector (custom-unlispify-menu-entry symbol)
1801                                    `(custom-buffer-create
1802                                      '((,symbol custom-variable)))
1803                                    ':style 'toggle
1804                                    ':selected symbol)))
1805
1806 (if (string-match "XEmacs" emacs-version)
1807     ;; XEmacs can create menus dynamically.
1808     (defun custom-group-menu-create (widget symbol)
1809       "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
1810       `( ,(custom-unlispify-menu-entry symbol t)
1811          :filter (lambda (&rest junk)
1812                    (cdr (custom-menu-create ',symbol)))))
1813   ;; But emacs can't.
1814   (defun custom-group-menu-create (widget symbol)
1815     "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
1816     ;; Limit the nesting.
1817     (let ((custom-menu-nesting (1- custom-menu-nesting)))
1818       (custom-menu-create symbol))))
1819
1820 (defun custom-menu-create (symbol &optional name)
1821   "Create menu for customization group SYMBOL.
1822 If optional NAME is given, use that as the name of the menu. 
1823 Otherwise make up a name from SYMBOL.
1824 The menu is in a format applicable to `easy-menu-define'."
1825   (unless name
1826     (setq name (custom-unlispify-menu-entry symbol)))
1827   (let ((item (vector name
1828                       `(custom-buffer-create '((,symbol custom-group)))
1829                       t)))
1830     (if (and (>= custom-menu-nesting 0)
1831              (< (length (get symbol 'custom-group)) widget-menu-max-size))
1832         (let ((custom-prefix-list (custom-prefix-add symbol
1833                                                      custom-prefix-list)))
1834           (custom-load-symbol symbol)
1835           `(,(custom-unlispify-menu-entry symbol t)
1836             ,item
1837             "--"
1838             ,@(mapcar (lambda (entry)
1839                         (widget-apply (if (listp (nth 1 entry))
1840                                           (nth 1 entry)
1841                                         (list (nth 1 entry)))
1842                                       :custom-menu (nth 0 entry)))
1843                       (get symbol 'custom-group))))
1844       item)))
1845
1846 ;;;###autoload
1847 (defun custom-menu-update (event)
1848   "Update customize menu."
1849   (interactive "e")
1850   (add-hook 'custom-define-hook 'custom-menu-reset)
1851   (let* ((emacs (widget-apply '(custom-group) :custom-menu 'emacs))
1852          (menu `(,(car custom-help-menu)
1853                  ,emacs
1854                  ,@(cdr (cdr custom-help-menu)))))
1855     (let ((map (easy-menu-create-keymaps (car menu) (cdr menu))))
1856       (define-key global-map [menu-bar help-menu customize-menu]
1857         (cons (car menu) map)))))
1858
1859 ;;; Dependencies.
1860
1861 ;;;###autoload
1862 (defun custom-make-dependencies ()
1863   "Batch function to extract custom dependencies from .el files.
1864 Usage: emacs -batch *.el -f custom-make-dependencies > deps.el"
1865   (let ((buffers (buffer-list)))
1866     (while buffers
1867       (set-buffer (car buffers))
1868       (setq buffers (cdr buffers))
1869       (let ((file (buffer-file-name)))
1870         (when (and file (string-match "\\`\\(.*\\)\\.el\\'" file))
1871           (goto-char (point-min))
1872           (condition-case nil
1873               (let ((name (file-name-nondirectory (match-string 1 file))))
1874                 (while t
1875                   (let ((expr (read (current-buffer))))
1876                     (when (and (listp expr)
1877                                (memq (car expr) '(defcustom defface defgroup)))
1878                       (eval expr)
1879                       (put (nth 1 expr) 'custom-where name)))))
1880             (error nil))))))
1881   (mapatoms (lambda (symbol)
1882               (let ((members (get symbol 'custom-group))
1883                     item where found)
1884                 (when members
1885                   (princ "(put '")
1886                   (princ symbol)
1887                   (princ " 'custom-loads '(")
1888                   (while members
1889                     (setq item (car (car members))
1890                           members (cdr members)
1891                           where (get item 'custom-where))
1892                     (unless (or (null where)
1893                                 (member where found))
1894                       (when found
1895                         (princ " "))
1896                       (prin1 where)
1897                       (push where found)))
1898                   (princ "))\n"))))))
1899
1900 ;;; The End.
1901
1902 (provide 'cus-edit)
1903
1904 ;; cus-edit.el ends here