*** empty log message ***
[gnus] / lisp / custom.el
index 9c0bfd0..32db9c7 100644 (file)
@@ -1,12 +1,30 @@
 ;;; custom.el --- User friendly customization support.
-;; Copyright (C) 1995 Free Software Foundation, Inc.
-;;
+
+;; Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
 ;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
 ;; Keywords: help
 ;; Version: 0.5
 
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
 ;;; Commentary:
-;;
+
 ;; WARNING: This package is still under construction and not all of
 ;; the features below are implemented.
 ;;
@@ -15,7 +33,7 @@
 ;; editing a text file in some arcane syntax is user hostile in the
 ;; extreme, and to most users emacs lisp definitely count as arcane.
 ;;
-;; The intension is that authors of emacs lisp packages declare the
+;; The intent is that authors of emacs lisp packages declare the
 ;; variables intended for user customization with `custom-declare'.
 ;; Custom can then automatically generate a customization buffer with
 ;; `custom-buffer-create' where the user can edit the package
@@ -32,7 +50,7 @@
 ;;; Todo:  
 ;;
 ;; - Toggle documentation in three states `none', `one-line', `full'.
-;; - Function to generate a XEmacs menu from a CUSTOM.
+;; - Function to generate an XEmacs menu from a CUSTOM.
 ;; - Write TeXinfo documentation.
 ;; - Make it possible to hide sections by clicking at the level.
 ;; - Declare AUC TeX variables.
 ;; - XEmacs port.
 ;; - Allow `URL', `info', and internal hypertext buttons.
 ;; - Support meta-variables and goal directed customization.
+;; - Make it easy to declare custom types independently.
+;; - Make it possible to declare default value and type for a single
+;;   variable, storing the data in a symbol property.
+;; - Syntactic sugar for CUSTOM declarations.
+;; - Use W3 for variable documentation.
 
 ;;; Code:
 
