]> code.delx.au - gnu-emacs/blobdiff - lisp/cus-edit.el
(dabbrev-case-replace, dabbrev-case-fold-search):
[gnu-emacs] / lisp / cus-edit.el
index 0327c7aa2861fb7c6326b43c09746adf27c5850b..023592a88a9c729c7543d134470cb071c11c3d4c 100644 (file)
@@ -4,11 +4,30 @@
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Keywords: help, faces
-;; Version: 1.71
+;; Version: 1.90
 ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
 
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
 ;;; Commentary:
 ;;
+;; This file implements the code to create and edit customize buffers.
+;; 
 ;; See `custom.el'.
 
 ;;; Code:
 (require 'cus-face)
 (require 'wid-edit)
 (require 'easymenu)
+(eval-when-compile (require 'cl))
+
+(condition-case nil
+    (require 'cus-load)
+  (error nil))
+
+(defun custom-face-display-set (face spec &optional frame)
+  (face-spec-set face spec frame))
+
+(defun custom-display-match-frame (display frame)
+  (face-spec-set-match-display display frame))
 
 (define-widget-keywords :custom-prefixes :custom-menu :custom-show
   :custom-magic :custom-state :custom-level :custom-form
   :custom-set :custom-save :custom-reset-current :custom-reset-saved 
   :custom-reset-factory)
 
+(put 'custom-define-hook 'custom-type 'hook)
+(put 'custom-define-hook 'factory-value '(nil))
+(custom-add-to-group 'customize 'custom-define-hook 'custom-variable)
+
 ;;; Customization Groups.
 
 (defgroup emacs nil
   :link '(url-link :tag "Development Page" 
                   "http://www.dina.kvl.dk/~abraham/custom/")
   :prefix "custom-"
-  :group 'help
+  :group 'help)
+
+(defgroup custom-faces nil
+  "Faces used by customize."
+  :group 'customize
   :group 'faces)
 
+(defgroup abbrev-mode nil
+  "Word abbreviations mode."
+  :group 'abbrev)
+
+(defgroup alloc nil
+  "Storage allocation and gc for GNU Emacs Lisp interpreter."
+  :tag "Storage Allocation"
+  :group 'internal)
+
+(defgroup undo nil
+  "Undoing changes in buffers."
+  :group 'editing)
+
+(defgroup modeline nil
+  "Content of the modeline."
+  :group 'environment)
+
+(defgroup fill nil
+  "Indenting and filling text."
+  :group 'editing)
+
+(defgroup editing-basics nil
+  "Most basic editing facilities."
+  :group 'editing)
+
+(defgroup display nil
+  "How characters are displayed in buffers."
+  :group 'environment)
+
+(defgroup execute nil
+  "Executing external commands."
+  :group 'processes)
+
+(defgroup installation nil
+  "The Emacs installation."
+  :group 'environment)
+
+(defgroup dired nil
+  "Directory editing."
+  :group 'environment)
+
+(defgroup limits nil
+  "Internal Emacs limits."
+  :group 'internal)
+
+(defgroup debug nil
+  "Debugging Emacs itself."
+  :group 'development)
+
+(defgroup minibuffer nil
+  "Controling the behaviour of the minibuffer."
+  :group 'environment)
+
+(defgroup keyboard nil
+  "Input from the keyboard."
+  :group 'environment)
+
+(defgroup mouse nil
+  "Input from the mouse."
+  :group 'environment)
+
+(defgroup menu nil
+  "Input from the menus."
+  :group 'environment)
+
+(defgroup auto-save nil
+  "Preventing accidential loss of data."
+  :group 'data)
+
+(defgroup processes-basics nil
+  "Basic stuff dealing with processes."
+  :group 'processes)
+
+(defgroup windows nil
+  "Windows within a frame."
+  :group 'processes)
+
 ;;; Utilities.
 
 (defun custom-quote (sexp)
@@ -236,6 +351,43 @@ IF REGEXP is not a string, return it unchanged."
        (nreverse (cons (substring regexp start) all)))
     regexp))
 
+(defun custom-variable-prompt ()
+  ;; Code stolen from `help.el'.
+  "Prompt for a variable, defaulting to the variable at point.
+Return a list suitable for use in `interactive'."
+   (let ((v (variable-at-point))
+        (enable-recursive-minibuffers t)
+        val)
+     (setq val (completing-read 
+               (if v
+                   (format "Customize variable (default %s): " v)
+                 "Customize variable: ")
+               obarray (lambda (symbol)
+                         (and (boundp symbol)
+                              (or (get symbol 'custom-type)
+                                  (user-variable-p symbol))))))
+     (list (if (equal val "")
+              v (intern val)))))
+
+(defun custom-menu-filter (menu widget)
+  "Convert MENU to the form used by `widget-choose'.
+MENU should be in the same format as `custom-variable-menu'.
+WIDGET is the widget to apply the filter entries of MENU on."
+  (let ((result nil)
+       current name action filter)
+    (while menu 
+      (setq current (car menu)
+           name (nth 0 current)
+           action (nth 1 current)
+           filter (nth 2 current)
+           menu (cdr menu))
+      (if (or (null filter) (funcall filter widget))
+         (push (cons name action) result)
+       (push name result)))
+    (nreverse result)))
+
+;;; Unlispify.
+
 (defvar custom-prefix-list nil
   "List of prefixes that should be ignored by `custom-unlispify'")
 
@@ -258,6 +410,10 @@ IF REGEXP is not a string, return it unchanged."
           (erase-buffer)
           (princ symbol (current-buffer))
           (goto-char (point-min))
+          (when (and (eq (get symbol 'custom-type) 'boolean)
+                     (re-search-forward "-p\\'" nil t))
+            (replace-match "" t t)
+            (goto-char (point-min)))
           (let ((prefixes custom-prefix-list)
                 prefix)
             (while prefixes
@@ -290,62 +446,73 @@ IF REGEXP is not a string, return it unchanged."
            (concat (symbol-name symbol) "-"))
        prefixes))
 
