
# ======================================================================
#
# treeview.tcl
#
# ----------------------------------------------------------------------
# Bindings for the BLT treeview widget
# ----------------------------------------------------------------------
#
#   AUTHOR:  George Howlett
#            Bell Labs Innovations for Lucent Technologies
#            gah@lucent.com
#            http://www.tcltk.com/blt
#
#      RCS:  $Id: treeview.tcl,v 1.24 2010/05/06 22:26:17 pcmacdon Exp $
#
# ----------------------------------------------------------------------
# Copyright (c) 1998  Lucent Technologies, Inc.
# ----------------------------------------------------------------------
#
# 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 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.
#
# ======================================================================

namespace eval ::blt::tv {
    variable afterId ""
    variable scroll 0
    variable column ""
    variable space   off
    variable x 0
    variable y 0
    variable script [info script]
    variable dirname [file dirname [info script]]
    if {[info exists ::tcl_warn(level)] && $::tcl_warn(level)} {
       source [file join $dirname tvutil.tcl]
    } else {
       set ::auto_index(::blt::tv::TableWid) [list source [file join $dirname tvutil.tcl]]
       set ::auto_index(::blt::tv::TableLoad) [list source [file join $dirname tvutil.tcl]]
       set ::auto_index(::blt::tv::TreeLoad) [list source [file join $dirname tvutil.tcl]]
       set ::auto_index(::blt::tv::TreeDump) [list source [file join $dirname tvutil.tcl]]
       set ::auto_index(::blt::tv::TreeFill) [list source [file join $dirname tvutil.tcl]]
}
    }


image create photo blt::tv::normalCloseFolder -format gif -data {
    R0lGODlhEAANAMIAAAAAAH9/f///////AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
    AAM8WBrM+rAEQWmIb5KxiWjNInCkV32AJHRlGQBgDA7vdN4vUa8tC78qlrCWmvRKsJTquHkp
    ZTKAsiCtWq0JADs=
}
image create photo blt::tv::normalOpenFolder -format gif -data {
    R0lGODlhEAANAMIAAAAAAH9/f///////AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
    AAM1WBrM+rAEMigJ8c3Kb3OSII6kGABhp1JnaK1VGwjwKwtvHqNzzd263M3H4n2OH1QBwGw6
    nQkAOw==
}
image create photo blt::tv::activeCloseFolder -format gif -data {
    R0lGODlhEAANAMIAAAAAAH9/f/////+/AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
    AAM8WBrM+rAEQWmIb5KxiWjNInCkV32AJHRlGQBgDA7vdN4vUa8tC78qlrCWmvRKsJTquHkp
    ZTKAsiCtWq0JADs=
}
image create photo blt::tv::activeOpenFolder -format gif -data {
    R0lGODlhEAANAMIAAAAAAH9/f/////+/AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
    AAM1WBrM+rAEMigJ8c3Kb3OSII6kGABhp1JnaK1VGwjwKwtvHqNzzd263M3H4n2OH1QBwGw6
    nQkAOw==
}

image create photo blt::tv::normalFile -format gif -data {
    R0lGODlhFAAQAMIAAP///wAAALq2VYKCgtvb2wAAAAAAAAAAACH5BAEAAAAA
    LAAAAAAUABAAAAM7CLrcriHK8BxlsWIgOqCXFkKkyHnT2KjYUFFdjLoPuwS0
    dcrCl6+i34Y3ewWNL+EtlVIuBtCodEBgJAAAOw==
}

image create photo blt::tv::openFile -format gif -data {
R0lGODlhEQAQAIUAAPwCBFxaXNze3Ly2rJyanPz+/Ozq7GxqbPz6/PT29HRy
dMzOzDQyNExGBIyKhERCRPz+hPz+BPz29MTCBPzy7PTq3NS+rPz+xPz27Pzu
5PTi1My2pPTm1PTezPTm3PTaxMyynMyqjPTizOzWvOzGrOTi3OzOtMSehNTK
xNTCtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAARABAAAAaj
QIBQGCgajcMkICBoNgcBQkBJFBSuBcMBIaUysYWEYsFlKBtWsENxeJgBDTik
UaBfE4KBw9yYNyINfXYFEnlvfxMRiYMSFBUWfA0TFxAXE4EFGBkVGhsMfRER
EAUQoXObHB2ecJKUloEJHB4aHyCHirgNGRmzHx8hfH6Agh4iHyMkwEJxghkN
HCXHJiQnb0MNCwsoKRYbICEh1UoBDOXm5wx2QQA7
}

image create photo blt::tv::empty

image create photo blt::tv::downarrow -format gif -data {
R0lGODlhEQAJAPABAAAAAP///yH5BAEAAAEALAAAAAARAAkAAAJXTJgwYcKE
CRMmTJgQIECAAAEiTJgwIUCAAAEiTJgwYUKAAAEiTJgwYcKEAAEiTJgwYcKE
CQEiTJgwYcKECRMiTJgwYcKECRMmTJgwYcKECRMmTJgwYcIUADs=
}

image create photo blt::tv::rightarrow -format gif -data {
R0lGODlhEAAQAIAAAPwCBAQCBCH5BAEAAAAALAAAAAAQABAAAAIdhI+pyxCt
woNHTmpvy3rxnnwQh1mUI52o6rCu6hcAIf5oQ3JlYXRlZCBieSBCTVBUb0dJ
RiBQcm8gdmVyc2lvbiAyLjUNCqkgRGV2ZWxDb3IgMTk5NywxOTk4LiBBbGwg
cmlnaHRzIHJlc2VydmVkLg0KaHR0cDovL3d3dy5kZXZlbGNvci5jb20AOw==
}

image create photo blt::tv::ball -format gif -data {
R0lGODlhEQAJAPABAAAAAP///yH5BAEAAAEALAAAAAARAAkAAAJXTJgwYcKE
CRMmTJgwYcKECRMmTJgwYcKAABMmTJgwYcKAAAEmTJgwYcKEAAEiTJgwYcKE
CQEiTJgwYcKECRMmTJgwYcKECRMmTJgwYcKECRMmTJgwYcIUADs=
}

# Seems to be a memory leak in @cursors and another in binds.
if { $tcl_platform(platform) == "windows" } {
    if { $tk_version >= 8.3  && ![string match /zvfs* $blt_library]} {
	set cursor "@[file join $blt_library treeview.cur]"
    } else {
	set cursor "size_we"
    }
    option add *${className}.ResizeCursor [list $cursor]
} else {
    option add *${className}.ResizeCursor \
	"@$blt_library/treeview.xbm $blt_library/treeview_m.xbm black white"
}

