Resolve of the configuration problemsWJG (30/10/06) Following on from the recents comments and requests on this page, I've gathered all the necessary bits together for this simple application so any configuration problems should 'go away'.-----> See older versions of this page for more details on the problems described by users.
#--------------- # treepad.tcl #--------------- # Adapted from TreeCTRLdemo.tcl # by William J Giddings, 2006. # # Description: # ----------- # # Multipage text editor using a treectrl tree widget # to organise the text in a structured manner. # #--------------- #--------------- package require treectrl #--------------- # popupmenu stuff # create some menu icons image create photo im_new -data "R0lGODlhEAAQAMQAAP////f33e/v9+3t1+rr6+fnztzcxtjWvdbOvdTQyMrJubm5qKmqmJiYh4yUiXh4dmZmZFZWVDY2NTIyKSUlIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAEAAQAAAFYGAijklFnmfFmChZVYZBteULxwXrPhByHIZCYYKqECaLn3AQ0ImMAoniUBgwmy5CQOKgWq86Y8SBCFoDaGytAVym0a9nLFh9BwzxEq7+zj+/fU4jFQtpfi0VASs0KYIjIQA7" image create photo im_copy -data R0lGODlhEAAQAIUAAFxaXPwCBNze3GxubERCRPz+/Pz29Pzy5OTe3LS2tAQCBPTq3PTizLyulKyqrOzexLymhLy+vPTy9OzWvLyifMTCxHRydOzSrLyihPz6/OTKpLyabOzu7OTm5MS2nMSqjKSipDQyNJyenLSytOTi5NTS1JyanNTW1JSWlLy6vKyurAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAEALAAAAAAQABAAAAaUQIBwCAgYj0eAYLkcEJBIZWFaGBie0ICUOnBiowKq4YBIKIbJcGG8YDQUDoHTKGU/HhBFpHrVIiQHbQ8TFAoVBRZeSoEIgxcYhhkSAmZKghcXGht6EhwdDmcRHh4NHxgbmwkcCwIgZwqwsbAhCR0CCiIKWQAOCQkjJAolJrpQShK2wicoxVEJKSMqDiAizLuysiF+QQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7 image create photo im_cut -data R0lGODlhEAAQAIEAAPwCBAQCBPz+/ISChCH5BAEAAAAALAAAAAAQABAAAAIwhI9pwaHrGFRBNDdPlYB3bWHQ1YXPtYln+iCpmqCDp6El7Ylsp6ssR1uYSKuW0V8AACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs= image create photo im_delete -data R0lGODlhEAAQAIUAAPwCBFxaXNze3Ly2rJyanPz+/Ozq7GxqbPT29GxubMzOzDQyNIyKjHRydERCROTi3IyKhPz29Ox6bPzCxPzy7PTm3NS6rIQCBMxCNPTq3PTi1PTezMyynPTm1PTaxOzWvMyulOzGrMymhPTq5OzOtNTKxNTOzNTCtNS+rMSehAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaKQAAgQCwahcihYMkcBAiBpLJApRoOBWgyIKhSEQkFgrBAcr1URiPhKAsDD3QB8RhA3FM0IlLHnyUTVBMSFBUWfl0XGBMTGBcZGodmcQWKjpAbHIgIBY2LHRoempOdjooTGx8giIOPFYofISJ+DyMXI6AfFySyfiUmJSUnKBYcICIpfgELzM3OZX5BACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs= image create photo im_paste -data R0lGODlhEAAQAIUAAPwCBCQiFHRqNIx+LFxSBDw6PKSaRPz+/NTOjKyiZDw+POTe3AQCBIR2HPT23Ly2dIR2FMTCxLS2tCQmJKSipExGLHx+fHR2dJyenJyanJSSlERCRGRmZNTW1ERGRNze3GxubBweHMzOzJSWlIyOjHRydPz29MzKzIyKjPTq3Ly2rLy+vISGhPzy5LymhISChPTizOzWvKyurPTexOzSrDQyNHx6fCwuLGxqbOzKpMSabAQGBMS2nLyulMSidAAAACH5BAEAAAAALAAAAAAQABAAAAa7QIBQGBAMCMMkoMAsGA6IBKFZECoWDEbDgXgYIIRIRDJZMigUMKHCrlgul7KCgcloNJu8fsMpFzoZgRoeHx0fHwsgGyEACiIjIxokhAeVByUmG0snkpIbC5YHF4obBREkJCgon5YmKQsqDAUrqiwsrAcmLSkpLrISLC/CrCYOKTAxvgUywhYvGx+6xzM0vjUSNhdvn7zIMdUMNxw4IByKH8fINDk6DABZWTsbYzw9Li4+7UoAHvD+4X6CAAAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7 image create photo im_redo -data R0lGODlhEAAQAIUAAPwCBBxOHBxSHBRGHKzCtNzu3MTSzBQ2FLzSxIzCjCSKFCyeHDzCLAxGHAwuFDSCNBxKLES+NHSmfBQ6FBxWJAQaDAQWFAw+HDSyLJzOnISyjMTexAQOBAwmDAw+FMzizAQODDymNKzWrAQKDAwaDEy6TFTGTFSyXDyKTAQCBAwiFBQyHAwSFAwmHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAZ2QIBwSCwaj0hAICBICgcDQsEgaB4PiIRiW0AEiE3sdsFgcK2CBsCheEAcjgYjoigwJRM2pUK0XDAKGRobDRwKHUcegAsfExUdIEcVCgshImojfEUkCiUmJygHACkqHEQpqKkpogAgK5FOQywtprFDKRwptrZ+QQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7 image create photo im_undo -data R0lGODlhEAAQAIUAAPwCBBxSHBxOHMTSzNzu3KzCtBRGHCSKFIzCjLzSxBQ2FAxGHDzCLCyeHBQ+FHSmfAwuFBxKLDSCNMzizISyjJzOnDSyLAw+FAQSDAQeDBxWJAwmDAQOBKzWrDymNAQaDAQODAwaDDyKTFSyXFTGTEy6TAQCBAQKDAwiFBQyHAwSFAwmHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAZ1QIBwSCwaj0hiQCBICpcDQsFgGAaIguhhi0gohIsrQEDYMhiNrRfgeAQC5fMCAolIDhD2hFI5WC4YRBkaBxsOE2l/RxsHHA4dHmkfRyAbIQ4iIyQlB5NFGCAACiakpSZEJyinTgAcKSesACorgU4mJ6uxR35BACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs= # define package namespace namespace eval popup { set VERSION 0.1 } #define menus, works for cascades too.. set ::popup::menu(main) { {cascade -label "New" -hidemargin 0 -compound left -image im_new -command {.txt1 delete 1.0 end}} {cascade -label "Edit" -menu .edit} {separator} {command -label Exit -command exit} } set ::popup::menu(edit) { {command -label Undo -hidemargin 0 -compound left -image im_undo -command {event generate [focus] <<Undo>>}} {command -label Redo -hidemargin 1 -compound left -image im_redo -command {event generate [focus] <<Redo>>}} {separator} {command -label Cut -compound left -image im_cut -command {event generate [focus] <<Cut>>}} {command -label Copy -compound left -image im_copy -command {event generate [focus] <<Copy>>}} {command -label Paste -compound left -image im_paste -command {event generate [focus] <<Paste>>}} } set ::popup::menu(file) { {command -label Open -command {File:Open .txt}} {command -label Save -command {File:Reload .txt}} {command -label Save -command {File:Save .txt}} } #---------------- # create menu (m) with from list of supplied items (a) #--------------- proc popup::create {m} { set c $m set m ".[string tolower $m]" # destroy any pre-exising menu with the same name destroy $m # create new menus menu $m -tearoff 0 foreach i $popup::menu($c) { eval $m add $i } } #--------------- # display the popup menu adjacent to the current pointer location #--------------- proc popup::show {w m} { set m ".[string tolower $m]" # set w [winfo parent $m] # lassign [winfo pointerxy $w] x y foreach {x y} [winfo pointerxy $w] {} set ::active(tag) $m #get active ta tk_popup $m $x $y } #--------------- # treepad stuff itself #--------------- #--------------- # some basic tree graphics #--------------- image create photo help-book-closed -data { R0lGODlhEAAQACIAACwAAAAAEAAQAIIAAAB/AH9/f3+/v7////8AAAAAAAAA AAADQEi6BMBwuRBeVJNSy7LWXDN8ZCUFYwliVKqagOaWTizTgMDeW07ou5ZD MCAMHKeNrngMNTbGhvOUQ14k1quWkQAAOw== } image create photo help-book-open -data { R0lGODlhEAAQACIAACwAAAAAEAAQAIIAAAB/AH9/f3///wC/v7////8AAAAA AAADTVgl2v6CsEdBKNKJ7aya3NJdWFgMAgAoHkucXxGsbQG8CirTpP0OsZmt d2vohLUiUIQMkIqfl3B4KW5w06Ht6shSnWDwqqMqm8eUtCIBADs= } image create photo small-txt -data { R0lGODlhEAAQALIAAAAAAAAAMwAAZgAAmQAAzAAA/wAzAAAzMyH5BAUAAAIA LAAAAAAQABAAggAAAH9/f/8AAL+/v////wAAAAAAAAAAAANAKArE3ioKFki9 MNbHs6hEKIoDoI0oUZ4N4DCqqYBpuM6hq8P3V5MyX2tnC9JqPdDOVWT9kr/m bECtWnuT5TKSAAAh/oBUaGlzIGFuaW1hdGVkIEdJRiBmaWxlIHdhcyBjb25z dHJ1Y3RlZCB1c2luZyBVbGVhZCBHSUYgQW5pbWF0b3IgTGl0ZSwgdmlzaXQg dXMgYXQgaHR0cDovL3d3dy51bGVhZC5jb20gdG8gZmluZCBvdXQgbW9yZS4B VVNTUENNVAAh/wtQSUFOWUdJRjIuMAdJbWFnZQEBADs= } # custom namespace namespace eval tree { variable path variable text variable active variable fname untitled.dat variable lines yes variable stripeClr #f2f8ff } #--------------- # rename the item tree label #--------------- proc tree:rename {path active x y} { # get the scrren position of the text box foreach {x1 y1 x2 y2} [$path item bbox $active colItem elemTxtName] {} # create entry then position it over tree text enrty entry $path.rename -borderwidth 1 -relief solid -background #ffffdd $path.rename insert 0 [ $path item element cget $active colItem elemTxtName -text ] # minimum width for entry, just in case text = " " set w [expr $x2-$x1] if {$w <50} {set w 50} place $path.rename \ -x [incr x1 -5] \ -y [incr y1 -5] \ -width $w \ -height [expr $y2-$y1] # edit it focus -force $path.rename # close and update when focus changes or return pressed bind $path.rename <Key-Return> { tree:rename:validate %W $::tree::path $tree::active } bind $path.rename <FocusOut> { tree:rename:validate %W $::tree::path $tree::active } } #--------------- # update tree, then destroy entry widget #--------------- proc tree:rename:validate {w path active} { # do not permit empty fields, must be a space set str [$w get] if {$str==""} {set str " "} $path item element configure $active colItem elemTxtName -text $str destroy $w # return focus back to the tree widget focus $path } #--------------- # tree:init #--------------- proc tree:init {T} { # Get environment default colors set w [listbox .listbox] set SystemHighlightText [$w cget -selectforeground] set SystemHighlight [$w cget -selectbackground] destroy $w # determine row height set height [font metrics [$T cget -font] -linespace] if {$height < 18} { set height 18 } # configure the treectrl widget $T configure \ -itemheight $height \ -selectmode single \ -showroot yes \ -showrootbutton yes \ -showbuttons yes \ -showlines $::tree::lines \ -scrollmargin 16 \ -xscrolldelay "500 50" \ -yscrolldelay "500 50" # Create columns.. $T column create \ -expand yes \ -text Item \ -itembackground "$::tree::stripeClr {}" \ -tag colItem # then configure $T configure -treecolumn colItem # Create elements $T element create elemImgFolder image -image {help-book-open {open} help-book-closed {}} $T element create elemImgFile image -image small-txt $T element create elemTxtName text -fill [list $SystemHighlightText {selected focus}] $T element create elemTxtCount text -fill blue $T element create elemTxtAny text $T element create elemRectSel rect -showfocus yes -fill [list $SystemHighlight {selected focus} gray {selected !focus}] # Create styles using the elements set S [$T style create styFolder] $T style elements $S {elemRectSel elemImgFolder elemTxtName elemTxtCount} $T style layout $S elemImgFolder -padx {0 4} -expand ns $T style layout $S elemTxtName -padx {0 4} -expand ns $T style layout $S elemTxtCount -padx {0 6} -expand ns $T style layout $S elemRectSel -union [list elemTxtName] -iexpand ns -ipadx 2 set S [$T style create styFile] $T style elements $S {elemRectSel elemImgFile elemTxtName} $T style layout $S elemImgFile -padx {0 4} -expand ns $T style layout $S elemTxtName -padx {0 4} -expand ns $T style layout $S elemRectSel -union [list elemTxtName] -iexpand ns -ipadx 2 set S [$T style create styAny] $T style elements $S {elemTxtAny} $T style layout $S elemTxtAny -padx 6 -expand ns TreeCtrl::SetSensitive $T { {colItem styFolder elemRectSel elemImgFolder elemTxtName} {colItem styFile elemRectSel elemImgFile elemTxtName} } TreeCtrl::SetDragImage $T { {colItem styFolder elemImgFolder elemTxtName} {colItem styFile elemImgFile elemTxtName} } # Add bindings bind tree:init <Double-ButtonPress-1> { TreeCtrl::DoubleButton1 %W %x %y break } bind tree:init <Control-ButtonPress-1> { set TreeCtrl::Priv(selectMode) toggle tree:button1 %W %x %y break } bind tree:init <Shift-ButtonPress-1> { set TreeCtrl::Priv(selectMode) add tree:button1 %W %x %y break } bind tree:init <ButtonPress-1> { set TreeCtrl::Priv(selectMode) set tree:button1 %W %x %y break } bind tree:init <Button1-Motion> { tree:motion1 %W %x %y break } bind tree:init <ButtonRelease-1> { tree:release1 %W %x %y break } bindtags $T [list $T tree:init TreeCtrl [winfo toplevel $T] all] return } #--------------- # toggle tree lines #--------------- proc tree:showlines {T} { if { [$T cget -showlines] } { $T configure -showlines no } else { $T configure -showlines yes } } #--------------- # choose view style, tree or collapser #--------------- proc tree:style {T {style tree}} { switch [string tolower $style] { collapser { set lines no set images "\{mac-collapse open mac-expand {}\}" } tree { set lines yes set images "\{\}" } default { return } } # apply the changes eval $T configure \ -showlines $lines \ -showbuttons yes \ -buttonimage $images } #--------------- # tree:button1 #--------------- proc tree:button1 {T x y} { variable TreeCtrl::Priv focus $T set id [$T identify $x $y] set Priv(buttonMode) "" # Click outside any item if {$id eq ""} { $T selection clear # Click in header } elseif {[lindex $id 0] eq "header"} { TreeCtrl::ButtonPress1 $T $x $y # Click in item } else { foreach {where item arg1 arg2 arg3 arg4} $id {} switch $arg1 { button { $T item toggle $item } line { $T item toggle $arg2 } column { set ok 0 # Clicked an element if {[llength $id] == 6} { set column [lindex $id 3] set E [lindex $id 5] foreach list $Priv(sensitive,$T) { set C [lindex $list 0] set S [lindex $list 1] set eList [lrange $list 2 end] if {[$T column compare $column != $C]} continue if {[$T item style set $item $C] ne $S} continue if {[lsearch -exact $eList $E] == -1} continue set ok 1 break } } if {!$ok} { $T selection clear return } set Priv(drag,motion) 0 set Priv(drag,click,x) $x set Priv(drag,click,y) $y set Priv(drag,x) [$T canvasx $x] set Priv(drag,y) [$T canvasy $y] set Priv(drop) "" if {$Priv(selectMode) eq "add"} { TreeCtrl::BeginExtend $T $item } elseif {$Priv(selectMode) eq "toggle"} { TreeCtrl::BeginToggle $T $item } elseif {![$T selection includes $item]} { TreeCtrl::BeginSelect $T $item } $T activate $item if {[$T selection includes $item]} { set Priv(buttonMode) drag } } } } return } #--------------- # tree:motion1 #--------------- proc tree:motion1 {T x y} { variable TreeCtrl::Priv switch $Priv(buttonMode) { "drag" { set Priv(autoscan,command,$T) {tree:motion %T %x %y} TreeCtrl::AutoScanCheck $T $x $y tree:motion $T $x $y } default { TreeCtrl::Motion1 $T $x $y } } return } #--------------- # motion #--------------- proc tree:motion {T x y} { variable TreeCtrl::Priv switch $Priv(buttonMode) { "drag" { if {!$Priv(drag,motion)} { # Detect initial mouse movement if {(abs($x - $Priv(drag,click,x)) <= 4) && (abs($y - $Priv(drag,click,y)) <= 4)} return set Priv(selection) [$T selection get] set Priv(drop) "" $T dragimage clear # For each selected item, add 2nd and 3rd elements of # column "item" to the dragimage foreach I $Priv(selection) { foreach list $Priv(dragimage,$T) { set C [lindex $list 0] set S [lindex $list 1] if {[$T item style set $I $C] eq $S} { eval $T dragimage add $I $C [lrange $list 2 end] } } } set Priv(drag,motion) 1 } # Find the item under the cursor set cursor X_cursor set drop "" set id [$T identify $x $y] set ok 0 if {($id ne "") && ([lindex $id 0] eq "item") && ([llength $id] == 6)} { set item [lindex $id 1] set column [lindex $id 3] set E [lindex $id 5] foreach list $Priv(sensitive,$T) { set C [lindex $list 0] set S [lindex $list 1] set eList [lrange $list 2 end] if {[$T column compare $column != $C]} continue if {[$T item style set $item $C] ne $S} continue if {[lsearch -exact $eList $E] == -1} continue set ok 1 break } } if {$ok} { # If the item is not in the pre-drag selection # (i.e. not being dragged) see if we can drop on it if {[lsearch -exact $Priv(selection) $item] == -1} { set drop $item # We can drop if dragged item isn't an ancestor foreach item2 $Priv(selection) { if {[$T item isancestor $item2 $item]} { set drop "" break } } if {$drop ne ""} { scan [$T item bbox $drop] "%d %d %d %d" x1 y1 x2 y2 if {$y < $y1 + 3} { set cursor top_side set Priv(drop,pos) prevsibling } elseif {$y >= $y2 - 3} { set cursor bottom_side set Priv(drop,pos) nextsibling } else { set cursor "" set Priv(drop,pos) lastchild } } } } if {[$T cget -cursor] ne $cursor} { $T configure -cursor $cursor } # Select the item under the cursor (if any) and deselect # the previous drop-item (if any) $T selection modify $drop $Priv(drop) set Priv(drop) $drop # Show the dragimage in its new position set x [expr {[$T canvasx $x] - $Priv(drag,x)}] set y [expr {[$T canvasy $y] - $Priv(drag,y)}] $T dragimage offset $x $y $T dragimage configure -visible yes } default { TreeCtrl::Motion1 $T $x $y } } return } #--------------- # release the dragged item #--------------- proc tree:release1 {T x y} { variable TreeCtrl::Priv if {![info exists Priv(buttonMode)]} return switch $Priv(buttonMode) { "drag" { TreeCtrl::AutoScanCancel $T $T dragimage configure -visible no $T selection modify {} $Priv(drop) $T configure -cursor "" if {$Priv(drop) ne ""} { tree:drop $T $Priv(drop) $Priv(selection) $Priv(drop,pos) } unset Priv(buttonMode) } default { TreeCtrl::Release1 $T $x $y } } return } #--------------- # drop the dragged item #--------------- proc tree:drop {T target source pos} { set parentList {} switch -- $pos { lastchild { set parent $target } prevsibling { set parent [$T item parent $target] } nextsibling { set parent [$T item parent $target] } } foreach item $source { # Ignore any item whose ancestor is also selected set ignore 0 foreach ancestor [$T item ancestors $item] { if {[lsearch -exact $source $ancestor] != -1} { set ignore 1 break } } if {$ignore} continue # Update the old parent of this moved item later if {[lsearch -exact $parentList $item] == -1} { lappend parentList [$T item parent $item] } # Add to target $T item $pos $target $item # Recursively update text: depth set itemList [$T item firstchild $item] while {[llength $itemList]} { # Pop set item [lindex $itemList end] set itemList [lrange $itemList 0 end-1] set item2 [$T item nextsibling $item] if {$item2 ne ""} { # Push lappend itemList $item2 } set item2 [$T item firstchild $item] if {$item2 ne ""} { # Push lappend itemList $item2 } } } # Update items that lost some children foreach item $parentList { set numChildren [$T item numchildren $item] if {$numChildren == 0} { $T item configure $item -button no $T item style map $item colItem styFile {elemTxtName elemTxtName} } else { $T item element configure $item colItem elemTxtCount -text "($numChildren)" } } # Update the target that gained some children if {[$T item style set $parent colItem] ne "styFolder"} { $T item configure $parent -button yes $T item style map $parent colItem styFolder {elemTxtName elemTxtName} } set numChildren [$T item numchildren $parent] $T item element configure $parent colItem elemTxtCount -text "($numChildren)" return } #--------------- # create the root #--------------- proc tree:addRoot {w txt} { global ${w}_data $w item configure root -button yes $w item style set root colItem styFolder $w item element configure root colItem elemTxtName -text $txt set ${w}_data(0) "ROOT DATA" } #--------------- # create the root #--------------- proc tree:showroot {w} { if {[$w cget -showroot]} { $w configure -showroot no -showrootbutton no } else { $w configure -showroot yes -showrootbutton yes } } #--------------- # create new entry #--------------- proc tree:addItem {w txt {parent 0} {data NEW}} { global ${w}_data set first yes if {[$w item children $parent] !=""} { set first no} set item [$w item create] $w item style set $item colItem styFile $w item element configure $item colItem elemTxtName -text $txt $w item lastchild $parent $item if {$first} { set str [ $w item element cget $parent colItem elemTxtName -text ] # update root if necessary $w item configure $parent -button yes $w item style set $parent colItem styFolder $w item element configure $parent colItem elemTxtName -text $str } set ${w}_data($item) $data return $item } #--------------- # delete tree item and associated data #--------------- proc tree:deleteItem {w i} { global ${w}_data # delete item, $w item delete $i # determine the differences between the tree and data lists # http://wiki.tcl.tk/15489 foreach i [array names ${w}_data] { if {[lsearch -exact [$w item range first last] $i]==-1} { lappend diff $i } } # reconcile two lists by deleting unwanted data entries array unset ${w}_data $diff } #--------------- # dump all values #--------------- proc tree:dump {w} { global ${w}_data foreach i [$w item range first last] { if {$i==""} {set item root} set parent [$w item parent $i] set children [$w item children $i] set txt [ $w item element cget $i colItem elemTxtName -text ] set data [set ${w}_data($i)] append j "\{Item#$i \{$parent\} \{$children\} \{$txt\} \{$data\}\}\n" } return $j } #--------------- # save treectrl contents #--------------- proc tree:save {w fname} { global ${w}_info set fp [open $fname "w"] # first entry is a file info block puts $fp "\{[array get ${w}_info]\}" # the tree and data puts $fp [tree:dump $w] close $fp } #--------------- # load treectrl contents #--------------- proc tree:load {w fname} { global ${w}_data ${w}_info # delete existing data $w item delete all array unset ${w}_info # open file set fp [open $fname "r"] set str [read $fp] # extract the info block, this is always list item 0 array set ${w}_info [lindex $str 0] for {set i 1} {$i <= [llength $str]} {incr i} { # now follows the actual data foreach {item parent children text data} [lindex $str $i] { if {$parent == ""} { # must be root tree:addRoot $w $text set ${w}_data(0) $data } else { # any other item catch { tree:addItem $w $text $parent $data } } } } close $fp } #--------------- # add a new item #--------------- proc tree:new {w t} { global ${w}_data #$w item delete first last $w item delete all $t delete 1.0 end array unset ${w}_data [array names ${w}_data] tree:addRoot $w Root tree:addItem $w page 0 } #--------------- # change the displayed item # a active item # p previus item #--------------- proc tree:show {t w a p} { global ${w}_data # save old data set ${w}_data($p) [$t get 1.0 end-1c] # show new data $t delete 1.0 end $t insert end [set ${w}_data($a)] } #--------------- # the ubiquitous demo #--------------- proc treepad { {base {}} } { global i path if {$base=="."} {set base ""} # create paned window to hold tree and text panedwindow ${base}.pane pack ${base}.pane -side top -expand yes -fill both # give path default value set ::tree::path ${base}.pane.tree treectrl $::tree::path \ -width 200 -height 300 \ -showrootbutton no \ -showbuttons yes \ -showlines yes \ -selectmode extended set tree::text ${base}.pane.txt text $tree::text -font {Times 12} -background #f8f8f8 -undo true # add to panes ${base}.pane add $tree::path $tree::text # binding to set active item $::tree::path notify bind $::tree::path <ActiveItem> { set tree::path %W set tree::active %c set x [winfo pointerx %W] set y [winfo pointery %W] tree:show $::tree::text %W %c %p } # a simple counter set i 0 # modify menus to suit application set ::popup::menu(main) { {cascade -label "Insert" -hidemargin 0 -command { tree:addItem $tree::path Item_A[incr i] $tree::active }} {command -label "Delete" -command {tree:deleteItem $tree::path $tree::active}} {command -label "Rename" -command { tree:rename $tree::path $tree::active $x $y}} {separator} {command -label "'Collapser'" -command {tree:style $tree::path collapser}} {command -label "'Tree'" -command {tree:style $tree::path tree}} {separator} {command -label "Toggle Lines" -command {tree:showlines $tree::path}} {command -label "Show Root" -command {tree:showroot $tree::path}} {separator} {command -label "New" -command { tree:new $tree::path $tree::text }} {command -label "Load Tree.." -command { set tree::fname [tk_getOpenFile \ -defaultextension {.dat} \ -initialdir . \ -filetypes {{{Tree Text} *.dat Text} {all *.* Text} } \ -initialfile $tree::fname \ -title "Load File..."] tree:load $tree::path $tree::fname }} {command -label "Save Tree.." -command { set tree::fname [tk_getSaveFile \ -defaultextension {.dat} \ -initialdir . \ -filetypes {{{Tree Text} *.dat Text} {all *.* Text} } \ -initialfile $tree::fname \ -title "Save File..."] tree:save $tree::path $tree::fname }} } # add a couple of items to the 'standard' edit popup append ::popup::menu(edit) { {separator} {command -label "Insert File.." -command { set tmp [tk_getOpenFile \ -defaultextension {.txt} \ -initialdir . \ -filetypes {{{Tree Text} *.txt Text} {all *.* Text} } \ -initialfile {} \ -title "Insert File..."] set fp [open $tmp r] $tree::text insert insert [read $fp] close $fp }} } # initlialise the popup menus.. popup::create main popup::create edit # assign bindings.. bind $tree::path <Button-3> {popup::show %W main} bind $tree::text <Button-3> {popup::show %W edit} tree:init $tree::path tree:new $tree::path $tree::text } #--------------- # run application #--------------- treepad
SEH 20061031 -- When I try to execute the above script, I get the following error:
expected integer but got "colItem" (processing "-treecolumn" option) invoked from within "$T configure -treecolumn colItem" (procedure "tree:init" line 15) invoked from within "tree:init $tree::path" (procedure "treepad" line 74) invoked from within "treepad"
Exe please![unperson] I like your ideas, William. Well, you are writing your own editors for writing your thesis on Eastern Religion. As they say: necessity is the mother of invention'. This is also the way I work.Is TreePad available as an exe that could run with Windows 98 or with Windows XP? If so, I'd love to try it! I have been looking for an outliner since the days of the Majestic GrandView on Dos but I can't seem to find one as good and moreover as intuitive (very important characteristic!).If you want easy instructions to produce an exe, see here: http://wiki.tcl.tk/11861Thanks in advance!WJG (30 Oct 06) I now have the above wrapped as a starkit, I'll email you a copy.[unperson] No, please! Starkits don't work with my OS. Please make an exe following the instructions posted here: How to compile a TCL script into an EXE programMany thanks!