+\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.
+
+(define-widget 'plist 'list
+ "A property list."
+ :key-type '(symbol :tag "Key")
+ :value-type '(sexp :tag "Value")
+ :convert-widget 'widget-plist-convert-widget
+ :tag "Plist")
+
+(defvar widget-plist-value-type) ;Dynamic variable
+
+(defun widget-plist-convert-widget (widget)
+ ;; Handle `:options'.
+ (let* ((options (widget-get widget :options))
+ (widget-plist-value-type (widget-get widget :value-type))
+ (other `(editable-list :inline t
+ (group :inline t
+ ,(widget-get widget :key-type)
+ ,widget-plist-value-type)))
+ (args (if options
+ (list `(checklist :inline t
+ :greedy t
+ ,@(mapcar 'widget-plist-convert-option
+ options))
+ other)
+ (list other))))
+ (widget-put widget :args args)
+ widget))
+
+(defun widget-plist-convert-option (option)
+ ;; Convert a single plist option.
+ (let (key-type value-type)
+ (if (listp option)
+ (let ((key (nth 0 option)))
+ (setq value-type (nth 1 option))
+ (if (listp key)
+ (setq key-type key)
+ (setq key-type `(const ,key))))
+ (setq key-type `(const ,option)
+ value-type widget-plist-value-type))
+ `(group :format "Key: %v" :inline t ,key-type ,value-type)))
+
+
+;;; The `alist' Widget.
+;;
+;; Association lists.
+
+(define-widget 'alist 'list
+ "An association list."
+ :key-type '(sexp :tag "Key")
+ :value-type '(sexp :tag "Value")
+ :convert-widget 'widget-alist-convert-widget
+ :tag "Alist")
+
+(defvar widget-alist-value-type) ;Dynamic variable
+
+(defun widget-alist-convert-widget (widget)
+ ;; Handle `:options'.
+ (let* ((options (widget-get widget :options))
+ (widget-alist-value-type (widget-get widget :value-type))
+ (other `(editable-list :inline t
+ (cons :format "%v"
+ ,(widget-get widget :key-type)
+ ,widget-alist-value-type)))
+ (args (if options
+ (list `(checklist :inline t
+ :greedy t
+ ,@(mapcar 'widget-alist-convert-option
+ options))
+ other)
+ (list other))))
+ (widget-put widget :args args)
+ widget))