NeXT-style file browser

Kevin Walzer I was trying to create a variation of the standard Tk file browser handled with a listbox, which would emulate the NeXT (now Mac OS X) style of file browsing: navigating horizontally across a file system. I hit a wall and asked for help on comp.lang.tcl. Kaitzschu came to the rescue and did more than add some guidance to my rough initial code; he radically transformed it. Thanks to Kaitzchu for the help.

The result is below, licensed under the standard Tcl BSD-style license. Embellishments and improvements are more than welcome!

 package require Tcl 8.5
 package require Tk

 namespace eval ::nextlist {namespace export nextlist}

 proc ::nextlist::nextlist {wid dir} {
   if {![file isdirectory $dir]} {error "non-directory $dir"}
   nlist [frame $wid] $dir 0
   return $wid
 }

 proc ::nextlist::nlist {base dir cnt} {
   if {![file isdirectory $dir]} {return}; # here file-clicketyclicks
   # check for [file readable]? nah
   # dear santa, let children be in order
   destroy {*}[lrange [winfo children $base] [expr {2*$cnt}] end]
   # exportselection 0 looks good, but selection gets easily out-of-sync
   set l [listbox "[set b "$base.nl$cnt"]-list" -yscrollcommand [list "$b-scroll" set] -height 20 -exportselection 0]
   pack $l [scrollbar "$b-scroll" -command [list $l yview]] -side left -expand 1 -fill y -anchor w
   # I like my directories first and everything dictionarized, love {*}
   # could be done with intermediate list and $l insert end {*}$lst
   foreach item [concat [lsort -dictionary [glob -directory $dir -nocomplain -types {d} -- *]] \
                        [lsort -dictionary [glob -directory $dir -nocomplain -types {f} -- *]]] \
           {$l insert end "[file tail $item][expr {[file isdirectory $item] ? {/} : {}}]"}
   bind $l <Double-1> [list ::nextlist::navigare $dir [incr cnt] %W %x %y]
 }

 proc ::nextlist::navigare {d c w x y} {
 if {[set subdir [$w get [$w index "@$x,$y"]]] eq {}} {return}
 nlist [winfo parent $w] [file join $d $subdir] $c
 }

 # To test this code, try the following command: 
   pack [::nextlist::nextlist .nl ~/Desktop]

You will see listboxes added and deleted horizontally as you navigate through the file hierarchy.

With all due respect to the author's love of {*}, the second use of it is rather pointless:

   [list {*}[lsort ...] {*}[lsort ...]]

is equivalent to, but not as clear as

   [concat [lsort ...] [lsort ...]]

Kaitzschu Guilty as charged. Use concat, it is also very, very fast once lists get bigger. I wasn't familiar with this command, because its manual page is confounding wrt use with lists (trimming and spaces, sounds too much like strings).

jcw - And by writing "eval destroy [...]", the same code runs in 8.x ...

NEM - Removed a rogue "09" in the code that caused a dreaded octal error. Not sure where the stray 9 came from.

RKzn 20161027 - changed

    if {![file isdirectory $dir]} {return; # here file-clicketyclicks}

to

    if {![file isdirectory $dir]} {return}; # here file-clicketyclicks

because the first one breaks auto-indentation, with emacs' tcl-mode; and I agree with it that the second version it is clearer.

Also changed the "[list {*}[lsort ..." to "[concat [lsort ..." as suggested above and it works just fine (8.6.3)