1 ;;; jde-tree-widget.el --- Tree widget
3 ;; Copyright (C) 2001, 2004 by David Ponce
5 ;; Author: David Ponce <david@dponce.com>
6 ;; Maintainer: David Ponce <david@dponce.com>
7 ;; Created: 16 Feb 2001
9 ;; Keywords: extensions
10 ;; VC: $Id: jde-tree-widget.el,v 1.3 2007-12-01 14:30:33 michaels Exp $
12 ;; This file is not part of Emacs
14 ;; This program is free software; you can redistribute it and/or
15 ;; modify it under the terms of the GNU General Public License as
16 ;; published by the Free Software Foundation; either version 2, or (at
17 ;; your option) any later version.
19 ;; This program is distributed in the hope that it will be useful, but
20 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 ;; General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with this program; see the file COPYING. If not, write to
26 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27 ;; Boston, MA 02111-1307, USA.
31 ;; This library provide a `tree-widget' useful to display data
32 ;; structures organized in hierarchical order.
34 ;; The following `tree-widget' extra properties are recognized:
37 ;; Set to non-nil to unfold the tree. By default the tree is
41 ;; The widget used for the tree node. By default this is an
42 ;; `item' widget which displays the tree :tag property value if
43 ;; defined or a string representation of the tree value using the
44 ;; function `widget-princ-to-string'.
47 ;; Specify a list of extra properties to keep when the tree is
48 ;; folded so they can be recovered when the tree is unfolded.
49 ;; This property is also honoured in `tree-widget' children.
52 ;; Specify a function to be called when the tree is unfolded.
53 ;; This function will receives the tree widget as its argument
54 ;; and must return a list of children widget definitions. Thus
55 ;; dynamlically providing the tree children in response to an
56 ;; unfold request. The list of children definitions is kept in
57 ;; the tree :args property and the :dynargs function can just
58 ;; return its value when unfolding the tree again. To force a
59 ;; new evaluation of the tree content just set its :args property
60 ;; to nil and redraw the node.
63 ;; Specify if this tree has children. This property has meaning
64 ;; only when used with the above :dynargs one. It indicates that
65 ;; children widget exist but will be provided when unfolding the
68 ;; :no-leaf-handle (default "*---- ")
69 ;; :close-handle (default "-- ")
70 ;; :no-guide (default " ")
71 ;; :open-handle (default "-, ")
72 ;; :guide (default " | ")
73 ;; :leaf-handle (default " |--- ")
74 ;; :last-leaf-handle (default " `--- ")
75 ;; These properties define the strings used to draw the tree
76 ;; like the following:
78 ;; *---- N0 :no-leaf-handle + node
80 ;; [-]-, N0 node-handle + :open-handle + node
81 ;; |--- N1 :no-guide + :leaf-handle + node
82 ;; [-]-, N2 :no-guide + node-handle + :open-handle + node
83 ;; | |--- N21 :no-guide + :guide + :leaf-handle + node
84 ;; | `--- N22 :no-guide + :guide + :last-leaf-handle + node
85 ;; [+]-- N3 :no-guide + node-handle + :close-handle + node
87 ;; About leaf node format
88 ;; To correctly draw the tree, that is insert the current leaf node
89 ;; prefix, leaf node widgets should prepend the "%p" escape to the
90 ;; value of the :format property. And set the :format-handler
91 ;; property to `tree-widget-format-handler'. Something like this:
93 ;; (define-widget 'leaf-node 'item
95 ;; :format-handler #'tree-widget-format-handler)
97 ;; Basic examples of `tree-widget' usage are provided in this file
98 ;; (see commands `tree-widget-example-1' and `tree-widget-example-2').
99 ;; A more sophisticated example is provided in the dir-tree.el
104 ;; Put this file on your Emacs-Lisp load path and add following into
105 ;; your ~/.emacs startup file
107 ;; (require 'tree-widget)
111 ;; This program is available at <http://www.dponce.com/>. Any
112 ;; comments, suggestions, bug reports or upgrade requests are welcome.
113 ;; Please send them to David Ponce <david@dponce.com>.
117 ;; $Log: jde-tree-widget.el,v $
118 ;; Revision 1.3 2007-12-01 14:30:33 michaels
119 ;; 2007-12-01 Mike Sperber <mike@xemacs.org>
121 ;; * jde-tree-widget.el (jde-tree-widget): Add another provide for
122 ;; jde-tree-widget, so regular require forms outside of this package
125 ;; Revision 1.2 2007/11/30 07:16:45 michaels
126 ;; 2007-11-30 Mike Sperber <mike@xemacs.org>
128 ;; * jde-tree-widget.el (jde-tree-widget): Fix typo in provide.
130 ;; Revision 1.1 2007/11/26 15:16:49 michaels
131 ;; Update jde to author version 2.3.5.1.
133 ;; Revision 1.1 2004/06/03 02:17:03 paulk
136 ;; Revision 1.4 2001/11/27 22:13:47 jslopez
137 ;; Adding David's change log entry.
139 ;; Revision 1.3 2001/11/27 20:56:25 jslopez
140 ;; Updates to the latest version. David added a hook
141 ;; to be able to persist the JDEBug local variables tree.
142 ;; (tree-widget-after-toggle-functions): New variable. Hooks run
143 ;; after toggling a `tree-widget' folding.
144 ;; (tree-widget-toggle-folding): Run above hooks. Updated doc
146 ;; Added pagination. Minor comment changes.
148 ;; Revision 1.2 2001/10/26 11:20:38 jslopez
149 ;; Removing control characters.
151 ;; Revision 1.1 2001/10/26 06:45:57 paulk
154 ;; Revision 1.5 2001/05/11 23:11:18 ponce
155 ;; Updated version to 1.0.5.
157 ;; Revision 1.4 2001/05/11 23:02:14 ponce
158 ;; (tree-widget-value-create): Fixed a bug when the dynamic tree :dynargs
159 ;; function returns nil (no children).
161 ;; Revision 1.3 2001/03/16 14:23:15 ponce
162 ;; (tree-widget-example-1): removed unused free variable
163 ;; `tree-widget-sample'.
165 ;; Revision 1.2 2001/03/16 14:15:09 ponce
166 ;; (tree-widget-children-value-save): use `tree-widget-node' to get the
167 ;; :node value of widgets. Check node and node-child values before
168 ;; saving properties.
170 ;; (tree-widget-button-keymap): new variable. Keymap used inside node
173 ;; (tree-widget-node-handle): use `tree-widget-button-keymap'.
175 ;; (tree-widget-map): new utility function.
177 ;; Revision 1.1 2001/02/19 22:51:23 ponce
187 (defgroup tree-widget nil
188 "Customization support for the Tree Widget Library."
191 (defcustom tree-widget-node-handle-widget 'tree-widget-node-handle
192 "Widget type used for tree node handle."
196 (defun tree-widget-get-super (widget property)
197 "Return WIDGET super class PROPERTY value."
200 (get (widget-type widget) 'widget-type))
204 (defun tree-widget-p (widget)
205 "Return non-nil if WIDGET inherits from a 'tree-widget' widget."
206 (let ((type (widget-type widget)))
207 (while (and type (not (eq type 'tree-widget)))
208 (setq type (widget-type (get type 'widget-type))))
209 (eq type 'tree-widget)))
211 (defun tree-widget-keep (arg widget)
212 "Save in ARG the WIDGET properties specified by :keep."
213 (let ((plist (widget-get widget :keep))
216 (setq prop (car plist)
218 (widget-put arg prop (widget-get widget prop)))))
220 (defun tree-widget-node (widget)
221 "Return the tree WIDGET :node value.
222 If not found setup a default 'item' widget."
223 (or (widget-get widget :node)
224 ;; Take care of actually return the :node property value.
225 ;; Because FSF Emacs `widget-put' returns the property value and
226 ;; XEmacs one returns the widget value!!! So don't use thing
228 ;; (or (widget-get widget :node)
229 ;; (widget-put widget :node node))
230 (let ((node `(item :tag ,(or (widget-get widget :tag)
231 (widget-princ-to-string
232 (widget-value widget))))))
233 (widget-put widget :node node)
236 (defun tree-widget-children-value-save (widget &optional args node)
237 "Save WIDGET children values.
238 Children properties and values are saved in ARGS if non-nil else in
239 WIDGET :args property value. Data node properties and value are saved
240 in NODE if non-nil else in WIDGET :node property value."
241 (let ((args (or args (widget-get widget :args)))
242 (node (or node (tree-widget-node widget)))
243 (children (widget-get widget :children))
244 (node-child (widget-get widget :tree-widget-node))
246 (while (and args children)
250 children (cdr children))
253 ;; The child is a tree node.
254 ((tree-widget-p child)
256 ;; Backtrack :args and :node properties.
257 (widget-put arg :args (widget-get child :args))
258 (widget-put arg :node (tree-widget-node child))
260 ;; Save :open property.
261 (widget-put arg :open (widget-get child :open))
264 (if (widget-get child :open)
266 ;; Save the widget value.
267 (widget-put arg :value (widget-value child))
268 ;; Save properties specified in :keep.
269 (tree-widget-keep arg child)
271 (tree-widget-children-value-save
273 (widget-get arg :args)
274 (widget-get arg :node)))))
276 ;; Another non tree node.
278 ;; Save the widget value
279 (widget-put arg :value (widget-value child))
280 ;; Save properties specified in :keep.
281 (tree-widget-keep arg child))))
283 (cond ((and node node-child)
284 ;; Assume that the node child widget is not a tree!
285 ;; Save the node child widget value.
286 (widget-put node :value (widget-value node-child))
287 ;; Save the node child properties specified in :keep.
288 (tree-widget-keep node node-child)))))
290 (defvar tree-widget-after-toggle-functions nil
291 "Hooks run after toggling a `tree-widget' folding.
292 Each function will receive the `tree-widget' as its unique argument.
293 This variable should be local to each buffer used to display
296 (defun tree-widget-toggle-folding (widget &rest ignore)
297 "Toggle a `tree-widget' folding.
298 WIDGET is a `tree-widget-node-handle-widget' and its parent the
299 `tree-widget' itself. IGNORE other arguments."
300 (let ((parent (widget-get widget :parent))
301 (open (widget-value widget)))
303 ;; Before folding the node up, save children values so next
304 ;; open can recover them.
305 (tree-widget-children-value-save parent))
306 (widget-put parent :open (not open))
307 (widget-value-set parent (not open))
308 (run-hook-with-args 'tree-widget-after-toggle-functions parent)))
310 (defvar tree-widget-button-keymap
311 (let (parent-keymap mouse-button1 keymap)
312 (if (featurep 'xemacs)
313 (setq parent-keymap widget-button-keymap
314 mouse-button1 [button1])
315 (setq parent-keymap widget-keymap
316 mouse-button1 [down-mouse-1]))
317 (setq keymap (copy-keymap parent-keymap))
318 (define-key keymap mouse-button1 #'widget-button-click)
320 "Keymap used inside node handle buttons.")
322 (define-widget 'tree-widget-node-handle 'toggle
323 "Tree node handle widget."
324 :button-keymap tree-widget-button-keymap ; XEmacs
325 :keymap tree-widget-button-keymap ; Emacs
329 :notify #'tree-widget-toggle-folding)
331 (define-widget 'tree-widget 'default
334 :convert-widget #'widget-types-convert-widget
335 :value-get #'widget-value-value-get
336 :value-create #'tree-widget-value-create
337 :value-delete #'tree-widget-value-delete
339 ;; *---- N :no-leaf-handle + node
341 ;; [-]-, N node-handle + :open-handle + node
342 ;; |--- N1 :no-guide + :leaf-handle + node
343 ;; [-]-, N2 :no-guide + node-handle + :open-handle + node
344 ;; | |--- N21 :no-guide + :guide + :leaf-handle + node
345 ;; | `--- N22 :no-guide + :guide + :last-leaf-handle + node
346 ;; [+]-- N3 :no-guide + node-handle + :close-handle + node
348 :no-leaf-handle "*---- "
353 :leaf-handle " |--- "
354 :last-leaf-handle " `--- ")
356 (defun tree-widget-format-handler (widget escape)
357 "Convenient %p format handler to insert a leaf node prefix.
358 WIDGET is a tree leaf node and ESCAPE a format character."
361 ;; If %p format insert the leaf node prefix.
363 (if (widget-get widget :indent)
364 (insert-char ? (widget-get widget :indent)))
366 (or (widget-get widget :tree-widget-leaf-handle)
369 ;; For other ESCAPE values call the WIDGET super class format
372 (let ((handler (tree-widget-get-super widget :format-handler)))
374 (funcall handler widget escape))))))
376 (defun tree-widget-value-delete (widget)
377 "Delete tree WIDGET children."
379 (widget-children-value-delete widget)
381 (widget-delete (widget-get widget :tree-widget-node))
382 (widget-put widget :tree-widget-node nil))
384 (defun tree-widget-value-create (widget)
385 "Create the tree WIDGET children."
386 (let ((args (widget-get widget :args))
387 (open (widget-get widget :open))
388 (node (tree-widget-node widget))
389 children buttons prefix)
395 ;; Take care of dynamic tree. If :has-children is
396 ;; non-nil let a chance to open the node later. So
397 ;; don't consider it as a leaf node even if it has not
398 ;; (yet) any children.
399 (and (widget-get widget :dynargs)
400 (widget-get widget :has-children))))
402 (insert (or (widget-get widget :tree-widget-leaf-handle)
403 (widget-get widget :no-leaf-handle)))
404 (widget-put widget :tree-widget-node
405 (widget-create-child-and-convert widget node)))
410 ;; Maybe the tree is dynamic.
411 (if (widget-get widget :dynargs)
413 ;; Request the definition of children.
414 (funcall (widget-get widget :dynargs) widget)))
415 ;; Maybe reuse definition from the :args cache.
416 (or (eq args newargs)
417 ;; Otherwise setup a new :args cache.
420 (setq args (mapcar #'widget-convert newargs))))))
423 (cons (widget-create-child-and-convert
424 widget tree-widget-node-handle-widget
425 :value nil :help-echo "Hide node")
427 (insert (widget-get widget (if args
430 (widget-put widget :tree-widget-node
431 (widget-create-child-and-convert widget node))
433 (concat (or (widget-get widget :tree-widget-prefix) "")
434 (or (widget-get widget :tree-widget-guide)
435 (widget-get widget :no-guide))))
441 (cons (widget-create-child-and-convert
443 :tree-widget-prefix prefix
444 :tree-widget-guide (widget-get widget :guide)
445 :tree-widget-leaf-handle
446 (widget-get widget :leaf-handle))
449 ;; The last non tree child uses the :last-leaf-handle.
452 (cons (widget-create-child-and-convert
454 :tree-widget-prefix prefix
455 :tree-widget-leaf-handle
456 (widget-get widget :last-leaf-handle))
464 (widget-create-child-and-convert
465 widget tree-widget-node-handle-widget
466 :value t :help-echo "Show node")
468 (insert (widget-get widget :close-handle))
469 (widget-put widget :tree-widget-node
470 (widget-create-child-and-convert widget node))))
472 (widget-put widget :children (nreverse children))
473 (widget-put widget :buttons buttons)))
479 (defun tree-widget-map (widget fun)
480 "For each WIDGET displayed child call function FUN.
481 FUN is called with three arguments like this:
483 (FUN CHILD IS-NODE WIDGET)
486 - - CHILD is the child widget.
487 - - IS-NODE is non-nil if CHILD is WIDGET node widget."
488 (if (widget-get widget :tree-widget-node)
489 (let ((children (widget-get widget :children))
491 (funcall fun (widget-get widget :tree-widget-node)
494 (setq child (car children)
495 children (cdr children))
496 (if (tree-widget-p child)
497 ;; The child is a tree node.
498 (tree-widget-map child fun)
499 ;; Another non tree node.
500 (funcall fun child nil widget))))))
508 (cond ((featurep 'xemacs)
510 (defalias 'tree-widget-sample-overlay-lists
511 (lambda () (list (extent-list))))
512 (defalias 'tree-widget-sample-delete-overlay 'delete-extent))
516 (defalias 'tree-widget-sample-overlay-lists 'overlay-lists)
517 (defalias 'tree-widget-sample-delete-overlay 'delete-overlay)))
519 (defun tree-widget-example-1 ()
520 "A simple usage of the `tree-widget'."
522 (switch-to-buffer "*`tree-widget' example 1*")
523 (kill-all-local-variables)
524 (let ((inhibit-read-only t))
526 (let ((all (tree-widget-sample-overlay-lists)))
527 (mapcar #'tree-widget-sample-delete-overlay (car all))
528 (mapcar #'tree-widget-sample-delete-overlay (cdr all)))
530 (widget-insert (format "%s. \n\n" (buffer-name)))
535 ;; Use a push button for this node.
540 (lambda (&rest ignore)
541 (message "This is the Root node")))
542 ;; Add subtrees (their nodes defaut to items).
543 '(tree-widget :tag "Child-1")
544 '(tree-widget :tag "Child-2"
545 (tree-widget :tag "Child-2.1")
546 (tree-widget :tag "Child-2.2"
547 (tree-widget :tag "Child-2.2.1")
548 (tree-widget :tag "Child-2.2.2")))
549 '(tree-widget :tag "Child-3"
550 (tree-widget :tag "Child-3.1")
551 (tree-widget :tag "Child-3.2")))
553 (use-local-map widget-keymap)
556 (defun tree-widget-example-2-dynargs (widget)
557 "Return the children definitions of WIDGET.
558 Reuse the cached :args property value if exists."
559 (or (widget-get widget :args)
560 '((tree-widget :tag "Child-2.1")
561 (tree-widget :tag "Child-2.2"
562 (tree-widget :tag "Child-2.2.1")
563 (tree-widget :tag "Child-2.2.2")))))
565 (defun tree-widget-example-2 ()
566 "A simple usage of the `tree-widget' with dynamic expansion."
568 (switch-to-buffer "*`tree-widget' example 2*")
569 (kill-all-local-variables)
570 (let ((inhibit-read-only t))
572 (let ((all (tree-widget-sample-overlay-lists)))
573 (mapcar #'tree-widget-sample-delete-overlay (car all))
574 (mapcar #'tree-widget-sample-delete-overlay (cdr all)))
576 (widget-insert (format "%s. \n\n" (buffer-name)))
581 ;; Use a push button for this node.
586 (lambda (&rest ignore)
587 (message "This is the Root node")))
588 ;; Add subtrees (their nodes defaut to items).
589 '(tree-widget :tag "Child-1")
590 ;; Dynamically retrieve children of this node.
591 '(tree-widget :tag "Child-2"
592 :dynargs tree-widget-example-2-dynargs
594 '(tree-widget :tag "Child-3"
595 (tree-widget :tag "Child-3.1")
596 (tree-widget :tag "Child-3.2")))
598 (use-local-map widget-keymap)
601 (provide 'jde-tree-widget)
602 (provide 'tree-widget)
604 ;;; jde-tree-widget.el ends here