tkoo::dtree

GJS 2012/5/6 The dtree widget is a directory tree.

Methods

  • BindAsterisk Private method called when the user presses * to expand all child nodes of the selected node
  • BindShiftAsterisk Private method called when the user presses Shift+* to collapse all child nodes of the selected node
  • BindF5 Private method called when the user presses F5 to refresh the selected node
  • BindTreeviewOpen Private method called when a treeview item is opened
  • Collapse node Private method called to fully collapse a node
  • Expand node Private method called to fully expand a node
  • get Returns the current selection
  • ListFile node type file Private method called to list a file
  • location ?directory? if directory is supplied changes the location of the dtree, otherwise it returns the location
  • Populate node ?dir? ?expand? Private method called to list subdirectories.

Options (Also supports standard ttk::treeview options)

  • -images is a dict defining the images to be displayed for items in the browser.
    • file
    • directory
    • drive
  • -list is a list of elements to be shown in the browser
    • hidden list hidden files and directories
    • directory, directories, folder, or folders list directories
    • file, or files list files
    • all shortcut to list everything, default
  • -singleexpand Only allow one directory to be expanded at a time. Set to true or false.
#tkoo/dtree-v0.1.1.tm
package provide tkoo::dtree 0.1.1

package require tkoo

tkoo::class ::tkoo::dtree {
        superclass tkoo::ttk_treeview
        variable widCmd pathname options exists location
        
        constructor {wid args} {
                #check to see if the class already exists
                my Exists $wid
                
                #defaults
                set pathname $wid
                set location [pwd]
                
                #create the widget
                if {![winfo exists $wid]} {
                        ttk::treeview $wid -columns [list fullpath expanded] -displaycolumns [list] -selectmode browse
                }
                
                #options
                #use in list of pairs, list type and image
                #-images [list drive driveimage directory dirimage file fileimage]
                #use dict
                my Opt add -images images Images [list] {
                        my variable widCmd pathname options exists location images
                        if {[llength $value] % 2} {
                                error [msgcat::mc "list must have an even number of elements"]
                        }
                }
                
                #what items to list should be a list of the following, or all
                #directory files hidden
                #defaults to all
                my Opt add -list list List [list directory files hidden] {
                        my variable widCmd pathname options exists location images
                        set list [list]
                        foreach v $value {
                                switch -exact -- $v {
                                        hidden {lappend list $v}
                                        directory - directories - folder - folders {lappend list directory}
                                        file - files {lappend list files}
                                        all {set list [list directory files hidden]}
                                        default {error [msgcat::mc "bad type \"$v\" for \"-list\": must be a list containing current, directory, drives, files, hidden, or up"]}
                                }
                        }
                        set options(-list,val) [lsort -dictionary -unique $list]
                }
                
                my Opt add -singleexpand singleExpand SingleExpand false {
                        my variable widCmd pathname options exists location images
                        if {![string is boolean -strict $value]} {error [msgcat::mc "expected boolean value but got \"%s\"" $value]}
                }
                
                #bindings
                bind $wid <<TreeviewOpen>> [namespace code {my BindTreeviewOpen}]
                bind $wid <*> [namespace code {my BindAsterisk}]
                bind $wid <Shift-*> [namespace code {my BindShiftAsterisk}]
                bind $wid <F5> [namespace code {my BindF5}]
                
                #default code
                next $wid {*}$args
                
                #configure the widget
                my configure {*}$args
        }
        
        #methods for bindings
        method BindTreeviewOpen {} {
                if {[my Opt get -singleexpand]} {
                        set node [my focus]
                        #get siblings
                        set siblings [lsearch -exact -all -inline -not [my children [my parent $node]] $node]
                        
                        #collapse siblings
                        foreach s $siblings {
                                my Collapse $s
                        }
                        
                        #scroll to show node
                        my see $node
                }
                my Populate [my focus] "" 1
        }
        method BindAsterisk {} {
                if {![my Opt get -singleexpand]} {
                        my Expand [my focus]
                }
        }
        method BindShiftAsterisk {} {
                if {![my Opt get -singleexpand]} {
                        my Collapse [my focus]
                }
        }
        
        method BindF5 {} {
                set node [my focus]
                my set $node expanded no
                my Populate $node "" 1
        }
        
        #method set the top level of the tree widget and return top level directory
        method location {{directory ""}} {
                if {$directory ne ""} {
                        if {[file isdirectory $directory]} {
                                my Populate {} $directory 1
                                set location $directory
                        }
                }
                return $location
        }
        
        #method to populate the treeview
        method Populate {node {dir {}} {expand 0}} {
                #set the dir variable properly
                if {$node eq {} && $dir eq ""} {
                        set dir [file normalize ~]
                } elseif {$node ne {}} {
                        #has the tree already been expanded
                        if {[my set $node expanded]} {
                                if {$expand} {
                                        foreach c [my children $node] {
                                                my Populate $c
                                        }
                                }
                                return
                        }
                        set dir [my set $node fullpath]
                } else {
                        set dir [file normalize $dir]
                }
                
                #clear children
                my delete [my children $node]
                
                #get options
                set list [my Opt get -list]
                
                #list directories
                if {"directory" in $list} {
                        set dList [list]
                        if {"hidden" in $list} {
                                catch {lappend dList {*}[glob -directory $dir -nocomplain -types [list d hidden] *]}
                        }
                        catch {lappend dList {*}[glob -directory $dir -nocomplain -types d *]}
                        set dList [lsort -dictionary -unique $dList]
                        foreach d $dList {
                                set id [my ListFile $node directory $d]
                                if {$expand} {
                                        my Populate $id
                                }
                        }
                }
                
                if {"files" in $list} {
                        set dList [list]
                        if {"hidden" in $list} {
                                catch {lappend dList {*}[glob -directory $dir -nocomplain -types [list f hidden] *]}
                        }
                        catch {lappend dList {*}[glob -directory $dir -nocomplain -types f *]}
                        set dList [lsort -dictionary -unique $dList]
                        foreach d $dList {
                                my ListFile $node file $d
                        }
                }
                
                #set flag that tells us not to run next time
                my set $node expanded yes
        }
        
        #method to list a file
        method ListFile {node type file} {
                set a [list]
                set imgs [my Opt get -images]
                switch -exact -- $type {
                        file {
                                if {[dict exists $imgs file]} {
                                        set img [dict get [my Opt get -images] file]
                                        lappend a -image $img
                                }
                        }
                        directory -
                        folder {
                                if {[dict exists $imgs directory]} {
                                        set img [dict get [my Opt get -images] directory]
                                        lappend a -image $img
                                }
                        }
                        volume -
                        drive {
                                if {[dict exists $imgs drive]} {
                                        set img [dict get [my Opt get -images] drive]
                                        lappend a -image $img
                                }
                        }
                        default {return}
                }
                
                if {[string length file]} {
                        set id [my insert $node end -text [file tail $file] {*}$a]
                        my set $id expanded no
                        my set $id fullpath $file
                        if {$type ne "file"} {
                                my insert $id 0 -text <load>
                        }
                }
                return $id
        }
        
        #method to fully expand a node
        method Expand {node} {
                set nList $node
                while {[llength $nList]} {
                        set n [lindex $nList 0]
                        set nList [lrange $nList 1 end]
                        my item $n -open true
                        my Populate $n
                        foreach cn [my children $n] {
                                if {$cn ne ""} {
                                        lappend nList $cn
                                }
                        }
                }
        }
        
        #method to fully collapse a node
        method Collapse {node} {
                set nList $node
                while {[llength $nList]} {
                        set n [lindex $nList 0]
                        set nList [lrange $nList 1 end]
                        my item $n -open false
                        foreach cn [my children $n] {
                                if {$cn ne ""} {
                                        lappend nList $cn
                                }
                        }
                }
        }
        
        #method to get current selection
        method get {args} {
                set nodes [my selection]
                set ret [list]
                
                foreach n $nodes {
                        set f [my set $n fullpath]
                        lappend ret $n $f
                }
                
                return $ret
        }
}