*** empty log message ***
[bpt/emacs.git] / lisp / cus-edit.el
index 7d545ba..c05c0d0 100644 (file)
@@ -1,11 +1,9 @@
-;;; cus-edit.el --- Tools for customization Emacs.
+;;; cus-edit.el --- Tools for customizing Emacs and Lisp packages.
 ;;
-;; Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+;; Copyright (C) 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Keywords: help, faces
-;; Version: 1.9908
-;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
 
 ;; This file is part of GNU Emacs.
 
 ;; 
 ;; See `custom.el'.
 
+;; No commands should have names starting with `custom-' because
+;; that interferes with completion.  Use `customize-' for commands
+;; that the user will run with M-x, and `Custom-' for interactive commands.
+
 ;;; Code:
 
 (require 'cus-face)
 (require 'wid-edit)
-(require 'easymenu)
-(eval-when-compile (require 'cl))
+(eval-when-compile
+  (defvar custom-versions-load-alist)) ; from cus-load
 
 (condition-case nil
     (require 'cus-load)
     (require 'cus-start)
   (error nil))
 
-(define-widget-keywords :custom-category :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-standard)
-
 (put 'custom-define-hook 'custom-type 'hook)
 (put 'custom-define-hook 'standard-value '(nil))
 (custom-add-to-group 'customize 'custom-define-hook 'custom-variable)
   :group 'external
   :group 'development)
 
+(defgroup convenience nil
+  "Convenience features for faster editing."
+  :group 'emacs)
+
 (defgroup programming nil
   "Support for programming in other languages."
   :group 'emacs)
   "Support editing files of data."
   :group 'emacs)
 
+(defgroup files nil
+  "Support editing files."
+  :group 'emacs)
+
 (defgroup wp nil
   "Word processing."
   :group 'emacs)
   "Support for on-line help systems."
   :group 'emacs)
 
+(defgroup multimedia nil
+  "Non-textual support, specifically images and sound."
+  :group 'emacs)
+
 (defgroup local nil
   "Code local to your site."
   :group 'emacs)
 
 (defgroup customize '((widgets custom-group))
   "Customization of the Customization support."
-  :link '(custom-manual "(custom)Top")
-  :link '(url-link :tag "Development Page" 
+  :link '(custom-manual "(elisp)Customization")
+  :link '(url-link :tag "(Old?) Development Page"
                   "http://www.dina.kvl.dk/~abraham/custom/")
   :prefix "custom-"
   :group 'help)
   :group 'customize
   :group 'faces)
 
+(defgroup custom-browse nil
+  "Control customize browser."
+  :prefix "custom-"
+  :group 'customize)
+
+(defgroup custom-buffer nil
+  "Control customize buffers."
+  :prefix "custom-"
+  :group 'customize)
+
+(defgroup custom-menu nil
+  "Control customize menus."
+  :prefix "custom-"
+  :group 'customize)
+
 (defgroup abbrev-mode nil
   "Word abbreviations mode."
   :group 'abbrev)
 
 (defgroup auto-save nil
   "Preventing accidential loss of data."
-  :group 'data)
+  :group 'files)
 
 (defgroup processes-basics nil
   "Basic stuff dealing with processes."
 (defun custom-quote (sexp)
   "Quote SEXP iff it is not self quoting."
   (if (or (memq sexp '(t nil))
-         (and (symbolp sexp)
-              (eq (aref (symbol-name sexp) 0) ?:))
+         (keywordp sexp)
          (and (listp sexp)
               (memq (car sexp) '(lambda)))
          (stringp sexp)
          (numberp sexp)
-         (and (fboundp 'characterp)
-              (characterp sexp)))
+         (vectorp sexp)
+;;;      (and (fboundp 'characterp)
+;;;           (characterp sexp))
+         )
       sexp
     (list 'quote sexp)))
 
 (defun custom-split-regexp-maybe (regexp)
   "If REGEXP is a string, split it to a list at `\\|'.
-You can get the original back with from the result with: 
+You can get the original back with from the result with:
   (mapconcat 'identity result \"\\|\")
 
 IF REGEXP is not a string, return it unchanged."
@@ -359,20 +383,20 @@ IF REGEXP is not a string, return it unchanged."
     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 
+     (setq val (completing-read
                (if (symbolp v)
                    (format "Customize option: (default %s) " v)
                  "Customize variable: ")
                obarray (lambda (symbol)
                          (and (boundp symbol)
                               (or (get symbol 'custom-type)
-                                  (user-variable-p symbol))))))
+                                  (get symbol 'custom-loads)
+                                  (get symbol 'standard-value)))) t))
      (list (if (equal val "")
               (if (symbolp v) v nil)
             (intern val)))))
@@ -383,7 +407,7 @@ 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 
+    (while menu
       (setq current (car menu)
            name (nth 0 current)
            action (nth 1 current)
@@ -397,15 +421,20 @@ WIDGET is the widget to apply the filter entries of MENU on."
 ;;; Unlispify.
 
 (defvar custom-prefix-list nil
-  "List of prefixes that should be ignored by `custom-unlispify'")
+  "List of prefixes that should be ignored by `custom-unlispify'.")
 
 (defcustom custom-unlispify-menu-entries t
   "Display menu entries as words instead of symbols if non nil."
-  :group 'customize
+  :group 'custom-menu
+  :type 'boolean)
+
+(defcustom custom-unlispify-remove-prefixes nil
+  "Non-nil means remove group prefixes from option names in buffer."
+  :group 'custom-menu
   :type 'boolean)
 
 (defun custom-unlispify-menu-entry (symbol &optional no-suffix)
-  "Convert symbol into a menu entry."
+  "Convert SYMBOL into a menu entry."
   (cond ((not custom-unlispify-menu-entries)
         (symbol-name symbol))
        ((get symbol 'custom-tag)
@@ -422,34 +451,35 @@ WIDGET is the widget to apply the filter entries of MENU on."
                      (re-search-forward "-p\\'" nil t))
             (replace-match "" t t)
             (goto-char (point-min)))
-          (let ((prefixes custom-prefix-list)
-                prefix)
-            (while prefixes
-              (setq prefix (car prefixes))
-              (if (search-forward prefix (+ (point) (length prefix)) t)
-                  (progn 
-                    (setq prefixes nil)
-                    (delete-region (point-min) (point)))
-                (setq prefixes (cdr prefixes)))))
+          (if custom-unlispify-remove-prefixes
+              (let ((prefixes custom-prefix-list)
+                    prefix)
+                (while prefixes
+                  (setq prefix (car prefixes))
+                  (if (search-forward prefix (+ (point) (length prefix)) t)
+                      (progn
+                        (setq prefixes nil)
+                        (delete-region (point-min) (point)))
+                    (setq prefixes (cdr prefixes))))))
           (subst-char-in-region (point-min) (point-max) ?- ?\  t)
           (capitalize-region (point-min) (point-max))
-          (unless no-suffix 
+          (unless no-suffix
             (goto-char (point-max))
             (insert "..."))
           (buffer-string)))))
 
 (defcustom custom-unlispify-tag-names t
   "Display tag names as words instead of symbols if non nil."
-  :group 'customize
+  :group 'custom-buffer
   :type 'boolean)
 
 (defun custom-unlispify-tag-name (symbol)
-  "Convert symbol into a menu entry."
+  "Convert SYMBOL into a menu entry."
   (let ((custom-unlispify-menu-entries custom-unlispify-tag-names))
     (custom-unlispify-menu-entry symbol t)))
 
 (defun custom-prefix-add (symbol prefixes)
-  ;; Addd SYMBOL to list of ignored PREFIXES.
+  "Add SYMBOL to list of ignored PREFIXES."
   (cons (or (get symbol 'custom-prefix)
            (concat (symbol-name symbol) "-"))
        prefixes))
@@ -467,10 +497,10 @@ WIDGET is the widget to apply the filter entries of MENU on."
     ("-alist\\'" (repeat (cons sexp sexp))))
   "Alist of (MATCH TYPE).
 
-MATCH should be a regexp matching the name of a symbol, and TYPE should 
+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. 
+used.
 
 This is used for guessing the type of variables not declared with
 customize."
@@ -493,7 +523,7 @@ customize."
 
 (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 
+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)
@@ -507,7 +537,7 @@ if that fails, the doc string with `custom-guess-doc-alist'."
     (unless found
       (let ((doc (documentation-property symbol 'variable-documentation))
            (docs custom-guess-doc-alist))
-       (when doc 
+       (when doc
          (while docs
            (setq current (car docs)
                  docs (cdr docs))
@@ -518,81 +548,114 @@ if that fails, the doc string with `custom-guess-doc-alist'."
 
 ;;; Sorting.
 
-(defcustom custom-buffer-sort-predicate 'custom-buffer-sort-alphabetically
-  "Function used for sorting group members in buffers.
-The value should be useful as a predicate for `sort'.  
-The list to be sorted is the value of the groups `custom-group' property."
-  :type '(radio (function-item custom-buffer-sort-alphabetically)
-               (function :tag "Other"))
-  :group 'customize)
-
-(defun custom-buffer-sort-alphabetically (a b)
-  "Return t iff is A should be before B.
-A and B should be members of a `custom-group' property. 
-The members are sorted alphabetically, except that all groups are
-sorted after all non-groups."
-  (cond ((and (eq (nth 1 a) 'custom-group) 
-             (not (eq (nth 1 b) 'custom-group)))
-        nil)
-       ((and (eq (nth 1 b) 'custom-group) 
-             (not (eq (nth 1 a) 'custom-group)))
-        t)
-       (t
-        (string-lessp (symbol-name (nth 0 a)) (symbol-name (nth 0 b))))))
-
-(defcustom custom-menu-sort-predicate 'custom-menu-sort-alphabetically
-  "Function used for sorting group members in menus.
-The value should be useful as a predicate for `sort'.  
-The list to be sorted is the value of the groups `custom-group' property."
-  :type '(radio (function-item custom-menu-sort-alphabetically)
-               (function :tag "Other"))
-  :group 'customize)
+(defcustom custom-browse-sort-alphabetically nil
+  "If non-nil, sort members of each customization group alphabetically."
+  :type 'boolean
+  :group 'custom-browse)
+
+(defcustom custom-browse-order-groups nil
+  "If non-nil, order group members within each customization group.
+If `first', order groups before non-groups.
+If `last', order groups after non-groups."
+  :type '(choice (const first)
+                (const last)
+                (const :tag "none" nil))
+  :group 'custom-browse)
+
+(defcustom custom-browse-only-groups nil
+  "If non-nil, show group members only within each customization group."
+  :type 'boolean
+  :group 'custom-browse)
 
-(defun custom-menu-sort-alphabetically (a b)
-  "Return t iff is A should be before B.
-A and B should be members of a `custom-group' property. 
-The members are sorted alphabetically, except that all groups are
-sorted before all non-groups."
-  (cond ((and (eq (nth 1 a) 'custom-group) 
-             (not (eq (nth 1 b) 'custom-group)))
-        t)
-       ((and (eq (nth 1 b) 'custom-group) 
-             (not (eq (nth 1 a) 'custom-group)))
-        nil)
-       (t
-        (string-lessp (symbol-name (nth 0 a)) (symbol-name (nth 0 b))))))
+(defcustom custom-buffer-sort-alphabetically nil
+  "If non-nil, sort members of each customization group alphabetically."
+  :type 'boolean
+  :group 'custom-buffer)
+
+(defcustom custom-buffer-order-groups 'last
+  "If non-nil, order group members within each customization group.
+If `first', order groups before non-groups.
+If `last', order groups after non-groups."
+  :type '(choice (const first)
+                (const last)
+                (const :tag "none" nil))
+  :group 'custom-buffer)
+
+(defcustom custom-menu-sort-alphabetically nil
+  "If non-nil, sort members of each customization group alphabetically."
+  :type 'boolean
+  :group 'custom-menu)
+
+(defcustom custom-menu-order-groups 'first
+  "If non-nil, order group members within each customization group.
+If `first', order groups before non-groups.
+If `last', order groups after non-groups."
+  :type '(choice (const first)
+                (const last)
+                (const :tag "none" nil))
+  :group 'custom-menu)
+
+;;;###autoload (add-hook 'same-window-regexps "\\`\\*Customiz.*\\*\\'")
+
+(defun custom-sort-items (items sort-alphabetically order-groups)
+  "Return a sorted copy of ITEMS.
+ITEMS should be a `custom-group' property.
+If SORT-ALPHABETICALLY non-nil, sort alphabetically.
+If ORDER-GROUPS is `first' order groups before non-groups, if `last' order
+groups after non-groups, if nil do not order groups at all."
+  (sort (copy-sequence items)
+   (lambda (a b)
+     (let ((typea (nth 1 a)) (typeb (nth 1 b))
+          (namea (nth 0 a)) (nameb (nth 0 b)))
+       (cond ((not order-groups)
+             ;; Since we don't care about A and B order, maybe sort.
+             (when sort-alphabetically
+               (string-lessp namea nameb)))
+            ((eq typea 'custom-group)
+             ;; If B is also a group, maybe sort.  Otherwise, order A and B.
+             (if (eq typeb 'custom-group)
+                 (when sort-alphabetically
+                   (string-lessp namea nameb))
+               (eq order-groups 'first)))
+            ((eq typeb 'custom-group)
+             ;; Since A cannot be a group, order A and B.
+             (eq order-groups 'last))
+            (sort-alphabetically
+             ;; Since A and B cannot be groups, sort.
+             (string-lessp namea nameb)))))))
 
 ;;; Custom Mode Commands.
 
 (defvar custom-options nil
   "Customization widgets in the current buffer.")
 
-(defun custom-set ()
+(defun Custom-set ()
   "Set changes in all modified options."
   (interactive)
   (let ((children custom-options))
-    (mapcar (lambda (child)
-             (when (eq (widget-get child :custom-state) 'modified)
-               (widget-apply child :custom-set)))
+    (mapc (lambda (child)
+           (when (eq (widget-get child :custom-state) 'modified)
+             (widget-apply child :custom-set)))
            children)))
 
-(defun custom-save ()
+(defun Custom-save ()
   "Set all modified group members and save them."
   (interactive)
   (let ((children custom-options))
-    (mapcar (lambda (child)
-             (when (memq (widget-get child :custom-state) '(modified set))
-               (widget-apply child :custom-save)))
+    (mapc (lambda (child)
+           (when (memq (widget-get child :custom-state)
+                       '(modified set changed rogue))
+             (widget-apply child :custom-save)))
            children))
   (custom-save-all))
 
-(defvar custom-reset-menu 
-  '(("Current" . custom-reset-current)
-    ("Saved" . custom-reset-saved)
-    ("Standard Settings" . custom-reset-standard))
+(defvar custom-reset-menu
+  '(("Current" . Custom-reset-current)
+    ("Saved" . Custom-reset-saved)
+    ("Erase Customization (use standard settings)" . Custom-reset-standard))
   "Alist of actions for the `Reset' button.
 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
+Lisp function taking the widget as an element which will be called
 when the action is chosen.")
 
 (defun custom-reset (event)
@@ -604,36 +667,45 @@ when the action is chosen.")
     (if answer
        (funcall answer))))
 
-(defun custom-reset-current (&rest ignore)
+(defun Custom-reset-current (&rest ignore)
   "Reset all modified group members to their current value."
   (interactive)
   (let ((children custom-options))
-    (mapcar (lambda (child)
-             (when (eq (widget-get child :custom-state) 'modified)
-               (widget-apply child :custom-reset-current)))
+    (mapc (lambda (widget)
+           (and (default-boundp (widget-value widget))
+                (if (memq (widget-get widget :custom-state)
+                          '(modified changed))
+                    (widget-apply widget :custom-reset-current))))
            children)))
 
-(defun custom-reset-saved (&rest ignore)
+(defun Custom-reset-saved (&rest ignore)
   "Reset all modified or set group members to their saved value."
   (interactive)
   (let ((children custom-options))
-    (mapcar (lambda (child)
-             (when (eq (widget-get child :custom-state) 'modified)
-               (widget-apply child :custom-reset-current)))
+    (mapc (lambda (widget)
+           (and (get (widget-value widget) 'saved-value)
+                (if (memq (widget-get widget :custom-state)
+                          '(modified set changed rogue))
+                    (widget-apply widget :custom-reset-saved))))
            children)))
 
-(defun custom-reset-standard (&rest ignore)
-  "Reset all modified, set, or saved group members to their standard settings."
+(defun Custom-reset-standard (&rest ignore)
+  "Erase all customization (either current or saved) for the group members.
+The immediate result is to restore them to their standard settings.
+This operation eliminates any saved settings for the group members,
+making them as if they had never been customized at all."
   (interactive)
   (let ((children custom-options))
-    (mapcar (lambda (child)
-             (when (eq (widget-get child :custom-state) 'modified)
-               (widget-apply child :custom-reset-current)))
+    (mapc (lambda (widget)
+           (and (get (widget-value widget) 'standard-value)
+                (if (memq (widget-get widget :custom-state)
+                          '(modified set changed saved rogue))
+                    (widget-apply widget :custom-reset-standard))))
            children)))
 
 ;;; The Customize Commands
 
-(defun custom-prompt-variable (prompt-var prompt-val)
+(defun custom-prompt-variable (prompt-var prompt-val &optional comment)
   "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
@@ -643,10 +715,13 @@ 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."
+`:prompt-value' property of that widget will be used for reading the value.
+
+If optional COMMENT argument is non nil, also prompt for a comment and return
+it as the third element in the list."
   (let* ((var (read-variable prompt-var))
-        (minibuffer-help-form '(describe-variable var)))
-    (list var
+        (minibuffer-help-form '(describe-variable var))
+        (val
          (let ((prop (get var 'variable-interactive))
                (type (get var 'custom-type))
                (prompt (format prompt-val var)))
@@ -665,24 +740,35 @@ If the variable has a `custom-type' property, it must be a widget and the
                                            (symbol-value var))
                                        (not (boundp var))))
                  (t
-                  (eval-minibuffer prompt)))))))
+                  (eval-minibuffer prompt))))))
+    (if comment
+       (list var val
+             (read-string "Comment: " (get var 'variable-comment)))
+      (list var val))))
 
 ;;;###autoload
-(defun custom-set-value (var val)
+(defun customize-set-value (var val &optional comment)
   "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." 
+`:prompt-value' property of that widget will be used for reading the value.
+
+If given a prefix (or a COMMENT argument), also prompt for a comment."
   (interactive (custom-prompt-variable "Set variable: "
-                                      "Set %s to value: "))
+                                      "Set %s to value: "
+                                      current-prefix-arg))
    
-  (set var val))
+  (set var val)
+  (cond ((string= comment "")
+        (put var 'variable-comment nil))
+       (comment
+        (put var 'variable-comment comment))))
 
 ;;;###autoload
-(defun custom-set-variable (var val)
+(defun customize-set-variable (variable value &optional comment)
   "Set the default for VARIABLE to VALUE.  VALUE is a Lisp object.
 
 If VARIABLE has a `custom-set' property, that is used for setting
@@ -695,11 +781,49 @@ 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. " 
+`:prompt-value' property of that widget will be used for reading the value.
+
+If given a prefix (or a COMMENT argument), also prompt for a comment."
   (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))))
+                                      "Set customized value for %s to: "
+                                      current-prefix-arg))
+  (funcall (or (get variable 'custom-set) 'set-default) variable value)
+  (put variable 'customized-value (list (custom-quote value)))
+  (cond ((string= comment "")
+        (put variable 'variable-comment nil)
+        (put variable 'customized-variable-comment nil))
+       (comment
+        (put variable 'variable-comment comment)
+        (put variable 'customized-variable-comment comment))))
+
+;;;###autoload
+(defun customize-save-variable (var value &optional comment)
+  "Set the default for VARIABLE to VALUE, and save it for future sessions.
+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.
+
+If given a prefix (or a COMMENT argument), also prompt for a comment."
+  (interactive (custom-prompt-variable "Set and ave variable: "
+                                      "Set and save value for %s as: "
+                                      current-prefix-arg))
+  (funcall (or (get var 'custom-set) 'set-default) var value)
+  (put var 'saved-value (list (custom-quote value)))
+  (cond ((string= comment "")
+        (put var 'variable-comment nil)
+        (put var 'saved-variable-comment nil))
+       (comment
+        (put var 'variable-comment comment)
+        (put var 'saved-variable-comment comment)))
+  (custom-save-all))
 
 ;;;###autoload
 (defun customize ()
@@ -713,36 +837,55 @@ are shown; the contents of those subgroups are initially hidden."
 ;;;###autoload
 (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)))
-
+  (interactive (list (let ((completion-ignore-case t))
+                      (completing-read "Customize group: (default emacs) "
+                                       obarray
+                                       (lambda (symbol)
+                                         (or (get symbol 'custom-loads)
+                                             (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))
-                       (format "*Customize Group: %s*"
-                               (custom-unlispify-tag-name group))))
+  (or (get group 'custom-group)
+      (custom-load-symbol group))
+  (let ((name (format "*Customize Group: %s*"
+                     (custom-unlispify-tag-name group))))
+    (if (get-buffer name)
+       (pop-to-buffer name)
+      (custom-buffer-create (list (list group 'custom-group))
+                           name
+                           (concat " for group "
+                                   (custom-unlispify-tag-name group))))))
 
 ;;;###autoload
-(defun customize-group-other-window (symbol)
-  "Customize SYMBOL, which must be a customization group."
-  (interactive (list (completing-read "Customize group: (default emacs) "
-                                     obarray 
-                                     (lambda (symbol)
-                                       (get symbol 'custom-group))
-                                     t)))
-
-  (when (stringp symbol)
-    (if (string-equal "" symbol)
-       (setq symbol 'emacs)
-      (setq symbol (intern symbol))))
-  (custom-buffer-create-other-window
-   (list (list symbol 'custom-group))
-   (format "*Customize Group: %s*" (custom-unlispify-tag-name symbol))))
+(defun customize-group-other-window (group)
+  "Customize GROUP, which must be a customization group."
+  (interactive (list (let ((completion-ignore-case t))
+                      (completing-read "Customize group: (default emacs) "
+                                       obarray
+                                       (lambda (symbol)
+                                         (or (get symbol 'custom-loads)
+                                             (get symbol 'custom-group)))
+                                       t))))
+  (when (stringp group)
+    (if (string-equal "" group)
+       (setq group 'emacs)
+      (setq group (intern group))))
+  (or (get group 'custom-group)
+      (custom-load-symbol group))
+  (let ((name (format "*Customize Group: %s*"
+                     (custom-unlispify-tag-name group))))
+    (if (get-buffer name)
+       (let ((window (selected-window)))
+         (pop-to-buffer name)
+         (select-window window))
+      (custom-buffer-create-other-window
+       (list (list group 'custom-group))
+       name
+       (concat " for group "
+              (custom-unlispify-tag-name group))))))
 
 ;;;###autoload
 (defalias 'customize-variable 'customize-option)
@@ -751,10 +894,125 @@ are shown; the contents of those subgroups are initially hidden."
 (defun customize-option (symbol)
   "Customize SYMBOL, which must be a user option variable."
   (interactive (custom-variable-prompt))
+  ;; If we don't have SYMBOL's real definition loaded,
+  ;; try to load it.
+  (unless (get symbol 'custom-type)
+    (let ((loaddefs-file (locate-library "loaddefs.el" t))
+         file)
+      ;; See if it is autoloaded from some library.
+      (when loaddefs-file
+       (with-temp-buffer
+         (insert-file-contents loaddefs-file)
+         (when (re-search-forward (concat "^(defvar " (symbol-name symbol))
+                                  nil t)
+           (search-backward "\n;;; Generated autoloads from ")
+           (goto-char (match-end 0))
+           (setq file (buffer-substring (point)
+                                        (progn (end-of-line) (point)))))))
+      ;; If it is, load that library.
+      (when file
+       (when (string-match "\\.el\\'" file)
+         (setq file (substring file 0 (match-beginning 0))))
+       (load file))))
+  (unless (get symbol 'custom-type)
+    (error "Variable %s cannot be customized" symbol))
   (custom-buffer-create (list (list symbol 'custom-variable))
                        (format "*Customize Option: %s*"
                                (custom-unlispify-tag-name symbol))))
 
+(defvar customize-changed-options-previous-release "20.2"
+  "Version for `customize-changed-options' to refer back to by default.")
+
+;;;###autoload
+(defun customize-changed-options (since-version)
+  "Customize all user option variables changed in Emacs itself.
+This includes new user option variables and faces, and new
+customization groups, as well as older options and faces whose default
+values have changed since the previous major Emacs release.
+
+With argument SINCE-VERSION (a string), customize all user option
+variables that were added (or their meanings were changed) since that
+version."
+
+  (interactive "sCustomize options changed, since version (default all versions): ")
+  (if (equal since-version "")
+      (setq since-version nil)
+    (unless (condition-case nil
+               (numberp (read since-version))
+             (error nil))
+      (signal 'wrong-type-argument (list 'numberp since-version))))
+  (unless since-version
+    (setq since-version customize-changed-options-previous-release))
+  (let ((found nil)
+       (versions nil))
+    (mapatoms (lambda (symbol)
+               (and (or (boundp symbol)
+                        ;; For variables not yet loaded.
+                        (get symbol 'standard-value)
+                        ;; For groups the previous test fails, this one
+                        ;; could be used to determine if symbol is a
+                        ;; group. Is there a better way for this?
+                        (get symbol 'group-documentation))
+                    (let ((version (get symbol 'custom-version)))
+                      (and version
+                           (or (null since-version)
+                               (customize-version-lessp since-version version))
+                           (if (member version versions)
+                               t
+                             ;;; Collect all versions that we use.
+                             (push version versions))))
+                    (setq found
+                          ;; We have to set the right thing here,
+                          ;; depending if we have a group or a
+                          ;; variable.
+                          (if (get  symbol 'group-documentation)
+                              (cons (list symbol 'custom-group) found)
+                            (cons (list symbol 'custom-variable) found))))))
+    (if (not found)
+       (error "No user option defaults have been changed since Emacs %s"
+              since-version)
+      (let ((flist nil))
+       (while versions
+         (push (copy-sequence
+                (cdr (assoc (car versions)  custom-versions-load-alist)))
+               flist)
+         (setq versions (cdr versions)))
+       (put 'custom-versions-load-alist 'custom-loads
+            ;; Get all the files that correspond to element from the
+            ;; VERSIONS list. This could use some simplification.
+            (apply 'nconc flist)))
+      ;; Because we set all the files needed to be loaded as a
+      ;; `custom-loads' property to `custom-versions-load-alist' this
+      ;; call will actually load them.
+      (custom-load-symbol 'custom-versions-load-alist)
+      ;; Clean up
+      (put 'custom-versions-load-alist 'custom-loads nil)
+      (custom-buffer-create (custom-sort-items found t 'first)
+                           "*Customize Changed Options*"))))
+
+(defun customize-version-lessp (version1 version2)
+  ;; Why are the versions strings, and given that they are, why aren't
+  ;; they converted to numbers and compared as such here?  -- fx
+
+  ;; In case someone made a mistake and left out the quotes
+  ;; in the :version value.
+  (if (numberp version2)
+      (setq version2 (prin1-to-string version2)))
+  (let (major1 major2 minor1 minor2)
+    (string-match "\\([0-9]+\\)\\(\\.\\([0-9]+\\)\\)?" version1)
+    (setq major1 (read (or (match-string 1 version1)
+                          "0")))
+    (setq minor1 (read (or (match-string 3 version1)
+                          "0")))
+    (string-match "\\([0-9]+\\)\\(\\.\\([0-9]+\\)\\)?" version2)
+    (setq major2 (read (or (match-string 1 version2)
+                          "0")))
+    (setq minor2 (read (or (match-string 3 version2)
+                          "0")))
+    (or (< major1 major2)
+       (and (= major1 major2)
+            (< minor1 minor2)))))
+  
 ;;;###autoload
 (defalias 'customize-variable-other-window 'customize-option-other-window)
 
@@ -771,20 +1029,17 @@ Show the buffer in another window, but don't select it."
 (defun customize-face (&optional symbol)
   "Customize SYMBOL, which should be a face name or nil.
 If SYMBOL is nil, customize all faces."
-  (interactive (list (completing-read "Customize face: (default all) " 
-                                     obarray 'custom-facep)))
+  (interactive (list (completing-read "Customize face: (default all) "
+                                     obarray 'custom-facep t)))
   (if (or (null symbol) (and (stringp symbol) (zerop (length symbol))))
-      (let ((found nil))
-       (message "Looking for faces...")
-       (mapcar (lambda (symbol)
-                 (setq found (cons (list symbol 'custom-face) found)))
-               (nreverse (mapcar 'intern 
-                                 (sort (mapcar 'symbol-name (face-list))
-                                       'string<))))
-                       
-       (custom-buffer-create found "*Customize Faces*"))
-    (if (stringp symbol)
-       (setq symbol (intern symbol)))
+      (custom-buffer-create (custom-sort-items
+                            (mapcar (lambda (symbol)
+                                      (list symbol 'custom-face))
+                                    (face-list))
+                            t nil)
+                           "*Customize Faces*")
+    (when (stringp symbol)
+      (setq symbol (intern symbol)))
     (unless (symbolp symbol)
       (error "Should be a symbol %S" symbol))
     (custom-buffer-create (list (list symbol 'custom-face))
@@ -793,16 +1048,16 @@ If SYMBOL is nil, customize all faces."
 
 ;;;###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)))
+  "Show customization buffer for face SYMBOL in other window."
+  (interactive (list (completing-read "Customize face: "
+                                     obarray 'custom-facep t)))
   (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 
+    (custom-buffer-create-other-window
      (list (list symbol 'custom-face))
      (format "*Customize Face: %s*" (custom-unlispify-tag-name symbol)))))
 
@@ -812,16 +1067,18 @@ If SYMBOL is nil, customize all faces."
   (interactive)
   (let ((found nil))
     (mapatoms (lambda (symbol)
-               (and (get symbol 'customized-face)
+               (and (or (get symbol 'customized-face)
+                        (get symbol 'customized-face-comment))
                     (custom-facep symbol)
-                    (setq found (cons (list symbol 'custom-face) found)))
-               (and (get symbol 'customized-value)
+                    (push (list symbol 'custom-face) found))
+               (and (or (get symbol 'customized-value)
+                        (get symbol 'customized-variable-comment))
                     (boundp symbol)
-                    (setq found
-                          (cons (list symbol 'custom-variable) found)))))
-    (if found 
-       (custom-buffer-create found "*Customize Customized*")
-      (error "No customized user options"))))
+                    (push (list symbol 'custom-variable) found))))
+    (if (not found)
+       (error "No customized user options")
+      (custom-buffer-create (custom-sort-items found t nil)
+                           "*Customize Customized*"))))
 
 ;;;###autoload
 (defun customize-saved ()
@@ -829,46 +1086,103 @@ If SYMBOL is nil, customize all faces."
   (interactive)
   (let ((found nil))
     (mapatoms (lambda (symbol)
-               (and (get symbol 'saved-face)
+               (and (or (get symbol 'saved-face)
+                        (get symbol 'saved-face-comment))
                     (custom-facep symbol)
-                    (setq found (cons (list symbol 'custom-face) found)))
-               (and (get symbol 'saved-value)
+                    (push (list symbol 'custom-face) found))
+               (and (or (get symbol 'saved-value)
+                        (get symbol 'saved-variable-comment))
                     (boundp symbol)
-                    (setq found
-                          (cons (list symbol 'custom-variable) found)))))
-    (if found 
-       (custom-buffer-create found "*Customize Saved*")
-      (error "No saved user options"))))
+                    (push (list symbol 'custom-variable) found))))
+    (if (not found )
+       (error "No saved user options")
+      (custom-buffer-create (custom-sort-items found t nil)
+                           "*Customize Saved*"))))
 
 ;;;###autoload
 (defun customize-apropos (regexp &optional all)
   "Customize all user options matching REGEXP.
-If ALL (e.g., started with a prefix key), include options which are not
-user-settable."
+If ALL is `options', include only options.
+If ALL is `faces', include only faces.
+If ALL is `groups', include only groups.
+If ALL is t (interactively, with prefix arg), include options which are not
+user-settable, as well as faces and groups."
   (interactive "sCustomize regexp: \nP")
   (let ((found nil))
     (mapatoms (lambda (symbol)
                (when (string-match regexp (symbol-name symbol))
-                 (when (get symbol 'custom-group)
-                   (setq found (cons (list symbol 'custom-group) found)))
-                 (when (custom-facep symbol)
-                   (setq found (cons (list symbol 'custom-face) found)))
-                 (when (and (boundp symbol)
+                 (when (and (not (memq all '(faces options)))
+                            (get symbol 'custom-group))
+                   (push (list symbol 'custom-group) found))
+                 (when (and (not (memq all '(options groups)))
+                            (custom-facep symbol))
+                   (push (list symbol 'custom-face) found))
+                 (when (and (not (memq all '(groups faces)))
+                            (boundp symbol)
                             (or (get symbol 'saved-value)
                                 (get symbol 'standard-value)
-                                (if all
-                                    (get symbol 'variable-documentation)
-                                  (user-variable-p symbol))))
-                   (setq found
-                         (cons (list symbol 'custom-variable) found))))))
-    (if found 
-       (custom-buffer-create found "*Customize Apropos*")
-      (error "No matches"))))
+                                (if (memq all '(nil options))
+                                    (user-variable-p symbol)
+                                  (get symbol 'variable-documentation))))
+                   (push (list symbol 'custom-variable) found)))))
+    (if (not found)
+       (error "No matches")
+      (custom-buffer-create (custom-sort-items found t
+                                              custom-buffer-order-groups)
+                           "*Customize Apropos*"))))
+
+;;;###autoload
+(defun customize-apropos-options (regexp &optional arg)
+  "Customize all user options matching REGEXP.
+With prefix arg, include options which are not user-settable."
+  (interactive "sCustomize regexp: \nP")
+  (customize-apropos regexp (or arg 'options)))
+
+;;;###autoload
+(defun customize-apropos-faces (regexp)
+  "Customize all user faces matching REGEXP."
+  (interactive "sCustomize regexp: \n")
+  (customize-apropos regexp 'faces))
+
+;;;###autoload
+(defun customize-apropos-groups (regexp)
+  "Customize all user groups matching REGEXP."
+  (interactive "sCustomize regexp: \n")
+  (customize-apropos regexp 'groups))
 
 ;;; Buffer.
 
+(defcustom custom-buffer-style 'links
+  "Control the presentation style for customization buffers.
+The value should be a symbol, one of:
+
+brackets: groups nest within each other with big horizontal brackets.
+links: groups have links to subgroups."
+  :type '(radio (const brackets)
+               (const links))
+  :group 'custom-buffer)
+
+;; If we pass BUFFER to `bury-buffer', the buffer isn't removed from
+;; the window.
+(defun custom-bury-buffer (buffer)
+  (bury-buffer))
+
+(defcustom custom-buffer-done-function 'custom-bury-buffer
+  "*Function called to remove a Custom buffer when the user is done with it.
+Called with one argument, the buffer to remove."
+  :type '(choice (function-item :tag "Bury buffer" custom-bury-buffer)
+                (function-item :tag "Kill buffer" kill-buffer)
+                (function :tag "Other"))
+  :version "21.1"
+  :group 'custom-buffer)
+
+(defcustom custom-buffer-indent 3
+  "Number of spaces to indent nested groups."
+  :type 'integer
+  :group 'custom-buffer)
+
 ;;;###autoload
-(defun custom-buffer-create (options &optional name)
+(defun custom-buffer-create (options &optional name description)
   "Create a buffer containing OPTIONS.
 Optional NAME is the name of the buffer.
 OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
@@ -876,11 +1190,11 @@ SYMBOL is a customization option, and WIDGET is a widget for editing
 that option."
   (unless name (setq name "*Customization*"))
   (kill-buffer (get-buffer-create name))
-  (switch-to-buffer (get-buffer-create name))
-  (custom-buffer-create-internal options))
+  (pop-to-buffer (get-buffer-create name))
+  (custom-buffer-create-internal options description))
 
 ;;;###autoload
-(defun custom-buffer-create-other-window (options &optional name)
+(defun custom-buffer-create-other-window (options &optional name description)
   "Create a buffer containing OPTIONS.
 Optional NAME is the name of the buffer.
 OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where
@@ -888,74 +1202,105 @@ SYMBOL is a customization option, and WIDGET is a widget for editing
 that option."
   (unless name (setq name "*Customization*"))
   (kill-buffer (get-buffer-create name))
-  (let ((window (selected-window)))
-    (switch-to-buffer-other-window (get-buffer-create name))
-    (custom-buffer-create-internal options)
+  (let ((window (selected-window))
+       (pop-up-windows t)
+       (special-display-buffer-names nil)
+       (special-display-regexps nil)
+       (same-window-buffer-names nil)
+       (same-window-regexps nil))
+    (pop-to-buffer (get-buffer-create name))
+    (custom-buffer-create-internal options description)
     (select-window window)))
 
 (defcustom custom-reset-button-menu nil
   "If non-nil, only show a single reset button in customize buffers.
 This button will have a menu with all three reset operations."
   :type 'boolean
-  :group 'customize)
+  :group 'custom-buffer)
+
+(defun Custom-buffer-done (&rest ignore)
+  "Remove current buffer by calling `custom-buffer-done-function'."
+  (interactive)
+  (funcall custom-buffer-done-function (current-buffer)))
+
+(defcustom custom-raised-buttons (not (equal (face-valid-attribute-values :box)
+                                            '(("unspecified" . unspecified))))
+  "If non-nil, indicate active buttons in a `raised-button' style.
+Otherwise use brackets."
+  :type 'boolean
+  :version "21.1"
+  :group 'custom-buffer)
 
-(defun custom-buffer-create-internal (options)
+(defun custom-buffer-create-internal (options &optional description)
   (message "Creating customization buffer...")
   (custom-mode)
-  (widget-insert "This is a customization buffer.
-Push RET or click mouse-2 on the word ")
-  (widget-create 'info-link 
-                :tag "help"
+  (widget-insert "This is a customization buffer")
+  (if description
+      (widget-insert description))
+  (widget-insert (format ".
+%s show active fields; type RET or click mouse-1
+on an active field to invoke its action.  Editing an option value
+changes the text in the buffer; invoke the State button and
+choose the Set operation to set the option value.
+Invoke " (if custom-raised-buttons
+            "`Raised' buttons"
+            "Square brackets")))
+  (widget-create 'info-link
+                :tag "Help"
                 :help-echo "Read the online help."
                 "(emacs)Easy Customization")
   (widget-insert " for more information.\n\n")
   (message "Creating customization buttons...")
+  (widget-insert "Operate on everything in this buffer:\n ")
   (widget-create 'push-button
-                :tag "Set"
-                :help-echo "Set all modifications for this session."
+                :tag "Set for Current Session"
+                :help-echo "\
+Make your editing in this buffer take effect for this session."
                 :action (lambda (widget &optional event)
-                          (custom-set)))
+                          (Custom-set)))
   (widget-insert " ")
   (widget-create 'push-button
-                :tag "Save"
+                :tag "Save for Future Sessions"
                 :help-echo "\
-Make the modifications default for future sessions."
+Make your editing in this buffer take effect for future Emacs sessions."
                 :action (lambda (widget &optional event)
-                          (custom-save)))
-  (widget-insert " ")
+                          (Custom-save)))
   (if custom-reset-button-menu
-      (widget-create 'push-button
-                    :tag "Reset"
-                    :help-echo "Show a menu with reset operations."
-                    :mouse-down-action (lambda (&rest junk) t)
-                    :action (lambda (widget &optional event)
-                              (custom-reset event)))
+      (progn
+       (widget-insert " ")
+       (widget-create 'push-button
+                      :tag "Reset"
+                      :help-echo "Show a menu with reset operations."
+                      :mouse-down-action (lambda (&rest junk) t)
+                      :action (lambda (widget &optional event)
+                                (custom-reset event))))
+    (widget-insert "\n ")
     (widget-create 'push-button
                   :tag "Reset"
                   :help-echo "\
-Reset all visible items in this buffer to their current settings."
-                  :action 'custom-reset-current)
+Reset all edited text in this buffer to reflect current values."
+                  :action 'Custom-reset-current)
     (widget-insert " ")
     (widget-create 'push-button
                   :tag "Reset to Saved"
                   :help-echo "\
-Reset all visible items in this buffer to their saved settings."
-                  :action 'custom-reset-saved)
+Reset all values in this buffer to their saved settings."
+                  :action 'Custom-reset-saved)
     (widget-insert " ")
     (widget-create 'push-button
-                  :tag "Reset to Standard"
+                  :tag "Erase Customization"
                   :help-echo "\
-Reset all visible items in this buffer to their standard settings."
-                  :action 'custom-reset-standard))
-  (widget-insert " ")
+Un-customize all values in this buffer.  They get their standard settings."
+                  :action 'Custom-reset-standard))
+  (widget-insert "   ")
   (widget-create 'push-button
-                :tag "Done"
-                :help-echo "Bury the buffer."
-                :action (lambda (widget &optional event)
-                          (bury-buffer)))
+                :tag "Finish"
+                :help-echo "Bury or kill the buffer."
+                :action #'Custom-buffer-done)
   (widget-insert "\n\n")
   (message "Creating customization items...")
-  (setq custom-options 
+  (buffer-disable-undo)
+  (setq custom-options
        (if (= (length options) 1)
            (mapcar (lambda (entry)
                      (widget-create (nth 1 entry)
@@ -969,7 +1314,7 @@ Reset all visible items in this buffer to their standard settings."
                (length (length options)))
            (mapcar (lambda (entry)
                        (prog2
-                           (message "Creating customization items %2d%%..."
+                           (message "Creating customization items ...%2d%%"
                                     (/ (* 100.0 count) length))
                            (widget-create (nth 1 entry)
                                         :tag (custom-unlispify-tag-name
@@ -982,13 +1327,120 @@ Reset all visible items in this buffer to their standard settings."
                      options))))
   (unless (eq (preceding-char) ?\n)
     (widget-insert "\n"))
-  (message "Creating customization magic...")
-  (mapcar 'custom-magic-reset custom-options)
+  (message "Creating customization items ...%2d%%done" 100)
+  (unless (eq custom-buffer-style 'tree)
+    (mapc 'custom-magic-reset custom-options))
   (message "Creating customization setup...")
   (widget-setup)
+  (buffer-enable-undo)
   (goto-char (point-min))
   (message "Creating customization buffer...done"))
 
+;;; The Tree Browser.
+
+;;;###autoload
+(defun customize-browse (&optional group)
+  "Create a tree browser for the customize hierarchy."
+  (interactive)
+  (unless group
+    (setq group 'emacs))
+  (let ((name "*Customize Browser*"))
+    (kill-buffer (get-buffer-create name))
+    (pop-to-buffer (get-buffer-create name)))
+  (custom-mode)
+  (widget-insert "\
+Square brackets show active fields; type RET or click mouse-1
+on an active field to invoke its action.
+Invoke [+] below to expand a group, and [-] to collapse an expanded group.\n")
+  (if custom-browse-only-groups
+      (widget-insert "\
+Invoke the [Group] button below to edit that item in another window.\n\n")
+    (widget-insert "Invoke the ")
+    (widget-create 'item
+                  :format "%t"
+                  :tag "[Group]"
+                  :tag-glyph "folder")
+    (widget-insert ", ")
+    (widget-create 'item
+                  :format "%t"
+                  :tag "[Face]"
+                  :tag-glyph "face")
+    (widget-insert ", and ")
+    (widget-create 'item
+                  :format "%t"
+                  :tag "[Option]"
+                  :tag-glyph "option")
+    (widget-insert " buttons below to edit that
+item in another window.\n\n"))
+  (let ((custom-buffer-style 'tree))
+    (widget-create 'custom-group
+                  :custom-last t
+                  :custom-state 'unknown
+                  :tag (custom-unlispify-tag-name group)
+                  :value group))
+  (goto-char (point-min)))
+
+(define-widget 'custom-browse-visibility 'item
+  "Control visibility of items in the customize tree browser."
+  :format "%[[%t]%]"
+  :action 'custom-browse-visibility-action)
+
+(defun custom-browse-visibility-action (widget &rest ignore)
+  (let ((custom-buffer-style 'tree))
+    (custom-toggle-parent widget)))
+
+(define-widget 'custom-browse-group-tag 'push-button
+  "Show parent in other window when activated."
+  :tag "Group"
+  :tag-glyph "folder"
+  :action 'custom-browse-group-tag-action)
+
+(defun custom-browse-group-tag-action (widget &rest ignore)
+  (let ((parent (widget-get widget :parent)))
+    (customize-group-other-window (widget-value parent))))
+
+(define-widget 'custom-browse-variable-tag 'push-button
+  "Show parent in other window when activated."
+  :tag "Option"
+  :tag-glyph "option"
+  :action 'custom-browse-variable-tag-action)
+
+(defun custom-browse-variable-tag-action (widget &rest ignore)
+  (let ((parent (widget-get widget :parent)))
+    (customize-variable-other-window (widget-value parent))))
+
+(define-widget 'custom-browse-face-tag 'push-button
+  "Show parent in other window when activated."
+  :tag "Face"
+  :tag-glyph "face"
+  :action 'custom-browse-face-tag-action)
+
+(defun custom-browse-face-tag-action (widget &rest ignore)
+  (let ((parent (widget-get widget :parent)))
+    (customize-face-other-window (widget-value parent))))
+
+(defconst custom-browse-alist '(("   " "space")
+                             (" | " "vertical")
+                             ("-\\ " "top")
+                             (" |-" "middle")
+                             (" `-" "bottom")))
+
+(defun custom-browse-insert-prefix (prefix)
+  "Insert PREFIX.  On XEmacs convert it to line graphics."
+  ;; Fixme: do graphics.
+  (if nil ; (string-match "XEmacs" emacs-version)
+      (progn
+       (insert "*")
+       (while (not (string-equal prefix ""))
+         (let ((entry (substring prefix 0 3)))
+           (setq prefix (substring prefix 3))
+           (let ((overlay (make-overlay (1- (point)) (point) nil t nil))
+                 (name (nth 1 (assoc entry custom-browse-alist))))
+             (overlay-put overlay 'end-glyph (widget-glyph-find name entry))
+             (overlay-put overlay 'start-open t)
+             (overlay-put overlay 'end-open t)))))
+    (insert prefix)))
+
 ;;; Modification of Basic Widgets.
 ;;
 ;; We add extra properties to the basic widgets needed here.  This is
@@ -1017,54 +1469,65 @@ Reset all visible items in this buffer to their standard settings."
 
 ;;; The `custom-magic' Widget.
 
+(defgroup custom-magic-faces nil
+  "Faces used by the magic button."
+  :group 'custom-faces
+  :group 'custom-buffer)
+
 (defface custom-invalid-face '((((class color))
                                (:foreground "yellow" :background "red"))
                               (t
                                (:bold t :italic t :underline t)))
-  "Face used when the customize item is invalid.")
+  "Face used when the customize item is invalid."
+  :group 'custom-magic-faces)
 
 (defface custom-rogue-face '((((class color))
                              (:foreground "pink" :background "black"))
                             (t
                              (:underline t)))
-  "Face used when the customize item is not defined for customization.")
+  "Face used when the customize item is not defined for customization."
+  :group 'custom-magic-faces)
 
-(defface custom-modified-face '((((class color)) 
+(defface custom-modified-face '((((class color))
                                 (:foreground "white" :background "blue"))
                                (t
                                 (:italic t :bold)))
-  "Face used when the customize item has been modified.")
+  "Face used when the customize item has been modified."
+  :group 'custom-magic-faces)
 
-(defface custom-set-face '((((class color)) 
+(defface custom-set-face '((((class color))
                                (:foreground "blue" :background "white"))
                               (t
                                (:italic t)))
-  "Face used when the customize item has been set.")
+  "Face used when the customize item has been set."
+  :group 'custom-magic-faces)
 
-(defface custom-changed-face '((((class color)) 
+(defface custom-changed-face '((((class color))
                                (:foreground "white" :background "blue"))
                               (t
                                (:italic t)))
-  "Face used when the customize item has been changed.")
+  "Face used when the customize item has been changed."
+  :group 'custom-magic-faces)
 
 (defface custom-saved-face '((t (:underline t)))
-  "Face used when the customize item has been saved.")
+  "Face used when the customize item has been saved."
+  :group 'custom-magic-faces)
 
 (defconst custom-magic-alist '((nil "#" underline "\
 uninitialized, you should not see this.")
                               (unknown "?" italic "\
 unknown, you should not see this.")
                               (hidden "-" default "\
-hidden, invoke the dots above to show." "\
-group now hidden, invoke the dots above to show contents.")
+hidden, invoke \"Show\" in the previous line to show." "\
+group now hidden, invoke \"Show\", above, to show contents.")
                               (invalid "x" custom-invalid-face "\
 the value displayed for this %c is invalid and cannot be set.")
                               (modified "*" custom-modified-face "\
-you have edited the value, and can now set the %c." "\
-you have edited something in this group, and can now set it.")
+you have edited the value as text, but you have not set the %c." "\
+you have edited something in this group, but not set it.")
                               (set "+" custom-set-face "\
-you have set this %c, but not saved it." "\
-something in this group has been set, but not yet saved.")
+you have set this %c, but not saved it for future sessions." "\
+something in this group has been set, but not saved.")
                               (changed ":" custom-changed-face "\
 this %c has been changed outside the customize buffer." "\
 something in this group has been changed outside customize.")
@@ -1078,7 +1541,7 @@ something in this group is not prepared for customization.")
 this %c is unchanged from its standard setting." "\
 visible group members are all at standard settings."))
   "Alist of customize option states.
-Each entry is of the form (STATE MAGIC FACE ITEM-DESC [ GROUP-DESC ]), where 
+Each entry is of the form (STATE MAGIC FACE ITEM-DESC [ GROUP-DESC ]), where
 
 STATE is one of the following symbols:
 
@@ -1087,7 +1550,7 @@ STATE is one of the following symbols:
 `unknown'
    For internal use, should never occur.
 `hidden'
-   This item is not being displayed. 
+   This item is not being displayed.
 `invalid'
    This item is modified, but has an invalid form.
 `modified'
@@ -1119,24 +1582,24 @@ The list should be sorted most significant first.")
 
 (defcustom custom-magic-show 'long
   "If non-nil, show textual description of the state.
-If non-nil and not the symbol `long', only show first word."
+If `long', show a full-line description, not just one word."
   :type '(choice (const :tag "no" nil)
-                (const short)
-                (const long))
-  :group 'customize)
+                (const long)
+                (other :tag "short" short))
+  :group 'custom-buffer)
 
 (defcustom custom-magic-show-hidden '(option face)
-  "Control whether the state button is shown for hidden items.
-The value should be a list with the custom categories where the state
+  "Control whether the State button is shown for hidden items.
+The value should be a list with the custom categories where the State
 button should be visible.  Possible categories are `group', `option',
 and `face'."
   :type '(set (const group) (const option) (const face))
-  :group 'customize)
+  :group 'custom-buffer)
 
 (defcustom custom-magic-show-button nil
-  "Show a magic button indicating the state of each customization option."
+  "Show a \"magic\" button indicating the state of each customization option."
   :type 'boolean
-  :group 'customize)
+  :group 'custom-buffer)
 
 (define-widget 'custom-magic 'default
   "Show and manipulate state for a customization option."
@@ -1149,12 +1612,12 @@ and `face'."
 
 (defun widget-magic-mouse-down-action (widget &optional event)
   ;; Non-nil unless hidden.
-  (not (eq (widget-get (widget-get (widget-get widget :parent) :parent) 
+  (not (eq (widget-get (widget-get (widget-get widget :parent) :parent)
                       :custom-state)
           'hidden)))
 
 (defun custom-magic-value-create (widget)
-  ;; Create compact status report for WIDGET.
+  "Create compact status report for WIDGET."
   (let* ((parent (widget-get widget :parent))
         (state (widget-get parent :custom-state))
         (hidden (eq state 'hidden))
@@ -1165,18 +1628,23 @@ and `face'."
         (text (or (and (eq category 'group)
                        (nth 4 entry))
                   (nth 3 entry)))
-        (lisp (eq (widget-get parent :custom-form) 'lisp))
+        (form (widget-get parent :custom-form))
         children)
     (while (string-match "\\`\\(.*\\)%c\\(.*\\)\\'" text)
-      (setq text (concat (match-string 1 text) 
+      (setq text (concat (match-string 1 text)
                         (symbol-name category)
                         (match-string 2 text))))
     (when (and custom-magic-show
               (or (not hidden)
                   (memq category custom-magic-show-hidden)))
       (insert "   ")
-      (push (widget-create-child-and-convert 
-            widget 'choice-item 
+      (when (and (eq category 'group)
+                (not (and (eq custom-buffer-style 'links)
+                          (> (widget-get parent :custom-level) 1))))
+       (insert-char ?\  (* custom-buffer-indent
+                           (widget-get parent :custom-level))))
+      (push (widget-create-child-and-convert
+            widget 'choice-item
             :help-echo "Change the state of this item."
             :format (if hidden "%t" "%[%t%]")
             :button-prefix 'widget-push-button-prefix
@@ -1185,26 +1653,35 @@ and `face'."
             :tag "State")
            children)
       (insert ": ")
-      (if (eq custom-magic-show 'long)
-         (insert text)
-       (insert (symbol-name state)))
-      (when lisp 
-       (insert " (lisp)"))
+      (let ((start (point)))
+       (if (eq custom-magic-show 'long)
+           (insert text)
+         (insert (symbol-name state)))
+       (cond ((eq form 'lisp)
+              (insert " (lisp)"))
+             ((eq form 'mismatch)
+              (insert " (mismatch)")))
+       (put-text-property start (point) 'face 'custom-state-face))
       (insert "\n"))
+    (when (and (eq category 'group)
+              (not (and (eq custom-buffer-style 'links)
+                        (> (widget-get parent :custom-level) 1))))
+      (insert-char ?\  (* custom-buffer-indent
+                         (widget-get parent :custom-level))))
     (when custom-magic-show-button
       (when custom-magic-show
        (let ((indent (widget-get parent :indent)))
          (when indent
            (insert-char ?  indent))))
-      (push (widget-create-child-and-convert 
-            widget 'choice-item 
+      (push (widget-create-child-and-convert
+            widget 'choice-item
             :mouse-down-action 'widget-magic-mouse-down-action
             :button-face face
             :button-prefix ""
             :button-suffix ""
             :help-echo "Change the state."
             :format (if hidden "%t" "%[%t%]")
-            :tag (if lisp 
+            :tag (if (memq form '(lisp mismatch))
                      (concat "(" magic ")")
                    (concat "[" magic "]")))
            children)
@@ -1218,11 +1695,52 @@ and `face'."
 
 ;;; The `custom' Widget.
 
+(defface custom-button-face
+  '((((type x) (class color))          ; Like default modeline
+     (:box (:line-width 2 :style released-button)
+          :background "lightgrey" :foreground "black"))
+    (((type w32) (class color))                ; Like default modeline
+     (:box (:line-width 2 :style released-button)
+          :background "lightgrey" :foreground "black"))
+    (t
+     nil))
+  "Face used for buttons in customization buffers."
+  :version "21.1"
+  :group 'custom-faces)
+
+(defface custom-button-pressed-face
+  '((((type x) (class color))
+     (:box (:line-width 2 :style pressed-button)
+          :background "lightgrey" :foreground "black"))
+    (((type w32) (class color))
+     (:box (:line-width 2 :style pressed-button)
+          :background "lightgrey" :foreground "black"))
+    (t
+     (:inverse-video t)))
+  "Face used for buttons in customization buffers."
+  :version "21.1"
+  :group 'custom-faces)
+
+(defface custom-documentation-face nil
+  "Face used for documentation strings in customization buffers."
+  :group 'custom-faces)
+
+(defface custom-state-face '((((class color)
+                              (background dark))
+                             (:foreground "lime green"))
+                            (((class color)
+                              (background light))
+                             (:foreground "dark green"))
+                            (t nil))
+  "Face used for State descriptions in the customize buffer."
+  :group 'custom-faces)
+
 (define-widget 'custom 'default
   "Customize a user option."
+  :format "%v"
   :convert-widget 'custom-convert-widget
-  :format-handler 'custom-format-handler
   :notify 'custom-notify
+  :custom-prefix ""
   :custom-level 1
   :custom-state 'hidden
   :documentation-property 'widget-subclass-responsibility
@@ -1233,81 +1751,15 @@ and `face'."
   :match (lambda (widget value) (symbolp value)))
 
 (defun custom-convert-widget (widget)
-  ;; Initialize :value and :tag from :args in WIDGET.
+  "Initialize :value and :tag from :args in WIDGET."
   (let ((args (widget-get widget :args)))
-    (when args 
+    (when args
       (widget-put widget :value (widget-apply widget
                                              :value-to-internal (car args)))
       (widget-put widget :tag (custom-unlispify-tag-name (car args)))
       (widget-put widget :args nil)))
   widget)
 
-(defun custom-format-handler (widget escape)
-  ;; We recognize extra escape sequences.
-  (let* ((buttons (widget-get widget :buttons))
-        (state (widget-get widget :custom-state))
-        (level (widget-get widget :custom-level)))
-    (cond ((eq escape ?l)
-          (when level 
-            (insert-char ?\  (1- level))
-            (if (eq state 'hidden)
-                (insert-char ?- (1+ level))
-              (insert "/")
-              (insert-char ?- level))))
-         ((eq escape ?e)
-          (when (and level (not (eq state 'hidden)))
-            (insert "\n")
-            (insert-char ?\  (1- level))
-            (insert "\\")
-            (insert-char ?-  level)
-            (insert " " (widget-get widget :tag) " group end ")
-            (insert-char ?- (- 75 (current-column) level))
-            (insert "/\n")))
-         ((eq escape ?-)
-          (when (and level (not (eq state 'hidden)))
-            (insert-char ?- (- 76 (current-column) level))
-            (insert "\\")))
-         ((eq escape ?L)
-          (push (widget-create-child-and-convert
-                 widget 'visibility
-                 :action 'custom-toggle-parent
-                 (not (eq state 'hidden)))
-                buttons))
-         ((eq escape ?m)
-          (and (eq (preceding-char) ?\n)
-               (widget-get widget :indent)
-               (insert-char ?  (widget-get widget :indent)))
-          (let ((magic (widget-create-child-and-convert
-                        widget 'custom-magic nil)))
-            (widget-put widget :custom-magic magic)
-            (push magic buttons)
-            (widget-put widget :buttons buttons)))
-         ((eq escape ?a)
-          (unless (eq state 'hidden)
-            (let* ((symbol (widget-get widget :value))
-                   (links (get symbol 'custom-links))
-                   (many (> (length links) 2)))
-              (when links
-                (and (eq (preceding-char) ?\n)
-                     (widget-get widget :indent)
-                     (insert-char ?  (widget-get widget :indent)))
-                (insert "See also ")
-                (while links
-                  (push (widget-create-child-and-convert widget (car links))
-                        buttons)
-                  (setq links (cdr links))
-                  (cond ((null links)
-                         (insert ".\n"))
-                        ((null (cdr links))
-                         (if many
-                             (insert ", and ")
-                           (insert " and ")))
-                        (t 
-                         (insert ", "))))
-                (widget-put widget :buttons buttons)))))
-         (t 
-          (widget-default-format-handler widget escape)))))
-
 (defun custom-notify (widget &rest args)
   "Keep track of changes."
   (let ((state (widget-get widget :custom-state)))
@@ -1329,7 +1781,7 @@ and `face'."
       (custom-redraw-magic widget))
     (when (and (>= pos from) (<= pos to))
       (condition-case nil
-         (progn 
+         (progn
            (if (> column 0)
                (goto-line line)
              (goto-line (1+ line)))
@@ -1338,13 +1790,14 @@ and `face'."
 
 (defun custom-redraw-magic (widget)
   "Redraw WIDGET state with current settings."
-  (while widget 
+  (while widget
     (let ((magic (widget-get widget :custom-magic)))
-      (unless magic 
-       (debug))
-      (widget-value-set magic (widget-value magic))
-      (when (setq widget (widget-get widget :group))
-       (custom-group-state-update widget))))
+      (cond (magic
+            (widget-value-set magic (widget-value magic))
+            (when (setq widget (widget-get widget :group))
+              (custom-group-state-update widget)))
+           (t
+            (setq widget nil)))))
   (widget-setup))
 
 (defun custom-show (widget value)
@@ -1363,7 +1816,7 @@ and `face'."
 (defun custom-load-symbol (symbol)
   "Load all dependencies for SYMBOL."
   (unless custom-load-recursion
-    (let ((custom-load-recursion t) 
+    (let ((custom-load-recursion t)
          (loads (get symbol 'custom-loads))
          load)
       (while loads
@@ -1374,6 +1827,8 @@ and `face'."
                   (require load)
                 (error nil)))
              ;; Don't reload a file already loaded.
+             ((and (boundp 'preloaded-file-list)
+                   (member load preloaded-file-list)))
              ((assoc load load-history))
              ((assoc (locate-library load) load-history))
              (t
@@ -1389,25 +1844,181 @@ and `face'."
   "Load all dependencies for WIDGET."
   (custom-load-symbol (widget-value widget)))
 
+(defun custom-unloaded-symbol-p (symbol)
+  "Return non-nil if the dependencies of SYMBOL has not yet been loaded."
+  (let ((found nil)
+       (loads (get symbol 'custom-loads))
+       load)
+    (while loads
+      (setq load (car loads)
+           loads (cdr loads))
+      (cond ((symbolp load)
+            (unless (featurep load)
+              (setq found t)))
+           ((assoc load load-history))
+           ((assoc (locate-library load) load-history)
+            (message nil))
+           (t
+            (setq found t))))
+    found))
+
+(defun custom-unloaded-widget-p (widget)
+  "Return non-nil if the dependencies of WIDGET has not yet been loaded."
+  (custom-unloaded-symbol-p (widget-value widget)))
+
 (defun custom-toggle-hide (widget)
   "Toggle visibility of WIDGET."
+  (custom-load-widget 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 
+         (t
           (widget-put widget :documentation-shown nil)
           (widget-put widget :custom-state 'hidden)))
-    (custom-redraw widget)))
+    (custom-redraw widget)
+    (widget-setup)))
 
 (defun custom-toggle-parent (widget &rest ignore)
-  "Toggle visibility of parent to WIDGET."
+  "Toggle visibility of parent of WIDGET."
   (custom-toggle-hide (widget-get widget :parent)))
 
+(defun custom-add-see-also (widget &optional prefix)
+  "Add `See also ...' to WIDGET if there are any links.
+Insert PREFIX first if non-nil."
+  (let* ((symbol (widget-get widget :value))
+        (links (get symbol 'custom-links))
+        (many (> (length links) 2))
+        (buttons (widget-get widget :buttons))
+        (indent (widget-get widget :indent)))
+    (when links
+      (when indent
+       (insert-char ?\  indent))
+      (when prefix
+       (insert prefix))
+      (insert "See also ")
+      (while links
+       (push (widget-create-child-and-convert widget (car links))
+             buttons)
+       (setq links (cdr links))
+       (cond ((null links)
+              (insert ".\n"))
+             ((null (cdr links))
+              (if many
+                  (insert ", and ")
+                (insert " and ")))
+             (t
+              (insert ", "))))
+      (widget-put widget :buttons buttons))))
+
+(defun custom-add-parent-links (widget &optional initial-string)
+  "Add \"Parent groups: ...\" to WIDGET if the group has parents.
+The value if non-nil if any parents were found.
+If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
+  (let ((name (widget-value widget))
+       (type (widget-type widget))
+       (buttons (widget-get widget :buttons))
+       (start (point))
+       found)
+    (insert (or initial-string "Parent groups:"))
+    (mapatoms (lambda (symbol)
+               (let ((entry (assq name (get symbol 'custom-group))))
+                 (when (eq (nth 1 entry) type)
+                   (insert " ")
+                   (push (widget-create-child-and-convert
+                          widget 'custom-group-link
+                          :tag (custom-unlispify-tag-name symbol)
+                          symbol)
+                         buttons)
+                   (setq found t)))))
+    (widget-put widget :buttons buttons)
+    (if found
+       (insert "\n")
+      (delete-region start (point)))
+    found))
+
+;;; The `custom-comment' Widget.
+
+;; like the editable field
+(defface custom-comment-face '((((class grayscale color)
+                                (background light))
+                               (:background "gray85"))
+                              (((class grayscale color)
+                                (background dark))
+                               (:background "dim gray"))
+                              (t
+                               (:italic t)))
+  "Face used for comments on variables or faces"
+  :version "21.1"
+  :group 'custom-faces)
+
+;; like font-lock-comment-face
+(defface custom-comment-tag-face
+  '((((class color) (background dark)) (:foreground "gray80"))
+    (((class color) (background light)) (:foreground "blue4"))
+    (((class grayscale) (background light))
+     (:foreground "DimGray" :bold t :italic t))
+    (((class grayscale) (background dark))
+     (:foreground "LightGray" :bold t :italic t))
+    (t (:bold t)))
+  "Face used for variables or faces comment tags"
+  :group 'custom-faces)
+
+(define-widget 'custom-comment 'string
+  "User comment."
+  :tag "Comment"
+  :help-echo "Edit a comment here."
+  :sample-face 'custom-comment-tag-face
+  :value-face 'custom-comment-face
+  :shown nil
+  :create 'custom-comment-create)
+
+(defun custom-comment-create (widget)
+  (let* ((null-comment (equal "" (widget-value widget))))
+    (if (or (widget-get (widget-get widget :parent) :comment-shown)
+           (not null-comment))
+       (widget-default-create widget)
+      ;; `widget-default-delete' expects markers in these slots --
+      ;; maybe it shouldn't.
+      (widget-put widget :from (point-marker))
+      (widget-put widget :to (point-marker)))))
+
+(defun custom-comment-hide (widget)
+  (widget-put (widget-get widget :parent) :comment-shown nil))
+
+;; Those functions are for the menu. WIDGET is NOT the comment widget. It's
+;; the global custom one
+(defun custom-comment-show (widget)
+  (widget-put widget :comment-shown t)
+  (custom-redraw widget)
+  (widget-setup))
+
+(defun custom-comment-invisible-p (widget)
+  (let ((val (widget-value (widget-get widget :comment-widget))))
+    (and (equal "" val)
+        (not (widget-get widget :comment-shown)))))
+
 ;;; The `custom-variable' Widget.
 
-(defface custom-variable-sample-face '((t (:underline t)))
+;; When this was underlined blue, users confused it with a
+;; Mosaic-style hyperlink...
+(defface custom-variable-tag-face
+  `((((class color)
+      (background dark))
+     (:foreground "light blue" :bold t :family "helv"
+                 :height ,(let ((height (face-attribute 'default :height)))
+                            (if (numberp height)
+                                (floor height 0.9)
+                              height))))
+    (((class color)
+      (background light))
+     (:foreground "blue" :family "helv" :bold t
+                 :height ,(let ((height (face-attribute 'default :height)))
+                            (if (numberp height)
+                                (floor height 0.9)
+                              height))))
+    (t (:bold t)))
   "Face used for unpushable variable tags."
   :group 'custom-faces)
 
@@ -1415,15 +2026,22 @@ and `face'."
   "Face used for pushable variable tags."
   :group 'custom-faces)
 
+(defcustom custom-variable-default-form 'edit
+  "Default form of displaying variable values."
+  :type '(choice (const edit)
+                (const lisp))
+  :group 'custom-buffer
+  :version "20.3")
+
 (define-widget 'custom-variable 'custom
   "Customize variable."
-  :format "%v%m%h%a"
+  :format "%v"
   :help-echo "Set or reset this variable."
   :documentation-property 'variable-documentation
   :custom-category 'option
   :custom-state nil
   :custom-menu 'custom-variable-menu-create
-  :custom-form 'edit
+  :custom-form nil ; defaults to value of `custom-variable-default-form'
   :value-create 'custom-variable-value-create
   :action 'custom-variable-action
   :custom-set 'custom-variable-set
@@ -1434,7 +2052,7 @@ and `face'."
 
 (defun custom-variable-type (symbol)
   "Return a widget suitable for editing the value of SYMBOL.
-If SYMBOL has a `custom-type' property, use that.  
+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 'standard-value))
@@ -1449,8 +2067,10 @@ Otherwise, look up symbol in `custom-guess-type-alist'."
     tmp))
 
 (defun custom-variable-value-create (widget)
-  "Here is where you edit the variables value."
+  "Here is where you edit the variable's value."
   (custom-load-widget widget)
+  (unless (widget-get widget :custom-form)
+    (widget-put widget :custom-form custom-variable-default-form))
   (let* ((buttons (widget-get widget :buttons))
         (children (widget-get widget :children))
         (form (widget-get widget :custom-form))
@@ -1460,10 +2080,12 @@ Otherwise, look up symbol in `custom-guess-type-alist'."
         (type (custom-variable-type symbol))
         (conv (widget-convert type))
         (get (or (get symbol 'custom-get) 'default-value))
+        (prefix (widget-get widget :custom-prefix))
+        (last (widget-get widget :custom-last))
         (value (if (default-boundp symbol)
                    (funcall get symbol)
                  (widget-get conv :value))))
-    ;; If the widget is new, the child determine whether it is hidden.
+    ;; If the widget is new, the child determines whether it is hidden.
     (cond (state)
          ((custom-show type value)
           (setq state 'unknown))
@@ -1473,23 +2095,31 @@ Otherwise, look up symbol in `custom-guess-type-alist'."
     (when (eq state 'unknown)
       (unless (widget-apply conv :match value)
        ;; (widget-apply (widget-convert type) :match value)
-       (setq form 'lisp)))
+       (setq form 'mismatch)))
     ;; Now we can create the child widget.
-    (cond ((eq state 'hidden)
+    (cond ((eq custom-buffer-style 'tree)
+          (insert prefix (if last " `--- " " |--- "))
+          (push (widget-create-child-and-convert
+                 widget 'custom-browse-variable-tag)
+                buttons)
+          (insert " " tag "\n")
+          (widget-put widget :buttons buttons))
+         ((eq state 'hidden)
           ;; Indicate hidden value.
-          (push (widget-create-child-and-convert 
+          (push (widget-create-child-and-convert
                  widget 'item
                  :format "%{%t%}: "
-                 :sample-face 'custom-variable-sample-face
+                 :sample-face 'custom-variable-tag-face
                  :tag tag
                  :parent widget)
                 buttons)
-          (push (widget-create-child-and-convert 
+          (push (widget-create-child-and-convert
                  widget 'visibility
+                 :help-echo "Show the value of this option."
                  :action 'custom-toggle-parent
                  nil)
                 buttons))
-         ((eq form 'lisp)
+         ((memq form '(lisp mismatch))
           ;; In lisp mode edit the saved value when possible.
           (let* ((value (cond ((get symbol 'saved-value)
                                (car (get symbol 'saved-value)))
@@ -1500,14 +2130,15 @@ Otherwise, look up symbol in `custom-guess-type-alist'."
                               (t
                                (custom-quote (widget-get conv :value))))))
             (insert (symbol-name symbol) ": ")
-            (push (widget-create-child-and-convert 
-                 widget 'visibility
-                 :action 'custom-toggle-parent
-                 t)
-                buttons)
+            (push (widget-create-child-and-convert
+                   widget 'visibility
+                   :help-echo "Hide the value of this option."
+                   :action 'custom-toggle-parent
+                   t)
+                  buttons)
             (insert " ")
-            (push (widget-create-child-and-convert 
-                   widget 'sexp 
+            (push (widget-create-child-and-convert
+                   widget 'sexp
                    :button-face 'custom-variable-button-face
                    :format "%v"
                    :tag (symbol-name symbol)
@@ -1519,38 +2150,72 @@ Otherwise, look up symbol in `custom-guess-type-alist'."
           (let* ((format (widget-get type :format))
                  tag-format value-format)
             (unless (string-match ":" format)
-              (error "Bad format."))
+              (error "Bad format"))
             (setq tag-format (substring format 0 (match-end 0)))
             (setq value-format (substring format (match-end 0)))
             (push (widget-create-child-and-convert
-                   widget 'item 
+                   widget 'item
                    :format tag-format
                    :action 'custom-tag-action
+                   :help-echo "Change value of this option."
                    :mouse-down-action 'custom-tag-mouse-down-action
                    :button-face 'custom-variable-button-face
-                   :sample-face 'custom-variable-sample-face
+                   :sample-face 'custom-variable-tag-face
                    tag)
                   buttons)
             (insert " ")
-            (push (widget-create-child-and-convert 
-                 widget 'visibility
-                 :action 'custom-toggle-parent
-                 t)
-                buttons)            
             (push (widget-create-child-and-convert
-                   widget type 
+                   widget 'visibility
+                   :help-echo "Hide the value of this option."
+                   :action 'custom-toggle-parent
+                   t)
+                  buttons)
+            (push (widget-create-child-and-convert
+                   widget type
                    :format value-format
                    :value value)
                   children))))
-    ;; Now update the state.
-    (unless (eq (preceding-char) ?\n)
-      (widget-insert "\n"))
-    (if (eq state 'hidden)
-       (widget-put widget :custom-state state)
-      (custom-variable-state-set widget))
-    (widget-put widget :custom-form form)           
-    (widget-put widget :buttons buttons)
-    (widget-put widget :children children)))
+    (unless (eq custom-buffer-style 'tree)
+      (unless (eq (preceding-char) ?\n)
+       (widget-insert "\n"))
+      ;; Create the magic button.
+      (let ((magic (widget-create-child-and-convert
+                   widget 'custom-magic nil)))
+       (widget-put widget :custom-magic magic)
+       (push magic buttons))
+      ;; ### NOTE: this is ugly!!!! I need to update the :buttons property
+      ;; before the call to `widget-default-format-handler'. Otherwise, I
+      ;; loose my current `buttons'. This function shouldn't be called like
+      ;; this anyway. The doc string widget should be added like the others.
+      ;; --dv
+      (widget-put widget :buttons buttons)
+      ;; Insert documentation.
+      (widget-default-format-handler widget ?h)
+
+      ;; The comment field
+      (unless (eq state 'hidden)
+       (let* ((comment (get symbol 'variable-comment))
+              (comment-widget
+               (widget-create-child-and-convert
+                widget 'custom-comment
+                :parent widget
+                :value (or comment ""))))
+         (widget-put widget :comment-widget comment-widget)
+         ;; Don't push it !!! Custom assumes that the first child is the
+         ;; value one.
+         (setq children (append children (list comment-widget)))))
+      ;; Update the rest of the properties properties.
+      (widget-put widget :custom-form form)
+      (widget-put widget :children children)
+      ;; Now update the state.
+      (if (eq state 'hidden)
+         (widget-put widget :custom-state state)
+       (custom-variable-state-set widget))
+      ;; See also.
+      (unless (eq state 'hidden)
+       (when (eq (widget-get widget :custom-level) 1)
+         (custom-add-parent-links widget))
+       (custom-add-see-also widget)))))
 
 (defun custom-tag-action (widget &rest args)
   "Pass :action to first child of WIDGET's parent."
@@ -1569,39 +2234,43 @@ Otherwise, look up symbol in `custom-guess-type-alist'."
         (value (if (default-boundp symbol)
                    (funcall get symbol)
                  (widget-get widget :value)))
+        (comment (get symbol 'variable-comment))
         tmp
-        (state (cond ((setq tmp (get symbol 'customized-value))
+        temp
+        (state (cond ((progn (setq tmp (get symbol 'customized-value))
+                             (setq temp
+                                   (get symbol 'customized-variable-comment))
+                             (or tmp temp))
                       (if (condition-case nil
-                              (equal value (eval (car tmp)))
+                              (and (equal value (eval (car tmp)))
+                                   (equal comment temp))
                             (error nil))
                           'set
                         'changed))
-                     ((setq tmp (get symbol 'saved-value))
+                     ((progn (setq tmp (get symbol 'saved-value))
+                             (setq temp (get symbol 'saved-variable-comment))
+                             (or tmp temp))
                       (if (condition-case nil
-                              (equal value (eval (car tmp)))
+                              (and (equal value (eval (car tmp)))
+                                   (equal comment temp))
                             (error nil))
                           'saved
                         'changed))
                      ((setq tmp (get symbol 'standard-value))
                       (if (condition-case nil
-                              (equal value (eval (car tmp)))
+                              (and (equal value (eval (car tmp)))
+                                   (equal comment nil))
                             (error nil))
                           'standard
                         'changed))
                      (t 'rogue))))
     (widget-put widget :custom-state state)))
 
-(defvar custom-variable-menu 
-  '(("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
+(defvar custom-variable-menu
+  '(("Set for Current Session" custom-variable-set
      (lambda (widget)
        (eq (widget-get widget :custom-state) 'modified)))
-    ("Save" custom-variable-save
+    ("Save for Future Sessions" custom-variable-save
      (lambda (widget)
        (memq (widget-get widget :custom-state) '(modified set changed rogue))))
     ("Reset to Current" custom-redraw
@@ -1610,20 +2279,30 @@ Otherwise, look up symbol in `custom-guess-type-alist'."
            (memq (widget-get widget :custom-state) '(modified changed)))))
     ("Reset to Saved" custom-variable-reset-saved
      (lambda (widget)
-       (and (get (widget-value widget) 'saved-value)
+       (and (or (get (widget-value widget) 'saved-value)
+               (get (widget-value widget) 'saved-variable-comment))
            (memq (widget-get widget :custom-state)
                  '(modified set changed rogue)))))
-    ("Reset to Standard Settings" custom-variable-reset-standard
+    ("Erase Customization" custom-variable-reset-standard
      (lambda (widget)
        (and (get (widget-value widget) 'standard-value)
            (memq (widget-get widget :custom-state)
-                 '(modified set changed saved rogue))))))
+                 '(modified set changed saved rogue)))))
+    ("---" ignore ignore)
+    ("Add Comment" custom-comment-show custom-comment-invisible-p)
+    ("---" ignore ignore)
+    ("Don't show as Lisp expression" custom-variable-edit
+     (lambda (widget)
+       (eq (widget-get widget :custom-form) 'lisp)))
+    ("Show initial Lisp expression" custom-variable-edit-lisp
+     (lambda (widget)
+       (eq (widget-get widget :custom-form) 'edit))))
   "Alist of actions for the `custom-variable' widget.
 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.")
+widget.  If FILTER is nil, ACTION is always valid.")
 
 (defun custom-variable-action (widget &optional event)
   "Show the menu for `custom-variable' WIDGET.
@@ -1650,7 +2329,7 @@ Optional EVENT is the location for the menu."
   (custom-redraw widget))
 
 (defun custom-variable-edit-lisp (widget)
-  "Edit the lisp representation of the value of WIDGET."
+  "Edit the Lisp representation of the value of WIDGET."
   (widget-put widget :custom-state 'unknown)
   (widget-put widget :custom-form 'lisp)
   (custom-redraw widget))
@@ -1662,43 +2341,71 @@ Optional EVENT is the location for the menu."
         (child (car (widget-get widget :children)))
         (symbol (widget-value widget))
         (set (or (get symbol 'custom-set) 'set-default))
-         val)
+        (comment-widget (widget-get widget :comment-widget))
+        (comment (widget-value comment-widget))
+        val)
     (cond ((eq state 'hidden)
-          (error "Cannot set hidden variable."))
+          (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)
+         ((memq form '(lisp mismatch))
+          (when (equal comment "")
+            (setq comment nil)
+            ;; Make the comment invisible by hand if it's empty
+            (custom-comment-hide comment-widget))
           (funcall set symbol (eval (setq val (widget-value child))))
-          (put symbol 'customized-value (list val)))
+          (put symbol 'customized-value (list val))
+          (put symbol 'variable-comment comment)
+          (put symbol 'customized-variable-comment comment))
          (t
+          (when (equal comment "")
+            (setq comment nil)
+            ;; Make the comment invisible by hand if it's empty
+            (custom-comment-hide comment-widget))
           (funcall set symbol (setq val (widget-value child)))
-          (put symbol 'customized-value (list (custom-quote val)))))
+          (put symbol 'customized-value (list (custom-quote val)))
+          (put symbol 'variable-comment comment)
+          (put symbol 'customized-variable-comment comment)))
     (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."
+  "Set and save the 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))
         (set (or (get symbol 'custom-set) 'set-default))
+        (comment-widget (widget-get widget :comment-widget))
+        (comment (widget-value comment-widget))
         val)
     (cond ((eq state 'hidden)
-          (error "Cannot set hidden variable."))
+          (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)
+         ((memq form '(lisp mismatch))
+          (when (equal comment "")
+            (setq comment nil)
+            ;; Make the comment invisible by hand if it's empty
+            (custom-comment-hide comment-widget))
           (put symbol 'saved-value (list (widget-value child)))
-          (funcall set symbol (eval (widget-value child))))
+          (funcall set symbol (eval (widget-value child)))
+          (put symbol 'variable-comment comment)
+          (put symbol 'saved-variable-comment comment))
          (t
-          (put symbol
-               'saved-value (list (custom-quote (widget-value
-                                                 child))))
-          (funcall set symbol (widget-value child))))
+          (when (equal comment "")
+            (setq comment nil)
+            ;; Make the comment invisible by hand if it's empty
+            (custom-comment-hide comment-widget))
+          (put symbol 'saved-value
+               (list (custom-quote (widget-value child))))
+          (funcall set symbol (widget-value child))
+          (put symbol 'variable-comment comment)
+          (put symbol 'saved-variable-comment comment)))
     (put symbol 'customized-value nil)
+    (put symbol 'customized-variable-comment nil)
     (custom-save-all)
     (custom-variable-state-set widget)
     (custom-redraw-magic widget)))
@@ -1706,28 +2413,42 @@ 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))
-        (set (or (get symbol 'custom-set) 'set-default)))
-    (if (get symbol 'saved-value)
-       (condition-case nil
-           (funcall set symbol (eval (car (get symbol 'saved-value))))
-         (error nil))
-      (error "No saved value for %s" symbol))
+        (set (or (get symbol 'custom-set) 'set-default))
+        (comment-widget (widget-get widget :comment-widget))
+        (value (get symbol 'saved-value))
+        (comment (get symbol 'saved-variable-comment)))
+    (cond ((or value comment)
+          (put symbol 'variable-comment comment)
+          (condition-case nil
+              (funcall set symbol (eval (car value)))
+            (error nil)))
+         (t
+          (error "No saved value for %s" symbol)))
     (put symbol 'customized-value nil)
+    (put symbol 'customized-variable-comment nil)
     (widget-put widget :custom-state 'unknown)
+    ;; This call will possibly make the comment invisible
     (custom-redraw widget)))
 
 (defun custom-variable-reset-standard (widget)
-  "Restore the standard setting for the variable being edited by WIDGET."
+  "Restore the standard setting for the variable being edited by WIDGET.
+This operation eliminates any saved setting for the variable,
+restoring it to the state of a variable that has never been customized."
   (let* ((symbol (widget-value widget))
-        (set (or (get symbol 'custom-set) 'set-default)))
+        (set (or (get symbol 'custom-set) 'set-default))
+        (comment-widget (widget-get widget :comment-widget)))
     (if (get symbol 'standard-value)
        (funcall set symbol (eval (car (get symbol 'standard-value))))
       (error "No standard setting known for %S" symbol))
+    (put symbol 'variable-comment nil)
     (put symbol 'customized-value nil)
-    (when (get symbol 'saved-value)
+    (put symbol 'customized-variable-comment nil)
+    (when (or (get symbol 'saved-value) (get symbol 'saved-variable-comment))
       (put symbol 'saved-value nil)
+      (put symbol 'saved-variable-comment nil)
       (custom-save-all))
     (widget-put widget :custom-state 'unknown)
+    ;; This call will possibly make the comment invisible
     (custom-redraw widget)))
 
 ;;; The `custom-face-edit' Widget.
@@ -1737,12 +2458,12 @@ Optional EVENT is the location for the menu."
   :format "%t: %v"
   :tag "Attributes"
   :extra-offset 12
-  :button-args '(:help-echo "Control whether this attribute have any effect.")
+  :button-args '(:help-echo "Control whether this attribute has any effect.")
   :args (mapcar (lambda (att)
-                 (list 'group 
+                 (list 'group
                        :inline t
                        :sibling-args (widget-get (nth 1 att) :sibling-args)
-                       (list 'const :format "" :value (nth 0 att)) 
+                       (list 'const :format "" :value (nth 0 att))
                        (nth 1 att)))
                custom-face-attributes))
 
@@ -1771,10 +2492,10 @@ The X11 Window System.")
                                           :sibling-args (:help-echo "\
 OS/2 Presentation Manager.")
                                           pm)
-                                   (const :format "Win32 "
+                                   (const :format "W32 "
                                           :sibling-args (:help-echo "\
-Windows NT/95/97.")
-                                          win32)
+Windows NT/9X.")
+                                          w32)
                                    (const :format "DOS "
                                           :sibling-args (:help-echo "\
 Plain MS-DOS.")
@@ -1819,22 +2540,32 @@ Match frames with dark backgrounds.")
 
 ;;; The `custom-face' Widget.
 
-(defface custom-face-tag-face '((t (:underline t)))
+(defface custom-face-tag-face
+  `((t (:bold t :family "helv"
+             :height ,(let ((height (face-attribute 'default :height)))
+                            (if (numberp height)
+                                (floor height 0.9)
+                              height)))))
   "Face used for face tags."
   :group 'custom-faces)
 
+(defcustom custom-face-default-form 'selected
+  "Default form of displaying face definition."
+  :type '(choice (const all)
+                (const selected)
+                (const lisp))
+  :group 'custom-buffer
+  :version "20.3")
+
 (define-widget 'custom-face 'custom
   "Customize face."
-  :format "%{%t%}: %s %L\n%m%h%a%v"
-  :format-handler 'custom-face-format-handler
   :sample-face 'custom-face-tag-face
   :help-echo "Set or reset this face."
-  :documentation-property '(lambda (face)
-                            (face-doc-string face))
+  :documentation-property #'face-doc-string
   :value-create 'custom-face-value-create
   :action 'custom-face-action
   :custom-category 'face
-  :custom-form 'selected
+  :custom-form nil ; defaults to value of `custom-face-default-form'
   :custom-set 'custom-face-set
   :custom-save 'custom-face-save
   :custom-reset-current 'custom-redraw
@@ -1842,27 +2573,7 @@ Match frames with dark backgrounds.")
   :custom-reset-standard 'custom-face-reset-standard
   :custom-menu 'custom-face-menu-create)
 
-(defun custom-face-format-handler (widget escape)
-  ;; We recognize extra escape sequences.
-  (let (child
-       (symbol (widget-get widget :value)))
-    (cond ((eq escape ?s)
-          (and (string-match "XEmacs" emacs-version)
-               ;; XEmacs cannot display initialized faces.
-               (not (custom-facep symbol))
-               (copy-face 'custom-face-empty symbol))
-          (setq child (widget-create-child-and-convert 
-                       widget 'item
-                       :format "(%{%t%})"
-                       :sample-face symbol
-                       :tag "sample")))
-         (t 
-          (custom-format-handler widget escape)))
-    (when child
-      (widget-put widget
-                 :buttons (cons child (widget-get widget :buttons))))))
-
-(define-widget 'custom-face-all 'editable-list 
+(define-widget 'custom-face-all 'editable-list
   "An editable list of display specifications and attributes."
   :entry-format "%i %d %v"
   :insert-button-args '(:help-echo "Insert new display specification here.")
@@ -1881,7 +2592,7 @@ Match frames with dark backgrounds.")
   "Non-nil if VALUE is an unselected display specification."
   (not (face-spec-set-match-display value (selected-frame))))
 
-(define-widget 'custom-face-selected 'group 
+(define-widget 'custom-face-selected 'group
   "Edit the attributes of the selected display in a face specification."
   :args '((repeat :format ""
                  :inline t
@@ -1895,61 +2606,139 @@ Match frames with dark backgrounds.")
   "Converted version of the `custom-face-selected' widget.")
 
 (defun custom-face-value-create (widget)
-  ;; Create a list of the display specifications.
-  (unless (eq (preceding-char) ?\n)
-    (insert "\n"))
-  (when (not (eq (widget-get widget :custom-state) 'hidden))
-    (message "Creating face editor...")
-    (custom-load-widget widget)
-    (let* ((symbol (widget-value widget))
-          (spec (or (get symbol 'saved-face)
-                    (get symbol 'face-defface-spec)
-                    ;; Attempt to construct it.
-                    (list (list t (custom-face-attributes-get 
-                                   symbol (selected-frame))))))
-          (form (widget-get widget :custom-form))
-          (indent (widget-get widget :indent))
-          (edit (widget-create-child-and-convert
-                 widget
-                 (cond ((and (eq form 'selected)
-                             (widget-apply custom-face-selected :match spec))
-                        (when indent (insert-char ?\  indent))
-                        'custom-face-selected)
-                       ((and (not (eq form 'lisp))
-                             (widget-apply custom-face-all :match spec))
-                        'custom-face-all)
-                       (t 
-                        (when indent (insert-char ?\  indent))
-                        'sexp))
-                 :value spec)))
-      (custom-face-state-set widget)
-      (widget-put widget :children (list edit)))
-    (message "Creating face editor...done")))
-
-(defvar custom-face-menu 
-  '(("Edit Selected" custom-face-edit-selected
+  "Create a list of the display specifications for WIDGET."
+  (let ((buttons (widget-get widget :buttons))
+       children
+       (symbol (widget-get widget :value))
+       (tag (widget-get widget :tag))
+       (state (widget-get widget :custom-state))
+       (begin (point))
+       (is-last (widget-get widget :custom-last))
+       (prefix (widget-get widget :custom-prefix)))
+    (unless tag
+      (setq tag (prin1-to-string symbol)))
+    (cond ((eq custom-buffer-style 'tree)
+          (insert prefix (if is-last " `--- " " |--- "))
+          (push (widget-create-child-and-convert
+                 widget 'custom-browse-face-tag)
+                buttons)
+          (insert " " tag "\n")
+          (widget-put widget :buttons buttons))
+         (t
+          ;; Create tag.
+          (insert tag)
+          (if (eq custom-buffer-style 'face)
+              (insert " ")
+            (widget-specify-sample widget begin (point))
+            (insert ": "))
+          ;; Sample.
+          (push (widget-create-child-and-convert widget 'item
+                                                 :format "(%{%t%})"
+                                                 :sample-face symbol
+                                                 :tag "sample")
+                buttons)
+          ;; Visibility.
+          (insert " ")
+          (push (widget-create-child-and-convert
+                 widget 'visibility
+                 :help-echo "Hide or show this face."
+                 :action 'custom-toggle-parent
+                 (not (eq state 'hidden)))
+                buttons)
+          ;; Magic.
+          (insert "\n")
+          (let ((magic (widget-create-child-and-convert
+                        widget 'custom-magic nil)))
+            (widget-put widget :custom-magic magic)
+            (push magic buttons))
+          ;; Update buttons.
+          (widget-put widget :buttons buttons)
+          ;; Insert documentation.
+          (widget-default-format-handler widget ?h)
+          ;; The comment field
+          (unless (eq state 'hidden)
+            (let* ((comment (get symbol 'face-comment))
+                   (comment-widget
+                    (widget-create-child-and-convert
+                     widget 'custom-comment
+                     :parent widget
+                     :value (or comment ""))))
+              (widget-put widget :comment-widget comment-widget)
+              (push comment-widget children)))
+          ;; See also.
+          (unless (eq state 'hidden)
+            (when (eq (widget-get widget :custom-level) 1)
+              (custom-add-parent-links widget))
+            (custom-add-see-also widget))
+          ;; Editor.
+          (unless (eq (preceding-char) ?\n)
+            (insert "\n"))
+          (unless (eq state 'hidden)
+            (message "Creating face editor...")
+            (custom-load-widget widget)
+            (unless (widget-get widget :custom-form)
+                (widget-put widget :custom-form custom-face-default-form))
+            (let* ((symbol (widget-value widget))
+                   (spec (or (get symbol 'saved-face)
+                             (get symbol 'face-defface-spec)
+                             ;; Attempt to construct it.
+                             (list (list t (custom-face-attributes-get
+                                            symbol (selected-frame))))))
+                   (form (widget-get widget :custom-form))
+                   (indent (widget-get widget :indent))
+                   edit)
+              ;; If the user has changed this face in some other way,
+              ;; edit it as the user has specified it.
+              (if (not (face-spec-match-p symbol spec (selected-frame)))
+                  (setq spec (list (list t (face-attr-construct symbol (selected-frame))))))
+              (setq edit (widget-create-child-and-convert
+                          widget
+                          (cond ((and (eq form 'selected)
+                                      (widget-apply custom-face-selected
+                                                    :match spec))
+                                 (when indent (insert-char ?\  indent))
+                                 'custom-face-selected)
+                                ((and (not (eq form 'lisp))
+                                      (widget-apply custom-face-all
+                                                    :match spec))
+                                 'custom-face-all)
+                                (t
+                                 (when indent (insert-char ?\  indent))
+                                 'sexp))
+                          :value spec))
+              (custom-face-state-set widget)
+              (push edit children)
+              (widget-put widget :children children))
+            (message "Creating face editor...done"))))))
+
+(defvar custom-face-menu
+  '(("Set for Current Session" custom-face-set)
+    ("Save for Future Sessions" custom-face-save-command)
+    ("Reset to Saved" custom-face-reset-saved
      (lambda (widget)
-       (not (eq (widget-get widget :custom-form) 'selected))))
-    ("Edit All" custom-face-edit-all
+       (or (get (widget-value widget) 'saved-face)
+          (get (widget-value widget) 'saved-face-comment))))
+    ("Erase Customization" custom-face-reset-standard
      (lambda (widget)
-       (not (eq (widget-get widget :custom-form) 'all))))
-    ("Edit Lisp" custom-face-edit-lisp
+       (get (widget-value widget) 'face-defface-spec)))
+    ("---" ignore ignore)
+    ("Add Comment" custom-comment-show custom-comment-invisible-p)
+    ("---" ignore ignore)
+    ("Show all display specs" custom-face-edit-all
      (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
+       (not (eq (widget-get widget :custom-form) 'all))))
+    ("Just current attributes" custom-face-edit-selected
      (lambda (widget)
-       (get (widget-value widget) 'saved-face)))
-    ("Reset to Standard Setting" custom-face-reset-standard
+       (not (eq (widget-get widget :custom-form) 'selected))))
+    ("Show as Lisp expression" custom-face-edit-lisp
      (lambda (widget)
-       (get (widget-value widget) 'face-defface-spec))))
+       (not (eq (widget-get widget :custom-form) 'lisp)))))
   "Alist of actions for the `custom-face' widget.
 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.")
+widget.  If FILTER is nil, ACTION is always valid.")
 
 (defun custom-face-edit-selected (widget)
   "Edit selected attributes of the value of WIDGET."
@@ -1964,22 +2753,37 @@ widget. If FILTER is nil, ACTION is always valid.")
   (custom-redraw widget))
 
 (defun custom-face-edit-lisp (widget)
-  "Edit the lisp representation of the value of WIDGET."
+  "Edit the Lisp representation of the value of WIDGET."
   (widget-put widget :custom-state 'unknown)
   (widget-put widget :custom-form 'lisp)
   (custom-redraw widget))
 
 (defun custom-face-state-set (widget)
   "Set the state of WIDGET."
-  (let ((symbol (widget-value widget)))
-    (widget-put widget :custom-state (cond ((get symbol 'customized-face)
-                                           'set)
-                                          ((get symbol 'saved-face)
-                                           'saved)
-                                          ((get symbol 'face-defface-spec)
-                                           'standard)
-                                          (t 
-                                           'rogue)))))
+  (let* ((symbol (widget-value widget))
+        (comment (get symbol 'face-comment))
+        tmp temp)
+    (widget-put widget :custom-state
+               (cond ((progn
+                        (setq tmp (get symbol 'customized-face))
+                        (setq temp (get symbol 'customized-face-comment))
+                        (or tmp temp))
+                      (if (equal temp comment)
+                          'set
+                        'changed))
+                     ((progn
+                        (setq tmp (get symbol 'saved-face))
+                        (setq temp (get symbol 'saved-face-comment))
+                        (or tmp temp))
+                      (if (equal temp comment)
+                          'saved
+                        'changed))
+                     ((get symbol 'face-defface-spec)
+                      (if (equal comment nil)
+                          'standard
+                        'changed))
+                     (t
+                      'rogue)))))
 
 (defun custom-face-action (widget &optional event)
   "Show the menu for `custom-face' WIDGET.
@@ -2000,20 +2804,43 @@ Optional EVENT is the location for the menu."
   "Make the face attributes in WIDGET take effect."
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children)))
-        (value (widget-value child)))
+        (value (widget-value child))
+        (comment-widget (widget-get widget :comment-widget))
+        (comment (widget-value comment-widget)))
+    (when (equal comment "")
+      (setq comment nil)
+      ;; Make the comment invisible by hand if it's empty
+      (custom-comment-hide comment-widget))
     (put symbol 'customized-face value)
     (face-spec-set symbol value)
+    (put symbol 'customized-face-comment comment)
+    (put symbol 'face-comment comment)
     (custom-face-state-set widget)
     (custom-redraw-magic widget)))
 
+(defun custom-face-save-command (widget)
+  "Save in `.emacs' the face attributes in WIDGET."
+  (custom-face-save widget)
+  (custom-save-all))
+
 (defun custom-face-save (widget)
-  "Make the face attributes in WIDGET default."
+  "Prepare for saving WIDGET's face attributes, but don't write `.emacs'."
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children)))
-        (value (widget-value child)))
+        (value (widget-value child))
+        (comment-widget (widget-get widget :comment-widget))
+        (comment (widget-value comment-widget)))
+    (when (equal comment "")
+      (setq comment nil)
+      ;; Make the comment invisible by hand if it's empty
+      (custom-comment-hide comment-widget))
     (face-spec-set symbol value)
     (put symbol 'saved-face value)
     (put symbol 'customized-face nil)
+    (put symbol 'face-comment comment)
+    (put symbol 'customized-face-comment nil)
+    (put symbol 'saved-face-comment comment)
+    (custom-save-all)
     (custom-face-state-set widget)
     (custom-redraw-magic widget)))
 
@@ -2021,28 +2848,42 @@ Optional EVENT is the location for the menu."
   "Restore WIDGET to the face's default attributes."
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children)))
-        (value (get symbol 'saved-face)))
-    (unless value
+        (value (get symbol 'saved-face))
+        (comment (get symbol 'saved-face-comment))
+        (comment-widget (widget-get widget :comment-widget)))
+    (unless (or value comment)
       (error "No saved value for this face"))
     (put symbol 'customized-face nil)
+    (put symbol 'customized-face-comment nil)
     (face-spec-set symbol value)
+    (put symbol 'face-comment comment)
     (widget-value-set child value)
+    ;; This call manages the comment visibility
+    (widget-value-set comment-widget (or comment ""))
     (custom-face-state-set widget)
     (custom-redraw-magic widget)))
 
 (defun custom-face-reset-standard (widget)
-  "Restore WIDGET to the face's standard settings."
+  "Restore WIDGET to the face's standard settings.
+This operation eliminates any saved setting for the face,
+restoring it to the state of a face that has never been customized."
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children)))
-        (value (get symbol 'face-defface-spec)))
+        (value (get symbol 'face-defface-spec))
+        (comment-widget (widget-get widget :comment-widget)))
     (unless value
       (error "No standard setting for this face"))
     (put symbol 'customized-face nil)
-    (when (get symbol 'saved-face)
+    (put symbol 'customized-face-comment nil)
+    (when (or (get symbol 'saved-face) (get symbol 'saved-face-comment))
       (put symbol 'saved-face nil)
+      (put symbol 'saved-face-comment nil)
       (custom-save-all))
     (face-spec-set symbol value)
+    (put symbol 'face-comment nil)
     (widget-value-set child value)
+    ;; This call manages the comment visibility
+    (widget-value-set comment-widget "")
     (custom-face-state-set widget)
     (custom-redraw-magic widget)))
 
@@ -2051,7 +2892,9 @@ Optional EVENT is the location for the menu."
 (define-widget 'face 'default
   "Select and customize a face."
   :convert-widget 'widget-value-convert-widget
-  :format "%[%t%]: %v"
+  :button-prefix 'widget-push-button-prefix
+  :button-suffix 'widget-push-button-suffix
+  :format "%t: %[select face%] %v"
   :tag "Face"
   :value 'default
   :value-create 'widget-face-value-create
@@ -2059,14 +2902,14 @@ Optional EVENT is the location for the menu."
   :value-get 'widget-value-value-get
   :validate 'widget-children-validate
   :action 'widget-face-action
-  :match '(lambda (widget value) (symbolp value)))
+  :match (lambda (widget value) (symbolp value)))
 
 (defun widget-face-value-create (widget)
-  ;; Create a `custom-face' child.
+  "Create a `custom-face' child."
   (let* ((symbol (widget-value widget))
+        (custom-buffer-style 'face)
         (child (widget-create-child-and-convert
                 widget 'custom-face
-                :format "%t %s %L\n%m%h%v"
                 :custom-level nil
                 :value symbol)))
     (custom-magic-reset child)
@@ -2074,7 +2917,7 @@ Optional EVENT is the location for the menu."
     (widget-put widget :children (list child))))
 
 (defun widget-face-value-delete (widget)
-  ;; Remove the child from the options.
+  "Remove the child from the options."
   (let ((child (car (widget-get widget :children))))
     (setq custom-options (delq child custom-options))
     (widget-children-value-delete widget)))
@@ -2088,7 +2931,7 @@ Optional EVENT is the location for the menu."
                                 (mapcar (lambda (face)
                                           (list (symbol-name face)))
                                         (face-list))
-                                nil nil nil                             
+                                nil nil nil
                                 'face-history)))
     (unless (zerop (length answer))
       (widget-value-set widget (intern answer))
@@ -2099,13 +2942,27 @@ Optional EVENT is the location for the menu."
 
 (define-widget 'hook 'list
   "A emacs lisp hook"
+  :value-to-internal (lambda (widget value)
+                      (if (and value (symbolp value))
+                          (list value)
+                        value))
+  :match (lambda (widget value)
+          (or (symbolp value)
+              (widget-group-match widget value)))
+  ;; Avoid adding undefined functions to the hook, especially for
+  ;; things like `find-file-hook' or even more basic ones, to avoid
+  ;; chaos.
+  :set (lambda (symbol value)
+        (dolist (elt value)
+          (if (fboundp elt)
+              (add-hook symbol elt))))
   :convert-widget 'custom-hook-convert-widget
   :tag "Hook")
 
 (defun custom-hook-convert-widget (widget)
-  ;; Handle `:custom-options'.
+  ;; Handle `:options'.
   (let* ((options (widget-get widget :options))
-        (other `(editable-list :inline t 
+        (other `(editable-list :inline t
                                :entry-format "%i %d%v"
                                (function :format " %v")))
         (args (if options
@@ -2118,10 +2975,21 @@ Optional EVENT is the location for the menu."
     (widget-put widget :args args)
     widget))
 
+;;; The `custom-group-link' Widget.
+
+(define-widget 'custom-group-link 'link
+  "Show parent in other window when activated."
+  :help-echo "Create customization buffer for this group."
+  :action 'custom-group-link-action)
+
+(defun custom-group-link-action (widget &rest ignore)
+  (customize-group (widget-value widget)))
+
 ;;; The `custom-group' Widget.
 
-(defcustom custom-group-tag-faces '(custom-group-tag-face-1)
+(defcustom custom-group-tag-faces nil
   ;; In XEmacs, this ought to play games with font size.
+  ;; Fixme: make it do so in Emacs.
   "Face used for group tags.
 The first member is used for level 1 groups, the second for level 2,
 and so forth.  The remaining group tags are shown with
@@ -2129,28 +2997,48 @@ and so forth.  The remaining group tags are shown with
   :type '(repeat face)
   :group 'custom-faces)
 
-(defface custom-group-tag-face-1 '((((class color)
-                                    (background dark))
-                                   (:foreground "pink" :underline t))
-                                  (((class color)
-                                    (background light))
-                                   (:foreground "red" :underline t))
-                                  (t (:underline t)))
-  "Face used for group tags.")
-
-(defface custom-group-tag-face '((((class color)
-                                  (background dark))
-                                 (:foreground "light blue" :underline t))
-                                (((class color)
-                                  (background light))
-                                 (:foreground "blue" :underline t))
-                                (t (:underline t)))
+(defface custom-group-tag-face-1
+  `((((class color)
+      (background dark))
+     (:foreground "pink" :family "helv"
+                 :height ,(let ((height (face-attribute 'default :height)))
+                            (if (numberp height)
+                                (floor height 0.9)
+                              height))
+                 :bold t))
+    (((class color)
+      (background light))
+     (:foreground "red" :bold t
+                 :height ,(let ((height (face-attribute 'default :height)))
+                            (if (numberp height)
+                                (floor height 0.9)
+                              height))))
+    (t (:bold t)))
+  "Face used for group tags."
+  :group 'custom-faces)
+
+(defface custom-group-tag-face
+  `((((class color)
+      (background dark))
+     (:foreground "light blue" :bold t
+                 :height ,(let ((height (face-attribute 'default :height)))
+                            (if (numberp height)
+                                (floor height 0.9)
+                              height))))
+    (((class color)
+      (background light))
+     (:foreground "blue" :bold t
+                 :height ,(let ((height (face-attribute 'default :height)))
+                            (if (numberp height)
+                                (floor height 0.9)
+                              height))))
+    (t (:bold t)))
   "Face used for low level group tags."
   :group 'custom-faces)
 
 (define-widget 'custom-group 'custom
   "Customize group."
-  :format "%l %{%t%} group: %L %-\n%m%h%a%v%e"
+  :format "%v"
   :sample-face-get 'custom-group-sample-face-get
   :documentation-property 'group-documentation
   :help-echo "Set or reset all members of this group."
@@ -2169,49 +3057,253 @@ and so forth.  The remaining group tags are shown with
   (or (nth (1- (widget-get widget :custom-level)) custom-group-tag-faces)
       'custom-group-tag-face))
 
+(define-widget 'custom-group-visibility 'visibility
+  "An indicator and manipulator for hidden group contents."
+  :create 'custom-group-visibility-create)
+
+(defun custom-group-visibility-create (widget)
+  (let ((visible (widget-value widget)))
+    (if visible
+       (insert "--------")))
+  (widget-default-create widget))
+
+(defun custom-group-members (symbol groups-only)
+  "Return SYMBOL's custom group members.
+If GROUPS-ONLY non-nil, return only those members that are groups."
+  (if (not groups-only)
+      (get symbol 'custom-group)
+    (let (members)
+      (dolist (entry (get symbol 'custom-group))
+       (when (eq (nth 1 entry) 'custom-group)
+         (push entry members)))
+      (nreverse members))))
+
 (defun custom-group-value-create (widget)
-  (let ((state (widget-get widget :custom-state)))
-    (unless (eq state 'hidden)
-      (message "Creating group...")
-      (custom-load-widget widget)
-      (let* ((level (widget-get widget :custom-level))
-            (symbol (widget-value widget))
-            (members (sort (get symbol 'custom-group) 
-                           custom-buffer-sort-predicate))
-            (prefixes (widget-get widget :custom-prefixes))
-            (custom-prefix-list (custom-prefix-add symbol prefixes))
-            (length (length members))
-            (count 0)
-            (children (mapcar (lambda (entry)
-                                (widget-insert "\n")
-                                (message "Creating group members... %2d%%"
-                                         (/ (* 100.0 count) length))
-                                (setq count (1+ count))
-                                (prog1
-                                    (widget-create-child-and-convert
-                                     widget (nth 1 entry)
-                                     :group widget
-                                     :tag (custom-unlispify-tag-name
-                                           (nth 0 entry))
-                                     :custom-prefixes custom-prefix-list
-                                     :custom-level (1+ level)
-                                     :value (nth 0 entry))
-                                  (unless (eq (preceding-char) ?\n)
-                                    (widget-insert "\n"))))
-                              members)))
-       (put symbol 'custom-group members)
-       (message "Creating group magic...")
-       (mapcar 'custom-magic-reset children)
-       (message "Creating group state...")
-       (widget-put widget :children children)
-       (custom-group-state-update widget)
-       (message "Creating group... done")))))
-
-(defvar custom-group-menu 
-  '(("Set" custom-group-set
+  "Insert a customize group for WIDGET in the current buffer."
+  (let* ((state (widget-get widget :custom-state))
+        (level (widget-get widget :custom-level))
+        ;; (indent (widget-get widget :indent))
+        (prefix (widget-get widget :custom-prefix))
+        (buttons (widget-get widget :buttons))
+        (tag (widget-get widget :tag))
+        (symbol (widget-value widget))
+        (members (custom-group-members symbol
+                                       (and (eq custom-buffer-style 'tree)
+                                            custom-browse-only-groups))))
+    (cond ((and (eq custom-buffer-style 'tree)
+               (eq state 'hidden)
+               (or members (custom-unloaded-widget-p widget)))
+          (custom-browse-insert-prefix prefix)
+          (push (widget-create-child-and-convert
+                 widget 'custom-browse-visibility
+                 ;; :tag-glyph "plus"
+                 :tag "+")
+                buttons)
+          (insert "-- ")
+          ;; (widget-glyph-insert nil "-- " "horizontal")
+          (push (widget-create-child-and-convert
+                 widget 'custom-browse-group-tag)
+                buttons)
+          (insert " " tag "\n")
+          (widget-put widget :buttons buttons))
+         ((and (eq custom-buffer-style 'tree)
+               (zerop (length members)))
+          (custom-browse-insert-prefix prefix)
+          (insert "[ ]-- ")
+          ;; (widget-glyph-insert nil "[ ]" "empty")
+          ;; (widget-glyph-insert nil "-- " "horizontal")
+          (push (widget-create-child-and-convert
+                 widget 'custom-browse-group-tag)
+                buttons)
+          (insert " " tag "\n")
+          (widget-put widget :buttons buttons))
+         ((eq custom-buffer-style 'tree)
+          (custom-browse-insert-prefix prefix)
+          (custom-load-widget widget)
+          (if (zerop (length members))
+              (progn
+                (custom-browse-insert-prefix prefix)
+                (insert "[ ]-- ")
+                ;; (widget-glyph-insert nil "[ ]" "empty")
+                ;; (widget-glyph-insert nil "-- " "horizontal")
+                (push (widget-create-child-and-convert
+                       widget 'custom-browse-group-tag)
+                      buttons)
+                (insert " " tag "\n")
+                (widget-put widget :buttons buttons))
+            (push (widget-create-child-and-convert
+                   widget 'custom-browse-visibility
+                   ;; :tag-glyph "minus"
+                   :tag "-")
+                  buttons)
+            (insert "-\\ ")
+            ;; (widget-glyph-insert nil "-\\ " "top")
+            (push (widget-create-child-and-convert
+                   widget 'custom-browse-group-tag)
+                  buttons)
+            (insert " " tag "\n")
+            (widget-put widget :buttons buttons)
+            (message "Creating group...")
+            (let* ((members (custom-sort-items members
+                             custom-browse-sort-alphabetically
+                             custom-browse-order-groups))
+                   (prefixes (widget-get widget :custom-prefixes))
+                   (custom-prefix-list (custom-prefix-add symbol prefixes))
+                   (extra-prefix (if (widget-get widget :custom-last)
+                                     "   "
+                                   " | "))
+                   (prefix (concat prefix extra-prefix))
+                   children entry)
+              (while members
+                (setq entry (car members)
+                      members (cdr members))
+                (push (widget-create-child-and-convert
+                       widget (nth 1 entry)
+                       :group widget
+                       :tag (custom-unlispify-tag-name (nth 0 entry))
+                       :custom-prefixes custom-prefix-list
+                       :custom-level (1+ level)
+                       :custom-last (null members)
+                       :value (nth 0 entry)
+                       :custom-prefix prefix)
+                      children))
+              (widget-put widget :children (reverse children)))
+            (message "Creating group...done")))
+         ;; Nested style.
+         ((eq state 'hidden)
+          ;; Create level indicator.
+          (unless (eq custom-buffer-style 'links)
+            (insert-char ?\  (* custom-buffer-indent (1- level)))
+            (insert "-- "))
+          ;; Create tag.
+          (let ((begin (point)))
+            (insert tag)
+            (widget-specify-sample widget begin (point)))
+          (insert " group: ")
+          ;; Create link/visibility indicator.
+          (if (eq custom-buffer-style 'links)
+              (push (widget-create-child-and-convert
+                     widget 'custom-group-link
+                     :tag "Go to Group"
+                     symbol)
+                    buttons)
+            (push (widget-create-child-and-convert
+                   widget 'custom-group-visibility
+                   :help-echo "Show members of this group."
+                   :action 'custom-toggle-parent
+                   (not (eq state 'hidden)))
+                  buttons))
+          (insert " \n")
+          ;; Create magic button.
+          (let ((magic (widget-create-child-and-convert
+                        widget 'custom-magic nil)))
+            (widget-put widget :custom-magic magic)
+            (push magic buttons))
+          ;; Update buttons.
+          (widget-put widget :buttons buttons)
+          ;; Insert documentation.
+          (if (and (eq custom-buffer-style 'links) (> level 1))
+              (widget-put widget :documentation-indent 0))
+          (widget-default-format-handler widget ?h))
+         ;; Nested style.
+         (t                            ;Visible.
+          ;; Add parent groups references above the group.
+          (if t    ;;; This should test that the buffer
+                   ;;; was made to display a group.
+              (when (eq level 1)
+                (if (custom-add-parent-links widget
+                                             "Go to parent group:")
+                    (insert "\n"))))
+          ;; Create level indicator.
+          (insert-char ?\  (* custom-buffer-indent (1- level)))
+          (insert "/- ")
+          ;; Create tag.
+          (let ((start (point)))
+            (insert tag)
+            (widget-specify-sample widget start (point)))
+          (insert " group: ")
+          ;; Create visibility indicator.
+          (unless (eq custom-buffer-style 'links)
+            (insert "--------")
+            (push (widget-create-child-and-convert
+                   widget 'visibility
+                   :help-echo "Hide members of this group."
+                   :action 'custom-toggle-parent
+                   (not (eq state 'hidden)))
+                  buttons)
+            (insert " "))
+          ;; Create more dashes.
+          ;; Use 76 instead of 75 to compensate for the temporary "<"
+          ;; added by `widget-insert'.
+          (insert-char ?- (- 76 (current-column)
+                             (* custom-buffer-indent level)))
+          (insert "\\\n")
+          ;; Create magic button.
+          (let ((magic (widget-create-child-and-convert
+                        widget 'custom-magic
+                        :indent 0
+                        nil)))
+            (widget-put widget :custom-magic magic)
+            (push magic buttons))
+          ;; Update buttons.
+          (widget-put widget :buttons buttons)
+          ;; Insert documentation.
+          (widget-default-format-handler widget ?h)
+          ;; Parent groups.
+          (if nil  ;;; This should test that the buffer
+                   ;;; was not made to display a group.
+              (when (eq level 1)
+                (insert-char ?\  custom-buffer-indent)
+                (custom-add-parent-links widget)))
+          (custom-add-see-also widget
+                               (make-string (* custom-buffer-indent level)
+                                            ?\ ))
+          ;; Members.
+          (message "Creating group...")
+          (custom-load-widget widget)
+          (let* ((members (custom-sort-items members
+                                             custom-buffer-sort-alphabetically
+                                             custom-buffer-order-groups))
+                 (prefixes (widget-get widget :custom-prefixes))
+                 (custom-prefix-list (custom-prefix-add symbol prefixes))
+                 (length (length members))
+                 (count 0)
+                 (children (mapcar (lambda (entry)
+                                     (widget-insert "\n")
+                                     (message "\
+Creating group members... %2d%%"
+                                              (/ (* 100.0 count) length))
+                                     (setq count (1+ count))
+                                     (prog1
+                                         (widget-create-child-and-convert
+                                          widget (nth 1 entry)
+                                          :group widget
+                                          :tag (custom-unlispify-tag-name
+                                                (nth 0 entry))
+                                          :custom-prefixes custom-prefix-list
+                                          :custom-level (1+ level)
+                                          :value (nth 0 entry))
+                                       (unless (eq (preceding-char) ?\n)
+                                         (widget-insert "\n"))))
+                                   members)))
+            (message "Creating group magic...")
+            (mapc 'custom-magic-reset children)
+            (message "Creating group state...")
+            (widget-put widget :children children)
+            (custom-group-state-update widget)
+            (message "Creating group... done"))
+          ;; End line
+          (insert "\n")
+          (insert-char ?\  (* custom-buffer-indent (1- level)))
+          (insert "\\- " (widget-get widget :tag) " group end ")
+          (insert-char ?- (- 75 (current-column) (* custom-buffer-indent level)))
+          (insert "/\n")))))
+
+(defvar custom-group-menu
+  '(("Set for Current Session" custom-group-set
      (lambda (widget)
        (eq (widget-get widget :custom-state) 'modified)))
-    ("Save" custom-group-save
+    ("Save for Future Sessions" custom-group-save
      (lambda (widget)
        (memq (widget-get widget :custom-state) '(modified set))))
     ("Reset to Current" custom-group-reset-current
@@ -2228,7 +3320,7 @@ 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.")
+widget.  If FILTER is nil, ACTION is always valid.")
 
 (defun custom-group-action (widget &optional event)
   "Show the menu for `custom-group' WIDGET.
@@ -2248,42 +3340,42 @@ Optional EVENT is the location for the menu."
 (defun custom-group-set (widget)
   "Set changes in all modified group members."
   (let ((children (widget-get widget :children)))
-    (mapcar (lambda (child)
-             (when (eq (widget-get child :custom-state) 'modified)
-               (widget-apply child :custom-set)))
+    (mapc (lambda (child)
+           (when (eq (widget-get child :custom-state) 'modified)
+             (widget-apply child :custom-set)))
            children )))
 
 (defun custom-group-save (widget)
   "Save all modified group members."
   (let ((children (widget-get widget :children)))
-    (mapcar (lambda (child)
-             (when (memq (widget-get child :custom-state) '(modified set))
-               (widget-apply child :custom-save)))
+    (mapc (lambda (child)
+           (when (memq (widget-get child :custom-state) '(modified set))
+             (widget-apply child :custom-save)))
            children )))
 
 (defun custom-group-reset-current (widget)
   "Reset all modified group members."
   (let ((children (widget-get widget :children)))
-    (mapcar (lambda (child)
-             (when (eq (widget-get child :custom-state) 'modified)
-               (widget-apply child :custom-reset-current)))
+    (mapc (lambda (child)
+           (when (eq (widget-get child :custom-state) 'modified)
+             (widget-apply child :custom-reset-current)))
            children )))
 
 (defun custom-group-reset-saved (widget)
   "Reset all modified or set group members."
   (let ((children (widget-get widget :children)))
-    (mapcar (lambda (child)
-             (when (memq (widget-get child :custom-state) '(modified set))
-               (widget-apply child :custom-reset-saved)))
+    (mapc (lambda (child)
+           (when (memq (widget-get child :custom-state) '(modified set))
+             (widget-apply child :custom-reset-saved)))
            children )))
 
 (defun custom-group-reset-standard (widget)
   "Reset all modified, set, or saved group members."
   (let ((children (widget-get widget :children)))
-    (mapcar (lambda (child)
-             (when (memq (widget-get child :custom-state)
-                         '(modified set saved))
-               (widget-apply child :custom-reset-standard)))
+    (mapc (lambda (child)
+           (when (memq (widget-get child :custom-state)
+                       '(modified set saved))
+             (widget-apply child :custom-reset-standard)))
            children )))
 
 (defun custom-group-state-update (widget)
@@ -2306,61 +3398,114 @@ Optional EVENT is the location for the menu."
   (custom-magic-reset widget))
 
 ;;; The `custom-save-all' Function.
-
-(defcustom custom-file "~/.emacs"
+;;;###autoload
+(defcustom custom-file nil
   "File used for storing customization information.
-If you change this from the default \"~/.emacs\" you need to
-explicitly load that file for the settings to take effect."
-  :type 'file
+The default is nil, which means to use your init file
+as specified by `user-init-file'.  If you specify some other file,
+you need to explicitly load that file for the settings to take effect.
+
+When you change this variable, look in the previous custom file
+\(usually your init file) for the forms `(custom-set-variables ...)'
+and `(custom-set-faces ...)', and copy them (whichever ones you find)
+to the new custom file.  This will preserve your existing customizations."
+  :type '(choice (const :tag "Your Emacs init file" nil) file)
   :group 'customize)
 
+(defun custom-file ()
+  "Return the file name for saving customizations."
+  (setq custom-file
+       (or custom-file
+           user-init-file
+           (read-file-name "File for customizations: "
+                           "~/" nil nil ".emacs"))))
+
 (defun custom-save-delete (symbol)
-  "Delete the call to SYMBOL form `custom-file'.
-Leave point at the location of the call, or after the last expression."
-  (set-buffer (find-file-noselect custom-file))
+  "Visit `custom-file' and delete all calls to SYMBOL from it.
+Leave point at the old location of the first such call,
+or (if there were none) at the end of the buffer."
+  (let ((default-major-mode))
+    (set-buffer (find-file-noselect (custom-file))))
   (goto-char (point-min))
-  (catch 'found
-    (while t
-      (let ((sexp (condition-case nil
-                     (read (current-buffer))
-                   (end-of-file (throw 'found nil)))))
-       (when (and (listp sexp)
-                  (eq (car sexp) symbol))
-         (delete-region (save-excursion
-                          (backward-sexp)
-                          (point))
-                        (point))
-         (throw 'found nil))))))
+  ;; Skip all whitespace and comments.
+  (while (forward-comment 1))
+  (or (eobp)
+      (save-excursion (forward-sexp (buffer-size)))) ; Test for scan errors.
+  (let (first)
+    (catch 'found
+      (while t ;; We exit this loop only via throw.
+       ;; Skip all whitespace and comments.
+       (while (forward-comment 1))
+       (let ((start (point))
+             (sexp (condition-case nil
+                       (read (current-buffer))
+                     (end-of-file (throw 'found nil)))))
+         (when (and (listp sexp)
+                    (eq (car sexp) symbol))
+           (delete-region start (point))
+           (unless first
+             (setq first (point)))))))
+    (if first
+       (goto-char first)
+      (goto-char (point-max)))))
 
 (defun custom-save-variables ()
   "Save all customized variables in `custom-file'."
   (save-excursion
     (custom-save-delete 'custom-set-variables)
-    (let ((standard-output (current-buffer)))
+    (let ((standard-output (current-buffer))
+         (saved-list (make-list 1 0))
+         sort-fold-case)
+      ;; First create a sorted list of saved variables.
+      (mapatoms
+       (lambda (symbol)
+        (if (get symbol 'saved-value)
+            (nconc saved-list (list symbol)))))
+      (setq saved-list (sort (cdr saved-list) 'string<))
       (unless (bolp)
        (princ "\n"))
-      (princ "(custom-set-variables")
-      (mapatoms (lambda (symbol)
-                 (let ((value (get symbol 'saved-value))
-                       (requests (get symbol 'custom-requests))
-                       (now (not (or (get symbol 'standard-value)
-                                     (and (not (boundp symbol))
-                                          (not (get symbol 'force-value)))))))
-                   (when value
-                     (princ "\n '(")
-                     (princ symbol)
-                     (princ " ")
-                     (prin1 (car value))
-                     (cond (requests
-                            (if now
-                                (princ " t ")
-                              (princ " nil "))
-                            (prin1 requests)
-                            (princ ")"))
-                           (now
-                            (princ " t)"))
-                           (t
-                            (princ ")")))))))
+      (princ "(custom-set-variables
+  ;; custom-set-variables was added by Custom -- don't edit or cut/paste it!
+  ;; Your init file must only contain one such instance.\n")
+      (mapcar
+       (lambda (symbol)
+        (let ((value (get symbol 'saved-value))
+              (requests (get symbol 'custom-requests))
+              (now (not (or (get symbol 'standard-value)
+                            (and (not (boundp symbol))
+                                 (not (get symbol 'force-value))))))
+              (comment (get symbol 'saved-variable-comment))
+              sep)
+          (when (or value comment)
+            (unless (bolp)
+              (princ "\n"))
+            (princ " '(")
+            (prin1 symbol)
+            (princ " ")
+            (prin1 (car value))
+            (cond ((or now requests comment)
+                   (princ " ")
+                   (if now
+                       (princ "t")
+                     (princ "nil"))
+                   (cond ((or requests comment)
+                          (princ " ")
+                          (if requests
+                              (prin1 requests)
+                            (princ "nil"))
+                          (cond (comment
+                                 (princ " ")
+                                 (prin1 comment)
+                                 (princ ")"))
+                                (t
+                                 (princ ")"))))
+                         (t
+                          (princ ")"))))
+                  (t
+                   (princ ")"))))))
+       saved-list)
+      (if (bolp)
+         (princ " "))
       (princ ")")
       (unless (looking-at "\n")
        (princ "\n")))))
@@ -2369,103 +3514,102 @@ Leave point at the location of the call, or after the last expression."
   "Save all customized faces in `custom-file'."
   (save-excursion
     (custom-save-delete 'custom-set-faces)
-    (let ((standard-output (current-buffer)))
+    (let ((standard-output (current-buffer))
+         (saved-list (make-list 1 0))
+         sort-fold-case)
+      ;; First create a sorted list of saved faces.
+      (mapatoms
+       (lambda (symbol)
+        (if (get symbol 'saved-face)
+            (nconc saved-list (list symbol)))))
+      (setq saved-list (sort (cdr saved-list) 'string<))
+      ;; The default face must be first, since it affects the others.
+      (if (memq 'default saved-list)
+         (setq saved-list (cons 'default (delq 'default saved-list))))
       (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 'face-defface-spec)
-                 (and (not (custom-facep 'default))
-                      (not (get 'default 'force-face))))
-             (princ ")")
-           (princ " t)"))))
-      (mapatoms (lambda (symbol)
-                 (let ((value (get symbol 'saved-face)))
-                   (when (and (not (eq symbol 'default))
-                              ;; Don't print default face here.
-                              value)
-                     (princ "\n '(")
-                     (princ symbol)
-                     (princ " ")
-                     (prin1 value)
-                     (if (or (get symbol 'face-defface-spec)
-                             (and (not (custom-facep symbol))
-                                  (not (get symbol 'force-face))))
-                         (princ ")")
-                       (princ " t)"))))))
+      (princ "(custom-set-faces
+  ;; custom-set-faces was added by Custom -- don't edit or cut/paste it!
+  ;; Your init file must only contain one such instance.\n")
+      (mapcar
+       (lambda (symbol)
+        (let ((value (get symbol 'saved-face))
+              (now (not (or (get 'default 'face-defface-spec)
+                            (and (not (custom-facep 'default))
+                                 (not (get 'default 'force-face))))))
+              (comment (get 'default 'saved-face-comment)))
+          (unless (eq symbol 'default))
+          ;; Don't print default face here.
+          (unless (bolp)
+            (princ "\n"))
+          (princ " '(")
+          (prin1 symbol)
+          (princ " ")
+          (prin1 value)
+          (cond ((or now comment)
+                 (princ " ")
+                 (if now
+                     (princ "t")
+                   (princ "nil"))
+                 (cond (comment
+                        (princ " ")
+                        (prin1 comment)
+                        (princ ")"))
+                       (t
+                        (princ ")"))))
+                (t
+                 (princ ")")))))
+       saved-list)
+      (if (bolp)
+         (princ " "))
       (princ ")")
       (unless (looking-at "\n")
        (princ "\n")))))
 
 ;;;###autoload
-(defun custom-save-customized ()
+(defun customize-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 
+                   (value (get symbol 'customized-value))
+                   (face-comment (get symbol 'customized-face-comment))
+                   (variable-comment
+                    (get symbol 'customized-variable-comment)))
+               (when face
                  (put symbol 'saved-face face)
                  (put symbol 'customized-face nil))
-               (when value 
+               (when value
                  (put symbol 'saved-value value)
-                 (put symbol 'customized-value nil)))))
+                 (put symbol 'customized-value nil))
+               (when variable-comment
+                 (put symbol 'saved-variable-comment variable-comment)
+                 (put symbol 'customized-variable-comment nil))
+               (when face-comment
+                 (put symbol 'saved-face-comment face-comment)
+                 (put symbol 'customized-face-comment nil)))))
   ;; We really should update all custom buffers here.
   (custom-save-all))
 
 ;;;###autoload
 (defun custom-save-all ()
   "Save all customizations in `custom-file'."
-  (custom-save-variables)
-  (custom-save-faces)
-  (save-excursion
-    (set-buffer (find-file-noselect custom-file))
-    (save-buffer)))
+  (let ((inhibit-read-only t))
+    (custom-save-variables)
+    (custom-save-faces)
+    (save-excursion
+      (let ((default-major-mode nil))
+       (set-buffer (find-file-noselect (custom-file))))
+      (save-buffer))))
 
 ;;; The Customize Menu.
 
 ;;; Menu support
 
-(unless (string-match "XEmacs" emacs-version)
-  (defconst custom-help-menu '("Customize"
-                              ["Update menu..." custom-menu-update t]
-                              ["Group..." customize-group t]
-                              ["Variable..." customize-variable t]
-                              ["Face..." customize-face t]
-                              ["Saved..." customize-saved t]
-                              ["Set..." 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)
+  :group 'custom-menu)
 
 (defun custom-face-menu-create (widget symbol)
   "Ignoring WIDGET, create a menu entry for customization face SYMBOL."
@@ -2492,19 +3636,11 @@ Leave point at the location of the call, or after the last expression."
                                   ':style 'toggle
                                   ':selected symbol)))
 
-(if (string-match "XEmacs" emacs-version)
-    ;; XEmacs can create menus dynamically.
-    (defun custom-group-menu-create (widget symbol)
-      "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
-      `( ,(custom-unlispify-menu-entry symbol t)
-        :filter (lambda (&rest junk)
-                  (cdr (custom-menu-create ',symbol)))))
-  ;; But emacs can't.
-  (defun custom-group-menu-create (widget symbol)
-    "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
-    ;; Limit the nesting.
-    (let ((custom-menu-nesting (1- custom-menu-nesting)))
-      (custom-menu-create symbol))))
+(defun custom-group-menu-create (widget symbol)
+  "Ignoring WIDGET, create a menu entry for customization group SYMBOL."
+  `( ,(custom-unlispify-menu-entry symbol t)
+     :filter (lambda (&rest junk)
+              (cdr (custom-menu-create ',symbol)))))
 
 ;;;###autoload
 (defun custom-menu-create (symbol)
@@ -2518,9 +3654,9 @@ The menu is in a format applicable to `easy-menu-define'."
             (< (length (get symbol 'custom-group)) widget-menu-max-size))
        (let ((custom-prefix-list (custom-prefix-add symbol
                                                     custom-prefix-list))
-             (members (sort (get symbol 'custom-group)
-                            custom-menu-sort-predicate)))
-         (put symbol 'custom-group members)
+             (members (custom-sort-items (get symbol 'custom-group)
+                                         custom-menu-sort-alphabetically
+                                         custom-menu-order-groups)))
          (custom-load-symbol symbol)
          `(,(custom-unlispify-menu-entry symbol t)
            ,item
@@ -2536,50 +3672,73 @@ The menu is in a format applicable to `easy-menu-define'."
 ;;;###autoload
 (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. 
+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)))))
+  `(,name
+    :filter (lambda (&rest junk)
+             (cdr (custom-menu-create ',symbol)))))
 
 ;;; 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)
   (suppress-keymap custom-mode-map)
-  (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 
+  (define-key custom-mode-map " " 'scroll-up)
+  (define-key custom-mode-map "\177" 'scroll-down)
+  (define-key custom-mode-map "q" 'Custom-buffer-done)
+  (define-key custom-mode-map "u" 'Custom-goto-parent)
+  (define-key custom-mode-map "n" 'widget-forward)
+  (define-key custom-mode-map "p" 'widget-backward)
+  (define-key custom-mode-map [mouse-1] 'Custom-move-and-invoke))
+
+(defun Custom-move-and-invoke (event)
+  "Move to where you click, and if it is an active field, invoke it."
+  (interactive "e")
+  (mouse-set-point event)
+  (if (widget-event-point event)
+      (let* ((pos (widget-event-point event))
+            (button (get-char-property pos 'button)))
+       (if button
+           (widget-button-click event)))))
+
+(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-standard t]
-    ["Info" (Info-goto-node "(custom)The Customization Buffer") t]))
+    ,(customize-menu-create 'customize)
+    ["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-standard t]
+    ["Info" (Info-goto-node "(emacs)Easy Customization") t]))
+
+(defun Custom-goto-parent ()
+  "Go to the parent group listed at the top of this buffer.
+If several parents are listed, go to the first of them."
+  (interactive)
+  (save-excursion
+    (goto-char (point-min))
+    (if (search-forward "\nGo to parent group: " nil t)
+       (let* ((button (get-char-property (point) 'button))
+              (parent (downcase (widget-get  button :tag))))
+         (customize-group parent)))))
 
 (defcustom custom-mode-hook nil
-  "Hook called when entering custom-mode."
+  "Hook called when entering Custom mode."
   :type 'hook
-  :group 'customize)
+  :group 'custom-buffer )
+
+(defun custom-state-buffer-message (widget)
+  (if (eq (widget-get (widget-get widget :parent) :custom-state) 'modified)
+      (message "To install your edits, invoke [State] and choose the Set operation")))
 
 (defun custom-mode ()
   "Major mode for editing customization buffers.
@@ -2588,13 +3747,16 @@ The following commands are available:
 
 Move to next button or editable field.     \\[widget-forward]
 Move to previous button or editable field. \\[widget-backward]
-Invoke button under the mouse pointer.     \\[widget-button-click]
+\\<widget-field-keymap>\
+Complete content of editable text field.   \\[widget-complete]
+\\<custom-mode-map>\
+Invoke button under the mouse pointer.     \\[Custom-move-and-invoke]
 Invoke 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-standard]
+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-standard]
 
 Entry to this mode calls the value of `custom-mode-hook'
 if that value is non-nil."
@@ -2602,13 +3764,33 @@ if that value is non-nil."
   (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)
+  (easy-menu-add Custom-mode-menu)
   (make-local-variable 'custom-options)
+  (make-local-variable 'widget-documentation-face)
+  (setq widget-documentation-face 'custom-documentation-face)
+  (make-local-variable 'widget-button-face)
+  (setq widget-button-face 'custom-button-face)
+  (set (make-local-variable 'widget-button-pressed-face)
+       'custom-button-pressed-face)
+  (set (make-local-variable 'widget-mouse-face)
+       'custom-button-pressed-face)    ; buttons `depress' when moused
+  ;; When possible, use relief for buttons, not bracketing.  This test
+  ;; may not be optimal.
+  (when custom-raised-buttons
+    (set (make-local-variable 'widget-push-button-prefix) "")
+    (set (make-local-variable 'widget-push-button-suffix) "")
+    (set (make-local-variable 'widget-link-prefix) "")
+    (set (make-local-variable 'widget-link-suffix) ""))
+  (make-local-hook 'widget-edit-functions)
+  (add-hook 'widget-edit-functions 'custom-state-buffer-message nil t)
   (run-hooks 'custom-mode-hook))
 
+(add-to-list
+ 'debug-ignored-errors
+ "^No user options have changed defaults in recent Emacs versions$")
+
 ;;; The End.
 
 (provide 'cus-edit)
 
-;; cus-edit.el ends here
+;;; cus-edit.el ends here