--- /dev/null
+# 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
+}