+(eval-when-compile
+  (require 'cl))
+
 ;;; Compatibility:
 
-(or (fboundp 'buffer-substring-no-properties)
-    ;; Introduced in Emacs 19.29.
-    (defun buffer-substring-no-properties (beg end)
-      "Return the text from BEG to END, without text properties, as a string."
-      (let ((string (buffer-substring beg end)))
-       (set-text-properties 0 (length string) nil string)
-       string)))
-
-(or (fboundp 'add-to-list)
-    ;; Introduced in Emacs 19.29.
-    (defun add-to-list (list-var element)
-      "Add to the value of LIST-VAR the element ELEMENT if it isn't there yet.
-If you want to use `add-to-list' on a variable that is not defined
-until a certain package is loaded, you should put the call to `add-to-list'
-into a hook function that will be run only after loading the package.
-`eval-after-load' provides one way to do this.  In some cases
-other hooks, such as major mode hooks, can do the job."
-      (or (member element (symbol-value list-var))
-         (set list-var (cons element (symbol-value list-var))))))
-
-(or (fboundp 'plist-get)
-    ;; Introduced in Emacs 19.29.
-    (defun plist-get (plist prop)
-      "Extract a value from a property list.
-PLIST is a property list, which is a list of the form
-\(PROP1 VALUE1 PROP2 VALUE2...).  This function returns the value
-corresponding to the given PROP, or nil if PROP is not
-one of the properties on the list."
-      (let (result)
-       (while plist
-         (if (eq (car plist) prop)
-             (setq result (car (cdr plist))
-                   plist nil)
-           (set plist (cdr (cdr plist)))))
-       result)))
-
-(or (fboundp 'plist-put)
-    ;; Introduced in Emacs 19.29.
-    (defun plist-put (plist prop val)    
-      "Change value in PLIST of PROP to VAL.
-PLIST is a property list, which is a list of the form
-\(PROP1 VALUE1 PROP2 VALUE2 ...).  PROP is a symbol and VAL is any object.
-If PROP is already a property on the list, its value is set to VAL,
-otherwise the new PROP VAL pair is added.  The new plist is returned;
-use `(setq x (plist-put x prop val))' to be sure to use the new value.
-The PLIST is modified by side effects."
-      (while plist
-       (cond ((eq (car plist) prop)
-              (setcar (cdr plist) val)
-              (setq plist nil))
-             ((null (cdr (cdr plist)))
-              (setcdr (cdr plist) (list prop val))
-              (setq plist nil))
-             (t
-              (setq plist (cdr (cdr plist))))))))
-
-(or (fboundp 'match-string)
-    ;; Introduced in Emacs 19.29.
-    (defun match-string (num &optional string)
-  "Return string of text matched by last search.
-NUM specifies which parenthesized expression in the last regexp.
- Value is nil if NUMth pair didn't match, or there were less than NUM pairs.
-Zero means the entire text matched by the whole regexp or whole string.
-STRING should be given if the last search was by `string-match' on STRING."
-  (if (match-beginning num)
-      (if string
-         (substring string (match-beginning num) (match-end num))
-       (buffer-substring (match-beginning num) (match-end num))))))
-
-(or (fboundp 'facep)
-    ;; Introduced in Emacs 19.29.
-    (defun facep (x)
-      "Return t if X is a face name or an internal face vector."
-      (and (or (and (fboundp 'internal-facep) (internal-facep x))
-              (and 
-               (symbolp x) 
-               (assq x (and (boundp 'global-face-data) global-face-data))))
-          t)))
-
-(if (facep 'underline)
+(defun custom-xmas-add-text-properties (start end props &optional object)
+  (add-text-properties start end props object)
+  (put-text-property start end 'start-open t object)
+  (put-text-property start end 'end-open t object))
+
+(defun custom-xmas-put-text-property (start end prop value &optional object)
+  (put-text-property start end prop value object)
+  (put-text-property start end 'start-open t object)
+  (put-text-property start end 'end-open t object))
+
+(defun custom-xmas-extent-start-open ()
+  (map-extents (lambda (extent arg)
+                (set-extent-property extent 'start-open t))
+              nil (point) (min (1+ (point)) (point-max))))
+                 
+(if (string-match "XEmacs\\|Lucid" emacs-version)
+    (progn
+      (fset 'custom-add-text-properties 'custom-xmas-add-text-properties)
+      (fset 'custom-put-text-property 'custom-xmas-put-text-property)
+      (fset 'custom-extent-start-open 'custom-xmas-extent-start-open)
+      (fset 'custom-set-text-properties
+           (if (fboundp 'set-text-properties)
+               'set-text-properties))
+      (fset 'custom-buffer-substring-no-properties
+           (if (fboundp 'buffer-substring-no-properties)
+               'buffer-substring-no-properties
+             'custom-xmas-buffer-substring-no-properties)))
+  (fset 'custom-add-text-properties 'add-text-properties)
+  (fset 'custom-put-text-property 'put-text-property)
+  (fset 'custom-extent-start-open 'ignore)
+  (fset 'custom-set-text-properties 'set-text-properties)
+  (fset 'custom-buffer-substring-no-properties 
+       'buffer-substring-no-properties))
+
+(defun custom-xmas-buffer-substring-no-properties (beg end)
+  "Return the text from BEG to END, without text properties, as a string."
+  (let ((string (buffer-substring beg end)))
+    (custom-set-text-properties 0 (length string) nil string)
+    string))
+
+;; XEmacs and Emacs 19.29 facep does different things.
+(if (fboundp 'find-face)
+    (fset 'custom-facep 'find-face)
+  (fset 'custom-facep 'facep))
+
+(if (custom-facep 'underline)
     ()
   ;; No underline face in XEmacs 19.12.
   (and (fboundp 'make-face)
@@ -139,18 +131,16 @@ STRING should be given if the last search was by `string-match' on STRING."
       (and (fboundp 'set-face-underline-p)
           (funcall 'set-face-underline-p 'underline t))))
 
-(or (fboundp 'set-text-properties)
-    ;; Missing in XEmacs 19.12.
-    (defun set-text-properties (start end props &optional buffer)
-      (if (or (null buffer) (bufferp buffer))
-         (if props
-             (while props
-               (put-text-property 
-                start end (car props) (nth 1 props) buffer)
-               (setq props (nthcdr 2 props)))
-           (remove-text-properties start end ())))))
-
-(or (fboundp 'event-closest-point)
+(defun custom-xmas-set-text-properties (start end props &optional buffer)
+  (if (null buffer)
+      (if props
+         (while props
+           (custom-put-text-property 
+            start end (car props) (nth 1 props) buffer)
+           (setq props (nthcdr 2 props)))
+       (remove-text-properties start end ()))))
+
+(or (fboundp 'event-point)
     ;; Missing in Emacs 19.29.
     (defun event-point (event)
       "Return the character position of the given mouse-motion, button-press,
@@ -169,60 +159,6 @@ into the buffer visible in the event's window."
   (defvar custom-mouse-face nil)
   (defvar custom-field-active-face nil))
 
-(or (and (fboundp 'modify-face) (not (featurep 'face-lock)))
-    ;; Introduced in Emacs 19.29.  Incompatible definition also introduced
-    ;; by face-lock.el version 3.00 and above for Emacs 19.28 and below.
-    ;; face-lock does not call modify-face, so we can safely redefine it.
-    (defun modify-face (face foreground background stipple
-                            bold-p italic-p underline-p)
-  "Change the display attributes for face FACE.
-FOREGROUND and BACKGROUND should be color strings or nil.
-STIPPLE should be a stipple pattern name or nil.
-BOLD-P, ITALIC-P, and UNDERLINE-P specify whether the face should be set bold,
-in italic, and underlined, respectively.  (Yes if non-nil.)
-If called interactively, prompts for a face and face attributes."
-  (interactive
-   (let* ((completion-ignore-case t)
-         (face        (symbol-name (read-face-name "Modify face: ")))
-         (colors      (mapcar 'list x-colors))
-         (stipples    (mapcar 'list
-                              (apply 'nconc
-                                     (mapcar 'directory-files
-                                             x-bitmap-file-path))))
-         (foreground  (modify-face-read-string
-                       face (face-foreground (intern face))
-                       "foreground" colors))
-         (background  (modify-face-read-string
-                       face (face-background (intern face))
-                       "background" colors))
-         (stipple     (modify-face-read-string
-                       face (face-stipple (intern face))
-                       "stipple" stipples))
-         (bold-p      (y-or-n-p (concat "Set face " face " bold ")))
-         (italic-p    (y-or-n-p (concat "Set face " face " italic ")))
-         (underline-p (y-or-n-p (concat "Set face " face " underline "))))
-     (message "Face %s: %s" face
-      (mapconcat 'identity
-       (delq nil
-       (list (and foreground (concat (downcase foreground) " foreground"))
-             (and background (concat (downcase background) " background"))
-             (and stipple (concat (downcase stipple) " stipple"))
-             (and bold-p "bold") (and italic-p "italic")
-             (and underline-p "underline"))) ", "))
-     (list (intern face) foreground background stipple
-          bold-p italic-p underline-p)))
-  (condition-case nil (set-face-foreground face foreground) (error nil))
-  (condition-case nil (set-face-background face background) (error nil))
-  (condition-case nil (set-face-stipple face stipple) (error nil))
-  (if (string-match "XEmacs" emacs-version)
-      (progn
-       (funcall (if bold-p 'make-face-bold 'make-face-unbold) face)
-       (funcall (if italic-p 'make-face-italic 'make-face-unitalic) face))
-    (funcall (if bold-p 'make-face-bold 'make-face-unbold) face nil t)
-    (funcall (if italic-p 'make-face-italic 'make-face-unitalic) face nil t))
-  (set-face-underline-p face underline-p)
-  (and (interactive-p) (redraw-display))))
-
 ;; We can't easily check for a working intangible.
 (defconst intangible (if (and (boundp 'emacs-minor-version)
                              (or (> emacs-major-version 19)
@@ -230,12 +166,12 @@ If called interactively, prompts for a face and face attributes."
                                       (> emacs-minor-version 28))))
                         (setq intangible 'intangible)
                       (setq intangible 'intangible-if-it-had-been-working))
-  "The symbol making text intangible")
+  "The symbol making text intangible.")
 
 (defconst rear-nonsticky (if (string-match "XEmacs" emacs-version)
                             'end-open
                           'rear-nonsticky)
-  "The symbol making text proeprties non-sticky in the rear end.")
+  "The symbol making text properties non-sticky in the rear end.")
 
 (defconst front-sticky (if (string-match "XEmacs" emacs-version)
                           'front-closed
@@ -248,11 +184,45 @@ If called interactively, prompts for a face and face attributes."
   "Symbol used for highlighting text under mouse.")
 
 ;; Put it in the Help menu, if possible.
-(condition-case nil
-    ;; This will not work under XEmacs.
-    (global-set-key [ menu-bar help-menu customize ]
-                   '("Customize..." . customize))
-  (error nil))
+(if (string-match "XEmacs" emacs-version)
+    (if (featurep 'menubar)
+       ;; XEmacs (disabled because it doesn't work)
+       (and current-menubar
+            (add-menu-item '("Help") "Customize..." 'customize t)))
+  ;; Emacs 19.28 and earlier
+  (global-set-key [ menu-bar help customize ]
+                 '("Customize..." . customize))
+  ;; Emacs 19.29 and later
+  (global-set-key [ menu-bar help-menu customize ] 
+                 '("Customize..." . customize)))
+
+;; XEmacs popup-menu stolen from w3.el.
+(defun custom-x-really-popup-menu (pos title menudesc)
+  "My hacked up function to do a blocking popup menu..."
+  (let ((echo-keystrokes 0)
+       event menu)
+    (while menudesc
+      (setq menu (cons (vector (car (car menudesc))
+                              (list (car (car menudesc))) t) menu)
+           menudesc (cdr menudesc)))
+    (setq menu (cons title menu))
+    (popup-menu menu)
+    (catch 'popup-done
+      (while t
+       (setq event (next-command-event event))
+       (cond ((and (misc-user-event-p event) (stringp (car-safe                                                   (event-object event))))
+              (throw 'popup-done (event-object event)))
+             ((and (misc-user-event-p event)
+                   (or (eq (event-object event) 'abort)
+                       (eq (event-object event) 'menu-no-selection-hook)))
+              nil)
+             ((not (popup-menu-up-p))
+              (throw 'popup-done nil))
+             ((button-release-event-p event);; don't beep twice
+              nil)
+             (t
+              (beep)
+              (message "please make a choice from the menu.")))))))
 
 ;;; Categories:
 ;;
@@ -294,7 +264,7 @@ If called interactively, prompts for a face and face attributes."
 
   (defun custom-category-set (from to category)
     "Make text between FROM and TWO have category CATEGORY."
-    (put-text-property from to 'category category)))
+    (custom-put-text-property from to 'category category)))
 
 ;;; External Data:
 ;; 
@@ -347,10 +317,10 @@ If called interactively, prompts for a face and face attributes."
 ;; The following functions are part of the public interface to the
 ;; CUSTOM datastructure.  Each CUSTOM describes a group of variables,
 ;; a single variable, or a component of a structured variable.  The
-;; CUSTOM instances are part of two hiearachies, the first is the
+;; CUSTOM instances are part of two hierarchies, the first is the
 ;; `part-of' hierarchy in which each CUSTOM is a component of another
 ;; CUSTOM, except for the top level CUSTOM which is contained in
-;; `custom-data'.  The second hiearachy is a `is-a' type hierarchy
+;; `custom-data'.  The second hierarchy is a `is-a' type hierarchy
 ;; where each CUSTOM is a leaf in the hierarchy defined by the `type'
 ;; property and `custom-type-properties'.
 
@@ -399,6 +369,7 @@ hierarchy the new entry should be added.  CUSTOM is the entry to add."
 
 (defconst custom-type-properties
   '((repeat (type . default)
+           ;; See `custom-match'.
            (import . custom-repeat-import)
            (eval . custom-repeat-eval)
            (quote . custom-repeat-quote)
@@ -412,6 +383,7 @@ hierarchy the new entry should be added.  CUSTOM is the entry to add."
            (del-tag . "[DEL]")
            (add-tag . "[INS]"))
     (pair (type . group)
+         ;; A cons-cell.
          (accept . custom-pair-accept)
          (eval . custom-pair-eval)
          (import . custom-pair-import)
@@ -419,10 +391,13 @@ hierarchy the new entry should be added.  CUSTOM is the entry to add."
          (valid . (lambda (c d) (consp d)))
          (extract . custom-pair-extract))
     (list (type . group)
+         ;; A lisp list.
          (quote . custom-list-quote)
-         (valid . (lambda (c d) (listp d)))
+         (valid . (lambda (c d)
+                    (listp d)))
          (extract . custom-list-extract))
     (group (type . default)
+          ;; See `custom-match'.
           (face-tag . nil)
           (eval . custom-group-eval)
           (import . custom-group-import)
@@ -437,13 +412,26 @@ hierarchy the new entry should be added.  CUSTOM is the entry to add."
           (insert . custom-group-insert)
           (find . custom-group-find))
     (toggle (type . choice)
+           ;; Booleans.
            (data ((type . const)
                   (tag . "On ")
                   (default . t))
                  ((type . const)
                   (tag . "Off")
                   (default . nil))))
+    (triggle (type . choice)
+            ;; On/Off/Default.
+            (data ((type . const)
+                   (tag . "On ")
+                   (default . t))
+                  ((type . const)
+                   (tag . "Off")
+                   (default . nil))
+                  ((type . const)
+                   (tag . "Def")
+                   (default . custom:asis))))
     (choice (type . default)
+           ;; See `custom-match'.
            (query . custom-choice-query)
            (accept . custom-choice-accept)
            (extract . custom-choice-extract)
@@ -453,12 +441,14 @@ hierarchy the new entry should be added.  CUSTOM is the entry to add."
                  (default . __uninitialized__)
                  (type . const)))
     (const (type . default)
+          ;; A `const' only matches a single lisp value.
           (extract . (lambda (c f) (list (custom-default c))))
           (validate . (lambda (c f) nil))
           (valid . custom-const-valid)
           (update . custom-const-update)
           (insert . custom-const-insert))
     (face-doc (type . doc)
+             ;; A variable containing a face.
              (doc . "\
 You can customize the look of Emacs by deciding which faces should be
 used when.  If you push one of the face buttons below, you will be
@@ -547,49 +537,57 @@ Select the properties you want this face to have.")
                        (type . string))
                       "\n"
                       ((tag . "Bold")
-                       (default . nil)
-                       (type . toggle))
+                       (default . custom:asis)
+                       (type . triggle))
                       "              "
                       ((tag . "Italic")
-                       (default . nil)
-                       (type . toggle))
+                       (default . custom:asis)
+                       (type . triggle))
                       "             "
                       ((tag . "Underline")
                        (hidden . t)
-                       (default . nil)
-                       (type . toggle)))
+                       (default . custom:asis)
+                       (type . triggle)))
                 (default . (custom-face-lookup "default" "default" "default"
                                                nil nil nil))
                 (type . list))
                ((prompt . "Other")
                 (face . custom-field-value)
+                (default . __uninitialized__)
                 (type . symbol))))
     (file (type . string)
+         ;; A string containing a file or directory name.
          (directory . nil)
          (default-file . nil)
          (query . custom-file-query))
     (sexp (type . default)
+         ;; Any lisp expression.
          (width . 40)
          (default . (__uninitialized__ . "Uninitialized"))
          (read . custom-sexp-read)
          (write . custom-sexp-write))
     (symbol (type . sexp)
+           ;; A lisp symbol.
            (width . 40)
            (valid . (lambda (c d) (symbolp d))))
     (integer (type . sexp)
+            ;; A lisp integer.
             (width . 10)
             (valid . (lambda (c d) (integerp d))))
     (string (type . default)
+           ;; A lisp string.
            (width . 40) 
            (valid . (lambda (c d) (stringp d)))
            (read . custom-string-read)
            (write . custom-string-write))
     (button (type . default)
+           ;; Push me.
            (accept . ignore)
            (extract . nil)
            (validate . ignore)
            (insert . custom-button-insert))
     (doc (type . default)
+        ;; A documentation only entry with no value.
         (header . nil)
         (reset . ignore)
         (extract . nil)
@@ -629,7 +627,9 @@ Select the properties you want this face to have.")
 The format is `((SYMBOL (PROPERTY . VALUE)... )... )'.")
 
 (defconst custom-local-type-properties nil
-  "Local type properties.")
+  "Local type properties.
+Entries in this list take precedence over `custom-type-properties'.")
+
 (make-variable-buffer-local 'custom-local-type-properties)
 
 (defconst custom-nil '__uninitialized__
@@ -638,6 +638,9 @@ The format is `((SYMBOL (PROPERTY . VALUE)... )... )'.")
 (defconst custom-invalid '__invalid__
   "Special value representing an invalid field.")
 
+(defconst custom:asis 'custom:asis)
+;; Bad, ugly, and horrible kludge.
+
 (defun custom-property (custom property)
   "Extract from CUSTOM property PROPERTY."
   (let ((entry (assq property custom)))
@@ -663,7 +666,7 @@ The format is `((SYMBOL (PROPERTY . VALUE)... )... )'.")
     (cdr entry)))
 
 (defun custom-property-set (custom property value)
-  "Set CUSTOM PROPERY to VALUE by side effect.
+  "Set CUSTOM PROPERTY to VALUE by side effect.
 CUSTOM must have at least one property already."
   (let ((entry (assq property custom)))
     (if entry
@@ -682,6 +685,10 @@ CUSTOM must have at least one property already."
   "Extract `tag' from CUSTOM."
   (custom-property custom 'tag))
 
+(defun custom-face-tag (custom)
+  "Extract `face-tag' from CUSTOM."
+  (custom-property custom 'face-tag))
+
 (defun custom-prompt (custom)
   "Extract `prompt' from CUSTOM.  
 If none exist, default to `tag' or, failing that, `type'."
@@ -717,12 +724,16 @@ If none exist, default to `tag' or, failing that, `type'."
   (custom-property custom 'padding))
 
 (defun custom-valid (custom value)
-  "Non-nil if CUSTOM may legally be set to VALUE."
+  "Non-nil if CUSTOM may validly be set to VALUE."
   (and (not (and (listp value) (eq custom-invalid (car value))))
        (funcall (custom-property custom 'valid) custom value)))
 
 (defun custom-import (custom value)
-  "Import CUSTOM VALUE from external variable."
+  "Import CUSTOM VALUE from external variable.
+
+This function change VALUE into a form that makes it easier to edit 
+internally.  What the internal form is exactly depends on CUSTOM.  
+The internal form is returned."
   (if (eq custom-nil value)
       (list custom-nil)
     (funcall (custom-property custom 'import) custom value)))
@@ -745,16 +756,27 @@ If none exist, default to `tag' or, failing that, `type'."
         (funcall (custom-property custom 'write) custom value))))
 
 (defun custom-read (custom string)
-  "Convert CUSTOM field content STRING into external form."
+  "Convert CUSTOM field content STRING into lisp."
   (condition-case nil
       (funcall (custom-property custom 'read) custom string)
     (error (cons custom-invalid string))))
 
 (defun custom-match (custom values)
   "Match CUSTOM with a list of VALUES.
+
 Return a cons-cell where the car is the sublist of VALUES matching CUSTOM,
-and the cdr is the remaining VALUES."
+and the cdr is the remaining VALUES.
+
+A CUSTOM is actually a regular expression over the alphabet of lisp
+types.  Most CUSTOM types are just doing a literal match, e.g. the
+`symbol' type matches any lisp symbol.  The exceptions are:
+
+group:    which corresponds to a `(' and `)' group in a regular expression.
+choice:   which corresponds to a group of `|' in a regular expression.
+repeat:   which corresponds to a `*' in a regular expression.
+optional: which corresponds to a `?', and isn't implemented yet."
   (if (memq values (list custom-nil nil))
+      ;; Nothing matches the uninitialized or empty list.
       (cons custom-nil nil)
     (funcall (custom-property custom 'match) custom values)))
 
@@ -793,7 +815,7 @@ position of the error, and the cdr is a text describing the error."
 ;; FIELD datatype.  The FIELD instance hold information about a
 ;; specific editing field in the customization buffer.
 ;;
-;; Each FIELD can be seen as an instanciation of a CUSTOM.
+;; Each FIELD can be seen as an instantiation of a CUSTOM.
 
 (defvar custom-field-last nil)
 ;; Last field containing point.
@@ -851,8 +873,8 @@ START and END are markers to the start and end of the field."
   (funcall (custom-property (custom-field-custom field) 'query) field))
 
 (defun custom-field-accept (field value &optional original)
-  "Accept FIELD VALUE.  
-If optional ORIGINAL is non-nil, concider VALUE for the original value."
+  "Store a new value into field FIELD, taking it from VALUE.
+If optional ORIGINAL is non-nil, consider VALUE for the original value."
   (let ((inhibit-point-motion-hooks t))
     (funcall (custom-property (custom-field-custom field) 'accept) 
             field value original)))
@@ -862,10 +884,11 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
   (let ((custom (custom-field-custom field)))
     (if (stringp custom)
        nil
-      (funcall (custom-property custom 'face) field))))
+      (let ((face (funcall (custom-property custom 'face) field)))
+       (if (custom-facep face) face nil)))))
 
 (defun custom-field-update (field)
-  "Update content of FIELD."
+  "Update the screen appearance of FIELD to correspond with the field's value."
   (let ((custom (custom-field-custom field)))
     (if (stringp custom)
        nil
@@ -898,7 +921,7 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
                           value))))
 
 (defun custom-repeat-accept (field value &optional original)
-  "Enter content of editing FIELD."
+  "Store a new value into field FIELD, taking it from VALUE."
   (let ((values (copy-sequence (custom-field-value field)))
        (all (custom-field-value field))
        (start (custom-field-start field))
@@ -962,6 +985,7 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
         (end (make-marker))
         (data (vector repeat nil start end))
         field)
+    (custom-extent-start-open)
     (insert-before-markers "\n")
     (backward-char 1)
     (set-marker start (point))
@@ -1014,7 +1038,7 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
     (cons (nreverse matches) values)))
 
 (defun custom-repeat-extract (custom field)
-  "Extract list of childrens values."
+  "Extract list of children's values."
   (let ((values (custom-field-value field))
        (data (custom-data custom))
        result)
@@ -1038,7 +1062,7 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
     result))
 
 (defun custom-pair-accept (field value &optional original)
-  "Enter content of editing FIELD with VALUE."
+  "Store a new value into field FIELD, taking it from VALUE."
   (custom-group-accept field (list (car value) (cdr value)) original))
 
 (defun custom-pair-eval (custom value)
@@ -1061,7 +1085,7 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
     (custom-default-quote custom value)))
 
 (defun custom-pair-extract (custom field)
-  "Extract cons of childrens values."
+  "Extract cons of children's values."
   (let ((values (custom-field-value field))
        (data (custom-data custom))
        result)
@@ -1082,7 +1106,7 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
     (custom-default-quote custom value)))
 
 (defun custom-list-extract (custom field)
-  "Extract list of childrens values."
+  "Extract list of children's values."
   (let ((values (custom-field-value field))
        (data (custom-data custom))
        result)
@@ -1184,14 +1208,14 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
        (setq data (cdr data))))))
 
 (defun custom-group-accept (field value &optional original)
-  "Enter content of editing FIELD with VALUE."
+  "Store a new value into field FIELD, taking it from VALUE."
   (let* ((values (custom-field-value field))
         (custom (custom-field-custom field))
         (from (custom-field-start field))
-        (face-tag (custom-property custom 'face-tag))
+        (face-tag (custom-face-tag custom))
         current)
     (if face-tag 
-       (put-text-property from (+ from (length (custom-tag custom)))
+       (custom-put-text-property from (+ from (length (custom-tag custom)))
                           'face (funcall face-tag field value)))
     (if original 
        (custom-field-original-set field value))
@@ -1211,7 +1235,7 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
         (from (point))
         (compact (custom-compact custom))
         (tag (custom-tag custom))
-        (face-tag (custom-property custom 'face-tag)))
+        (face-tag (custom-face-tag custom)))
     (cond (face-tag (custom-text-insert tag))
          (tag (custom-tag-insert tag field)))
     (or compact (custom-documentation-insert custom))
@@ -1242,7 +1266,7 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
     field))
 
 (defun custom-choice-accept (field value &optional original)
-  "Reset content of editing FIELD."
+  "Store a new value into field FIELD, taking it from VALUE."
   (let ((custom (custom-field-custom field))
        (start (custom-field-start field))
        (end (custom-field-end field))
@@ -1277,9 +1301,10 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
          ()
        (setq begin (point)
              found (custom-insert (custom-property custom 'none) nil))
-       (add-text-properties begin (point)
-                            (list rear-nonsticky t
-                                  'face custom-field-uninitialized-face)))
+       (custom-add-text-properties 
+        begin (point)
+        (list rear-nonsticky t
+              'face custom-field-uninitialized-face)))
       (or original
          (custom-field-original-set found (custom-field-original field)))
       (custom-field-accept found value original)
@@ -1287,12 +1312,12 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
       (custom-field-move field from end))))
 
 (defun custom-choice-extract (custom field)
-  "Extract childs value."
+  "Extract child's value."
   (let ((value (custom-field-value field)))
     (custom-field-extract (custom-field-custom value) value)))
 
 (defun custom-choice-validate (custom field)
-  "Validate childs value."
+  "Validate child's value."
   (let ((value (custom-field-value field))
        (custom (custom-field-custom field)))
     (if (or (eq value custom-nil)
@@ -1316,15 +1341,25 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
        (setq current (car data)
              data (cdr data))
        (setq alist (cons (cons (custom-prompt current) current) alist)))
-      (let ((answer (if (listp last-input-event)
-                       (x-popup-menu last-input-event
-                                     (list tag (cons "" (reverse alist))))
-                     (let ((choice (completing-read (concat tag " (default "
-                                                            default "): ") 
-                                                    alist nil t)))
-                       (if (or (null choice) (string-equal choice ""))
-                           (setq choice default))
-                       (cdr (assoc choice alist))))))
+      (let ((answer (cond ((and (fboundp 'button-press-event-p)
+                               (fboundp 'popup-menu)
+                               (button-press-event-p last-input-event))
+                          (cdr (assoc (car (custom-x-really-popup-menu 
+                                            last-input-event tag 
+                                            (reverse alist)))
+                                      alist)))
+                         ((listp last-input-event)
+                          (x-popup-menu last-input-event
+                                        (list tag (cons "" (reverse alist)))))
+                         (t 
+                          (let ((choice (completing-read (concat tag
+                                                                 " (default "
+                                                                 default 
+                                                                 "): ") 
+                                                         alist nil t)))
+                            (if (or (null choice) (string-equal choice ""))
+                                (setq choice default))
+                            (cdr (assoc choice alist)))))))
        (if answer
            (custom-field-accept field (custom-default answer)))))))
 
@@ -1355,7 +1390,8 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
 
 (defun custom-face-import (custom value)
   "Modify CUSTOM's VALUE to match internal expectations."
-  (let ((name (symbol-name value)))
+  (let ((name (or (and (facep value) (symbol-name (face-name value)))
+                 (symbol-name value))))
     (list (if (string-match "\
 custom-face-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)"
                            name)