]> code.delx.au - gnu-emacs/commitdiff
2003-12-12 Jesper Harder <harder@ifa.au.dk>
authorPer Abrahamsen <abraham@dina.kvl.dk>
Sat, 27 Dec 2003 16:41:13 +0000 (16:41 +0000)
committerPer Abrahamsen <abraham@dina.kvl.dk>
Sat, 27 Dec 2003 16:41:13 +0000 (16:41 +0000)
* cus-edit.el (custom-add-parent-links): Define "many".

2003-12-08  Per Abrahamsen  <abraham@dina.kvl.dk>

* wid-edit.el (widget-child-value-get, widget-child-value-inline)
(widget-child-validate, widget-type-value-create)
(widget-type-default-get, widget-type-match): New functions.
(lazy): New widget.
(menu-choice, checklist, radio-button-choice, editable-list)
(group, documentation-string): Removed redundant (per 2003-10-25
change) calls to `widget-children-value-delete'.
(widget-choice-value-get, widget-choice-value-inline): Removed
functions.
(menu-choice): Updated widget.

lisp/ChangeLog
lisp/cus-edit.el
lisp/wid-edit.el
lispref/customize.texi

index da52c2aa19015ddfab1cd22118210aa5a01c4395..a2e7f95747cce8d3c835a5155e15f78e8c20b4b1 100644 (file)
        * info.el (Info-unescape-quotes, Info-split-parameter-string)
        (Info-goto-emacs-command-node): Doc fixes.
 
+2003-12-12  Jesper Harder <harder@ifa.au.dk>
+
+       * cus-edit.el (custom-add-parent-links): Define "many".
+
+2003-12-08  Per Abrahamsen  <abraham@dina.kvl.dk>
+
+       * wid-edit.el (widget-child-value-get, widget-child-value-inline)
+       (widget-child-validate, widget-type-value-create)
+       (widget-type-default-get, widget-type-match): New functions.
+       (lazy): New widget.
+       (menu-choice, checklist, radio-button-choice, editable-list)
+       (group, documentation-string): Removed redundant (per 2003-10-25
+       change) calls to `widget-children-value-delete'.
+       (widget-choice-value-get, widget-choice-value-inline): Removed
+       functions. 
+       (menu-choice): Updated widget.
+
 2003-12-03  Kenichi Handa  <handa@m17n.org>
 
        * language/cyrillic.el: Register "microsoft-cp1251" in
