#!/bin/sh  
# \
exec oagwish "$0" "$@"

#
# $Log: not supported by cvs2svn $
# Revision 1.10  2005/05/21 20:03:47  borland
# Added APSStandardSetup to avoid a problem with an undefined variable.
#
# Revision 1.9  2002/07/10 19:39:44  soliday
# Added features requested by Mike.
#
# Revision 1.8  2002/07/08 15:06:41  soliday
# Added an adjustable timeout value.
#
# Revision 1.7  2002/03/22 16:25:52  soliday
# Changed the time stamps to avoid a bug in "clock scan"
#
# Revision 1.6  2002/02/27 23:15:45  soliday
# Added some startup options.
#
# Revision 1.5  2002/02/27 22:57:30  soliday
# Added the ability to sync directories.
#
# Revision 1.4  2002/02/26 22:57:24  soliday
# Added the ablility to upload and download directories.
#
# Revision 1.3  2002/02/26 21:28:34  soliday
# Fixed but with get and put on linux machines.
#
# Revision 1.2  2002/02/26 20:49:37  soliday
# Added a password prompt.
#
# Revision 1.1  2002/02/25 22:56:52  soliday
# First version
#
#
#

set auto_path [linsert $auto_path 0 /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSStandardSetup

if {[catch {package require Expect} results]} {
    APSAlertBox [APSUniqueName .] -errorMessage "$results"
    exit 1
}

set CVSRevisionAuthor "\$Revision: 1.11 $ \$Author: borland $"

set apsScaleUpSize -1

proc CreateFilter {args} {
    APSSetVarAndUpdate status "Creating filter file"
    APSSetVarAndUpdate status "Remember to load the filter file when done editing it"
    set fileName [APSTmpDir]/[APSTmpString]
    APSAddToTempFileList $fileName
    set filterdata(ParameterNames) "Type"
    set filterdata(ColumnNames) "Filter"
    set filterdata(Parameter.Type) [list Exclusion Inclusion]
    set filterdata(Column.Filter) [list "" ""]
    if {[catch {sdds save $fileName filterdata} result]} {
	APSSetVarAndUpdate status $result
	bell
	return
    }
    if {[catch {exec sddsedit $fileName &} result]} {
	APSSetVarAndUpdate status $result
	bell
	return
    }
}

proc EditFilter {args} {
    set fileName [APSFileSelectDialog .openDialog -contextHelp "Select a filter file" -title "Select a filter file" -path [pwd]]
    if {[llength $fileName]} {
	if {![file exists $fileName]} {
	    CreateFilter
	} else {
	    APSSetVarAndUpdate status "Editing filter file"
	    APSSetVarAndUpdate status "Remember to load the filter file when done editing it"
	    if {[catch {exec sddsedit $fileName &} result]} {
		APSSetVarAndUpdate status $result
		bell
		return
	    }
	}
    }
}

set filters(Inclusion) ""
set filters(Exclusion) ""
proc LoadFilter {args} {
    set fileName [APSFileSelectDialog .openDialog -contextHelp "Select a filter file" -title "Select a filter file" -path [pwd]]
    if {[llength $fileName]} {
	APSSetVarAndUpdate status "Loading filter file"
	if {[catch {sdds load $fileName filterdata} result]} {
	    APSSetVarAndUpdate status $result
	    bell
	    return
	}
	if {![info exists filterdata(Parameter.Type)]} {
	    APSSetVarAndUpdate status "Parameter called Type is missing"
	    bell
	    return	    
	}
	if {![info exists filterdata(Column.Filter)]} {
	    APSSetVarAndUpdate status "Column called Filter is missing"
	    bell
	    return	    
	}
	global filters
	foreach type $filterdata(Parameter.Type) filter $filterdata(Column.Filter) {
	    set filters($type) $filter
	}
    }
}

proc CheckForValidHost {} {
    global remoteHost tcl_platform
    if {$tcl_platform(os) == "Linux"} {
	if [catch {exec ping -q -c 1 -w 10 $remoteHost | fgrep "1 received"} result] {
	    APSSetVarAndUpdate status "Problem pinging $remoteHost: $result"
	    return -code error
	}
	if {![llength $result]} {
	    APSSetVarAndUpdate status "$remoteHost is not responding"
	    return -code error
	}	    
    } else {
	if [catch {exec ping $remoteHost 2} result] {
	    APSSetVarAndUpdate status "Problem pinging $remoteHost: $result"
	    return -code error
	}
	if [string compare $result "$remoteHost is alive"]!=0 {
	    APSSetVarAndUpdate status "$remoteHost is not responding"
	    return -code error
	}
    }
}

proc SyncDirectory {} {
    global boxtop boxbottom
    set localIndexes [$boxtop(name) curselection]
    set remoteIndexes [$boxbottom(name) curselection]
    if {([llength $localIndexes] > 1) || ([llength $remoteIndexes] > 1)} {
	APSSetVarAndUpdate status "Please selected only one directory to sync at a time"
	return
    }
    if {([llength $localIndexes] == 0) && ([llength $remoteIndexes] == 0)} {
	APSSetVarAndUpdate status "Please selected a directory to sync"
	return
    }
    if {([llength $localIndexes] == 1) && ([llength $remoteIndexes] == 1)} {
	if {[$boxtop(name) get $localIndexes] != [$boxbottom(name) get $remoteIndexes]} {
	    APSSetVarAndUpdate status "Remote and local directories selected are not the same"
	    return
	}
    }
    if {[llength $localIndexes]} {
	set dirname [$boxtop(name) get $localIndexes]
    } else {
	set dirname [$boxbottom(name) get $remoteIndexes]
    }
    TransferFiles -sync 1
}

proc PrepareSync {args} {
    set onlydirectories 0
    APSStrictParseArguments {onlydirectories}
    global boxtop boxbottom
    global filesRemote files
    $boxtop(name) selection clear 0 end
    $boxbottom(name) selection clear 0 end

    if {$onlydirectories} {
	set i -1
	foreach l_name $files(name) l_size $files(size) {
	    incr i
	    if {($l_size == " Directory") && ($l_name != "..")} {
		$boxtop(name) selection set $i
	    }
	}
	set i -1
	foreach r_name $filesRemote(name) r_size $filesRemote(size) {
	    incr i
	    if {($r_size == " Directory") && ($r_name != "..")} {
		$boxbottom(name) selection set $i
	    }
	}
	return
    }
    set i -1
    foreach l_name $files(name) l_bytes $files(bytes) l_size $files(size) l_mtime $files(mtime) {
	incr i
	if {($l_size == " Directory") || ($l_mtime == "Invalid Link")} {
	    continue
	}
	set index [lsearch -exact $filesRemote(name) $l_name]
	if {$index == -1} {
	    $boxtop(name) selection set $i
	} else {
	    if {$l_bytes == [lindex $filesRemote(bytes) $index]} {
		continue
	    }
	    if {[clock scan $l_mtime] > [clock scan [lindex $filesRemote(mtime) $index]]} {
		$boxtop(name) selection set $i
	    } elseif {[clock scan $l_mtime] < [clock scan [lindex $filesRemote(mtime) $index]]} {
		$boxbottom(name) selection set $index
	    } else {
		APSSetVarAndUpdate "Unable to determine newer file for $l_name"
	    }
	}
    }
    set i -1
    foreach r_name $filesRemote(name) r_size $filesRemote(size) {
	incr i
	if {$r_size == " Directory"} {
	    continue
	}
	set index [lsearch -exact $files(name) $r_name]
	if {$index == -1} {
	    $boxbottom(name) selection set $i
	}
    }
}

proc TransferFiles {args} {
    set mode ""
    set sync 0
    APSStrictParseArguments {mode sync}
    global remoteUserName remoteHost remoteDir password transfertimeout timeout localDir expect_out
    global boxtop boxbottom
    global filesRemote files tcl_platform transfermaxsize filters
    set maxsize [expr {$transfermaxsize * 1000.0}]

    set filenames ""
    set dirnames ""
    if {$mode == "download"} {
	set indexes [$boxbottom(name) curselection]
	foreach index $indexes {
	    if {[lindex $filesRemote(size) $index] == " Directory"} {
		if {$tcl_platform(os) == "Linux"} {
		    lappend dirnames [lindex $filesRemote(name) $index]
		} else {
		    lappend filenames [lindex $filesRemote(name) $index]
		}
	    } else {
		if {$maxsize >= [lindex $filesRemote(bytes) $index]} {
		    set f [lindex $filesRemote(name) $index]
		    set add 1
		    if {[llength $filters(Exclusion)]} {
			foreach ex $filters(Exclusion) {
			    if {[lsearch -glob $f $ex] != -1} {
				set add 0
			    }
			}
		    }
		    if {[llength $filters(Inclusion)]} {
			set add 0
			foreach in $filters(Inclusion) {
			    if {[lsearch -glob $f $in] != -1} {
				set add 1
			    }
			}
		    }
		    if {$add} {
			lappend filenames $f
		    }
		}
	    }
	}
    } elseif {$mode == "upload"} {
	set indexes [$boxtop(name) curselection]
	foreach index $indexes {
	    if {[lindex $files(size) $index] == " Directory"} {
		if {$tcl_platform(os) == "Linux"} {
		    lappend dirnames [lindex $files(name) $index]
		} else {
		    lappend filenames [lindex $files(name) $index]
		}
	    } else {
		if {$maxsize >= [lindex $files(bytes) $index]} {
		    set f [lindex $files(name) $index]
		    set add 1
		    if {[llength $filters(Exclusion)]} {
			foreach ex $filters(Exclusion) {
			    if {[lsearch -glob $f $ex] != -1} {
				set add 0
			    }
			}
		    }
		    if {[llength $filters(Inclusion)]} {
			set add 0
			foreach in $filters(Inclusion) {
			    if {[lsearch -glob $f $in] != -1} {
				set add 1
			    }
			}
		    }
		    if {$add} {
			lappend filenames $f
		    }
		}
	    }
	}
    } elseif {$sync == 1} {
	set indexes [$boxbottom(name) curselection]
	set remotedirnames ""
	set localdirnames ""
	foreach index $indexes {
	    if {[lindex $filesRemote(size) $index] == " Directory"} {
		lappend remotedirnames [lindex $filesRemote(name) $index]
	    }
	}
	set indexes [$boxtop(name) curselection]
	foreach index $indexes {
	    if {[lindex $files(size) $index] == " Directory"} {
		lappend localdirnames [lindex $files(name) $index]
	    }
	}
	set dirnames [lsort -dictionary -unique "$remotedirnames $localdirnames"]
	set newdir(remote) ""
	set newdir(local) ""
	foreach dir $localdirnames {
	    if {[lsearch -exact $remotedirnames $dir] == -1} {
		lappend newdir(remote) $dir
	    }	
	}
	foreach dir $remotedirnames {
	    if {[lsearch -exact $localdirnames $dir] == -1} {
		lappend newdir(local) $dir
	    }	
	}
    }
    set nfiles [llength $filenames]
    set nfile 0
    set ndirs [llength $dirnames]
    set ndir 0
    set nlocaldir 0
    set nremotedir 0
    if {(!$nfiles) && (!$ndirs)} {
	return
    }
    if {[catch {CheckForValidHost} results]} {
	return
    }
    set timeout $transfertimeout
    spawn -noecho sftp ${remoteUserName}@${remoteHost}
    match_max -i $spawn_id 50000
    log_user 0
    set fileInfo ""

    if {[llength $remoteDir]} {
	set step 1
    } else {
	set step 2
    }
    expect {
	"Are you sure you want to continue connecting (yes/no)?" {
	    exp_send "yes\r"
	    exp_continue
	} "Do you want to change the host key on disk (yes/no)?" {
	    exp_send "yes\r"
	    exp_continue
	} "password:" {
	    APSSetVarAndUpdate status "Sending password"
	    exp_send "${password}\r"
	    exp_continue
	} "sftp>" {
	    if {$step == 1} {
		incr step
		exp_send "cd ${remoteDir}\r"
		exp_continue
	    } elseif {$step == 2} {
		incr step
		exp_send "lcd ${localDir}\r"
		exp_continue
	    } elseif {$step == 3} {
		incr step
		APSSetVarAndUpdate status "Getting remote directory name"
		exp_send "pwd\r"
		exp_continue
	    } elseif {$step == 4} {
		if {$tcl_platform(os) == "Linux"} {
		    set remoteDir [lrange [string trimright [lindex [split $expect_out(buffer) \n] 1] \r] 3 end]
		} else {
		    set remoteDir [string trimright [lindex [split $expect_out(buffer) \n] 1] \r]
		}
		if {$sync == 1} {
		    set step 20
		} else {
		    incr step
		}
		exp_send "\r"
		exp_continue
	    } elseif {$step == 5} {
		if {$tcl_platform(os) == "Linux"} {
                    APSSetVarAndUpdate status "Down/up load $nfile/$nfiles"
		    if {!$nfiles} {
			incr step
			exp_send "\r"
		    } else {
			if {$mode == "download"} {
			    APSSetVarAndUpdate status "Downloading file [lindex $filenames $nfile]"
			    exp_send "get [lindex $filenames $nfile]\r"
			} elseif {$mode == "upload"} {
			    APSSetVarAndUpdate status "Uploading files [lindex $filenames $nfile]"
			    exp_send "put [lindex $filenames $nfile]\r"
			}
			incr nfile
			if {$nfile == $nfiles} {
			    incr step
			}
		    }
		} else {
		    incr step
		    if {$mode == "download"} {
			APSSetVarAndUpdate status "Downloading files"
			eval exp_send "\"get ${filenames}\r\""
		    } elseif {$mode == "upload"} {
			APSSetVarAndUpdate status "Uploading files"
			eval exp_send "\"put ${filenames}\r\""
		    }
		}
		exp_continue
	    } elseif {$step == 6} {
		if {!$ndirs} {
		    incr step
		    exp_send "\r"
		} else {
		    if {$mode == "download"} {
			exp_send "lmkdir [lindex $dirnames $ndir]\r"
		    } elseif {$mode == "upload"} {
			exp_send "mkdir [lindex $dirnames $ndir]\r"
		    }
		    incr ndir
		    if {$ndir == $ndirs} {
			incr step
		    }
		}
		exp_continue
	    } elseif {$step == 7} {
		incr step
		if {$mode == "download"} {
		    exp_send "\r"
		} elseif {$mode == "upload"} {
		    APSSetVarAndUpdate status "Getting filelist"
		    if {$tcl_platform(os) == "Linux"} {
			exp_send "ls -l\r"
		    } else {
			exp_send "ls -l\r"
		    }
		}
		exp_continue
	    } elseif {$step == 20} {
		if {![llength $newdir(local)]} {
		    incr step
		    exp_send "\r"
		} else {
		    exp_send "lmkdir [lindex $newdir(local) $nlocaldir]\r"
		    incr nlocaldir
		    if {$nlocaldir == [llength $newdir(local)]} {
			incr step
		    }
		}
		exp_continue
	    } elseif {$step == 21} {
		if {![llength $newdir(remote)]} {
		    incr step
		    exp_send "\r"
		} else {
		    exp_send "mkdir [lindex $newdir(remote) $nremotedir]\r"
		    incr nremotedir
		    if {$nremotedir == [llength $newdir(remote)]} {
			incr step
		    }
		}
		exp_continue
	    } else {
		APSSetVarAndUpdate status "Closing connection"
		catch {exp_close}
	    }
	} "quit>" {
	    if {$step == 8} {
		append fileInfo "$expect_out(buffer)\n"
		exp_send "\r"
		exp_continue
	    } else {
		APSSetVarAndUpdate status "Unexpected sftp statement"
		catch {exp_close}
	    }
	} timeout {
	    APSSetVarAndUpdate status "connection to $remoteHost timed out"
	    catch {exp_close}
	} eof {
	    APSSetVarAndUpdate status "Error attempting to connect to remote host"
	    return
	}
    }
    if {!$sync} {
	if {$mode == "download"} {
	    listLocalFiles $localDir
	} elseif {$mode == "upload"} {
	    append fileInfo "$expect_out(buffer)\n"
	    DisplayRemoteFiles $fileInfo
	}
    }
    set currentLocalDir $localDir
    set currentRemoteDir $remoteDir
    for {set ndir 0} {$ndir < $ndirs} {incr ndir} {
	listLocalFiles [file join $currentLocalDir [lindex $dirnames $ndir]]
	set remoteDir [file join $currentRemoteDir [lindex $dirnames $ndir]]
	SSHConnect
	if {$sync == 1} {
	    PrepareSync
	    TransferFiles -mode upload -sync 2
	    TransferFiles -mode download -sync 2
	    PrepareSync -onlydirectories 1
	    TransferFiles -sync 1
	} else {
	    if {$mode == "download"} {
		$boxbottom(name) selection set 1 end
	    } elseif {$mode == "upload"} {
		$boxtop(name) selection set 1 end
	    }
	    TransferFiles -mode $mode
	}
    }
    if {$ndirs} {
	listLocalFiles $currentLocalDir
	set remoteDir $currentRemoteDir
	SSHConnect
    }
    APSSetVarAndUpdate status Done
}

proc SSHConnect {} {
    global remoteHost remoteUserName password expect_out remoteDir timeout tcl_platform
    if {[catch {CheckForValidHost} results]} {
	return
    }
    if {![llength $password]} {
        APSSetVarAndUpdate status "Error: No password entered"
        return
    }
    set timeout 60
    spawn -noecho sftp ${remoteUserName}@${remoteHost}
    match_max -i $spawn_id 50000
    log_user 0
    set fileInfo ""
    if {[llength $remoteDir]} {
	set step 1
    } else {
	set step 2
    }
    expect {
	"Are you sure you want to continue connecting (yes/no)?" {
	    exp_send "yes\r"
	    exp_continue
	} "Do you want to change the host key on disk (yes/no)?" {
	    exp_send "yes\r"
	    exp_continue
	} "password:" {
	    APSSetVarAndUpdate status "Sending password"
	    exp_send "${password}\r"
	    exp_continue
	} "sftp>" {
	    if {$step == 1} {
		incr step
		exp_send "cd ${remoteDir}\r"
		exp_continue
	    } elseif {$step == 2} {
		incr step
		APSSetVarAndUpdate status "Getting remote directory name"
		exp_send "pwd\r"
		exp_continue
	    } elseif {$step == 3} {
		if {$tcl_platform(os) == "Linux"} {
		    set remoteDir [lrange [string trimright [lindex [split $expect_out(buffer) \n] 1] \r] 3 end]
		} else {
		    set remoteDir [string trimright [lindex [split $expect_out(buffer) \n] 1] \r]
		}
		incr step
		APSSetVarAndUpdate status "Getting filelist"
		if {$tcl_platform(os) == "Linux"} {
		    exp_send "ls -l\r"
		} else {
		    exp_send "ls -l\r"
		}
		exp_continue
	    } else {
		APSSetVarAndUpdate status "Closing connection"
		catch {exp_close}
	    }
	} "quit>" {
	    if {$step == 4} {
		append fileInfo "$expect_out(buffer)\n"
		exp_send "\r"
		exp_continue
	    } else {
		APSSetVarAndUpdate status "Unexpected sftp statement"
		catch {exp_close}
	    }
	} timeout {
	    APSSetVarAndUpdate status "connection to $remoteHost timed out"
	    catch {exp_close}
	} eof {
	    APSSetVarAndUpdate status "Error attempting to connect to remote host"
	    return
	}
    }
    append fileInfo "$expect_out(buffer)\n"

    DisplayRemoteFiles $fileInfo
    APSSetVarAndUpdate status Done

}

proc DisplayRemoteFiles {fileInfo} {
    global filesRemote remoteDir
    set filesRemote(name) ""
    set filesRemote(bytes) ""
    set filesRemote(size) ""
    set filesRemote(mtime) ""
    set remoteFileInfo ""
    set remoteDirInfo ""
    foreach line [split $fileInfo "\n"] {
	set data ""
	set line [string trim $line]
	if {($line == "<Press any key for more or q to quit>") || ($line == "sftp>")} {
	    continue
	}
	set name [lrange $line 8 end]
	set i [lsearch -exact $name "->"]
	if {$i != -1} {
	    set name [lrange $name 0 [expr {$i - 1}]]
	}
	if {![llength $name]} {
	    continue
	}
	if {[string index $name 0] == "."} {
	    continue
	}
	set name [string trimright $name "*"]
	lappend data $name

	if {[string index [lindex $line 0] 0] == "d"} {
	    set size " Directory"
	    lappend data 0
	    lappend data $size
	    set isdir 1
	} else {
	    set size [lindex $line 4]
	    lappend data $size
	    if {$size > 1024} {
		set size "[expr round($size / 1024.0)] KB"
	    }
	    lappend data [format %10s $size]
	    set isdir 0
	}
	
	if {[string first ":" [lindex $line 7]] != -1} {
	    set mtime "[lindex $line 5] [format %2s [lindex $line 6]] [format %5s [lindex $line 7]]"
	} else {
	    set mtime "[format %2s [lindex $line 6]] [lindex $line 5] [format %5s [lindex $line 7]]"
	}
	lappend data $mtime
	if {$isdir} {
	    lappend remoteDirInfo $data
	} else {
	    lappend remoteFileInfo $data
	}
    }
    set remoteDirInfo [lsort -dictionary -index 0 $remoteDirInfo]
    set remoteFileInfo [lsort -dictionary -index 0 $remoteFileInfo]

    if {$remoteDir != "/"} {
	lappend filesRemote(name) ".."
	lappend filesRemote(bytes) "0"
	lappend filesRemote(size) " Directory"
	lappend filesRemote(mtime) ""	
    }
    foreach line "$remoteDirInfo $remoteFileInfo" {
	lappend filesRemote(name) [string trimright [lindex $line 0] "/"]
	lappend filesRemote(bytes) [lindex $line 1]
	lappend filesRemote(size) [lindex $line 2]
	lappend filesRemote(mtime) [lindex $line 3]
    }
}

proc listLocalFiles {dir} {
    if {![file isdirectory $dir]} {
	return
    }
    global files localDir
    set origDir [pwd]
    cd $dir
    set localDir [pwd]
    set names [lsort -dictionary [glob -nocomplain *]]
    set files(name) ""
    set files(bytes) ""
    set files(size) ""
    set files(mtime) ""
    set list1 ""
    set list2 ""
    foreach f $names {
	if {[file isdirectory $f]} {
	    lappend list1 $f
	} else {
	    lappend list2 $f
	}
    }
    if {$localDir != "/"} {
	set files(name) ".. $list1 $list2"
    } else {
	set files(name) "$list1 $list2"
    }
    set year [clock format [clock seconds] -format "%Y"]
    foreach f $files(name) {
	if {[file isdirectory $f]} {
	    lappend files(bytes) 0
	    lappend files(size) " Directory"
	} else {
	    if {[catch {set size [file size $f]} result]} {
		lappend files(bytes) 0
		lappend files(size) "   Invalid"
		lappend files(mtime) "Invalid Link"
		continue
	    }
	    lappend files(bytes) $size
	    if {$size > 1024} {
		set size "[expr round($size / 1024.0)] KB"
	    }
	    lappend files(size) [format %10s $size]
	}

	if {$year == [clock format [file mtime $f] -format "%Y"]} {
	    lappend files(mtime) [clock format [file mtime $f] -format "%b %d %H:%M"]
	} else {
	    lappend files(mtime) [clock format [file mtime $f] -format "%d %b  %Y"]
	}
    }
    cd $origDir
}

APSApplication . -name "File Sync" -version $CVSRevisionAuthor

APSMenubarAddMenu .filters -parent .menu -text Filters
.menu.filters.menu add command -label "Create Filters" -command "CreateFilter"
.menu.filters.menu add command -label "Load Filters" -command "LoadFilter"
.menu.filters.menu add command -label "Edit Filters" -command "EditFilter"

set status Ready...
APSScrolledStatus .status \
    -parent .userFrame \
    -textVariable status \
    -packOption "-side top -fill x"
pack [frame .userFrame.localHost] -fill x
pack [frame .userFrame.top -bd 2 -relief groove] -fill both -expand true
pack [frame .userFrame.middle] -fill x
pack [frame .userFrame.middle2] -fill x
pack [frame .userFrame.middle3] -fill x
pack [frame .userFrame.middle4] -fill x
pack [frame .userFrame.bottom -bd 2 -relief groove] -fill both -expand true

set localDir [pwd]
APSLabeledEntry .localHost \
    -parent .userFrame.localHost \
    -label "Local Directory" \
    -textVariable localDir \
    -width 53

pack configure .userFrame.localHost.localHost.entry -fill x -expand true
pack [button .userFrame.localHost.localHost.change -text "Change" -command {listLocalFiles $localDir}] -side right -before .userFrame.localHost.localHost.entry

listLocalFiles $localDir


ttk::panedwindow .userFrame.top.pw -orient horizontal
pack .userFrame.top.pw -side left -fill both -expand true

.userFrame.top.pw add [ttk::frame .name -relief sunken -borderwidth 1] -weight 1
.userFrame.top.pw add [ttk::frame .size -relief sunken -borderwidth 1] -weight 0
.userFrame.top.pw add [ttk::frame .time -relief sunken -borderwidth 1] -weight 0

set pane .name
listbox $pane.list \
    -relief flat \
    -listvar files(name) \
    -exportselection false \
    -selectmode extended \
    -yscrollcommand topscroll
pack $pane.list -fill both -expand true
lappend boxtop(name) $pane.list

set pane .size
listbox $pane.list \
    -relief flat \
    -listvar files(size) \
    -exportselection false \
    -yscrollcommand topscroll
pack $pane.list -fill both -expand true
lappend boxtop(size) $pane.list

set pane .time
listbox $pane.list \
    -relief flat \
    -listvar files(mtime) \
    -exportselection false \
    -yscrollcommand topscroll
pack $pane.list -fill both -expand true
lappend boxtop(mtime) $pane.list

scrollbar .userFrame.top.scroll -orient v -command [list topset yview]
pack .userFrame.top.scroll -side right -fill y -before .userFrame.top.pw
set scrollName .userFrame.top.scroll

proc topscroll {args} {
    eval .userFrame.top.scroll set $args
    topset yview moveto [lindex $args 0]
}

proc topset {args} {
    global boxtop
    eval $boxtop(name) $args
    eval $boxtop(size) $args
    eval $boxtop(mtime) $args
}

proc LBtopset {args} {
    global boxtop
    eval $boxtop(name) $args
    eval $boxtop(size) select clear 0 end
    eval $boxtop(mtime) select clear 0 end
}

foreach lb "$boxtop(size) $boxtop(mtime)" {
    bind $lb <ButtonPress-1> {
        LBtopset select set [%W nearest %y]
    }
    bind $lb <B1-Motion> {
        LBtopset select set [%W nearest %y]
        LBtopset see [%W nearest %y]
    }
    bind $lb <ButtonRelease-1> {
        LBtopset select set [%W nearest %y]
    }
}

bind $boxtop(name) <Double-1> {
    set name [$boxtop(name) get [$boxtop(name) nearest %y]]
    if {[file isdirectory [file join $localDir $name]]} {
	listLocalFiles [file join $localDir $name]
    }
}
##############
set remoteHost ""
set remoteUserName [exec whoami]
set password ""
APSLabeledEntry .remoteHost \
    -parent .userFrame.middle \
    -label "Remote Host" \
    -textVariable remoteHost \
    -packOption "-side left" \
    -width 12

APSLabeledEntry .remoteUserName \
    -parent .userFrame.middle \
    -label "User Name" \
    -textVariable remoteUserName \
    -packOption "-side left" \
    -width 10

APSLabeledEntry .password \
    -parent .userFrame.middle \
    -label "Password" \
    -textVariable password \
    -packOption "-side left" \
    -width 10
.userFrame.middle.password.entry configure -show "*"

APSButton .connect \
    -parent .userFrame.middle \
    -text "Connect" \
    -packOption "-side left" \
    -command "SSHConnect"

APSLabeledEntry .remoteDir \
    -parent .userFrame.middle2 \
    -label "Remote Directory" \
    -textVariable remoteDir \
    -width 62

pack configure .userFrame.middle2.remoteDir.entry -fill x -expand true

APSButton .download \
    -parent .userFrame.middle3 \
    -text "Download From Remote Host" \
    -command "TransferFiles -mode download"

APSButton .upload \
    -parent .userFrame.middle3 \
    -text "Upload To Remote Host" \
    -command "TransferFiles -mode upload"

APSButton .prepareSync \
    -parent .userFrame.middle4 \
    -text "PrepareSync" \
    -command "PrepareSync"

APSButton .syncDirectory \
    -parent .userFrame.middle4 \
    -text "Sync Selected Directory" \
    -command "SyncDirectory"

set transfertimeout 300
APSLabeledEntry .timeout \
    -parent .userFrame.middle4 \
    -label "Timeout (s)" \
    -textVariable transfertimeout \
    -packOption "-side left" \
    -width 5

set transfermaxsize 99999
APSLabeledEntry .maxsize \
    -parent .userFrame.middle4 \
    -label "Max Size (MB)" \
    -textVariable transfermaxsize \
    -packOption "-side left" \
    -width 5

##############
ttk::panedwindow .userFrame.bottom.pw -orient horizontal
pack .userFrame.bottom.pw -side left -fill both -expand true

.userFrame.bottom.pw add [ttk::frame .name2 -relief sunken -borderwidth 1] -weight 1
.userFrame.bottom.pw add [ttk::frame .size2 -relief sunken -borderwidth 1] -weight 0
.userFrame.bottom.pw add [ttk::frame .time2 -relief sunken -borderwidth 1] -weight 0

set pane .name2
listbox $pane.list \
    -relief flat \
    -listvar filesRemote(name) \
    -exportselection false \
    -selectmode extended \
    -yscrollcommand bottomscroll
pack $pane.list -fill both -expand true
lappend boxbottom(name) $pane.list

set pane .size2
listbox $pane.list \
    -relief flat \
    -listvar filesRemote(size) \
    -exportselection false \
    -yscrollcommand bottomscroll
pack $pane.list -fill both -expand true
lappend boxbottom(size) $pane.list

set pane .time2
listbox $pane.list \
    -relief flat \
    -listvar filesRemote(mtime) \
    -exportselection false \
    -yscrollcommand bottomscroll
pack $pane.list -fill both -expand true
lappend boxbottom(mtime) $pane.list

scrollbar .userFrame.bottom.scroll -orient v -command [list bottomset yview]
pack .userFrame.bottom.scroll -side right -fill y -before .userFrame.bottom.pw
set scrollName .userFrame.bottom.scroll



proc bottomscroll {args} {
    eval .userFrame.bottom.scroll set $args
    bottomset yview moveto [lindex $args 0]
}

proc bottomset {args} {
    global boxbottom
    eval $boxbottom(name) $args
    eval $boxbottom(size) $args
    eval $boxbottom(mtime) $args
}

proc LBbottomset {args} {
    global boxbottom
    eval $boxbottom(name) $args
    eval $boxbottom(size) select clear 0 end
    eval $boxbottom(mtime) select clear 0 end
}

foreach lb "$boxbottom(size) $boxbottom(mtime)" {
    bind $lb <ButtonPress-1> {
        LBbottomset select set [%W nearest %y]
    }
    bind $lb <B1-Motion> {
        LBbottomset select set [%W nearest %y]
        LBbottomset see [%W nearest %y]
    }
    bind $lb <ButtonRelease-1> {
        LBbottomset select set [%W nearest %y]
    }
}

bind $boxbottom(name) <Double-1> {
    set index [$boxbottom(name) nearest %y]
    set name [$boxbottom(name) get $index]
    set size [$boxbottom(size) get $index]
    if {$size == " Directory"} {
	set remoteDir [file join $remoteDir $name]
	SSHConnect
    }
}

##########

set args $argv
set selectList ""
set connect 0
set prepareSync 0
set tmp $localDir
APSStrictParseArguments {remoteHost remoteDir localDir selectList connect prepareSync}
if {[llength $localDir]} {
    listLocalFiles $localDir
} else {
    set localDir $tmp
}
if {($connect) || ($prepareSync)} {
    tkwait visibility .userFrame
    SSHConnect
}
if {$prepareSync} {
    PrepareSync
}
if {[llength $selectList]} {
    foreach name $selectList {
	set i [lsearch -exact $files(name) $name]
	if {$i != -1} {
	    $boxtop(name) selection set $i
	}
	set i [lsearch -exact $filesRemote(name) $name]
	if {$i != -1} {
	    $boxbottom(name) selection set $i
	}
    }
}


