*** empty log message ***
authorLars Magne Ingebrigtsen <larsi@gnus.org>
Tue, 4 Mar 1997 23:08:41 +0000 (23:08 +0000)
committerLars Magne Ingebrigtsen <larsi@gnus.org>
Tue, 4 Mar 1997 23:08:41 +0000 (23:08 +0000)
13 files changed:
lisp/ChangeLog
lisp/article.el
lisp/custom-edit.el
lisp/custom.el
lisp/gnus-art.el
lisp/gnus-load.el
lisp/gnus.el
lisp/nnweb.el
lisp/widget-edit.el
lisp/widget.el
texi/ChangeLog
texi/Makefile
texi/gnus.texi

index 1790bd4..50dd30a 100644 (file)
@@ -1,11 +1,29 @@
-Thu Sep 19 03:05:01 1996  Lars Magne Ingebrigtsen  <larsi@ylfing.ifi.uio.no>
+Sun Sep 22 11:48:02 1996  Lars Magne Ingebrigtsen  <larsi@hler.ifi.uio.no>
 
-       * gnus.el: Red Gnus v0.37 is released.
+       * custom.el (defcustom): Eval and compile.
+       * widget.el (define-widget-keywords): Ditto.
+
+Sat Sep 21 09:29:54 1996  Lars Magne Ingebrigtsen  <lars@eyesore.no>
+
+       * article.el (article-strip-multiple-blank-lines): Would strip all
+       blank lines.
+
+Fri Sep 20 06:52:07 1996  Lars Magne Ingebrigtsen  <larsi@ylfing.ifi.uio.no>
+
+       * gnus.el: Red Gnus v0.39 is released.
 
 Thu Sep 19 18:57:59 1996  Lars Magne Ingebrigtsen  <lars@eyesore.no>
 
        * message.el (message-ignored-cited-headers): Doc fix.
 
+Thu Sep 19 03:05:01 1996  Lars Magne Ingebrigtsen  <larsi@ylfing.ifi.uio.no>
+
+       * gnus.el: Red Gnus v0.38 is released.
+
+Thu Sep 19 03:05:01 1996  Lars Magne Ingebrigtsen  <larsi@ylfing.ifi.uio.no>
+
+       * gnus.el: Red Gnus v0.37 is released.
+
 Wed Sep 18 10:36:08 1996  Lars Magne Ingebrigtsen  <lars@eyesore.no>
 
        * gnus-async.el (gnus-async-prefetch-article-p): New variable.