index bf92e8df9cfca568f9bfdc431ab0ae33b53b21bd..fc5e7ecb8af000c189a8c1700d1a240c4d68e82e 100644 (file)
@@ -1970,7 +1970,8 @@ If INITIAL-STRING is non-nil, use that rather than \"Parent groups:\"."
                    (setq parents (cons symbol parents))))))
     (and (null (get symbol 'custom-links)) ;No links of its own.
          (= (length parents) 1)         ;A single parent.
-         (let ((links (get (car parents) 'custom-links)))
+         (let* ((links (get (car parents) 'custom-links))
+                (many (> (length links) 2)))
            (when links
              (insert "\nParent documentation: ")
              (while links
index 4c70334e908d6576e9b7069e13e36b497dc3c8ed..63a254d1d671a5404cff6808ddface2a6a4bede4 100644 (file)
@@ -1267,6 +1267,42 @@ Optional EVENT is the event that triggered the action."
            found (widget-apply child :validate)))
     found))
 
+(defun widget-child-value-get (widget)
+  "Get the value of the first member of :children in WIDGET."
+  (widget-value (car (widget-get widget :children))))
+
+(defun widget-child-value-inline (widget)
+  "Get the inline value of the first member of :children in WIDGET."
+  (widget-apply (car (widget-get widget :children)) :value-inline))
+
+(defun widget-child-validate (widget)
+  "The result of validating the first member of :children in WIDGET."
+  (widget-apply (car (widget-get widget :children)) :validate))
+
+(defun widget-type-value-create (widget)
+  "Convert and instantiate the value of the :type attribute of WIDGET.
+Store the newly created widget in the :children attribute.
+
+The value of the :type attribute should be an unconverted widget type."
+  (let ((value (widget-get widget :value))
+       (type (widget-get widget :type)))
+    (widget-put widget :children 
+                (list (widget-create-child-value widget 
+                                                 (widget-convert type)
+                                                 value)))))
+
+(defun widget-type-default-get (widget)
+  "Get default value from the :type attribute of WIDGET.
+
+The value of the :type attribute should be an unconverted widget type."
+  (widget-default-get (widget-convert (widget-get widget :type))))
+
+(defun widget-type-match (widget value)
+  "Non-nil if the :type value of WIDGET matches VALUE.
+
+The value of the :type attribute should be an unconverted widget type."
+  (widget-apply (widget-convert (widget-get widget :type)) :match value))
+
 (defun widget-types-copy (widget)
   "Copy :args as widget types in WIDGET."
   (widget-put widget :args (mapcar 'widget-copy (widget-get widget :args)))
@@ -1862,9 +1898,8 @@ the earlier input."
   :tag "choice"
   :void '(item :format "invalid (%t)\n")
   :value-create 'widget-choice-value-create
-  :value-delete 'widget-children-value-delete
-  :value-get 'widget-choice-value-get
-  :value-inline 'widget-choice-value-inline
+  :value-get 'widget-child-value-get
+  :value-inline 'widget-child-value-inline
   :default-get 'widget-choice-default-get
   :mouse-down-action 'widget-choice-mouse-down-action
   :action 'widget-choice-action
@@ -1901,14 +1936,6 @@ the earlier input."
                                              widget void :value value)))
          (widget-put widget :choice void))))))
 
-(defun widget-choice-value-get (widget)
-  ;; Get value of the child widget.
-  (widget-value (car (widget-get widget :children))))
-
-(defun widget-choice-value-inline (widget)
-  ;; Get value of the child widget.
-  (widget-apply (car (widget-get widget :children)) :value-inline))
-
 (defun widget-choice-default-get (widget)
   ;; Get default for the first choice.
   (widget-default-get (car (widget-get widget :args))))
@@ -2099,7 +2126,6 @@ when he invoked the menu."
   :entry-format "%b %v"
   :greedy nil
   :value-create 'widget-checklist-value-create
-  :value-delete 'widget-children-value-delete
   :value-get 'widget-checklist-value-get
   :validate 'widget-checklist-validate
   :match 'widget-checklist-match
@@ -2276,7 +2302,6 @@ Return an alist of (TYPE MATCH)."
   :format "%v"
   :entry-format "%b %v"
   :value-create 'widget-radio-value-create
-  :value-delete 'widget-children-value-delete
   :value-get 'widget-radio-value-get
   :value-inline 'widget-radio-value-inline
   :value-set 'widget-radio-value-set
@@ -2466,7 +2491,6 @@ Return an alist of (TYPE MATCH)."
   :format-handler 'widget-editable-list-format-handler
   :entry-format "%i %d %v"
   :value-create 'widget-editable-list-value-create
-  :value-delete 'widget-children-value-delete
   :value-get 'widget-editable-list-value-get
   :validate 'widget-children-validate
   :match 'widget-editable-list-match
@@ -2637,7 +2661,6 @@ Return an alist of (TYPE MATCH)."
   :copy 'widget-types-copy
   :format "%v"
   :value-create 'widget-group-value-create
-  :value-delete 'widget-children-value-delete
   :value-get 'widget-editable-list-value-get
   :default-get 'widget-group-default-get
   :validate 'widget-children-validate
@@ -2803,7 +2826,6 @@ link for that string."
   "A documentation string."
   :format "%v"
   :action 'widget-documentation-string-action
-  :value-delete 'widget-children-value-delete
   :value-create 'widget-documentation-string-value-create)
 
 (defun widget-documentation-string-value-create (widget)
@@ -3250,6 +3272,62 @@ To use this type, you must define :match or :match-alternatives."
        (widget-group-match widget
                           (widget-apply widget :value-to-internal value))))
 \f
+;;; The `lazy' Widget.
+;;
+;; Recursive datatypes.
+
+(define-widget 'lazy 'default
+  "Base widget for recursive datastructures.
+
+The `lazy' widget will, when instantiated, contain a single inferior
+widget, of the widget type specified by the :type parameter.  The
+value of the `lazy' widget is the same as the value of the inferior
+widget.  When deriving a new widget from the 'lazy' widget, the :type
+parameter is allowed to refer to the widget currently being defined,
+thus allowing recursive datastructures to be described.
+
+The :type parameter takes the same arguments as the defcustom
+parameter with the same name.
+
+Most composite widgets, i.e. widgets containing other widgets, does
+not allow recursion.  That is, when you define a new widget type, none
+of the inferior widgets may be of the same type you are currently
+defining.
+
+In Lisp, however, it is custom to define datastructures in terms of
+themselves.  A list, for example, is defined as either nil, or a cons
+cell whose cdr itself is a list.  The obvious way to translate this
+into a widget type would be
+
+  (define-widget 'my-list 'choice
+    \"A list of sexps.\"
+    :tag \"Sexp list\"
+    :args '((const nil) (cons :value (nil) sexp my-list)))
+
+Here we attempt to define my-list as a choice of either the constant
+nil, or a cons-cell containing a sexp and my-lisp.  This will not work
+because the `choice' widget does not allow recursion.
+
+Using the `lazy' widget you can overcome this problem, as in this 
+example: 
+
+  (define-widget 'sexp-list 'lazy
+    \"A list of sexps.\"
+    :tag \"Sexp list\"
+    :type '(choice (const nil) (cons :value (nil) sexp sexp-list)))"
+  :format "%{%t%}: %v"
+  ;; We don't convert :type because we want to allow recursive
+  ;; datastructures.  This is slow, so we should not create speed
+  ;; critical widgets by deriving from this. 
+  :convert-widget 'widget-value-convert-widget
+  :value-create 'widget-type-value-create
+  :value-get 'widget-child-value-get
+  :value-inline 'widget-child-value-inline
+  :default-get 'widget-type-default-get
+  :match 'widget-type-match
+  :validate 'widget-child-validate)
+
+\f
 ;;; The `plist' Widget.
 ;;
 ;; Property lists.
index 8621cb65662ea210d580699e05490ca62a8365d3..90600f410b7b198256595e986c48dd9c55ab4985 100644 (file)
@@ -1,6 +1,6 @@
 @c -*-texinfo-*-
 @c This is part of the GNU Emacs Lisp Reference Manual.
-@c Copyright (C) 1997, 1998, 1999, 2000, 2002 Free Software Foundation, Inc.
+@c Copyright (C) 1997, 1998, 1999, 2000, 2002, 2003 Free Software Foundation, Inc.
 @c See the file elisp.texi for copying conditions.
 @setfilename ../info/customize
 @node Customization, Loading, Macros, Top
@@ -373,6 +373,7 @@ equivalent to @code{(string)}.
 * Composite Types::
 * Splicing into Lists::
 * Type Keywords::
+* Defining New Types::
 @end menu
 
 All customization types are implemented as widgets; see @ref{Top, ,
@@ -1056,6 +1057,76 @@ arguments, which will be used when creating the @code{radio-button} or
 @end ignore
 @end table
 
+@node Defining New Types
+@subsection Defining New Types
+
+In the previous sections we have described how to construct elaborate
+type specifications for @code{defcustom}.  In some cases you may want to
+give such a type specification a name.  The obvious case is when you are
+using the same type for many user options, rather than repeat the
+specification for each option, you can give the type specification a
+name once, and use that name each @code{defcustom}.  The other case is
+when a user option accept a recursive datastructure.  To make it
+possible for a datatype to refer to itself, it needs to have a name.
+
+Since custom types are implemented as widgets, the way to define a new
+customize type is to define a new widget.  We are not going to describe
+the widget interface here in details, see @ref{Top, , Introduction,
+widget, The Emacs Widget Library}, for that.  Instead we are going to
+demonstrate the minimal functionality needed for defining new customize
+types by a simple example.
+
+@example
+(define-widget 'binary-tree-of-string 'lazy
+  "A binary tree made of cons-cells and strings."
+  :offset 4
+  :tag "Node"
+  :type '(choice (string :tag "Leaf" :value "")
+                 (cons :tag "Interior"
+                       :value ("" . "") 
+                       binary-tree-of-string
+                       binary-tree-of-string)))
+
+(defcustom foo-bar ""
+  "Sample variable holding a binary tree of strings."
+  :type 'binary-tree-of-string)
+@end example
+
+The function to define a new widget is name @code{define-widget}.  The
+first argument is the symbol we want to make a new widget type.  The
+second argument is a symbol representing an existing widget, the new
+widget is going to be defined in terms of difference from the existing
+widget.  For the purpose of defining new customization types, the
+@code{lazy} widget is perfect, because it accept a @code{:type} keyword
+argument with the same syntax as the keyword argument to
+@code{defcustom} with the same name.  The third argument is a
+documentation string for the new widget.  You will be able to see that
+string with the @kbd{M-x widget-browse @key{ret} binary-tree-of-string
+@key{ret}} command.  
+
+After these mandatory arguments follows the keyword arguments.  The most
+important is @code{:type}, which describes the datatype we want to match
+with this widget.  Here a @code{binary-tree-of-string} is described as
+being either a string, or a cons-cell whose car and cdr are themselves
+both @code{binary-tree-of-string}.  Note the reference to the widget
+type we are currently in the process of defining.  The @code{:tag}
+attribute is a string to name the widget in the user interface, and the
+@code{:offset} argument are there to ensure that child nodes are
+indented four spaces relatively to the parent node, making the tree
+structure apparent in the customization buffer.
+
+The @code{defcustom} shows how the new widget can be used as an ordinary
+customization type. 
+
+If you wonder about the name @code{lazy}, know that the other composite
+widgets convert their inferior widgets to internal form when the widget
+is instantiated in a buffer.  This conversion is recursive, so the
+inferior widgets will convert @emph{their} inferior widgets.  If the
+datastructure is itself recursive, this conversion will go on forever,
+or at least until Emacs run out of stack space.  The @code{lazy} widget
+stop this recursion, it will only convert its @code{:type} argument when
+needed.
+
 @ignore
    arch-tag: d1b8fad3-f48c-4ce4-a402-f73b5ef19bd2
 @end ignore