(parse_charset_map): Remove an unused variable.
[bpt/emacs.git] / lisp / cus-edit.el
index a559fbe..eb7fe04 100644 (file)
@@ -1,6 +1,6 @@
-;;; cus-edit.el --- Tools for customizing Emacs and Lisp packages.
+;;; cus-edit.el --- tools for customizing Emacs and Lisp packages
 ;;
-;; Copyright (C) 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
+;; Copyright (C) 1996, 1997, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Keywords: help, faces
@@ -36,9 +36,7 @@
 
 (require 'cus-face)
 (require 'wid-edit)
-(require 'easymenu)
 (eval-when-compile
-  (require 'cl)
   (defvar custom-versions-load-alist)) ; from cus-load
 
 (condition-case nil
@@ -385,7 +383,6 @@ 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))
@@ -399,7 +396,7 @@ Return a list suitable for use in `interactive'."
                          (and (boundp symbol)
                               (or (get symbol 'custom-type)
                                   (get symbol 'custom-loads)
-                                  (user-variable-p symbol)))) t))
+                                  (get symbol 'standard-value)))) t))
      (list (if (equal val "")
               (if (symbolp v) v nil)
             (intern val)))))
@@ -434,6 +431,7 @@ WIDGET is the widget to apply the filter entries of MENU on."
 (defcustom custom-unlispify-remove-prefixes nil
   "Non-nil means remove group prefixes from option names in buffer."
   :group 'custom-menu
+  :group 'custom-buffer
   :type 'boolean)
 
 (defun custom-unlispify-menu-entry (symbol &optional no-suffix)
@@ -636,19 +634,19 @@ groups after non-groups, if nil do not order groups at all."
   "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 ()
   "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 changed rogue))
-               (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))
 
@@ -674,22 +672,22 @@ when the action is chosen.")
   "Reset all modified group members to their current value."
   (interactive)
   (let ((children custom-options))
-    (mapcar (lambda (widget)
-             (and (default-boundp (widget-value widget))
-                  (if (memq (widget-get widget :custom-state)
-                            '(modified changed))
-                      (widget-apply widget :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)
   "Reset all modified or set group members to their saved value."
   (interactive)
   (let ((children custom-options))
-    (mapcar (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))))
+    (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)
@@ -699,11 +697,11 @@ 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 (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))))
+    (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
@@ -751,7 +749,7 @@ it as the third element in the list."
 
 ;;;###autoload
 (defun customize-set-value (var val &optional comment)
-  "Set VARIABLE to VALUE.  VALUE is a Lisp object.
+  "Set VARIABLE to VALUE, and return 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.
@@ -764,15 +762,16 @@ If given a prefix (or a COMMENT argument), also prompt for a comment."
                                       "Set %s to value: "
                                       current-prefix-arg))
    
-  (set var val)
   (cond ((string= comment "")
         (put var 'variable-comment nil))
        (comment
-        (put var 'variable-comment comment))))
+        (put var 'variable-comment comment)))
+  (set var val))
 
 ;;;###autoload
 (defun customize-set-variable (variable value &optional comment)
-  "Set the default for VARIABLE to VALUE.  VALUE is a Lisp object.
+  "Set the default for VARIABLE to VALUE, and return VALUE.
+VALUE is a Lisp object.
 
 If VARIABLE has a `custom-set' property, that is used for setting
 VARIABLE, otherwise `set-default' is used.
@@ -790,6 +789,7 @@ 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: "
                                       current-prefix-arg))
+  (custom-load-symbol variable)
   (funcall (or (get variable 'custom-set) 'set-default) variable value)
   (put variable 'customized-value (list (custom-quote value)))
   (cond ((string= comment "")
@@ -797,11 +797,14 @@ If given a prefix (or a COMMENT argument), also prompt for a comment."
         (put variable 'customized-variable-comment nil))
        (comment
         (put variable 'variable-comment comment)
-        (put variable 'customized-variable-comment comment))))
+        (put variable 'customized-variable-comment comment)))
+  value)
 
 ;;;###autoload
 (defun customize-save-variable (var value &optional comment)
   "Set the default for VARIABLE to VALUE, and save it for future sessions.
+Return VALUE.
+
 If VARIABLE has a `custom-set' property, that is used for setting
 VARIABLE, otherwise `set-default' is used.
 
@@ -815,7 +818,7 @@ 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: "
+  (interactive (custom-prompt-variable "Set and save variable: "
                                       "Set and save value for %s as: "
                                       current-prefix-arg))
   (funcall (or (get var 'custom-set) 'set-default) var value)
@@ -826,7 +829,8 @@ If given a prefix (or a COMMENT argument), also prompt for a comment."
        (comment
         (put var 'variable-comment comment)
         (put var 'saved-variable-comment comment)))
-  (custom-save-all))
+  (custom-save-all)
+  value)
 
 ;;;###autoload
 (defun customize ()
@@ -939,7 +943,11 @@ version."
 
   (interactive "sCustomize options changed, since version (default all versions): ")
   (if (equal since-version "")
-      (setq since-version nil))
+      (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)
@@ -990,17 +998,24 @@ version."
                            "*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 (match-string 1 version1)))
-    (setq minor1 (read (match-string 2 version1)))
-    (string-match "\\([0-9]+\\)[.]\\([0-9]+\\)" version2)
-    (setq major2 (read (match-string 1 version2)))
-    (setq minor2 (read (match-string 2 version2)))
+    (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)))))
@@ -1287,10 +1302,19 @@ Un-customize all values in this buffer.  They get their standard settings."
   (widget-insert "   ")
   (widget-create 'push-button
                 :tag "Finish"
-                :help-echo "Bury or kill the buffer."
+                :help-echo
+                (lambda (&rest ignore)
+                  (cond
+                   ((eq custom-buffer-done-function
+                        'custom-bury-buffer)
+                    "Bury this buffer")
+                   ((eq custom-buffer-done-function 'kill-buffer)
+                    "Kill this buffer")
+                   (t "Finish with this buffer")))
                 :action #'Custom-buffer-done)
   (widget-insert "\n\n")
   (message "Creating customization items...")
+  (buffer-disable-undo)
   (setq custom-options
        (if (= (length options) 1)
            (mapcar (lambda (entry)
@@ -1304,25 +1328,26 @@ Un-customize all values in this buffer.  They get their standard settings."
          (let ((count 0)
                (length (length options)))
            (mapcar (lambda (entry)
-                       (prog2
-                           (message "Creating customization items ...%2d%%"
-                                    (/ (* 100.0 count) length))
-                           (widget-create (nth 1 entry)
+                     (prog2
+                         (message "Creating customization items ...%2d%%"
+                                  (/ (* 100.0 count) length))
+                         (widget-create (nth 1 entry)
                                         :tag (custom-unlispify-tag-name
                                               (nth 0 entry))
                                         :value (nth 0 entry))
-                         (setq count (1+ count))
-                         (unless (eq (preceding-char) ?\n)
-                           (widget-insert "\n"))
-                         (widget-insert "\n")))
-                     options))))
+                       (setq count (1+ count))
+                       (unless (eq (preceding-char) ?\n)
+                         (widget-insert "\n"))
+                       (widget-insert "\n")))
+                   options))))
   (unless (eq (preceding-char) ?\n)
     (widget-insert "\n"))
-  (message "Creating customization items ...%2d%%done" 100)
+  (message "Creating customization items ...done")
   (unless (eq custom-buffer-style 'tree)
-    (mapcar 'custom-magic-reset custom-options))
+    (mapc 'custom-magic-reset custom-options))
   (message "Creating customization setup...")
   (widget-setup)
+  (buffer-enable-undo)
   (goto-char (point-min))
   (message "Creating customization buffer...done"))
 
@@ -1467,7 +1492,7 @@ item in another window.\n\n"))
 (defface custom-invalid-face '((((class color))
                                (:foreground "yellow" :background "red"))
                               (t
-                               (:bold t :italic t :underline t)))
+                               (:weight bold :slant italic :underline t)))
   "Face used when the customize item is invalid."
   :group 'custom-magic-faces)
 
@@ -1481,21 +1506,21 @@ item in another window.\n\n"))
 (defface custom-modified-face '((((class color))
                                 (:foreground "white" :background "blue"))
                                (t
-                                (:italic t :bold)))
+                                (:slant italic :bold)))
   "Face used when the customize item has been modified."
   :group 'custom-magic-faces)
 
 (defface custom-set-face '((((class color))
                                (:foreground "blue" :background "white"))
                               (t
-                               (:italic t)))
+                               (:slant italic)))
   "Face used when the customize item has been set."
   :group 'custom-magic-faces)
 
 (defface custom-changed-face '((((class color))
                                (:foreground "white" :background "blue"))
                               (t
-                               (:italic t)))
+                               (:slant italic)))
   "Face used when the customize item has been changed."
   :group 'custom-magic-faces)
 
@@ -1686,10 +1711,9 @@ 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"))
-    (((type w32) (class color))                ; Like default modeline
-     (:box (:line-width 2 :style released-button) :background "lightgrey"))
+  '((((type x w32 mac) (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."
@@ -1697,10 +1721,9 @@ and `face'."
   :group 'custom-faces)
 
 (defface custom-button-pressed-face
-  '((((type x) (class color))
-     (:box (:line-width 2 :style pressed-button) :background "lightgrey"))
-    (((type w32) (class color))
-     (:box (:line-width 2 :style pressed-button) :background "lightgrey"))
+  '((((type x w32 mac) (class color))
+     (:box (:line-width 2 :style pressed-button)
+          :background "lightgrey" :foreground "black"))
     (t
      (:inverse-video t)))
   "Face used for buttons in customization buffers."
@@ -1799,6 +1822,7 @@ and `face'."
 (defvar custom-load-recursion nil
   "Hack to avoid recursive dependencies.")
 
+;;;###autoload
 (defun custom-load-symbol (symbol)
   "Load all dependencies for SYMBOL."
   (unless custom-load-recursion
@@ -1816,14 +1840,22 @@ and `face'."
              ((and (boundp 'preloaded-file-list)
                    (member load preloaded-file-list)))
              ((assoc load load-history))
-             ((assoc (locate-library load) load-history))
+             ;; This was just (assoc (locate-library load) load-history)
+             ;; but has been optimized not to load locate-library
+             ;; if not necessary.
+             ((let (found (regexp (regexp-quote load)))
+                (dolist (loaded load-history)
+                  (and (string-match regexp (car loaded))
+                       (eq (locate-library load) (car loaded))
+                       (setq found t)))
+                found))
+             ;; Without this, we would load cus-edit recursively.
+             ;; We are still loading it when we call this,
+             ;; and it is not in load-history yet.
+             ((equal load "cus-edit"))
              (t
               (condition-case nil
-                  ;; Without this, we would load cus-edit recursively.
-                  ;; We are still loading it when we call this,
-                  ;; and it is not in load-history yet.
-                  (or (equal load "cus-edit")
-                      (load-library load))
+                  (load-library load)
                 (error nil))))))))
 
 (defun custom-load-widget (widget)
@@ -1934,7 +1966,7 @@ If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
                                 (background dark))
                                (:background "dim gray"))
                               (t
-                               (:italic t)))
+                               (:slant italic)))
   "Face used for comments on variables or faces"
   :version "21.1"
   :group 'custom-faces)
