# I am D. Richard Hipp, the author of this code. I hereby # disavow all claims to copyright on this program and release # it into the public domain. # # D. Richard Hipp # January 31, 2001 # # As an historical record, the original copyright notice is # reproduced below: # # Copyright (C) 1997,1998 D. Richard Hipp # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # # Author contact information: # drh@acm.org # http://www.hwaci.com/drh/ # # $Revision: 1.1 $ # option add *highlightThickness 0 switch $tcl_platform(platform) { unix { set Tree(font) \ -adobe-helvetica-medium-r-normal-*-11-80-100-100-p-56-iso8859-1 } windows { set Tree(font) \ -adobe-helvetica-medium-r-normal-*-14-100-100-100-p-76-iso8859-1 } } # # Create a new tree widget. $args become the configuration arguments to # the canvas widget from which the tree is constructed. # proc Tree:create {w args} { global Tree eval Tree:reuse $w $args Tree:dfltconfig $w / set Tree($w:reuse) 0 bind $w "Tree:destroy $w" } proc Tree:reuse {w args} { global Tree eval canvas $w -bg white $args Tree:buildwhenidle $w set Tree($w:selection) {} set Tree($w:selidx) {} set Tree($w:reuse) 1 } proc Tree:setreuse {w arg} { global Tree set Tree($w:reuse) $arg if {$arg} { bind $w "" } else { bind $w "Tree:destroy $w" } } # Initialize a element of the tree. # Internal use only # proc Tree:dfltconfig {w v} { global Tree set Tree($w:$v:children) {} set Tree($w:$v:open) 0 set Tree($w:$v:icon) {} set Tree($w:$v:tags) {} } proc Tree:flush {w} { global Tree Tree:delitem $w / Tree:dfltconfig $w / set Tree($w:selection) {} set Tree($w:selidx) {} } proc Tree:destroy {w} { Tree:delitem $w / # delete the whole widget catch {destroy $w} foreach t [array names Tree $w:*] { unset Tree($t) } } # # Pass configuration options to the tree widget # proc Tree:config {w args} { eval $w config $args } # # Insert a new element $v into the tree $w. # proc Tree:newitem {w v args} { global Tree set dir [file dirname $v] set n [file tail $v] if {![info exists Tree($w:$dir:open)]} { error "parent item \"$dir\" is missing" } set i [lsearch -exact $Tree($w:$dir:children) $n] if {$i>=0} { error "item \"$v\" already exists" } lappend Tree($w:$dir:children) $n set Tree($w:$dir:children) [lsort $Tree($w:$dir:children)] Tree:dfltconfig $w $v foreach {op arg} $args { switch -exact -- $op { -image {set Tree($w:$v:icon) $arg} -tags {set Tree($w:$v:tags) $arg} } } Tree:buildwhenidle $w } # # Delete element $v from the tree $w. If $v is /, then the widget is # deleted. # proc Tree:delitem {w v} { global Tree if {![info exists Tree($w:$v:open)]} return foreach c $Tree($w:$v:children) { catch {Tree:delitem $w $v/$c} } unset Tree($w:$v:open) unset Tree($w:$v:children) unset Tree($w:$v:icon) # special case -- if 'v' is the root directory, then don't try to # remove its "parent" if {![string equal $v "/"]} { set dir [file dirname $v] set n [file tail $v] set i [lsearch -exact $Tree($w:$dir:children) $n] if {$i>=0} { set Tree($w:$dir:children) [lreplace $Tree($w:$dir:children) $i $i] } } Tree:buildwhenidle $w } # # Change the selection to the indicated item # proc Tree:setselection {w v} { global Tree set Tree($w:selection) $v Tree:drawselection $w } # # Retrieve the current selection # proc Tree:getselection w { global Tree return $Tree($w:selection) } # # Bitmaps used to show which parts of the tree can be opened. # set maskdata "#define solid_width 9\n#define solid_height 9" append maskdata { static unsigned char solid_bits[] = { 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01 }; } set data "#define open_width 9\n#define open_height 9" append data { static unsigned char open_bits[] = { 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0x01 }; } image create bitmap Tree:openbm -data $data -maskdata $maskdata \ -foreground black -background white set data "#define closed_width 9\n#define closed_height 9" append data { static unsigned char closed_bits[] = { 0xff, 0x01, 0x01, 0x01, 0x11, 0x01, 0x11, 0x01, 0x7d, 0x01, 0x11, 0x01, 0x11, 0x01, 0x01, 0x01, 0xff, 0x01 }; } image create bitmap Tree:closedbm -data $data -maskdata $maskdata \ -foreground black -background white # Internal use only. # Draw the tree on the canvas proc Tree:build w { global Tree $w delete all catch {unset Tree($w:buildpending)} set Tree($w:y) 12 Tree:buildlayer $w / 8 set bbox [$w bbox all] $w config -scrollregion [list 0 [expr [lindex $bbox 1] + 4] \ [lindex $bbox 2] [lindex $bbox 3]] Tree:drawselection $w } # Internal use only. # Build a single layer of the tree on the canvas. Indent by $in pixels proc Tree:buildlayer {w v in} { global Tree if {$v=="/"} { set vx {} } else { set vx $v } set start [expr $Tree($w:y)-10] foreach c $Tree($w:$v:children) { set y $Tree($w:y) incr Tree($w:y) 17 $w create line $in $y [expr $in+10] $y -fill gray50 set icon $Tree($w:$vx/$c:icon) set taglist x foreach tag $Tree($w:$vx/$c:tags) { lappend taglist $tag } set x [expr $in+12] if {[string length $icon]>0} { set k [$w create image $x $y -image $icon -anchor w -tags $taglist] incr x 20 set Tree($w:tag:$k) $vx/$c } set j [$w create text $x $y -text $c -font $Tree(font) \ -anchor w -tags $taglist] set Tree($w:tag:$j) $vx/$c set Tree($w:$vx/$c:tag) $j if {[string length $Tree($w:$vx/$c:children)]} { regsub -all { } $vx/$c "\\ " child if {$Tree($w:$vx/$c:open)} { set j [$w create image $in $y -image Tree:openbm] $w bind $j <1> "set Tree($w:$child:open) 0; Tree:build $w" Tree:buildlayer $w $vx/$c [expr $in+18] } else { set j [$w create image $in $y -image Tree:closedbm] $w bind $j <1> "set Tree($w:$child:open) 1; Tree:build $w" } } } if {![info exist y]} { set y 0 } set j [$w create line $in $start $in [expr $y+1] -fill gray50 ] $w lower $j } # Open a branch of a tree # proc Tree:open {w v} { global Tree if {[info exists Tree($w:$v:open)] && $Tree($w:$v:open)==0 && [info exists Tree($w:$v:children)] && [string length $Tree($w:$v:children)]>0} { set Tree($w:$v:open) 1 Tree:build $w } } proc Tree:openall {w v} { set parent [file dirname $v] if {![string equal $parent $v]} { Tree:openall $w $parent } Tree:open $w $v } proc Tree:close {w v} { global Tree if {[info exists Tree($w:$v:open)] && $Tree($w:$v:open)==1} { set Tree($w:$v:open) 0 Tree:build $w } } proc Tree:closeall {w} { global Tree foreach node [array names Tree *:open] { set Tree($node) 0 } Tree:build $w } # Internal use only. # Draw the selection highlight proc Tree:drawselection w { global Tree if {[string length $Tree($w:selidx)]} { $w delete $Tree($w:selidx) } set v $Tree($w:selection) if {[string length $v]==0} return if {![info exists Tree($w:$v:tag)]} return set bbox [$w bbox $Tree($w:$v:tag)] if {[llength $bbox]==4} { set i [eval $w create rectangle $bbox -fill skyblue -outline {{}}] set Tree($w:selidx) $i $w lower $i } else { set Tree($w:selidx) {} } } # Internal use only # Call Tree:build then next time we're idle proc Tree:buildwhenidle w { global Tree if {![info exists Tree($w:buildpending)]} { set Tree($w:buildpending) 1 after idle "Tree:build $w" } } # # Return the full pathname of the label for widget $w that is located # at real coordinates $x, $y # proc Tree:labelat {w x y} { set x [$w canvasx $x] set y [$w canvasy $y] global Tree foreach m [$w find overlapping $x $y $x $y] { if {[info exists Tree($w:tag:$m)]} { return $Tree($w:tag:$m) } } return "" }