OSDN Git Service

Enable to track git://github.com/monaka/binutils.git
[pf3gnuchains/pf3gnuchains3x.git] / itcl / iwidgets / generic / hierarchy.itk
diff --git a/itcl/iwidgets/generic/hierarchy.itk b/itcl/iwidgets/generic/hierarchy.itk
new file mode 100644 (file)
index 0000000..3bb8f3c
--- /dev/null
@@ -0,0 +1,1983 @@
+# Hierarchy
+# ----------------------------------------------------------------------
+# Hierarchical data viewer.  Manages a list of nodes that can be
+# expanded or collapsed.  Individual nodes can be highlighted.
+# Clicking with the right mouse button on any item brings up a
+# special item menu.  Clicking on the background area brings up
+# a different popup menu.
+# ----------------------------------------------------------------------
+#   AUTHOR:  Michael J. McLennan
+#            Bell Labs Innovations for Lucent Technologies
+#            mmclennan@lucent.com
+#
+#            Mark L. Ulferts
+#            DSC Communications
+#            mulferts@austin.dsccc.com
+#
+#      RCS:  $Id$
+# ----------------------------------------------------------------------
+#                Copyright (c) 1996  Lucent Technologies
+# ======================================================================
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that the copyright notice and warranty disclaimer appear in
+# supporting documentation, and that the names of Lucent Technologies
+# any of their entities not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Lucent Technologies disclaims all warranties with regard to this
+# software, including all implied warranties of merchantability and
+# fitness.  In no event shall Lucent Technologies be liable for any
+# special, indirect or consequential damages or any damages whatsoever
+# resulting from loss of use, data or profits, whether in an action of
+# contract, negligence or other tortuous action, arising out of or in
+# connection with the use or performance of this software.
+#
+# ----------------------------------------------------------------------
+#            Copyright (c) 1996 DSC Technologies Corporation
+# ======================================================================
+# Permission to use, copy, modify, distribute and license this software 
+# and its documentation for any purpose, and without fee or written 
+# agreement with DSC, is hereby granted, provided that the above copyright 
+# notice appears in all copies and that both the copyright notice and 
+# warranty disclaimer below appear in supporting documentation, and that 
+# the names of DSC Technologies Corporation or DSC Communications 
+# Corporation not be used in advertising or publicity pertaining to the 
+# software without specific, written prior permission.
+# 
+# DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 
+# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
+# INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
+# AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, 
+# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL 
+# DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 
+# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
+# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
+# SOFTWARE.
+# ======================================================================
+
+#
+# Usual options.
+#
+itk::usual Hierarchy {
+    keep -cursor -textfont -font
+    keep -background -foreground -textbackground 
+    keep -selectbackground -selectforeground 
+}
+
+# ------------------------------------------------------------------
+#                            HIERARCHY
+# ------------------------------------------------------------------
+itcl::class iwidgets::Hierarchy {
+    inherit iwidgets::Scrolledwidget
+
+    constructor {args} {}
+
+    destructor {}
+
+    itk_option define -alwaysquery alwaysQuery AlwaysQuery 0
+    itk_option define -closedicon closedIcon Icon {}
+    itk_option define -dblclickcommand dblClickCommand Command {}
+    itk_option define -expanded expanded Expanded 0 
+    itk_option define -filter filter Filter 0 
+    itk_option define -font font Font \
+       -*-Courier-Medium-R-Normal--*-120-*-*-*-*-*-* 
+    itk_option define -height height Height 0
+    itk_option define -iconcommand iconCommand Command {}
+    itk_option define -icondblcommand iconDblCommand Command {}
+    itk_option define -imagecommand imageCommand Command {}
+    itk_option define -imagedblcommand imageDblCommand Command {}
+    itk_option define -imagemenuloadcommand imageMenuLoadCommand Command {}
+    itk_option define -markbackground markBackground Foreground #a0a0a0 
+    itk_option define -markforeground markForeground Background Black 
+    itk_option define -nodeicon nodeIcon Icon {}
+    itk_option define -openicon openIcon Icon {}
+    itk_option define -querycommand queryCommand Command {}
+    itk_option define -selectcommand selectCommand Command {}
+    itk_option define -selectbackground selectBackground Foreground #c3c3c3 
+    itk_option define -selectforeground selectForeground Background Black 
+    itk_option define -textmenuloadcommand textMenuLoadCommand Command {}
+    itk_option define -visibleitems visibleItems VisibleItems 80x24
+    itk_option define -width width Width 0
+
+    public {
+       method clear {}
+       method collapse {node}
+       method current {}
+       method draw {{when -now}}
+       method expand {node}
+       method expanded {node}
+       method expState { }
+       method mark {op args}
+       method prune {node}
+       method refresh {node}
+       method selection {op args}
+       method toggle {node}
+       
+       method bbox {index} 
+       method compare {index1 op index2} 
+       method debug {args} {eval $args}
+       method delete {first {last {}}} 
+       method dlineinfo {index} 
+       method dump {args}
+       method get {index1 {index2 {}}} 
+       method index {index} 
+       method insert {args} 
+       method scan {option args} 
+       method search {args} 
+       method see {index} 
+       method tag {op args} 
+       method window {option args} 
+       method xview {args}
+       method yview {args}
+    }
+
+    protected {
+       method _contents {uid}
+       method _post {x y}
+       method _drawLevel {node indent}
+       method _select {x y}
+       method _deselectSubNodes {uid}
+       method _deleteNodeInfo {uid}
+       method _getParent {uid}
+       method _getHeritage {uid}
+       method _isInternalTag {tag}
+       method _iconSelect {node icon}
+       method _iconDblSelect {node icon}
+       method _imageSelect {node}
+       method _imageDblClick {node}
+       method _imagePost {node image type x y}
+       method _double {x y}
+    }
+    
+    private {
+        method _configureTags {}
+
+       variable _filterCode ""  ;# Compact view flag.
+       variable _hcounter 0     ;# Counter for hierarchy icons
+       variable _icons          ;# Array of user icons by uid
+       variable _images         ;# Array of our icons by uid
+       variable _indents        ;# Array of indentation by uid
+       variable _marked         ;# Array of marked nodes by uid
+       variable _markers ""     ;# List of markers for level being drawn
+       variable _nodes          ;# Array of subnodes by uid
+       variable _pending ""     ;# Pending draw flag
+       variable _posted ""      ;# List of tags at posted menu position
+       variable _selected       ;# Array of selected nodes by uid
+       variable _tags           ;# Array of user tags by uid
+       variable _text           ;# Array of displayed text by uid
+       variable _states         ;# Array of selection state by uid
+       variable _ucounter 0     ;# Counter for user icons
+    }
+}
+
+#
+# Provide a lowercased access method for the Hierarchy class.
+# 
+proc ::iwidgets::hierarchy {pathName args} {
+    uplevel ::iwidgets::Hierarchy $pathName $args
+}
+
+#
+# Use option database to override default resources of base classes.
+#
+option add *Hierarchy.menuCursor arrow widgetDefault
+option add *Hierarchy.labelPos n widgetDefault
+option add *Hierarchy.tabs 30 widgetDefault
+
+# ------------------------------------------------------------------
+#                        CONSTRUCTOR
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::constructor {args} {
+    itk_option remove iwidgets::Labeledwidget::state
+
+    #
+    # Our -width and -height options are slightly different than
+    # those implemented by our base class, so we're going to
+    # remove them and redefine our own.
+    #
+    itk_option remove iwidgets::Scrolledwidget::width
+    itk_option remove iwidgets::Scrolledwidget::height
+
+    #
+    # Create a clipping frame which will provide the border for
+    # relief display.
+    #
+    itk_component add clipper {
+       frame $itk_interior.clipper
+    } {
+       usual
+
+       keep -borderwidth -relief -highlightthickness -highlightcolor
+       rename -highlightbackground -background background Background
+    }  
+    grid $itk_component(clipper) -row 0 -column 0 -sticky nsew
+    grid rowconfigure $_interior 0 -weight 1
+    grid columnconfigure $_interior 0 -weight 1
+
+    #
+    # Create a text widget for displaying our hierarchy.
+    #
+    itk_component add list {
+       text $itk_component(clipper).list -wrap none -cursor center_ptr \
+                -state disabled -width 1 -height 1 \
+               -xscrollcommand \
+               [itcl::code $this _scrollWidget $itk_interior.horizsb] \
+               -yscrollcommand \
+               [itcl::code $this _scrollWidget $itk_interior.vertsb] \
+               -borderwidth 0 -highlightthickness 0
+    } {
+       usual
+
+       keep -spacing1 -spacing2 -spacing3 -tabs
+       rename -font -textfont textFont Font
+       rename -background -textbackground textBackground Background
+       ignore -highlightthickness -highlightcolor
+       ignore -insertbackground -insertborderwidth
+       ignore -insertontime -insertofftime -insertwidth
+       ignore -selectborderwidth
+       ignore -borderwidth
+    }
+    grid $itk_component(list) -row 0 -column 0 -sticky nsew
+    grid rowconfigure $itk_component(clipper) 0 -weight 1
+    grid columnconfigure $itk_component(clipper) 0 -weight 1
+    
+    # 
+    # Configure the command on the vertical scroll bar in the base class.
+    #
+    $itk_component(vertsb) configure \
+       -command [itcl::code $itk_component(list) yview]
+
+    #
+    # Configure the command on the horizontal scroll bar in the base class.
+    #
+    $itk_component(horizsb) configure \
+               -command [itcl::code $itk_component(list) xview]
+    
+    #
+    # Configure our text component's tab settings for twenty levels.
+    #
+    set tabs ""
+    for {set i 1} {$i < 20} {incr i} {
+       lappend tabs [expr {$i*12+4}]
+    }
+    $itk_component(list) configure -tabs $tabs
+
+    #
+    # Add popup menus that can be configured by the user to add
+    # new functionality.
+    #
+    itk_component add itemMenu {
+       menu $itk_component(list).itemmenu -tearoff 0
+    } {
+       usual
+       ignore -tearoff
+       rename -cursor -menucursor menuCursor Cursor
+    }
+
+    itk_component add bgMenu {
+       menu $itk_component(list).bgmenu -tearoff 0
+    } {
+       usual
+       ignore -tearoff
+       rename -cursor -menucursor menuCursor Cursor
+    }
+
+    #
+    # Adjust the bind tags to remove the class bindings.  Also, add
+    # bindings for mouse button 1 to do selection and button 3 to 
+    # display a popup.
+    #
+    bindtags $itk_component(list) [list $itk_component(list) . all]
+    
+    bind $itk_component(list) <ButtonPress-1> \
+            [itcl::code $this _select %x %y]
+
+    bind $itk_component(list) <Double-1> \
+            [itcl::code $this _double %x %y]
+
+    bind $itk_component(list) <ButtonPress-3> \
+            [itcl::code $this _post %x %y]
+    
+    #
+    # Initialize the widget based on the command line options.
+    #
+    eval itk_initialize $args
+}
+
+# ------------------------------------------------------------------
+#                           DESTRUCTOR
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::destructor {} {
+    if {$_pending != ""} {
+       after cancel $_pending
+    }
+}
+
+# ------------------------------------------------------------------
+#                             OPTIONS
+# ------------------------------------------------------------------
+
+# ------------------------------------------------------------------
+# OPTION: -font
+#
+# Font used for text in the list.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::font {
+    $itk_component(list) tag configure info \
+            -font $itk_option(-font) -spacing1 6
+}
+
+# ------------------------------------------------------------------
+# OPTION: -selectbackground
+#
+# Background color scheme for selected nodes.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::selectbackground {
+    $itk_component(list) tag configure hilite \
+            -background $itk_option(-selectbackground)
+}
+
+# ------------------------------------------------------------------
+# OPTION: -selectforeground
+#
+# Foreground color scheme for selected nodes.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::selectforeground {
+    $itk_component(list) tag configure hilite \
+            -foreground $itk_option(-selectforeground)
+}
+
+# ------------------------------------------------------------------
+# OPTION: -markbackground
+#
+# Background color scheme for marked nodes.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::markbackground {
+    $itk_component(list) tag configure lowlite \
+            -background $itk_option(-markbackground)
+}
+
+# ------------------------------------------------------------------
+# OPTION: -markforeground
+#
+# Foreground color scheme for marked nodes.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::markforeground {
+    $itk_component(list) tag configure lowlite \
+            -foreground $itk_option(-markforeground)
+}
+
+# ------------------------------------------------------------------
+# OPTION: -querycommand
+#
+# Command executed to query the contents of each node.  If this 
+# command contains "%n", it is replaced with the name of the desired 
+# node.  In its simpilest form it should return the children of the 
+# given node as a list which will be depicted in the display.
+#
+# Since the names of the children are used as tags in the underlying 
+# text widget, each child must be unique in the hierarchy.  Due to
+# the unique requirement, the nodes shall be reffered to as uids 
+# or uid in the singular sense.
+# 
+#   {uid [uid ...]}
+#
+#   where uid is a unique id and primary key for the hierarchy entry
+#
+# Should the unique requirement pose a problem, the list returned
+# can take on another more extended form which enables the 
+# association of text to be displayed with the uids.  The uid must
+# still be unique, but the text does not have to obey the unique
+# rule.  In addition, the format also allows the specification of
+# additional tags to be used on the same entry in the hierarchy
+# as the uid and additional icons to be displayed just before
+# the node.  The tags and icons are considered to be the property of
+# the user in that the hierarchy widget will not depend on any of 
+# their values.
+#
+#   {{uid [text [tags [icons]]]} {uid [text [tags [icons]]]} ...}
+#
+#   where uid is a unique id and primary key for the hierarchy entry
+#         text is the text to be displayed for this uid
+#         tags is a list of user tags to be applied to the entry
+#         icons is a list of icons to be displayed in front of the text
+#
+# The hierarchy widget does a look ahead from each node to determine
+# if the node has a children.  This can be cost some performace with
+# large hierarchies.  User's can avoid this by providing a hint in
+# the user tags.  A tag of "leaf" or "branch" tells the hierarchy
+# widget the information it needs to know thereby avoiding the look
+# ahead operation.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::querycommand {
+    clear
+    draw -eventually
+
+    # Added for SF ticket #596111
+    _configureTags
+}
+
+# ------------------------------------------------------------------
+# OPTION: -selectcommand
+#
+# Command executed to select an item in the list.  If this command
+# contains "%n", it is replaced with the name of the selected node.  
+# If it contains a "%s", it is replaced with a boolean indicator of 
+# the node's current selection status, where a value of 1 denotes
+# that the node is currently selected and 0 that it is not.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::selectcommand {
+}
+
+# ------------------------------------------------------------------
+# OPTION: -dblclickcommand
+#
+# Command executed to double click an item in the list.  If this command
+# contains "%n", it is replaced with the name of the selected node.  
+# If it contains a "%s", it is replaced with a boolean indicator of 
+# the node's current selection status, where a value of 1 denotes
+# that the node is currently selected and 0 that it is not.
+#
+# Douglas R. Howard, Jr.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::dblclickcommand {
+}
+
+# ------------------------------------------------------------------
+# OPTION: -iconcommand
+#
+# Command executed upon selection of user icons.  If this command 
+# contains "%n", it is replaced with the name of the node the icon
+# belongs to.  Should it contain "%i" then the icon name is 
+# substituted.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::iconcommand {
+}
+
+# ------------------------------------------------------------------
+# OPTION: -icondblcommand
+#
+# Command executed upon double selection of user icons.  If this command 
+# contains "%n", it is replaced with the name of the node the icon
+# belongs to.  Should it contain "%i" then the icon name is 
+# substituted.
+#
+# Douglas R. Howard, Jr.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::icondblcommand {
+}
+
+# ------------------------------------------------------------------
+# OPTION: -imagecommand
+#
+# Command executed upon selection of image icons.  If this command 
+# contains "%n", it is replaced with the name of the node the icon
+# belongs to.  Should it contain "%i" then the icon name is 
+# substituted.
+#
+# Douglas R. Howard, Jr.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::imagecommand {
+}
+
+# ------------------------------------------------------------------
+# OPTION: -imagedblcommand
+#
+# Command executed upon double selection of user icons.  If this command 
+# contains "%n", it is replaced with the name of the node the icon
+# belongs to.
+#
+# Douglas R. Howard, Jr.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::imagedblcommand {
+}
+
+# ------------------------------------------------------------------
+# OPTION: -alwaysquery
+#
+# Boolean flag which tells the hierarchy widget weather or not
+# each refresh of the display should be via a new query using
+# the -querycommand option or use the values previous found the
+# last time the query was made.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::alwaysquery {
+    switch -- $itk_option(-alwaysquery) {
+           1 - true - yes - on {
+               ;# okay
+           }
+           0 - false - no - off {
+               ;# okay
+           }
+           default {
+               error "bad alwaysquery option \"$itk_option(-alwaysquery)\":\
+                    should be boolean"
+           }
+    }
+}
+
+# ------------------------------------------------------------------
+# OPTION: -filter
+#
+# When true only the branch nodes and selected items are displayed.
+# This gives a compact view of important items.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::filter {
+    switch -- $itk_option(-filter) {
+           1 - true - yes - on {
+               set newCode {set display [info exists _selected($child)]}
+           }
+           0 - false - no - off {
+               set newCode {set display 1}
+           }
+           default {
+               error "bad filter option \"$itk_option(-filter)\":\
+                   should be boolean"
+           }
+    }
+    if {$newCode != $_filterCode} {
+           set _filterCode $newCode
+           draw -eventually
+    }
+}
+
+# ------------------------------------------------------------------
+# OPTION: -expanded
+#
+# When true, the hierarchy will be completely expanded when it
+# is first displayed.  A fresh display can be triggered by
+# resetting the -querycommand option.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::expanded {
+    switch -- $itk_option(-expanded) {
+           1 - true - yes - on {
+               ;# okay
+           }
+           0 - false - no - off {
+               ;# okay
+           }
+           default {
+               error "bad expanded option \"$itk_option(-expanded)\":\
+                   should be boolean"
+           }
+    }
+}
+    
+# ------------------------------------------------------------------
+# OPTION: -openicon
+#
+# Specifies the open icon image to be used in the hierarchy.  Should
+# one not be provided, then one will be generated, pixmap if 
+# possible, bitmap otherwise.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::openicon {
+    if {$itk_option(-openicon) == {}} {
+       if {[lsearch [image names] openFolder] == -1} {
+           if {[lsearch [image types] pixmap] != -1} {
+               image create pixmap openFolder -data {
+                   /* XPM */
+                   static char * dir_opened [] = {
+                       "16 16 4 1",
+                       /* colors */
+                       ". c grey85 m white g4 grey90",
+                       "b c black  m black g4 black",
+                       "y c yellow m white g4 grey80",
+                       "g c grey70 m white g4 grey70",
+                       /* pixels */
+                       "................",
+                       "................",
+                       "................",
+                       "..bbbb..........",
+                       ".bggggb.........",
+                       "bggggggbbbbbbb..",
+                       "bggggggggggggb..",
+                       "bgbbbbbbbbbbbbbb",
+                       "bgbyyyyyyyyyyybb",
+                       "bbyyyyyyyyyyyyb.",
+                       "bbyyyyyyyyyyybb.",
+                       "byyyyyyyyyyyyb..",
+                       "bbbbbbbbbbbbbb..",
+                       "................",
+                       "................",
+                       "................"};
+               }
+           } else {
+               image create bitmap openFolder -data {
+                   #define open_width 16
+                   #define open_height 16
+                   static char open_bits[] = {
+                       0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x42, 0x00, 
+                       0x81, 0x3f, 0x01, 0x20, 0xf9, 0xff, 0x0d, 0xc0, 
+                       0x07, 0x40, 0x03, 0x60, 0x01, 0x20, 0x01, 0x30,
+                       0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+               }
+           }
+       }
+       set itk_option(-openicon) openFolder
+    } else {
+       if {[lsearch [image names] $itk_option(-openicon)] == -1} {
+           error "bad openicon option \"$itk_option(-openicon)\":\
+                   should be an existing image"
+       }
+    }
+}
+
+# ------------------------------------------------------------------
+# OPTION: -closedicon
+#
+# Specifies the closed icon image to be used in the hierarchy.  
+# Should one not be provided, then one will be generated, pixmap if 
+# possible, bitmap otherwise.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::closedicon {
+    if {$itk_option(-closedicon) == {}} {
+       if {[lsearch [image names] closedFolder] == -1} {
+           if {[lsearch [image types] pixmap] != -1} {
+               image create pixmap closedFolder -data {
+                   /* XPM */
+                   static char *dir_closed[] = {
+                       "16 16 3 1",
+                       ". c grey85 m white g4 grey90",
+                       "b c black  m black g4 black",
+                       "y c yellow m white g4 grey80",
+                       "................",
+                       "................",
+                       "................",
+                       "..bbbb..........",
+                       ".byyyyb.........",
+                       "bbbbbbbbbbbbbb..",
+                       "byyyyyyyyyyyyb..",
+                       "byyyyyyyyyyyyb..",
+                       "byyyyyyyyyyyyb..",
+                       "byyyyyyyyyyyyb..",
+                       "byyyyyyyyyyyyb..",
+                       "byyyyyyyyyyyyb..",
+                       "bbbbbbbbbbbbbb..",
+                       "................",
+                       "................",
+                       "................"};    
+               }
+           } else {
+               image create bitmap closedFolder -data {
+                   #define closed_width 16
+                   #define closed_height 16
+                   static char closed_bits[] = {
+                       0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x84, 0x00, 
+                       0xfe, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 
+                       0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40,
+                       0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+               }
+           }
+       }
+       set itk_option(-closedicon) closedFolder
+    } else {
+       if {[lsearch [image names] $itk_option(-closedicon)] == -1} {
+           error "bad closedicon option \"$itk_option(-closedicon)\":\
+                   should be an existing image"
+       }
+    }
+}
+
+# ------------------------------------------------------------------
+# OPTION: -nodeicon
+#
+# Specifies the node icon image to be used in the hierarchy.  Should 
+# one not be provided, then one will be generated, pixmap if 
+# possible, bitmap otherwise.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::nodeicon {
+    if {$itk_option(-nodeicon) == {}} {
+       if {[lsearch [image names] nodeFolder] == -1} {
+           if {[lsearch [image types] pixmap] != -1} {
+               image create pixmap nodeFolder -data {
+                   /* XPM */
+                   static char *dir_node[] = {
+                       "16 16 3 1",
+                       ". c grey85 m white g4 grey90",
+                       "b c black  m black g4 black",
+                       "y c yellow m white g4 grey80",
+                       "................",
+                       "................",
+                       "................",
+                       "...bbbbbbbbbbb..",
+                       "..bybyyyyyyyyb..",
+                       ".byybyyyyyyyyb..",
+                       "byyybyyyyyyyyb..",
+                       "bbbbbyyyyyyyyb..",
+                       "byyyyyyyyyyyyb..",
+                       "byyyyyyyyyyyyb..",
+                       "byyyyyyyyyyyyb..",
+                       "byyyyyyyyyyyyb..",
+                       "bbbbbbbbbbbbbb..",
+                       "................",
+                       "................",
+                       "................"};    
+               }
+           } else {
+               image create bitmap nodeFolder -data {
+                   #define node_width 16
+                   #define node_height 16
+                   static char node_bits[] = {
+                       0x00, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x50, 0x40, 
+                       0x48, 0x40, 0x44, 0x40, 0x42, 0x40, 0x7e, 0x40, 
+                       0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40,
+                       0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+               }
+           }
+       }
+       set itk_option(-nodeicon) nodeFolder
+    } else {
+       if {[lsearch [image names] $itk_option(-nodeicon)] == -1} {
+           error "bad nodeicon option \"$itk_option(-nodeicon)\":\
+                   should be an existing image"
+       }
+    }
+}
+
+# ------------------------------------------------------------------
+# OPTION: -width
+#
+# Specifies the width of the hierarchy widget as an entire unit.
+# The value may be specified in any of the forms acceptable to 
+# Tk_GetPixels.  Any additional space needed to display the other
+# components such as labels, margins, and scrollbars force the text
+# to be compressed.  A value of zero along with the same value for 
+# the height causes the value given for the visibleitems option 
+# to be applied which administers geometry constraints in a different
+# manner.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::width {
+    if {$itk_option(-width) != 0} {
+       set shell [lindex [grid info $itk_component(clipper)] 1]
+
+       #
+       # Due to a bug in the tk4.2 grid, we have to check the 
+       # propagation before setting it.  Setting it to the same
+       # value it already is will cause it to toggle.
+       #
+       if {[grid propagate $shell]} {
+           grid propagate $shell no
+       }
+       
+       $itk_component(list) configure -width 1
+       $shell configure \
+               -width [winfo pixels $shell $itk_option(-width)] 
+    } else {
+       configure -visibleitems $itk_option(-visibleitems)
+    }
+}
+
+# ------------------------------------------------------------------
+# OPTION: -height
+#
+# Specifies the height of the hierarchy widget as an entire unit.
+# The value may be specified in any of the forms acceptable to 
+# Tk_GetPixels.  Any additional space needed to display the other
+# components such as labels, margins, and scrollbars force the text
+# to be compressed.  A value of zero along with the same value for 
+# the width causes the value given for the visibleitems option 
+# to be applied which administers geometry constraints in a different
+# manner.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::height {
+    if {$itk_option(-height) != 0} {
+       set shell [lindex [grid info $itk_component(clipper)] 1]
+
+       #
+       # Due to a bug in the tk4.2 grid, we have to check the 
+       # propagation before setting it.  Setting it to the same
+       # value it already is will cause it to toggle.
+       #
+       if {[grid propagate $shell]} {
+           grid propagate $shell no
+       }
+       
+       $itk_component(list) configure -height 1
+       $shell configure \
+               -height [winfo pixels $shell $itk_option(-height)] 
+    } else {
+       configure -visibleitems $itk_option(-visibleitems)
+    }
+}
+
+# ------------------------------------------------------------------
+# OPTION: -visibleitems
+#
+# Specified the widthxheight in characters and lines for the text.
+# This option is only administered if the width and height options
+# are both set to zero, otherwise they take precedence.  With the
+# visibleitems option engaged, geometry constraints are maintained
+# only on the text.  The size of the other components such as 
+# labels, margins, and scroll bars, are additive and independent, 
+# effecting the overall size of the scrolled text.  In contrast,
+# should the width and height options have non zero values, they
+# are applied to the scrolled text as a whole.  The text is 
+# compressed or expanded to maintain the geometry constraints.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::visibleitems {
+    if {[regexp {^[0-9]+x[0-9]+$} $itk_option(-visibleitems)]} {
+       if {($itk_option(-width) == 0) && \
+               ($itk_option(-height) == 0)} {
+           set chars [lindex [split $itk_option(-visibleitems) x] 0]
+           set lines [lindex [split $itk_option(-visibleitems) x] 1]
+           
+           set shell [lindex [grid info $itk_component(clipper)] 1]
+
+           #
+           # Due to a bug in the tk4.2 grid, we have to check the 
+           # propagation before setting it.  Setting it to the same
+           # value it already is will cause it to toggle.
+           #
+           if {! [grid propagate $shell]} {
+               grid propagate $shell yes
+           }
+           
+           $itk_component(list) configure -width $chars -height $lines
+       }
+       
+    } else {
+       error "bad visibleitems option\
+               \"$itk_option(-visibleitems)\": should be\
+               widthxheight"
+    }
+}
+
+# ------------------------------------------------------------------
+# OPTION: -textmenuloadcommand
+#
+# Dynamically loads the popup menu based on what was selected.
+#
+# Douglas R. Howard, Jr.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::textmenuloadcommand {}
+
+# ------------------------------------------------------------------
+# OPTION: -imagemenuloadcommand
+#
+# Dynamically loads the popup menu based on what was selected.
+#
+# Douglas R. Howard, Jr.
+# ------------------------------------------------------------------
+itcl::configbody iwidgets::Hierarchy::imagemenuloadcommand {}
+
+
+# ------------------------------------------------------------------
+#                         PUBLIC METHODS
+# ------------------------------------------------------------------
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: clear
+#
+# Removes all items from the display including all tags and icons.  
+# The display will remain empty until the -filter or -querycommand 
+# options are set.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::clear {} {
+    $itk_component(list) configure -state normal -cursor watch
+    $itk_component(list) delete 1.0 end
+    $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
+
+    # Clear the tags
+    eval $itk_component(list) tag delete [$itk_component(list) tag names]
+    
+    catch {unset _nodes}
+    catch {unset _text}
+    catch {unset _tags}
+    catch {unset _icons}
+    catch {unset _states}
+    catch {unset _images}
+    catch {unset _indents}
+    catch {unset _marked}
+    catch {unset _selected}
+    set _markers  ""
+    set _posted   ""
+    set _ucounter 0
+    set _hcounter 0 
+
+    foreach mark [$itk_component(list) mark names] {
+        $itk_component(list) mark unset $mark
+    }
+
+    return
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: selection option ?uid uid...?
+#
+# Handles all operations controlling selections in the hierarchy.
+# Selections may be cleared, added, removed, or queried.  The add and
+# remove options accept a series of unique ids.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::selection {op args} {
+    switch -- $op {
+        clear {
+            $itk_component(list) tag remove hilite 1.0 end
+            catch {unset _selected}
+           return
+        }
+        add {
+            foreach node $args {
+                set _selected($node) 1
+                catch {
+                    $itk_component(list) tag add hilite \
+                           "$node.first" "$node.last"
+                }
+            }
+        }
+        remove {
+            foreach node $args {
+                catch {
+                    unset _selected($node)
+                    $itk_component(list) tag remove hilite \
+                           "$node.first" "$node.last"
+                }
+            }
+        }
+       get {
+           return [array names _selected]
+       }
+        default {
+            error "bad selection operation \"$op\":\
+                   should be add, remove, clear or get"
+        }
+    }
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: mark option ?arg arg...?
+#
+# Handles all operations controlling marks in the hierarchy.  Marks may 
+# be cleared, added, removed, or queried.  The add and remove options 
+# accept a series of unique ids.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::mark {op args} {
+    switch -- $op {
+        clear {
+            $itk_component(list) tag remove lowlite 1.0 end
+            catch {unset _marked}
+           return
+        }
+        add {
+            foreach node $args {
+                set _marked($node) 1
+                catch {
+                    $itk_component(list) tag add lowlite \
+                           "$node.first" "$node.last"
+                }
+            }
+        }
+        remove {
+            foreach node $args {
+                catch {
+                    unset _marked($node)
+                    $itk_component(list) tag remove lowlite \
+                           "$node.first" "$node.last"
+                }
+            }
+        }
+       get {
+           return [array names _marked]
+       }
+        default {
+            error "bad mark operation \"$op\":\
+                   should be add, remove, clear or get"
+        }
+    }
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: current
+#
+# Returns the node that was most recently selected by the right mouse
+# button when the item menu was posted.  Usually used by the code
+# in the item menu to figure out what item is being manipulated.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::current {} {
+    return $_posted
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: expand node
+#
+# Expands the hierarchy beneath the specified node.  Since this can take
+# a moment for large hierarchies, the cursor will be changed to a watch
+# during the expansion.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::expand {node} {
+    if {! [info exists _states($node)]} {
+       error "bad expand node argument: \"$node\", the node doesn't exist"
+    }
+
+    if {!$_states($node) && \
+           (([lsearch $_tags($node) branch] != -1) || \
+            ([llength [_contents $node]] > 0))} {
+        $itk_component(list) configure -state normal -cursor watch
+        update
+
+       #
+       # Get the indentation level for the node.
+       #
+        set indent $_indents($node)
+
+        set _markers ""
+        $itk_component(list) mark set insert "$node:start"
+        _drawLevel $node $indent
+
+       #
+       # Following the draw, all our markers need adjusting.
+       #
+        foreach {name index} $_markers {
+            $itk_component(list) mark set $name $index
+        }
+
+       #
+       # Set the image to be the open icon, denote the new state,
+       # and set the cursor back to normal along with the state.
+       #
+       $_images($node) configure -image $itk_option(-openicon)
+
+        set _states($node) 1
+
+        $itk_component(list) configure -state disabled \
+               -cursor $itk_option(-cursor)
+    }
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: collapse node
+#
+# Collapses the hierarchy beneath the specified node.  Since this can 
+# take a moment for large hierarchies, the cursor will be changed to a 
+# watch during the expansion.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::collapse {node} {
+    if {! [info exists _states($node)]} {
+       error "bad collapse node argument: \"$node\", the node doesn't exist"
+    }
+
+    if {[info exists _states($node)] && $_states($node) && \
+           (([lsearch $_tags($node) branch] != -1) || \
+            ([llength [_contents $node]] > 0))} {
+        $itk_component(list) configure -state normal -cursor watch
+       update
+
+       _deselectSubNodes $node
+
+        $itk_component(list) delete "$node:start" "$node:end"
+
+       catch {$_images($node) configure -image $itk_option(-closedicon)}
+
+        set _states($node) 0
+
+        $itk_component(list) configure -state disabled \
+           -cursor $itk_option(-cursor)
+    }
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: toggle node
+#
+# Toggles the hierarchy beneath the specified node.  If the hierarchy
+# is currently expanded, then it is collapsed, and vice-versa.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::toggle {node} {
+    if {! [info exists _states($node)]} {
+       error "bad toggle node argument: \"$node\", the node doesn't exist"
+    }
+
+    if {$_states($node)} {
+        collapse $node
+    } else {
+        expand $node
+    }
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: prune node
+#
+# Removes a particular node from the hierarchy.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::prune {node} {
+    #
+    # While we're working, change the state and cursor so we can
+    # edit the text and give a busy visual clue.
+    #
+    $itk_component(list) configure -state normal -cursor watch
+
+    #
+    # Recursively delete all the subnode information from our internal
+    # arrays and remove all the tags.  
+    #
+    _deleteNodeInfo $node
+
+    #
+    # If the mark $node:end exists then the node has decendents so
+    # so we'll remove from the mark $node:start to $node:end in order 
+    # to delete all the subnodes below it in the text.  
+    # 
+    if {[lsearch [$itk_component(list) mark names] $node:end] != -1} {
+       $itk_component(list) delete $node:start $node:end
+       $itk_component(list) mark unset $node:end
+    } 
+
+    #
+    # Next we need to remove the node itself.  Using the ranges for
+    # its tag we'll remove it from line start to the end plus one
+    # character which takes us to the start of the next node.
+    #
+    foreach {start end} [$itk_component(list) tag ranges $node] {
+       $itk_component(list) delete "$start linestart" "$end + 1 char"
+    }
+
+    #
+    # Delete the tag for this node.
+    #
+    $itk_component(list) tag delete $node
+
+    #
+    # The node must be removed from the list of subnodes for its parent.
+    # We don't really have a clean way to do upwards referencing, so
+    # the dirty way will have to do.  We'll cycle through each node
+    # and if this node is in its list of subnodes, we'll remove it.
+    #
+    foreach uid [array names _nodes] {
+       if {[set index [lsearch $_nodes($uid) $node]] != -1} {
+           set _nodes($uid) [lreplace $_nodes($uid) $index $index]
+       }
+    }
+
+    #
+    # We're done, so change the state and cursor back to their 
+    # original values.
+    #
+    $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: draw ?when?
+#
+# Performs a complete draw of the entire hierarchy.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::draw {{when -now}} {
+    if {$when == "-eventually"} {
+        if {$_pending == ""} {
+            set _pending [after idle [itcl::code $this draw -now]]
+        }
+        return
+    } elseif {$when != "-now"} {
+        error "bad when option \"$when\": should be -eventually or -now"
+    }
+    $itk_component(list) configure -state normal -cursor watch
+    update
+
+    $itk_component(list) delete 1.0 end
+    catch {unset _images}
+    set _markers ""
+
+    _drawLevel "" ""
+
+    foreach {name index} $_markers {
+        $itk_component(list) mark set $name $index
+    }
+
+    $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
+    set _pending ""
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: refresh node
+#
+# Performs a redraw of a specific node.  If that node is currently 
+# not visible, then no action is taken.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::refresh {node} {
+    if {! [info exists _nodes($node)]} {
+       error "bad refresh node argument: \"$node\", the node doesn't exist"
+    }
+
+    
+    if {! $_states($node)} {return}
+
+    foreach parent [_getHeritage $node] {
+       if {! $_states($parent)} {return}
+    }
+
+    $itk_component(list) configure -state normal -cursor watch
+    $itk_component(list) delete $node:start $node:end
+
+    set _markers ""
+    $itk_component(list) mark set insert "$node:start"
+    set indent $_indents($node)
+
+    _drawLevel $node $indent
+
+    foreach {name index} $_markers {
+        $itk_component(list) mark set $name $index
+    }
+
+    $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
+}
+
+# ------------------------------------------------------------------
+# THIN WRAPPED TEXT METHODS:
+#
+# The following methods are thin wraps of standard text methods.
+# Consult the Tk text man pages for functionallity and argument
+# documentation.
+# ------------------------------------------------------------------
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD: bbox index
+#
+# Returns four element list describing the bounding box for the list
+# item at index
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::bbox {index} {
+    return [$itk_component(list) bbox $index]
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD compare index1 op index2
+#
+# Compare indices according to relational operator.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::compare {index1 op index2} {
+    return [$itk_component(list) compare $index1 $op $index2]
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD delete first ?last?
+#
+# Delete a range of characters from the text.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::delete {first {last {}}} {
+    $itk_component(list) configure -state normal -cursor watch
+    $itk_component(list) delete $first $last
+    $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD dump ?switches? index1 ?index2?
+#
+# Returns information about the contents of the text widget from 
+# index1 to index2.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::dump {args} {
+    return [eval $itk_component(list) dump $args]
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD dlineinfo index
+#
+# Returns a five element list describing the area occupied by the
+# display line containing index.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::dlineinfo {index} {
+    return [$itk_component(list) dlineinfo $index]
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD get index1 ?index2?
+#
+# Return text from start index to end index.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::get {index1 {index2 {}}} {
+    return [$itk_component(list) get $index1 $index2]
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD index index
+#
+# Return position corresponding to index.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::index {index} {
+    return [$itk_component(list) index $index]
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD insert index chars ?tagList?
+#
+# Insert text at index.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::insert {args} {
+    $itk_component(list) configure -state normal -cursor watch
+    eval $itk_component(list) insert $args
+    $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD scan option args
+#
+# Implements scanning on texts.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::scan {option args} {
+    eval $itk_component(list) scan $option $args
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD search ?switches? pattern index ?varName?
+#
+# Searches the text for characters matching a pattern.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::search {args} {
+    return [eval $itk_component(list) search $args]
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD see index
+#
+# Adjusts the view in the window so the character at index is 
+# visible.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::see {index} {
+    $itk_component(list) see $index
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD tag option ?arg arg ...?
+#
+# Manipulate tags dependent on options.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::tag {op args} {
+    return [eval $itk_component(list) tag $op $args]
+}
+
+# ------------------------------------------------------------------
+# PUBLIC METHOD window option ?arg arg ...?
+#
+# Manipulate embedded windows.
+# ------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::window {option args} {
+    return [eval $itk_component(list) window $option $args]
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: xview args
+#
+# Thin wrap of the text widget's xview command.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::xview {args} {
+    return [eval itk_component(list) xview $args]
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: yview args
+#
+# Thin wrap of the text widget's yview command.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::yview {args} {
+    return [eval $itk_component(list) yview $args]
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: expanded node
+#
+# Tells if a node is expanded or collapsed
+#
+# Douglas R. Howard, Jr.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::expanded {node} {
+    if {! [info exists _states($node)]} {
+       error "bad collapse node argument: \"$node\", the node doesn't exist"
+    }
+    
+    return $_states($node)
+}
+
+# ----------------------------------------------------------------------
+# PUBLIC METHOD: expState
+#
+# Returns a list of all expanded nodes
+#
+# Douglas R. Howard, Jr.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::expState {} {
+    set nodes [_contents ""]
+    set open ""
+    set i 0
+    while {1} {
+       if {[info exists _states([lindex $nodes $i])] &&
+       $_states([lindex $nodes $i])} {
+           lappend open [lindex $nodes $i]
+           foreach child [_contents [lindex $nodes $i]] {
+               lappend nodes $child
+           }
+       }
+       incr i
+       if {$i >= [llength $nodes]} {break}
+    }
+    
+    return $open
+}
+
+# ------------------------------------------------------------------
+#                       PROTECTED METHODS
+# ------------------------------------------------------------------
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _drawLevel node indent
+#
+# Used internally by draw to draw one level of the hierarchy.
+# Draws all of the nodes under node, using the indent string to
+# indent nodes.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_drawLevel {node indent} {
+    lappend _markers "$node:start" [$itk_component(list) index insert]
+    set bg [$itk_component(list) cget -background]
+
+    #
+    # Obtain the list of subnodes for this node and cycle through
+    # each one displaying it in the hierarchy.
+    #
+    foreach child [_contents $node] {
+       set _images($child) "$itk_component(list).hicon[incr _hcounter]"
+
+        if {![info exists _states($child)]} {
+            set _states($child) $itk_option(-expanded)
+        }
+
+       #
+       # Check the user tags to see if they have been kind enough
+       # to tell us ahead of time what type of node we are dealing
+       # with branch or leaf.  If they neglected to do so, then
+       # get the contents of the child node to see if it has children
+       # itself.
+       #
+       set display 0
+
+       if {[lsearch $_tags($child) leaf] != -1} {
+           set type leaf
+       } elseif {[lsearch $_tags($child) branch] != -1} {
+           set type branch
+       } else {
+           if {[llength [_contents $child]] == 0} {
+               set type leaf
+           } else {
+               set type branch
+           }
+       }
+
+       #
+       # Now that we know the type of node, branch or leaf, we know
+       # the type of icon to use.
+       #
+       if {$type == "leaf"} {
+            set icon $itk_option(-nodeicon)
+            eval $_filterCode
+       } else {
+            if {$_states($child)} {
+                set icon $itk_option(-openicon)
+            } else {
+                set icon $itk_option(-closedicon)
+            }
+            set display 1
+       }
+
+       #
+       # If display is set then we're going to be drawing this node.
+       # Save off the indentation level for this node and do the indent.
+       #
+       if {$display} {
+           set _indents($child) "$indent\t"
+           $itk_component(list) insert insert $indent
+
+           #
+           # Add the branch or leaf icon and setup a binding to toggle
+           # its expanded/collapsed state.
+           #
+           label $_images($child) -image $icon -background $bg 
+           # DRH - enhanced and added features that handle image clicking,
+           # double clicking, and right clicking behavior
+           bind $_images($child) <ButtonPress-1> \
+             "[itcl::code $this toggle $child]; [itcl::code $this _imageSelect $child]"
+           bind $_images($child) <Double-1> [itcl::code $this _imageDblClick $child]
+           bind $_images($child) <ButtonPress-3> \
+             [itcl::code $this _imagePost $child $_images($child) $type %x %y]
+           $itk_component(list) window create insert -window $_images($child)
+
+           #
+           # If any user icons exist then draw them as well.  The little
+           # regexp is just to check and see if they've passed in a
+           # command which needs to be evaluated as opposed to just
+           # a variable.  Also, attach a binding to call them if their
+           # icon is selected.
+           #
+           if {[info exists _icons($child)]} {
+               foreach image $_icons($child) {
+                   set wid "$itk_component(list).uicon[incr _ucounter]"
+
+                   if {[regexp {\[.*\]} $image]} {
+                       eval label $wid -image $image -background $bg 
+                   } else {
+                       label $wid -image $image -background $bg 
+                   }
+
+                   # DRH - this will bind events to the icons to allow
+                   # clicking, double clicking, and right clicking actions.
+                   bind $wid <ButtonPress-1> \
+                           [itcl::code $this _iconSelect $child $image]
+                   bind $wid <Double-1> \
+                           [itcl::code $this _iconDblSelect $child $image]
+                   bind $wid <ButtonPress-3> \
+                           [itcl::code $this _imagePost $child $wid $type %x %y]
+                   $itk_component(list) window create insert -window $wid
+               }
+           }
+
+           #
+           # Create the list of tags to be applied to the text.  Start
+           # out with a tag of "info" and append "hilite" if the node
+           # is currently selected, finally add the tags given by the
+           # user.
+           #
+           set texttags [list "info" $child]
+
+           if {[info exists _selected($child)]} {
+               lappend texttags hilite
+           } 
+
+            # The following conditional added for SF ticket #600941.
+            if {[info exists _marked($child)]} { 
+                lappend texttags lowlite 
+            } 
+
+           foreach tag $_tags($child) {
+               lappend texttags $tag
+           }
+
+           #
+           # Insert the text for the node along with the tags and 
+           # append to the markers the start of this node.  The text
+           # has been broken at newlines into a list.  We'll make sure
+           # that each line is at the same indentation position.
+           #
+           set firstline 1
+           foreach line $_text($child) {
+               if {$firstline} {
+                   $itk_component(list) insert insert " "
+               } else {
+                   $itk_component(list) insert insert "$indent\t"
+               }
+
+               $itk_component(list) insert insert $line $texttags "\n"
+               set firstline 0
+           }
+
+           $itk_component(list) tag raise $child
+           lappend _markers "$child:start" [$itk_component(list) index insert]
+
+           #
+           # If the state of the node is open, proceed to draw the next 
+           # node below it in the hierarchy.
+           #
+           if {$_states($child)} {
+               _drawLevel $child "$indent\t"
+           }
+       }
+    }
+
+    lappend _markers "$node:end" [$itk_component(list) index insert]
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _contents uid
+#
+# Used internally to get the contents of a particular node.  If this
+# is the first time the node has been seen or the -alwaysquery
+# option is set, the -querycommand code is executed to query the node 
+# list, and the list is stored until the next time it is needed.
+#
+# The querycommand may return not only the list of subnodes for the 
+# node but additional information on the tags and icons to be used.  
+# The return value must be parsed based on the number of elements in 
+# the list where the format is a list of lists:
+#
+# {{uid [text [tags [icons]]]} {uid [text [tags [icons]]]} ...}
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_contents {uid} {
+    if {$itk_option(-alwaysquery)} {
+    } else {
+      if {[info exists _nodes($uid)]} {
+          return $_nodes($uid)
+      }
+    }
+
+    # 
+    # Substitute any %n's for the node name whose children we're
+    # interested in obtaining.
+    #
+    set cmd $itk_option(-querycommand)
+    regsub -all {%n} $cmd [list $uid] cmd
+
+    set nodeinfolist [uplevel \#0 $cmd]
+
+    #
+    # Cycle through the node information returned by the query
+    # command determining if additional information such as text,
+    # user tags, or user icons have been provided.  For text,
+    # break it into a list at any newline characters.
+    #
+    set _nodes($uid) {}
+
+    foreach nodeinfo $nodeinfolist {
+       set subnodeuid [lindex $nodeinfo 0]
+       lappend _nodes($uid) $subnodeuid
+
+       set llen [llength $nodeinfo] 
+
+       if {$llen == 0 || $llen > 4} {
+           error "invalid number of elements returned by query\
+                       command for node: \"$uid\",\
+                       should be uid \[text \[tags \[icons\]\]\]"
+       }
+
+       if {$llen == 1} {
+           set _text($subnodeuid) [split $subnodeuid \n]
+       } 
+       if {$llen > 1} {
+           set _text($subnodeuid) [split [lindex $nodeinfo 1] \n]
+       }
+       if {$llen > 2} {
+           set _tags($subnodeuid) [lindex $nodeinfo 2]
+       } else {
+           set _tags($subnodeuid) unknown
+       }
+       if {$llen > 3} {
+           set _icons($subnodeuid) [lindex $nodeinfo 3]
+       }
+    }
+                 
+    #
+    # Return the list of nodes.
+    #
+    return $_nodes($uid)
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _post x y
+#
+# Used internally to post the popup menu at the coordinate (x,y)
+# relative to the widget.  If (x,y) is on an item, then the itemMenu
+# component is posted.  Otherwise, the bgMenu is posted.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_post {x y} {
+    set rx [expr {[winfo rootx $itk_component(list)]+$x}]
+    set ry [expr {[winfo rooty $itk_component(list)]+$y}]
+
+    set index [$itk_component(list) index @$x,$y]
+
+    #
+    # The posted variable will hold the list of tags which exist at
+    # this x,y position that will be passed back to the user.  They
+    # don't need to know about our internal tags, info, hilite, and
+    # lowlite, so remove them from the list.
+    # 
+    set _posted {}
+
+    foreach tag [$itk_component(list) tag names $index] {
+        if {![_isInternalTag $tag]} {
+            lappend _posted $tag
+        }
+    }
+
+    #
+    # If we have tags then do the popup at this position.
+    #
+    if {$_posted != {}} {
+       # DRH - here is where the user's function for dynamic popup
+       # menu loading is done, if the user has specified to do so with the
+       # "-textmenuloadcommand"
+       if {$itk_option(-textmenuloadcommand) != {}} {
+           eval $itk_option(-textmenuloadcommand)
+       }
+       tk_popup $itk_component(itemMenu) $rx $ry
+    } else {
+       tk_popup $itk_component(bgMenu) $rx $ry
+    }
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _imagePost node image type x y
+#
+# Used internally to post the popup menu at the coordinate (x,y)
+# relative to the widget.  If (x,y) is on an image, then the itemMenu
+# component is posted.
+#
+# Douglas R. Howard, Jr.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_imagePost {node image type x y} {
+    set rx [expr {[winfo rootx $image]+$x}]
+    set ry [expr {[winfo rooty $image]+$y}]
+
+    #
+    # The posted variable will hold the list of tags which exist at
+    # this x,y position that will be passed back to the user.  They
+    # don't need to know about our internal tags, info, hilite, and
+    # lowlite, so remove them from the list.
+    # 
+    set _posted {}
+
+    lappend _posted $node $type
+
+    #
+    # If we have tags then do the popup at this position.
+    #
+    if {$itk_option(-imagemenuloadcommand) != {}} {
+       eval $itk_option(-imagemenuloadcommand)
+    }
+    tk_popup $itk_component(itemMenu) $rx $ry
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _select x y
+#
+# Used internally to select an item at the coordinate (x,y) relative 
+# to the widget.  The command associated with the -selectcommand
+# option is execute following % character substitutions.  If %n
+# appears in the command, the selected node is substituted.  If %s
+# appears, a boolean value representing the current selection state
+# will be substituted.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_select {x y} {
+    if {$itk_option(-selectcommand) != {}} {
+       if {[set seltags [$itk_component(list) tag names @$x,$y]] != {}} {
+           foreach tag $seltags {
+               if {![_isInternalTag $tag]} {
+                   lappend node $tag
+               }
+           }
+
+           if {[lsearch $seltags "hilite"] == -1} {
+               set selectstatus 0
+           } else {
+               set selectstatus 1
+           }
+
+           set cmd $itk_option(-selectcommand)
+           regsub -all {%n} $cmd [lindex $node end] cmd
+           regsub -all {%s} $cmd [list $selectstatus] cmd
+
+           uplevel #0 $cmd
+       }
+    }
+
+    return
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _double x y
+#
+# Used internally to double click an item at the coordinate (x,y) relative 
+# to the widget.  The command associated with the -dblclickcommand
+# option is execute following % character substitutions.  If %n
+# appears in the command, the selected node is substituted.  If %s
+# appears, a boolean value representing the current selection state
+# will be substituted.
+#
+# Douglas R. Howard, Jr.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_double {x y} {
+    if {$itk_option(-dblclickcommand) != {}} {
+       if {[set seltags [$itk_component(list) tag names @$x,$y]] != {}} {
+           foreach tag $seltags {
+               if {![_isInternalTag $tag]} {
+                   lappend node $tag
+               }
+           }
+
+           if {[lsearch $seltags "hilite"] == -1} {
+               set selectstatus 0
+           } else {
+               set selectstatus 1
+           }
+
+           set cmd $itk_option(-dblclickcommand)
+           regsub -all {%n} $cmd [list $node] cmd
+           regsub -all {%s} $cmd [list $selectstatus] cmd
+
+           uplevel #0 $cmd
+       }
+    }
+
+    return
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _iconSelect node icon
+#
+# Used internally to upon selection of user icons.  The -iconcommand
+# is executed after substitution of the node for %n and icon for %i.
+#
+# Douglas R. Howard, Jr.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_iconSelect {node icon} {
+    set cmd $itk_option(-iconcommand)
+    regsub -all {%n} $cmd [list $node] cmd
+    regsub -all {%i} $cmd [list $icon] cmd
+
+    uplevel \#0 $cmd
+
+    return {}
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _iconDblSelect node icon
+#
+# Used internally to upon double selection of user icons.  The 
+# -icondblcommand is executed after substitution of the node for %n and 
+# icon for %i.
+#
+# Douglas R. Howard, Jr.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_iconDblSelect {node icon} {
+    if {$itk_option(-icondblcommand) != {}} {
+       set cmd $itk_option(-icondblcommand)
+       regsub -all {%n} $cmd [list $node] cmd
+       regsub -all {%i} $cmd [list $icon] cmd
+       
+       uplevel \#0 $cmd
+    }
+    return {}
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _imageSelect node icon
+#
+# Used internally to upon selection of user icons.  The -imagecommand
+# is executed after substitution of the node for %n.
+#
+# Douglas R. Howard, Jr.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_imageSelect {node} {
+    if {$itk_option(-imagecommand) != {}} {
+       set cmd $itk_option(-imagecommand)
+       regsub -all {%n} $cmd [list $node] cmd
+       
+       uplevel \#0 $cmd
+    }
+    return {}
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _imageDblClick node
+#
+# Used internally to upon double selection of images.  The 
+# -imagedblcommand is executed.
+#
+# Douglas R. Howard, Jr.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_imageDblClick {node} {
+    if {$itk_option(-imagedblcommand) != {}} {
+       set cmd $itk_option(-imagedblcommand)
+       regsub -all {%n} $cmd [list $node] cmd
+       
+       uplevel \#0 $cmd
+    }
+    return {}
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _deselectSubNodes uid
+#
+# Used internally to recursively deselect all the nodes beneath a 
+# particular node.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_deselectSubNodes {uid} {
+    foreach node $_nodes($uid) {
+       if {[array names _selected $node] != {}} {
+           unset _selected($node)
+       }
+       
+       if {[array names _nodes $node] != {}} {
+           _deselectSubNodes $node
+       }
+    }
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _deleteNodeInfo uid
+#
+# Used internally to recursively delete all the information about a
+# node and its decendents.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_deleteNodeInfo {uid} {
+    #
+    # Recursively call ourseleves as we go down the hierarchy beneath
+    # this node.
+    #
+    if {[info exists _nodes($uid)]} {
+       foreach node $_nodes($uid) {
+           if {[array names _nodes $node] != {}} {
+               _deleteNodeInfo $node
+           }
+       }
+    }
+
+    #
+    # Unset any entries in our arrays for the node.
+    #
+    catch {unset _nodes($uid)}
+    catch {unset _text($uid)}
+    catch {unset _tags($uid)}
+    catch {unset _icons($uid)}
+    catch {unset _states($uid)}
+    catch {unset _images($uid)}
+    catch {unset _indents($uid)}
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _getParent uid
+#
+# Used internally to determine the parent for a node.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_getParent {uid} {
+    foreach node [array names _nodes] {
+       if {[set index [lsearch $_nodes($node) $uid]] != -1} {
+           return $node
+       }
+    }
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD: _getHeritage uid
+#
+# Used internally to determine the list of parents for a node.
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_getHeritage {uid} {
+    set parents {}
+
+    if {[set parent [_getParent $uid]] != {}} {
+       lappend parents $parent
+    }
+
+    return $parents
+}
+
+# ----------------------------------------------------------------------
+# PROTECTED METHOD (could be proc?): _isInternalTag tag
+#
+# Used internally to tags not to used for user callback commands
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_isInternalTag {tag} {
+   set ii [expr {[lsearch -exact {info hilite lowlite unknown} $tag] != -1}];
+   return $ii;
+}
+
+# ----------------------------------------------------------------------
+# PRIVATE METHOD: _configureTags
+#
+# This method added to fix SF ticket #596111.  When the -querycommand
+# is reset after initial construction, the text component loses its
+# tag configuration.  This method resets the hilite, lowlite, and info
+# tags.  csmith: 9/5/02
+# ----------------------------------------------------------------------
+itcl::body iwidgets::Hierarchy::_configureTags {} {
+  tag configure hilite -background $itk_option(-selectbackground) \
+    -foreground $itk_option(-selectforeground)
+  tag configure lowlite -background $itk_option(-markbackground) \
+    -foreground $itk_option(-markforeground)
+  tag configure info -font $itk_option(-font) -spacing1 6
+}