# Copyright (c) 1993 by Sanjay Ghemawat
#############################################################################
# File Selector Widget

# Autoloading support
proc FileSelector {} {}

class FileSelector {name} {
    set slot(window) 	$name
    set slot(directory) [$self canonicalize .]
    set slot(child) 	{}
    set slot(all) 	0

    # Create frame structure
    frame $name -bd 1

    set slot(parents) [StringBrowser $name.parents Parents left]
    set slot(children) [StringBrowser $name.children Children right]
    $name.parents.box configure -geometry 20x10
    $name.children.box configure -geometry 20x10

    checkbutton $name.showall -relief raised -bd 1 -anchor w\
	-text {Show Dot-Files}\
	-onvalue 1 -offvalue 0\
	-command [list $self toggle_all]

    entry $name.entry -relief sunken -bd 1

    pack append $name $name.entry {bottom expand fillx}
    pack append $name $name.children {right expand fill}
    pack append $name $name.parents {top expand fill}
    pack append $name $name.showall {bottom fillx}

    bind $name.parents.box <ButtonRelease-1> [list $self parent]
    bind $name.children.box <ButtonRelease-1> [list $self change_file]

    bind $name.entry <Tab> [list $self complete]
    bind $name.entry <space> [list $self complete]

    $self rescan
    $self setentry
}

# effects - Return canonicalized version of $dir
method FileSelector canonicalize {dir} {
    # Convert directory name to full file name
    if {$dir == ""} {set dir "."}
    if {[string index $dir 0] == "~"} {
	# Perform tilde expansion
	catch {set dir [concat [file rootname $dir] [file extension $dir]]}
    }

    set leader [string index $dir 0]
    if {($leader != "~") && ($leader != "/")} {
	# Name is relative
	set dirlist [split $dir "/"]
	set dir [pwd]

	foreach component $dirlist {
	    if {$component != "."} {
		set dir "$dir/$component"
	    }
	}
    }
    return $dir
}

# effects - Return file name for dir/child
method FileSelector descend {dir child} {
    if {$dir == "/"} {
	return "/$child"
    } else {
	return "$dir/$child"
    }
}

# effects - Set entry from directory/child
method FileSelector setentry {} {
    # Set-up entry
    $slot(window).entry delete 0 end
    $slot(window).entry insert 0 [$self descend $slot(directory) $slot(child)]
}

# effects - Update listbox contents for dir
method FileSelector rescan {} {
    # Set the show-all button
    if $slot(all) {
	$slot(window).showall select
    } else {
	$slot(window).showall deselect
    }

    # Fill children list
    set dir $slot(directory)
    set contents {}
    if $slot(all) {
	catch {set contents [lsort [glob -nocomplain $dir/.* $dir/*]]}
    } else {
	catch {set contents [lsort [glob -nocomplain $dir/*]]}
    }

    $slot(children) clear
    catch {
	foreach file $contents {
	    $slot(children) add [file tail $file]
	}
    }

    # Fill parent list
    set ancestors {}
    catch {
	while {$dir != "/"} {
	    set ancestors [linsert $ancestors  0 [file tail $dir]]
	    set dir [file dirname $dir]
	}
	set ancestors [linsert $ancestors 0 "/"]
    }

    $slot(parents) clear
    foreach dir $ancestors {
	$slot(parents) add $dir
    }
}

method FileSelector goto {str} {
    if [catch {file isdirectory $str}] {
	# Tilde-expansion problems
	return
    }

    if [file isdirectory $str] {
	$self cd $str
	return
    }

    $self cd [file dirname $str]
    set slot(child) [file tail $str]
    $self setentry
}

method FileSelector cd {dir} {
    set slot(directory) [$self canonicalize $dir]
    set slot(child) ""
    $self rescan
    $self setentry
}

method FileSelector toggle_all {} {
    set slot(all) [expr "!$slot(all)"]
    $self rescan
    $self setentry
}

method FileSelector parent {} {
    set browser $slot(parents)
    if [catch {set index [$browser selection]}] {
	return
    }

    set dir ""
    for {set i 1} {$i <= $index} {incr i} {
	set dir "$dir/[$browser fetch $i]"
    }
    if {$dir == ""} {set dir "/"}

    $self cd $dir
}

method FileSelector change_file {} {
    set browser $slot(children)

    if [catch {set file [$browser fetch [$browser selection]]}] {
	return
    }

    set slot(child) $file
    set file [$self descend $slot(directory) $slot(child)]

    catch {
	if [file isdirectory $file] {
	    $self cd $file
	    return
	}
    }

    $self setentry
}

method FileSelector complete {} {
    set str [$self canonicalize [$slot(window).entry get]]

    if [string match */ $str] {
	set str [string range $str 0 [expr [string length $str]-2]]
    }

    set complete ""
    catch {set complete [lsort [glob -nocomplain $str*]]}
    if {[llength $complete] == 1} {
	set str [lindex $complete 0]
    }

    $self goto $str
}

method FileSelector filename {} {
    return [$slot(window).entry get]
}