# ----------------------------------------------------------------------
#
# Initialize --
#
#	Invoked by internally by Treeview_Init routine.  Initializes
#	the default bindings for the treeview widget entries.  These
#	are local to the widget, so they can't be set through the
#	widget's class bind tags.
#
#       TODO: get rid of most of this in favor of class binds.
#
# ----------------------------------------------------------------------
variable ::blt::tv::oldedit 0
proc ::blt::tv::Initialize { w } {
    #
    # Active entry bindings
    #
    variable oldedit
    $w bind Entry <Enter> { 
	%W entry activate current 
    }
    $w bind Entry <Leave> { 
	%W entry activate "" 
    }

    #
    # Button bindings
    #
    $w button bind all <ButtonRelease-1> {
	blt::tv::Toggle %W current
    }
    $w button bind all <Enter> {
	%W button activate current
    }
    $w button bind all <Leave> {
	%W button activate ""
    }

    #
    # ButtonPress-1
    #
    #	Performs the following operations:
    #
    #	1. Clears the previous selection.
    #	2. Selects the current entry.
    #	3. Sets the focus to this entry.
    #	4. Scrolls the entry into view.
    #	5. Sets the selection anchor to this entry, just in case
    #	   this is "multiple" mode.
    #
    
    $w bind Entry <ButtonPress-1> { 	
	blt::tv::SetSelectionSetAnchor %W %x %y
	set blt::tv::scroll 1
    }

    #$w bind Entry <Double-ButtonPress-1> { %W toggle current }

    #
    # B1-Motion
    #
    #	For "multiple" mode only.  Saves the current location of the
    #	pointer for auto-scrolling.  Resets the selection mark.  
    #
    $w bind Entry <B1-Motion> { 
	set blt::tv::x %x
	set blt::tv::y %y
	set index [%W nearest %x %y]
	if { [%W cget -selectmode] == "multiple" } {
	    %W selection mark $index
	} elseif { [%W cget -selectmode] != "none" } {
	    blt::tv::SetSelectionAnchor %W $index
	}
    }

    #
    # ButtonRelease-1
    #
    #	For "multiple" mode only.  
    #
    $w bind Entry <ButtonRelease-1> { 
	if { [%W cget -selectmode] == "multiple" } {
	    %W selection anchor current
	}
	after cancel $blt::tv::afterId
	set blt::tv::scroll 0
    }

    #
    # Shift-ButtonPress-1
    #
    #	For "multiple" mode only.
    #

    $w bind Entry <Shift-ButtonPress-1> { 
        blt::tv::SetSelectionExtendAnchor %W %x %y
    }
    $w bind Entry <Shift-Double-ButtonPress-1> {
	# do nothing
    }
    $w bind Entry <Shift-B1-Motion> { 
	# do nothing
    }
    $w bind Entry <Shift-ButtonRelease-1> { 
	after cancel $blt::tv::afterId
	set blt::tv::scroll 0
    }

    #
    # Control-ButtonPress-1
    #
    #	For "multiple" mode only.  
    #
    $w bind Entry <Control-ButtonPress-1> { 
        blt::tv::SetSelectionAdd %W %x %y
    }
    $w bind Entry <Control-Double-ButtonPress-1> {
	# do nothing
    }
    $w bind Entry <Control-B1-Motion> { 
	# do nothing
    }
    $w bind Entry <Control-ButtonRelease-1> { 
	after cancel $blt::tv::afterId
	set blt::tv::scroll 0
    }

    $w bind Entry <Control-Shift-ButtonPress-1> { 
	if { [%W cget -selectmode] == "multiple" && [%W selection present] } {
	    if { [%W index anchor] == "" } {
		%W selection anchor current
	    }
	    if { [%W selection includes anchor] } {
		%W selection set anchor current
	    } else {
		%W selection clear anchor current
		%W selection set current
	    }
	} elseif { [%W cget -selectmode] != "none" } {
	    blt::tv::SetSelectionAnchor %W current
	}
    }
    $w bind Entry <Control-Shift-Double-ButtonPress-1> {
	# do nothing
    }
    $w bind Entry <Control-Shift-B1-Motion> { 
	# do nothing
    }

    $w bind Entry <Shift-ButtonPress-3> { 
	blt::tv::EditColumn %W %X %Y
    }

    $w column bind all <Enter> {
	%W column activate [%W column current]
    }
    $w column bind all <Leave> {
	%W column activate ""
    }
    $w column bind Rule <Enter> {
	%W column activate [%W column current]
	%W column resize activate [%W column current]
    }
    $w column bind Rule <Leave> {
	%W column activate ""
	%W column resize activate ""
    }
    $w column bind Rule <ButtonPress-1> {
	%W column resize anchor %x
    }
    $w column bind Rule <B1-Motion> {
	%W column resize mark %x
    }
    $w column bind Rule <ButtonRelease-1> {
	%W column configure [%W column current] -width [%W column resize set]
    }
    set ::blt::tv::curRelief raised
    $w column bind all <ButtonPress-1> {
	set blt::tv::column [%W column current]
	set blt::tv::curRelief [%W column cget $blt::tv::column -titlerelief]
	%W column configure $blt::tv::column -titlerelief sunken
    }
    $w column bind all <ButtonRelease-1> {
	set column [%W column current]
	if { $column != "" } {
	    %W column invoke $column
	}
         %W column configure $blt::tv::column -titlerelief $blt::tv::curRelief
    }
    if {$oldedit} {
    $w bind TextBoxStyle <Alt-ButtonPress-3> { 
	if { [%W edit -root -test %X %Y] } {
	    break
	}
    }
    $w bind TextBoxStyle <Shift-ButtonRelease-1> { 
        if { [%W edit -root -test %X %Y] } {
            blt::tv::EditColumn %W %X %Y
            break
        }
    }
    $w bind TextBoxStyle <Double-1> { 
	if { [%W edit -root -test %X %Y] } {
	    blt::tv::EditColumn %W %X %Y
	    break
	}
    }
    }
#    $w bind CheckBoxStyle <Enter> { 
#	set column [%W column current]
#	if { [%W column cget $column -edit] } {
#	    %W style activate current $column
#	} 
#    }
#    $w bind CheckBoxStyle <Leave> { 
#	%W style activate ""
#    }
    $w bind CheckBoxStyle <ButtonPress-1> { 
        if { [%W edit -root -test %X %Y] } {
            event generate %W <<TreeViewEditStart>> -x [%W col index [%W col current]] -y [%W index @%x,%y]
            break
        }
    }
    $w bind CheckBoxStyle <B1-Motion> { 
	if { [%W column cget [%W column current] -edit] } {
	    break
	}
    }
    $w bind CheckBoxStyle <ButtonRelease-1> { 
	if { [%W edit -root -test %X %Y] } {
	    %W edit -root %X %Y
            event generate %W <<TreeViewEditEnd>> -x [%W col index [%W col current]] -y [%W index @%x,%y]
	    break
	}
    }
    if 0 {
    $w bind ComboBoxStyle <ButtonPress-1> { 
	set column [%W column current]
        %W style activate focus $column
	if { [%W column cget $column -edit] } {
	    break
	}
    }
    $w bind ComboBoxStyle <ButtonRelease-1> { 
        %W style activate 0
	if { [%W edit -root -test %X %Y] } {
	    %W edit -root %X %Y
	    break
	}
    }
    
    $w bind ComboBoxStyle <Double-1> { 
        if { [%W edit -root -test %X %Y] } {
            blt::tv::EditColumn %W %X %Y
            break
        }
    }
}
    if {$oldedit} {
    $w bind ComboBoxStyle <Shift-ButtonRelease-1> { 
        if { [%W edit -root -test %X %Y] } {
            blt::tv::EditColumn %W %X %Y
            break
        }
    }
    
    } else {
    $w bind ComboBoxStyle <ButtonPress-1> {}
    }
    $w bind ComboBoxStyle <ButtonRelease-1> {
        if { [%W edit -root -test %X %Y] } {
           blt::tv::Combobox-List %W %X %Y
        }
    }

}

    
proc ::blt::tv::Combobox-List-Done {W entry col args} {
    set i [$W index active]
    set v [$W get $i]
    set l [winfo parent $W]
    set w [winfo parent $l]
    $w entry set $entry $col $v
    event generate $w <<TreeViewEditEnd>> -x [$w col index $col] -y $entry
    destroy $l
    $w style activate 0
}

proc ::blt::tv::Combobox-List-Close {l} {
    set w [winfo parent $l]
    destroy $l
    $w style activate 0
}

