lisp/ChangeLog: Update.
[bpt/emacs.git] / lisp / cus-edit.el
index ed9db15..7e014b4 100644 (file)
@@ -1,7 +1,7 @@
 ;;; cus-edit.el --- tools for customizing Emacs and Lisp packages
 ;;
 ;; Copyright (C) 1996, 1997, 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007 Free Software Foundation, Inc.
+;;   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Maintainer: FSF
 
 (require 'cus-face)
 (require 'wid-edit)
-(eval-when-compile
-  (defvar custom-versions-load-alist)  ; from cus-load
-  (defvar recentf-exclude))            ; from recentf.el
+
+(defvar custom-versions-load-alist)    ; from cus-load
+(defvar recentf-exclude)               ; from recentf.el
 
 (condition-case nil
     (require 'cus-load)
   :prefix "custom-"
   :group 'customize)
 
-(defgroup abbrev-mode nil
-  "Word abbreviations mode."
-  :link '(custom-manual "(emacs)Abbrevs")
-  :group 'abbrev)
-
 (defgroup alloc nil
   "Storage allocation and gc for GNU Emacs Lisp interpreter."
   :tag "Storage Allocation"
     map)
   "Local keymap for links in `custom-mode'.")
 
+(defvar custom-field-keymap
+  (let ((map (copy-keymap widget-field-keymap)))
+    (define-key map "\C-c\C-c" 'Custom-set)
+    (define-key map "\C-x\C-s" 'Custom-save)
+    map)
+  "Keymap used inside editable fields in customization buffers.")
+
+(widget-put (get 'editable-field 'widget-type) :keymap custom-field-keymap)
 
 ;;; Utilities.
 
 (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 from the result with:
   (mapconcat 'identity result \"\\|\")
 
 IF REGEXP is not a string, return it unchanged."
   (if (stringp regexp)
-      (let ((start 0)
-           all)
-       (while (string-match "\\\\|" regexp start)
-         (setq all (cons (substring regexp start (match-beginning 0)) all)
-               start (match-end 0)))
-       (nreverse (cons (substring regexp start) all)))
+      (split-string regexp "\\\\|")
     regexp))
 
 (defun custom-variable-prompt ()
@@ -760,52 +758,89 @@ groups after non-groups, if nil do not order groups at all."
 
 ;;; Custom Mode Commands.
 
-(defvar custom-options nil
-  "Customization widgets in the current buffer.")
-
-(defun Custom-set ()
-  "Set the current value of all edited settings in the buffer."
-  (interactive)
-  (let ((children custom-options))
-    (if (or (and (= 1 (length children))
-                (memq (widget-type (car children))
-                      '(custom-variable custom-face)))
-           (y-or-n-p "Set all values according to this buffer? "))
-       (mapc (lambda (child)
-               (when (eq (widget-get child :custom-state) 'modified)
-                 (widget-apply child :custom-set)))
-             children)
-      (message "Aborted"))))
-
-(defun Custom-save ()
-  "Set all edited settings, then save all settings that have been set.
-If a setting was edited and set before, this saves it.
-If a setting was merely edited before, this sets it then saves it."
+;; This variable is used by `custom-tool-bar-map', or directly by
+;; `custom-buffer-create-internal' if the toolbar is not present and
+;; `custom-buffer-verbose-help' is non-nil.
+
+(defvar custom-commands
+  '(("Set for current session" Custom-set t
+     "Apply all settings in this buffer to the current session"
+     "index")
+    ("Save for future sessions" Custom-save
+     (or custom-file user-init-file)
+     "Apply all settings in this buffer and save them for future Emacs sessions."
+     "save")
+    ("Undo edits" Custom-reset-current t
+     "Restore all settings in this buffer to reflect their current values."
+     "refresh")
+    ("Reset to saved" Custom-reset-saved t
+     "Restore all settings in this buffer to their saved values (if any)."
+     "undo")
+    ("Erase customizations" Custom-reset-standard
+     (or custom-file user-init-file)
+     "Un-customize all settings in this buffer and save them with standard values."
+     "delete")
+    ("Help for Customize" Custom-help t
+     "Get help for using Customize."
+     "help")
+    ("Exit" Custom-buffer-done t "Exit Customize." "exit")))
+
+(defun Custom-help ()
+  "Read the node on Easy Customization in the Emacs manual."
   (interactive)
-  (let ((children custom-options))
-    (if (or (and (= 1 (length children))
-                (memq (widget-type (car children))
-                      '(custom-variable custom-face)))
-           (yes-or-no-p "Save all settings in this buffer? "))
-       (progn
-         (mapc (lambda (child)
-                 (when (memq (widget-get child :custom-state)
-                             '(modified set changed rogue))
-                   (widget-apply child :custom-save)))
-               children)
-         (custom-save-all))
-      (message "Aborted"))))
+  (info "(emacs)Easy Customization"))
 
 (defvar custom-reset-menu
   '(("Undo Edits" . Custom-reset-current)
     ("Reset to Saved" . Custom-reset-saved)
-    ("Erase Customization (use standard values)" . Custom-reset-standard))
+    ("Erase Customizations (use standard values)" . 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
 when the action is chosen.")
 
-(defun custom-reset (event)
+(defvar custom-options nil
+  "Customization widgets in the current buffer.")
+
+(defun custom-command-apply (fun query &optional strong-query)
+  "Call function FUN on all widgets in `custom-options'.
+If there is more than one widget, ask user for confirmation using
+the query string QUERY, using `y-or-n-p' if STRONG-QUERY is nil,
+and `yes-or-no-p' otherwise."
+  (if (or (and (= 1 (length custom-options))
+              (memq (widget-type (car custom-options))
+                    '(custom-variable custom-face)))
+         (funcall (if strong-query 'yes-or-no-p 'y-or-n-p) query))
+      (progn (mapc fun custom-options) t)
+    (message "Aborted")
+    nil))
+
+(defun Custom-set (&rest ignore)
+  "Set the current value of all edited settings in the buffer."
+  (interactive)
+  (custom-command-apply
+   (lambda (child)
+     (when (eq (widget-get child :custom-state) 'modified)
+       (widget-apply child :custom-set)))
+   "Set all values according to this buffer? "))
+
+(defun Custom-save (&rest ignore)
+  "Set all edited settings, then save all settings that have been set.
+If a setting was edited and set before, this saves it.  If a
+setting was merely edited before, this sets it then saves it."
+  (interactive)
+  (when (custom-command-apply
+        (lambda (child)
+          (when (memq (widget-get child :custom-state)
+                      '(modified set changed rogue))
+            (widget-apply child :custom-mark-to-save)))
+        "Save all settings in this buffer? " t)
+    ;; Save changes to buffer and redraw.
+    (custom-save-all)
+    (dolist (child custom-options)
+      (widget-apply child :custom-state-set-and-redraw))))
+
+(defun custom-reset (widget &optional event)
   "Select item from reset menu."
   (let* ((completion-ignore-case t)
         (answer (widget-choose "Reset settings"
@@ -817,54 +852,83 @@ when the action is chosen.")
 (defun Custom-reset-current (&rest ignore)
   "Reset all edited settings in the buffer to show their current values."
   (interactive)
-  (let ((children custom-options))
-    (if (or (and (= 1 (length children))
-                (memq (widget-type (car children))
-                      '(custom-variable custom-face)))
-           (y-or-n-p "Reset all settings' buffer text to show current values? "))
-       (mapc (lambda (widget)
-               (if (memq (widget-get widget :custom-state)
-                         '(modified changed))
-                   (widget-apply widget :custom-reset-current)))
-             children)
-      (message "Aborted"))))
+  (custom-command-apply
+   (lambda (widget)
+     (if (memq (widget-get widget :custom-state) '(modified changed))
+        (widget-apply widget :custom-reset-current)))
+   "Reset all settings' buffer text to show current values? "))
 
 (defun Custom-reset-saved (&rest ignore)
   "Reset all edited or set settings in the buffer to their saved value.
 This also shows the saved values in the buffer."
   (interactive)
-  (let ((children custom-options))
-    (if (or (and (= 1 (length children))
-                (memq (widget-type (car children))
-                      '(custom-variable custom-face)))
-           (y-or-n-p "Reset all settings (current values and buffer text) to saved values? "))
-       (mapc (lambda (widget)
-               (if (memq (widget-get widget :custom-state)
-                         '(modified set changed rogue))
-                   (widget-apply widget :custom-reset-saved)))
-             children)
-      (message "Aborted"))))
+  (custom-command-apply
+   (lambda (widget)
+     (if (memq (widget-get widget :custom-state) '(modified set changed rogue))
+        (widget-apply widget :custom-reset-saved)))
+   "Reset all settings (current values and buffer text) to saved values? "))
+
+;; The next two variables are bound to '(t) by `Custom-reset-standard'
+;; and `custom-group-reset-standard'.  If these variables are nil, both
+;; `custom-variable-reset-standard' and `custom-face-reset-standard'
+;; save, reset and redraw the handled widget immediately.  Otherwise,
+;; they add the widget to the corresponding list and leave it to
+;; `custom-reset-standard-save-and-update' to save, reset and redraw it.
+(defvar custom-reset-standard-variables-list nil)
+(defvar custom-reset-standard-faces-list nil)
+
+;; The next function was excerpted from `custom-variable-reset-standard'
+;; and `custom-face-reset-standard' and is used to avoid calling
+;; `custom-save-all' repeatedly (and thus saving settings to file one by
+;; one) when erasing all customizations.
+(defun custom-reset-standard-save-and-update ()
+  "Save settings and redraw after erasing customizations."
+  (when (or (and custom-reset-standard-variables-list
+                (not (eq custom-reset-standard-variables-list  '(t))))
+           (and custom-reset-standard-faces-list
+                (not (eq custom-reset-standard-faces-list '(t)))))
+    ;; Save settings to file.
+    (custom-save-all)
+    ;; Set state of and redraw variables.
+    (dolist (widget custom-reset-standard-variables-list)
+      (unless (eq widget t)
+       (widget-put widget :custom-state 'unknown)
+       (custom-redraw widget)))
+    ;; Set state of and redraw faces.
+    (dolist (widget custom-reset-standard-faces-list)
+      (unless (eq widget t)
+       (let* ((symbol (widget-value widget))
+              (child (car (widget-get widget :children)))
+              (value (get symbol 'face-defface-spec))
+              (comment-widget (widget-get widget :comment-widget)))
+         (put symbol 'face-comment nil)
+         (widget-value-set child
+                           (custom-pre-filter-face-spec
+                            (list (list t (custom-face-attributes-get
+                                           symbol nil)))))
+         ;; This call manages the comment visibility
+         (widget-value-set comment-widget "")
+         (custom-face-state-set widget)
+         (custom-redraw-magic widget))))))
 
 (defun Custom-reset-standard (&rest ignore)
-  "Erase all customization (either current or saved) for the group members.
+  "Erase all customizations (either current or saved) in current buffer.
 The immediate result is to restore them to their standard values.
 This operation eliminates any saved values for the group members,
 making them as if they had never been customized at all."
   (interactive)
-  (let ((children custom-options))
-    (if (or (and (= 1 (length children))
-                (memq (widget-type (car children))
-                      '(custom-variable custom-face)))
-           (yes-or-no-p "Erase all customizations for settings in this buffer? "))
-       (mapc (lambda (widget)
-               (and (if (widget-get widget :custom-standard-value)
-                        (widget-apply widget :custom-standard-value)
-                      t)
-                    (memq (widget-get widget :custom-state)
-                          '(modified set changed saved rogue))
-                    (widget-apply widget :custom-reset-standard)))
-             children)
-      (message "Aborted"))))
+  ;; Bind these temporarily.
+  (let ((custom-reset-standard-variables-list '(t))
+       (custom-reset-standard-faces-list '(t)))
+    (custom-command-apply
+     (lambda (widget)
+       (and (or (null (widget-get widget :custom-standard-value))
+               (widget-apply widget :custom-standard-value))
+           (memq (widget-get widget :custom-state)
+                 '(modified set changed saved rogue))
+           (widget-apply widget :custom-mark-to-reset-standard)))
+     "Erase all customizations for settings in this buffer? " t)
+    (custom-reset-standard-save-and-update)))
 
 ;;; The Customize Commands
 
@@ -893,9 +957,9 @@ it as the third element in the list."
            (cond (prop
                   ;; Use VAR's `variable-interactive' property
                   ;; as an interactive spec for prompting.
-                  (call-interactively (list 'lambda '(arg)
-                                            (list 'interactive prop)
-                                            'arg)))
+                  (call-interactively `(lambda (arg)
+                                         (interactive ,prop)
+                                         arg)))
                  (type
                   (widget-prompt-value type
                                        prompt
@@ -1021,19 +1085,20 @@ then prompt for the MODE to customize."
                          t nil nil (if group (symbol-name major-mode))))))))
   (customize-group (custom-group-of-mode mode)))
 
+(defun customize-read-group ()
+  (let ((completion-ignore-case t))
+    (completing-read "Customize group (default emacs): "
+                     obarray
+                     (lambda (symbol)
+                       (or (and (get symbol 'custom-loads)
+                                (not (get symbol 'custom-autoload)))
+                           (get symbol 'custom-group)))
+                     t)))
 
 ;;;###autoload
-(defun customize-group (group)
+(defun customize-group (&optional 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 (and (get symbol 'custom-loads)
-                                      (not (get symbol 'custom-autoload)))
-                                 (get symbol 'custom-group)))
-                           t))))
+  (interactive (list (customize-read-group)))
   (when (stringp group)
     (if (string-equal "" group)
        (setq group 'emacs)
@@ -1041,42 +1106,21 @@ then prompt for the MODE to customize."
   (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 (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 (and (get symbol 'custom-loads)
-                                      (not (get symbol 'custom-autoload)))
-                                 (get symbol 'custom-group)))
-                           t))))
-  (when (stringp group)
-    (if (string-equal "" group)
-       (setq group 'emacs)
-      (setq group (intern group))))
-  (let ((name (format "*Customize Group: %s*"
-                     (custom-unlispify-tag-name group))))
-    (if (get-buffer name)
-       (let (
-             ;; Copied from `custom-buffer-create-other-window'.
-             (pop-up-windows t)
-             (same-window-buffer-names nil)
-             (same-window-regexps nil))
-         (pop-to-buffer name))
-      (custom-buffer-create-other-window
+        (pop-to-buffer name)
+      (custom-buffer-create
        (list (list group 'custom-group))
        name
        (concat " for group "
-              (custom-unlispify-tag-name group))))))
+               (custom-unlispify-tag-name group))))))
+
+;;;###autoload
+(defun customize-group-other-window (&optional group)
+  "Customize GROUP, which must be a customization group, in another window."
+  (interactive (list (customize-read-group)))
+  (let ((pop-up-windows t)
+        (same-window-buffer-names nil)
+        (same-window-regexps nil))
+    (customize-group group)))
 
 ;;;###autoload
 (defalias 'customize-variable 'customize-option)
@@ -1151,7 +1195,7 @@ the official name of the package, such as MH-E or Gnus.")
 (defalias 'customize-changed 'customize-changed-options)
 
 ;;;###autoload
-(defun customize-changed-options (since-version)
+(defun customize-changed-options (&optional since-version)
   "Customize all settings whose meanings have changed in Emacs itself.
 This includes new user option variables and faces, and new
 customization groups, as well as older options and faces whose meanings
@@ -1264,27 +1308,26 @@ face-alias, customize the face it is aliased to.
 
 Interactively, when point is on text which has a face specified,
 suggest to customize that face, if it's customizable."
-  (interactive
-   (list (read-face-name "Customize face" "all faces" t)))
+  (interactive (list (read-face-name "Customize face" "all faces" t)))
   (if (member face '(nil ""))
       (setq face (face-list)))
   (if (and (listp face) (null (cdr face)))
       (setq face (car face)))
   (if (listp face)
-      (custom-buffer-create (custom-sort-items
-                            (mapcar (lambda (s)
-                                      (list s 'custom-face))
-                                    face)
-                            t nil)
-                           "*Customize Faces*")
+      (custom-buffer-create
+       (custom-sort-items
+        (mapcar (lambda (s) (list s 'custom-face)) face)
+        t nil)
+       "*Customize Faces*")
     ;; If FACE is actually an alias, customize the face it is aliased to.
     (if (get face 'face-alias)
         (setq face (get face 'face-alias)))
     (unless (facep face)
       (error "Invalid face %S" face))
-    (custom-buffer-create (list (list face 'custom-face))
-                         (format "*Customize Face: %s*"
-                                 (custom-unlispify-tag-name face)))))
+    (custom-buffer-create
+     (list (list face 'custom-face))
+     (format "*Customize Face: %s*"
+             (custom-unlispify-tag-name face)))))
 
 ;;;###autoload
 (defun customize-face-other-window (&optional face)
@@ -1293,28 +1336,11 @@ If FACE is actually a face-alias, customize the face it is aliased to.
 
 Interactively, when point is on text which has a face specified,
 suggest to customize that face, if it's customizable."
-  (interactive
-   (list (read-face-name "Customize face" "all faces" t)))
-  (if (member face '(nil ""))
-      (setq face (face-list)))
-  (if (and (listp face) (null (cdr face)))
-      (setq face (car face)))
-  (if (listp face)
-      (custom-buffer-create-other-window
-       (custom-sort-items
-       (mapcar (lambda (s)
-                 (list s 'custom-face))
-               face)
-       t nil)
-       "*Customize Faces*")
-    (if (get face 'face-alias)
-        (setq face (get face 'face-alias)))
-    (unless (facep face)
-      (error "Invalid face %S" face))
-    (custom-buffer-create-other-window
-     (list (list face 'custom-face))
-     (format "*Customize Face: %s*"
-            (custom-unlispify-tag-name face)))))
+  (interactive (list (read-face-name "Customize face" "all faces" t)))
+  (let ((pop-up-windows t)
+        (same-window-buffer-names nil)
+        (same-window-regexps nil))
+    (customize-face face)))
 
 (defalias 'customize-customized 'customize-unsaved)
 
@@ -1384,7 +1410,7 @@ If ALL is `groups', include only groups.
 If ALL is t (interactively, with prefix arg), include variables
 that are not customizable options, as well as faces and groups
 \(but we recommend using `apropos-variable' instead)."
-  (interactive "sCustomize regexp: \nP")
+  (interactive "sCustomize (regexp): \nP")
   (let ((found nil))
     (mapatoms (lambda (symbol)
                (when (string-match regexp (symbol-name symbol))
@@ -1403,7 +1429,14 @@ that are not customizable options, as well as faces and groups
                                      (get symbol 'variable-documentation))))
                    (push (list symbol 'custom-variable) found)))))
     (if (not found)
-       (error "No customizable items matching %s" regexp)
+       (error "No %s matching %s"
+               (if (eq all t)
+                   "items"
+                 (format "customizable %s"
+                         (if (memq all '(options faces groups))
+                             (symbol-name all)
+                           "items")))
+               regexp)
       (custom-buffer-create
        (custom-sort-items found t custom-buffer-order-groups)
        "*Customize Apropos*"))))
@@ -1412,20 +1445,20 @@ that are not customizable options, as well as faces and groups
 (defun customize-apropos-options (regexp &optional arg)
   "Customize all loaded customizable options matching REGEXP.
 With prefix arg, include variables that are not customizable options
-\(but we recommend using `apropos-variable' instead)."
-  (interactive "sCustomize regexp: \nP")
+\(but it is better to use `apropos-variable' if you want to find those)."
+  (interactive "sCustomize options (regexp): \nP")
   (customize-apropos regexp (or arg 'options)))
 
 ;;;###autoload
 (defun customize-apropos-faces (regexp)
   "Customize all loaded faces matching REGEXP."
-  (interactive "sCustomize regexp: \n")
+  (interactive "sCustomize faces (regexp): \n")
   (customize-apropos regexp 'faces))
 
 ;;;###autoload
 (defun customize-apropos-groups (regexp)
   "Customize all loaded groups matching REGEXP."
-  (interactive "sCustomize regexp: \n")
+  (interactive "sCustomize groups (regexp): \n")
   (customize-apropos regexp 'groups))
 
 ;;; Buffer.
@@ -1546,99 +1579,61 @@ Otherwise use brackets."
 
 (defun custom-buffer-create-internal (options &optional description)
   (custom-mode)
-  (if custom-buffer-verbose-help
-      (progn
-       (widget-insert "This is a customization buffer")
-       (if description
-           (widget-insert description))
-       (widget-insert (format ".
-%s buttons; type RET or click mouse-1 to actuate one.
-Editing a setting changes only the text in the buffer."
-                              (if custom-raised-buttons
-                                  "`Raised' text indicates"
-                                "Square brackets indicate")))
-       (if init-file-user
-           (widget-insert "
-Use the setting's State button to set it or save changes in it.
-Saving a change normally works by editing your Emacs init file.")
-           (widget-insert "
-\nSince you started Emacs with `-q', which inhibits use of the
-Emacs init file, you cannot save settings into the Emacs init file."))
-       (widget-insert "\nSee ")
-       (widget-create 'custom-manual
-                      :tag "Custom file"
-                      "(emacs)Saving Customizations")
-       (widget-insert
-        " for information on how to save in a different file.\n
-See ")
-       (widget-create 'custom-manual
-                      :tag "Help"
-                      :help-echo "Read the online help."
-                      "(emacs)Easy Customization")
-       (widget-insert " for more information.\n\n")
-       (widget-insert "Operate on all settings in this buffer that \
-are not marked HIDDEN:\n "))
-    (widget-insert " "))
-  (widget-create 'push-button
-                :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)))
-  (if (not custom-buffer-verbose-help)
-      (progn
-       (widget-insert " ")
-       (widget-create 'custom-manual
-                      :tag "Help"
-                      :help-echo "Read the online help."
-                      "(emacs)Easy Customization")))
-  (when (or custom-file user-init-file)
-    (widget-insert " ")
-    (widget-create 'push-button
-                  :tag "Save for Future Sessions"
-                  :help-echo "\
-Make your editing in this buffer take effect for future Emacs sessions.
-This updates your Emacs initialization file or creates a new one."
-                  :action (lambda (widget &optional event)
-                            (Custom-save))))
-  (if custom-reset-button-menu
-      (progn
-       (widget-insert " ")
-       (widget-create 'push-button
-                      :tag "Reset buffer"
-                      :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 "Undo Edits"
-                  :help-echo "\
-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 settings in this buffer to their saved values."
-                  :action 'Custom-reset-saved)
-    (widget-insert " ")
-    (when (or custom-file user-init-file)
-      (widget-create 'push-button
-                    :tag "Erase Customization"
-                    :help-echo "\
-Un-customize all settings in this buffer and save them with standard values."
-                    :action 'Custom-reset-standard)))
-  (widget-insert "   ")
-  (widget-create 'push-button
-                :tag "Finish"
-                :help-echo
-                (lambda (&rest ignore)
-                  (if custom-buffer-done-kill
-                      "Kill this buffer"
-                    "Bury this buffer"))
-                :action #'Custom-buffer-done)
-  (widget-insert "\n\n")
+  (let ((init-file (or custom-file user-init-file)))
+    ;; Insert verbose help at the top of the custom buffer.
+    (when custom-buffer-verbose-help
+      (widget-insert "Editing a setting changes only the text in this buffer."
+                    (if init-file
+                        "
+To apply your changes, use the Save or Set buttons.
+Saving a change normally works by editing your init file."
+                      "
+Currently, these settings cannot be saved for future Emacs sessions,
+possibly because you started Emacs with `-q'.")
+                    "\nFor details, see ")
+      (widget-create 'custom-manual
+                    :tag "Saving Customizations"
+                    "(emacs)Saving Customizations")
+      (widget-insert " in the ")
+      (widget-create 'custom-manual
+                    :tag "Emacs manual"
+                    :help-echo "Read the Emacs manual."
+                    "(emacs)Top")
+      (widget-insert "."))
+    ;; Insert custom command buttons if the toolbar is not in use.
+
+    (widget-insert "\n")
+    ;; tool-bar is not dumped in builds without x.
+    (when (not (and (bound-and-true-p tool-bar-mode) (display-graphic-p)))
+      (if custom-buffer-verbose-help
+         (widget-insert "\n
+ Operate on all settings in this buffer that are not marked HIDDEN:\n"))
+      (let ((button (lambda (tag action active help icon)
+                     (widget-insert " ")
+                     (if (eval active)
+                         (widget-create 'push-button :tag tag
+                                        :help-echo help :action action))))
+           (commands custom-commands))
+       (apply button (pop commands)) ; Set for current session
+       (apply button (pop commands)) ; Save for future sessions
+       (if custom-reset-button-menu
+           (progn
+             (widget-insert " ")
+             (widget-create 'push-button
+                            :tag "Reset buffer"
+                            :help-echo "Show a menu with reset operations."
+                            :mouse-down-action 'ignore
+                            :action 'custom-reset))
+         (widget-insert "\n")
+         (apply button (pop commands)) ; Undo edits
+         (apply button (pop commands)) ; Reset to saved
+         (apply button (pop commands)) ; Erase customization
+         (widget-insert "  ")
+         (pop commands) ; Help (omitted)
+         (apply button (pop commands))))) ; Exit
+    (widget-insert "\n\n"))
+
+  ;; Now populate the custom buffer.
   (message "Creating customization items...")
   (buffer-disable-undo)
   (setq custom-options
@@ -1776,7 +1771,7 @@ item in another window.\n\n"))
 (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)
+  (if nil ; (featurep 'xemacs)
       (progn
        (insert "*")
        (while (not (string-equal prefix ""))
@@ -2313,7 +2308,8 @@ Insert PREFIX first if non-nil."
               (insert ", "))))
       (widget-put widget :buttons buttons))))
 
-(defun custom-add-parent-links (widget &optional initial-string)
+(defun custom-add-parent-links (widget &optional initial-string
+                                      doc-initial-string)
   "Add \"Parent groups: ...\" to WIDGET if the group has parents.
 The value is non-nil if any parents were found.
 If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
@@ -2322,7 +2318,7 @@ If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
        (buttons (widget-get widget :buttons))
        (start (point))
        (parents nil))
-    (insert (or initial-string "Parent groups:"))
+    (insert (or initial-string "Groups:"))
     (mapatoms (lambda (symbol)
                (when (member (list name type) (get symbol 'custom-group))
                  (insert " ")
@@ -2341,23 +2337,27 @@ If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
                                         (get (car parents) 'custom-links))))
                 (many (> (length links) 2)))
            (when links
-             (insert "\nParent documentation: ")
-             (while links
-               (push (widget-create-child-and-convert
-                     widget (car links)
-                     :button-face 'custom-link
-                     :mouse-face 'highlight
-                     :pressed-face 'highlight)
-                     buttons)
-               (setq links (cdr links))
-               (cond ((null links)
-                      (insert ".\n"))
-                     ((null (cdr links))
-                      (if many
-                          (insert ", and ")
-                        (insert " and ")))
-                     (t
-                      (insert ", ")))))))
+             (let ((pt (point))
+                   (left-margin (+ left-margin 2)))
+              (insert "\n" (or doc-initial-string "Group documentation:") " ")
+              (while links
+                (push (widget-create-child-and-convert
+                       widget (car links)
+                       :button-face 'custom-link
+                       :mouse-face 'highlight
+                       :pressed-face 'highlight)
+                      buttons)
+                (setq links (cdr links))
+                (cond ((null links)
+                       (insert ".\n"))
+                      ((null (cdr links))
+                       (if many
+                           (insert ", and ")
+                         (insert " and ")))
+                      (t
+                        (insert ", "))))
+               (fill-region-as-paragraph pt (point))
+               (delete-to-left-margin (1+ pt) (+ pt 2))))))
     (if parents
         (insert "\n")
       (delete-region start (point)))
@@ -2439,13 +2439,13 @@ If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
 (defface custom-variable-tag
   `((((class color)
       (background dark))
-     (:foreground "light blue" :weight bold :height 1.2 :inherit variable-pitch))
+     (:foreground "light blue" :weight bold))
     (((min-colors 88) (class color)
       (background light))
-     (:foreground "blue1" :weight bold :height 1.2 :inherit variable-pitch))
+     (:foreground "blue1" :weight bold))
     (((class color)
       (background light))
-     (:foreground "blue" :weight bold :height 1.2 :inherit variable-pitch))
+     (:foreground "blue" :weight bold))
     (t (:weight bold)))
   "Face used for unpushable variable tags."
   :group 'custom-faces)
@@ -2491,11 +2491,13 @@ However, setting it through Custom sets the default value.")
   :value-create 'custom-variable-value-create
   :action 'custom-variable-action
   :custom-set 'custom-variable-set
-  :custom-save 'custom-variable-save
+  :custom-mark-to-save 'custom-variable-mark-to-save
   :custom-reset-current 'custom-redraw
   :custom-reset-saved 'custom-variable-reset-saved
   :custom-reset-standard 'custom-variable-reset-standard
-  :custom-standard-value 'custom-variable-standard-value)
+  :custom-mark-to-reset-standard 'custom-variable-mark-to-reset-standard
+  :custom-standard-value 'custom-variable-standard-value
+  :custom-state-set-and-redraw 'custom-variable-state-set-and-redraw)
 
 (defun custom-variable-type (symbol)
   "Return a widget suitable for editing the value of SYMBOL.
@@ -2636,15 +2638,11 @@ try matching its doc string against `custom-guess-doc-alist'."
                    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 "\n")
       ;; Insert documentation.
-      (widget-default-format-handler widget ?h)
+      (widget-put widget :documentation-indent 3)
+      (widget-add-documentation-string-button
+       widget :visibility-widget 'custom-visibility)
 
       ;; The comment field
       (unless (eq state 'hidden)
@@ -2744,7 +2742,12 @@ try matching its doc string against `custom-guess-doc-alist'."
   `(("Set for Current Session" custom-variable-set
      (lambda (widget)
        (eq (widget-get widget :custom-state) 'modified)))
-    ,@(when (or custom-file user-init-file)
+    ;; Note that in all the backquoted code in this file, we test
+    ;; init-file-user rather than user-init-file.  This is in case
+    ;; cus-edit is loaded by something in site-start.el, because
+    ;; user-init-file is not set at that stage.
+    ;; http://lists.gnu.org/archive/html/emacs-devel/2007-10/msg00310.html
+    ,@(when (or custom-file init-file-user)
        '(("Save for Future Sessions" custom-variable-save
           (lambda (widget)
             (memq (widget-get widget :custom-state)
@@ -2759,7 +2762,7 @@ try matching its doc string against `custom-guess-doc-alist'."
                (get (widget-value widget) 'saved-variable-comment))
            (memq (widget-get widget :custom-state)
                  '(modified set changed rogue)))))
-    ,@(when (or custom-file user-init-file)
+    ,@(when (or custom-file init-file-user)
        '(("Erase Customization" custom-variable-reset-standard
           (lambda (widget)
             (and (get (widget-value widget) 'standard-value)
@@ -2856,8 +2859,8 @@ Optional EVENT is the location for the menu."
     (custom-variable-state-set widget)
     (custom-redraw-magic widget)))
 
-(defun custom-variable-save (widget)
-  "Set and save the value for the variable being edited by WIDGET."
+(defun custom-variable-mark-to-save (widget)
+  "Set value and mark for saving the variable edited by WIDGET."
   (let* ((form (widget-get widget :custom-form))
         (state (widget-get widget :custom-state))
         (child (car (widget-get widget :children)))
@@ -2895,10 +2898,18 @@ Optional EVENT is the location for the menu."
           (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)))
+    (put symbol 'customized-variable-comment nil)))
+
+(defsubst custom-variable-state-set-and-redraw (widget)
+  "Set state of variable widget WIDGET and redraw with current settings."
+  (custom-variable-state-set widget)
+  (custom-redraw-magic widget))
+
+(defun custom-variable-save (widget)
+  "Save value of variable edited by widget WIDGET."
+  (custom-variable-mark-to-save widget)
+  (custom-save-all)
+  (custom-variable-state-set-and-redraw widget))
 
 (defun custom-variable-reset-saved (widget)
   "Restore the saved value for the variable being edited by WIDGET.
@@ -2924,12 +2935,10 @@ becomes the backup value, so you can get it again."
     ;; 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.
-This operation eliminates any saved setting for the variable,
-restoring it to the state of a variable that has never been customized.
-The value that was current before this operation
-becomes the backup value, so you can get it again."
+(defun custom-variable-mark-to-reset-standard (widget)
+  "Mark to restore standard setting for the variable edited by widget WIDGET.
+If `custom-reset-standard-variables-list' is nil, save, reset and
+redraw the widget immediately."
   (let* ((symbol (widget-value widget)))
     (if (get symbol 'standard-value)
        (custom-variable-backup-value widget)
@@ -2939,13 +2948,32 @@ becomes the backup value, so you can get it again."
     (put symbol 'customized-variable-comment nil)
     (custom-push-theme 'theme-value symbol 'user 'reset)
     (custom-theme-recalc-variable symbol)
-    (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)))
+    (if (and custom-reset-standard-variables-list
+            (or (get symbol 'saved-value) (get symbol 'saved-variable-comment)))
+       (progn
+         (put symbol 'saved-value nil)
+         (put symbol 'saved-variable-comment nil)
+         ;; Append this to `custom-reset-standard-variables-list' to
+         ;; have `custom-reset-standard-save-and-update' save setting
+         ;; to the file, update the widget's state, and redraw it.
+         (setq custom-reset-standard-variables-list
+               (cons widget custom-reset-standard-variables-list)))
+      (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))))
+
+(defun custom-variable-reset-standard (widget)
+  "Restore standard setting for the variable 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.
+The value that was current before this operation
+becomes the backup value, so you can get it again."
+  (let (custom-reset-standard-variables-list)
+    (custom-variable-mark-to-reset-standard widget)))
 
 (defun custom-variable-backup-value (widget)
   "Back up the current value for WIDGET's variable.
@@ -2984,6 +3012,21 @@ to switch between two values."
     ;; This call will possibly make the comment invisible
     (custom-redraw widget)))
 
+;;; The `custom-visibility' Widget
+
+(define-widget 'custom-visibility 'visibility
+  "Show or hide a documentation string."
+  :button-face 'custom-visibility
+  :pressed-face 'custom-visibility
+  :mouse-face 'highlight
+  :pressed-face 'highlight)
+
+(defface custom-visibility
+  '((t :height 0.8 :inherit link))
+  "Face for the `custom-visibility' widget."
+  :version "23.1"
+  :group 'custom-faces)
+
 ;;; The `custom-face-edit' Widget.
 
 (define-widget 'custom-face-edit 'checklist
@@ -3058,7 +3101,7 @@ Also change :reverse-video to :inverse-video."
                    (cons value (cons from (- (point) from))))))))
 
 (defun custom-face-edit-activate (widget)
-  "Make face widget WIDGET inactive for user modifications."
+  "Make face widget WIDGET active for user modifications."
   (let ((inactive (widget-get widget :inactive))
        (inhibit-read-only t)
        (inhibit-modification-hooks t))
@@ -3206,11 +3249,13 @@ Only match frames that support the specified face attributes.")
   :custom-category 'face
   :custom-form nil ; defaults to value of `custom-face-default-form'
   :custom-set 'custom-face-set
-  :custom-save 'custom-face-save
+  :custom-mark-to-save 'custom-face-mark-to-save
   :custom-reset-current 'custom-redraw
   :custom-reset-saved 'custom-face-reset-saved
   :custom-reset-standard 'custom-face-reset-standard
+  :custom-mark-to-reset-standard 'custom-face-mark-to-reset-standard
   :custom-standard-value 'custom-face-standard-value
+  :custom-state-set-and-redraw 'custom-face-state-set-and-redraw
   :custom-menu 'custom-face-menu-create)
 
 (define-widget 'custom-face-all 'editable-list
@@ -3355,7 +3400,10 @@ SPEC must be a full face spec."
           ;; Update buttons.
           (widget-put widget :buttons buttons)
           ;; Insert documentation.
-          (widget-default-format-handler widget ?h)
+          (widget-put widget :documentation-indent 3)
+          (widget-add-documentation-string-button
+           widget :visibility-widget 'custom-visibility)
+
           ;; The comment field
           (unless (eq state 'hidden)
             (let* ((comment (get symbol 'face-comment))
@@ -3416,7 +3464,7 @@ SPEC must be a full face spec."
 
 (defvar custom-face-menu
   `(("Set for Current Session" custom-face-set)
-    ,@(when (or custom-file user-init-file)
+    ,@(when (or custom-file init-file-user)
        '(("Save for Future Sessions" custom-face-save)))
     ("Undo Edits" custom-redraw
      (lambda (widget)
@@ -3425,7 +3473,7 @@ SPEC must be a full face spec."
      (lambda (widget)
        (or (get (widget-value widget) 'saved-face)
           (get (widget-value widget) 'saved-face-comment))))
-    ,@(when (or custom-file user-init-file)
+    ,@(when (or custom-file init-file-user)
        '(("Erase Customization" custom-face-reset-standard
           (lambda (widget)
             (get (widget-value widget) 'face-defface-spec)))))
@@ -3533,17 +3581,17 @@ Optional EVENT is the location for the menu."
     (put symbol 'customized-face value)
     (custom-push-theme 'theme-face symbol 'user 'set value)
     (if (face-spec-choose value)
-       (face-spec-set symbol value)
+       (face-spec-set symbol value t)
       ;; face-set-spec ignores empty attribute lists, so just give it
       ;; something harmless instead.
-      (face-spec-set symbol '((t :foreground unspecified))))
+      (face-spec-set symbol '((t :foreground unspecified)) t))
     (put symbol 'customized-face-comment comment)
     (put symbol 'face-comment comment)
     (custom-face-state-set widget)
     (custom-redraw-magic widget)))
 
-(defun custom-face-save (widget)
-  "Save in `.emacs' the face attributes in WIDGET."
+(defun custom-face-mark-to-save (widget)
+  "Mark for saving the face edited by WIDGET."
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children)))
         (value (custom-post-filter-face-spec (widget-value child)))
@@ -3555,19 +3603,27 @@ Optional EVENT is the location for the menu."
       (custom-comment-hide comment-widget))
     (custom-push-theme 'theme-face symbol 'user 'set value)
     (if (face-spec-choose value)
-       (face-spec-set symbol value)
+       (face-spec-set symbol value t)
       ;; face-set-spec ignores empty attribute lists, so just give it
       ;; something harmless instead.
-      (face-spec-set symbol '((t :foreground unspecified))))
+      (face-spec-set symbol '((t :foreground unspecified)) t))
     (unless (eq (widget-get widget :custom-state) 'standard)
       (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)))
+    (put symbol 'saved-face-comment comment)))
+
+(defsubst custom-face-state-set-and-redraw (widget)
+  "Set state of face widget WIDGET and redraw with current settings."
+  (custom-face-state-set widget)
+  (custom-redraw-magic widget))
+
+(defun custom-face-save (widget)
+  "Save the face edited by WIDGET."
+  (custom-face-mark-to-save widget)
+  (custom-save-all)
+  (custom-face-state-set-and-redraw widget))
 
 ;; For backward compatibility.
 (define-obsolete-function-alias 'custom-face-save-command 'custom-face-save
@@ -3585,7 +3641,7 @@ Optional EVENT is the location for the menu."
     (put symbol 'customized-face nil)
     (put symbol 'customized-face-comment nil)
     (custom-push-theme 'theme-face symbol 'user 'set value)
-    (face-spec-set symbol value)
+    (face-spec-set symbol value t)
     (put symbol 'face-comment comment)
     (widget-value-set child value)
     ;; This call manages the comment visibility
@@ -3596,10 +3652,10 @@ Optional EVENT is the location for the menu."
 (defun custom-face-standard-value (widget)
   (get (widget-value widget) 'face-defface-spec))
 
-(defun custom-face-reset-standard (widget)
-  "Restore WIDGET to the face's standard attribute values.
-This operation eliminates any saved attributes for the face,
-restoring it to the state of a face that has never been customized."
+(defun custom-face-mark-to-reset-standard (widget)
+  "Restore widget WIDGET to the face's standard attribute values.
+If `custom-reset-standard-faces-list' is nil, save, reset and
+redraw the widget immediately."
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children)))
         (value (get symbol 'face-defface-spec))
@@ -3609,21 +3665,39 @@ restoring it to the state of a face that has never been customized."
     (put symbol 'customized-face nil)
     (put symbol 'customized-face-comment nil)
     (custom-push-theme 'theme-face symbol 'user 'reset)
-    (face-spec-set symbol value)
+    (face-spec-set symbol value t)
     (custom-theme-recalc-face symbol)
-    (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))
-    (put symbol 'face-comment nil)
-    (widget-value-set child
-                     (custom-pre-filter-face-spec
-                      (list (list t (custom-face-attributes-get
-                                     symbol nil)))))
-    ;; This call manages the comment visibility
-    (widget-value-set comment-widget "")
-    (custom-face-state-set widget)
-    (custom-redraw-magic widget)))
+    (if (and custom-reset-standard-faces-list
+            (or (get symbol 'saved-face) (get symbol 'saved-face-comment)))
+       ;; Do this later.
+       (progn
+         (put symbol 'saved-face nil)
+         (put symbol 'saved-face-comment nil)
+         ;; Append this to `custom-reset-standard-faces-list' and have
+         ;; `custom-reset-standard-save-and-update' save setting to the
+         ;; file, update the widget's state, and redraw it.
+         (setq custom-reset-standard-faces-list
+               (cons widget custom-reset-standard-faces-list)))
+      (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))
+      (put symbol 'face-comment nil)
+      (widget-value-set child
+                       (custom-pre-filter-face-spec
+                        (list (list t (custom-face-attributes-get
+                                       symbol nil)))))
+      ;; This call manages the comment visibility
+      (widget-value-set comment-widget "")
+      (custom-face-state-set widget)
+      (custom-redraw-magic widget))))
+
+(defun custom-face-reset-standard (widget)
+  "Restore WIDGET to the face's standard attribute values.
+This operation eliminates any saved attributes for the face,
+restoring it to the state of a face that has never been customized."
+  (let (custom-reset-standard-faces-list)
+    (custom-face-mark-to-reset-standard widget)))
 
 ;;; The `face' Widget.
 
@@ -3745,13 +3819,13 @@ and so forth.  The remaining group tags are shown with `custom-group-tag'."
 (defface custom-group-tag
   `((((class color)
       (background dark))
-     (:foreground "light blue" :weight bold :height 1.2))
+     (:foreground "light blue" :weight bold :height 1.2 :inherit variable-pitch))
     (((min-colors 88) (class color)
       (background light))
-     (:foreground "blue1" :weight bold :height 1.2))
+     (:foreground "blue1" :weight bold :height 1.2 :inherit variable-pitch))
     (((class color)
       (background light))
-     (:foreground "blue" :weight bold :height 1.2))
+     (:foreground "blue" :weight bold :height 1.2 :inherit variable-pitch))
     (t (:weight bold)))
   "Face used for low level group tags."
   :group 'custom-faces)
@@ -3768,10 +3842,12 @@ and so forth.  The remaining group tags are shown with `custom-group-tag'."
   :action 'custom-group-action
   :custom-category 'group
   :custom-set 'custom-group-set
-  :custom-save 'custom-group-save
+  :custom-mark-to-save 'custom-group-mark-to-save
   :custom-reset-current 'custom-group-reset-current
   :custom-reset-saved 'custom-group-reset-saved
   :custom-reset-standard 'custom-group-reset-standard
+  :custom-mark-to-reset-standard 'custom-group-mark-to-reset-standard
+  :custom-state-set-and-redraw 'custom-group-state-set-and-redraw
   :custom-menu 'custom-group-menu-create)
 
 (defun custom-group-sample-face-get (widget)
@@ -3895,28 +3971,22 @@ If GROUPS-ONLY non-nil, return only those members that are groups."
          ;; 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"
+                     :tag tag
                      symbol)
                     buttons)
+            (insert-char ?\  (* custom-buffer-indent (1- level)))
+            (insert "-- ")
             (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")
+          (insert " ")
           ;; Create magic button.
           (let ((magic (widget-create-child-and-convert
                         widget 'custom-magic nil)))
@@ -3927,7 +3997,9 @@ If GROUPS-ONLY non-nil, return only those members that are groups."
           ;; Insert documentation.
           (if (and (eq custom-buffer-style 'links) (> level 1))
               (widget-put widget :documentation-indent 0))
-          (widget-default-format-handler widget ?h))
+          (widget-add-documentation-string-button
+           widget :visibility-widget 'custom-visibility))
+
          ;; Nested style.
          (t                            ;Visible.
           ;; Add parent groups references above the group.
@@ -3935,16 +4007,17 @@ If GROUPS-ONLY non-nil, return only those members that are groups."
                    ;;; was made to display a group.
               (when (eq level 1)
                 (if (custom-add-parent-links widget
-                                             "Parent groups:")
+                                             "Parent groups:"
+                                             "Parent group documentation:")
                     (insert "\n"))))
           ;; Create level indicator.
           (insert-char ?\  (* custom-buffer-indent (1- level)))
           (insert "/- ")
           ;; Create tag.
           (let ((start (point)))
-            (insert tag)
+            (insert tag " group: ")
             (widget-specify-sample widget start (point)))
-          (insert " group: ")
+          (insert (widget-docstring widget))
           ;; Create visibility indicator.
           (unless (eq custom-buffer-style 'links)
             (insert "--------")
@@ -3971,7 +4044,9 @@ If GROUPS-ONLY non-nil, return only those members that are groups."
           ;; Update buttons.
           (widget-put widget :buttons buttons)
           ;; Insert documentation.
-          (widget-default-format-handler widget ?h)
+          (widget-add-documentation-string-button
+           widget :visibility-widget 'custom-visibility)
+
           ;; Parent groups.
           (if nil  ;;; This should test that the buffer
                    ;;; was not made to display a group.
@@ -4025,7 +4100,7 @@ Creating group members... %2d%%"
   `(("Set for Current Session" custom-group-set
      (lambda (widget)
        (eq (widget-get widget :custom-state) 'modified)))
-    ,@(when (or custom-file user-init-file)
+    ,@(when (or custom-file init-file-user)
        '(("Save for Future Sessions" custom-group-save
           (lambda (widget)
             (memq (widget-get widget :custom-state) '(modified set))))))
@@ -4035,7 +4110,7 @@ Creating group members... %2d%%"
     ("Reset to Saved" custom-group-reset-saved
      (lambda (widget)
        (memq (widget-get widget :custom-state) '(modified set))))
-    ,@(when (or custom-file user-init-file)
+    ,@(when (or custom-file init-file-user)
        '(("Erase Customization" custom-group-reset-standard
           (lambda (widget)
             (memq (widget-get widget :custom-state) '(modified set saved)))))))
@@ -4063,44 +4138,53 @@ 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)))
-    (mapc (lambda (child)
-           (when (eq (widget-get child :custom-state) 'modified)
-             (widget-apply child :custom-set)))
-           children )))
+  (dolist (child (widget-get widget :children))
+    (when (eq (widget-get child :custom-state) 'modified)
+      (widget-apply child :custom-set))))
+
+(defun custom-group-mark-to-save (widget)
+  "Mark all modified group members for saving."
+  (dolist (child (widget-get widget :children))
+    (when (memq (widget-get child :custom-state) '(modified set))
+      (widget-apply child :custom-mark-to-save))))
+
+(defsubst custom-group-state-set-and-redraw (widget)
+  "Set state of group widget WIDGET and redraw with current settings."
+  (dolist (child (widget-get widget :children))
+    (when (memq (widget-get child :custom-state) '(modified set))
+      (widget-apply child :custom-state-set-and-redraw))))
 
 (defun custom-group-save (widget)
   "Save all modified group members."
-  (let ((children (widget-get widget :children)))
-    (mapc (lambda (child)
-           (when (memq (widget-get child :custom-state) '(modified set))
-             (widget-apply child :custom-save)))
-           children )))
+  (custom-group-mark-to-save widget)
+  (custom-save-all)
+  (custom-group-state-set-and-redraw widget))
 
 (defun custom-group-reset-current (widget)
   "Reset all modified group members."
-  (let ((children (widget-get widget :children)))
-    (mapc (lambda (child)
-           (when (eq (widget-get child :custom-state) 'modified)
-             (widget-apply child :custom-reset-current)))
-           children )))
+  (dolist (child (widget-get widget :children))
+    (when (eq (widget-get child :custom-state) 'modified)
+      (widget-apply child :custom-reset-current))))
 
 (defun custom-group-reset-saved (widget)
   "Reset all modified or set group members."
-  (let ((children (widget-get widget :children)))
-    (mapc (lambda (child)
-           (when (memq (widget-get child :custom-state) '(modified set))
-             (widget-apply child :custom-reset-saved)))
-           children )))
+  (dolist (child (widget-get widget :children))
+    (when (memq (widget-get child :custom-state) '(modified set))
+      (widget-apply child :custom-reset-saved))))
 
 (defun custom-group-reset-standard (widget)
   "Reset all modified, set, or saved group members."
-  (let ((children (widget-get widget :children)))
-    (mapc (lambda (child)
-           (when (memq (widget-get child :custom-state)
-                       '(modified set saved))
-             (widget-apply child :custom-reset-standard)))
-           children )))
+  (let ((custom-reset-standard-variables-list '(t))
+       (custom-reset-standard-faces-list '(t)))
+    (custom-group-mark-to-reset-standard widget)
+    (custom-reset-standard-save-and-update)))
+
+(defun custom-group-mark-to-reset-standard (widget)
+  "Mark to reset all modified, set, or saved group members."
+  (dolist (child (widget-get widget :children))
+    (when (memq (widget-get child :custom-state)
+               '(modified set saved))
+      (widget-apply child :custom-mark-to-reset-standard))))
 
 (defun custom-group-state-update (widget)
   "Update magic."
@@ -4183,6 +4267,9 @@ if only the first line of the docstring is shown."))
           (setq user-init-file default-init-file))
         user-init-file))))
 
+;; If recentf-mode is non-nil, this is defined.
+(declare-function recentf-expand-file-name "recentf" (name))
+
 ;;;###autoload
 (defun custom-save-all ()
   "Save all customizations in `custom-file'."
@@ -4489,6 +4576,29 @@ The format is suitable for use with `easy-menu-define'."
              (let ((menu (custom-menu-create ',symbol)))
                (if (consp menu) (cdr menu) menu)))))
 
+;;; Toolbar and menubar support
+
+(easy-menu-define
+  Custom-mode-menu (list custom-mode-map custom-field-keymap)
+  "Menu used in customization buffers."
+  (nconc (list "Custom"
+              (customize-menu-create 'customize))
+        (mapcar (lambda (arg)
+                  (let ((tag     (nth 0 arg))
+                        (command (nth 1 arg))
+                        (active  (nth 2 arg))
+                        (help    (nth 3 arg)))
+                    (vector tag command :active (eval active) :help help)))
+                custom-commands)))
+
+(defvar tool-bar-map)
+
+;;; `custom-tool-bar-map' used to be set up here.  This will fail to
+;;; DTRT when `display-graphic-p' returns nil during compilation.  Hence
+;;; we set this up lazily in `custom-mode'.
+(defvar custom-tool-bar-map nil
+  "Keymap for toolbar in Custom mode.")
+
 ;;; The Custom Mode.
 
 (defun Custom-no-edit (pos &optional event)
@@ -4504,27 +4614,6 @@ The format is suitable for use with `easy-menu-define'."
        (widget-apply-action button event)
       (error "You can't edit this part of the Custom buffer"))))
 
-(easy-menu-define Custom-mode-menu
-    custom-mode-map
-  "Menu used in customization buffers."
-  `("Custom"
-    ,(customize-menu-create 'customize)
-    ["Set" Custom-set t]
-    ["Save" Custom-save t]
-    ["Undo Edits" Custom-reset-current t]
-    ["Reset to Saved" Custom-reset-saved t]
-    ["Erase Customization" Custom-reset-standard t]
-    ["Info" (info "(emacs)Easy Customization") t]))
-
-(defvar custom-field-keymap
-  (let ((map (copy-keymap widget-field-keymap)))
-    (define-key map "\C-c\C-c" 'Custom-set)
-    (define-key map "\C-x\C-s" 'Custom-save)
-    map)
-  "Keymap used inside editable fields in customization buffers.")
-
-(widget-put (get 'editable-field 'widget-type) :keymap custom-field-keymap)
-
 (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."
@@ -4545,7 +4634,7 @@ If several parents are listed, go to the first of them."
   (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 ()
+(define-derived-mode custom-mode nil "Custom"
   "Major mode for editing customization buffers.
 
 The following commands are available:
@@ -4567,17 +4656,26 @@ Erase customizations; set options
 
 Entry to this mode calls the value of `custom-mode-hook'
 if that value is non-nil."
-  (kill-all-local-variables)
-  (setq major-mode 'custom-mode
-       mode-name "Custom")
   (use-local-map custom-mode-map)
   (easy-menu-add Custom-mode-menu)
+  (when (display-graphic-p)
+    (set (make-local-variable 'tool-bar-map)
+        (or custom-tool-bar-map
+            ;; Set up `custom-tool-bar-map'.
+            (let ((map (make-sparse-keymap)))
+              (mapc
+               (lambda (arg)
+                 (tool-bar-local-item-from-menu
+                  (nth 1 arg) (nth 4 arg) map custom-mode-map))
+               custom-commands)
+              (setq custom-tool-bar-map map)))))
   (make-local-variable 'custom-options)
   (make-local-variable 'custom-local-buffer)
   (make-local-variable 'widget-documentation-face)
   (setq widget-documentation-face 'custom-documentation)
   (make-local-variable 'widget-button-face)
   (setq widget-button-face custom-button)
+  (setq show-trailing-whitespace nil)
 
   ;; We need this because of the "More" button on docstrings.
   ;; Otherwise clicking on "More" can push point offscreen, which
@@ -4595,8 +4693,7 @@ if that value is non-nil."
     (set (make-local-variable 'widget-push-button-suffix) "")
     (set (make-local-variable 'widget-link-prefix) "")
     (set (make-local-variable 'widget-link-suffix) ""))
-  (add-hook 'widget-edit-functions 'custom-state-buffer-message nil t)
-  (run-mode-hooks 'custom-mode-hook))
+  (add-hook 'widget-edit-functions 'custom-state-buffer-message nil t))
 
 (put 'custom-mode 'mode-class 'special)