*** empty log message ***
[gnus] / lisp / custom.el
index 08d912c..d05c9b6 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
 ;; - 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 documenation.
+;; - 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)))
+(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))
 
 (or (fboundp 'add-to-list)
     ;; Introduced in Emacs 19.29.
@@ -153,18 +206,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 (or (null buffer) (bufferp 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,
@@ -183,60 +234,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)
@@ -244,12 +241,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
@@ -264,7 +261,8 @@ If called interactively, prompts for a face and face attributes."
 ;; Put it in the Help menu, if possible.
 (if (string-match "XEmacs" emacs-version)
     ;; XEmacs (disabled because it doesn't work)
-    (add-menu-item '("Help") "Customize..." 'customize nil)
+    (and current-menubar
+        (add-menu-item '("Help") "Customize..." 'customize t))
   ;; Emacs 19.28 and earlier
   (global-set-key [ menu-bar help customize ]
                  '("Customize..." . customize))
@@ -340,7 +338,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:
 ;; 
@@ -393,10 +391,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'.
 
@@ -495,6 +493,17 @@ hierarchy the new entry should be added.  CUSTOM is the entry to add."
                  ((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)
@@ -602,17 +611,17 @@ 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))
@@ -703,6 +712,9 @@ Entries in this list take precedence over `custom-type-properties'.")
 (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)))
@@ -728,7 +740,7 @@ Entries in this list take precedence over `custom-type-properties'.")
     (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
@@ -877,7 +889,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.
@@ -936,7 +948,7 @@ START and END are markers to the start and end of the field."
 
 (defun custom-field-accept (field value &optional original)
   "Store a new value into field FIELD, taking it from VALUE.
-If optional ORIGINAL is non-nil, concider VALUE for the original 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)))
@@ -1047,6 +1059,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))
@@ -1099,7 +1112,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)
@@ -1146,7 +1159,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)
@@ -1167,7 +1180,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)
@@ -1276,7 +1289,7 @@ If optional ORIGINAL is non-nil, concider VALUE for the original value."
         (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))
@@ -1362,9 +1375,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)
@@ -1372,12 +1386,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)
@@ -1463,9 +1477,8 @@ custom-face-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)"
                    (intern (match-string 6 name)))
            value))))
 