proc ::blt::tv::Combobox-List {w x y} {
    # Popup combo-list for combobox.
    # TODO: could put frame in toplevel so not clipped.
    set Opts {
        { -height   6      "Listbox height" }
        { -leafs    False   "Edit only leaf nodes" }
        { -leave    1       "Setup handler for leave" }
        { -conf     {}      "Listbox widget configuration options" }
        { -optscmd  {}      "Callback to get edit options" -type {cmd w r c} }
        { -readonly False   "Do not allow editing" }
        { -useframe 1       "Use a frame below treeview widget" }
        { -usetopframe 0    "Use a frame at toplevel" }
        { -withouttag {}    "Edit only entries without tag"}
        { -withtag  {}      "Edit only entries with tag"}
    }
    if {[winfo exists $w.edit]} return
    set lx [expr {$x-[winfo rootx $w]}]
    set ly [expr {$y-[winfo rooty $w]}]
    set ind [$w index @$lx,$ly]
    #if {[llength [set lst [$w cget -values]]] == 0} return
    set col [$w column current]
    if {![$w column cget $col -edit]} return
    set widopts [$w column cget $col -editopts]
    set cellstyle [lindex [$w style get $col $ind] 0]
    if {$cellstyle != {}} {
         if {[$w style cget $cellstyle -readonly]} return
         set widopts [concat $widopts [$w style cget $cellstyle -editopts]]
    }
    set edopts {}
    foreach i $Opts {
        set q([lindex $i 0]) [lindex $i 1]
        lappend edopts [lindex $i 0] [lindex $i 1]
    }
    set opts { -activestyle dotbox -bd 2 -pad 10 -relief sunken -selectmode single }
    set style $cellstyle
    if {$style == {}} {
      set style [$w column cget $col -style]
    }
    set ckey [$w style cget $style -choicekey]
    set cmd [$w style cget $style -choicecmd]
    set lst [$w style cget $style -choices]
    if {$ckey != {}} {
        set lst [$w entry get $ind $ckey {}]
    }
    if {$cmd != {} && $lst == {}} {
        set cmd [string map [list %% % %W $w %X $x %Y $y %# $ind %C $col] $cmd]
        set lst [namespace eval :: $cmd]
    }
    set offs [$w column offsets]
    set cind [$w column index $col]
    set xstart [lindex $offs $cind]
    if {$cind >= ([llength $offs]-1)} {
        set xend [winfo width $w]
    } else {
        set xend [lindex $offs [expr {$cind+1}]]
    }
    if {$q(-optscmd) != {}} {
        set ewopts [eval $q(-optscmd) $w $ind $col]
        if {[llength $ewopts]%2} {
           tclLog "TreeView -optscmd: odd length: '$ewopts' for $w"
        } else {
           array set q $ewopts
        }
    }
    if {$q(-readonly)} return
    if {$q(-leafs) && [llength [$w entry children $ind]]} return
    set tags [$w tag names $ind]
    if {$q(-withtag) != {}} {
        if {[lsearch -exact $tags $q(-withtag)]<0} return
    }
    if {$q(-withouttag) != {}} {
        if {[lsearch -exact $tags $q(-withouttag)]>=0} return
    }
    set xsiz [expr {$xend-$xstart}]
    set entry $ind
    set bb [$w bbox -screen $ind]
    if {[llength $bb]!=4} return
    foreach {xbegin ystart xwidth ywidth} $bb break
    #set ystart [lindex $bb 1]
    set yend [expr {$ywidth+$ystart}]
    #tclLog "COL: $col, $lst, entry=$entry, offs=$offs, cind=$cind, xsiz=$xsiz, xstart=$xstart, bb=$bb"
    if {$q(-usetopframe)} {
        set wl [string trimright [winfo toplevel $w] .]._list
    } else {
        set wl $w._list
    }
    if {[winfo exists $wl]} {
        destroy $wl
        focus -force $w
        $w style activate 0
        return
    }
    event generate $w <<TreeViewEditStart>> -x $cind -y $ind
    $w style activate focus $cind
    focus $w
    if {$q(-useframe) || $q(-usetopframe)} {
        canvas $wl
        if {$q(-leave)} {
            bind $wl  <Leave> { ::blt::tv::Combobox-List-Close %W }
        }
    } else {
        toplevel $wl
        wm withdraw $wl
        wm transient $wl [winfo toplevel $w]
        raise [winfo toplevel $w]
        wm overrideredirect $wl 1
        if {$q(-leave)} {
            bind $wl  <Leave> { if {[winfo toplevel %W] == "%W"} { ::blt::tv::Combobox-List-Close %W } }
        }
    }
    set h $q(-height)
    if {[llength $lst]<=$h} {
        set h [expr {1+[llength $lst]}]
    }
    set opts [concat -height $h $opts]
    set l $wl._l
    listbox $l
    foreach {i j} $opts {
       catch { $l conf $i $j }
    }
    if {$q(-conf) != {}} {
       catch { eval $l conf $q(-conf) }
    }
    bindtags $l [concat [bindtags $l] TVComboBox::List]
    $l conf -yscrollcommand [list $wl._vscroll set] 
    scrollbar $wl._vscroll -orient vertical -command [list $l yview]
    if {[llength $lst]>$h} {
        grid $wl._vscroll -row 1 -column 2 -sticky ns
    }
    grid $l -row 1 -column 1 -sticky news
    grid columnconf $wl 1 -weight 1
    grid rowconf $wl 1 -weight 1
    set val [$w get]
    foreach i $lst {
        $l insert end $i
        if {[string equal $i $val]} { $l activate end; $l see end }
    }
    bind $l <Visibility> [subst -nocommands {
        bind $l <Visibility> {}
        if {[$l xview] != "0 1"} {
            $l conf -xscrollcommand [list $wl._hscroll set] 
            scrollbar $wl._hscroll -orient horizontal -command [list $wl xview]
            grid $wl._hscroll -row 2 -column 1 -sticky we
            grid $wl._vscroll -row 1 -column 2 -sticky ns
            focus -force $l
        }
    }]
    bind $l <<TVComboBox-List-Done>> [list [namespace current]::Combobox-List-Done  $l $entry $col]
    set H [winfo reqheight $l]
    set Xstart [expr {$xstart+[winfo x $w]}]
    set Yend [expr {$yend+[winfo x $w]}]
    set wwhig [winfo height $w]
    if {!(($ystart+$H) <= $wwhig || ($ystart-$H)<0)} {
        set Yend [expr {$ystart-$H}]
    }
    if {$q(-usetopframe)} {
        place $wl -in $w -width ${xsiz} -height $H -x $Xstart -y $Yend
        $wl conf -width $xsiz -height $H
    } elseif {$q(-useframe)} {
        place $wl -in $w -width ${xsiz} -height $H -x $Xstart -y $Yend
        $wl conf -width $xsiz -height $H
    } else {
        wm geometry $wl ${xsiz}x${H}+$Xstart+$Yend
        wm deiconify $wl
    }
    $w edit -noscroll
    if {$q(-usetopframe)} {
        bind $e <Destroy> +[list catch "$wl edit -scroll" ]
    } else {
        set tag [namespace current]
        bindtags $wl [concat $tag [bindtags $wl]]
        bind $tag <Destroy> {catch {[winfo parent %W] edit -scroll}}
    }
    after idle [list catch "focus -force $l"]
    return -code break
}
    
proc ::blt::tv::Combobox-List-Key {l ch args} {
    # Navigate to the first item starting with char ch.
    array set p { -now 0 }
    array set p $args
    if {![string is alpha -strict $ch]} return
    set cur [$l index active]
    set e [$l index end]
    foreach i {0 1} {
        set n -1
        while {[incr n]<$e} {
            set c [string index [$l get $n] 0]
            if {$i} { set c [string toupper $c] }
            if {[string equal $c $ch]} {
                $l activate $n
                $l see $n
                if {$n == $cur || $p(-now)} {
                    event generate $l <Return>
                }
                return
            }
        }
        set ch [string toupper $ch]
    }
    return -code break
}

proc ::blt::tv::SortColumn {t {column {}} args} {
    # Provide sorting for a column.
    array set p {-hold 1 -see 1 -highlight 0}
    array set p $args
    set do 1
    if {$column == {}} {
       set column [$t column current]
    }
    if {[string equal $column [$t sort cget -column]]} {
        set decr [expr {![$t sort cget -decreasing]}]
        if {!$decr} {
           $t sort conf -column {} -decreasing 0
           if {[$t sort cget -setflat] } {
              $t configure -flat no
              $t sort configure -setflat no
           }
           set do 0
        }
    } else {
        set decr 0
    }
    if {$do} {
        set mode [$t column cget $column -sortmode]
        if {$mode != "none"} {
           $t sort configure  -mode $mode
        }
        $t sort configure -decreasing $decr -column $column
        if {![$t cget -flat] } {
            $t configure -flat yes
            $t sort configure -setflat yes
        }
        $t sort auto yes
        if {$p(-hold)} {
            blt::busy hold $t
            update
            blt::busy release $t
        }
        if {$p(-highlight)} {
           after 300 [list $t column activate $column]
        }
    } else {
        if {$p(-highlight)} {
           $t column activate {}
        }
    }
    if {$p(-see)} {
       set sel [$t curselection]
       if {$sel != {}} {
         after idle [list $t see [lindex $sel 0]]
       }
    }
    set cind [$t column index $column]
    event generate $t <<TreeViewSortColumn>> -x $cind
}


bind TVComboBox <3>  [list blt::tv::Combobox-List %W %x %y]

bind TVComboBox::List  <Enter> { focus -force %W }
bind TVComboBox::List  <KeyRelease-Escape> { destroy [winfo parent %W] }
bind TVComboBox::List  <Return> { event generate %W <<TVComboBox-List-Done>>}
bind TVComboBox::List  <space> [bind TVComboBox::List <Return>]
bind TVComboBox::List  <ButtonRelease-1> [bind TVComboBox::List <Return>]
bind TVComboBox::List  <KeyRelease-space> [bind TVComboBox::List <Return>]
bind TVComboBox::List  <Control-n> [bind Listbox <Down>]
bind TVComboBox::List  <Control-p> [bind Listbox <Up>]
bind TVComboBox::List  <Control-d> [bind Listbox <Next>]
bind TVComboBox::List  <Control-u> [bind Listbox <Prior>]
bind TVComboBox::List  <KeyPress> [list blt::tv::Combobox-List-Key %W %A]

proc blt::tv::SetSelectionAnchor { w tagOrId } {
    if {$tagOrId == ""} return
    set index [$w index $tagOrId]
    # If the anchor hasn't changed, don't do anything
    if { $index != [$w index anchor] } {
	$w selection clearall
	$w see $index
	$w focus $index
	$w selection set $index
	$w selection anchor $index
    }
}

proc blt::tv::SetSelectionSetAnchor { w x y} {
    set mode [$w cget -selectmode]
    switch -- $mode {
        none return
        cell { }
        multicell {}
        default {
            set blt::tv::x $x
            set blt::tv::y $y
            SetSelectionAnchor $w @$x,$y
            return
        }
    }
    $w selection clearall
    set index [$w index @$x,$y]
    set col [$w column nearest $x]
    if {$index != {} && $col != {}} {
        $w selection set $index $index $col
        $w selection anchor $index $col
        $w focus $index
    }
}

proc  blt::tv::SetSelectionAdd {w x y} {
    set mode [$w cget -selectmode]
    set index [$w index @$x,$y]
    switch -- $mode {
        none return
        cell { $w selection clearall }
        multicell { }
        multiple {
            set index [$w index current]
            $w selection toggle $index
            $w selection anchor $index
            return
        }
        default {
            SetSelectionAnchor $w current
            return
        }
    }
    set col [$w column nearest $x]
    if {$index != {} && $col != {}} {
        $w selection toggle $index $index $col
    }
}

proc blt::tv::SetSelectionExtendAnchor {w x y} {
    set mode [$w cget -selectmode]
    switch -- $mode {
        none {}
        multiple {
            if {[$w selection present] } {
                if { [$w index anchor] == "" } {
                    $w selection anchor current
                }
                set index [$w index anchor]
                $w selection clearall
                $w selection set $index current
            }
        }
        single {
            blt::tv::SetSelectionAnchor $w current
        }
        cell {
            SetSelectionSetAnchor $w $x $y
        }
        multicell {
            # Select range.
            set col [$w column nearest $x]
            set oanch [$w selection anchor]
            set anch [$w index anchor]
            set ocell [lindex $oanch 1]
            set index [$w index @$x,$y]
            if {$col == {} || $ocell == {}} {
                return [SetSelectionSetAnchor $w $x $y]
            }
            set cols [$w column names]
            set coli [lsearch $cols $col]
            set ocelli [lsearch $cols $ocell]
            if {$coli<0 || $ocelli<0} {
                return [SetSelectionSetAnchor $w $x $y]
            }
            if {$coli<$ocelli} {
                set sci $coli
                set coli $ocelli
                set ocelli $sci
            }
            set clst {}
            foreach c [lrange $cols $ocelli $coli] {
                if {$c == "#0"} continue
                if {[$w column cget $c -hide]} continue
                lappend clst $c
            }
            set nlst {}
            foreach n [$w find $index $anch] {
                if {[$w entry cget $n -hide]} continue
                lappend nlst $n
            }
            $w selection clearall
            foreach n $nlst {
                foreach c $clst {
                    $w selection set $n $n $c
                }
            }
        }
    }
}

# ----------------------------------------------------------------------
#
# AutoScroll --
#
#	Invoked when the user is selecting elements in a treeview
#	widget and drags the mouse pointer outside of the widget.
#	Scrolls the view in the direction of the pointer.
#
# ----------------------------------------------------------------------
proc blt::tv::AutoScroll { w } {
    if { ![winfo exists $w] } {
	return
    }
    set x $blt::tv::x
    set y $blt::tv::y

    set index [$w nearest $x $y]

    if {$y >= [winfo height $w]} {
	$w yview scroll 1 units
	set neighbor down
    } elseif {$y < 0} {
	$w yview scroll -1 units
	set neighbor up
    } else {
	set neighbor $index
    }
    if { [$w cget -selectmode] == "single" } {
	blt::tv::SetSelectionAnchor $w $neighbor
    } elseif { [$w cget -selectmode] != "none" } {
	catch {$w selection mark $index}
    }
    set ::blt::tv::afterId [after 50 blt::tv::AutoScroll $w]
}

proc blt::tv::SetFocus { w tagOrId } {
    # Set focus at index given by tagOrId.
    if {[catch {$w index $tagOrId} t]} return
    if {[catch {$w focus $t}]} return
    $w selection clearall
    if {[catch {$w selection set $t}]} return
    $w selection anchor $t
    $w entry activate $t
    $w see $t
    event generate $w <<TreeViewFocusEvent>>
    return $t
}
# ----------------------------------------------------------------------
#
# MoveFocus --
#
#	Invoked by KeyPress bindings.  Moves the active selection to
#	the entry <where>, which is an index such as "up", "down",
#	"prevsibling", "nextsibling", etc.
#
# ----------------------------------------------------------------------
proc blt::tv::MoveFocus { w tagOrId {flag 0}} {
    set mode [$w cget -selectmode]
    switch -- $mode {
        multiple {
           catch {$w focus $tagOrId}
           if {!$flag} {
               $w selection clearall
           }
           if {[catch {$w selection set focus}]} return
           $w selection anchor focus
       }
        single {
           catch {$w focus $tagOrId}
           $w selection clearall
           if {[catch {$w selection set focus}]} return
	   $w selection anchor focus
        }
        multicell -
        cell {
           set cells [$w selection cells]
           catch {$w focus $tagOrId}
           $w selection clearall
           if {[catch {$w selection set focus}]} return
	   $w selection anchor focus
	   set ind [$w index focus]
           if {$cells != {}} {
               set col [lindex $cells 1]
           } else {
               set vcols [$w column names -visible]
               set col [lindex $vcols 0]
           }
           $w selection set focus focus $col
       }
    }
    $w see focus
    event generate $w <<TreeViewFocusEvent>>
}

# ----------------------------------------------------------------------
#
# MovePage --
#
#	Invoked by KeyPress bindings.  Pages the current view up or
#	down.  The <where> argument should be either "top" or
#	"bottom".
#
# ----------------------------------------------------------------------
proc blt::tv::MovePage { w where } {

    # If the focus is already at the top/bottom of the window, we want
    # to scroll a page. It's really one page minus an entry because we
    # want to see the last entry on the next/last page.
    set focus [$w index focus]
    if {$where == "top"} {
        if {[$w index up] != $focus} {
            $w yview scroll -1 pages
            $w yview scroll 1 units
        }
        if {[$w index focus] == $focus} {
           catch { $w entry select up }
        }
    } else {
        if {[$w index down] != $focus} {
            $w yview scroll 1 pages
            $w yview scroll -1 units
        }
        if {[$w index focus] == $focus} {
           catch { $w entry select down }
        }
    }
    update

    # Adjust the entry focus and the view.  Also activate the entry.
    # just in case the mouse point is not in the widget.
    $w entry activate view.$where
    $w focus view.$where
    $w see view.$where
    if { [$w cget -selectmode] == "single" } {
        $w selection clearall
        catch {$w selection set focus}
    }
    event generate $w <<TreeViewFocusEvent>>
}

# ----------------------------------------------------------------------
#
# NextMatch --
#
#	Invoked by KeyPress bindings.  Searches for an entry that
#	starts with the letter <char> and makes that entry active.
#
# ----------------------------------------------------------------------
proc blt::tv::NextMatch { w key state} {
    if {$state != 0 && $state != 1} return
    if {[string match {[ -~]} $key]} {
	set last [$w index focus]
	set next [$w index next]
	while { $next != {} && $next != $last } {
	    set label [$w entry cget $next -label]
	    set label [string index $label 0]
	    if { [string tolower $label] == [string tolower $key] } {
		break
	    }
	    set next [$w index -at $next next]
	}
        if {$next == {}} return
	$w focus $next
	if {[$w cget -selectmode] == "single"} {
	    $w selection clearall
	    $w selection set focus
            event generate $w <<TreeViewFocusEvent>>
	}
	$w see focus
    }
}

#------------------------------------------------------------------------
#
# InsertText --
#
#	Inserts a text string into an entry at the insertion cursor.  
#	If there is a selection in the entry, and it covers the point 
#	of the insertion cursor, then delete the selection before 
#	inserting.
#
# Arguments:
#	w 	Widget where to insert the text.
#	text	Text string to insert (usually just a single character)
#
#------------------------------------------------------------------------
proc blt::tv::InsertText { w text } {
    if { [string length $text] > 0 } {
	set index [$w index insert]
	if { ($index >= [$w index sel.first]) && 
	     ($index <= [$w index sel.last]) } {
	    $w delete sel.first sel.last
	}
	$w insert $index $text
    }
}

#------------------------------------------------------------------------
#
# Transpose -
#
#	This procedure implements the "transpose" function for entry
#	widgets.  It tranposes the characters on either side of the
#	insertion cursor, unless the cursor is at the end of the line.
#	In this case it transposes the two characters to the left of
#	the cursor.  In either case, the cursor ends up to the right
#	of the transposed characters.
#
# Arguments:
#	w 	The entry window.
#
#------------------------------------------------------------------------
proc blt::tv::Transpose { w } {
    set i [$w index insert]
    if {$i < [$w index end]} {
	incr i
    }
    set first [expr {$i-2}]
    if {$first < 0} {
	return
    }
    set new [string index [$w get] [expr {$i-1}]][string index [$w get] $first]
    $w delete $first $i
    $w insert insert $new
}

#------------------------------------------------------------------------
#
# GetSelection --
#
#	Returns the selected text of the entry with respect to the
#	-show option.
#
# Arguments:
#	w          Entry window from which the text to get
#
#------------------------------------------------------------------------

proc blt::tv::GetSelection { w } {
    set text [string range [$w get] [$w index sel.first] \
                       [expr {[$w index sel.last] - 1}]]
    if {[$w cget -show] != ""} {
	regsub -all . $text [string index [$w cget -show] 0] text
    }
    return $text
}

proc blt::tv::TextCopy {w {edit 0} {aslist 0}} {
    # Handle <<Copy>> event, copying selection/focus to clipboard.
    if {!$edit} {
      catch {
       set inds [$w curselection]
       if {$inds == {}} {
           set inds [$w index focus]
       }
       set all {}
       set n -1
       foreach ind $inds {
           incr n
           set data {}
           foreach i [$w column names] {
               if {[$w col cget $i -hid]} continue
               if {$i == "#0"} {
                   set val [$w entry cget $ind -label]
               } else {
                   set val [$w entry set $ind $i]
               }
               if {$aslist} {
                   lappend data $val
               } else {
                   append data " " $val
               }
           }
           if {$aslist} {
               lappend all $data
           } else {
               if {$n} { append all \n }
               append all $data 
           }

       }
       clipboard clear -displayof $w
       clipboard append -displayof $w $all
      }
    } else {
    catch {
       set w [winfo parent $w]
       set ind [$w index focus]
       set col [$w column current]
       if {$col == {}} {
           set col $::blt::tv::curCol
       }
       set data [$w entry set $ind $col]
       clipboard clear -displayof $w
       clipboard append -displayof $w $data
    }
   }
}

proc ::blt::tv::Toggle {w ind} {
    # Toggle and set view.
    set ind [$w index $ind]
    if {$ind == {}} return
    $w toggle $ind
    if {[$w entry isopen $ind] && [$w cget -openanchor] != {} && [$w entry children $ind] != {}} {
        $w see -anchor [$w cget -openanchor] $ind
    } else {
        $w see $ind
    }
}

proc ::blt::tv::Click {w x y} {
   if {[focus] != $w} { focus $w }
   set ::blt::tv::space off
   #if {[winfo exists $w.edit]} { destroy $w.edit }
   event generate $w <<TreeViewFocusEvent>>
}

bind $className <ButtonRelease-1> {::blt::tv::Click %W %x %y}
bind $className <Double-ButtonPress-1> {blt::tv::EditCol %W %x %y } 
bind $className <Alt-ButtonPress-1> {blt::tv::EditCol %W %x %y } 
bind $className <Control-minus> {if {[%W index parent]>0} { after idle "%W entry select [%W entry parent focus]"; %W close [%W entry parent focus] }}
bind $className <Control-o> { ::blt::tv::Toggle %W focus }
bind $className <Control-a> { blt::tv::MoveFocus %W parent }
bind $className <Control-Shift-O> { %W open -recurse focus }
bind $className <Control-Shift-C> { %W close -recurse focus }

bind TreeViewEditWin <KeyRelease-Escape> {focus [winfo parent %W]; destroy %W; break}
bind TreeViewEditWin <KeyPress-Return> {event generate %W <<TreeViewEditComplete>>; break}
#bind TreeViewEditWin <KeyPress-Return> {break}

proc ::blt::tv::EditDone {w e x y ind col cind data styledata cellstyle ied endcmd treelabel vcmd} {
    # # Handle edit completion: call $endcmd and widget -vcmd if req.
    switch -- [winfo class $e] {
        Entry - Spinbox {
            set newdata [$e get]
        }
        Text {
            set newdata [string trimright [$e get 1.0 end]]
        }
        default {
            set newdata $data
        }
    }
    # Invoke validation for Entry/Spinbox/Text if string changed.
    set ok 1
    if {![string equal $data $newdata]} {
        if {$vcmd == {}} {
            set vcmd [$w column cget $col -validatecmd]
        }
        if {$vcmd != {}} {
            if {[string first % $vcmd]>=0} {
                set ccmd [string map [list %% % %W $w %X $x %Y $y %# $ind %C $cind %V [list $newdata]] $vcmd]
            } else {
                set ccmd [concat $vcmd [list $w $newdata $data $ind $col]]
	    }
            set newdata [namespace eval :: $ccmd]
        }
        if {![winfo exists $w]} return
    }
    if {![string equal $data $newdata]} {
        set istree [$w column istree $cind]
        if {$ind == -1} {
            $w col conf $cind -title $newdata
        } elseif {$istree} {
            if {$treelabel} {
                [$w cget -tree] label $ind $newdata
            } else {
                $w entry conf $ind -label $newdata
            }
        } else {
            if {$styledata != {}} {
                set newdata [list $styledata $newdata]
            }
            $w entry set $ind $col $newdata
        }
        if {$endcmd != {}} {
            if {[string first % $endcmd]>=0} {
                set ccmd [string map [list %% % %W $w %X $x %Y $y %# $ind %C $cind %V [list $newdata]] $endcmd]
                set ccmd [concat $endcmd [list $w $newdata $data $ind $col]]
            }
            namespace eval :: $ccmd
        }
        if {![winfo exists $w]} return
        event generate $w <<TreeViewEditEnd>> -x $cind -y $ind
    }
    if {$ied} {
      catch { bind $e <Destroy> {} }
      $w style set $cellstyle $col $ind
    } else {
      catch { place forget $e }
    }
    destroy $e
    catch { focus $w }
    #after idle [list destroy $e]
}

proc ::blt::tv::TabMove {w ind cind args} {
    # Handle Tab char.
    #Opts p $args {
    #    { -cmd      {}      "Callback to get next cell" -type {cmd ind cind} }
    #    { -endcol   {}      "Maximum column (defaults to last col)" }
    #    { -startcol 0       "Column to start new row at" }
    #    { -wrap     True    "At last row return to top" }
    #}
    array set p { -cmd {} -endcol {} -startcol 0 -wrap True -opts {}}
    array set p $args
    set vis [$w column names -visible]
    set maxc [expr {[llength $vis]-1}]
    if {$p(-endcol) == {} || $p(-endcol) > $maxc} {
        set p(-endcol) $maxc
    }
    set maxr [$w index end]
    if {$p(-cmd) != {}} {
        set ncol [eval $p(-cmd) $ind,$cind]
        if {$ncol == {}} return
        foreach {ind col} $ncol break
        EditCell $w $ind $col
        return

    }
    set down [expr {$p(-wrap)?"next":"down"}]
    set cnt 100
    while 1 {
        if {[incr cnt -1] == 0} return
        incr cind 1
        if {$cind > $p(-endcol)} {
            set cind $p(-startcol)
            set ind [$w index $down]
            $w focus $ind
        }
        if {[$w column cget $cind -edit] && ![$w column cget $cind -hide]} break
    }
    EditCell $w $ind $cind
    return
}

proc ::blt::tv::EditCell { w ind col {x {}} {y {}}} {
    # Handle text editing of a cell.
    if {![winfo exists $w]} return
    # Option choices for -editopts.
    set Opts {
        { -allowtypes textbox "List of types to allow text editing for (or *)" }
        { -autonl   False   "Default to text widget if newlines in data"}
        { -choices  {}      "Choices for combo/spinbox" }
        { -conf     {}      "Extra entry/text widget options to set" }
        { -embed    False   "Use an embedded window style for edit window" }
        { -endcmd   {}      "Command to invoke at end of edit" -type cmd  }
        { -leafs    False   "Edit only leaf nodes" }
        { -nlkeys {<Control-r> <Shift-Return>} "Keys for inserting newline" }
        { -notnull  False   "Field may not be null" }
        { -optscmd  {}      "Callback to get edit options" -type {cmd w r c} }
        { -readonly False   "Do not allow editing" }
        { -sel      True    "Value is selected on edit" }
        { -startcmd {}      "Command to invoke at start of edit" -type {cmd w r c}  }
        { -tab      {}      "bind Tab char in edit (bool or args passed to TabMove)" }
        { -titles   False   "Allow edit of titles" }
        { -treelabel True   "Edit -tree cmd label rather than treeview label" }
        { -type     {}      "Support basic Wize types like bool, int, and choice" }
        { -typecol  {}      "Column/key to get -type from" }
        { -undo     True    "Text widget enables undo" }
        { -vcmd     {}      "Validate command to override -validatecmd" -type cmd }
        { -widget   {}      "Widget to use (defaults to entry)" }
        { -withouttag {}    "Edit only entries without tag"}
        { -withtag  {}      "Edit only entries with tag"}
        { -wrap     none    "Wrap mode for text widget" }
    }
    if {[winfo exists $w._list]} return
    $w see current
    set e $w.edit
    if { [winfo exists $e] } { destroy $e }
    set ind [$w index $ind]
    set cind [$w column index $col]
    set ed [$w column cget $col -edit]
    if { !$ed  } return
    set intitle 0
    if {$x == {}} {
        set bb [$w col bbox $col $ind]
        set x [lindex $bb 0]
        set y [lindex $bb 1]
    }
    set istree [$w column istree $col]
    set edopts {}
    foreach i $Opts {
        set q([lindex $i 0]) [lindex $i 1]
        lappend edopts [lindex $i 0] [lindex $i 1]
    }
    set widopts [$w column cget $col -editopts]
    set cellstyle [lindex [$w style get $cind $ind] 0]
    if {$cellstyle != {}} {
         if {[$w style cget $cellstyle -readonly]} return
         set widopts [concat $widopts [$w style cget $cellstyle -editopts]]
    }
    if {$widopts != {}} {
        if {[llength $widopts]%2} {
            tclLog "TreeView -editopts: odd length: '$widopts' for $w"
        } else {
            array set q $widopts
            if {$q(-optscmd) != {}} {
                set ewopts [eval $q(-optscmd) $w $ind $col]
                if {[llength $ewopts]%2} {
                   tclLog "TreeView -optscmd: odd length: '$ewopts' for $w"
                } else {
                   array set q $ewopts
                }
            }
            if {[array size q] != ([llength $edopts]/2)} {
               set bad {}
               array set r $edopts
               set good [lsort [array names r]]
               foreach {i j} $widopts {
                  if {![info exists r($i)]} { lappend bad $i }
               }
               tclLog "TreeView -editopts: bad option: '$bad' not in '$good'"
            }
        }
    }
    if {$q(-typecol) != {} && $q(-type) == {}} {
        if {[catch { set q(-type) [$w entry set $ind $q(-typecol)] }] &&
            [catch { set q(-type) [[$w cget -tree] get $ind $q(-typecol)] }]} {
            tclLog "Failed to get -typecol $q(-typecol)"
        }
    }
    set wopts {}
    if {$q(-type) != {}} {
        switch -- [lindex $q(-type) 0] {
            bool {
                set q(-choices) {"" True False}
                set q(-widget) spinbox
            }
            Bool {
                lset q(-choices) {True False}
                set q(-widget) spinbox
            }
            int - Int -
            double - Double {
                array set qq {-min -999999999 -max 99999999 -incr 1}
                array set qq [lrange $q(-type) 1 end]
                set wopts [list -from $qq(-min) -to $qq(-max) -increment $qq(-incr)]
                set q(-widget) spinbox
            }
            Choice {
                set q(-choices) [lrange $q(-type) 1 end]
                set q(-widget) spinbox
            }
        }
    }
    if {$q(-readonly)} return
    if {!$q(-titles) && [set intitle [expr {[$w column nearest $x $y] != {}}]]} return
    if {$q(-leafs) && [llength [$w entry children $ind]]} return
    set tags [$w tag names $ind]
    if {$q(-withtag) != {}} {
        if {[lsearch -exact $tags $q(-withtag)]<0} return
    }
    if {$q(-withouttag) != {}} {
        if {[lsearch -exact $tags $q(-withouttag)]>=0} return
    }
    set styledata {}
    if {$intitle} {
        set data [$w column cget $col -title]
        set ind -1
    } elseif {$istree } {
        if {$q(-treelabel) && [namespace which [$w cget -tree]] == {}} {
            set q(-treelabel) 0
        }
        if {$q(-treelabel)} {
            set data [[$w cget -tree] label $ind]
        } else {
            set data [$w entry cget $ind -label]
        }
    } else {
        set data [$w entry set $ind $col]
        if {[$w cget -inlinedata] && [string first @ $data]>=0} {
            if {![catch {llength $data} len] && $len <= 2 &&
            [string match @?* [lindex $data 0]] &&
            [lsearch -exact [$w style names] [string range [lindex $data 0] 1 end]]>=0} {
                #set styledata [lindex $data 0]
                set data [lindex $data 1]
            }
        }
    }
    set bbox [$w column bbox -visible $col $ind]
    if {![llength $bbox]} return
    foreach {X Y W H} $bbox break
    set wid entry
    set style [$w col cget $cind -style]
    set rstyle [expr {$cellstyle == {} ? $style : $cellstyle }]
    set stype [expr {$rstyle == {} ? {} : [$w style type $rstyle]}]
    #if {[$w style cget $style -readonly]} return
    if {[lsearch $q(-allowtypes) $stype]<0 && $q(-allowtypes) != "*"} return
    if {$widopts != {}} {
        if {$q(-widget) != {}} {
            set wid $q(-widget)
        } elseif {$q(-autonl)} {
            if {$stype == "combobox"} {
                set wid spinbox
            } elseif {[string first \n $data]>=0} {
                set wid text
            } else {
                set wid entry
            }
        }
    }
    if {[catch {eval $wid $e} err]} {
        entry $e
    }
    foreach {i j} $wopts {
       catch { $e conf $i $j}
    }
    catch { $e conf -font [$w cget -font] }
    set ied 0
    if {$q(-embed) && !$istree} {
      catch {$w style create windowbox editwin}
      if {$bbox != {}} {
        #TODO: embedded should temporarily set col width, if currently is 0.
        set mwid $W
        if {$mwid>16} { incr mwid -10 }
        set mhig $H
        $w style conf editwin -minheight $mhig -minwidth $mwid
      }
      $w style set editwin $cind $ind
      $w entry set $ind $col $e
      bind $e <Destroy> [list $w entry set $ind $col $data]
      bind $e <Destroy> +[list $w style set $cellstyle $col $ind]
      set ied 1
    } else {
      place $e -x $X -y $Y -width $W -height $H
    }
    switch -- [winfo class $e] {
        Entry {
            $e insert end $data
            if {$q(-sel)} { $e selection range 0 end }
            foreach i $q(-nlkeys) {
                bind $e $i "$e insert insert {\n}; break"
            }
        }
        Spinbox {
            $e insert end $data
            if {$q(-choices) != {}} {
                $e conf -values $q(-choices)
            }
            if {$q(-sel)} { $e selection range 0 end }
            set style [$w col cget $cind -style]
            if {$stype == "combobox"} {
                set ch [$w style cget $style -choices]
                if {$ch == {} && [set ccmd [$w style cget $style -choicecmd]] != {}} {
                    set ccmd [string map [list %% % %W $w %X $x %Y $y %# $ind %C $cind] $ccmd]
                    set ch [namespace eval :: $ccmd]
                }
                if {$ch != {}} {
                    if {[set nn [lsearch -exact $ch $data]]<0} {
                        set ch [concat [list $data] $ch]
                    } elseif {$n != 0} {
                        set ch [concat [list $data] [lreplace $ch $nn $nn]]
                    }
                    $e conf -values $ch
                }
            }
            foreach i $q(-nlkeys) {
                bind $e $i "$e insert insert {\n}; break"
            }
        }
        Text {
            $e conf -highlightthick 0 -padx 0 -pady 0 -bd 1
            $e conf -undo $q(-undo) -wrap $q(-wrap)
            $e insert end $data
            if {$q(-sel)} { $e tag add sel 1.0 end }
            foreach i $q(-nlkeys) {
                bind $e $i "$e insert insert {\n}; $e see insert; break"
            }
        }
    }
    catch {$e conf -highlightthick 0}
    if {$q(-conf) != {}} {
        if {[catch {eval $e conf $q(-conf)} err]} {
            tclLog "Opts err: $err"
        }
    }
    bind $e <1> [list focus $e]
    bindtags $e [concat TreeViewEditWin [bindtags $e]]
    tkwait visibility $e
    focus $e
    after 100 [list catch [list focus $e]]
    after 300 [list catch [list focus $e]]
    bind $e <<TreeViewEditComplete>> [list ::blt::tv::EditDone $w $e $x $y $ind $col $cind $data $styledata $cellstyle $ied $q(-endcmd) $q(-treelabel) $q(-vcmd)]
    if {![string is false $q(-tab)]} {
        set topts {}
        if {![string is true $q(-tab)]} {
            set topts $q(-tab)
        }
        bind $e <Tab> "event generate $e <<TreeViewEditComplete>>; [namespace current]::TabMove $w $ind $cind $topts; break"
    }
    event generate $w <<TreeViewEditStart>> -x $cind -y $ind
    if {[winfo exists $e] && $q(-startcmd) != {}} {
       if {[string first % $vcmd]>=0} {
           set ccmd [string map [list %% % %W $w %X $x %Y $y %# $ind %C $cind %V [list $data]] $q(-startcmd)]
       } else {
           set ccmd [concat $q(-startcmd) [list $w $col $ind] ]
       }
       namespace eval :: $ccmd
    }
    if {![winfo exists $e]} return
    $w edit -noscroll
    set tag [namespace current]
    bindtags $e [concat $tag [bindtags $e]]
    bind $tag <Destroy> { catch {[winfo parent %W] edit -scroll} }
    return
}

proc ::blt::tv::commify {num {sep ,}} {
    # Make number comma seperated every 3 digits.
    while {[regsub {^([-+]?\d+)(\d\d\d)} $num "\\1$sep\\2" num]} {}
    return $num
}

proc ::blt::tv::EditCol { w x y } {
    # Main handler for cell-edit/toggle-open.
    if {![winfo exists $w]} return
    $w see current
    set e $w.edit
    if { [winfo exists $e] } { destroy $e }
    set ::blt::tv::curCol [set col [$w column nearest $x]]
    if {$col == {}} return
    set cind [$w column index $col]
    set ind [$w index @$x,$y]
    if { ![$w column cget $col -edit]  } {
        if {$cind == 0} {
            Toggle $w $ind
        }
        return
    } elseif {$cind == 0} {
        set object {}
        $w nearest $x $y object
        if {$object != "label"} {
            Toggle $w $ind
            return
        }
    }
    EditCell $w $ind $col $x $y
}


proc blt::tv::EditColumn { w x y } {
    # Old edit function.
    $w see current
    if { [winfo exists $w.edit] } {
        destroy $w.edit
    }
    set col [$w column current]
    if {$col == {}} return
    set ::blt::tv::curCol $col
    if { ![$w edit -root -test $x $y] } {
        return
    }
    set ind [$w index @$x,$y]
    if {$ind == {}} return
    set data [$w entry set $ind $col]
    $w edit -root $x $y
    update
    focus $w.edit
    $w.edit selection range 0 end
    event generate $w <<TreeViewEditStart>> -x $x -y $y
    grab set $w.edit
    tkwait window $w.edit
    grab release $w.edit
    if {[winfo exists $w]} {
        event generate $w <<TreeViewEditEnd>> -x $x -y $y
    }
}

proc ::blt::tv::SortTree {t column {ind {}} {uselabel 1} {see 1}} {
    # Sort the children of tree.
    set istree [$t column istree $column]
    if {$ind == {}} {
       set ind [lindex [$t curselection] 0]
       if {$ind == {}} {
          if {$istree} { set ind 0 } else {  set ind focus }
       }
    }
    set ind [$t index $ind]
    set clst [$t entry children $ind]
    if {$clst == {}} return
    set slst {}
    foreach i $clst {
       
       if {$istree} {
          set txt [expr {$uselabel?[$t entry cget $i -label]:[$t get $i]}]
       } else {
          set txt [$t entry set $i $column]
       }
       lappend slst [list $txt $i]
    }
    if {[set decreasing [$t sort cget -decreasing]]} {
      set dec -decreasing
    } else {
      set dec -increasing
    }
    #set decreasing [expr {!$decreasing}]
    #$t sort conf -decreasing $decreasing
    set mode [$t column cget $column -sortmode]
    if {$mode == "none"} {
       set mode [$t sort cget -mode]
    }
    if {$mode == "none"} return
    if {$mode == "command"} {
       set slst [lsort $dec -command [$t column cget $column -sortcommand] $slst]
    } else {
       set slst [lsort $dec -$mode $slst]
    }
    foreach i $slst {
       set oi [lindex $i 1]
       $t move $oi into $ind
    }
    if {$see} {
       set sel [$t curselection]
       if {$sel != {}} {
         after idle [list $t see [lindex $sel 0]]
       }
    }
    set cind [$t column index $column]
    event generate $t <<TreeViewSortTree>> -x $cind -y $ind
}


# 
# ButtonPress assignments
#
#	B1-Enter	start auto-scrolling
#	B1-Leave	stop auto-scrolling
#	ButtonPress-2	start scan
#	B2-Motion	adjust scan
#	ButtonRelease-2 stop scan
#
bind ${className} <ButtonPress-2> {
    focus %W
}

bind ${className} <ButtonPress-2> {
    set blt::tv::cursor [%W cget -cursor]
    %W configure -cursor hand1
    %W scan mark %x %y
}

bind ${className} <B2-Motion> {
    catch { %W scan dragto %x %y }
}

bind ${className} <ButtonRelease-2> {
    catch { %W configure -cursor $blt::tv::cursor }
}

bind ${className} <B1-Leave> {
    if { $blt::tv::scroll } {
	blt::tv::AutoScroll %W 
    }
}

bind ${className} <B1-Enter> {
    after cancel $blt::tv::afterId
}

# 
# KeyPress assignments
#
#	Up			
#	Down
#	Shift-Up
#	Shift-Down
#	Prior (PageUp)
#	Next  (PageDn)
#	Left
#	Right
#	space		Start selection toggle of entry currently with focus.
#	Return		Start selection toggle of entry currently with focus.
#	Home
#	End
#	F1
#	F2
#	ASCII char	Go to next open entry starting with character.
#
# KeyRelease
#
#	space		Stop selection toggle of entry currently with focus.
#	Return		Stop selection toggle of entry currently with focus.


bind ${className} <KeyPress-Up> {
    blt::tv::MoveFocus %W up
    if { $blt::tv::space } {
	%W selection toggle focus
    }
}

bind ${className} <KeyPress-Down> {
    blt::tv::MoveFocus %W down
    if { $blt::tv::space } {
	%W selection toggle focus
    }
}
bind ${className} <Control-KeyPress-n> [bind ${className} <KeyPress-Down>]
bind ${className} <Control-KeyPress-p> [bind ${className} <KeyPress-Up>]

bind ${className} <Shift-KeyPress-Up> {
    blt::tv::MoveFocus %W up 1
}

bind ${className} <Shift-KeyPress-Down> {
    blt::tv::MoveFocus %W down 1
}

bind ${className} <KeyPress-Prior> {
    blt::tv::MovePage %W top
}

bind ${className} <KeyPress-Next> {
    blt::tv::MovePage %W bottom
}
bind ${className} <Control-KeyPress-d> [bind ${className} <KeyPress-Next>]
bind ${className} <Control-KeyPress-u> [bind ${className} <KeyPress-Prior>]

#bind ${className} <KeyPress-Left> {
#    %W close focus
#}
#bind ${className} <KeyPress-Right> {
#    %W open focus
#    %W see focus -anchor w
#}
proc blt::tv::MoveKey {w cnt} {
    set mode [$w cget -selectmode]
    set iscell [expr {$mode == "cell" || $mode == "multicell"}]
    if {!$iscell} { return [$w xview scroll $cnt unit] }
    set cells [$w selection cells]
    if {$cells != {}} {
        set vcols [$w col names -visible]
        foreach {ind col} $cells break
        set cind [lsearch $vcols $col]
        if {$cind >= 0} {
            set cind [expr {$cind+$cnt}]
            if {$cind>=[llength $vcols]} {
                set cind [expr {[llength $vcols]-1}]
            } elseif {$cind < 0} {
                set cind 0
            }
            set ncol [lindex $vcols $cind]
            $w selection clearall
            $w selection set $ind $ind $ncol
            $w column see $ncol
        }
    }
}

proc blt::tv::MarkPos {w} {
    if { [$w cget -selectmode] == "single" } {
	if { [$w selection includes focus] } {
	    $w selection clearall
	} else {
	    $w selection clearall
	    $w selection set focus
	}
     } elseif { [$w cget -selectmode] != "none" } {
	$w selection toggle focus
    }
    set blt::tv::space on
}

bind ${className} <KeyPress-Left> { blt::tv::MoveKey %W -1}
bind ${className} <KeyPress-Right> { blt::tv::MoveKey %W 1}
#bind ${className} <KeyPress-Left> { %W xview scroll -1 unit}
#bind ${className} <KeyPress-Right> { %W xview scroll 1 unit}
bind ${className} <Control-KeyPress-Left> { %W xview scroll -1 page}
bind ${className} <Control-KeyPress-Right> { %W xview scroll 1 page}

bind ${className} <KeyPress-space> { catch {blt::tv::MarkPos %W } }

bind ${className} <KeyRelease-space> { 
    set blt::tv::space off
}

#bind ${className} <KeyPress-Return> {
#    blt::tv::MoveFocus %W focus
#    set blt::tv::space on
#}

#bind ${className} <KeyRelease-Return> { 
#    set blt::tv::space off
#}

bind ${className} <KeyPress-Return> { 
    #set blt::tv::space on
    blt::tv::Toggle %W focus
    #set blt::tv::space off
}

bind ${className} <KeyRelease-Return> { 
    #set blt::tv::space off
}

bind ${className} <KeyPress> {
    blt::tv::NextMatch %W %A %s
}

bind ${className} <KeyPress-Home> {
    blt::tv::MoveFocus %W top
}

bind ${className} <KeyPress-End> {
    blt::tv::MoveFocus %W bottom
}

bind ${className} <Control-F1> {
    %W open -trees root
}

bind ${className} <Control-F2> {
    eval %W close -trees root
}

bind ${className} <Control-F3> {
    %W conf -flat [expr {![%W cget -flat]}]
}

bind ${className} <Control-F4> {
    eval %W col conf [%W col names] -width 0
}

bind ${className} <MouseWheel> {
        if {%D >= 0} {
            %W yview scroll [expr {-%D/30}] units
        } else {
            %W yview scroll [expr {(2-%D)/30}] units
        }
}

if {[tk windowingsystem] == "x11"} {
  bind ${className} <4> { %W yview scroll -3 unit }
  bind ${className} <5> { %W yview scroll 3 unit }
}

#
# Differences between id "current" and operation nearest.
#
#	set index [$w index current]
#	set index [$w nearest $x $y]
#
#	o Nearest gives you the closest entry.
#	o current is "" if
#	   1) the pointer isn't over an entry.
#	   2) the pointer is over a open/close button.
#	   3) 
#

#
#  Edit mode assignments
#
#	ButtonPress-3   Enables/disables edit mode on entry.  Sets focus to 
#			entry.
#
#  KeyPress
#
#	Left		Move insertion position to previous.
#	Right		Move insertion position to next.
#	Up		Move insertion position up one line.
#	Down		Move insertion position down one line.
#	Return		End edit mode.
#	Shift-Return	Line feed.
#	Home		Move to first position.
#	End		Move to last position.
#	ASCII char	Insert character left of insertion point.
#	Del		Delete character right of insertion point.
#	Delete		Delete character left of insertion point.
#	Ctrl-X		Cut
#	Ctrl-V		Copy
#	Ctrl-P		Paste
#	
#  KeyRelease
#
#	ButtonPress-1	Start selection if in entry, otherwise clear selection.
#	B1-Motion	Extend/reduce selection.
#	ButtonRelease-1 End selection if in entry, otherwise use last
#			selection.
#	B1-Enter	Disabled.
#	B1-Leave	Disabled.
#	ButtonPress-2	Same as above.
#	B2-Motion	Same as above.
#	ButtonRelease-2	Same as above.
#	
#


# Standard Motif bindings:

bind ${className}Editor <ButtonPress-1> {
    %W icursor @%x,%y
    %W selection clear
}

bind ${className}Editor <Left> {
    %W icursor prev
    %W selection clear
}

bind ${className}Editor <Right> {
    %W icursor next
    %W selection clear
}

bind ${className}Editor <Shift-Left> {
    set new [expr {[%W index insert] - 1}]
    if {![%W selection present]} {
	%W selection from insert
	%W selection to $new
    } else {
	%W selection adjust $new
    }
    %W icursor $new
}

bind ${className}Editor <Shift-Right> {
    set new [expr {[%W index insert] + 1}]
    if {![%W selection present]} {
	%W selection from insert
	%W selection to $new
    } else {
	%W selection adjust $new
    }
    %W icursor $new
}

bind ${className}Editor <Home> {
    %W icursor 0
    %W selection clear
}
bind ${className}Editor <Shift-Home> {
    set new 0
    if {![%W selection present]} {
	%W selection from insert
	%W selection to $new
    } else {
	%W selection adjust $new
    }
    %W icursor $new
}
bind ${className}Editor <End> {
    %W icursor end
    %W selection clear
}
bind ${className}Editor <Shift-End> {
    set new end
    if {![%W selection present]} {
	%W selection from insert
	%W selection to $new
    } else {
	%W selection adjust $new
    }
    %W icursor $new
}

bind ${className}Editor <Delete> {
    if { [%W selection present]} {
	%W delete sel.first sel.last
    } else {
	%W delete insert
    }
}

bind ${className}Editor <BackSpace> {
    if { [%W selection present] } {
	%W delete sel.first sel.last
    } else {
	set index [expr [%W index insert] - 1]
	if { $index >= 0 } {
	    %W delete $index $index
	}
    }
}

bind ${className}Editor <Control-space> {
    %W selection from insert
}

bind ${className}Editor <Select> {
    %W selection from insert
}

bind ${className}Editor <Control-Shift-space> {
    %W selection adjust insert
}

bind ${className}Editor <Shift-Select> {
    %W selection adjust insert
}

bind ${className}Editor <Control-slash> {
    %W selection range 0 end
}

bind ${className}Editor <Control-backslash> {
    %W selection clear
}

bind ${className}Editor <KeyPress> {
    blt::tv::InsertText %W %A
}

# Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
# Otherwise, if a widget binding for one of these is defined, the
# <KeyPress> class binding will also fire and insert the character,
# which is wrong.  Ditto for Escape, Return, and Tab.

bind ${className}Editor <Alt-KeyPress> {
    # nothing
}

bind ${className}Editor <Meta-KeyPress> {
    # nothing
}

bind ${className}Editor <Control-KeyPress> {
    # nothing
}

bind ${className}Editor <KeyRelease-Escape> { 
    %W cancel 
}

bind ${className}Editor <Return> { 
    %W apply 
}

bind ${className}Editor <Shift-Return> {
    blt::tv::InsertText %W "\n"
}

bind ${className}Editor <KP_Enter> {
    # nothing
}

bind ${className}Editor <Tab> {
    # nothing
}

if {![string compare $tcl_platform(platform) "macintosh"]} {
    bind ${className}Editor <Command-KeyPress> {
	# nothing
    }
}

# On Windows, paste is done using Shift-Insert.  Shift-Insert already
# generates the <<Paste>> event, so we don't need to do anything here.
if { [string compare $tcl_platform(platform) "windows"] != 0 } {
    bind ${className}Editor <Insert> {
	catch {blt::tv::InsertText %W [::tk::GetSelection %W CLIPBOARD]}
        #catch {blt::tv::InsertText %W [selection get -displayof %W]}
    }
}
bind ${className}Editor <<Paste>> {
    catch {blt::tv::InsertText %W [::tk::GetSelection %W CLIPBOARD]}
}

bind ${className}Editor <<Copy>> { ::blt::tv::TextCopy %W 1 }

bind ${className} <<Copy>> { ::blt::tv::TextCopy %W }


# Additional emacs-like bindings:
bind ${className}Editor <Double-1> {
    set parent [winfo parent %W]
    %W cancel
    after idle {
	blt::tv::EditColumn $parent %X %Y
    }
}
bind ${className}Editor <ButtonPress-3> {
    set parent [winfo parent %W]
    %W cancel
    after idle {
	blt::tv::EditColumn $parent %X %Y
    }
}

bind ${className}Editor <Control-a> {
    %W icursor 0
    %W selection clear
}

bind ${className}Editor <Control-b> {
    catch {%W icursor [expr {[%W index insert] - 1}]}
    %W selection clear
}

bind ${className}Editor <Control-d> {
    %W delete insert
}

bind ${className}Editor <Control-e> {
    %W icursor end
    %W selection clear
}

bind ${className}Editor <Control-f> {
    %W icursor [expr {[%W index insert] + 1}]
    %W selection clear
}

bind ${className}Editor <Control-h> {
    if {[%W selection present]} {
	%W delete sel.first sel.last
    } else {
	set index [expr [%W index insert] - 1]
	if { $index >= 0 } {
	    %W delete $index $index
	}
    }
}

bind ${className}Editor <Control-k> {
    %W delete insert end
}

if 0 {
    bind ${className}Editor <Control-t> {
	blt::tv::Transpose %W
    }
    bind ${className}Editor <Meta-b> {
	%W icursor [blt::tv::PreviousWord %W insert]
	%W selection clear
    }
    bind ${className}Editor <Meta-d> {
	%W delete insert [blt::tv::NextWord %W insert]
    }
    bind ${className}Editor <Meta-f> {
	%W icursor [blt::tv::NextWord %W insert]
	%W selection clear
    }
    bind ${className}Editor <Meta-BackSpace> {
	%W delete [blt::tv::PreviousWord %W insert] insert
    }
    bind ${className}Editor <Meta-Delete> {
	%W delete [blt::tv::PreviousWord %W insert] insert
    }
    # tkEntryNextWord -- Returns the index of the next word position
    # after a given position in the entry.  The next word is platform
    # dependent and may be either the next end-of-word position or the
    # next start-of-word position after the next end-of-word position.
    #
    # Arguments:
    # w -		The entry window in which the cursor is to move.
    # start -	Position at which to start search.
    
    if {![string compare $tcl_platform(platform) "windows"]}  {
	proc ::blt::tv::NextWord {w start} {
	    set pos [tcl_endOfWord [$w get] [$w index $start]]
	    if {$pos >= 0} {
		set pos [tcl_startOfNextWord [$w get] $pos]
	    }
	    if {$pos < 0} {
		return end
	    }
	    return $pos
	}
    } else {
	proc ::blt::tv::NextWord {w start} {
	    set pos [tcl_endOfWord [$w get] [$w index $start]]
	    if {$pos < 0} {
		return end
	    }
	    return $pos
	}
    }
    
    # PreviousWord --
    #
    # Returns the index of the previous word position before a given
    # position in the entry.
    #
    # Arguments:
    # w -		The entry window in which the cursor is to move.
    # start -	Position at which to start search.
    
    proc ::blt::tv::PreviousWord {w start} {
	set pos [tcl_startOfPreviousWord [$w get] [$w index $start]]
	if {$pos < 0} {
	    return 0
	}
	return $pos
    }

}

proc ::blt::tv::FmtStr {str {len 12}} {
    # Break a string into fixed size chunks.
    if {[string length $str]<=$len} {
        return $str
    }
    set lm [expr {$len-1}]
    set rc {}
    while {[string length $str]>0} {
        append rc [string range $str 0 $lm]
        set str [string range $str $len end]
        if {$str != {}} { append rc \n }
    }
    return $rc   
}

proc ::blt::tv::FmtString {str {len 12} {class alnum}} {
    # Wrap long strings at word boundries.
    if {[string length $str]<=$len} {
        return $str
    }
    if {[string is $class $str]} {
        return [FmtStr $str $len]
    }
    set rc {}
    set crc {}
    set lw 1
    foreach i [split $str {}] {
        set isw [string is $class $i]
        if {(($isw && $lw) || (!$isw && !$lw)) && [string length $crc]<$len} {
            append crc $i
        } else {
            lappend rc $crc
            set crc $i
        }
        set lw $isw
    }
    if {$crc != {}} {
        lappend rc $crc
    }
    set src {}
    set cln {}
    foreach i $rc {
        if {[string length $cln$i]<=$len} {
            append cln $i
        } else {
            if {$src != {}} { append src \n }
            append src $cln
            set cln $i
        }
    }
    append src \n $cln
    return $src   
}