-;;; The Custom Mode.
-
-(defvar custom-options nil
-  "Customization widgets in the current buffer.")
-
-(defvar custom-mode-map nil
-  "Keymap for `custom-mode'.")
-  
-(unless custom-mode-map
-  (setq custom-mode-map (make-sparse-keymap))
-  (set-keymap-parent custom-mode-map widget-keymap)
-  (define-key custom-mode-map "q" 'bury-buffer))
-
-(easy-menu-define custom-mode-menu 
-    custom-mode-map
-  "Menu used in customization buffers."
-    '("Custom"
-      ["Set" custom-set t]
-      ["Save" custom-save t]
-      ["Reset to Current" custom-reset-current t]
-      ["Reset to Saved" custom-reset-saved t]
-      ["Reset to Factory Settings" custom-reset-factory t]
-      ["Info" (Info-goto-node "(custom)The Customization Buffer") t]))
-
-(defcustom custom-mode-hook nil
-  "Hook called when entering custom-mode."
-  :type 'hook
+;;; Guess.
+
+(defcustom custom-guess-name-alist
+  '(("-p\\'" boolean)
+    ("-hook\\'" hook)
+    ("-face\\'" face)
+    ("-file\\'" file)
+    ("-function\\'" function)
+    ("-functions\\'" (repeat function))
+    ("-list\\'" (repeat sexp))
+    ("-alist\\'" (repeat (cons sexp sexp))))
+  "Alist of (MATCH TYPE).
+
+MATCH should be a regexp matching the name of a symbol, and TYPE should 
+be a widget suitable for editing the value of that symbol.  The TYPE
+of the first entry where MATCH matches the name of the symbol will be
+used. 
+
+This is used for guessing the type of variables not declared with
+customize."
+  :type '(repeat (group (regexp :tag "Match") (sexp :tag "Type")))
   :group 'customize)
 
-(defun custom-mode ()
-  "Major mode for editing customization buffers.
+(defcustom custom-guess-doc-alist
+  '(("\\`\\*?Non-nil " boolean))
+  "Alist of (MATCH TYPE).
 
-The following commands are available:
+MATCH should be a regexp matching a documentation string, and TYPE
+should be a widget suitable for editing the value of a variable with
+that documentation string.  The TYPE of the first entry where MATCH
+matches the name of the symbol will be used.
 
-\\[widget-forward]             Move to next button or editable field.
-\\[widget-backward]            Move to previous button or editable field.
-\\[widget-button-click]                Activate button under the mouse pointer.
-\\[widget-button-press]                Activate button under point.
-\\[custom-set]                 Set all modifications.
-\\[custom-save]                Make all modifications default.
-\\[custom-reset-current]        Reset all modified options. 
-\\[custom-reset-saved]         Reset all modified or set options.
-\\[custom-reset-factory]       Reset all options.
+This is used for guessing the type of variables not declared with
+customize."
+  :type '(repeat (group (regexp :tag "Match") (sexp :tag "Type")))
+  :group 'customize)
 
-Entry to this mode calls the value of `custom-mode-hook'
-if that value is non-nil."
-  (kill-all-local-variables)
-  (setq major-mode 'custom-mode
-       mode-name "Custom")
-  (use-local-map custom-mode-map)
-  (easy-menu-add custom-mode-menu)
-  (make-local-variable 'custom-options)
-  (run-hooks 'custom-mode-hook))
+(defun custom-guess-type (symbol)
+  "Guess a widget suitable for editing the value of SYMBOL.
+This is done by matching SYMBOL with `custom-guess-name-alist' and 
+if that fails, the doc string with `custom-guess-doc-alist'."
+  (let ((name (symbol-name symbol))
+       (names custom-guess-name-alist)
+       current found)
+    (while names
+      (setq current (car names)
+           names (cdr names))
+      (when (string-match (nth 0 current) name)
+       (setq found (nth 1 current)
+             names nil)))
+    (unless found
+      (let ((doc (documentation-property symbol 'variable-documentation))
+           (docs custom-guess-doc-alist))
+       (when doc 
+         (while docs
+           (setq current (car docs)
+                 docs (cdr docs))
+           (when (string-match (nth 0 current) doc)
+             (setq found (nth 1 current)
+                   docs nil))))))
+    found))
 
 ;;; Custom Mode Commands.
 
+(defvar custom-options nil
+  "Customization widgets in the current buffer.")
+
 (defun custom-set ()
   "Set changes in all modified options."
   (interactive)
@@ -402,7 +569,7 @@ when the action is chosen.")
            children)))
 
 (defun custom-reset-factory ()
-  "Reset all modified, set, or saved group members to their factory settings."
+  "Reset all modified, set, or saved group members to their standard settings."
   (interactive)
   (let ((children custom-options))
     (mapcar (lambda (child)
@@ -412,8 +579,100 @@ when the action is chosen.")
 
 ;;; The Customize Commands
 
+(defun custom-prompt-variable (prompt-var prompt-val)
+  "Prompt for a variable and a value and return them as a list.
+PROMPT-VAR is the prompt for the variable, and PROMPT-VAL is the
+prompt for the value.  The %s escape in PROMPT-VAL is replaced with
+the name of the variable.
+
+If the variable has a `variable-interactive' property, that is used as if
+it were the arg to `interactive' (which see) to interactively read the value.
+
+If the variable has a `custom-type' property, it must be a widget and the
+`:prompt-value' property of that widget will be used for reading the value."
+  (let* ((var (read-variable prompt-var))
+        (minibuffer-help-form '(describe-variable var)))
+    (list var
+         (let ((prop (get var 'variable-interactive))
+               (type (get var 'custom-type))
+               (prompt (format prompt-val var)))
+           (unless (listp type)
+             (setq type (list type)))
+           (cond (prop
+                  ;; Use VAR's `variable-interactive' property
+                  ;; as an interactive spec for prompting.
+                  (call-interactively (list 'lambda '(arg)
+                                            (list 'interactive prop)
+                                            'arg)))
+                 (type
+                  (widget-prompt-value type
+                                       prompt
+                                       (if (boundp var)
+                                           (symbol-value var))
+                                       (not (boundp var))))
+                 (t
+                  (eval-minibuffer prompt)))))))
+
+;;;###autoload
+(defun custom-set-value (var val)
+  "Set VARIABLE to VALUE.  VALUE is a Lisp object.
+
+If VARIABLE has a `variable-interactive' property, that is used as if
+it were the arg to `interactive' (which see) to interactively read the value.
+
+If VARIABLE has a `custom-type' property, it must be a widget and the
+`:prompt-value' property of that widget will be used for reading the value." 
+  (interactive (custom-prompt-variable "Set variable: "
+                                      "Set %s to value: "))
+   
+  (set var val))
+
+;;;###autoload
+(defun custom-set-variable (var val)
+  "Set the default for VARIABLE to VALUE.  VALUE is a Lisp object.
+
+If VARIABLE has a `custom-set' property, that is used for setting
+VARIABLE, otherwise `set-default' is used.
+
+The `customized-value' property of the VARIABLE will be set to a list
+with a quoted VALUE as its sole list member.
+
+If VARIABLE has a `variable-interactive' property, that is used as if
+it were the arg to `interactive' (which see) to interactively read the value.
+
+If VARIABLE has a `custom-type' property, it must be a widget and the
+`:prompt-value' property of that widget will be used for reading the value. " 
+  (interactive (custom-prompt-variable "Set variable: "
+                                      "Set customized value for %s to: "))
+  (funcall (or (get var 'custom-set) 'set-default) var val)
+  (put var 'customized-value (list (custom-quote val))))
+
+;;;###autoload
+(defun customize ()
+  "Select a customization buffer which you can use to set user options.
+User options are structured into \"groups\".
+Initially the top-level group `Emacs' and its immediate subgroups
+are shown; the contents of those subgroups are initially hidden."
+  (interactive)
+  (customize-group 'emacs))
+
 ;;;###autoload
-(defun customize (symbol)
+(defun customize-group (group)
+  "Customize GROUP, which must be a customization group."
+  (interactive (list (completing-read "Customize group: (default emacs) "
+                                     obarray 
+                                     (lambda (symbol)
+                                       (get symbol 'custom-group))
+                                     t)))
+
+  (when (stringp group)
+    (if (string-equal "" group)
+       (setq group 'emacs)
+      (setq group (intern group))))
+  (custom-buffer-create (list (list group 'custom-group))))
+
+;;;###autoload
+(defun customize-other-window (symbol)
   "Customize SYMBOL, which must be a customization group."
   (interactive (list (completing-read "Customize group: (default emacs) "
                                      obarray 
@@ -425,25 +684,21 @@ when the action is chosen.")
     (if (string-equal "" symbol)
        (setq symbol 'emacs)
       (setq symbol (intern symbol))))
-  (custom-buffer-create (list (list symbol 'custom-group))))
+  (custom-buffer-create-other-window (list (list symbol 'custom-group))))
 
 ;;;###autoload
 (defun customize-variable (symbol)
   "Customize SYMBOL, which must be a variable."
-  (interactive
-   ;; Code stolen from `help.el'.
-   (let ((v (variable-at-point))
-        (enable-recursive-minibuffers t)
-        val)
-     (setq val (completing-read 
-               (if v
-                   (format "Customize variable (default %s): " v)
-                 "Customize variable: ")
-               obarray 'boundp t))
-     (list (if (equal val "")
-              v (intern val)))))
+  (interactive (custom-variable-prompt))
   (custom-buffer-create (list (list symbol 'custom-variable))))
 
+;;;###autoload
+(defun customize-variable-other-window (symbol)
+  "Customize SYMBOL, which must be a variable.
+Show the buffer in another window, but don't select it."
+  (interactive (custom-variable-prompt))
+  (custom-buffer-create-other-window (list (list symbol 'custom-variable))))
+
 ;;;###autoload
 (defun customize-face (&optional symbol)
   "Customize SYMBOL, which should be a face name or nil.
@@ -455,7 +710,10 @@ If SYMBOL is nil, customize all faces."
        (message "Looking for faces...")
        (mapcar (lambda (symbol)
                  (setq found (cons (list symbol 'custom-face) found)))
-               (face-list))
+               (nreverse (mapcar 'intern 
+                                 (sort (mapcar 'symbol-name (face-list))
+                                       'string<))))
+                       
        (custom-buffer-create found))
     (if (stringp symbol)
        (setq symbol (intern symbol)))
@@ -463,9 +721,39 @@ If SYMBOL is nil, customize all faces."
       (error "Should be a symbol %S" symbol))
     (custom-buffer-create (list (list symbol 'custom-face)))))
 
+;;;###autoload
+(defun customize-face-other-window (&optional symbol)
+  "Show customization buffer for FACE in other window."
+  (interactive (list (completing-read "Customize face: " 
+                                     obarray 'custom-facep)))
+  (if (or (null symbol) (and (stringp symbol) (zerop (length symbol))))
+      ()
+    (if (stringp symbol)
+       (setq symbol (intern symbol)))
+    (unless (symbolp symbol)
+      (error "Should be a symbol %S" symbol))
+    (custom-buffer-create-other-window (list (list symbol 'custom-face)))))
+
 ;;;###autoload
 (defun customize-customized ()
-  "Customize all already customized user options."
+  "Customize all user options set since the last save in this session."
+  (interactive)
+  (let ((found nil))
+    (mapatoms (lambda (symbol)
+               (and (get symbol 'customized-face)
+                    (custom-facep symbol)
+                    (setq found (cons (list symbol 'custom-face) found)))
+               (and (get symbol 'customized-value)
+                    (boundp symbol)
+                    (setq found
+                          (cons (list symbol 'custom-variable) found)))))
+    (if found 
+       (custom-buffer-create found)
+      (error "No customized user options"))))
+
+;;;###autoload
+(defun customize-saved ()
+  "Customize all already saved user options."
   (interactive)
   (let ((found nil))
     (mapatoms (lambda (symbol)
@@ -478,7 +766,7 @@ If SYMBOL is nil, customize all faces."
                           (cons (list symbol 'custom-variable) found)))))
     (if found 
        (custom-buffer-create found)
-      (error "No customized user options"))))
+      (error "No saved user options"))))
 
 ;;;###autoload
 (defun customize-apropos (regexp &optional all)
@@ -505,15 +793,33 @@ user-settable."
        (custom-buffer-create found)
       (error "No matches"))))
 
+;;; Buffer.
+
 ;;;###autoload
 (defun custom-buffer-create (options)
   "Create a buffer containing OPTIONS.
 OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
 SYMBOL is a customization option, and WIDGET is a widget for editing
 that option."
-  (message "Creating customization buffer...")
   (kill-buffer (get-buffer-create "*Customization*"))
   (switch-to-buffer (get-buffer-create "*Customization*"))
+  (custom-buffer-create-internal options))
+
+;;;###autoload
+(defun custom-buffer-create-other-window (options)
+  "Create a buffer containing OPTIONS.
+OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
+SYMBOL is a customization option, and WIDGET is a widget for editing
+that option."
+  (kill-buffer (get-buffer-create "*Customization*"))
+  (let ((window (selected-window)))
+    (switch-to-buffer-other-window (get-buffer-create "*Customization*"))
+    (custom-buffer-create-internal options)
+    (select-window window)))
+  
+
+(defun custom-buffer-create-internal (options)
+  (message "Creating customization buffer...")
   (custom-mode)
   (widget-insert "This is a customization buffer.
 Push RET or click mouse-2 on the word ")
@@ -591,6 +897,7 @@ Make the modifications default for future sessions."
   (message "Creating customization setup...")
   (widget-setup)
   (goto-char (point-min))
+  (forward-line 3)                     ;Kludge: bob is writable in XEmacs.
   (message "Creating customization buffer...done"))
 
 ;;; Modification of Basic Widgets.
@@ -673,7 +980,7 @@ this item has been saved.")
                                (rogue "@" custom-rogue-face "\
 this item is not prepared for customization.")
                                (factory " " nil "\
-this item is unchanged from its factory setting."))
+this item is unchanged from its standard setting."))
   "Alist of customize option states.
 Each entry is of the form (STATE MAGIC FACE DESCRIPTION), where 
 
@@ -698,7 +1005,7 @@ STATE is one of the following symbols:
 `rogue'
    This item has no customization information.
 `factory'
-   This item is unchanged from the factory default.
+   This item is unchanged from the standard setting.
 
 MAGIC is a string used to present that state.
 
@@ -753,7 +1060,110 @@ The list should be sorted most significant first."
                                     (string :tag "Magic")
                                     face
                                     (string :tag "Description"))))
-  :group 'customize)
+  :group 'customize
+  :group 'custom-faces)
+
+(defcustom custom-group-magic-alist '((nil "#" underline "\
+uninitialized, you should not see this.")
+                               (unknown "?" italic "\
+unknown, you should not see this.")
+                               (hidden "-" default "\
+group now hidden; click on the asterisks above to show contents.")
+                               (invalid "x" custom-invalid-face "\
+the value displayed for this item is invalid and cannot be set.")
+                               (modified "*" custom-modified-face "\
+you have edited something in this group, and can now set it.")
+                               (set "+" custom-set-face "\
+something in this group has been set, but not yet saved.")
+                               (changed ":" custom-changed-face "\
+this item has been changed outside customize.")
+                               (saved "!" custom-saved-face "\
+something in this group has been set and saved.")
+                               (rogue "@" custom-rogue-face "\
+this item is not prepared for customization.")
+                               (factory " " nil "\
+nothing in this group has been changed."))
+  "Alist of customize option states.
+Each entry is of the form (STATE MAGIC FACE DESCRIPTION), where 
+
+STATE is one of the following symbols:
+
+`nil'
+   For internal use, should never occur.
+`unknown'
+   For internal use, should never occur.
+`hidden'
+   This item is not being displayed. 
+`invalid'
+   This item is modified, but has an invalid form.
+`modified'
+   This item is modified, and has a valid form.
+`set'
+   This item has been set but not saved.
+`changed'
+   The current value of this item has been changed temporarily.
+`saved'
+   This item is marked for saving.
+`rogue'
+   This item has no customization information.
+`factory'
+   This item is unchanged from the standard setting.
+
+MAGIC is a string used to present that state.
+
+FACE is a face used to present the state.
+
+DESCRIPTION is a string describing the state.
+
+The list should be sorted most significant first."
+  :type '(list (checklist :inline t
+                         (group (const nil)
+                                (string :tag "Magic")
+                                face 
+                                (string :tag "Description"))
+                         (group (const unknown)
+                                (string :tag "Magic")
+                                face 
+                                (string :tag "Description"))
+                         (group (const hidden)
+                                (string :tag "Magic")
+                                face 
+                                (string :tag "Description"))
+                         (group (const invalid)
+                                (string :tag "Magic")
+                                face 
+                                (string :tag "Description"))
+                         (group (const modified)
+                                (string :tag "Magic")
+                                face 
+                                (string :tag "Description"))
+                         (group (const set)
+                                (string :tag "Magic")
+                                face 
+                                (string :tag "Description"))
+                         (group (const changed)
+                                (string :tag "Magic")
+                                face 
+                                (string :tag "Description"))
+                         (group (const saved)
+                                (string :tag "Magic")
+                                face 
+                                (string :tag "Description"))
+                         (group (const rogue)
+                                (string :tag "Magic")
+                                face 
+                                (string :tag "Description"))
+                         (group (const factory)
+                                (string :tag "Magic")
+                                face 
+                                (string :tag "Description")))
+              (editable-list :inline t
+                             (group symbol
+                                    (string :tag "Magic")
+                                    face
+                                    (string :tag "Description"))))
+  :group 'customize
+  :group 'custom-faces)
 
 (defcustom custom-magic-show 'long
   "Show long description of the state of each customization option."
@@ -771,6 +1181,7 @@ The list should be sorted most significant first."
   "Show and manipulate state for a customization option."
   :format "%v"
   :action 'widget-choice-item-action
+  :notify 'ignore
   :value-get 'ignore
   :value-create 'custom-magic-value-create
   :value-delete 'widget-children-value-delete)
@@ -779,7 +1190,9 @@ The list should be sorted most significant first."
   ;; Create compact status report for WIDGET.
   (let* ((parent (widget-get widget :parent))
         (state (widget-get parent :custom-state))
-        (entry (assq state custom-magic-alist))
+        (entry (assq state (if (eq (car parent) 'custom-group)
+                               custom-group-magic-alist
+                             custom-magic-alist)))
         (magic (nth 1 entry))
         (face (nth 2 entry))
         (text (nth 3 entry))
@@ -830,15 +1243,7 @@ Change the state of this item."
 
 (defun custom-level-action (widget &optional event)
   "Toggle visibility for parent to WIDGET."
-  (let* ((parent (widget-get widget :parent))
-        (state (widget-get parent :custom-state)))
-    (cond ((memq state '(invalid modified))
-          (error "There are unset changes"))
-         ((eq state 'hidden)
-          (widget-put parent :custom-state 'unknown))
-         (t
-          (widget-put parent :custom-state 'hidden)))
-    (custom-redraw parent)))
+  (custom-toggle-hide (widget-get widget :parent)))
 
 ;;; The `custom' Widget.
 
@@ -926,14 +1331,20 @@ Change the state of this item."
 
 (defun custom-redraw (widget)
   "Redraw WIDGET with current settings."
-  (let ((pos (point))
+  (let ((line (count-lines (point-min) (point)))
+       (column (current-column))
+       (pos (point))
        (from (marker-position (widget-get widget :from)))
        (to (marker-position (widget-get widget :to))))
     (save-excursion
       (widget-value-set widget (widget-value widget))
       (custom-redraw-magic widget))
     (when (and (>= pos from) (<= pos to))
-      (goto-char pos))))
+      (condition-case nil
+         (progn 
+           (goto-line line)
+           (move-to-column column))
+       (error nil)))))
 
 (defun custom-redraw-magic (widget)
   "Redraw WIDGET state with current settings."
@@ -956,36 +1367,52 @@ Change the state of this item."
          (t
           (funcall show widget value)))))
 
+(defvar custom-load-recursion nil
+  "Hack to avoid recursive dependencies.")
+
 (defun custom-load-symbol (symbol)
   "Load all dependencies for SYMBOL."
-  (let ((loads (get symbol 'custom-loads))
-       load)
-    (while loads
-      (setq load (car loads)
-           loads (cdr loads))
-      (cond ((symbolp load)
-            (condition-case nil
-                (require load)
-              (error nil)))
-           ((assoc load load-history))
-           (t
-            (condition-case nil
-                (load-library load)
-              (error nil)))))))
+  (unless custom-load-recursion
+    (let ((custom-load-recursion t) 
+         (loads (get symbol 'custom-loads))
+         load)
+      (while loads
+       (setq load (car loads)
+             loads (cdr loads))
+       (cond ((symbolp load)
+              (condition-case nil
+                  (require load)
+                (error nil)))
+             ((assoc load load-history))
+             (t
+              (condition-case nil
+                  (load-library load)
+                (error nil))))))))
 
 (defun custom-load-widget (widget)
   "Load all dependencies for WIDGET."
   (custom-load-symbol (widget-value widget)))
 
+(defun custom-toggle-hide (widget)
+  "Toggle visibility of WIDGET."
+  (let ((state (widget-get widget :custom-state)))
+    (cond ((memq state '(invalid modified))
+          (error "There are unset changes"))
+         ((eq state 'hidden)
+          (widget-put widget :custom-state 'unknown))
+         (t 
+          (widget-put widget :custom-state 'hidden)))
+    (custom-redraw widget)))
+
 ;;; The `custom-variable' Widget.
 
 (defface custom-variable-sample-face '((t (:underline t)))
   "Face used for unpushable variable tags."
-  :group 'customize)
+  :group 'custom-faces)
 
 (defface custom-variable-button-face '((t (:underline t :bold t)))
   "Face used for pushable variable tags."
-  :group 'customize)
+  :group 'custom-faces)
 
 (define-widget 'custom-variable 'custom
   "Customize variable."
@@ -1003,6 +1430,22 @@ Change the state of this item."
   :custom-reset-saved 'custom-variable-reset-saved
   :custom-reset-factory 'custom-variable-reset-factory)
 
+(defun custom-variable-type (symbol)
+  "Return a widget suitable for editing the value of SYMBOL.
+If SYMBOL has a `custom-type' property, use that.  
+Otherwise, look up symbol in `custom-guess-type-alist'."
+  (let* ((type (or (get symbol 'custom-type)
+                  (and (not (get symbol 'factory-value))
+                       (custom-guess-type symbol))
+                  'sexp))
+        (options (get symbol 'custom-options))
+        (tmp (if (listp type)
+                 (copy-sequence type)
+               (list type))))
+    (when options
+      (widget-put tmp :options options))
+    tmp))
+
 (defun custom-variable-value-create (widget)
   "Here is where you edit the variables value."
   (custom-load-widget widget)
@@ -1011,18 +1454,13 @@ Change the state of this item."
         (form (widget-get widget :custom-form))
         (state (widget-get widget :custom-state))
         (symbol (widget-get widget :value))
-        (options (get symbol 'custom-options))
-        (child-type (or (get symbol 'custom-type) 'sexp))
         (tag (widget-get widget :tag))
-        (type (let ((tmp (if (listp child-type)
-                             (copy-list child-type)
-                           (list child-type))))
-                (when options
-                  (widget-put tmp :options options))
-                tmp))
+        (type (custom-variable-type symbol))
         (conv (widget-convert type))
+        (get (or (get symbol 'custom-get) 'default-value))
+        (set (or (get symbol 'custom-set) 'set-default))
         (value (if (default-boundp symbol)
-                   (default-value symbol)
+                   (funcall get symbol)
                  (widget-get conv :value))))
     ;; If the widget is new, the child determine whether it is hidden.
     (cond (state)
@@ -1052,7 +1490,7 @@ Change the state of this item."
                               ((get symbol 'factory-value)
                                (car (get symbol 'factory-value)))
                               ((default-boundp symbol)
-                               (custom-quote (default-value symbol)))
+                               (custom-quote (funcall get symbol)))
                               (t
                                (custom-quote (widget-get conv :value))))))
             (push (widget-create-child-and-convert 
@@ -1084,8 +1522,9 @@ Change the state of this item."
 (defun custom-variable-state-set (widget)
   "Set the state of WIDGET."
   (let* ((symbol (widget-value widget))
+        (get (or (get symbol 'custom-get) 'default-value))
         (value (if (default-boundp symbol)
-                   (default-value symbol)
+                   (funcall get symbol)
                  (widget-get widget :value)))
         tmp
         (state (cond ((setq tmp (get symbol 'customized-value))
@@ -1110,29 +1549,52 @@ Change the state of this item."
     (widget-put widget :custom-state state)))
 
 (defvar custom-variable-menu 
-  '(("Edit" . custom-variable-edit)
-    ("Edit Lisp" . custom-variable-edit-lisp)
-    ("Set" . custom-variable-set)
-    ("Save" . custom-variable-save)
-    ("Reset to Current" . custom-redraw)
-    ("Reset to Saved" . custom-variable-reset-saved)
-    ("Reset to Factory Settings" . custom-variable-reset-factory))
+  '(("Hide" custom-toggle-hide
+     (lambda (widget)
+       (not (memq (widget-get widget :custom-state) '(modified invalid)))))
+     ("Edit" custom-variable-edit 
+     (lambda (widget)
+       (not (eq (widget-get widget :custom-form) 'edit))))
+    ("Edit Lisp" custom-variable-edit-lisp
+     (lambda (widget)
+       (not (eq (widget-get widget :custom-form) 'lisp))))
+    ("Set" custom-variable-set
+     (lambda (widget)
+       (eq (widget-get widget :custom-state) 'modified)))
+    ("Save" custom-variable-save
+     (lambda (widget)
+       (memq (widget-get widget :custom-state) '(modified set changed rogue))))
+    ("Reset to Current" custom-redraw
+     (lambda (widget)
+       (and (default-boundp (widget-value widget))
+           (memq (widget-get widget :custom-state) '(modified)))))
+    ("Reset to Saved" custom-variable-reset-saved
+     (lambda (widget)
+       (and (get (widget-value widget) 'saved-value)
+           (memq (widget-get widget :custom-state)
+                 '(modified set changed rogue)))))
+    ("Reset to Standard Settings" custom-variable-reset-factory
+     (lambda (widget)
+       (and (get (widget-value widget) 'factory-value)
+           (memq (widget-get widget :custom-state)
+                 '(modified set changed saved rogue))))))
   "Alist of actions for the `custom-variable' widget.
-The key is a string containing the name of the action, the value is a
-lisp function taking the widget as an element which will be called
-when the action is chosen.")
+Each entry has the form (NAME ACTION FILTER) where NAME is the name of
+the menu entry, ACTION is the function to call on the widget when the
+menu is selected, and FILTER is a predicate which takes a `custom-variable'
+widget as an argument, and returns non-nil if ACTION is valid on that
+widget. If FILTER is nil, ACTION is always valid.")
 
 (defun custom-variable-action (widget &optional event)
   "Show the menu for `custom-variable' WIDGET.
 Optional EVENT is the location for the menu."
   (if (eq (widget-get widget :custom-state) 'hidden)
-      (progn 
-       (widget-put widget :custom-state 'unknown)
-       (custom-redraw widget))
+      (custom-toggle-hide widget)
     (let* ((completion-ignore-case t)
           (answer (widget-choose (custom-unlispify-tag-name
                                   (widget-get widget :value))
-                                 custom-variable-menu
+                                 (custom-menu-filter custom-variable-menu
+                                                     widget)
                                  event)))
       (if answer
          (funcall answer widget)))))
@@ -1151,32 +1613,34 @@ Optional EVENT is the location for the menu."
 
 (defun custom-variable-set (widget)
   "Set the current value for the variable being edited by WIDGET."
-  (let ((form (widget-get widget :custom-form))
-       (state (widget-get widget :custom-state))
-       (child (car (widget-get widget :children)))
-       (symbol (widget-value widget))
-       val)
+  (let* ((form (widget-get widget :custom-form))
+        (state (widget-get widget :custom-state))
+        (child (car (widget-get widget :children)))
+        (symbol (widget-value widget))
+        (set (or (get symbol 'custom-set) 'set-default))
+         val)
     (cond ((eq state 'hidden)
           (error "Cannot set hidden variable."))
          ((setq val (widget-apply child :validate))
           (goto-char (widget-get val :from))
           (error "%s" (widget-get val :error)))
          ((eq form 'lisp)
-          (set symbol (eval (setq val (widget-value child))))
+          (funcall set symbol (eval (setq val (widget-value child))))
           (put symbol 'customized-value (list val)))
          (t
-          (set symbol (setq val (widget-value child)))
+          (funcall set symbol (setq val (widget-value child)))
           (put symbol 'customized-value (list (custom-quote val)))))
     (custom-variable-state-set widget)
     (custom-redraw-magic widget)))
 
 (defun custom-variable-save (widget)
   "Set the default value for the variable being edited by WIDGET."
-  (let ((form (widget-get widget :custom-form))
-       (state (widget-get widget :custom-state))
-       (child (car (widget-get widget :children)))
-       (symbol (widget-value widget))
-       val)
+  (let* ((form (widget-get widget :custom-form))
+        (state (widget-get widget :custom-state))
+        (child (car (widget-get widget :children)))
+        (symbol (widget-value widget))
+        (set (or (get symbol 'custom-set) 'set-default))
+        val)
     (cond ((eq state 'hidden)
           (error "Cannot set hidden variable."))
          ((setq val (widget-apply child :validate))
@@ -1184,12 +1648,12 @@ Optional EVENT is the location for the menu."
           (error "%s" (widget-get val :error)))
          ((eq form 'lisp)
           (put symbol 'saved-value (list (widget-value child)))
-          (set symbol (eval (widget-value child))))
+          (funcall set symbol (eval (widget-value child))))
          (t
           (put symbol
                'saved-value (list (custom-quote (widget-value
                                                  child))))
-          (set symbol (widget-value child))))
+          (funcall set symbol (widget-value child))))
     (put symbol 'customized-value nil)
     (custom-save-all)
     (custom-variable-state-set widget)
@@ -1197,10 +1661,11 @@ Optional EVENT is the location for the menu."
 
 (defun custom-variable-reset-saved (widget)
   "Restore the saved value for the variable being edited by WIDGET."
-  (let ((symbol (widget-value widget)))
+  (let* ((symbol (widget-value widget))
+        (set (or (get symbol 'custom-set) 'set-default)))
     (if (get symbol 'saved-value)
        (condition-case nil
-           (set symbol (eval (car (get symbol 'saved-value))))
+           (funcall set symbol (eval (car (get symbol 'saved-value))))
          (error nil))
       (error "No saved value for %s" symbol))
     (put symbol 'customized-value nil)
@@ -1208,11 +1673,12 @@ Optional EVENT is the location for the menu."
     (custom-redraw widget)))
 
 (defun custom-variable-reset-factory (widget)
-  "Restore the factory setting for the variable being edited by WIDGET."
-  (let ((symbol (widget-value widget)))
+  "Restore the standard setting for the variable being edited by WIDGET."
+  (let* ((symbol (widget-value widget))
+        (set (or (get symbol 'custom-set) 'set-default)))
     (if (get symbol 'factory-value)
-       (set symbol (eval (car (get symbol 'factory-value))))
-      (error "No factory default for %S" symbol))
+       (funcall set symbol (eval (car (get symbol 'factory-value))))
+      (error "No standard setting known for %S" symbol))
     (put symbol 'customized-value nil)
     (when (get symbol 'saved-value)
       (put symbol 'saved-value nil)
@@ -1311,7 +1777,7 @@ Match frames with dark backgrounds.")
 
 (defface custom-face-tag-face '((t (:underline t)))
   "Face used for face tags."
-  :group 'customize)
+  :group 'custom-faces)
 
 (define-widget 'custom-face 'custom
   "Customize face."
@@ -1368,9 +1834,7 @@ Match frames with dark backgrounds.")
 
 (defun custom-display-unselected-match (widget value)
   "Non-nil if VALUE is an unselected display specification."
-  (and (listp value)
-       (eq (length value) 2)
-       (not (custom-display-match-frame value (selected-frame)))))
+  (not (custom-display-match-frame value (selected-frame))))
 
 (define-widget 'custom-face-selected 'group 
   "Edit the attributes of the selected display in a face specification."
@@ -1418,17 +1882,32 @@ Match frames with dark backgrounds.")
     (message "Creating face editor...done")))
 
 (defvar custom-face-menu 
-  '(("Edit Selected" . custom-face-edit-selected)
-    ("Edit All" . custom-face-edit-all)
-    ("Edit Lisp" . custom-face-edit-lisp)
-    ("Set" . custom-face-set)
-    ("Save" . custom-face-save)
-    ("Reset to Saved" . custom-face-reset-saved)
-    ("Reset to Factory Setting" . custom-face-reset-factory))
+  '(("Hide" custom-toggle-hide
+     (lambda (widget)
+       (not (memq (widget-get widget :custom-state) '(modified invalid)))))
+    ("Edit Selected" custom-face-edit-selected
+     (lambda (widget)
+       (not (eq (widget-get widget :custom-form) 'selected))))
+    ("Edit All" custom-face-edit-all
+     (lambda (widget)
+       (not (eq (widget-get widget :custom-form) 'all))))
+    ("Edit Lisp" custom-face-edit-lisp
+     (lambda (widget)
+       (not (eq (widget-get widget :custom-form) 'lisp))))
+    ("Set" custom-face-set)
+    ("Save" custom-face-save)
+    ("Reset to Saved" custom-face-reset-saved
+     (lambda (widget)
+       (get (widget-value widget) 'saved-face)))
+    ("Reset to Standard Setting" custom-face-reset-factory
+     (lambda (widget)
+       (get (widget-value widget) 'factory-face))))
   "Alist of actions for the `custom-face' widget.
-The key is a string containing the name of the action, the value is a
-lisp function taking the widget as an element which will be called
-when the action is chosen.")
+Each entry has the form (NAME ACTION FILTER) where NAME is the name of
+the menu entry, ACTION is the function to call on the widget when the
+menu is selected, and FILTER is a predicate which takes a `custom-face'
+widget as an argument, and returns non-nil if ACTION is valid on that
+widget. If FILTER is nil, ACTION is always valid.")
 
 (defun custom-face-edit-selected (widget)
   "Edit selected attributes of the value of WIDGET."
@@ -1464,13 +1943,13 @@ when the action is chosen.")
   "Show the menu for `custom-face' WIDGET.
 Optional EVENT is the location for the menu."
   (if (eq (widget-get widget :custom-state) 'hidden)
-      (progn 
-       (widget-put widget :custom-state 'unknown)
-       (custom-redraw widget))
+      (custom-toggle-hide widget)
     (let* ((completion-ignore-case t)
           (symbol (widget-get widget :value))
           (answer (widget-choose (custom-unlispify-tag-name symbol)
-                                 custom-face-menu event)))
+                                 (custom-menu-filter custom-face-menu
+                                                     widget)
+                                 event)))
       (if answer
          (funcall answer widget)))))
 
@@ -1480,8 +1959,6 @@ Optional EVENT is the location for the menu."
         (child (car (widget-get widget :children)))
         (value (widget-value child)))
     (put symbol 'customized-face value)
-    (when (fboundp 'copy-face)
-      (copy-face 'custom-face-empty symbol))
     (custom-face-display-set symbol value)
     (custom-face-state-set widget)
     (custom-redraw-magic widget)))
@@ -1491,8 +1968,6 @@ Optional EVENT is the location for the menu."
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children)))
         (value (widget-value child)))
-    (when (fboundp 'copy-face)
-      (copy-face 'custom-face-empty symbol))
     (custom-face-display-set symbol value)
     (put symbol 'saved-face value)
     (put symbol 'customized-face nil)
@@ -1507,26 +1982,22 @@ Optional EVENT is the location for the menu."
     (unless value
       (error "No saved value for this face"))
     (put symbol 'customized-face nil)
-    (when (fboundp 'copy-face)
-      (copy-face 'custom-face-empty symbol))
     (custom-face-display-set symbol value)
     (widget-value-set child value)
     (custom-face-state-set widget)
     (custom-redraw-magic widget)))
 
 (defun custom-face-reset-factory (widget)
-  "Restore WIDGET to the face's factory settings."
+  "Restore WIDGET to the face's standard settings."
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children)))
         (value (get symbol 'factory-face)))
     (unless value
-      (error "No factory default for this face"))
+      (error "No standard setting for this face"))
     (put symbol 'customized-face nil)
     (when (get symbol 'saved-face)
       (put symbol 'saved-face nil)
       (custom-save-all))
-    (when (fboundp 'copy-face)
-      (copy-face 'custom-face-empty symbol))
     (custom-face-display-set symbol value)
     (widget-value-set child value)
     (custom-face-state-set widget)
@@ -1613,7 +2084,7 @@ The first member is used for level 1 groups, the second for level 2,
 and so forth.  The remaining group tags are shown with
 `custom-group-tag-face'."
   :type '(repeat face)
-  :group 'customize)
+  :group 'custom-faces)
 
 (defface custom-group-tag-face-1 '((((class color)
                                     (background dark))
@@ -1632,7 +2103,7 @@ and so forth.  The remaining group tags are shown with
                                  (:foreground "blue" :underline t))
                                 (t (:underline t)))
   "Face used for low level group tags."
-  :group 'customize)
+  :group 'custom-faces)
 
 (define-widget 'custom-group 'custom
   "Customize group."
@@ -1691,27 +2162,44 @@ and so forth.  The remaining group tags are shown with
        (message "Creating group... done")))))
 
 (defvar custom-group-menu 
-  '(("Set" . custom-group-set)
-    ("Save" . custom-group-save)
-    ("Reset to Current" . custom-group-reset-current)
-    ("Reset to Saved" . custom-group-reset-saved)
-    ("Reset to Factory" . custom-group-reset-factory))
+  '(("Hide" custom-toggle-hide
+     (lambda (widget)
+       (not (memq (widget-get widget :custom-state) '(modified invalid)))))
+    ("Set" custom-group-set
+     (lambda (widget)
+       (eq (widget-get widget :custom-state) 'modified)))
+    ("Save" custom-group-save
+     (lambda (widget)
+       (memq (widget-get widget :custom-state) '(modified set))))
+    ("Reset to Current" custom-group-reset-current
+     (lambda (widget)
+       (and (default-boundp (widget-value widget))
+           (memq (widget-get widget :custom-state) '(modified)))))
+    ("Reset to Saved" custom-group-reset-saved
+     (lambda (widget)
+       (and (get (widget-value widget) 'saved-value)
+           (memq (widget-get widget :custom-state) '(modified set)))))
+    ("Reset to Standard Settings" custom-group-reset-factory
+     (lambda (widget)
+       (and (get (widget-value widget) 'factory-value)
+           (memq (widget-get widget :custom-state) '(modified set saved))))))
   "Alist of actions for the `custom-group' widget.
-The key is a string containing the name of the action, the value is a
-lisp function taking the widget as an element which will be called
-when the action is chosen.")
+Each entry has the form (NAME ACTION FILTER) where NAME is the name of
+the menu entry, ACTION is the function to call on the widget when the
+menu is selected, and FILTER is a predicate which takes a `custom-group'
+widget as an argument, and returns non-nil if ACTION is valid on that
+widget. If FILTER is nil, ACTION is always valid.")
 
 (defun custom-group-action (widget &optional event)
   "Show the menu for `custom-group' WIDGET.
 Optional EVENT is the location for the menu."
   (if (eq (widget-get widget :custom-state) 'hidden)
-      (progn 
-       (widget-put widget :custom-state 'unknown)
-       (custom-redraw widget))
+      (custom-toggle-hide widget)
     (let* ((completion-ignore-case t)
           (answer (widget-choose (custom-unlispify-tag-name
                                   (widget-get widget :value))
-                                 custom-group-menu
+                                 (custom-menu-filter custom-group-menu
+                                                     widget)
                                  event)))
       (if answer
          (funcall answer widget)))))
@@ -1764,7 +2252,7 @@ Optional EVENT is the location for the menu."
           (states (mapcar (lambda (child)
                             (widget-get child :custom-state))
                           children))
-          (magics custom-magic-alist)
+          (magics custom-group-magic-alist)
           (found 'factory))
       (while magics
        (let ((magic (car (car magics))))
@@ -1812,17 +2300,26 @@ Leave point at the location of the call, or after the last expression."
        (princ "\n"))
       (princ "(custom-set-variables")
       (mapatoms (lambda (symbol)
-                 (let ((value (get symbol 'saved-value)))
+                 (let ((value (get symbol 'saved-value))
+                       (requests (get symbol 'custom-requests))
+                       (now (not (or (get symbol 'factory-value)
+                                     (and (not (boundp symbol))
+                                          (not (get symbol 'force-value)))))))
                    (when value
                      (princ "\n '(")
                      (princ symbol)
                      (princ " ")
                      (prin1 (car value))
-                     (if (or (get symbol 'factory-value)
-                             (and (not (boundp symbol))
-                                  (not (get symbol 'force-value))))
-                         (princ ")")
-                       (princ " t)"))))))
+                     (cond (requests
+                            (if now
+                                (princ " t ")
+                              (princ " nil "))
+                            (prin1 requests)
+                            (princ ")"))
+                           (now
+                            (princ " t)"))
+                           (t
+                            (princ ")")))))))
       (princ ")")
       (unless (looking-at "\n")
        (princ "\n")))))
@@ -1835,9 +2332,21 @@ Leave point at the location of the call, or after the last expression."
       (unless (bolp)
        (princ "\n"))
       (princ "(custom-set-faces")
+      (let ((value (get 'default 'saved-face)))
+       ;; The default face must be first, since it affects the others.
+       (when value
+         (princ "\n '(default ")
+         (prin1 value)
+         (if (or (get 'default 'factory-face)
+                 (and (not (custom-facep 'default))
+                      (not (get 'default 'force-face))))
+             (princ ")")
+           (princ " t)"))))
       (mapatoms (lambda (symbol)
                  (let ((value (get symbol 'saved-face)))
-                   (when value
+                   (when (and (not (eq symbol 'default))
+                              ;; Don't print default face here.
+                              value)
                      (princ "\n '(")
                      (princ symbol)
                      (princ " ")
@@ -1851,6 +2360,22 @@ Leave point at the location of the call, or after the last expression."
       (unless (looking-at "\n")
        (princ "\n")))))
 
+;;;###autoload
+(defun custom-save-customized ()
+  "Save all user options which have been set in this session."
+  (interactive)
+  (mapatoms (lambda (symbol)
+             (let ((face (get symbol 'customized-face))
+                   (value (get symbol 'customized-value)))
+               (when face 
+                 (put symbol 'saved-face face)
+                 (put symbol 'customized-face nil))
+               (when value 
+                 (put symbol 'saved-value value)
+                 (put symbol 'customized-value nil)))))
+  ;; We really should update all custom buffers here.
+  (custom-save-all))
+
 ;;;###autoload
 (defun custom-save-all ()
   "Save all customizations in `custom-file'."
@@ -1862,10 +2387,43 @@ Leave point at the location of the call, or after the last expression."
 
 ;;; The Customize Menu.
 
-(defcustom custom-menu-nesting 2
-  "Maximum nesting in custom menus."
-  :type 'integer
-  :group 'customize)
+;;; Menu support
+
+(unless (string-match "XEmacs" emacs-version)
+  (defconst custom-help-menu '("Customize"
+                              ["Update menu..." custom-menu-update t]
+                              ["Group..." customize t]
+                              ["Variable..." customize-variable t]
+                              ["Face..." customize-face t]
+                              ["Saved..." customize-customized t]
+                              ["Apropos..." customize-apropos t])
+    ;; This menu should be identical to the one defined in `menu-bar.el'. 
+    "Customize menu")
+
+  (defun custom-menu-reset ()
+    "Reset customize menu."
+    (remove-hook 'custom-define-hook 'custom-menu-reset)
+    (define-key global-map [menu-bar help-menu customize-menu]
+      (cons (car custom-help-menu)
+           (easy-menu-create-keymaps (car custom-help-menu)
+                                     (cdr custom-help-menu)))))
+
+  (defun custom-menu-update (event)
+    "Update customize menu."
+    (interactive "e")
+    (add-hook 'custom-define-hook 'custom-menu-reset)
+    (let* ((emacs (widget-apply '(custom-group) :custom-menu 'emacs))
+          (menu `(,(car custom-help-menu)
+                  ,emacs
+                  ,@(cdr (cdr custom-help-menu)))))
+      (let ((map (easy-menu-create-keymaps (car menu) (cdr menu))))
+       (define-key global-map [menu-bar help-menu customize-menu]
+         (cons (car menu) map)))))
+
+  (defcustom custom-menu-nesting 2
+    "Maximum nesting in custom menus."
+    :type 'integer
+    :group 'customize))
 
 (defun custom-face-menu-create (widget symbol)
   "Ignoring WIDGET, create a menu entry for customization face SYMBOL."
@@ -1884,6 +2442,7 @@ Leave point at the location of the call, or after the last expression."
              `(custom-buffer-create '((,symbol custom-variable)))
              t))))
 
+;; Add checkboxes to boolean variable entries.
 (widget-put (get 'boolean 'widget-type)
            :custom-menu (lambda (widget symbol)
                           (vector (custom-unlispify-menu-entry symbol)
@@ -1906,17 +2465,15 @@ Leave point at the location of the call, or after the last expression."
     (let ((custom-menu-nesting (1- custom-menu-nesting)))
       (custom-menu-create symbol))))
 
-(defun custom-menu-create (symbol &optional name)
+;;;###autoload
+(defun custom-menu-create (symbol)
   "Create menu for customization group SYMBOL.
-If optional NAME is given, use that as the name of the menu. 
-Otherwise make up a name from SYMBOL.
 The menu is in a format applicable to `easy-menu-define'."
-  (unless name
-    (setq name (custom-unlispify-menu-entry symbol)))
-  (let ((item (vector name
-                     `(custom-buffer-create '((,symbol custom-group)))
-                     t)))
-    (if (and (>= custom-menu-nesting 0)
+  (let* ((item (vector (custom-unlispify-menu-entry symbol)
+                      `(custom-buffer-create '((,symbol custom-group)))
+                      t)))
+    (if (and (or (not (boundp 'custom-menu-nesting))
+                (>= custom-menu-nesting 0))
             (< (length (get symbol 'custom-group)) widget-menu-max-size))
        (let ((custom-prefix-list (custom-prefix-add symbol
                                                     custom-prefix-list)))
@@ -1933,58 +2490,77 @@ The menu is in a format applicable to `easy-menu-define'."
       item)))
 
 ;;;###autoload
-(defun custom-menu-update (event)
-  "Update customize menu."
-  (interactive "e")
-  (add-hook 'custom-define-hook 'custom-menu-reset)
-  (let* ((emacs (widget-apply '(custom-group) :custom-menu 'emacs))
-        (menu `(,(car custom-help-menu)
-                ,emacs
-                ,@(cdr (cdr custom-help-menu)))))
-    (let ((map (easy-menu-create-keymaps (car menu) (cdr menu))))
-      (define-key global-map [menu-bar help-menu customize-menu]
-       (cons (car menu) map)))))
-
-;;; Dependencies.
+(defun customize-menu-create (symbol &optional name)
+  "Return a customize menu for customization group SYMBOL.
+If optional NAME is given, use that as the name of the menu. 
+Otherwise the menu will be named `Customize'.
+The format is suitable for use with `easy-menu-define'."
+  (unless name
+    (setq name "Customize"))
+  (if (string-match "XEmacs" emacs-version)
+      ;; We can delay it under XEmacs.
+      `(,name
+       :filter (lambda (&rest junk)
+                 (cdr (custom-menu-create ',symbol))))
+    ;; But we must create it now under Emacs.
+    (cons name (cdr (custom-menu-create symbol)))))
 
-;;;###autoload
-(defun custom-make-dependencies ()
-  "Batch function to extract custom dependencies from .el files.
-Usage: emacs -batch *.el -f custom-make-dependencies > deps.el"
-  (let ((buffers (buffer-list)))
-    (while buffers
-      (set-buffer (car buffers))
-      (setq buffers (cdr buffers))
-      (let ((file (buffer-file-name)))
-       (when (and file (string-match "\\`\\(.*\\)\\.el\\'" file))
-         (goto-char (point-min))
-         (condition-case nil
-             (let ((name (file-name-nondirectory (match-string 1 file))))
-               (while t
-                 (let ((expr (read (current-buffer))))
-                   (when (and (listp expr)
-                              (memq (car expr) '(defcustom defface defgroup)))
-                     (eval expr)
-                     (put (nth 1 expr) 'custom-where name)))))
-           (error nil))))))
-  (mapatoms (lambda (symbol)
-             (let ((members (get symbol 'custom-group))
-                   item where found)
-               (when members
-                 (princ "(put '")
-                 (princ symbol)
-                 (princ " 'custom-loads '(")
-                 (while members
-                   (setq item (car (car members))
-                         members (cdr members)
-                         where (get item 'custom-where))
-                   (unless (or (null where)
-                               (member where found))
-                     (when found
-                       (princ " "))
-                     (prin1 where)
-                     (push where found)))
-                 (princ "))\n"))))))
+;;; The Custom Mode.
+
+(defvar custom-mode-map nil
+  "Keymap for `custom-mode'.")
+  
+(unless custom-mode-map
+  (setq custom-mode-map (make-sparse-keymap))
+  (set-keymap-parent custom-mode-map widget-keymap)
+  (define-key custom-mode-map "q" 'bury-buffer))
+
+(easy-menu-define custom-mode-customize-menu 
+    custom-mode-map
+  "Menu used to customize customization buffers."
+  (customize-menu-create 'customize))
+
+(easy-menu-define custom-mode-menu 
+    custom-mode-map
+  "Menu used in customization buffers."
+  `("Custom"
+    ["Set" custom-set t]
+    ["Save" custom-save t]
+    ["Reset to Current" custom-reset-current t]
+    ["Reset to Saved" custom-reset-saved t]
+    ["Reset to Standard Settings" custom-reset-factory t]
+    ["Info" (Info-goto-node "(custom)The Customization Buffer") t]))
+
+(defcustom custom-mode-hook nil
+  "Hook called when entering custom-mode."
+  :type 'hook
+  :group 'customize)
+
+(defun custom-mode ()
+  "Major mode for editing customization buffers.
+
+The following commands are available:
+
+Move to next button or editable field.     \\[widget-forward]
+Move to previous button or editable field. \\[widget-backward]
+Activate button under the mouse pointer.   \\[widget-button-click]
+Activate button under point.              \\[widget-button-press]
+Set all modifications.                    \\[custom-set]
+Make all modifications default.                   \\[custom-save]
+Reset all modified options.               \\[custom-reset-current]
+Reset all modified or set options.        \\[custom-reset-saved]
+Reset all options.                        \\[custom-reset-factory]
+
+Entry to this mode calls the value of `custom-mode-hook'
+if that value is non-nil."
+  (kill-all-local-variables)
+  (setq major-mode 'custom-mode
+       mode-name "Custom")
+  (use-local-map custom-mode-map)
+  (easy-menu-add custom-mode-customize-menu)
+  (easy-menu-add custom-mode-menu)
+  (make-local-variable 'custom-options)
+  (run-hooks 'custom-mode-hook))
 
 ;;; The End.