-(defun custom-face-lookup (fg bg stipple bold italic underline)
-  "Lookup or create a face with specified attributes.
-FG BG STIPPLE BOLD ITALIC UNDERLINE"
+(defun custom-face-lookup (&optional fg bg stipple bold italic underline)
+  "Lookup or create a face with specified attributes."
   (let ((name (intern (format "custom-face-%s-%s-%s-%S-%S-%S"
                              (or fg "default")
                              (or bg "default")
@@ -1474,19 +1487,44 @@ FG BG STIPPLE BOLD ITALIC UNDERLINE"
     (if (and (custom-facep name)
             (fboundp 'make-face))
        ()
-      (make-face name)
-      (modify-face name
-                  (if (string-equal fg "default") nil fg)
-                  (if (string-equal bg "default") nil bg)
-                  (if (string-equal stipple "default") nil stipple)
-                  bold italic underline))
+      (copy-face 'default name)
+      (when (and fg
+                (not (string-equal fg "default")))
+       (condition-case ()
+           (set-face-foreground name fg)
+         (error nil)))
+      (when (and bg
+                (not (string-equal bg "default")))
+       (condition-case ()
+           (set-face-background name bg)
+         (error nil)))
+      (when (and stipple
+                (not (string-equal stipple "default"))
+                (not (eq stipple 'custom:asis))
+                (fboundp 'set-face-stipple))
+       (set-face-stipple name stipple))
+      (when (and bold
+                (not (eq bold 'custom:asis)))
+       (condition-case ()
+           (make-face-bold name)
+         (error nil)))
+      (when (and italic
+                (not (eq italic 'custom:asis)))
+       (condition-case ()
+           (make-face-italic name)
+         (error nil)))
+      (when (and underline
+                (not (eq underline 'custom:asis)))
+       (condition-case ()
+           (set-face-underline-p name t)
+         (error nil))))
     name))
 
 (defun custom-face-hack (field value)
   "Face that should be used for highlighting FIELD containing VALUE."
   (let* ((custom (custom-field-custom field))
-        (face (eval (funcall (custom-property custom 'export) 
-                             custom value))))
+        (form (funcall (custom-property custom 'export) custom value))
+        (face (apply (car form) (cdr form))))
     (if (custom-facep face) face nil)))
 
 (defun custom-const-insert (custom level)
@@ -1495,7 +1533,7 @@ FG BG STIPPLE BOLD ITALIC UNDERLINE"
         (face (custom-field-face field))
         (from (point)))
     (custom-text-insert (custom-tag custom))
-    (add-text-properties from (point) 
+    (custom-add-text-properties from (point) 
                         (list 'face face
                               rear-nonsticky t))
     (custom-documentation-insert custom)
@@ -1506,7 +1544,7 @@ FG BG STIPPLE BOLD ITALIC UNDERLINE"
   "Update face of FIELD."
   (let ((from (custom-field-start field))
        (custom (custom-field-custom field)))
-    (put-text-property from (+ from (length (custom-tag custom)))
+    (custom-put-text-property from (+ from (length (custom-tag custom)))
                       'face (custom-field-face field))))
 
 (defun custom-const-valid (custom value)
@@ -1665,7 +1703,7 @@ FG BG STIPPLE BOLD ITALIC UNDERLINE"
     (cond ((eq value custom-nil)
           (cons start "Uninitialized field"))
          ((and (consp value) (eq (car value) custom-invalid))
-          (cons start "Unparseable field content"))
+          (cons start "Unparsable field content"))
          ((custom-valid custom value)
           nil)
          (t
@@ -1795,9 +1833,9 @@ If the optional argument SAVE is non-nil, use that for saving changes."
   (let ((from (point)))
     (insert tag)
     (custom-category-set from (point) 'custom-button-properties)
-    (put-text-property from (point) 'custom-tag field)
+    (custom-put-text-property from (point) 'custom-tag field)
     (if data
-       (add-text-properties from (point) (list 'custom-data data)))))
+       (custom-add-text-properties from (point) (list 'custom-data data)))))
 
 (defun custom-documentation-insert (custom &rest ignore)
   "Insert documentation from CUSTOM in current buffer."
@@ -1816,11 +1854,13 @@ If the optional argument SAVE is non-nil, use that for saving changes."
   "Describe how to execute COMMAND."
   (let ((from (point)))
     (insert "`" (key-description (where-is-internal command nil t)) "'")
-    (set-text-properties from (point)
-                        (list 'face custom-button-face
-                              mouse-face custom-mouse-face
-                              'custom-jump t ;Make TAB jump over it.
-                              'custom-tag command))
+    (custom-set-text-properties from (point)
+                               (list 'face custom-button-face
+                                     mouse-face custom-mouse-face
+                                     'custom-jump t ;Make TAB jump over it.
+                                     'custom-tag command
+                                     'start-open t
+                                     'end-open t))
     (custom-category-set from (point) 'custom-documentation-properties))
   (custom-help-insert ": " (custom-first-line (documentation command)) "\n"))
 
@@ -1829,7 +1869,7 @@ If the optional argument SAVE is non-nil, use that for saving changes."
 ;; The Customization major mode and interactive commands. 
 
 (defvar custom-mode-map nil
-  "Keymap for Custum Mode.")
+  "Keymap for Custom Mode.")
 (if custom-mode-map
     nil
   (setq custom-mode-map (make-sparse-keymap))
@@ -2142,17 +2182,18 @@ If the optional argument is non-nil, show text iff the argument is positive."
     (insert-char (custom-padding custom)
                 (- (custom-width custom) (- (point) from)))
     (custom-field-move field from (point))
-    (set-text-properties 
+    (custom-set-text-properties 
      from (point)
      (list 'custom-field field
           'custom-tag field
           'face (custom-field-face field)
-          front-sticky t))))
+          'start-open t
+          'end-open t))))
 
 (defun custom-field-read (field)
   ;; Read the screen content of FIELD.
   (custom-read (custom-field-custom field)
-              (buffer-substring-no-properties (custom-field-start field)
+              (custom-buffer-substring-no-properties (custom-field-start field)
                                               (custom-field-end field))))
 
 ;; Fields are shown in a special `active' face when point is inside
@@ -2163,7 +2204,7 @@ If the optional argument is non-nil, show text iff the argument is positive."
   ;; Deactivate FIELD.
   (let ((before-change-functions nil)
        (after-change-functions nil))
-    (put-text-property (custom-field-start field) (custom-field-end field)
+    (custom-put-text-property (custom-field-start field) (custom-field-end field)
                       'face (custom-field-face field))))
 
 (defun custom-field-enter (field)
@@ -2181,7 +2222,7 @@ If the optional argument is non-nil, show text iff the argument is positive."
            (setq pos (1- pos)))
          (if (< pos (point))
              (goto-char pos))))
-    (put-text-property start end 'face custom-field-active-face)))
+    (custom-put-text-property start end 'face custom-field-active-face)))
 
 (defun custom-field-resize (field)
   ;; Resize FIELD after change.
@@ -2263,7 +2304,7 @@ If the optional argument is non-nil, show text iff the argument is positive."
     (let ((field custom-field-was))
       (custom-assert '(prog1 field (setq custom-field-was nil)))
       ;; Prevent mixing fields properties.
-      (put-text-property begin end 'custom-field field)
+      (custom-put-text-property begin end 'custom-field field)
       ;; Update the field after modification.
       (if (eq (custom-field-property begin) field)
          (let ((field-end (custom-field-end field)))