changeset 53319:36b31fc002f2

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.
author Per Abrahamsen <abraham@dina.kvl.dk>
date Sat, 27 Dec 2003 16:41:13 +0000
parents d7e333de59f9
children 15f16532d823
files lisp/ChangeLog lisp/cus-edit.el lisp/wid-edit.el lispref/customize.texi
diffstat 4 files changed, 185 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Sat Dec 27 08:18:42 2003 +0000
+++ b/lisp/ChangeLog	Sat Dec 27 16:41:13 2003 +0000
@@ -76,6 +76,23 @@
 	* 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
--- a/lisp/cus-edit.el	Sat Dec 27 08:18:42 2003 +0000
+++ b/lisp/cus-edit.el	Sat Dec 27 16:41:13 2003 +0000
@@ -1970,7 +1970,8 @@
 		    (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
--- a/lisp/wid-edit.el	Sat Dec 27 08:18:42 2003 +0000
+++ b/lisp/wid-edit.el	Sat Dec 27 16:41:13 2003 +0000
@@ -1267,6 +1267,42 @@
 	    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 @@
   :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 @@
 					      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 @@
   :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 @@
   :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 @@
   :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 @@
   :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 @@
   "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 @@
        (widget-group-match widget
 			   (widget-apply widget :value-to-internal value))))
 
+;;; 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)
+
+
 ;;; The `plist' Widget.
 ;;
 ;; Property lists.
--- a/lispref/customize.texi	Sat Dec 27 08:18:42 2003 +0000
+++ b/lispref/customize.texi	Sat Dec 27 16:41:13 2003 +0000
@@ -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 @@
 * 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 @@
 @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