index 215eb44..fe48539 100644 (file)
 (require 'nnheader)
 (require 'gnus-util)
 (require 'message)
-
-(defvar gnus-ignored-headers
-   "^Path:\\|^Posting-Version:\\|^Article-I.D.:\\|^Expires:\\|^Date-Received:\\|^References:\\|^Control:\\|^Xref:\\|^Lines:\\|^Posted:\\|^Relay-Version:\\|^Message-ID:\\|^Nf-ID:\\|^Nf-From:\\|^Approved:\\|^Sender:\\|^Received:\\|^Mail-from:"
-  "*All headers that match this regexp will be hidden.
+(require 'custom)
+
+(defgroup article nil
+  "Article display."
+  :group 'gnus)
+
+(defcustom gnus-ignored-headers
+  '("^Path:" "^Posting-Version:" "^Article-I.D.:" "^Expires:"
+    "^Date-Received:" "^References:" "^Control:" "^Xref:" "^Lines:"
+    "^Posted:" "^Relay-Version:" "^Message-ID:" "^Nf-ID:" "^Nf-From:"
+    "^Approved:" "^Sender:" "^Received:" "^Mail-from:") 
+  "All headers that match this regexp will be hidden.
 This variable can also be a list of regexps of headers to be ignored.
-If `article-visible-headers' is non-nil, this variable will be ignored.")
-
-(defvar gnus-visible-headers "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-"
-  "*All headers that do not match this regexp will be hidden.
+If `article-visible-headers' is non-nil, this variable will be ignored."
+  :type '(repeat string)               ;Leave monster regexp to lisp.
+  :group 'article)
+
+(defcustom gnus-visible-headers 
+  '("^From:" "^Newsgroups:" "^Subject:" "^Date:" "^Followup-To:"
+    "^Reply-To:" "^Organization:" "^Summary:" "^Keywords:" "^To:"
+    "^Cc:" "^Posted-To:" "^Mail-Copies-To:" "^Apparently-To:"
+    "^Gnus-Warning:" "^Resent-")
+  "All headers that do not match this regexp will be hidden.
 This variable can also be a list of regexp of headers to remain visible.
-If this variable is non-nil, `article-ignored-headers' will be ignored.")
+If this variable is non-nil, `article-ignored-headers' will be ignored."
+  :type '(repeat string)               ;Leave monster regexp to lisp.
+  :group 'article)
 
-(defvar gnus-sorted-header-list
+(defcustom gnus-sorted-header-list
   '("^From:" "^Subject:" "^Summary:" "^Keywords:" "^Newsgroups:" "^To:"
     "^Cc:" "^Date:" "^Organization:")
-  "*This variable is a list of regular expressions.
+  "This variable is a list of regular expressions.
 If it is non-nil, headers that match the regular expressions will
 be placed first in the article buffer in the sequence specified by
-this list.")
+this list."
+  :type '(repeat string)
+  :group 'article)
 
-(defvar gnus-boring-article-headers
-  '(empty followup-to reply-to)
-  "*Headers that are only to be displayed if they have interesting data.
+(defcustom gnus-boring-article-headers '(empty followup-to reply-to)
+  "Headers that are only to be displayed if they have interesting data.
 Possible values in this list are `empty', `newsgroups', `followup-to',
-`reply-to', and `date'.")
-
-(defvar gnus-signature-separator '("^-- $" "^-- *$")
+`reply-to', and `date'."
+  :type '(set (item :tag "Headers with no content." empty)
+             (item :tag "Newsgroups with only one group." newsgroups)
+             (item :tag "Followup-to identical to newsgroups." followup-to)
+             (item :tag "Reply-to identical to from." reply-to)
+             (item :tag "Date less than four days old." date))
+  :group 'article)
+
+(defcustom gnus-signature-separator '("^-- $" "^-- *$")
   "Regexp matching signature separator.
 This can also be a list of regexps.  In that case, it will be checked
 from head to tail looking for a separator.  Searches will be done from
-the end of the buffer.")
+the end of the buffer."
+  :type '(repeat string)
+  :group 'article)
 
-(defvar gnus-signature-limit nil
-  "Provide a limit to what is considered a signature.
+(defcustom gnus-signature-limit nil
+   "Provide a limit to what is considered a signature.
 If it is a number, no signature may not be longer (in characters) than
 that number.  If it is a floating point number, no signature may be
 longer (in lines) than that number.  If it is a function, the function
 will be called without any parameters, and if it returns nil, there is
 no signature in the buffer.  If it is a string, it will be used as a
-regexp.  If it matches, the text in question is not a signature.")
+regexp.  If it matches, the text in question is not a signature."
+  :type '(choice integer number function string)
+  :group 'article)
 
-(defvar gnus-hidden-properties '(invisible t intangible t)
-  "Property list to use for hiding text.")
+(defcustom gnus-hidden-properties '(invisible t intangible t)
+  "Property list to use for hiding text."
+  :type 'sexp 
+  :group 'article)
 
-(defvar gnus-article-x-face-command
+(defcustom gnus-article-x-face-command
   "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | xv -quit -"
   "String or function to be executed to display an X-Face header.
 If it is a string, the command will be executed in a sub-shell
-asynchronously.         The compressed face will be piped to this command.")
-
-(defvar gnus-article-x-face-too-ugly nil
-  "Regexp matching posters whose face shouldn't be shown automatically.")
-
-(defvar gnus-emphasis-alist
-  '(("_\\(\\w+\\)_" 0 1 'underline)
-    ("\\W\\(/\\(\\w+\\)/\\)\\W" 1 2 'italic)
-    ("\\(_\\*\\|\\*_\\)\\(\\w+\\)\\(_\\*\\|\\*_\\)" 0 2 'bold-underline)
-    ("\\*\\(\\w+\\)\\*" 0 1 'bold))
+asynchronously.         The compressed face will be piped to this command."
+  :type 'string                                ;Leave function case to Lisp.
+  :group 'article)
+
+(defcustom gnus-article-x-face-too-ugly nil
+  "Regexp matching posters whose face shouldn't be shown automatically."
+  :type 'regexp
+  :group 'article)
+
+(defcustom gnus-emphasis-alist
+  '(("_\\(\\w+\\)_" 0 1 underline)
+    ("\\W\\(/\\(\\w+\\)/\\)\\W" 1 2 italic)
+    ("\\(_\\*\\|\\*_\\)\\(\\w+\\)\\(_\\*\\|\\*_\\)" 0 2 bold-underline)
+    ("\\*\\(\\w+\\)\\*" 0 1 bold))
   "Alist that says how to fontify certain phrases.
 Each item looks like this:
 
@@ -95,7 +128,13 @@ The first element is a regular expression to be matched.  The second
 is a number that says what regular expression grouping used to find
 the entire emphasized word.  The third is a number that says what
 regexp grouping should be displayed and highlighted.  The fourth
-is the face used for highlighting.")
+is the face used for highlighting."
+  :type '(repeat (list :value ("" 0 0 default)
+                      regexp
+                      (integer :tag "Match group")
+                      (integer :tag "Emphasize group")
+                      face))
+  :group 'article)
 
 (eval-and-compile
   (autoload 'hexl-hex-string-to-integer "hexl")
@@ -576,8 +615,8 @@ always hide."
        (replace-match "" nil t))
       ;; Then replace multiple empty lines with a single empty line.
       (goto-char (point-min))
-      (while (re-search-forward "\n\n+" nil t)
-       (replace-match "\n" nil t)))))
+      (while (re-search-forward "\n\n\n+" nil t)
+       (replace-match "\n\n" t t)))))
 
 (defun article-strip-blank-lines ()
   "Strip leading, trailing and multiple blank lines."
@@ -688,7 +727,7 @@ If HIDE, hide the text instead."
        (setq beg (point)))
       t)))
 
-(defvar article-time-units
+(defconst article-time-units
   `((year . ,(* 365.25 24 60 60))
     (week . ,(* 7 24 60 60))
     (day . ,(* 24 60 60))
index 97236e5..3fa5b03 100644 (file)
@@ -4,7 +4,7 @@
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Keywords: help, faces
-;; Version: 0.9
+;; Version: 0.94
 ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
 
 ;;; Commentary:
 (require 'custom)
 (require 'widget-edit)
 
-(let ((keywords '(:custom-show 
-                 :custom-documentation-show 
-                 :custom-documentation-property 
-                 :custom-level
-                 :custom-status)))
-  (while keywords
-    (or (boundp (car keywords))
-       (set (car keywords) (car keywords)))
-    (setq keywords (cdr keywords))))
+(define-widget-keywords :custom-show :custom-doc :custom-magic
+  :custom-state :custom-documentation-property :custom-level :custom-form
+  :custom-apply :custom-set-default :custom-reset)
 
 ;;; Utilities.
 
       sexp
     (list 'quote sexp)))
 
+(defun custom-unimplemented (&rest ignore)
+  "Apologize for my laziness."
+  (error "Sorry, not implemented"))
+
+;;; Modification of Basic Widgets.
+;;
+;; We add extra properties to the basic widgets needed here.  This is
+;; fine, as long as we are careful to stay within out own namespace.
+;;
+;; We want simple widgets to be displayed by default, but complex
+;; widgets to be hidden.
+
+(widget-put (get 'item 'widget-type) :custom-show t)
+(widget-put (get 'editable-field 'widget-type) :custom-show t)
+(widget-put (get 'menu-choice 'widget-type) :custom-show t)
+
+;;; The `custom-magic' Widget
+
+(define-widget 'custom-magic 'item
+  "Status feedback for customization option."
+  :format "%[%v%]"
+  :action 'widget-choice-item-action
+  :value-create 'custom-magic-value-create)
+
+(defface custom-invalid-face '((((class color))
+                               (:foreground "yellow" :background "red"))
+                              (t
+                               (:bold t :italic t :underline t)))
+  "Face used when the customize item is invalid."
+  :group 'customize)
+
+(defface custom-rogue-face '((((class color))
+                             (:foreground "pink" :background "black"))
+                            (t
+                             (:underline t)))
+  "Face used when the customize item is not defined for customization."
+  :group 'customize)
+
+(defface custom-modified-face '((((class color)) 
+                                (:foreground "white" :background "blue"))
+                               (t
+                                (:italic t :bold)))
+  "Face used when the customize item has been modified."
+  :group 'customize)
+
+(defface custom-applied-face '((((class color)) 
+                               (:foreground "blue" :background "white"))
+                              (t
+                               (:italic t)))
+  "Face used when the customize item has been applied."
+  :group 'customize)
+
+(defface custom-saved-face '((t (:underline t)))
+  "Face used when the customize item has been saved."
+  :group 'customize)
+
+(defcustom custom-magic-alist '((nil "#" underline)
+                               (unknown "?" italic)
+                               (hidden "-" default)
+                               (invalid "x" custom-invalid-face)
+                               (modified "*" custom-modified-face)
+                               (applied "+" custom-applied-face)
+                               (saved "!" custom-saved-face)
+                               (rogue "@" custom-rogue-face)
+                               (factory " " nil))
+  "Alist of magic representing a customize items status.
+Each entry is of the form (STATE MAGIC FACE), where 
+
+STATE is one of the following symbols:
+
+`nil'
+   For internal use, should never occur.
+`unknown'
+   For internal use, should never occur.
+`hidden'
+   This item is not being displayed. 
+`invalid'
+   This item is modified, but has an invalid form.
+`modified'
+   This item is modified, and has a valid form.
+`applied'
+   This items current value has been changed temporarily.
+`saved'
+   This item is marked for saving.
+`rogue'
+   This item has no customization information.
+`factory'
+   This item is unchanged from the factory default.
+
+MAGIC is a string used to present that state.
+
+FACE is a face used to present the state.
+
+The list should be sorted most significant first."
+  :type '(repeat (list (choice (item nil)
+                              (item unknown)
+                              (item hidden)
+                              (item invalid)
+                              (item modified)
+                              (item applied)
+                              (item saved)
+                              (item rogue)
+                              (item factory))
+                      string face))
+  :group 'customize)
+
+(defun custom-magic-value-create (widget)
+  ;; Create compact status report for WIDGET.
+  (let* ((parent (widget-get widget :parent))
+        (state (widget-get parent :custom-state))
+        (entry (assq state custom-magic-alist))
+        (magic (nth 1 entry))
+        (face (nth 2 entry)))
+    (if (eq (widget-get parent :custom-form) 'lisp)
+       (widget-insert "(" magic ")")
+      (widget-insert "[" magic "]"))
+    (widget-put widget :button-face face)))
+
+(defun custom-magic-reset (widget)
+  "Redraw the :custom-magic property of WIDGET."
+  (let ((magic (widget-get widget :custom-magic)))
+    (widget-value-set magic (widget-value magic))))
+
 ;;; The `custom-level' Widget.
 
 (define-widget 'custom-level 'item
 (defun custom-level-action (widget &optional event)
   "Toggle visibility for parent to WIDGET."
   (let* ((parent (widget-get widget :parent))
-        (show (widget-get parent :custom-show)))
-    (widget-apply parent :validate)
-    (widget-put parent :custom-show (not show))
-    (custom-reset parent)))
+        (state (widget-get parent :custom-state)))
+    (cond ((memq state '(invalid modified))
+          (error "There are unapplied changes"))
+         ((eq state 'hidden)
+          (widget-put parent :custom-state 'unknown))
+         (t
+          (widget-put parent :custom-state 'hidden)))
+    (custom-redraw parent)))
 
 ;;; The `custom-help' Widget.
 
-(define-widget 'custom-help 'push
+(define-widget 'custom-help 'push-button
   "The custom documentation button."
+  :format "%[[%t]%] %d"
   :help-echo "Push me to toggle the documentation."
   :action 'custom-help-action)
 
-(defun custom-help-action (widget  &optional event)
-  "Toggle documentation visibility for parent to WIDGET."
-  (let* ((parent (widget-get widget :parent))
-        (symbol (widget-get parent :value))
-        (property (widget-get parent :custom-documentation-property))
-        (text (or (widget-get parent :doc)
-                  (documentation-property symbol property)))
-        (newline (string-match "\n." text))
-        (old (widget-get parent :custom-documentation-show))
-        (new (cond ((eq old t)
-                    nil)
-                   ((null old)
-                    (if newline 
-                        'first-line
-                      t))
-                   (t 
-                    (if newline
-                        t
-                      nil)))))
-    (widget-apply parent :validate)
-    (widget-put parent :custom-documentation-show new)
-    (custom-reset parent)))
+(defun custom-help-action (widget &optional event)
+  "Toggle documentation for WIDGET."
+  (let ((old (widget-get widget :doc))
+       (new (widget-get widget :custom-doc)))
+    (widget-put widget :doc new)
+    (widget-put widget :custom-doc old))
+  (widget-value-set widget (widget-value widget)))
 
 ;;; The `custom' Widget.
 
 (define-widget 'custom 'default
   "Customize a user option."
   :convert-widget 'widget-item-convert-widget
-  :format "%l%h%[%t%]: %v%x"
+  :format "%l%[%t%]: %v%m %h"
   :format-handler 'custom-format-handler
+  :notify 'custom-notify
   :custom-level 1
-  :custom-show nil
-  :custom-documentation-show 'first-line
+  :custom-state 'hidden
   :custom-documentation-property 'widget-subclass-responsibility
   :value-create 'widget-subclass-responsibility
   :value-delete 'widget-radio-value-delete
   :value-get 'widget-item-value-get
-  :validate 'widget-repeat-validate
+  :validate 'widget-editable-list-validate
   :match (lambda (widget value) (symbolp value)))
 
 (defun custom-format-handler (widget escape)
   ;; We recognize extra escape sequences.
-  (let* (child 
-        (symbol (widget-get widget :value))
+  (let* ((symbol (widget-get widget :value))
+        (buttons (widget-get widget :buttons))
         (level (widget-get widget :custom-level))
         (doc-property (widget-get widget :custom-documentation-property))
         (doc-try (or (widget-get widget :doc)
                      (documentation-property symbol doc-property)))
         (doc-text (and (stringp doc-try)
                        (> (length doc-try) 1)
-                       doc-try))
-        (doc-show (widget-get widget :custom-documentation-show)))
+                       doc-try)))
     (cond ((eq escape ?l)
           (when level 
-            (setq child
-                  (widget-create 'custom-level
-                                 :parent widget
-                                 (make-string level ?*)))
+            (push (widget-create-child-and-convert
+                   widget 'custom-level (make-string level ?*))
+                  buttons)
             (widget-insert " ")))
+         ((eq escape ?m)
+          (and (eq (preceding-char) ?\n)
+               (widget-get widget :indent)
+               (insert-char ?  (widget-get widget :indent)))
+          (let ((magic (widget-create-child-and-convert
+                        widget 'custom-magic nil)))
+            (widget-put widget :custom-magic magic)
+            (push magic buttons)))
          ((eq escape ?h)
           (when doc-text
-            (setq child (widget-create 'custom-help 
-                                       :parent widget
-                                       "?"))
-            (widget-insert " ")))
-         ((eq escape ?x)
-          (and doc-text doc-show
-               (let ((start (point)))
-                 ;; The first "*" in a doc string means interactively
-                 ;; user editable.  Since we are providing a facility
-                 ;; for the user to interactively edit the variable,
-                 ;; that information is redundant.  Remove it.
-                 (cond ((eq doc-show t)
-                        (if (eq (aref doc-text  0) ?*)
-                            (widget-insert (substring doc-text 1))
-                          (widget-insert doc-text)))
-                       ((eq (aref doc-text 0) ?*)
-                        (string-match "\\`.\\(.*\\)" doc-text)
-                        (widget-insert (match-string 1 doc-text)))
-                       (t
-                        (string-match "\\`.*" doc-text)
-                        (widget-insert (match-string 0 doc-text))))
-                 (unless (eq (preceding-char) ?\n)
-                   (widget-insert "\n"))
-                 (widget-specify-doc widget start (point)))))
+            (and (eq (preceding-char) ?\n)
+                 (widget-get widget :indent)
+                 (insert-char ?  (widget-get widget :indent)))
+            ;; The `*' in the beginning is redundant.
+            (when (eq (aref doc-text  0) ?*)
+              (setq doc-text (substring doc-text 1)))
+            ;; Get rid of trailing newlines.
+            (when (string-match "\n+\\'" doc-text)
+              (setq doc-text (substring doc-text 0 (match-beginning 0))))
+            (push (if (string-match "\n." doc-text)
+                      ;; Allow multiline doc to be hiden.
+                      (widget-create-child-and-convert
+                       widget 'custom-help 
+                       :doc (progn
+                              (string-match "\\`.*" doc-text)
+                              (match-string 0 doc-text))
+                       :custom-doc doc-text
+                       "?")
+                    ;; A single line is just inserted.
+                    (widget-create-child-and-convert
+                     widget 'item :format "%d" :doc doc-text nil))
+                  buttons)))
          (t 
           (widget-default-format-handler widget escape)))
-    (when child
-      (widget-put widget
-                 :buttons (cons child (widget-get widget :buttons))))))
+    (widget-put widget :buttons buttons)))
 
-(defun custom-unimplemented (&rest ignore)
-  "Apologize for my laziness."
-  (error "Sorry, not implemented"))
+(defun custom-notify (widget &rest args)
+  "Keep track of changes."
+  (widget-put widget :custom-state 'modified)
+  (let ((buffer-undo-list t))
+    (custom-magic-reset widget))
+  (apply 'widget-default-notify widget args))
 
-(defun custom-reset (widget)
+(defun custom-redraw (widget)
   "Redraw WIDGET with current settings."
   (widget-value-set widget (widget-value widget))
+  (custom-redraw-magic widget))
+
+(defun custom-redraw-magic (widget)
+  "Redraw WIDGET state with current settings."
+  (while widget 
+    (let ((magic (widget-get widget :custom-magic)))
+      (unless magic 
+       (debug))
+      (widget-value-set magic (widget-value magic))
+      (when (setq widget (widget-get widget :group))
+       (custom-group-state-update widget))))
   (widget-setup))
 
 ;;; The `custom-variable' Widget.
 
 (define-widget 'custom-variable 'custom
   "Customize variable."
+  :format "%l%v%m %h"
   :help-echo "Push me to set or reset this variable."
   :custom-documentation-property 'variable-documentation
-  :custom-show 'child
-  :custom-status 'edit
+  :custom-state nil
+  :custom-form 'edit
   :value-create 'custom-variable-value-create
-  :action 'custom-variable-action)
-
-(widget-put (get 'default 'widget-type) :custom-show t)
+  :action 'custom-variable-action
+  :custom-apply 'custom-variable-apply
+  :custom-set-default 'custom-variable-set-default
+  :custom-reset 'custom-redraw)
 
 (defun custom-variable-value-create (widget)
   "Here is where you edit the variables value."
-  (let* ((status (widget-get widget :custom-status))
-        (child-show (widget-get widget :custom-show))
+  (let* ((buttons (widget-get widget :buttons))
+        (children (widget-get widget :children))
+        (form (widget-get widget :custom-form))
+        (state (widget-get widget :custom-state))
         (symbol (widget-get widget :value))
         (child-type (or (get symbol 'custom-type) 'sexp))
-        (type (if (listp child-type) child-type (list child-type)))
-        (show (if (eq child-show 'child)
-                  (widget-get type :custom-show)
-                child-show))
-        (dummy (widget-put widget :custom-show show))
-        (child (cond ((not show)
-                      (widget-create 'custom-level
-                                     "[show]"))
-                     ((eq status 'lisp)
-                      (let ((value (cond ((get symbol 'saved-value)
-                                          (car (get symbol 'saved-value)))
-                                         ((get symbol 'factory-value)
-                                          (car (get symbol 'factory-value)))
-                                         ((boundp symbol)
-                                          (custom-quote
-                                           (symbol-value symbol)))
-                                         (t
-                                          (custom-quote
-                                           (widget-get type :value))))))
-                        (widget-create 'sexp :value value)))
-                     (t
-                      (let ((value (if (boundp symbol)
-                                       (symbol-value symbol)
-                                     (widget-get type :value))))
-                        (widget-create type :value value))))))
+        (type (if (listp child-type)
+                  child-type
+                (list child-type)))
+        conv value)
+    ;; If the widget is new, the child determine whether it is hidden.
+    (cond (state)
+         ((widget-get type :custom-show)
+          (setq state 'unknown))
+         (t
+          (setq state 'hidden)))
+    ;; If the widget is not hidden, we will need its value.
+    (unless (eq state 'hidden)
+      (setq conv (widget-convert type)
+           value  (if (boundp symbol)
+                      (symbol-value symbol)
+                    (widget-get conv :value))))
+    ;; If we don't know the state, see if we need to edit it in lisp form.
+    (when (eq state 'unknown)
+      (unless (widget-apply (widget-convert type) :match value)
+       (setq form 'lisp)))
+    ;; Now we can create the child widget.
+    (cond ((eq state 'hidden)
+          ;; Make hidden value easy to show.
+          (push (widget-create-child-and-convert
+                 widget 'custom-level
+                 :tag (symbol-name symbol)
+                 :format "%t: %[show%]")
+                buttons))
+         ((eq form 'lisp)
+          ;; In lisp mode edit the saved value when possible.
+          (let* ((value (cond ((get symbol 'saved-value)
+                               (car (get symbol 'saved-value)))
+                              ((get symbol 'factory-value)
+                               (car (get symbol 'factory-value)))
+                              ((boundp symbol)
+                               (custom-quote (symbol-value symbol)))
+                              (t
+                               (custom-quote (widget-get conv :value))))))
+            (push (widget-create-child-and-convert widget 'sexp 
+                                                   :tag (symbol-name symbol)
+                                                   :parent widget
+                                                   :value value)
+                  children)))
+         (t
+          ;; Edit mode.
+          (push (widget-create-child-and-convert widget type 
+                                                 :tag (symbol-name symbol)
+                                                 :value value)
+                children)))
+    ;; Now update the state.
     (unless (eq (preceding-char) ?\n)
       (widget-insert "\n"))
-    (widget-put child :parent widget)
-    (widget-put widget :children (list child))))
+    (if (eq state 'hidden)
+       (widget-put widget :custom-state state)
+      (custom-variable-state-set widget))
+    (widget-put widget :custom-form form)           
+    (widget-put widget :buttons buttons)
+    (widget-put widget :children children)))
+
+(defun custom-variable-state-set (widget)
+  "Set the state of WIDGET."
+  (let* ((symbol (widget-value widget))
+        (value (symbol-value symbol)))
+    (widget-put widget
+               :custom-state (if (get symbol 'saved-value)
+                                 (if (equal (custom-quote value)
+                                            (car (get symbol 'saved-value)))
+                                     'saved
+                                   'applied)
+                               (if (get symbol 'factory-value)
+                                   (if (equal (custom-quote value)
+                                              (car (get symbol
+                                                        'factory-value)))
+                                       'factory
+                                     'applied)
+                                 'rogue)))))
 
 (defvar custom-variable-menu 
   '(("Edit" . custom-variable-edit)
-    ("Edit Lisp" . custom-variable-edit-lisp)
+    ("Edit Default" . custom-variable-edit-lisp)
     ("Apply" . custom-variable-apply)
     ("Set Default" . custom-variable-set-default)
-    ("Reset" . custom-reset)
+    ("Reset" . custom-redraw)
     ("Reset to Default" . custom-variable-default)
     ("Reset to Factory Settings" . custom-variable-factory))
   "Alist of actions for the `custom-variable' widget.
@@ -237,62 +411,73 @@ Optional EVENT is the location for the menu."
 
 (defun custom-variable-edit (widget)
   "Edit value of WIDGET."
-  (custom-variable-apply widget)
-  (widget-put widget :custom-show t)
-  (widget-put widget :custom-status 'edit)
-  (custom-reset widget))
+  (widget-put widget :custom-state 'unknown)
+  (widget-put widget :custom-form 'edit)
+  (custom-redraw widget))
 
 (defun custom-variable-edit-lisp (widget)
   "Edit the lisp representation of the value of WIDGET."
-  (custom-variable-apply widget)
-  (widget-put widget :custom-show t)
-  (widget-put widget :custom-status 'lisp)
-  (custom-reset widget))
+  (widget-put widget :custom-state 'unknown)
+  (widget-put widget :custom-form 'lisp)
+  (custom-redraw widget))
 
 (defun custom-variable-apply (widget)
   "Set the current value for the variable being edited by WIDGET."
-  (let ((status (widget-get widget :custom-status))
-       (show (widget-get widget :custom-show))
+  (let ((form (widget-get widget :custom-form))
+       (state (widget-get widget :custom-state))
        (child (car (widget-get widget :children)))
-       (symbol (widget-value widget)))
-    (unless show
-      (error "You can only apply visible options"))
-    (widget-apply child :validate)
-    (if (eq status 'lisp)
-       (set symbol (eval (widget-value child)))
-      (set symbol (widget-value child)))))
+       (symbol (widget-value widget))
+       val)
+    (cond ((eq state 'hidden)
+          (error "Cannot apply hidden variable."))
+         ((setq val (widget-apply child :validate))
+          (error "Invalid %S"))
+         ((eq form 'lisp)
+          (set symbol (eval (widget-value child))))
+         (t
+          (set symbol (widget-value child))))
+    (custom-variable-state-set widget)
+    (custom-redraw-magic widget)))
 
 (defun custom-variable-set-default (widget)
   "Set the default value for the variable being edited by WIDGET."
-  (let ((status (widget-get widget :custom-status))
-       (show (widget-get widget :custom-show))
+  (let ((form (widget-get widget :custom-form))
+       (state (widget-get widget :custom-state))
        (child (car (widget-get widget :children)))
-       (symbol (widget-value widget)))
-    (unless show
-      (error "Can't apply hidden value."))
-    (widget-apply child :validate)
-    (if (eq status 'lisp)
-       (put symbol 'saved-value (list (widget-value child)))
-      (put symbol 'saved-value (list (custom-quote (widget-value child)))))))
+       (symbol (widget-value widget))
+       val)
+    (cond ((eq state 'hidden)
+          (error "Cannot apply hidden variable."))
+         ((setq val (widget-apply child :validate))
+          (error "Invalid %S"))
+         ((eq form 'lisp)
+          (put symbol 'saved-value (list (widget-value child))))
+         (t
+          (put symbol
+               'saved-value (list (custom-quote (widget-value
+                                                 child))))))
+    (custom-variable-state-set widget)
+    (custom-redraw-magic widget)))
 
 (defun custom-variable-default (widget)
   "Restore the default value for the variable being edited by WIDGET."
   (let ((symbol (widget-value widget)))
-    (cond ((get symbol 'saved-value)
-          (set symbol (car (get symbol 'saved-value))))
-         ((get symbol 'factory-value)
-          (set symbol (car (get symbol 'factory-value))))
-         (t
-          (error "No default value for %s" symbol))))
-  (custom-reset widget))
+    (if (get symbol 'saved-value)
+       (set symbol (car (get symbol 'saved-value)))
+      (error "No default value for %s" symbol))
+    (widget-put widget :custom-state 'unknown)
+    (custom-redraw widget)))
 
 (defun custom-variable-factory (widget)
   "Restore the factory setting for the variable being edited by WIDGET."
   (let ((symbol (widget-value widget)))
     (if (get symbol 'factory-value)
        (set symbol (car (get symbol 'factory-value)))
-      (error "No factory default for %S" symbol)))
-  (custom-reset widget))
+      (error "No factory default for %S" symbol))
+    (when (get symbol 'saved-value)
+      (put symbol 'saved-value nil))
+    (widget-put widget :custom-state 'unknown)
+    (custom-redraw widget)))
 
 ;;; The `custom-face-edit' Widget.
 
@@ -306,8 +491,9 @@ Optional EVENT is the location for the menu."
 
 (define-widget 'custom-face-edit 'checklist
   "Edit face attributes."
-  :format "%t:\n%v"
+  :format "%t: %v"
   :tag "Attributes"
+  :extra-offset 12
   :args (mapcar (lambda (att)
                  (list 'group 
                        :inline t
@@ -317,54 +503,61 @@ Optional EVENT is the location for the menu."
 
 ;;; The `custom-display' Widget.
 
-(define-widget 'custom-display 'choice
+(define-widget 'custom-display 'menu-choice
   "Select a display type."
   :tag "Display"
   :value t
   :args '((const :tag "all" t)
-         (checklist :entry-format "\n%b %v"
-                    :args ((list (const :format "Type: " type)
-                                 (checklist :inline t
-                                            (const :format "X "
-                                                   x)
-                                            (const :format "TTY"
-                                                   tty)))
-                           (list (const :format "Class: " class)
-                                 (checklist :inline t
-                                            (const :format "Color "
-                                                   color)
-                                            (const :format
-                                                   "Grayscale "
-                                                   grayscale)
-                                            (const :format "Monochrome"
-                                                   mono)))
-                           (list (const :format "Background: " background)
-                                 (checklist :inline t
-                                            (const :format "Light "
-                                                   light)
-                                            (const :format "Dark\n"
-                                                   dark)))))))
+         (checklist :offset 0
+                    :extra-offset 9
+                    :args ((group (const :format "Type: " type)
+                                  (checklist :inline t
+                                             :offset 0
+                                             (const :format "X "
+                                                    x)
+                                             (const :format "TTY%n"
+                                                    tty)))
+                           (group (const :format "Class: " class)
+                                  (checklist :inline t
+                                             :offset 0
+                                             (const :format "Color "
+                                                    color)
+                                             (const :format
+                                                    "Grayscale "
+                                                    grayscale)
+                                             (const :format "Monochrome%n"
+                                                    mono)))
+                           (group  (const :format "Background: " background)
+                                   (checklist :inline t
+                                              :offset 0
+                                              (const :format "Light "
+                                                     light)
+                                              (const :format "Dark\n"
+                                                     dark)))))))
 
 ;;; The `custom-face' Widget.
 
 (define-widget 'custom-face 'custom
   "Customize face."
-  :format "%l%h%[%t%]: %s%x%v"
+  :format "%l%[%t%]: %s%m %h%v"
   :format-handler 'custom-face-format-handler
   :help-echo "Push me to set or reset this face."
   :custom-documentation-property 'face-documentation
   :value-create 'custom-face-value-create
-  :action 'custom-face-action)
+  :action 'custom-face-action
+  :custom-apply 'custom-face-apply
+  :custom-set-default 'custom-face-set-default
+  :custom-reset 'custom-redraw)
 
 (defun custom-face-format-handler (widget escape)
   ;; We recognize extra escape sequences.
   (let* (child 
         (symbol (widget-get widget :value)))
     (cond ((eq escape ?s)
-          (setq child (widget-create 'choice-item
-                                     :parent widget
-                                     :format "(%[sample%])\n"
-                                     :button-face symbol)))
+          (setq child (widget-create-child-and-convert 
+                       widget 'custom-level
+                       :format "(%[sample%])\n"
+                       :button-face symbol)))
          (t 
           (custom-format-handler widget escape)))
     (when child
@@ -373,40 +566,55 @@ Optional EVENT is the location for the menu."
 
 (defun custom-face-value-create (widget)
   ;; Create a list of the display specifications.
-  (when (widget-get widget :custom-show)
+  (unless (eq (preceding-char) ?\n)
+    (insert "\n"))
+  (when (not (eq (widget-get widget :custom-state) 'hidden))
     (let* ((symbol (widget-value widget))
-          (edit (widget-create 'repeat 
-                               :entry-format "%i %d %v"
-                               :parent widget
-                               :value (or (get symbol 'saved-face)
-                                          (get symbol 'factory-face))
-                               '(list custom-display custom-face-edit))))
+          (edit (widget-create-child-and-convert
+                 widget 'editable-list
+                 :entry-format "%i %d %v"
+                 :value (or (get symbol 'saved-face)
+                            (get symbol 'factory-face))
+                 '(group :format "%v"
+                         custom-display custom-face-edit))))
+      (custom-face-state-set widget)
       (widget-put widget :children (list edit)))))
 
 (defvar custom-face-menu 
   '(("Apply" . custom-face-apply)
     ("Set Default" . custom-face-set-default)
-    ("Default" . custom-face-default)
-    ("Factory" . custom-face-factory))
+    ("Reset to Default" . custom-face-default)
+    ("Reset to Factory Setting" . custom-face-factory))
   "Alist of actions for the `custom-face' widget.
 The key is a string containing the name of the action, the value is a
 lisp function taking the widget as an element which will be called
 when the action is chosen.")
 
+(defun custom-face-state-set (widget)
+  "Set the state of WIDGET."
+  (let ((symbol (widget-value widget)))
+    (widget-put widget :custom-state (cond ((get symbol 'saved-face)
+                                           'saved)
+                                          ((get symbol 'factory-face)
+                                           'factory)
+                                          (t 
+                                           'rogue)))))
+
 (defun custom-face-action (widget &optional event)
   "Show the menu for `custom-face' WIDGET.
 Optional EVENT is the location for the menu."
+  (when (eq (widget-get widget :custom-state) 'hidden)
+    (error "You cannot edit a hidden face"))
   (let* ((completion-ignore-case t)
-        (answer (widget-choose (symbol-name (widget-get widget :value))
-                               custom-face-menu
-                              event)))
+        (symbol (widget-get widget :value))
+        (answer (widget-choose (symbol-name symbol) custom-face-menu event)))
     (if answer
-       (funcall answer widget))))
+       (funcall answer widget))
+    (custom-face-state-set widget)
+    (custom-redraw-magic widget)))
 
 (defun custom-face-apply (widget)
   "Make the face attributes in WIDGET take effect."
-  (unless (widget-get widget :custom-show)
-    (error "You cannot apply hidden face"))
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children)))
         (value (widget-value child)))
@@ -414,8 +622,6 @@ Optional EVENT is the location for the menu."
 
 (defun custom-face-set-default (widget)
   "Make the face attributes in WIDGET default."
-  (unless (widget-get widget :custom-show)
-    (error "You cannot set default for hidden face"))
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children)))
         (value (widget-value child)))
@@ -423,46 +629,45 @@ Optional EVENT is the location for the menu."
 
 (defun custom-face-default (widget)
   "Restore WIDGET to the face's default attributes."
-  (unless (widget-get widget :custom-show)
-    (error "You cannot reset to default for hidden face"))
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children))))
-    (widget-value-set child (or (get symbol 'saved-face)
-                                (get symbol 'factory-face)))
-    (widget-setup)))
+    (unless (get symbol 'saved-face)
+      (error "No saved value for this face")
+    (widget-value-set child (get symbol 'saved-face)))))
 
 (defun custom-face-factory (widget)
   "Restore WIDGET to the face's factory settings."
-  (unless (widget-get widget :custom-show)
-    (error "You cannot reset to factory setting for hidden face"))
   (let* ((symbol (widget-value widget))
         (child (car (widget-get widget :children))))
-    (widget-value-set child (or (get symbol 'factory-face)
-                                (get symbol 'saved-face)))
-    (widget-setup)))
+    (unless (get symbol 'factory-face)
+      (error "No factory default for this face"))
+    (when (get symbol 'saved-face)
+      (put symbol 'saved-face nil))
+    (widget-value-set child (get symbol 'factory-face))))
 
 ;;; The `face' Widget.
 
 (define-widget 'face 'default
   "Select and customize a face."
   :convert-widget 'widget-item-convert-widget
-  :custom-show nil
-  :format "%[%t%]\n%v"
+  :format "%[%t%]%v"
+  :value 'default
   :value-create 'widget-face-value-create
   :value-delete 'widget-radio-value-delete
   :value-get 'widget-item-value-get
-  :validate 'widget-repeat-validate
+  :validate 'widget-editable-list-validate
   :action 'widget-face-action
   :match '(lambda (widget value) (symbolp value)))
 
 (defun widget-face-value-create (widget)
   ;; Create a `custom-face' child.
   (let* ((symbol (widget-value widget))
-        (child (widget-create 'custom-face
-                              :custom-level nil
-                              :custom-show t
-                              :tag "Face"
-                              :value symbol)))
+        (child (widget-create-child-and-convert
+                widget 'custom-face
+                :custom-level nil
+                :tag ""
+                :value symbol)))
+    (custom-magic-reset child)
     (widget-put widget :children (list child))))
 
 (defvar face-history nil
@@ -484,37 +689,40 @@ Optional EVENT is the location for the menu."
 
 (define-widget 'custom-group 'custom
   "Customize group."
-  :format "%l%h%[%t%]:\n%x%v"
+  :format "%l%[%t%]:\n%m %h%v"
   :custom-documentation-property 'group-documentation
   :help-echo "Push me to set or reset all members of this group."
   :value-create 'custom-group-value-create
-  :action 'custom-group-action)
+  :action 'custom-group-action
+  :custom-apply 'custom-group-apply
+  :custom-set-default 'custom-group-set-default
+  :custom-reset 'custom-group-reset)
 
 (defun custom-group-value-create (widget)
-  (let* ((show (widget-get widget :custom-show))
+  (let* ((state (widget-get widget :custom-state))
         (level (widget-get widget :custom-level))
         (symbol (widget-value widget))
         (members (get symbol 'custom-group)))
-    (when show
-      (widget-put widget
-                 :children (mapcar (lambda (entry)
-                                     (widget-insert "\n")
-                                     (prog1
-                                         (widget-create (nth 1 entry)
-                                                        :parent widget
-                                                        :custom-level 
-                                                        (1+ level)
-                                                        :value (nth 0 entry))
-                                       (unless (eq (preceding-char) ?\n)
-                                         (widget-insert "\n"))))
-                                   members)))))
+    (unless (eq state 'hidden)
+      (let* ((children (mapcar (lambda (entry)
+                                (widget-insert "\n")
+                                (prog1
+                                    (widget-create-child-and-convert
+                                     widget (nth 1 entry)
+                                     :group widget
+                                     :custom-level (1+ level)
+                                     :value (nth 0 entry))
+                                  (unless (eq (preceding-char) ?\n)
+                                    (widget-insert "\n"))))
+                              members)))
+       (mapcar 'custom-magic-reset children)
+       (widget-put widget :children children)
+       (custom-group-state-update widget)))))
 
 (defvar custom-group-menu 
-  '(("Apply" . custom-unimplemented)
-    ("Set Default" . custom-unimplemented)
-    ("Reset" . custom-reset)
-    ("Reset to Default" . custom-unimplemented)
-    ("Reset to Factory Settings" . custom-unimplemented))
+  '(("Apply" . custom-group-apply)
+    ("Set Default" . custom-group-set-default)
+    ("Reset" . custom-group-reset))
   "Alist of actions for the `custom-group' widget.
 The key is a string containing the name of the action, the value is a
 lisp function taking the widget as an element which will be called
@@ -530,12 +738,57 @@ Optional EVENT is the location for the menu."
     (if answer
        (funcall answer widget))))
 
+(defun custom-group-apply (widget)
+  "Apply 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-apply)))
+           children )))
+
+(defun custom-group-set-default (widget)
+  "Set default 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-default)))
+           children )))
+
+(defun custom-group-reset (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)))
+           children )))
+
+(defun custom-group-state-update (widget)
+  "Update magic."
+  (unless (eq (widget-get widget :custom-state) 'hidden)
+    (let* ((children (widget-get widget :children))
+          (states (mapcar (lambda (child)
+                            (widget-get child :custom-state))
+                          children))
+          (magics custom-magic-alist)
+          (found 'factory))
+      (while magics
+       (let ((magic (car (car magics))))
+         (if (and (not (eq magic 'hidden))
+                  (memq magic states))
+             (setq found magic
+                   magics nil)
+           (setq magics (cdr magics)))))
+      (widget-put widget :custom-state found)))
+  (custom-magic-reset widget))
+
 ;;; The `custom-save' Command.
 
 (defcustom custom-file "~/.emacs"
+  "File used for storing customization information.
+If you change this from the default \"~/.emacs\" you need to
+explicitly load that file for the settings to take effect."
   :type 'file
-  :group 'customize
-  "File used for storing customization information.")
+  :group 'customize)
 
 (defun custom-save-delete (symbol)
   "Delete the call to SYMBOL form `custom-file'.
@@ -614,17 +867,66 @@ Leave point at the location of the call, or after the last expression."
   (setq custom-mode-map (make-sparse-keymap))
   (set-keymap-parent custom-mode-map widget-keymap))
 
+(easy-menu-define custom-mode-menu 
+    custom-mode-map
+  "Menu used in customization buffers."
+    '("Custom"
+      ["Apply" custom-apply t]
+      ["Set Default" custom-set-default t]
+      ["Reset" custom-reset t]
+      ["Save" custom-save t]))
+
 (defun custom-mode ()
   "Major mode for editing customization buffers.
 
 Read the non-existing manual for information about how to use it.
 
-\\{custom-mode-map}"
+\\[widget-forward]             Move to next button or editable field.
+\\[widget-backward]            Move to previous button or editable field.
+\\[widget-button-click]                Activate button under the mouse pointer.
+\\[widget-button-press]                Activate button under point.
+\\[custom-apply]               Apply all modifications.
+\\[custom-set-default]         Make all modifications default.
+\\[custom-reset]               Undo all modifications.
+\\[custom-save]                        Save defaults for future emacs sessions.
+
+Entry to this mode calls the value of `custom-mode-hook'
+if that value is non-nil."
   (kill-all-local-variables)
   (setq major-mode 'custom-mode
        mode-name "Custom")
   (use-local-map custom-mode-map)
-  (make-local-variable 'custom-options))
+  (make-local-variable 'custom-options)
+  (run-hooks 'custom-mode-hook))
+
+;;; Custom Mode Commands.
+
+(defun custom-apply ()
+  "Apply 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-apply)))
+           children)))
+
+(defun custom-set-default ()
+  "Set default in all modified group members."
+  (interactive)
+  (let ((children custom-options))
+    (mapcar (lambda (child)
+             (when (eq (widget-get child :custom-state) 'modified)
+               (widget-apply child :custom-set-default)))
+           children)))
+
+(defun custom-reset ()
+  "Reset all modified group members."
+  (interactive)
+  (let ((children custom-options))
+    (mapcar (lambda (child)
+             (when (eq (widget-get child :custom-state) 'modified)
+               (widget-apply child :custom-reset)))
+           children)))
 
 ;;; The Customize Commands
 
@@ -669,9 +971,11 @@ Read the non-existing manual for information about how to use it.
   (custom-buffer-create (list (list symbol 'custom-face))))
 
 ;;;###autoload
-(defun customize-apropos (regexp)
-  "Customize all user options matching REGEXP"
-  (interactive "sCustomize regexp: ")
+(defun customize-apropos (regexp &optional all)
+  "Customize all user options matching REGEXP.
+If ALL (e.g., started with a prefix key), include options which are not
+user-settable."
+  (interactive "sCustomize regexp: \nP")
   (let ((found nil))
     (mapatoms (lambda (symbol)
                (when (string-match regexp (symbol-name symbol))
@@ -682,7 +986,9 @@ Read the non-existing manual for information about how to use it.
                  (when (and (boundp symbol)
                             (or (get symbol 'default-value)
                                 (get symbol 'factory-value)
-                                (get symbol 'variable-documentation)))
+                                (if all
+                                    (get symbol 'variable-documentation)
+                                  (user-variable-p symbol))))
                    (setq found
                          (cons (list symbol 'custom-variable) found))))))
     (if found 
@@ -703,16 +1009,37 @@ Press `C-h m' for to get help.
 ")
   (setq custom-options 
        (mapcar (lambda (entry)
-                 (widget-create (nth 1 entry)
-                                :value (nth 0 entry))
-                 (unless (eq (preceding-char) ?\n)
-                   (widget-insert "\n"))
-                 (widget-insert "\n"))
+                 (prog1 
+                     (widget-create (nth 1 entry)
+                                    :value (nth 0 entry))
+                   (unless (eq (preceding-char) ?\n)
+                     (widget-insert "\n"))
+                   (widget-insert "\n")))
                options))
-  (widget-create 'push 
+  (widget-create 'push-button
+                :tag "Apply"
+                :help-echo "Push me to apply all modifications,"
+                :action (lambda (widget &optional event)
+                          (custom-apply)))
+  (widget-insert " ")
+  (widget-create 'push-button
+                :tag "Set Default"
+                :help-echo "Push me to make the modifications default."
+                :action (lambda (widget &optional event)
+                          (custom-set-default)))
+  (widget-insert " ")
+  (widget-create 'push-button
+                :tag "Reset"
+                :help-echo "Push me to undo all modifications.."
+                :action (lambda (widget &optional event)
+                          (custom-reset)))
+  (widget-insert " ")
+  (widget-create 'push-button
                 :tag "Save"
                 :help-echo "Push me to store the new defaults permanently."
-                :action (lambda (widget &optional event) (custom-save)))
+                :action (lambda (widget &optional event)
+                          (custom-save)))
+  (widget-insert "\n")
   (widget-setup))
 
 ;;; The End.
index 0cec53c..ce5dd5d 100644 (file)
@@ -4,7 +4,7 @@
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Keywords: help, faces
-;; Version: 0.9
+;; Version: 0.94
 ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
 
 ;;; Commentary:
 
 (require 'widget)
 
-(let ((keywords '(:type :group)))
-  (while keywords
-    (or (boundp (car keywords))
-       (set (car keywords) (car keywords)))
-    (setq keywords (cdr keywords))))
+(define-widget-keywords :type :group)
 
 ;; These autoloads should be deleted when the file is added to Emacs
 (autoload 'customize "custom-edit" nil t)
 (autoload 'customize-face "custom-edit" nil t)
 (autoload 'customize-apropos "custom-edit" nil t)
 
-;;; Face Utilities.
-
-(make-face 'custom-face-empty)
-
-(defun custom-face-display-set (face spec &optional frame)
-  "Set FACE to the attributes to the first matching entry in SPEC.
-Iff optional FRAME is non-nil, set it for that frame only.
-See `defface' for information about SPEC."
-  (make-face face)
-  (copy-face 'custom-face-empty face)
-  (while spec 
-    (let* ((entry (car spec))
-          (display (nth 0 entry))
-          (atts (nth 1 entry)))
-      (setq spec (cdr spec))
-      (when (custom-display-match-frame display frame)
-       (apply 'custom-face-attribites-set face frame atts)
-       (setq spec nil)))))
-
-(defun custom-display-match-frame (display frame)
-  "Non-nil iff DISPLAY matches FRAME.
-If FRAME is nil, the current FRAME is used."
-  (unless frame 
-    (setq frame (selected-frame)))
-  (if (eq display t)
-      t
-    (let ((match t)
-         (pars (frame-parameters frame)))
-      (while (and display match)
-       (let* ((entry (car display))
-              (req (car entry))
-              (options (cdr entry)))
-         (setq display (cdr display))
-         (cond ((eq req 'type)
-                (setq match (memq window-system options)))
-               ((eq req 'class)
-                (let ((class (cdr (assq 'display-type pars))))
-                  (setq match (memq class options))))
-               ((eq req 'background)
-                (let ((background (cdr (assq 'background-mode pars))))
-                  (setq match (memq background options))))
-               (t
-                (error "Unknown req `%S' with options `%S'" req options)))))
-      match)))
-
-(defvar custom-face-attributes
-  '((:bold (toggle :format "Bold: %v") custom-set-face-bold)
-    (:italic (toggle :format "Italic: %v") custom-set-face-italic)
-    (:underline
-     (toggle :format "Underline: %v") set-face-underline-p)
-    (:foreground (color :tag "Foreground") set-face-foreground)
-    (:background (color :tag "Background") set-face-background)
-    (:stipple (field :format "Stipple: %v") set-face-stipple))
-  "Alist of face attributes. 
-
-The elements are of the form (KEY TYPE SET) where KEY is a symbol
-identifying the attribute, TYPE is a widget type for editing the
-attibute, SET is a function for setting the attribute value.
-
-The SET function should take three arguments, the face to modify, the
-value of the attribute, and optionally the frame where the face should
-be changed.")
-
-(defun custom-face-attribites-set (face frame &rest atts)
-  "For FACE on FRAME set the attributes [KEYWORD VALUE]....
-Each keyword should be listed in `custom-face-attributes'.
-
-If FRAME is nil, set the default face."
-  (while atts 
-    (let* ((name (nth 0 atts))
-          (value (nth 1 atts))
-          (fun (nth 2 (assq name custom-face-attributes))))
-      (setq atts (cdr (cdr atts))) 
-      (funcall fun face value))))
-
-(defun custom-set-face-bold (face value &optional frame)
-  "Set the bold property of FACE to VALUE."
-  (condition-case nil
-      (if value
-         (make-face-bold face frame)
-       (make-face-unbold face frame))
-    (error nil)))
-
-(defun custom-set-face-italic (face value &optional frame)
-  "Set the italic property of FACE to VALUE."
-  (condition-case nil
-      (if value
-         (make-face-italic face frame)
-       (make-face-unitalic face frame))
-    (error nil)))
-
-;;;###autoload
-(defun custom-initialize-faces (&optional frame)
-  "Initialize all custom faces for FRAME.
-If FRAME is nil or omitted, initialize them for all frames."
-  (mapatoms (lambda (symbol)
-             (let ((spec (or (get symbol 'saved-face)
-                             (get symbol 'factory-face))))
-               (when spec 
-                 (custom-face-display-set symbol spec frame))))))
+;;; Compatibility.
+
+(fset 'custom-x-color-values 
+      (if (fboundp 'x-color-values)
+         'x-color-values
+       (lambda (color)
+         (color-instance-rgb-components
+          (make-color-instance color)))))
+
+(defun custom-background-mode ()
+  "Kludge to detext background mode."
+  (let* ((bg-resource 
+         (condition-case ()
+             (x-get-resource ".backgroundMode" "BackgroundMode" 'string)
+           (error nil)))
+        (params (frame-parameters))
+        (color (condition-case ()
+                   (or (assq 'background-color params)
+                       (color-instance-name
+                        (specifier-instance
+                         (face-background 'default))))
+                 (error nil))))
+    (cond (bg-resource (intern (downcase bg-resource)))
+         ((and color
+               (< (apply '+ (custom-x-color-values color))
+                  (/ (apply '+ (custom-x-color-values "white")) 3)))
+          'dark)
+         (t 'light))))
 
 ;;; The `defcustom' Macro.
 
 ;;;###autoload
-(defun custom-declare-variable (symbol value &rest args)
+(defun custom-declare-variable (symbol value doc &rest args)
   "Like `defcustom', but SYMBOL and VALUE are evaluated as notmal arguments."
   (unless (default-boundp symbol)
     (set-default symbol (eval value)))
   (put symbol 'factory-value (list value))
+  (when doc
+    (put symbol 'variable-documentation doc))
   (while args 
     (let ((arg (car args)))
       (setq args (cdr args))
-      (cond ((symbolp arg)
-            (let ((keyword arg)
-                  (value (car args)))
-              (unless args
-                (error "Keyword %s is missing an argument" keyword))
-              (setq args (cdr args))
-              (cond ((eq keyword :type)
-                     (put symbol 'custom-type value))
-                    ((eq keyword :group)
-                     (custom-add-to-group value symbol 'custom-variable))
-                    (t
-                     (error "Unknown keyword %s" symbol)))))
-           ((stringp arg)
-            (put symbol 'variable-documentation arg)
-            (when args
-              (error "Junk at end of args %s" args)))
-           (t
-            (error "Junk in args %S"))))))
+      (unless (symbolp arg)
+       (error "Junk in args %S" args))
+      (let ((keyword arg)
+           (value (car args)))
+       (unless args
+         (error "Keyword %s is missing an argument" keyword))
+       (setq args (cdr args))
+       (cond ((eq keyword :type)
+              (put symbol 'custom-type value))
+             ((eq keyword :group)
+              (custom-add-to-group value symbol 'custom-variable))
+             (t
+              (error "Unknown keyword %s" symbol)))))))
 
 ;;;###autoload
-(defmacro defcustom (symbol value &rest args)
+(defmacro defcustom (symbol value doc &rest args)
   "Declare SYMBOL as a customizable variable that defaults to VALUE.
+DOC is the variable documentation.
+
 Neither SYMBOL nor VALUE needs to be quoted.
 If SYMBOL is not already bound, initialize it to VALUE.
 The remaining arguments should have the form
 
-   [KEYWORD VALUE]... DOC
-
-where DOC is the variable documentation.
+   [KEYWORD VALUE]... 
 
 The following KEYWORD's are defined:
 
@@ -181,50 +102,48 @@ The following KEYWORD's are defined:
 
 Read the section about customization in the emacs lisp manual for more
 information."
-  `(custom-declare-variable (quote ,symbol) (quote ,value) ,@args))
+  `(eval-and-compile
+     (custom-declare-variable (quote ,symbol) (quote ,value) ,doc ,@args)))
 
 ;;; The `defface' Macro.
 
 ;;;###autoload
-(defun custom-declare-face (face spec &rest args)
+(defun custom-declare-face (face spec doc &rest args)
   "Like `defface', but FACE is evaluated as a normal argument."
   (put face 'factory-face spec)
   (let ((value (or (get face 'saved-face) spec)))
     (custom-face-display-set face value))
+  (when doc
+    (put face 'face-documentation doc))
   (while args 
     (let ((arg (car args)))
       (setq args (cdr args))
-      (cond ((symbolp arg)
-            (let ((keyword arg)
-                  (value (car args)))
-              (unless args
-                (error "Keyword %s is missing an argument" :type))
-              (setq args (cdr args))
-              (cond ((eq keyword :group)
-                     (custom-add-to-group value face 'custom-face))
-                    (t
-                     (error "Unknown keyword %s" face)))))
-           ((stringp arg)
-            (put face 'face-documentation arg)
-            (when args
-              (error "Junk at end of args %s" args)))
-           (t
-            (error "Junk in args %S"))))))
+      (unless (symbolp arg)
+       (error "Junk in args %S" args))
+      (let ((keyword arg)
+           (value (car args)))
+       (unless args
+         (error "Keyword %s is missing an argument" :type))
+       (setq args (cdr args))
+       (cond ((eq keyword :group)
+              (custom-add-to-group value face 'custom-face))
+             (t
+              (error "Unknown keyword %s" face)))))))
 
 ;;;###autoload
-(defmacro defface (face spec &rest args)
+(defmacro defface (face spec doc &rest args)
   "Declare FACE as a customizable face that defaults to SPEC.
 FACE does not need to be quoted.
 
+Third argument DOC is the face documentation.
+
 If FACE has been set with `custom-set-face', set the face attributes
 as specified by that function, otherwise set the face attributes
 according to SPEC.
 
 The remaining arguments should have the form
 
-   [KEYWORD VALUE]... DOC
-
-where DOC is the face documentation.
+   [KEYWORD VALUE]...
 
 The following KEYWORD's are defined:
 
@@ -257,39 +176,38 @@ match one of the ITEM.  The following REQ are defined:
 
 Read the section about customization in the emacs lisp manual for more
 information."
-  `(custom-declare-face (quote ,face) ,spec ,@args))
+  `(custom-declare-face (quote ,face) ,spec ,doc ,@args))
 
 ;;; The `defgroup' Macro.
 
 ;;;###autoload
-(defun custom-declare-group (symbol members &rest args)
+(defun custom-declare-group (symbol members doc &rest args)
   "Like `defgroup', but SYMBOL is evaluated as a normal argument."
   (put symbol 'custom-group (nconc members (get symbol 'custom-group)))
+  (when doc
+    (put symbol 'group-documentation doc))
   (while args 
     (let ((arg (car args)))
       (setq args (cdr args))
-      (cond ((symbolp arg)
-            (let ((keyword arg)
-                  (value (car args)))
-              (unless args
-                (error "Keyword %s is missing an argument" :type))
-              (setq args (cdr args))
-              (cond ((eq keyword :group)
-                     (custom-add-to-group value symbol 'custom-group))
-                    (t
-                     (error "Unknown keyword %s" symbol)))))
-           ((stringp arg)
-            (put symbol 'group-documentation arg)
-            (when args
-              (error "Junk at end of args %s" args)))
-           (t
-            (error "Junk in args %S"))))))
+      (unless (symbolp arg)
+       (error "Junk in args %S" args))
+      (let ((keyword arg)
+           (value (car args)))
+       (unless args
+         (error "Keyword %s is missing an argument" :type))
+       (setq args (cdr args))
+       (cond ((eq keyword :group)
+              (custom-add-to-group value symbol 'custom-group))
+             (t
+              (error "Unknown keyword %s" symbol)))))))
 
 ;;;###autoload
-(defmacro defgroup (symbol members &rest args)
+(defmacro defgroup (symbol members doc &rest args)
   "Declare SYMBOL as a customization group containing MEMBERS.
 SYMBOL does not need to be quoted.
 
+Third arg DOC is the group documentation.
+
 MEMBERS should be an alist of the form ((NAME WIDGET)...) where
 NAME is a symbol and WIDGET is a widget is a widget for editing that
 symbol.  Useful widgets are `custom-variable' for editing variables,
@@ -297,9 +215,7 @@ symbol.  Useful widgets are `custom-variable' for editing variables,
 
 The remaining arguments should have the form
 
-   [KEYWORD VALUE]... DOC
-
-where DOC is the group documentation.
+   [KEYWORD VALUE]... 
 
 The following KEYWORD's are defined:
 
@@ -308,7 +224,7 @@ The following KEYWORD's are defined:
 
 Read the section about customization in the emacs lisp manual for more
 information."
-  `(custom-declare-group (quote ,symbol) ,members ,@args))
+  `(custom-declare-group (quote ,symbol) ,members ,doc ,@args))
 
 ;;;###autoload
 (defun custom-add-to-group (group option widget)
@@ -320,6 +236,124 @@ If there already is an entry for that option, overwrite it."
        (setcar (cdr old) widget)
       (put group 'custom-group (nconc members (list (list option widget)))))))
 
+;;; Face Utilities.
+
+(make-face 'custom-face-empty)
+
+(defun custom-face-display-set (face spec &optional frame)
+  "Set FACE to the attributes to the first matching entry in SPEC.
+Iff optional FRAME is non-nil, set it for that frame only.
+See `defface' for information about SPEC."
+  (make-face face)
+  (copy-face 'custom-face-empty face)
+  (while spec 
+    (let* ((entry (car spec))
+          (display (nth 0 entry))
+          (atts (nth 1 entry)))
+      (setq spec (cdr spec))
+      (when (custom-display-match-frame display frame)
+       (apply 'custom-face-attribites-set face frame atts)
+       (setq spec nil)))))
+
+(defcustom custom-background-mode nil
+  "The brightness of the background.
+Set this to the symbol dark if your background color is dark, light if
+your background is light, or nil (default) if you want Emacs to
+examine the brightness for you."
+  :group 'customize
+  :type '(choice (choice-item dark) 
+                (choice-item light)
+                (choice-item :tag "default" nil)))
+
+(defun custom-display-match-frame (display frame)
+  "Non-nil iff DISPLAY matches FRAME.
+If FRAME is nil, the current FRAME is used."
+  ;; This is a kludge to get started, we realle should use specifiers!
+  (unless frame 
+    (setq frame (selected-frame)))
+  (if (eq display t)
+      t
+    (let ((match t)
+         (pars (frame-parameters frame)))
+      (while (and display match)
+       (let* ((entry (car display))
+              (req (car entry))
+              (options (cdr entry)))
+         (setq display (cdr display))
+         (cond ((eq req 'type)
+                (setq match (if (fboundp 'device-type)
+                                (device-type frame)
+                              (memq window-system options))))
+               ((eq req 'class)
+                (let ((class (if (fboundp 'device-class)
+                                 (device-class frame)
+                               (cdr (assq 'display-type pars)))))
+                  (setq match (memq class options))))
+               ((eq req 'background)
+                (let ((background (or custom-background-mode
+                                      (cdr (assq 'background-mode pars))
+                                      (custom-background-mode))))
+                  (setq match (memq background options))))
+               (t
+                (error "Unknown req `%S' with options `%S'" req options)))))
+      match)))
+
+(defvar custom-face-attributes
+  '((:bold (toggle :format "Bold: %v") custom-set-face-bold)
+    (:italic (toggle :format "Italic: %v") custom-set-face-italic)
+    (:underline
+     (toggle :format "Underline: %v") set-face-underline-p)
+    (:foreground (color :tag "Foreground") set-face-foreground)
+    (:background (color :tag "Background") set-face-background)
+    (:stipple (editable-field :format "Stipple: %v") set-face-stipple))
+  "Alist of face attributes. 
+
+The elements are of the form (KEY TYPE SET) where KEY is a symbol
+identifying the attribute, TYPE is a widget type for editing the
+attibute, SET is a function for setting the attribute value.
+
+The SET function should take three arguments, the face to modify, the
+value of the attribute, and optionally the frame where the face should
+be changed.")
+
+(defun custom-face-attribites-set (face frame &rest atts)
+  "For FACE on FRAME set the attributes [KEYWORD VALUE]....
+Each keyword should be listed in `custom-face-attributes'.
+
+If FRAME is nil, set the default face."
+  (while atts 
+    (let* ((name (nth 0 atts))
+          (value (nth 1 atts))
+          (fun (nth 2 (assq name custom-face-attributes))))
+      (setq atts (cdr (cdr atts))) 
+      (funcall fun face value))))
+
+(defun custom-set-face-bold (face value &optional frame)
+  "Set the bold property of FACE to VALUE."
+  (condition-case nil
+      (if value
+         (make-face-bold face frame)
+       (make-face-unbold face frame))
+    (error nil)))
+
+(defun custom-set-face-italic (face value &optional frame)
+  "Set the italic property of FACE to VALUE."
+  (condition-case nil
+      (if value
+         (make-face-italic face frame)
+       (make-face-unitalic face frame))
+    (error nil)))
+
+;;;###autoload
+(defun custom-initialize-faces (&optional frame)
+  "Initialize all custom faces for FRAME.
+If FRAME is nil or omitted, initialize them for all frames."
+  (mapatoms (lambda (symbol)
+             (let ((spec (or (get symbol 'saved-face)
+                             (get symbol 'factory-face))))
+               (when spec 
+                 (custom-face-display-set symbol spec frame))))))
+
 ;;; Initializing.
 
 ;;;###autoload
@@ -355,8 +389,8 @@ See `defface' for the format of SPEC."
   "Customization of the One True Editor.")
 
 (defgroup customize nil
-  :group 'emacs
-  "Customization of the Customization support.")
+  "Customization of the Customization support."
+  :group 'emacs)
 
 ;;; The End.
 
index 77edaa7..1eb3674 100644 (file)
@@ -66,19 +66,19 @@ Gnus provides the following functions:
 * gnus-summary-save-in-file (article format).
 * gnus-summary-save-in-vm (use VM's folder format).")
 
-(defvar gnus-rmail-save-name (function gnus-plain-save-name)
+(defvar gnus-rmail-save-name 'gnus-plain-save-name
   "*A function generating a file name to save articles in Rmail format.
 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
 
-(defvar gnus-mail-save-name (function gnus-plain-save-name)
+(defvar gnus-mail-save-name 'gnus-plain-save-name
   "*A function generating a file name to save articles in Unix mail format.
 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
 
-(defvar gnus-folder-save-name (function gnus-folder-save-name)
+(defvar gnus-folder-save-name 'gnus-folder-save-name
   "*A function generating a file name to save articles in MH folder.
 The function is called with NEWSGROUP, HEADERS, and optional LAST-FOLDER.")
 
-(defvar gnus-file-save-name (function gnus-numeric-save-name)
+(defvar gnus-file-save-name 'gnus-numeric-save-name
   "*A function generating a file name to save articles in article format.
 The function is called with NEWSGROUP, HEADERS, and optional
 LAST-FILE.")
index 2d04551..845c1d5 100644 (file)
 (require 'nnheader)
 
 (defcustom gnus-directory (or (getenv "SAVEDIR") "~/News/")
-  :group 'gnus
-  :type 'directory
-  "*Directory variable from which all other Gnus file variables are derived.")
+  "*Directory variable from which all other Gnus file variables are derived."
+  :group 'gnus-start
+  :type 'directory)
 
 (defcustom gnus-default-directory nil
-  :group 'gnus
-  :type 'directory
-  "*Default directory for all Gnus buffers.")
+  "*Default directory for all Gnus buffers."
+  :group 'gnus-start
+  :type 'directory)
 
 ;; Site dependent variables.  These variables should be defined in
 ;; paths.el.
@@ -91,12 +91,6 @@ used to 899, you would say something along these lines:
           (equal gnus-nntp-service "nntp"))
        nil
      (list gnus-nntp-service)))
-  :group 'gnus
-  :type '(list
-         (choice
-          (item :tag "NNTP server" nntp)
-          (item :tag "Local spool" nnspool))
-         (field :tag "The name of server"))
   "*Default method for selecting a newsgroup.
 This variable should be a list, where the first element is how the
 news is to be fetched, the second is the address.
index 7d29f56..7cb5aa7 100644 (file)
 (require 'custom)
 
 (defgroup gnus nil
-  :group 'emacs
-  "The coffee-brewing, all singing, all dancing, kitchen sink newsreader.")
+  "The coffee-brewing, all singing, all dancing, kitchen sink newsreader."
+  :group 'emacs)
 
 (defgroup gnus-start nil
-  :group 'gnus
-  "Starting your favorite newsreader.")
+  "Starting your favorite newsreader."
+  :group 'gnus)
 
 (defgroup gnus-score nil
-  :group 'gnus 
-  "Score and kill file handling.")
+  "Score and kill file handling."
+  :group 'gnus )
 
-(defconst gnus-version-number "0.39"
+(defconst gnus-version-number "0.40"
   "Version number for this version of Gnus.")
 
 (defconst gnus-version (format "Red Gnus v%s" gnus-version-number)
   "Version string for this version of Gnus.")
 
 (defcustom gnus-inhibit-startup-message nil
+  "*If non-nil, the startup message will not be displayed."
   :group 'gnus-start
-  :type 'toggle
-  "*If non-nil, the startup message will not be displayed.")
+  :type 'toggle)
 
 (defcustom gnus-play-startup-jingle nil
+  "If non-nil, play the Gnus jingle at startup."
   :group 'gnus-start
-  :type 'toggle
-  "If non-nil, play the Gnus jingle at startup.")
+  :type 'toggle)
 
 ;;; Kludges to help the transition from the old `custom.el'.
 
@@ -775,9 +775,9 @@ Returns the number of articles marked as read."
        (kill-buffer (current-buffer))))))
 
 (defcustom gnus-kill-file-name "KILL"
+  "Suffix of the kill files."
   :group 'gnus-score
-  :type 'string
-  "Suffix of the kill files.")
+  :type 'string)
 
 (defun gnus-newsgroup-kill-file (newsgroup)
   "Return the name of a kill file name for NEWSGROUP.
index b72d1cb..6d7576d 100644 (file)
           ("r" . "")
           ("d0" . "")
           ("d1" . "")))))
-    (setq buffer-file-name nil))
-  )t
+    (setq buffer-file-name nil)))
 
 (provide 'nnweb)
 
index 15aa6c7..07d3db4 100644 (file)
@@ -4,7 +4,7 @@
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Keywords: extensions
-;; Version: 0.9
+;; Version: 0.94
 ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
 
 ;;; Commentary:
@@ -16,6 +16,7 @@
 (require 'widget)
 (require 'custom)
 (require 'cl)
+(autoload 'pp-to-string "pp")
 
 ;;; Compatibility.
 
@@ -31,37 +32,39 @@ into the buffer visible in the event's window."
 ;;; Customization.
 
 (defgroup widgets nil
-  :group 'emacs
-  "Customization support for the Widget Library.")
+  "Customization support for the Widget Library."
+  :group 'emacs)
 
-(defface widget-button-face 
-  '((t (:bold t)))
-  :group 'widgets
-  "Face used for widget buttons.")
+(defface widget-documentation-face '((t ()))
+  "Face used for documentation text."
+  :group 'widgets)
+
+(defface widget-button-face '((t (:bold t)))
+  "Face used for widget buttons."
+  :group 'widgets)
 
 (defcustom widget-mouse-face 'highlight
+  "Face used for widget buttons when the mouse is above them."
   :type 'face
-  :group 'widgets
-  "Face used for widget buttons when the mouse is above them.")
-
-(defface widget-field-face 
-  '((((type x)
-      (class grayscale color)
-      (background light))
-     (:background "light gray"))
-    (((type x)
-      (class grayscale color)
-      (background dark))
-     (:background "dark gray"))
-    (t 
-     (:italic t)))
-  :group 'widgets
-  "Face used for editable fields.")
+  :group 'widgets)
+
+(defface widget-field-face '((((type x)
+                              (class grayscale color)
+                              (background light))
+                             (:background "light gray"))
+                            (((type x)
+                              (class grayscale color)
+                              (background dark))
+                             (:background "dark gray"))
+                            (t 
+                             (:italic t)))
+  "Face used for editable fields."
+  :group 'widgets)
 
 (defcustom widget-menu-max-size 40
-  :type 'integer
   "Largest number of items allowed in a popup-menu.
-Larger menus are read through the minibuffer.")
+Larger menus are read through the minibuffer."
+  :type 'integer)
 
 ;;; Utility functions.
 ;;
@@ -176,8 +179,8 @@ minibuffer."
 
 (defun widget-specify-doc (widget from to)
   ;; Specify documentation for WIDGET between FROM and TO.
-  (put-text-property from to 'widget-doc widget))
-
+  (add-text-properties from to (list 'widget-doc widget
+                                    'face 'widget-documentation-face)))
 
 (defmacro widget-specify-insert (&rest form)
   ;; Execute FORM without inheriting any text properties.
@@ -255,6 +258,28 @@ The optional ARGS are additional keyword arguments."
     (widget-apply widget :create)
     widget))
 
+(defun widget-create-child-and-convert (parent type &rest args)
+  "As part of the widget PARENT, create a child widget TYPE.
+The child is converted, using the keyword arguments ARGS."
+  (let ((widget (apply 'widget-convert type args)))
+    (widget-put widget :parent parent)
+    (unless (widget-get widget :indent)
+      (widget-put widget :indent (+ (or (widget-get parent :indent) 0)
+                                   (or (widget-get widget :extra-offset) 0)
+                                   (widget-get parent :offset))))
+    (widget-apply widget :create)
+    widget))
+
+(defun widget-create-child (parent type)
+  "Create widget of TYPE.  "
+  (let ((widget (copy-list type)))
+    (widget-put widget :parent parent)
+    (unless (widget-get widget :indent)
+      (widget-put widget :indent (+ (or (widget-get parent :indent) 0)
+                                   (widget-get parent :offset))))
+    (widget-apply widget :create)
+    widget))
+
 ;;;###autoload
 (defun widget-delete (widget)
   "Delete WIDGET."
@@ -527,13 +552,15 @@ With optional ARG, move across that many fields."
   :value-to-internal (lambda (widget value) value)
   :value-to-external (lambda (widget value) value)
   :create 'widget-default-create
+  :indent nil
+  :offset 0
   :format-handler 'widget-default-format-handler
   :button-face-get 'widget-default-button-face-get 
   :delete 'widget-default-delete
   :value-set 'widget-default-value-set
   :value-inline 'widget-default-value-inline
   :menu-tag-get 'widget-default-menu-tag-get
-  :validate (lambda (widget) t)
+  :validate (lambda (widget) nil)
   :action 'widget-default-action
   :notify 'widget-default-notify)
 
@@ -558,6 +585,10 @@ With optional ARG, move across that many fields."
                (setq button-begin (point)))
               ((eq escape ?\])
                (setq button-end (point)))
+              ((eq escape ?n)
+               (when (widget-get widget :indent)
+                 (insert "\n")
+                 (insert-char ?  (widget-get widget :indent))))
               ((eq escape ?t)
                (if tag
                    (insert tag)
@@ -653,7 +684,7 @@ With optional ARG, move across that many fields."
   :match 'widget-item-match
   :match-inline 'widget-item-match-inline
   :action 'widget-item-action
-  :format "%t\n")
+  :format "%t\n%d")
 
 (defun widget-item-convert-widget (widget)
   ;; Initialize :value and :tag from :args in WIDGET.
@@ -690,9 +721,9 @@ With optional ARG, move across that many fields."
   ;; Items are simple.
   (widget-get widget :value))
 
-;;; The `push' Widget.
+;;; The `push-button' Widget.
 
-(define-widget 'push 'item
+(define-widget 'push-button 'item
   "A pushable button."
   :format "%[[%t]%]%d")
 
@@ -702,19 +733,32 @@ With optional ARG, move across that many fields."
   "An embedded link."
   :format "%[_%t_%]%d")
 
-;;; The `field' Widget.
+;;; The `editable-field' Widget.
 
-(define-widget 'field 'default
+(define-widget 'editable-field 'default
   "An editable text field."
   :convert-widget 'widget-item-convert-widget
   :format "%v"
   :value ""
-  :tag "field"
+  :action 'widget-field-action
   :value-create 'widget-field-value-create
   :value-delete 'widget-field-value-delete
   :value-get 'widget-field-value-get
   :match 'widget-field-match)
 
+;; History of field minibuffer edits.
+(defvar widget-field-history nil)
+
+(defun widget-field-action (widget &optional event)
+  ;; Edit the value in the minibuffer.
+  (let ((tag (widget-apply widget :menu-tag-get))
+       (invalid (widget-apply widget :validate)))
+    (when invalid
+      (error (widget-get invalid :error)))
+    (widget-value-set widget (read-string (concat tag ": ") 
+                                         (widget-get widget :value)
+                                         'widget-field-history))))
+
 (defun widget-field-value-create (widget)
   ;; Create an editable text field.
   (insert " ")
@@ -768,9 +812,9 @@ With optional ARG, move across that many fields."
 (define-widget 'text 'field
   "A multiline text area.")
 
-;;; The `choice' Widget.
+;;; The `menu-choice' Widget.
 
-(define-widget 'choice 'default
+(define-widget 'menu-choice 'default
   "A menu of options."
   :convert-widget  'widget-choice-convert-widget
   :format "%[%t%]: %v"
@@ -800,17 +844,15 @@ With optional ARG, move across that many fields."
       (setq current (car args)
            args (cdr args))
       (when (widget-apply current :match value)
-       (widget-put widget :children (list (widget-create current
-                                                         :parent widget
-                                                         :value value)))
+       (widget-put widget :children (list (widget-create-child-and-convert
+                                           widget current :value value)))
        (widget-put widget :choice current)
        (setq args nil
              current nil)))
     (when current
       (let ((void (widget-get widget :void)))
-       (widget-put widget :children (list (widget-create void
-                                                         :parent widget
-                                                         :value value)))
+       (widget-put widget :children (list (widget-create-child-and-convert
+                                           widget void :value value)))
        (widget-put widget :choice void)))))
 
 (defun widget-choice-value-get (widget)
@@ -892,7 +934,7 @@ With optional ARG, move across that many fields."
 
 ;;; The `toggle' Widget.
 
-(define-widget 'toggle 'choice
+(define-widget 'toggle 'menu-choice
   "Toggle between two states."
   :convert-widget 'widget-toggle-convert-widget
   :format "%v"
@@ -929,6 +971,7 @@ With optional ARG, move across that many fields."
   "A multiple choice widget."
   :convert-widget 'widget-choice-convert-widget
   :format "%v"
+  :offset 4
   :entry-format "%b %v"
   :menu-tag "checklist"
   :greedy nil
@@ -951,6 +994,9 @@ With optional ARG, move across that many fields."
 (defun widget-checklist-add-item (widget type chosen)
   ;; Create checklist item in WIDGET of type TYPE.
   ;; If the item is checked, CHOSEN is a cons whose cdr is the value.
+  (and (eq (preceding-char) ?\n)
+       (widget-get widget :indent)
+       (insert-char ?  (widget-get widget :indent)))
   (widget-specify-insert 
    (let* ((children (widget-get widget :children))
          (buttons (widget-get widget :buttons))
@@ -965,21 +1011,18 @@ With optional ARG, move across that many fields."
         (cond ((eq escape ?%)
                (insert "%"))
               ((eq escape ?b)
-               (setq button (widget-create 'checkbox
-                                           :parent widget
-                                           :value (not (null chosen)))))
+               (setq button (widget-create-child-and-convert
+                             widget 'checkbox :value (not (null chosen)))))
               ((eq escape ?v)
                (setq child
                      (cond ((not chosen)
-                            (widget-create type :parent widget))
+                            (widget-create-child widget type))
                            ((widget-get type :inline)
-                            (widget-create type
-                                           :parent widget
-                                           :value (cdr chosen)))
+                            (widget-create-child-and-convert
+                             widget type :value (cdr chosen)))
                            (t
-                            (widget-create type
-                                           :parent widget
-                                           :value (car (cdr chosen)))))))
+                            (widget-create-child-and-convert
+                             widget type :value (car (cdr chosen)))))))
               (t 
                (error "Unknown escape `%c'" escape)))))
      ;; Update properties.
@@ -1092,11 +1135,12 @@ With optional ARG, move across that many fields."
   ;; Notify the parent.
   (widget-apply (widget-get widget :parent) :action widget event))
 
-;;; The `radio' Widget.
+;;; The `radio-button-choice' Widget.
 
-(define-widget 'radio 'default
+(define-widget 'radio-button-choice 'default
   "Select one of multiple options."
   :convert-widget 'widget-choice-convert-widget
+  :offset 4
   :format "%v"
   :entry-format "%b %v"
   :menu-tag "radio"
@@ -1114,17 +1158,18 @@ With optional ARG, move across that many fields."
 (defun widget-radio-value-create (widget)
   ;; Insert all values
   (let ((args (widget-get widget :args))
-       (indent (widget-get widget :indent))
        arg)
     (while args 
       (setq arg (car args)
            args (cdr args))
-      (widget-radio-add-item widget arg)
-      (and indent args (insert-char ?\  indent)))))
+      (widget-radio-add-item widget arg))))
 
 (defun widget-radio-add-item (widget type)
   "Add to radio widget WIDGET a new radio button item of type TYPE."
-  (setq type (widget-convert type))
+  ;; (setq type (widget-convert type))
+  (and (eq (preceding-char) ?\n)
+       (widget-get widget :indent)
+       (insert-char ?  (widget-get widget :indent)))
   (widget-specify-insert 
    (let* ((value (widget-get widget :value))
          (children (widget-get widget :children))
@@ -1142,15 +1187,14 @@ With optional ARG, move across that many fields."
         (cond ((eq escape ?%)
                (insert "%"))
               ((eq escape ?b)
-               (setq button (widget-create 'radio-button
-                                           :parent widget
-                                           :value (not (null chosen)))))
+               (setq button (widget-create-child-and-convert
+                             widget 'radio-button 
+                             :value (not (null chosen)))))
               ((eq escape ?v)
                (setq child (if chosen
-                               (widget-create type
-                                              :parent widget
-                                              :value value)
-                             (widget-create type :parent widget))))
+                               (widget-create-child-and-convert
+                                widget type :value value)
+                             (widget-create-child widget type))))
               (t 
                (error "Unknown escape `%c'" escape)))))
      ;; Update properties.
@@ -1252,8 +1296,8 @@ With optional ARG, move across that many fields."
 
 ;;; The `insert-button' Widget.
 
-(define-widget 'insert-button 'push
-  "An insert button for the `repeat' widget."
+(define-widget 'insert-button 'push-button
+  "An insert button for the `editable-list' widget."
   :tag "INS"
   :action 'widget-insert-button-action)
 
@@ -1264,8 +1308,8 @@ With optional ARG, move across that many fields."
 
 ;;; The `delete-button' Widget.
 
-(define-widget 'delete-button 'push
-  "A delete button for the `repeat' widget."
+(define-widget 'delete-button 'push-button
+  "A delete button for the `editable-list' widget."
   :tag "DEL"
   :action 'widget-delete-button-action)
 
@@ -1274,38 +1318,48 @@ With optional ARG, move across that many fields."
   (widget-apply (widget-get widget :parent) 
                :delete-at (widget-get widget :widget)))
 
-;;; The `repeat' Widget.
+;;; The `editable-list' Widget.
 
-(define-widget 'repeat 'default
+(define-widget 'editable-list 'default
   "A variable list of widgets of the same type."
   :convert-widget 'widget-choice-convert-widget
+  :offset 12
   :format "%v%i\n"
-  :format-handler 'widget-repeat-format-handler
+  :format-handler 'widget-editable-list-format-handler
   :entry-format "%i %d %v"
-  :menu-tag "repeat"
-  :value-create 'widget-repeat-value-create
+  :menu-tag "editable-list"
+  :value-create 'widget-editable-list-value-create
   :value-delete 'widget-radio-value-delete
-  :value-get 'widget-repeat-value-get
-  :validate 'widget-repeat-validate
-  :match 'widget-repeat-match
-  :match-inline 'widget-repeat-match-inline
-  :insert-before 'widget-repeat-insert-before
-  :delete-at 'widget-repeat-delete-at)
-
-(defun widget-repeat-format-handler (widget escape)
+  :value-get 'widget-editable-list-value-get
+  :validate 'widget-editable-list-validate
+  :match 'widget-editable-list-match
+  :match-inline 'widget-editable-list-match-inline
+  :insert-before 'widget-editable-list-insert-before
+  :delete-at 'widget-editable-list-delete-at)
+
+(defun widget-editable-list-format-handler (widget escape)
   ;; We recognize the insert button.
   (cond ((eq escape ?i)
-        (insert " ")                   
-        (backward-char 1)
-        (let* ((from (point))
-               (button (widget-create (list 'insert-button 
-                                            :parent widget))))
-          (widget-specify-button button from (point)))
-        (forward-char 1))
+        (and (widget-get widget :indent)
+             (insert-char ?  (widget-get widget :indent)))
+        (widget-create-child-and-convert widget 'insert-button))
        (t 
         (widget-default-format-handler widget escape))))
 
-(defun widget-repeat-value-create (widget)
+;(defun widget-editable-list-format-handler (widget escape)
+;  ;; We recognize the insert button.
+;  (cond ((eq escape ?i)
+;       (insert " ")                   
+;       (backward-char 1)
+;       (let* ((from (point))
+;              (button (widget-create-child-and-convert
+;                       widget 'insert-button)))
+;         (widget-specify-button button from (point)))
+;       (forward-char 1))
+;      (t 
+;       (widget-default-format-handler widget escape))))
+
+(defun widget-editable-list-value-create (widget)
   ;; Insert all values
   (let* ((value (widget-get widget :value))
         (type (nth 0 (widget-get widget :args)))
@@ -1316,21 +1370,23 @@ With optional ARG, move across that many fields."
     (while value
       (let ((answer (widget-match-inline type value)))
        (if answer
-           (setq children (cons (widget-repeat-entry-create
-                                 widget (if inlinep
-                                            (car answer)
-                                          (car (car answer))))
+           (setq children (cons (widget-editable-list-entry-create
+                                 widget
+                                 (if inlinep
+                                     (car answer)
+                                   (car (car answer)))
+                                 t)
                                 children)
                  value (cdr answer))
          (setq value nil))))
     (widget-put widget :children (nreverse children))))
 
-(defun widget-repeat-value-get (widget)
+(defun widget-editable-list-value-get (widget)
   ;; Get value of the child widget.
   (apply 'append (mapcar (lambda (child) (widget-apply child :value-inline))
                         (widget-get widget :children))))
 
-(defun widget-repeat-validate (widget)
+(defun widget-editable-list-validate (widget)
   ;; All the chilren must be valid.
   (let ((children (widget-get widget :children))
        child found)
@@ -1340,12 +1396,12 @@ With optional ARG, move across that many fields."
            found (widget-apply child :validate)))
     found))
 
-(defun widget-repeat-match (widget value)
-  ;; Value must be a list and all the members must match the repeat type.
+(defun widget-editable-list-match (widget value)
+  ;; Value must be a list and all the members must match the type.
   (and (listp value)
-       (null (cdr (widget-repeat-match-inline widget value)))))
+       (null (cdr (widget-editable-list-match-inline widget value)))))
 
-(defun widget-repeat-match-inline (widget value)
+(defun widget-editable-list-match-inline (widget value)
   (let ((type (nth 0 (widget-get widget :args)))
        (ok t)
        found)
@@ -1357,7 +1413,7 @@ With optional ARG, move across that many fields."
          (setq ok nil))))
     (cons found value)))
 
-(defun widget-repeat-insert-before (widget before)
+(defun widget-editable-list-insert-before (widget before)
   ;; Insert a new child in the list of children.
   (save-excursion
     (let ((children (widget-get widget :children))
@@ -1367,9 +1423,11 @@ With optional ARG, move across that many fields."
             (goto-char (widget-get before :entry-from)))
            (t
             (goto-char (widget-get widget :value-pos))))
-      (let ((child (widget-repeat-entry-create 
-                   widget (widget-get (nth 0 (widget-get widget :args))
-                                      :value))))
+      (let ((child (widget-editable-list-entry-create 
+                   widget nil nil)))
+       (when (< (widget-get child :entry-from) (widget-get widget :from))
+         (set-marker (widget-get widget :from)
+                     (widget-get child :entry-from)))
        (widget-specify-text (widget-get child :entry-from)
                             (widget-get child :entry-to))
        (if (eq (car children) before)
@@ -1380,7 +1438,7 @@ With optional ARG, move across that many fields."
   (widget-setup)
   (widget-apply widget :notify widget))
 
-(defun widget-repeat-delete-at (widget child)
+(defun widget-editable-list-delete-at (widget child)
   ;; Delete child from list of children.
   (save-excursion
     (let ((buttons (copy-list (widget-get widget :buttons)))
@@ -1406,16 +1464,15 @@ With optional ARG, move across that many fields."
   (widget-setup)
   (widget-apply widget :notify widget))
 
-(defun widget-repeat-entry-create (widget value)
+(defun widget-editable-list-entry-create (widget value conv)
   ;; Create a new entry to the list.
   (let ((type (nth 0 (widget-get widget :args)))
-       (indent (widget-get widget :indent))
        child delete insert)
     (widget-specify-insert 
      (save-excursion
-       (insert (widget-get widget :entry-format))
-       (if indent
-          (insert-char ?\  indent)))
+       (and (widget-get widget :indent)
+           (insert-char ?  (widget-get widget :indent)))
+       (insert (widget-get widget :entry-format)))
      ;; Parse % escapes in format.
      (while (re-search-forward "%\\(.\\)" nil t)
        (let ((escape (aref (match-string 1) 0)))
@@ -1423,15 +1480,16 @@ With optional ARG, move across that many fields."
         (cond ((eq escape ?%)
                (insert "%"))
               ((eq escape ?i)
-               (setq insert (widget-create 'insert-button 
-                                           :parent widget)))
+               (setq insert (widget-create-child-and-convert
+                             widget 'insert-button)))
               ((eq escape ?d)
-               (setq delete (widget-create 'delete-button 
-                                           :parent widget)))
+               (setq delete (widget-create-child-and-convert
+                             widget 'delete-button)))
               ((eq escape ?v)
-               (setq child (widget-create type
-                                          :parent widget
-                                          :value value)))
+               (if conv
+                   (setq child (widget-create-child-and-convert 
+                                widget type :value value))
+                 (setq child (widget-create-child widget type))))
               (t 
                (error "Unknown escape `%c'" escape)))))
      (widget-put widget 
@@ -1457,8 +1515,8 @@ With optional ARG, move across that many fields."
   :format "%v"
   :value-create 'widget-group-value-create
   :value-delete 'widget-radio-value-delete
-  :value-get 'widget-repeat-value-get
-  :validate 'widget-repeat-validate
+  :value-get 'widget-editable-list-value-get
+  :validate 'widget-editable-list-validate
   :match 'widget-group-match
   :match-inline 'widget-group-match-inline)
 
@@ -1466,25 +1524,24 @@ With optional ARG, move across that many fields."
   ;; Create each component.
   (let ((args (widget-get widget :args))
        (value (widget-get widget :value))
-       (indent (widget-get widget :indent))
        arg answer children)
     (while args
       (setq arg (car args)
            args (cdr args)
            answer (widget-match-inline arg value)
-           value (cdr answer)
-           children (cons (cond ((null answer)
-                                 (widget-create arg :parent widget))
-                                ((widget-get arg :inline)
-                                 (widget-create arg
-                                                :parent widget
-                                                :value (car answer)))
-                                (t
-                                 (widget-create arg
-                                                :parent widget
-                                                :value (car (car answer)))))
-                          children))
-      (and args indent (insert-char ?\  indent)))
+           value (cdr answer))
+      (and (eq (preceding-char) ?\n)
+          (widget-get widget :indent)
+          (insert-char ?  (widget-get widget :indent)))
+      (push (cond ((null answer)
+                  (widget-create-child widget arg))
+                 ((widget-get arg :inline)
+                  (widget-create-child-and-convert
+                   widget arg :value (car answer)))
+                 (t
+                  (widget-create-child-and-convert
+                   widget arg :value (car (car answer)))))
+           children))
     (widget-put widget :children (nreverse children))))
 
 (defun widget-group-match (widget values)
@@ -1516,8 +1573,14 @@ With optional ARG, move across that many fields."
   "An immutable sexp."
   :format "%t\n")
 
-(define-widget 'string 'field
-  "A string")
+(define-widget 'string 'editable-field
+  "A string"
+  :tag "String"
+  :format "%[%t%]: %v")
+
+(define-widget 'regexp 'string
+  ;; Should do validation.
+  "A regular expression.")
 
 (define-widget 'file 'string
   "A file widget.  
@@ -1546,56 +1609,85 @@ It will read a directory name from the minibuffer when activated."
 (define-widget 'symbol 'string
   "A lisp symbol."
   :value nil
+  :tag "Symbol"
   :match (lambda (widget value) (symbolp value))
   :value-to-internal (lambda (widget value) (symbol-name value))
   :value-to-external (lambda (widget value) (intern value)))
 
+(define-widget 'function 'symbol
+  ;; Should complete on functions.
+  "A lisp function."
+  :tag "Function")
+
+(define-widget 'variable 'symbol
+  ;; Should complete on variables.
+  "A lisp variable."
+  :tag "Variable")
+
 (define-widget 'sexp 'string
   "An arbitrary lisp expression."
+  :tag "Lisp expression"
   :value nil
   :validate 'widget-sexp-validate
-  :match (lambda  (widget value) t)
-  :value-to-internal (lambda (widget value) (pp-to-string value))
+  :match (lambda (widget value) t)
+  :value-to-internal 'widget-sexp-value-to-internal
   :value-to-external (lambda (widget value) (read value)))
 
+(defun widget-sexp-value-to-internal (widget value)
+  ;; Use pp for printer representation.
+  (let ((pp (pp-to-string value)))
+    (while (string-match "\n\\'" pp)
+      (setq pp (substring pp 0 -1)))
+    (if (or (string-match "\n\\'" pp)
+           (> (length pp) 40))
+       (concat "\n" pp)
+      pp)))
+
 (defun widget-sexp-validate (widget)
   ;; Valid if we can read the string and there is no junk left after it.
   (save-excursion
-    (set-buffer (get-buffer-create " *Widget Scratch*"))
-    (erase-buffer)
-    (insert (widget-apply widget :value-get))
-    (goto-char (point-min))
-    (condition-case data
-       (let ((value (read (current-buffer))))
-         (if (eobp)
-             (if (widget-apply widget :match value)
-                 t
-               (widget-put widget :error (widget-get widget :type-error))
-               nil)
-           (widget-put widget
-                       :error (format "Junk at end of expression: %s"
-                                      (buffer-substring (point) (point-max))))
-           nil))
-      (error (widget-put widget :error (error-message-string data))
-            nil))))
+    (let ((buffer (set-buffer (get-buffer-create " *Widget Scratch*"))))
+      (erase-buffer)
+      (insert (widget-apply widget :value-get))
+      (goto-char (point-min))
+      (condition-case data
+         (let ((value (read buffer)))
+           (if (eobp)
+               (if (widget-apply widget :match value)
+                   nil
+                 (widget-put widget :error (widget-get widget :type-error))
+                 widget)
+             (widget-put widget
+                         :error (format "Junk at end of expression: %s"
+                                        (buffer-substring (point)
+                                                          (point-max))))
+             widget))
+       (error (widget-put widget :error (error-message-string data))
+              widget)))))
 
 (define-widget 'integer 'sexp
   "An integer."
+  :tag "Integer"
   :value 0
   :type-error "This field should contain an integer"
   :match (lambda (widget value) (integerp value)))
 
 (define-widget 'number 'sexp
   "A floating point number."
+  :tag "Number"
   :value 0.0
   :type-error "This field should contain a number"
   :match (lambda (widget value) (numberp value)))
 
 (define-widget 'list 'group
-  "A lisp list.")
+  "A lisp list."
+  :tag "List"
+  :format "%t:\n%v")
 
 (define-widget 'vector 'group
   "A lisp vector."
+  :tag "Vector"
+  :format "%t:\n%v"
   :match 'widget-vector-match
   :value-to-internal (lambda (widget value) (append value nil))
   :value-to-external (lambda (widget value) (apply 'vector value)))
@@ -1607,6 +1699,8 @@ It will read a directory name from the minibuffer when activated."
 
 (define-widget 'cons 'group
   "A cons-cell."
+  :tag "Cons-cell"
+  :format "%t:\n%v"
   :match 'widget-cons-match
   :value-to-internal (lambda (widget value)
                       (list (car value) (cdr value)))
@@ -1618,6 +1712,26 @@ It will read a directory name from the minibuffer when activated."
        (widget-group-match widget
                           (widget-apply :value-to-internal widget value))))
 
+(define-widget 'choice 'menu-choice
+  "A union of several sexp types."
+  :tag "Choice"
+  :format "%[%t%]: %v")
+  
+(define-widget 'repeat 'editable-list
+  "A variable length homogeneous list."
+  :tag "Repeat"
+  :format "%[%t%]:\n%v%i\n")
+
+(define-widget 'set 'checklist
+  "A list of members from a fixed set."
+  :tag "Set"
+  :format "%[%t%]:\n%v")
+
+(define-widget 'boolean 'toggle
+  "To be nil or non-nil, that is the question."
+  :tag "Boolean"
+  :format "%t: %v")
+
 ;;; The `color' Widget.
 
 (define-widget 'color-item 'choice-item
@@ -1632,9 +1746,10 @@ It will read a directory name from the minibuffer when activated."
       (facemenu-get-face (intern (concat "fg:" (widget-value widget))))
     (error 'default)))
 
-(define-widget 'color 'push
+(define-widget 'color 'push-button
   "Choose a color name (with sample)."
   :format "%[%t%]: %v"
+  :tag "Color"
   :value "default"
   :value-create 'widget-color-value-create
   :value-delete 'widget-radio-value-delete
@@ -1655,9 +1770,8 @@ It will read a directory name from the minibuffer when activated."
   widget-color-choice-list)
 
 (defun widget-color-value-create (widget)
-  (let ((child (widget-create 'color-item 
-                             :parent widget
-                             (widget-get widget :value))))
+  (let ((child (widget-create-child-and-convert
+               widget 'color-item (widget-get widget :value))))
     (widget-put widget :children (list child))))
 
 (defun widget-color-value-get (widget)
index 6f9ee80..e5eadc8 100644 (file)
@@ -4,7 +4,7 @@
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Keywords: help, extensions, faces, hypermedia
-;; Version: 0.9
+;; Version: 0.94
 ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
 
 ;;; Commentary:
 
 (eval-when-compile (require 'cl))
 
-(let ((keywords 
-       '(:create :convert-widget :format :value-create :tag :doc :from :to
-                :args :value :value-from :value-to :action :value-set
-                :value-delete :match :parent :delete :menu-tag-get
-                :value-get :choice :void :menu-tag :on :off :on-type 
-                :off-type :notify :entry-format :button :children
-                :buttons :insert-before :delete-at :format-handler
-                :widget :value-pos :value-to-internal :indent :size
-                :value-to-external :validate :error :directory :must-match
-                :type-error :value-inline :inline :match-inline
-                :greedy :button-face-get :button-face :value-face :keymap
-                :entry-from :entry-to :help-echo)))
-  (while keywords
-    (or (boundp (car keywords))
-       (set (car keywords) (car keywords)))
-    (setq keywords (cdr keywords))))
+(defmacro define-widget-keywords (&rest keys)
+  `(eval-and-compile
+     (let ((keywords (quote ,keys)))
+       (while keywords
+        (or (boundp (car keywords))
+            (set (car keywords) (car keywords)))
+        (setq keywords (cdr keywords))))))
+
+(define-widget-keywords
+  :create :convert-widget :format :value-create :offset :extra-offset
+  :tag :doc :from :to :args :value :value-from :value-to :action
+  :value-set :value-delete :match :parent :delete :menu-tag-get
+  :value-get :choice :void :menu-tag :on :off :on-type :off-type
+  :notify :entry-format :button :children :buttons :insert-before
+  :delete-at :format-handler :widget :value-pos :value-to-internal
+  :indent :size :value-to-external :validate :error :directory
+  :must-match :type-error :value-inline :inline :match-inline :greedy
+  :button-face-get :button-face :value-face :keymap :entry-from
+  :entry-to :help-echo) 
 
 ;; These autoloads should be deleted when the file is added to Emacs.
 (autoload 'widget-create "widget-edit")
index cdf116b..2031926 100644 (file)
@@ -1,3 +1,9 @@
+Sat Sep 21 08:11:43 1996  Lars Magne Ingebrigtsen  <lars@eyesore.no>
+
+       * gnus.texi (New Groups): Split into three nodes.
+       (Group Parameters): Shortened.
+       (Browse Foreign Server): Corrected.
+
 Thu Sep 19 18:45:15 1996  Lars Magne Ingebrigtsen  <lars@eyesore.no>
 
        * gnus.texi (Mail and Procmail): Addition.
index ab62f0a..b793883 100644 (file)
@@ -53,8 +53,18 @@ latexps:
        egrep -v "end{document}" gnus.tmplatexi1 > gnus.tmplatexi
        cat postamble.tex >> gnus.tmplatexi
        $(LATEX) gnus.tmplatexi 
+       $(LATEX) gnus.tmplatexi 
        $(DVIPS) -f gnus.dvi > gnus.ps
 
+pss:   
+       make latex
+       make latexps
+
+psout:
+       make latex
+       make latexboth
+       make out
+
 latexboth: 
        rm -f gnus-manual-a4.ps.gz gnus-manual-standard.ps.gz 
        make latexps
@@ -66,5 +76,7 @@ latexboth:
        gzip gnus-manual-standard.ps 
 
 out:
+       cp gnus-manual-standard.ps.gz gnus-manual-a4.ps.gz \
+       /local/ftp/pub/emacs/gnus/manual
        mv gnus-manual-standard.ps.gz gnus-manual-a4.ps.gz \
        /hom/larsi/www_docs/gnus/manual
index 23568a3..cdb5b55 100644 (file)
@@ -1,7 +1,7 @@
 \input texinfo                  @c -*-texinfo-*-
 
 @setfilename gnus
-@settitle Red Gnus 0.24 Manual
+@settitle Red Gnus 0.40 Manual
 @synindex fn cp
 @synindex vr cp
 @synindex pg cp
@@ -88,7 +88,7 @@
 \clearpage
 }
 
-\newcommand{\gnusitemx}[1]{\vspace{-\itemsep}\item#1}
+\newcommand{\gnusitemx}[1]{\mbox{}\vspace*{-\itemsep}\vspace*{-\parsep}\item#1}
 
 \newcommand{\gnussection}[1]{
 \renewcommand{\gnussectionname}{#1}
 {
 \ifodd\count0
 {
-\hspace*{-0.23cm}\underline{\makebox[\gnusheadtextwidth]{\textbf{\gnuschaptername\hfill\arabic{page}
-}
-}
-}
+\hspace*{-0.23cm}\underline{\makebox[\gnusheadtextwidth]{\textbf{\gnuschaptername\hfill\arabic{page}}}}
 }
 \else
 {
-\hspace*{-3.25cm}\underline{\makebox[\gnusheadtextwidth]{\textbf{\arabic{page}\hfill\gnuschaptername}}
-}
+\hspace*{-3.25cm}\underline{\makebox[\gnusheadtextwidth]{\textbf{\arabic{page}\hfill\gnuschaptername}}}
 }
 \fi
 }
@@ -291,7 +287,7 @@ into another language, under the above conditions for modified versions.
 @tex
 
 @titlepage
-@title Red Gnus 0.24 Manual
+@title Red Gnus 0.40 Manual
 
 @author by Lars Magne Ingebrigtsen
 @page
@@ -531,8 +527,8 @@ your mail without bothering with the server at all, you can use the
 @code{gnus-no-server} command to start Gnus.  That might come in handy
 if you're in a hurry as well.  This command will not attempt to contact
 your primary server---instead, it will just activate all groups on level
-1 and 2.  (You should preferably keep no native groups on those two
-levels.) 
+@code{1} and @code{2}.  (You should preferably keep no native groups on
+those two levels.)
 
 
 @node Slave Gnusae
@@ -585,6 +581,58 @@ It takes the group name as a parameter.
 @cindex new groups
 @cindex subscription
 
+@vindex gnus-check-new-newsgroups
+If you are satisfied that you really never want to see any new groups,
+you can set @code{gnus-check-new-newsgroups} to @code{nil}.  This will
+also save you some time at startup.  Even if this variable is
+@code{nil}, you can always subscribe to the new groups just by pressing
+@kbd{U} in the group buffer (@pxref{Group Maintenance}).  This variable
+is @code{t} by default.
+
+@menu
+* Checking New Groups::      Determining what groups are new.
+* Subscription Methods::     What Gnus should do with new groups.
+* Filtering New Groups::     Making Gnus ignore certain new groups.
+@end menu
+
+
+@node Checking New Groups
+@subsection Checking New Groups
+
+Gnus normally determines whether a group is new or not by comparing the
+list of groups from the active file(s) with the lists of subscribed and
+dead groups.  This isn't a particularly fast method.  If
+@code{gnus-check-new-newsgroups} is @code{ask-server}, Gnus will ask the
+server for new groups since the last time.  This is both faster and
+cheaper.  This also means that you can get rid of the list of killed
+groups altogether, so you may set @code{gnus-save-killed-list} to
+@code{nil}, which will save time both at startup, at exit, and all over.
+Saves disk space, too.  Why isn't this the default, then?
+Unfortunately, not all servers support this command. 
+
+I bet I know what you're thinking now: How do I find out whether my
+server supports @code{ask-server}?  No?  Good, because I don't have a
+fail-safe answer.  I would suggest just setting this variable to
+@code{ask-server} and see whether any new groups appear within the next
+few days.  If any do, then it works.  If none do, then it doesn't
+work.  I could write a function to make Gnus guess whether the server
+supports @code{ask-server}, but it would just be a guess.  So I won't.
+You could @code{telnet} to the server and say @code{HELP} and see
+whether it lists @samp{NEWGROUPS} among the commands it understands.  If
+it does, then it might work.  (But there are servers that lists
+@samp{NEWGROUPS} without supporting the function properly.)
+
+This variable can also be a list of select methods.  If so, Gnus will
+issue an @code{ask-server} command to each of the select methods, and
+subscribe them (or not) using the normal methods.  This might be handy
+if you are monitoring a few servers for new groups.  A side effect is
+that startup will take much longer, so you can meditate while waiting.
+Use the mantra ``dingnusdingnusdingnus'' to achieve permanent bliss.
+
+
+@node Subscription Methods
+@subsection Subscription Methods
+
 @vindex gnus-subscribe-newsgroup-method
 What Gnus does when it encounters a new group is determined by the
 @code{gnus-subscribe-newsgroup-method} variable.
@@ -641,6 +689,10 @@ One common mistake is to set the variable a few paragraphs above to
 @code{gnus-subscribe-hierarchical-interactive}.  This is an error.  This
 will not work.  This is ga-ga.  So don't do it.
 
+
+@node Filtering New Groups
+@subsection Filtering New Groups
+
 A nice and portable way to control which new newsgroups should be
 subscribed (or ignored) is to put an @dfn{options} line at the start of
 the @file{.newsrc} file.  Here's an example:
@@ -679,44 +731,6 @@ that come from mail backends (@code{nnml}, @code{nnbabyl},
 @code{nnfolder}, @code{nnmbox}, and @code{nnmh}) subscribed.  If you
 don't like that, just set this variable to @code{nil}.
 
-@vindex gnus-check-new-newsgroups
-If you are satisfied that you really never want to see any new groups,
-you could set @code{gnus-check-new-newsgroups} to @code{nil}.  This will
-also save you some time at startup.  Even if this variable is
-@code{nil}, you can always subscribe to the new groups just by pressing
-@kbd{U} in the group buffer (@pxref{Group Maintenance}).  This variable
-is @code{t} by default.
-
-Gnus normally determines whether a group is new or not by comparing the
-list of groups from the active file(s) with the lists of subscribed and
-dead groups.  This isn't a particularly fast method.  If
-@code{gnus-check-new-newsgroups} is @code{ask-server}, Gnus will ask the
-server for new groups since the last time.  This is both faster and
-cheaper.  This also means that you can get rid of the list of killed
-groups altogether, so you may set @code{gnus-save-killed-list} to
-@code{nil}, which will save time both at startup, at exit, and all over.
-Saves disk space, too.  Why isn't this the default, then?
-Unfortunately, not all servers support this command. 
-
-I bet I know what you're thinking now: How do I find out whether my
-server supports @code{ask-server}?  No?  Good, because I don't have a
-fail-safe answer.  I would suggest just setting this variable to
-@code{ask-server} and see whether any new groups appear within the next
-few days.  If any do, then it works.  If none do, then it doesn't
-work.  I could write a function to make Gnus guess whether the server
-supports @code{ask-server}, but it would just be a guess.  So I won't.
-You could @code{telnet} to the server and say @code{HELP} and see
-whether it lists @samp{NEWGROUPS} among the commands it understands.  If
-it does, then it might work.  (But there are servers that lists
-@samp{NEWGROUPS} without supporting the function properly.)
-
-This variable can also be a list of select methods.  If so, Gnus will
-issue an @code{ask-server} command to each of the select methods, and
-subscribe them (or not) using the normal methods.  This might be handy
-if you are monitoring a few servers for new groups.  A side effect is
-that startup will take much longer, so you can meditate while waiting.
-Use the mantra ``dingnusdingnusdingnus'' to achieve permanent bliss.
-
 
 @node Changing Servers
 @section Changing Servers
@@ -900,7 +914,7 @@ you actually subscribe to.
 Note that if you subscribe to lots and lots of groups, setting this
 variable to @code{nil} will probably make Gnus slower, not faster.  At
 present, having this variable @code{nil} will slow Gnus down
-considerably, unless you read news over a 2400 baud modem.  
+considerably, unless you read news over a @code{2400} baud modem.  
 
 This variable can also have the value @code{some}.  Gnus will then
 attempt to read active info only on the subscribed groups.  On some
@@ -1010,7 +1024,7 @@ Here's a couple of example group lines:
 
 Quite simple, huh?
 
-You can see that there are 25 unread articles in
+You can see that there are @code{25} unread articles in
 @samp{news.announce.newusers}.  There are no unread articles, but some
 ticked articles, in @samp{alt.fan.andrea-dworkin} (see that little
 asterisk at the beginning of the line?)
@@ -1061,7 +1075,7 @@ Number of read articles.
 
 @item t
 Estimated total number of articles.  (This is really @var{max-number}
-minus @var{min-number} plus 1.)
+minus @var{min-number} plus @code{1}.)
 
 @item y
 Number of unread, unticked, non-dormant articles.
@@ -1312,7 +1326,7 @@ This does the same as the command above, but tries to do it with the
 minimum amount of fuzz (@code{gnus-group-quick-select-group}).  No
 scoring/killing will be performed, there will be no highlights and no
 expunging.  This might be useful if you're in a real hurry and have to
-enter some humongous group.  If you give a 0 prefix to this command
+enter some humongous group.  If you give a @code{0} prefix to this command
 (i.e., @kbd{0 M-RET}), Gnus won't even generate the summary buffer.
 This might be useful if you want to toggle threading before entering the
 group.
@@ -1357,7 +1371,7 @@ caution.
 
 @vindex gnus-large-newsgroup
 The @code{gnus-large-newsgroup} variable says what Gnus should consider
-to be a big group.  This is 200 by default.  If the group has more
+to be a big group.  This is @code{200} by default.  If the group has more
 unread articles than this, Gnus will query the user before entering the
 group.  The user can then specify how many articles should be fetched
 from the server.  If the user specifies a negative number (@code{-n}),
@@ -1467,7 +1481,7 @@ Also @pxref{Group Levels}.
 @cindex level
 
 All groups have a level of @dfn{subscribedness}.  For instance, if a
-group is on level 2, it is more subscribed than a group on level 5.  You
+group is on level @code{2}, it is more subscribed than a group on level @code{5}.  You
 can ask Gnus to just list groups on a given level or lower
 (@pxref{Listing Groups}), or to just check for new articles in groups on
 a given level or lower (@pxref{Scanning New Messages}).
@@ -1488,12 +1502,12 @@ prompted for a level.
 @vindex gnus-level-zombie
 @vindex gnus-level-unsubscribed
 @vindex gnus-level-subscribed
-Gnus considers groups on between levels 1 and
-@code{gnus-level-subscribed} (inclusive) (default 5) to be subscribed,
+Gnus considers groups on between levels @code{1} and
+@code{gnus-level-subscribed} (inclusive) (default @code{5}) to be subscribed,
 @code{gnus-level-subscribed} (exclusive) and
-@code{gnus-level-unsubscribed} (inclusive) (default 7) to be
+@code{gnus-level-unsubscribed} (inclusive) (default @code{7}) to be
 unsubscribed, @code{gnus-level-zombie} to be zombies (walking dead)
-(default 8) and @code{gnus-level-killed} to be killed (default 9),
+(default @code{8}) and @code{gnus-level-killed} to be killed (default @code{9}),
 completely dead.  Gnus treats subscribed and unsubscribed groups exactly
 the same, but zombie and killed groups have no information on what
 articles you have read, etc, stored.  This distinction between dead and
@@ -1501,7 +1515,7 @@ living groups isn't done because it is nice or clever, it is done purely
 for reasons of efficiency.
 
 It is recommended that you keep all your mail groups (if any) on quite
-low levels (e.g. 1 or 2).
+low levels (e.g. @code{1} or @code{2}).
 
 If you want to play with the level variables, you should show some care.
 Set them once, and don't touch them ever again.  Better yet, don't touch
@@ -1510,7 +1524,7 @@ them at all unless you know exactly what you're doing.
 @vindex gnus-level-default-unsubscribed
 @vindex gnus-level-default-subscribed
 Two closely related variables are @code{gnus-level-default-subscribed}
-(default 3) and @code{gnus-level-default-unsubscribed} (default 6),
+(default @code{3}) and @code{gnus-level-default-unsubscribed} (default @code{6}),
 which are the levels that new groups will be put on if they are
 (un)subscribed.  These two variables should, of course, be inside the
 relevant legal ranges.
@@ -1543,7 +1557,7 @@ use this level as the ``work'' level.
 Gnus will normally just activate groups that are on level
 @code{gnus-activate-level} or less.  If you don't want to activate
 unsubscribed groups, for instance, you might set this variable to
-@code{5}. 
+@code{5}.  The default is @code{6}.
 
 
 @node Group Score
@@ -1559,8 +1573,8 @@ This is what @dfn{group score} is for.  You can assign a score to each
 group.  You can then sort the group buffer based on this score.
 Alternatively, you can sort on score and then level.  (Taken together,
 the level and the score is called the @dfn{rank} of the group.  A group
-that is on level 4 and has a score of 1 has a higher rank than a group
-on level 5 that has a score of 300.  (The level is the most significant
+that is on level @code{4} and has a score of @code{1} has a higher rank than a group
+on level @code{5} that has a score of @code{300}.  (The level is the most significant
 part and the score is the least significant part.)
 
 @findex gnus-summary-bubble-group
@@ -1788,27 +1802,6 @@ groups from different @sc{nntp} servers.
 @section Group Parameters
 @cindex group parameters
 
-Gnus stores all information on a group in a list that is usually known
-as the @dfn{group info}.  This list has from three to six elements.
-Here's an example info.
-
-@lisp
-("nnml:mail.ding" 3 ((1 . 232) 244 (256 . 270)) ((tick 246 249))
-                  (nnml "private") ((to-address . "ding@@ifi.uio.no")))
-@end lisp
-
-The first element is the @dfn{group name}, as Gnus knows the group,
-anyway.  The second element is the @dfn{subscription level}, which
-normally is a small integer.  The third element is a list of ranges of
-read articles.  The fourth element is a list of lists of article marks
-of various kinds.  The fifth element is the select method (or virtual
-server, if you like).  The sixth element is a list of @dfn{group
-parameters}, which is what this section is about.
-
-Any of the last three elements may be missing if they are not required.
-In fact, the vast majority of groups will normally only have the first
-three elements, which saves quite a lot of cons cells.
-
 The group parameters store information local to a particular group:
 
 @table @code
@@ -2222,16 +2215,8 @@ then attempt to contact this server and let you browse the groups there
 
 @findex gnus-browse-mode
 A new buffer with a list of available groups will appear.  This buffer
-will be use the @code{gnus-browse-mode}.  This buffer looks a bit
-(well, a lot) like a normal group buffer, but with one major difference
-- you can't enter any of the groups.  If you want to read any of the
-news available on that server, you have to subscribe to the groups you
-think may be interesting, and then you have to exit this buffer.  The
-new groups will be added to the group buffer, and then you can read them
-as you would any other group.
-
-Future versions of Gnus may possibly permit reading groups straight from
-the browse buffer.
+will be use the @code{gnus-browse-mode}.  This buffer looks a bit (well,
+a lot) like a normal group buffer.
 
 Here's a list of keystrokes available in the browse mode:
 
@@ -2300,7 +2285,8 @@ Quit Gnus (@code{gnus-group-exit}).
 @item Q
 @kindex Q (Group)
 @findex gnus-group-quit
-Quit Gnus without saving any startup files (@code{gnus-group-quit}).
+Quit Gnus without saving the @file{.newsrc} files (@code{gnus-group-quit}).
+The dribble file will be saved, though (@pxref{Auto Save}).
 @end table
 
 @vindex gnus-exit-gnus-hook
@@ -2337,6 +2323,21 @@ you put in some misc section that you never bother with anyway.  You can
 even group the Emacs sex groups as a sub-topic to either the Emacs
 groups or the sex groups---or both!  Go wild!
 
+Here's an example:
+
+@example
+Gnus
+  Emacs -- I wuw it!
+     3: comp.emacs
+     2: alt.religion.emacs
+    Naughty Emacs
+     452: alt.sex.emacs
+       0: comp.talk.emacs.recovery
+  Misc
+     8: comp.binaries.fractals
+    13: comp.sources.unix
+@end example
+
 @findex gnus-topic-mode
 @kindex t (Group)
 To get this @emph{fab} functionality you simply turn on (ooh!) the
@@ -2596,7 +2597,7 @@ Gnus
 @end example
 
 So, here we have one top-level topic, two topics under that, and one
-sub-topic under one of the sub-topics.  (There is always just one (1)
+sub-topic under one of the sub-topics.  (There is always just one (@code{1})
 top-level topic).  This topology can be expressed as follows:
 
 @lisp
@@ -2776,8 +2777,10 @@ news.
 
 @table @kbd
 
-@item M-f
-@kindex M-f (Group)
+
+@item H f
+@kindex H f (Group)
+@itemx M-f
 @findex gnus-group-fetch-faq
 @vindex gnus-group-faq-directory
 @cindex FAQ
@@ -2787,8 +2790,8 @@ Try to fetch the FAQ for the current group
 @code{gnus-group-faq-directory}, which is usually a directory on a
 remote machine.  This variable can also be a list of directories.  In
 that case, giving a prefix to this command will allow you to choose
-between the various sites.  @code{ange-ftp} will be used for fetching
-the file.  
+between the various sites.  @code{ange-ftp} (or @code{efs}) will be used
+for fetching the file.
 
 If fetching from the first site is unsuccessful, Gnus will attempt to go
 through @code{gnus-group-faq-directory} and try to open them one by one.
@@ -2838,7 +2841,7 @@ Go to the Gnus info node (@code{gnus-info-find-node}).
 @findex gnus-group-read-init-file
 @vindex gnus-init-file
 @cindex reading init file
-Read the init file (@code{gnus-init-file}, which defaults to
+Re-read the init file (@code{gnus-init-file}, which defaults to
 @file{~/.gnus}) (@code{gnus-group-read-init-file}).
 
 @item s
@@ -3034,8 +3037,10 @@ This restriction may disappear in later versions of Gnus.
 
 @vindex gnus-summary-mode-line-format
 You can also change the format of the summary mode bar.  Set
-@code{gnus-summary-mode-line-format} to whatever you like.  Here are the
-elements you can play with:
+@code{gnus-summary-mode-line-format} to whatever you like.  The default
+is @samp{Gnus: %%b [%A] %Z}.  
+
+Here are the elements you can play with:
 
 @table @samp
 @item G
@@ -3099,7 +3104,7 @@ highlight the current article in the summary buffer.
 @item gnus-summary-highlight
 @vindex gnus-summary-highlight
 Summary lines are highlighted according to this variable, which is a
-list where the elements are on the format @code{(FORM . FACE)}.  If you
+list where the elements are on the format @var{(FORM . FACE)}.  If you
 would, for instance, like ticked articles to be italic and high-scored
 articles to be bold, you could set this variable to something like
 @lisp
@@ -3207,6 +3212,15 @@ threads.
 @section Choosing Articles
 @cindex selecting articles
 
+@menu
+* Choosing Commands::        Commands for choosing articles.
+* Choosing Variables::       Variables that influence these commands.
+@end menu
+
+
+@node Choosing Commands
+@subsection Choosing Commands
+
 None of the following movement commands understand the numeric prefix,
 and they all select and display an article.
 
@@ -3288,6 +3302,10 @@ command above in that you can pop as many previous articles off the
 history as you like.
 @end table
 
+
+@node Choosing Variables
+@subsection Choosing Variables
+
 Some variables that are relevant for moving and selecting articles:
 
 @table @code
@@ -3371,7 +3389,9 @@ Scroll to the beginning of the article
 Scroll to the end of the article (@code{gnus-summary-end-of-article}).
 
 @item A s 
+@itemx s
 @kindex A s (Summary)
+@kindex s (Summary)
 @findex gnus-summary-isearch-article
 Perform an isearch in the article buffer
 (@code{gnus-summary-isearch-article}). 
@@ -3420,12 +3440,6 @@ command uses the process/prefix convention.
 Forward the current article to some other person
 (@code{gnus-summary-mail-forward}). 
 
-@item S o p
-@kindex S o p (Summary)
-@findex gnus-summary-post-forward
-Forward the current article to a newsgroup
-(@code{gnus-summary-post-forward}).
-
 @item S m
 @itemx m
 @kindex m (Summary)
@@ -3472,12 +3486,6 @@ Digest the current series and forward the result using mail
 (@code{gnus-uu-digest-mail-forward}).  This command uses the
 process/prefix convention (@pxref{Process/Prefix}). 
 
-@item S O p
-@kindex S O p (Summary)
-@findex gnus-uu-digest-post-forward
-Digest the current series and forward the result to a newsgroup
-(@code{gnus-uu-digest-mail-forward}).  
-
 @item S M-c
 @kindex S M-c (Summary)
 @findex gnus-summary-mail-crosspost-complaint
@@ -3528,6 +3536,18 @@ Post a followup to the current article and include the original message
 (@code{gnus-summary-followup-with-original}).   This command uses the
 process/prefix convention.
 
+@item S o p
+@kindex S o p (Summary)
+@findex gnus-summary-post-forward
+Forward the current article to a newsgroup
+(@code{gnus-summary-post-forward}).
+
+@item S O p
+@kindex S O p (Summary)
+@findex gnus-uu-digest-post-forward
+Digest the current series and forward the result to a newsgroup
+(@code{gnus-uu-digest-mail-forward}).  
+
 @item S u
 @kindex S u (Summary)
 @findex gnus-uu-post-news
@@ -3578,9 +3598,10 @@ waiting for the article to appear on your site first.  You simply return
 to the post buffer (which is called @code{*post-buf*}).  There you will
 find the article you just posted, with all the headers intact.  Change
 the @code{Message-ID} header to a @code{Cancel} or @code{Supersedes}
-header by substituting one of those words for @code{Message-ID}.  Then
-just press @kbd{C-c C-c} to send the article as you would do normally.
-The previous article will be canceled/superseded.
+header by substituting one of those words for the word
+@code{Message-ID}.  Then just press @kbd{C-c C-c} to send the article as
+you would do normally.  The previous article will be
+canceled/superseded.
 
 Just remember, kids: There is no 'c' in 'supersede'.
 
@@ -4188,8 +4209,8 @@ variable is @code{nil}, Gnus requires an exact match between the
 subjects of the loose threads before gathering them into one big
 super-thread.  This might be too strict a requirement, what with the
 presence of stupid newsreaders that chop off long subjects lines.  If
-you think so, set this variable to, say, 20 to require that only the
-first 20 characters of the subjects have to match.  If you set this
+you think so, set this variable to, say, @code{20} to require that only the
+first @code{20} characters of the subjects have to match.  If you set this
 variable to a really low number, you'll find that Gnus will gather
 everything in sight into one thread, which isn't very helpful.
 
@@ -4206,7 +4227,7 @@ simplification is used.
 @item gnus-simplify-ignored-prefixes
 @vindex gnus-simplify-ignored-prefixes
 If you set @code{gnus-summary-gather-subject-limit} to something as low
-as 10, you might consider setting this variable to something sensible:
+as @code{10}, you might consider setting this variable to something sensible:
 
 @c Written by Michael Ernst <mernst@cs.rice.edu>
 @lisp
@@ -4568,10 +4589,10 @@ while you are reading the previous one?  Why not, indeed.
 First, some caveats.  There are some pitfalls to using asynchronous
 article fetching, especially the way Gnus does it.  
 
-Let's say you are reading article 1, which is short, and article 2 is
+Let's say you are reading article @code{1}, which is short, and article @code{2} is
 quite long, and you are not interested in reading that.  Gnus does not
-know this, so it goes ahead and fetches article 2.  You decide to read
-article 3, but since Gnus is in the process of fetching article 2, the
+know this, so it goes ahead and fetches article @code{2}.  You decide to read
+article @code{3}, but since Gnus is in the process of fetching article @code{2}, the
 connection is blocked.
 
 To avoid these situations, Gnus will open two (count 'em two)
@@ -4594,9 +4615,9 @@ happen automatically.
 
 @vindex gnus-use-article-prefetch
 You can control how many articles that are to be pre-fetched by setting
-@code{gnus-use-article-prefetch}.  This is 30 by default, which means
+@code{gnus-use-article-prefetch}.  This is @code{30} by default, which means
 that when you read an article in the group, the backend will pre-fetch
-the next 30 articles.  If this variable is @code{t}, the backend will
+the next @code{30} articles.  If this variable is @code{t}, the backend will
 pre-fetch all the articles that it can without bound.  If it is
 @code{nil}, no pre-fetching will be made.
 
@@ -4611,7 +4632,7 @@ pre-fetched.  The default is @code{gnus-async-read-p}, which returns
 data structure as the only parameter.
 
 If, for instance, you wish to pre-fetch only unread articles that are
-shorter than 100 lines, you could say something like:
+shorter than @code{100} lines, you could say something like:
 
 @lisp
 (defun my-async-short-unread-p (data)
@@ -4692,9 +4713,9 @@ symbols in these two lists are @code{ticked}, @code{dormant},
 So where does the massive article-fetching and storing come into the
 picture?  The @code{gnus-jog-cache} command will go through all
 subscribed newsgroups, request all unread articles, and store them in
-the cache.  You should only ever, ever ever ever, use this command if 1)
+the cache.  You should only ever, ever ever ever, use this command if @code{1})
 your connection to the @sc{nntp} server is really, really, really slow
-and 2) you have a really, really, really huge disk.  Seriously.
+and @code{2}) you have a really, really, really huge disk.  Seriously.
 
 @vindex gnus-uncacheable-groups
 It is likely that you do not want caching on some groups.  For instance,
@@ -6253,10 +6274,10 @@ Here's and example from a horizontal tree buffer:
 
 @example
 @{***@}-(***)-[odd]-[Gun]
-     |     \[Jan]
-     |     \[odd]-[Eri]
-     |     \(***)-[Eri]
-     |           \[odd]-[Paa]
+     |      \[Jan]
+     |      \[odd]-[Eri]
+     |      \(***)-[Eri]
+     |            \[odd]-[Paa]
      \[Bjo]
      \[Gun]
      \[Gun]-[Jor]
@@ -8481,19 +8502,19 @@ the five possible split syntaxes:
 @samp{group}: If the split is a string, that will be taken as a group name. 
 
 @item
-@code{(FIELD VALUE SPLIT)}: If the split is a list, and the first
+@var{(FIELD VALUE SPLIT)}: If the split is a list, and the first
 element is a string, then that means that if header FIELD (a regexp)
 contains VALUE (also a regexp), then store the message as specified by
 SPLIT.
 
 @item
-@code{(| SPLIT...)}: If the split is a list, and the first element is
+@var{(| SPLIT...)}: If the split is a list, and the first element is
 @code{|} (vertical bar), then process each SPLIT until one of them
 matches.  A SPLIT is said to match if it will cause the mail message to
 be stored in one or more groups.
 
 @item
-@code{(& SPLIT...)}: If the split is a list, and the first element is
+@var{(& SPLIT...)}: If the split is a list, and the first element is
 @code{&}, then process all SPLITs in the list.
 
 @item 
@@ -12926,6 +12947,7 @@ Ol' Charles Reznikoff said it pretty well, so I leave the floor to him:
 
 @quotation
 @strong{Te Deum}
+
 @sp 1
 Not because of victories @*
 I sing,@*
@@ -12933,6 +12955,7 @@ having none,@*
 but for the common sunshine,@*
 the breeze,@*
 the largess of the spring.
+
 @sp 1
 Not for victory@*
 but for the day's work done@*
@@ -15263,10 +15286,17 @@ second is a more complex one:
                 (auto-expire (to-address "ding@@ifi.uio.no")))
 @end example
 
-The first element is the group name as Gnus knows the group; the second
-is the group level; the third is the read articles in range format; the
-fourth is a list of article marks lists; the fifth is the select method;
-and the sixth contains the group parameters.
+The first element is the @dfn{group name}---as Gnus knows the group,
+anyway.  The second element is the @dfn{subscription level}, which
+normally is a small integer.  The third element is a list of ranges of
+read articles.  The fourth element is a list of lists of article marks
+of various kinds.  The fifth element is the select method (or virtual
+server, if you like).  The sixth element is a list of @dfn{group
+parameters}, which is what this section is about.
+
+Any of the last three elements may be missing if they are not required.
+In fact, the vast majority of groups will normally only have the first
+three elements, which saves quite a lot of cons cells.
 
 Here's a BNF definition of the group info format: