#!/bin/sh
# the next line restarts using wish \
exec wish "$0" "$@" -name fileShell

if {[info tclversion] < 7.6} {
	puts "You must have Tcl version 7.6 or above to run this program."
	exit
}

set version 0.7


proc err_msg {msg} {
        tk_messageBox -icon error -title "Error" -message $msg -type ok
}


proc input_box {w transient width title label select} {
	global input

	toplevel $w -class Input
	if $transient {
		set bd 5
		wm transient $w .
	} else {
		set bd 20
		wm title $w $title
	}

	if {$label != ""} {
		label $w.label -text $label
		pack $w.label -side left -padx 1.5m -pady 5m
	}
	entry $w.entry -textvariable input -width $width
	pack $w.entry -side left -padx 3m -pady 5m

	wm withdraw $w
	update idletasks
	set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 - 5]
	set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2 - $bd]
	wm geom $w [winfo reqwidth $w]x[winfo reqheight $w]+$x+$y
	wm deiconify $w

	bind $w.entry <Return> { 
		destroy [winfo toplevel %W] 
	}
	bind $w.entry <Escape> {
		set input ""
		destroy [winfo toplevel %W]
	}

	if $select {
		$w.entry selection range 0 end
	}
	update
	event generate $w <Tab>
	update
}


proc refresh_list {l d} {
	global dir old_dir old_active filter

	if [catch {eval exec ls -Fab {$dir($l)}} res] {
		err_msg $res
		return 1
	}

	set res [lrange $res 1 end]
	set dirs {}
	set files {}
	foreach i $res {
		set si [string trim $i " *@"]
		if [file isdirectory $si] {
			lappend dirs " $i"
		} else {
			if [string match $filter($l) $si] {
				lappend files " $i"
			}
		}
	}

	$l delete 0 end
	eval $l insert end $dirs $files
	
	if $d {
		if {$d == 2} {
			set i [lsearch -regexp $dirs \
				"^ [file tail $old_dir($l)]"]
			$l activate $i
			$l selection set $i $i
			$l see $i
		} else {
			$l activate 0
			$l selection set 0 0
		}
	} else {
		$l activate $old_active($l)
		$l selection set $old_active($l) $old_active($l)
		$l see $old_active($l)
	}
	return 0
}


proc invoke {f mode} {
	global runcmds

	if {! [llength $runcmds]} {return}

	switch $mode {
	  255 {
		foreach i $runcmds {
			if [regexp [lindex $i 0] $f] {
				if [catch {eval exec [lrange $i 1 end] {$f} \
					>/dev/null 2>/dev/null &} res] {
					err_msg $res
				}
				break
			}
		}
	      }
	  256 {
		exec xterm -e less $f &
	      }
	  257 {
		exec xterm -e vi $f &
	      }
	}
}


proc file_info {l} {
	global dir input
	
	if {$dir($l) != [pwd]} {
		cd $dir($l)
	}
	set f [string trim [$l get active] " *@/"]
	set input [eval exec ls -Fadl {$f}]
	input_box .finfo 0 60 "Extended file information" {} 0
	tkwait window .finfo
}


proc filter {l mode} {
	global filter input
		
	set titles {{Selection Filter:} {Path Filter:}}
	
	if {$mode} {
		set input $filter($l)
	} else {
		set input "*"
	}

	input_box .filter 1 20 {} [lindex $titles $mode] 1
	tkwait window .filter
	if {$input == ""} {
		return
	}

	if {$mode} {
		set filter($l) $input
		refresh_list $l 1
	} else {
		if {! [catch {glob $input} r]} {
			set list [$l get 0 end]
			$l selection clear 0 end
			foreach n $r {
				set i [lsearch $list " $n*"]
				$l selection set $i $i
				$l see $i
			}
		}
	}
}


proc same_dir {src mode} {
	global input

	set title {Copy Rename {Create Directory}}
	set action {copy rename mkdir}
	
	if {$mode == 1} {
		set label "New Name:"
	} else {
		set label "Name:"
	}

	if {$mode != 2} {
		set input $src
	} else {
		set input ""
	}

	input_box .same_dir 0 40 [lindex $title $mode] $label 1
	tkwait window .same_dir
	if {$input == ""} {
		return
	}

	if {$mode == 2} {
		set sd "{$input}"
	} else {
		set sd "{$src} {$input}"
	}
	if [catch {eval file [lindex $action $mode] $sd} res] {
		err_msg $res
	}
}


proc process {l mode} {
	global dir old_dir src other_list old_active _mode
	set rmcmd {rm rmdir}

	set old_active($l) [$l index active]

	cd $dir($l)

	if {$mode >= 255} {
		set f [string trim [$l get active] " *@"]
		if [file isdirectory $f] {
			set up 1
			if [string match "../" $f] {
				set up 2
			}
			set old_dir($l) $dir($l)
			if [catch {cd $f} res] {
				err_msg $res
			} else {
				set dir($l) [pwd]
				if [refresh_list $l $up] {
					cd $old_dir($l)
					set dir($l) $old_dir($l)
				}
			}
		} else {
			if {[file executable $f] && $mode == 255} {
				if [catch {exec $f &} res] {
					err_msg $res
				}
			} else {
				invoke $f $mode
			}
		}
	} else {
		set sel {}
		foreach i [$l curselection] {
			lappend sel [string trim [$l get $i] " *@"]
		}
		if {$mode < 3} {
			foreach i $sel {
				same_dir $i $mode
				refresh_list $l 0
			}
		} else {
			set dest $dir($other_list($l))
			switch $mode {
		  	  3 { if [catch {eval file copy -force $sel {$dest}} res] {
				err_msg $res 
			      }
			  }
			  4 { if [catch {eval file rename -force $sel {$dest}} res] {
				err_msg $res 
			      }
			  }
			  5 { if [catch {eval file delete -force $sel} res] {
			       	err_msg $res 
			      }
			    }
			}
			if {$mode == 3 || $mode == 4 || \
				$dir($other_list($l)) == $dir($l)} {
				refresh_list $other_list($l) 0
			}
			refresh_list $l 0
		}
	}
}


##########################################################################


set dir(.list0) [pwd]
set dir(.list1) $dir(.list0)

if {$argc} {
	set d [lindex $argv 0]
	if {![file isdirectory $d]} {
		puts "Invalid directory $d"
		exit 1
	} else {
		set dir(.list0) $d
	}
	if {$argc > 1} {
		set d [lindex $argv 1]
		if {![file isdirectory $d]} {
			puts "Invalid directory $d"
			exit 1
		} else {
			set dir(.list1) $d
		}
	}
}

set lwidth [option get . lwidth FileShell]
if {$lwidth == ""} {
	set lwidth 55
}
set lheight [option get . lheight FileShell]
if {$lheight == ""} {
	set lheight 15
}

set runcmds {}
if [file exists ~/.fs.ext] {
	set f [open ~/.fs.ext]
	while {[gets $f line] >= 0} {
		if {[string index $line 0] == "#" || \
			[regexp {^[ 	]*$} $line]} {
			continue
		}
		lappend runcmds $line
	}
	close $f
}


wm title . "File Shell v$version"
wm geometry . [format "%sx%s" $lwidth $lheight]

frame .spacer -height 2m
pack .spacer -side top

label .label0 -width 1 -textvariable dir(.list0) -anchor w
pack .label0 -side top -padx 3m -fill x
listbox .list0 -width 1 -height 1 -highlightthickness 0 \
	-selectmode extended -setgrid 1
pack .list0 -side top -padx 2m -pady 2m -expand yes -fill both

label .label1 -width 1 -textvariable dir(.list1) -anchor w
pack .label1 -side top -padx 3m -fill x
listbox .list1 -width 1 -height 1 -highlightthickness 0 \
	-selectmode extended
pack .list1 -side top -padx 2m -pady 2m -expand yes -fill both


bind Listbox <F3> {
	if {[llength [%W  curselection]] > 1} {
		err_msg "Error: Selection > 1"
	} else {
		process %W 256
	}
}

bind Listbox <F4> {
	if {[llength [%W  curselection]] > 1} {
		err_msg "Error: Selection > 1"
	} else {
		process %W 257
	}
}

bind Listbox <F5> {
	process %W 0
}

bind Listbox <Control-F5> {
	process %W 3
}

bind Listbox <F6> {
	process %W 1
}

bind Listbox <Control-F6> {
	process %W 4
}

bind Listbox <F7> {
	process %W 2
}

bind Listbox <F8> {
	process %W 5
}

bind Listbox <Return> {
	if {[llength [%W  curselection]] > 1} {
		err_msg "Error: Selection > 1"
	} else {
		process %W 255
	}
}

bind Listbox <Tab> {
	focus $other_list(%W)
	[focus] selection set active active
}

bind Listbox <Home> {
	%W activate 0
	%W see 0
	%W selection clear 0 end
	%W selection set 0
}

bind Listbox <End> {
	%W activate end
	%W see end
	%W selection clear 0 end
	%W selection set end
}

bind Listbox <Insert> {
	tkListboxExtendUpDown %W 1
}

bind Listbox <KeyPress> {
	switch %A {
	  q { 
		exit 0
	    }
	  \$ {
		cd [set dir(%W) $env(HOME)]
		set old_dir(%W) $dir(%W)
		refresh_list %W 1
	    }
	  / {
		cd [set dir(%W) /]
		set old_dir(%W) $dir(%W)
		refresh_list %W 1
	    }
	  = {
		cd [set dir(%W) $dir($other_list(%W))]
		set old_dir(%W) $dir(%W)
		refresh_list %W 1
	    }
	  + {
		filter %W 0
	    }
	  default {
		if {[set i [lsearch -regexp [%W get 0 end] "^ %A"]] != -1} {
			%W activate $i
			%W see $i
			%W selection clear 0 end
			%W selection set $i
		}
	    }
	}
}

bind Listbox <Alt-Key-i> {
	file_info %W
}

bind Listbox <Alt-Key-f> {
	filter %W 1
}

bind Listbox <Alt-Key-r> {
	cd $dir(%W)
	refresh_list %W 1
}

bind Listbox <Alt-Key-x> {
	cd $dir(%W)
	exec xterm &
}


set other_list(.list0) .list1
set other_list(.list1) .list0

set old_dir(.list0) $dir(.list0)
set old_dir(.list1) $dir(.list1)

set filter(.list0) *
set filter(.list1) *

set old_active(.list0) 0
set old_active(.list1) 0 

refresh_list .list1 1
refresh_list .list0 1

focus .list0
