Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /home/zhenxiangba/zhenxiangba.com/public_html/phproxy-improved-master/index.php on line 456
;;; tree-widget.el --- Tree widget
;; Copyright (C) 2001 by David Ponce
;; Author: David Ponce
;; Maintainer: David Ponce
;; Created: 16 Feb 2001
;; Version: 1.0.5
;; Keywords: extensions
;; VC: $Id: tree-widget.el,v 1.4 2001/11/27 22:13:47 jslopez Exp $
;; This file is not part of Emacs
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or (at
;; your option) any later version.
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;
;; This library provide a `tree-widget' useful to display data
;; structures organized in hierarchical order.
;;
;; The following `tree-widget' extra properties are recognized:
;;
;; :open
;; Set to non-nil to unfold the tree. By default the tree is
;; folded.
;;
;; :node
;; The widget used for the tree node. By default this is an
;; `item' widget which displays the tree :tag property value if
;; defined or a string representation of the tree value using the
;; function `widget-princ-to-string'.
;;
;; :keep
;; Specify a list of extra properties to keep when the tree is
;; folded so they can be recovered when the tree is unfolded.
;; This property is also honoured in `tree-widget' children.
;;
;; :dynargs
;; Specify a function to be called when the tree is unfolded.
;; This function will receives the tree widget as its argument
;; and must return a list of children widget definitions. Thus
;; dynamlically providing the tree children in response to an
;; unfold request. The list of children definitions is kept in
;; the tree :args property and the :dynargs function can just
;; return its value when unfolding the tree again. To force a
;; new evaluation of the tree content just set its :args property
;; to nil and redraw the node.
;;
;; :has-children
;; Specify if this tree has children. This property has meaning
;; only when used with the above :dynargs one. It indicates that
;; children widget exist but will be provided when unfolding the
;; node.
;;
;; :no-leaf-handle (default "*---- ")
;; :close-handle (default "-- ")
;; :no-guide (default " ")
;; :open-handle (default "-, ")
;; :guide (default " | ")
;; :leaf-handle (default " |--- ")
;; :last-leaf-handle (default " `--- ")
;; These properties define the strings used to draw the tree
;; like the following:
;;
;; *---- N0 :no-leaf-handle + node
;;
;; [-]-, N0 node-handle + :open-handle + node
;; |--- N1 :no-guide + :leaf-handle + node
;; [-]-, N2 :no-guide + node-handle + :open-handle + node
;; | |--- N21 :no-guide + :guide + :leaf-handle + node
;; | `--- N22 :no-guide + :guide + :last-leaf-handle + node
;; [+]-- N3 :no-guide + node-handle + :close-handle + node
;;
;; About leaf node format
;; To correctly draw the tree, that is insert the current leaf node
;; prefix, leaf node widgets should prepend the "%p" escape to the
;; value of the :format property. And set the :format-handler
;; property to `tree-widget-format-handler'. Something like this:
;;
;; (define-widget 'leaf-node 'item
;; :format "%p%t\n"
;; :format-handler #'tree-widget-format-handler)
;;
;; Basic examples of `tree-widget' usage are provided in this file
;; (see commands `tree-widget-example-1' and `tree-widget-example-2').
;; A more sophisticated example is provided in the dir-tree.el
;; source.
;;
;; Installation
;; Put this file on your Emacs-Lisp load path and add following into
;; your ~/.emacs startup file
;;
;; (require 'tree-widget)
;; Support
;;
;; This program is available at . Any
;; comments, suggestions, bug reports or upgrade requests are welcome.
;; Please send them to David Ponce .
;;; History:
;;
;; $Log: tree-widget.el,v $
;; Revision 1.4 2001/11/27 22:13:47 jslopez
;; Adding David's change log entry.
;;
;; Revision 1.3 2001/11/27 20:56:25 jslopez
;; Updates to the latest version. David added a hook
;; to be able to persist the JDEBug local variables tree.
;; (tree-widget-after-toggle-functions): New variable. Hooks run
;; after toggling a `tree-widget' folding.
;; (tree-widget-toggle-folding): Run above hooks. Updated doc
;; string.
;; Added pagination. Minor comment changes.
;;
;; Revision 1.2 2001/10/26 11:20:38 jslopez
;; Removing control characters.
;;
;; Revision 1.1 2001/10/26 06:45:57 paulk
;; Initial revision.
;;
;; Revision 1.5 2001/05/11 23:11:18 ponce
;; Updated version to 1.0.5.
;;
;; Revision 1.4 2001/05/11 23:02:14 ponce
;; (tree-widget-value-create): Fixed a bug when the dynamic tree :dynargs
;; function returns nil (no children).
;;
;; Revision 1.3 2001/03/16 14:23:15 ponce
;; (tree-widget-example-1): removed unused free variable
;; `tree-widget-sample'.
;;
;; Revision 1.2 2001/03/16 14:15:09 ponce
;; (tree-widget-children-value-save): use `tree-widget-node' to get the
;; :node value of widgets. Check node and node-child values before
;; saving properties.
;;
;; (tree-widget-button-keymap): new variable. Keymap used inside node
;; handle buttons.
;;
;; (tree-widget-node-handle): use `tree-widget-button-keymap'.
;;
;; (tree-widget-map): new utility function.
;;
;; Revision 1.1 2001/02/19 22:51:23 ponce
;; Initial revision.
;;
;;; Code:
(require 'wid-edit)
;;; Customization.
(defgroup tree-widget nil
"Customization support for the Tree Widget Library."
:group 'widgets)
(defcustom tree-widget-node-handle-widget 'tree-widget-node-handle
"Widget type used for tree node handle."
:type 'symbol
:group 'tree-widget)
(defun tree-widget-get-super (widget property)
"Return WIDGET super class PROPERTY value."
(widget-get
(get (widget-type
(get (widget-type widget) 'widget-type))
'widget-type)
property))
(defun tree-widget-p (widget)
"Return non-nil if WIDGET inherits from a 'tree-widget' widget."
(let ((type (widget-type widget)))
(while (and type (not (eq type 'tree-widget)))
(setq type (widget-type (get type 'widget-type))))
(eq type 'tree-widget)))
(defun tree-widget-keep (arg widget)
"Save in ARG the WIDGET properties specified by :keep."
(let ((plist (widget-get widget :keep))
prop)
(while plist
(setq prop (car plist)
plist (cdr plist))
(widget-put arg prop (widget-get widget prop)))))
(defun tree-widget-node (widget)
"Return the tree WIDGET :node value.
If not found setup a default 'item' widget."
(or (widget-get widget :node)
;; Take care of actually return the :node property value.
;; Because FSF Emacs `widget-put' returns the property value and
;; XEmacs one returns the widget value!!! So don't use thing
;; like this ;-)
;; (or (widget-get widget :node)
;; (widget-put widget :node node))
(let ((node `(item :tag ,(or (widget-get widget :tag)
(widget-princ-to-string
(widget-value widget))))))
(widget-put widget :node node)
node)))
(defun tree-widget-children-value-save (widget &optional args node)
"Save WIDGET children values.
Children properties and values are saved in ARGS if non-nil else in
WIDGET :args property value. Data node properties and value are saved
in NODE if non-nil else in WIDGET :node property value."
(let ((args (or args (widget-get widget :args)))
(node (or node (tree-widget-node widget)))
(children (widget-get widget :children))
(node-child (widget-get widget :tree-widget-node))
arg child)
(while (and args children)
(setq arg (car args)
args (cdr args)
child (car children)
children (cdr children))
(cond
;; The child is a tree node.
((tree-widget-p child)
;; Backtrack :args and :node properties.
(widget-put arg :args (widget-get child :args))
(widget-put arg :node (tree-widget-node child))
;; Save :open property.
(widget-put arg :open (widget-get child :open))
;; The node is open.
(if (widget-get child :open)
(progn
;; Save the widget value.
(widget-put arg :value (widget-value child))
;; Save properties specified in :keep.
(tree-widget-keep arg child)
;; Save children.
(tree-widget-children-value-save
child
(widget-get arg :args)
(widget-get arg :node)))))
;; Another non tree node.
(t
;; Save the widget value
(widget-put arg :value (widget-value child))
;; Save properties specified in :keep.
(tree-widget-keep arg child))))
(cond ((and node node-child)
;; Assume that the node child widget is not a tree!
;; Save the node child widget value.
(widget-put node :value (widget-value node-child))
;; Save the node child properties specified in :keep.
(tree-widget-keep node node-child)))))
(defvar tree-widget-after-toggle-functions nil
"Hooks run after toggling a `tree-widget' folding.
Each function will receive the `tree-widget' as its unique argument.
This variable should be local to each buffer used to display
widgets.")
(defun tree-widget-toggle-folding (widget &rest ignore)
"Toggle a `tree-widget' folding.
WIDGET is a `tree-widget-node-handle-widget' and its parent the
`tree-widget' itself. IGNORE other arguments."
(let ((parent (widget-get widget :parent))
(open (widget-value widget)))
(if open
;; Before folding the node up, save children values so next
;; open can recover them.
(tree-widget-children-value-save parent))
(widget-put parent :open (not open))
(widget-value-set parent (not open))
(run-hook-with-args 'tree-widget-after-toggle-functions parent)))
(defvar tree-widget-button-keymap
(let (parent-keymap mouse-button1 keymap)
(if (featurep 'xemacs)
(setq parent-keymap widget-button-keymap
mouse-button1 [button1])
(setq parent-keymap widget-keymap
mouse-button1 [down-mouse-1]))
(setq keymap (copy-keymap parent-keymap))
(define-key keymap mouse-button1 #'widget-button-click)
keymap)
"Keymap used inside node handle buttons.")
(define-widget 'tree-widget-node-handle 'toggle
"Tree node handle widget."
:button-keymap tree-widget-button-keymap ; XEmacs
:keymap tree-widget-button-keymap ; Emacs
:format "%[%v%]"
:on "[+]"
:off "[-]"
:notify #'tree-widget-toggle-folding)
(define-widget 'tree-widget 'default
"Tree node widget."
:format "%v"
:convert-widget #'widget-types-convert-widget
:value-get #'widget-value-value-get
:value-create #'tree-widget-value-create
:value-delete #'tree-widget-value-delete
;; *---- N :no-leaf-handle + node
;; [-]-, N node-handle + :open-handle + node
;; |--- N1 :no-guide + :leaf-handle + node
;; [-]-, N2 :no-guide + node-handle + :open-handle + node
;; | |--- N21 :no-guide + :guide + :leaf-handle + node
;; | `--- N22 :no-guide + :guide + :last-leaf-handle + node
;; [+]-- N3 :no-guide + node-handle + :close-handle + node
:no-leaf-handle "*---- "
:close-handle "-- "
:no-guide " "
:open-handle "-, "
:guide " | "
:leaf-handle " |--- "
:last-leaf-handle " `--- ")
(defun tree-widget-format-handler (widget escape)
"Convenient %p format handler to insert a leaf node prefix.
WIDGET is a tree leaf node and ESCAPE a format character."
(cond
;; If %p format insert the leaf node prefix.
((eq escape ?p)
(if (widget-get widget :indent)
(insert-char ? (widget-get widget :indent)))
(insert
(or (widget-get widget :tree-widget-leaf-handle)
"")))
;; For other ESCAPE values call the WIDGET super class format
;; handler.
(t
(let ((handler (tree-widget-get-super widget :format-handler)))
(if handler
(funcall handler widget escape))))))
(defun tree-widget-value-delete (widget)
"Delete tree WIDGET children."
;; Delete children
(widget-children-value-delete widget)
;; Delete node child
(widget-delete (widget-get widget :tree-widget-node))
(widget-put widget :tree-widget-node nil))
(defun tree-widget-value-create (widget)
"Create the tree WIDGET children."
(let ((args (widget-get widget :args))
(open (widget-get widget :open))
(node (tree-widget-node widget))
children buttons prefix)
(cond
;; Leaf node.
((not (or args
;; Take care of dynamic tree. If :has-children is
;; non-nil let a chance to open the node later. So
;; don't consider it as a leaf node even if it has not
;; (yet) any children.
(and (widget-get widget :dynargs)
(widget-get widget :has-children))))
(insert (or (widget-get widget :tree-widget-leaf-handle)
(widget-get widget :no-leaf-handle)))
(widget-put widget :tree-widget-node
(widget-create-child-and-convert widget node)))
;; Unfolded node.
(open
;; Maybe the tree is dynamic.
(if (widget-get widget :dynargs)
(let ((newargs
;; Request the definition of children.
(funcall (widget-get widget :dynargs) widget)))
;; Maybe reuse definition from the :args cache.
(or (eq args newargs)
;; Otherwise setup a new :args cache.
(widget-put
widget :args
(setq args (mapcar #'widget-convert newargs))))))
(setq buttons
(cons (widget-create-child-and-convert
widget tree-widget-node-handle-widget
:value nil :help-echo "Hide node")
buttons))
(insert (widget-get widget (if args
:open-handle
:close-handle)))
(widget-put widget :tree-widget-node
(widget-create-child-and-convert widget node))
(setq prefix
(concat (or (widget-get widget :tree-widget-prefix) "")
(or (widget-get widget :tree-widget-guide)
(widget-get widget :no-guide))))
(if (null args)
nil
(while (cdr args)
(insert prefix)
(setq children
(cons (widget-create-child-and-convert
widget (car args)
:tree-widget-prefix prefix
:tree-widget-guide (widget-get widget :guide)
:tree-widget-leaf-handle
(widget-get widget :leaf-handle))
children)
args (cdr args)))
;; The last non tree child uses the :last-leaf-handle.
(insert prefix)
(setq children
(cons (widget-create-child-and-convert
widget (car args)
:tree-widget-prefix prefix
:tree-widget-leaf-handle
(widget-get widget :last-leaf-handle))
children))))
;; Folded node.
(t
(setq buttons
(cons
(widget-create-child-and-convert
widget tree-widget-node-handle-widget
:value t :help-echo "Show node")
buttons))
(insert (widget-get widget :close-handle))
(widget-put widget :tree-widget-node
(widget-create-child-and-convert widget node))))
(widget-put widget :children (nreverse children))
(widget-put widget :buttons buttons)))
;;;;
;;;; Utilities
;;;;
(defun tree-widget-map (widget fun)
"For each WIDGET displayed child call function FUN.
FUN is called with three arguments like this:
(FUN CHILD IS-NODE WIDGET)
where:
- - CHILD is the child widget.
- - IS-NODE is non-nil if CHILD is WIDGET node widget."
(if (widget-get widget :tree-widget-node)
(let ((children (widget-get widget :children))
child)
(funcall fun (widget-get widget :tree-widget-node)
t widget)
(while children
(setq child (car children)
children (cdr children))
(if (tree-widget-p child)
;; The child is a tree node.
(tree-widget-map child fun)
;; Another non tree node.
(funcall fun child nil widget))))))
;;;;
;;;; Samples
;;;;
;;; Compatibility
(cond ((featurep 'xemacs)
(defalias 'tree-widget-sample-overlay-lists
(lambda () (list (extent-list))))
(defalias 'tree-widget-sample-delete-overlay 'delete-extent))
(t
(defalias 'tree-widget-sample-overlay-lists 'overlay-lists)
(defalias 'tree-widget-sample-delete-overlay 'delete-overlay)))
(defun tree-widget-example-1 ()
"A simple usage of the `tree-widget'."
(interactive)
(switch-to-buffer "*`tree-widget' example 1*")
(kill-all-local-variables)
(let ((inhibit-read-only t))
(erase-buffer))
(let ((all (tree-widget-sample-overlay-lists)))
(mapcar #'tree-widget-sample-delete-overlay (car all))
(mapcar #'tree-widget-sample-delete-overlay (cdr all)))
(widget-insert (format "%s. \n\n" (buffer-name)))
(widget-create
;; Open this level.
'tree-widget :open t
;; Use a push button for this node.
:node '(push-button
:tag "Root"
:format "%[%t%]\n"
:notify
(lambda (&rest ignore)
(message "This is the Root node")))
;; Add subtrees (their nodes defaut to items).
'(tree-widget :tag "Child-1")
'(tree-widget :tag "Child-2"
(tree-widget :tag "Child-2.1")
(tree-widget :tag "Child-2.2"
(tree-widget :tag "Child-2.2.1")
(tree-widget :tag "Child-2.2.2")))
'(tree-widget :tag "Child-3"
(tree-widget :tag "Child-3.1")
(tree-widget :tag "Child-3.2")))
(use-local-map widget-keymap)
(widget-setup))
(defun tree-widget-example-2-dynargs (widget)
"Return the children definitions of WIDGET.
Reuse the cached :args property value if exists."
(or (widget-get widget :args)
'((tree-widget :tag "Child-2.1")
(tree-widget :tag "Child-2.2"
(tree-widget :tag "Child-2.2.1")
(tree-widget :tag "Child-2.2.2")))))
(defun tree-widget-example-2 ()
"A simple usage of the `tree-widget' with dynamic expansion."
(interactive)
(switch-to-buffer "*`tree-widget' example 2*")
(kill-all-local-variables)
(let ((inhibit-read-only t))
(erase-buffer))
(let ((all (tree-widget-sample-overlay-lists)))
(mapcar #'tree-widget-sample-delete-overlay (car all))
(mapcar #'tree-widget-sample-delete-overlay (cdr all)))
(widget-insert (format "%s. \n\n" (buffer-name)))
(widget-create
;; Open this level.
'tree-widget :open t
;; Use a push button for this node.
:node '(push-button
:tag "Root"
:format "%[%t%]\n"
:notify
(lambda (&rest ignore)
(message "This is the Root node")))
;; Add subtrees (their nodes defaut to items).
'(tree-widget :tag "Child-1")
;; Dynamically retrieve children of this node.
'(tree-widget :tag "Child-2"
:dynargs tree-widget-example-2-dynargs
:has-children t)
'(tree-widget :tag "Child-3"
(tree-widget :tag "Child-3.1")
(tree-widget :tag "Child-3.2")))
(use-local-map widget-keymap)
(widget-setup))
(provide 'tree-widget)
;;; tree-widget.el ends here