@@ -1944,10 +1976,10 @@ If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
   '((((class color) (background dark)) (:foreground "gray80"))
     (((class color) (background light)) (:foreground "blue4"))
     (((class grayscale) (background light))
-     (:foreground "DimGray" :bold t :italic t))
+     (:foreground "DimGray" :weight bold :slant italic))
     (((class grayscale) (background dark))
-     (:foreground "LightGray" :bold t :italic t))
-    (t (:bold t)))
+     (:foreground "LightGray" :weight bold :slant italic))
+    (t (:weight bold)))
   "Face used for variables or faces comment tags"
   :group 'custom-faces)
 
@@ -1992,23 +2024,15 @@ If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
 (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))))
+     (:foreground "light blue" :weight bold :height 1.2 :inherit variable-pitch))
     (((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)))
+     (:foreground "blue" :weight bold :height 1.2 :inherit variable-pitch))
+    (t (:weight bold)))
   "Face used for unpushable variable tags."
   :group 'custom-faces)
 
-(defface custom-variable-button-face '((t (:underline t :bold t)))
+(defface custom-variable-button-face '((t (:underline t :weight bold)))
   "Face used for pushable variable tags."
   :group 'custom-faces)
 
@@ -2370,7 +2394,7 @@ Optional EVENT is the location for the menu."
           (error "Cannot set hidden variable"))
          ((setq val (widget-apply child :validate))
           (goto-char (widget-get val :from))
-          (error "%s" (widget-get val :error)))
+          (error "Saving %s: %s" symbol (widget-get val :error)))
          ((memq form '(lisp mismatch))
           (when (equal comment "")
             (setq comment nil)
@@ -2400,7 +2424,6 @@ Optional EVENT is the location for the menu."
   "Restore the saved value for the variable being edited by WIDGET."
   (let* ((symbol (widget-value widget))
         (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)
@@ -2421,8 +2444,7 @@ Optional EVENT is the location for the menu."
 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))
-        (comment-widget (widget-get widget :comment-widget)))
+        (set (or (get symbol 'custom-set) 'set-default)))
     (if (get symbol 'standard-value)
        (funcall set symbol (eval (car (get symbol 'standard-value))))
       (error "No standard setting known for %S" symbol))
@@ -2445,6 +2467,11 @@ restoring it to the state of a variable that has never been customized."
   :tag "Attributes"
   :extra-offset 12
   :button-args '(:help-echo "Control whether this attribute has any effect.")
+  :value-to-internal 'custom-face-edit-fix-value
+  :match (lambda (widget value)
+          (widget-checklist-match widget 
+                                  (custom-face-edit-fix-value widget value)))
+  :convert-widget 'custom-face-edit-convert-widget
   :args (mapcar (lambda (att)
                  (list 'group
                        :inline t
@@ -2453,6 +2480,91 @@ restoring it to the state of a variable that has never been customized."
                        (nth 1 att)))
                custom-face-attributes))
 
+(defun custom-face-edit-fix-value (widget value)
+  "Ignoring WIDGET, convert :bold and :italic in VALUE to new form."
+  (let (result)
+    (while value
+      (let ((key (car value))
+           (val (car (cdr value))))
+       (cond ((eq key :italic)
+              (push :slant result)
+              (push (if val 'italic 'normal) result))
+             ((eq key :bold)
+              (push :weight result)
+              (push (if val 'bold 'normal) result))
+             (t 
+              (push key result)
+              (push val result))))
+      (setq value (cdr (cdr value))))
+    (setq result (nreverse result))
+    result))
+
+(defun custom-face-edit-convert-widget (widget)
+  "Convert :args as widget types in WIDGET."
+  (widget-put
+   widget
+   :args (mapcar (lambda (arg)
+                  (widget-convert arg
+                                  :deactivate 'custom-face-edit-deactivate
+                                  :activate 'custom-face-edit-activate
+                                  :delete 'custom-face-edit-delete))
+                (widget-get widget :args)))
+  widget)
+
+(defun custom-face-edit-deactivate (widget)
+  "Make face widget WIDGET inactive for user modifications."
+  (unless (widget-get widget :inactive)
+    (let ((tag (custom-face-edit-attribute-tag widget))
+         (from (copy-marker (widget-get widget :from)))
+         (to (widget-get widget :to))
+         (value (widget-value widget))
+         (inhibit-read-only t)
+         (inhibit-modification-hooks t))
+      (save-excursion
+       (goto-char from)
+       (widget-default-delete widget)
+       (insert tag ": *\n")
+       (widget-put widget :inactive
+                   (cons value (cons from (- (point) from))))))))
+
+(defun custom-face-edit-activate (widget)
+  "Make face widget WIDGET inactive for user modifications."
+  (let ((inactive (widget-get widget :inactive))
+       (inhibit-read-only t)
+       (inhibit-modification-hooks t))
+    (when (consp inactive)
+      (save-excursion
+       (goto-char (car (cdr inactive)))
+       (delete-region (point) (+ (point) (cdr (cdr inactive))))
+       (widget-put widget :inactive nil)
+       (widget-apply widget :create)
+       (widget-value-set widget (car inactive))
+       (widget-setup)))))
+
+(defun custom-face-edit-delete (widget)
+  "Remove widget from the buffer."
+  (let ((inactive (widget-get widget :inactive))
+       (inhibit-read-only t)
+       (inhibit-modification-hooks t))
+    (if (not inactive)
+       ;; Widget is alive, we don't have to do anything special
+       (widget-default-delete widget)
+      ;; WIDGET is already deleted because we did so to inactivate it;
+      ;; now just get rid of the label we put in its place.
+      (delete-region (car (cdr inactive))
+                    (+ (car (cdr inactive)) (cdr (cdr inactive))))
+      (widget-put widget :inactive nil))))
+      
+
+(defun custom-face-edit-attribute-tag (widget)
+  "Returns the first :tag property in WIDGET or one of its children."
+  (let ((tag (widget-get widget :tag)))
+    (or (and (not (equal tag "")) tag)
+       (let ((children (widget-get widget :children)))
+         (while (and (null tag) children)
+           (setq tag (custom-face-edit-attribute-tag (pop children))))
+         tag))))
+
 ;;; The `custom-display' Widget.
 
 (define-widget 'custom-display 'menu-choice
@@ -2527,11 +2639,7 @@ Match frames with dark backgrounds.")
 ;;; The `custom-face' Widget.
 
 (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)))))
+  `((t (:weight bold :height 1.2 :inherit variable-pitch)))
   "Face used for face tags."
   :group 'custom-faces)
 
@@ -2547,8 +2655,7 @@ Match frames with dark backgrounds.")
   "Customize face."
   :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
@@ -2592,6 +2699,47 @@ Match frames with dark backgrounds.")
 (defconst custom-face-selected (widget-convert 'custom-face-selected)
   "Converted version of the `custom-face-selected' widget.")
 
+(defun custom-filter-face-spec (spec filter-index &optional default-filter)
+  "Return a canonicalized version of SPEC using.
+FILTER-INDEX is the index in the entry for each attribute in
+`custom-face-attributes' at which the appropriate filter function can be
+found, and DEFAULT-FILTER is the filter to apply for attributes that
+don't specify one."
+  (mapcar (lambda (entry)
+           ;; Filter a single face-spec entry
+           (let ((tests (car entry))
+                 (unfiltered-attrs
+                  ;; Handle both old- and new-style attribute syntax
+                  (if (listp (car (cdr entry)))
+                      (car (cdr entry))
+                    (cdr entry)))
+                 (filtered-attrs nil))
+             ;; Filter each face attribute
+             (while unfiltered-attrs
+               (let* ((attr (pop unfiltered-attrs))
+                      (pre-filtered-value (pop unfiltered-attrs))
+                      (filter
+                       (or (nth filter-index (assq attr custom-face-attributes))
+                           default-filter))
+                      (filtered-value
+                       (if filter
+                           (funcall filter pre-filtered-value)
+                         pre-filtered-value)))
+                 (push filtered-value filtered-attrs)
+                 (push attr filtered-attrs)))
+             ;;
+             (list tests filtered-attrs)))
+         spec))
+
+(defun custom-pre-filter-face-spec (spec)
+  "Return SPEC changed as necessary for editing by the face customization widget.
+SPEC must be a full face spec."
+  (custom-filter-face-spec spec 2))
+
+(defun custom-post-filter-face-spec (spec)
+  "Return the customized SPEC in a form suitable for setting the face."
+  (custom-filter-face-spec spec 3))
+
 (defun custom-face-value-create (widget)
   "Create a list of the display specifications for WIDGET."
   (let ((buttons (widget-get widget :buttons))
@@ -2614,10 +2762,12 @@ Match frames with dark backgrounds.")
          (t
           ;; Create tag.
           (insert tag)
+          (widget-specify-sample widget begin (point))
           (if (eq custom-buffer-style 'face)
               (insert " ")
-            (widget-specify-sample widget begin (point))
-            (insert ": "))
+            (if (string-match "face\\'" tag)
+                (insert ":")
+              (insert " face: ")))
           ;; Sample.
           (push (widget-create-child-and-convert widget 'item
                                                  :format "(%{%t%})"
@@ -2666,7 +2816,8 @@ Match frames with dark backgrounds.")
             (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)
+                   (spec (or (get symbol 'customized-face)
+                             (get symbol 'saved-face)
                              (get symbol 'face-defface-spec)
                              ;; Attempt to construct it.
                              (list (list t (custom-face-attributes-get
@@ -2678,6 +2829,7 @@ Match frames with dark backgrounds.")
               ;; 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 spec (custom-pre-filter-face-spec spec))
               (setq edit (widget-create-child-and-convert
                           widget
                           (cond ((and (eq form 'selected)
@@ -2791,7 +2943,7 @@ 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 (custom-post-filter-face-spec (widget-value child)))
         (comment-widget (widget-get widget :comment-widget))
         (comment (widget-value comment-widget)))
     (when (equal comment "")
@@ -2799,7 +2951,11 @@ Optional EVENT is the location for the menu."
       ;; 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)
+    (if (face-spec-choose value)
+       (face-spec-set symbol value)
+      ;; face-set-spec ignores empty attribute lists, so just give it
+      ;; something harmless instead.
+      (face-spec-set symbol '((t :foreground unspecified))))
     (put symbol 'customized-face-comment comment)
     (put symbol 'face-comment comment)
     (custom-face-state-set widget)
@@ -2814,14 +2970,18 @@ Optional EVENT is the location for the menu."
   "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 (custom-post-filter-face-spec (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)
+    (if (face-spec-choose value)
+       (face-spec-set symbol value)
+      ;; face-set-spec ignores empty attribute lists, so just give it
+      ;; something harmless instead.
+      (face-spec-set symbol '((t :foreground unspecified))))
     (put symbol 'saved-face value)
     (put symbol 'customized-face nil)
     (put symbol 'face-comment comment)
@@ -2881,7 +3041,7 @@ restoring it to the state of a face that has never been customized."
   :convert-widget 'widget-value-convert-widget
   :button-prefix 'widget-push-button-prefix
   :button-suffix 'widget-push-button-suffix
-  :format "%t: %[select face%] %v"
+  :format "%{%t%}: %[select face%] %v"
   :tag "Face"
   :value 'default
   :value-create 'widget-face-value-create
@@ -2889,7 +3049,7 @@ restoring it to the state of a face that has never been customized."
   :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."
@@ -2987,39 +3147,22 @@ and so forth.  The remaining group tags are shown with
 (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))
+     (:foreground "pink" :weight bold :height 1.2 :inherit variable-pitch))
     (((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)))
+     (:foreground "red" :weight bold :height 1.2 :inherit variable-pitch))
+    (t (:weight bold)))
   "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))))
+     (:foreground "light blue" :weight bold :height 1.2))
     (((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)))
+     (:foreground "blue" :weight bold :height 1.2))
+    (t (:weight bold)))
   "Face used for low level group tags."
   :group 'custom-faces)
 
@@ -3274,7 +3417,7 @@ Creating group members... %2d%%"
                                          (widget-insert "\n"))))
                                    members)))
             (message "Creating group magic...")
-            (mapcar 'custom-magic-reset children)
+            (mapc 'custom-magic-reset children)
             (message "Creating group state...")
             (widget-put widget :children children)
             (custom-group-state-update widget)
@@ -3327,42 +3470,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)
@@ -3403,9 +3546,19 @@ to the new custom file.  This will preserve your existing customizations."
   "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"))))
+           (let ((user-init-file user-init-file)
+                 (default-init-file
+                   (if (eq system-type 'ms-dos) "~/_emacs" "~/.emacs")))
+             (when (null user-init-file)
+               (if (or (file-exists-p default-init-file)
+                       (and (eq system-type 'windows-nt)
+                            (file-exists-p "~/_emacs")))
+                   ;; Started with -q, i.e. the file containing
+                   ;; Custom settings hasn't been read.  Saving
+                   ;; settings there would overwrite other settings.
+                   (error "Saving settings from \"emacs -q\" would overwrite existing customizations"))
+               (setq user-init-file default-init-file))
+             user-init-file))))
 
 (defun custom-save-delete (symbol)
   "Visit `custom-file' and delete all calls to SYMBOL from it.
@@ -3434,7 +3587,17 @@ or (if there were none) at the end of the buffer."
              (setq first (point)))))))
     (if first
        (goto-char first)
-      (goto-char (point-max)))))
+      ;; Move in front of local variables, otherwise long Custom
+      ;; entries would make them ineffective.
+      (let ((pos (point-max))
+           (case-fold-search t))
+       (save-excursion
+         (goto-char (point-max))
+         (search-backward "\n\^L" (max (- (point-max) 3000) (point-min))
+                          'move)
+         (when (search-forward "Local Variables:" nil t)
+           (setq pos (line-beginning-position))))
+       (goto-char pos)))))
 
 (defun custom-save-variables ()
   "Save all customized variables in `custom-file'."
@@ -3453,7 +3616,7 @@ or (if there were none) at the end of the buffer."
        (princ "\n"))
       (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")
+  ;; Your init file should contain only one such instance.\n")
       (mapcar
        (lambda (symbol)
         (let ((value (get symbol 'saved-value))
@@ -3517,7 +3680,7 @@ or (if there were none) at the end of the buffer."
        (princ "\n"))
       (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")
+  ;; Your init file should contain only one such instance.\n")
       (mapcar
        (lambda (symbol)
         (let ((value (get symbol 'saved-face))
@@ -3587,7 +3750,62 @@ or (if there were none) at the end of the buffer."
     (save-excursion
       (let ((default-major-mode nil))
        (set-buffer (find-file-noselect (custom-file))))
-      (save-buffer))))
+      (let ((file-precious-flag t))
+       (save-buffer)))))
+
+;;;###autoload
+(defun customize-mark-to-save (symbol)
+  "Mark SYMBOL for later saving.
+
+If the default value of SYMBOL is different from the standard value, 
+set the `saved-value' property to a list whose car evaluates to the
+default value. Otherwise, set it til nil.
+
+To actually save the value, call `custom-save-all'.
+
+Return non-nil iff the `saved-value' property actually changed."
+  (let* ((get (or (get symbol 'custom-get) 'default-value))
+        (value (funcall get symbol))
+        (saved (get symbol 'saved-value))
+        (standard (get symbol 'standard-value))
+        (comment (get symbol 'customized-variable-comment)))
+    ;; Save default value iff different from standard value.
+    (if (or (null standard)
+           (not (equal value (condition-case nil
+                                 (eval (car standard))
+                               (error nil)))))
+       (put symbol 'saved-value (list (custom-quote value)))
+      (put symbol 'saved-value nil))
+    ;; Clear customized information (set, but not saved).
+    (put symbol 'customized-value nil)
+    ;; Save any comment that might have been set.
+    (when comment
+      (put symbol 'saved-variable-comment comment))
+    (not (equal saved (get symbol 'saved-value)))))
+
+;;;###autoload
+(defun customize-mark-as-set (symbol)
+  "Mark current value of SYMBOL as being set from customize.
+
+If the default value of SYMBOL is different from the saved value if any, 
+or else if it is different from the standard value, set the
+`customized-value' property to a list whose car evaluates to the 
+default value. Otherwise, set it til nil.
+
+Return non-nil iff the `customized-value' property actually changed."
+  (let* ((get (or (get symbol 'custom-get) 'default-value))
+        (value (funcall get symbol))
+        (customized (get symbol 'customized-value))
+        (old (or (get symbol 'saved-value) (get symbol 'standard-value))))
+    ;; Mark default value as set iff different from old value.
+    (if (or (null old)
+           (not (equal value (condition-case nil 
+                                 (eval (car old))
+                               (error nil)))))
+       (put symbol 'customized-value (list (custom-quote value)))
+      (put symbol 'customized-value nil))
+    ;; Changed?
+    (not (equal customized (get symbol 'customized-value)))))
 
 ;;; The Customize Menu.
 
@@ -3623,20 +3841,11 @@ or (if there were none) at the end of the buffer."
                                   ':style 'toggle
                                   ':selected symbol)))
 
-;; Fixme: sort out use of :filter in Emacs 21.
-(if nil ; (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)
@@ -3673,14 +3882,9 @@ Otherwise the menu will be named `Customize'.
 The format is suitable for use with `easy-menu-define'."
   (unless name
     (setq name "Customize"))
-  ;; Fixme: sort out use of :filter in Emacs 21.
-  (if nil ;(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)
+             (custom-menu-create ',symbol))))
 
 ;;; The Custom Mode.
 
@@ -3688,6 +3892,8 @@ The format is suitable for use with `easy-menu-define'."
   "Keymap for `custom-mode'.")
 
 (unless custom-mode-map
+  ;; This keymap should be dense, but a dense keymap would prevent inheriting
+  ;; "\r" bindings from the parent map.
   (setq custom-mode-map (make-sparse-keymap))
   (set-keymap-parent custom-mode-map widget-keymap)
   (suppress-keymap custom-mode-map)
@@ -3782,10 +3988,11 @@ 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) ""))
-  (make-local-hook 'widget-edit-functions)
   (add-hook 'widget-edit-functions 'custom-state-buffer-message nil t)
   (run-hooks 'custom-mode-hook))
 
+(put 'custom-mode 'mode-class 'special)
+
 (add-to-list
  'debug-ignored-errors
  "^No user options have changed defaults in recent Emacs versions$")