#!/bin/sh
# \
exec oagwish "$0" "$@"
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

#
# Program to provide easy interface to sddssort, sddsxref, and other data manipulation
# programs.
#
# $Log: not supported by cvs2svn $
# Revision 1.4  2005/07/21 22:31:10  shang
# added filter, sort, combine, collapse, expand, xref, DeleteRename and convert
#
# Revision 1.3  2005/04/15 19:13:39  borland
# Now actually uses the -filename argument value.
#
# Revision 1.2  2005/04/11 19:40:32  borland
# Removed debugging statement.
#
# Revision 1.1  2005/04/11 19:38:00  borland
# First version.
#
#

proc setStatus {text} {
    global status
    set status "[clock format [clock seconds] -format %H:%M:%S]: $text"
    update
}

proc setStatusNoDate {text} {
    global status
    set status "$text"
    update
}

APSApplication . -name quickSDDSDataManip -version 1.0 \
    -overview "Allows easily using various features of SDDS data manipulation programs."

set status "Tip: reading the SDDS commands that appear in this window can help you learn to use SDDS from the commandline and write your own scripts."
APSScrolledStatus .status -parent .userFrame -textVariable status -width 85 -packOption "-fill x"

set filename ""
if [llength $argv] {
    set args $argv
    APSParseArguments {filename}
}

# Creates windows to allow user to select the columns for indep. and dep. variables

proc varSelect {args} {
    set arrayName ""
    set filenameVariable ""
    set type ""
    set slot ""
    set single 0
    set name Independent
    set parameter 0
    set page 0
    APSParseArguments {arrayName filenameVariable type slot single name parameter page}
    
    global $arrayName 
    global [os editstring S/(/Z) $filenameVariable]
    set $filenameVariable [string trim [string trimright [set $filenameVariable]]]

    if ![string length [set $filenameVariable]] {
        setStatus "Give a filename!"
        return
    }
    if !$page {
        set class column
        if $parameter {
            set class parameter
        }
        if [catch {APSGetSDDSNames -class $class -fileName [set $filenameVariable]} varList] {
            setStatus "$varList"
            return
        } 
        set multiItem 1
        if $single {
            set multiItem 0
        }
        set choiceList [APSChooseItemFromList -name "$name variable" \
                          -itemList $varList -returnList $varList -returnIndices 0 -multiItem $multiItem]
    } else {
        set pages [exec sdds2stream -npages=bare [set $filenameVariable]]
        
        for {set i 1} {$i<=$pages} {incr i} {
            lappend pageList $i
        }
        
        set multiItem 1
        if $single {
            set multiItem 0
        }
        set choiceList [APSChooseItemFromList -name "$name variable" \
                          -itemList $pageList -returnList $pageList -returnIndices 0 -multiItem $multiItem]
    }
    set $arrayName\($slot\) [join $choiceList ,]
}
    
# Makes entry box and buttons for variable selection	

proc makeVariableFrame {widget args} {
    set filenameVariable ""
    set arrayName ""
    set slot ""
    set parent ""
    set single 0
    set label ""
    set parameter 0
    set page 0
    set width 90
    set packOption "-side top"
    set contextHelp "No context help defined."
    APSStrictParseArguments {filenameVariable arrayName parent single label parameter slot page contextHelp width packOption}
    
    APSFrame $widget -parent $parent -label "" -contextHelp $contextHelp -packOption $packOption
    set vf $parent$widget.frame

    APSFrame .vframe -parent $vf -label "" -relief flat 
    set vf $vf.vframe.frame

    global $arrayName
    APSLabeledEntry .xVar -width $width -label $label -packOption "-side left" \
	-textVariable $arrayName\($slot\) -parent $vf \
	-packOption "-side left" -contextHelp $contextHelp
    APSButton .xselect -parent $vf -text "Select..." -packOption "-side right" -command \
	"varSelect -arrayName $arrayName -slot $slot -page $page -single $single -parameter $parameter -filenameVariable $filenameVariable" \
	-contextHelp "Press to call up column or parameter selection box."
}

proc AddSortOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}
    global Sort
    set Sort(columnList) ""
    set Sort(columnDirectionList) increasing
    set Sort(parameterList) ""
    set Sort(parameterDirectionList) increasing
    set Sort(unique) 0
    set Sort(numericHigh) 0

    makeVariableFrame .columnList -parent $parent -label "Sort column(s): " -width 55 \
	-arrayName Sort -slot columnList -single 0 -filenameVariable Sort(inputFile) -contextHelp \
	"Entry a comma- or space-separated list of columns by which to sort."
    APSLabeledEntry .dirList -parent $parent.columnList.frame \
	-packOption "-side top -anchor w" \
	-label "Sort column directions(s): " -width 60 \
	-textVariable Sort(columnDirectionList) -contextHelp \
	"Entry a comma- or space-separated list of orders by which to sort columns.  There must be one for each sort column."

    makeVariableFrame .parameterList -parent $parent -label "Sort parameter(s): " -width 50 \
	-parameter 1 -arrayName Sort -slot parameterList -single 0 -filenameVariable Sort(inputFile) -contextHelp \
	"Entry a comma- or space-separated list of parameters by which to sort."
    APSLabeledEntry .dirList -parent $parent.parameterList.frame \
	-packOption "-side top -anchor w" \
	-label "Sort parameter directions(s): " -width 60 \
	-textVariable Sort(parameterDirectionList) -contextHelp \
	"Entry a comma- or space-separated list of orders by which to sort parameters.  There must be one for each sort parameter."

    APSRadioButtonFrame .unique -parent $parent -label "Eliminate duplicates: " -orientation horizontal \
	-variable Sort(unique) -buttonList "Yes No" -valueList "1 0" \
	-contextHelp "If yes is chosen, when rows (or pages) have identical values of the sort columns (or parameters) they are considered duplicates and only one is kept."

    APSRadioButtonFrame .numericHigh -parent $parent -label "Numeric high sort: " -orientation horizontal \
	-variable Sort(numericHigh) -buttonList "Yes No" -valueList "1 0" \
	-contextHelp "If yes, then strings that contain numbers are sorted so that smaller numbers come first.  Otherwise, ASCII sorting is used."

    APSLabeledEntry .output -parent $parent -label "Output file: " -width 90 \
	-commandButton 1 -textVariable Sort(outputFile) 

    APSButton .run -parent $parent -text "Run" -command "RunSort"
}

proc RunSort {} {
    global Sort
    if ![string length $Sort(inputFile)] {
        setStatus "Supply an input filename"
        return
    }
    if ![string length $Sort(outputFile)] {
        setStatus "Supply an output filename"
        return
    }

    set Sort(columnList) [string trim $Sort(columnList)]
    set Sort(parameterList) [string trim $Sort(parameterList)]
    if {![string length $Sort(columnList)] && ![string length $Sort(parameterList)]} {
	setStatus "Supply something for sort column(s) or sort parameter(s)."
	return
    }
    if {[string length $Sort(columnList)] && [string length $Sort(parameterList)]} {
	setStatus "Supply sort column(s) or sort parameter(s), not both."
	return
    }

    if [string length $Sort(columnList)] {
	set sortType column
	set dataList [join [os editstring "1000%/,/ /1000(a%/  / /)" $Sort(columnList)] ]
	set directionList [join [os editstring "1000%/,/ /1000(a%/  / /)" $Sort(columnDirectionList)]]
    } else {
	set sortType parameter
	set dataList [join [os editstring "1000%/,/ /1000(a%/  / /)" $Sort(parameterList)] ]
	set directionList [join [os editstring "1000%/,/ /1000(a%/  / /)" $Sort(parameterDirectionList)]]
    }
    if [llength $directionList] {
	if [llength $dataList]!=[llength $directionList] {
	    setStatus "$sortType list and direction list must be the same length"
	    return
	}
    } else {
	set directionList [APSReplicateItem -number [llength $dataList] -item "increasing"]
    }


    set command "sddssort $Sort(inputFile) $Sort(outputFile) "
    foreach data $dataList direction $directionList {
	append command "-$sortType=$data,$direction "
    }

    foreach item [list unique numericHigh] {
	if $Sort($item) {
	    append command "-$item "
	} 
    }

    APSAddToRecentFileList -filename $Sort(inputFile)
    APSAddToRecentFileList -filename $Sort(outputFile)

    setStatus "$command"
    if [catch {eval exec $command} result] {
	setStatus "$result"
	return
    }
    setStatus "Done."
}

proc makeFilterEntryFrame {widget args} {
    set filenameVariable ""
    set parent ""
    set single 0
    set label ""
    set parameter 0
    set width 90
    set contextHelp "No context help defined."
    APSStrictParseArguments {filenameVariable parent single label parameter contextHelp width}
    global Filter

    set type column
    if $parameter {
	set type parameter
    }
    set count $Filter(count,$type)
    APSFrame .m$count -parent $parent -packOption "-side top -fill x"
    set w $parent.m$count.frame
    $w configure -bd 0 -relief flat
    APSLabeledEntry .xVar -width 25 -label $label \
	-textVariable Filter($type,$count) -parent $w \
	-packOption "-side left" -contextHelp $contextHelp
    APSButton .xselect -parent $w -text "Select..." -packOption "-side left" -command \
	"varSelect -arrayName Filter -single 1 -slot $type,$count -parameter $parameter -filenameVariable $filenameVariable" \
	-contextHelp "Press to call up column or parameter selection box."
    APSLabeledEntry .leMin -parent $w -label "  Min: " -width 10 -packOption "-side left" \
	-textVariable Filter($type,$count,min) 
    APSLabeledEntry .leMax -parent $w -label "  Max: " -width 10 -packOption "-side left"\
	-textVariable Filter($type,$count,max) 
    set Filter($type,$count,min) 0
    set Filter($type,$count,max) 0
    incr Filter(count,$type) 
    tkwait visibility $w.leMax
    if $parameter {
        APSScrollAdjust $Filter(parScrollFrame)  -numVisible 4
    } else {
        APSScrollAdjust $Filter(colScrollFrame)  -numVisible 4
    }
}

proc AddFilterItemFrame {args} {
    set parent ""
    set parameter 0
    APSStrictParseArguments {parent parameter}
    global Filter
    set type column 
    if $parameter {
	set type parameter
    }
    set count $Filter(count,$type)
    makeFilterEntryFrame .$type$count -parent $parent -label "Column: " -width 60 \
	-single 1 -filenameVariable Filter(inputFile) -parameter $parameter -contextHelp \
	"Enter a $type by which to filter."
    
}

proc DeleteFilterItemFrame {args} {
    set parent ""
    set parameter 0
    APSStrictParseArguments {parent parameter}
    global Filter
    set type column 
    if $parameter {
	set type parameter
    }
    if $Filter(count,$type)==0 return
    incr Filter(count,$type) -1
    set count $Filter(count,$type)
    pack forget $parent.m$count
    destroy $parent.m$count
    set Filter($type,$count) ""
    set Filter($type,$count,min) 0
    set Filter($type,$count,max) 0
}

proc AddFilterOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}
    global Filter
    set Filter(columnList) ""
    set Filter(columnDirectionList) increasing
    set Filter(parameterList) ""
    set Filter(parameterDirectionList) increasing

    APSFrame .column -parent $parent -label "Column-based filters" -packOption "-side top -fill x"
    set cp $parent.column.frame
    set Filter(colScroll) [APSScroll .column -parent $cp -name "Column filters"  -contextHelp \
                             "Enter one or more columns and limits for filtering."]
    set Filter(colScrollFrame) $cp.column
    set Filter(count,column) 0
    APSButton .add -parent $cp -text "Add" \
	-command "AddFilterItemFrame -parent $Filter(colScroll)"
    APSButton .delete -parent $cp -text "Delete" -command "DeleteFilterItemFrame -parent $cp.column.frame.canvas.frame"

    APSFrame .parameter -parent $parent -label "Parameter-based filters"  -packOption "-side top -fill x"
    set cp $parent.parameter.frame
    set Filter(parScroll) [APSScroll .parameter -parent $cp -name "Parameter filters" -contextHelp \
                             "Enter one or more parameters and limits for filtering."]
    set Filter(parScrollFrame) $cp.parameter
   # $cp.parameter.frame.canvas config -height 120 -width 600
    set Filter(count,parameter) 0
    APSButton .add -parent $cp -text "Add" \
	-command "AddFilterItemFrame -parent $cp.parameter.frame.canvas.frame -parameter 1"
    APSButton .delete -parent $cp -text "Delete" -command "DeleteFilterItemFrame -parent $cp.parameter.frame.canvas.frame -parameter 1"

    APSLabeledEntry .output -parent $parent -label "Output file: " -width 90 \
	-commandButton 1 -textVariable Filter(outputFile) 

    APSButton .run -parent $parent -text "Run" -command "RunFilter"
}

proc RunFilter {} {
    global Filter
    if ![string length $Filter(inputFile)] {
        setStatus "Supply an input filename"
        return
    }
    if ![string length $Filter(outputFile)] {
        setStatus "Supply an output filename"
        return
    }

    if {$Filter(count,column)==0 && $Filter(count,parameter)==0} {
	setStatus "Supply at least one column filter or one parameter filter"
	return
    }

    set command "sddsprocess $Filter(inputFile) $Filter(outputFile) "
    for {set i 0} {$i<$Filter(count,column)} {incr i} {
	append command "-filter=column,$Filter(column,$i),$Filter(column,$i,min),$Filter(column,$i,max) "
    }
    for {set i 0} {$i<$Filter(count,parameter)} {incr i} {
	append command "-filter=parameter,$Filter(parameter,$i),$Filter(parameter,$i,min),$Filter(parameter,$i,max) "
    }
    
    APSAddToRecentFileList -filename $Filter(inputFile)
    APSAddToRecentFileList -filename $Filter(outputFile)

    setStatus "$command"
    if [catch {eval exec $command} result] {
	setStatus "$result"
	return
    }
    setStatus "Done."
}

proc AddCombineInputFrame {args} {
    set parent ""
    APSStrictParseArguments {parent}

    global Combine
    set i $Combine(additionalInputFiles)
    APSLabeledEntry .le$i -parent $parent -width 80 -label "Inputfile: " \
	-textVariable Combine(additionalInputFile,$i) -commandButton 1
    set Combine(additionalInputFile,$i) ""
    incr Combine(additionalInputFiles)
}

proc DeleteCombineInputFrame {args} {
    set parent ""
    APSStrictParseArguments {parent}

    global Combine
    if $Combine(additionalInputFiles)==0 return
    incr Combine(additionalInputFiles) -1
    set i $Combine(additionalInputFiles)
    pack forget $parent.le$i
    destroy $parent.le$i
    set Combine(additionalInputFile,$i) ""
}

proc AddCombineOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}

    global Combine
    set Combine(merge) 0
    set Combine(merge,parameter) ""

    APSFrame .inputs -parent $parent -label "Additional input files" 
    set cp $parent.inputs.frame
    APSScroll .scroll -parent $cp -contextHelp "Enter zero or more additional input files"
    $cp.scroll.frame.canvas config -height 140 -width 600 
    set Combine(additionalInputFiles) 0
    APSButton .add -parent $cp -text "Add" \
	-command "AddCombineInputFrame -parent $cp.scroll.frame.canvas"
    APSButton .delete -parent $cp -text "Delete" \
	-command "DeleteCombineInputFrame -parent $cp.scroll.frame.canvas"

    APSFrame .merge -parent $parent -label "" 
    APSRadioButtonFrame .merge -parent $parent.merge.frame -label "Merge: " -buttonList "Yes No" \
	-valueList "1 0" -orientation horizontal -variable Combine(merge) \
	-commandList \
	[list "APSEnableWidget $parent.merge.frame.mergeParameter" "APSDisableWidget $parent.merge.frame.mergeParameter"]  \
	-contextHelp \
	"If Yes, then all pages in all files are merged into one page.  If the merge parameter is supplied, then merging is only of pages that have the same value for the specified parameter."
    APSLabeledEntry .mergeParameter -parent $parent.merge.frame -label "Merge parameter (optional): " \
	-textVariable Combine(merge,parameter) -width 30
    APSDisableWidget $parent.merge.frame.mergeParameter

    APSLabeledEntry .output -parent $parent -label "Output file: " -width 90 \
	-commandButton 1 -textVariable Combine(outputFile) 

    APSButton .run -parent $parent -text "Run" -command "RunCombine"
}

proc RunCombine {} {
    global Combine
    if ![string length $Combine(inputFile)] {
        setStatus "Supply an input filename"
        return
    }
    if ![string length $Combine(outputFile)] {
        setStatus "Supply an output filename"
        return
    }

    set command "sddscombine $Combine(inputFile) "
    for {set i 0} {$i<$Combine(additionalInputFiles)} {incr i} {
	set filename [string trim $Combine(additionalInputFile,$i)]
	if ![string length $filename] {
	    setStatus "Input filename is blank"
	    return
	}
	append command "$filename "
    }
    append command "-overwrite $Combine(outputFile) "
    if $Combine(merge) {
	set parameter [string trim $Combine(merge,parameter)] 
	if [string length $parameter] {
	    append command "-merge=$parameter "
	} else {
	    append command "-merge "
	}
    }

    APSAddToRecentFileList -filename $Combine(inputFile)
    APSAddToRecentFileList -filename $Combine(outputFile)
	
    setStatus "$command"
    if [catch {eval exec $command} result] {
	setStatus "$result"
	return
    }
    setStatus "Done."
}

proc AddCollapseOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}

    APSLabeledEntry .output -parent $parent -label "Output file: " -width 90 \
	-commandButton 1 -textVariable Collapse(outputFile) 

    APSButton .run -parent $parent -text "Run" -command "RunCollapse"
}

proc RunCollapse {} {
    global Collapse
    if ![string length $Collapse(inputFile)] {
        setStatus "Supply an input filename"
        return
    }
    if ![string length $Collapse(outputFile)] {
        setStatus "Supply an output filename"
        return
    }

    APSAddToRecentFileList -filename $Collapse(inputFile)
    APSAddToRecentFileList -filename $Collapse(outputFile)
	
    set command "sddscollapse $Collapse(inputFile) $Collapse(outputFile)"
    setStatus "$command"
    if [catch {eval exec $command} result] {
	setStatus "$result"
	return
    }
    setStatus "Done."
}

proc AddExpandOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}

    APSLabeledEntry .output -parent $parent -label "Output file: " -width 90 \
	-commandButton 1 -textVariable Expand(outputFile) 

    APSButton .run -parent $parent -text "Run" -command "RunExpand"
}

proc RunExpand {} {
    global Expand
    if ![string length $Expand(inputFile)] {
        setStatus "Supply an input filename"
        return
    }
    if ![string length $Expand(outputFile)] {
        setStatus "Supply an output filename"
        return
    }

    APSAddToRecentFileList -filename $Expand(inputFile)
    APSAddToRecentFileList -filename $Expand(outputFile)
	
    set command "sddsexpand $Expand(inputFile) $Expand(outputFile)"
    setStatus "$command"
    if [catch {eval exec $command} result] {
	setStatus "$result"
	return
    }
    setStatus "Done."
}

#sddsxref
proc XrefDisableMatchOptions {args} {
    set parent ""
    APSParseArguments {parent}
    global Xref
    switch $Xref(matchOption) {
        none {
            set Xref(fillIn) 0
            set Xref(reuse) 0
            set Xref(reusePage) 0
            APSDisableWidget $parent.match1
            APSDisableWidget $parent.match2
            APSDisableWidget $parent.check
        }
        default {
            APSEnableWidget $parent.match1
            APSEnableWidget $parent.match2
            APSEnableWidget $parent.check
        }
    }
}
proc AddXrefOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}

    global Xref

    APSFrame .inputs -parent $parent -label "Additional input files" -packOption "-side top -fill x"
    set cp $parent.inputs.frame
    set Xref(scroll) [APSScroll .scroll -parent $cp ]
    set Xref(scrollFrame) $cp.scroll
    set Xref(additionalInputFiles) 0
    APSButton .add -parent $cp -text "Add" \
	-command "AddXrefInputFrame -parent $Xref(scroll)"
    APSButton .delete -parent $cp -text "Delete" \
	-command "DeleteXrefInputFrame -parent $Xref(scroll)"

    set Xref(takeColumnList) ""
    set Xref(leaveColumnList) ""
    set Xref(matchColumns) ""
    set Xref(equateColumns) ""
    set Xref(renameColumnList) ""
    set Xref(renameParameterList) ""
    set Xref(editColumnList) ""
    set Xref(editParameterList) ""
    set Xref(matchOption) match
    set Xref(fillIn) 1
    set Xref(reuse) 1
    set Xref(reusePage) 0
    set Xref(additionalInputFiles) 0
    set Xref(additionalInputFile,0) ""
    set tabList {Option Column Parameter}
    APSFrame .opts -parent $parent -label "Options"
    set w $parent.opts.frame
    $w configure -relief flat -bd 0
    set tabWidgetList [APSTabFrame .opt -parent $w -labelList $tabList -width 800 -height 240]
    set opt [lindex $tabWidgetList 0]
    set col [lindex $tabWidgetList 1]
    set par [lindex $tabWidgetList 2]
    
    set commandList [list "XrefDisableMatchOptions -parent $opt" "XrefDisableMatchOptions -parent $opt" "XrefDisableMatchOptions -parent $opt"]
    APSRadioButtonFrame .opt -parent $opt -label "    Match type:" -buttonList {match equate none} \
      -valueList {match equate none} -variable Xref(matchOption) -orientation horizontal \
      -commandList $commandList \
      -contextHelp "choose match for matching two string columns or equate for matching two numerical columns or none without any matches" 
    APSCheckButtonFrame .check -parent $opt -label "Other options:" \
      -buttonList {"fillIn  " "reuse  " "reuse=page"} \
      -variableList {Xref(fillIn) Xref(reuse) Xref(reusePage)} -orientation horizontal -label "   "

    makeVariableFrame .match1 -parent $opt -label "Match column1:" -width 50  \
	-arrayName Xref -slot matchColumn1 -single 1 -filenameVariable Xref(inputFile) -contextHelp \
	"Entry a match column in the input file."
    makeVariableFrame .match2 -parent $opt -label "Match column2:" -width 50  \
      -arrayName Xref -slot matchColumn2 -single 1 -filenameVariable Xref(additionalInputFile,0) -contextHelp \
      "Entry a match column in the addition input file."
    
    makeVariableFrame .take -parent $col -label "Take column(s): " -width 58 \
      -arrayName Xref -slot takeColumnList -single 0 -filenameVariable Xref(additionalInputFile,0) -contextHelp \
      "Enter a comma- or space-separated list of columns that will be taken from the second and higher input files."
    makeVariableFrame .leave -parent $col -label "Leave column(s):" -width 58 \
      -arrayName Xref -slot leaveColumnList -single 0 -filenameVariable Xref(additionalInputFile,0) -contextHelp \
      "Enter a comma- or space-separated list of columns that will not be taken from the second and higher input files."
    
    set Xref(transferParameterList) ""
    makeVariableFrame .transfer -parent $par -label "Transfer parameter(s):" -width 50  -parameter 1\
      -arrayName Xref -slot transferParameterList -single 0 -filenameVariable Xref(additionalInputFile,0) -contextHelp \
      "Entry a comma- or space-separated list of parameters to transfer from the second and higher input files to the output file."
    foreach type {col par} name {column parameter} ispar {0 1} {
        set nm [string toupper $name 0 0]
        set Xref(rename${nm}List) ""
        makeVariableFrame .rename -parent [set $type] -label "Rename ${name}(s):" -width 50 -parameter $ispar \
          -arrayName Xref -slot rename${nm}List -single 0 -filenameVariable Xref(additionalInputFile,0) \
          -contextHelp \
          "Entry a a comma- or space-separated list of ${name}s for renaming"
        set Xref(new${nm}List) ""
        APSLabeledEntry .new -parent [set $type].rename.frame -label "New $name name(s):" \
          -width 60 -textVariable Xref(new${nm}List) \
          -contextHelp \
          "Entry a a comma- or space-separated list of new $name names for the rename ${name}s"
       
        set Xref(edit${nm}) ""
        set Xref(edit${nm}Command) ""
        APSFrame .edit -parent [set $type]
        set w [set $type].edit.frame
        APSLabeledEntry .edit -parent $w -label "Edit ${name} name:" -textVariable Xref(edit${nm}) \
          -width 60 -contextHelp "enter the wildcard string for editting ${name}(s)"
        APSLabeledEntry .com -parent $w -label "  Edit command:" \
          -width 60 -textVariable Xref(edit${nm}Command)   \
          -contextHelp \
          "enter the edit command"
    }
    APSLabeledEntry .output -parent $parent -label "Output file: " -width 90 \
	-commandButton 1 -textVariable Xref(outputFile) 

    APSButton .run -parent $parent -text "Run" -command "RunXref"
}

proc AddXrefInputFrame {args} {
    set parent ""
    APSStrictParseArguments {parent}
    
    global Xref
    set i $Xref(additionalInputFiles)
    APSLabeledEntry .le$i -parent $parent -width 60 -label "Inputfile:" \
      -textVariable Xref(additionalInputFile,$i) -commandButton 1
    set Xref(additionalInputFile,$i) ""
    incr Xref(additionalInputFiles)
    $parent configure -height 100 -width 600 
    APSScrollAdjust $Xref(scrollFrame)  -numVisible 4
}

proc DeleteXrefInputFrame {args} {
    set parent ""
    APSStrictParseArguments {parent}

    global Xref
    if $Xref(additionalInputFiles)==0 return
    incr Xref(additionalInputFiles) -1
    set i $Xref(additionalInputFiles)
    pack forget $parent.le$i
    destroy $parent.le$i
    set Xref(additionalInputFile,$i) ""
}

proc RunXref {} {
    global Xref
    set Xref(inputFile) [string trim [string trimright $Xref(inputFile)]]
    set Xref(outputFile) [string trim [string trimright $Xref(outputFile)]]
    if ![string length $Xref(inputFile)] {
        setStatus "Supply an input filename"
        return
    }
    if ![string length $Xref(outputFile)] {
        setStatus "Supply an output filename"
        return
    }

    set command "sddsxref $Xref(inputFile) "
    for {set i 0} {$i<$Xref(additionalInputFiles)} {incr i} {
	set filename [string trim [string trimright $Xref(additionalInputFile,$i)]]
	if ![string length $filename] {
	    setStatus "Input filename is blank"
	    return
	}
	append command "$filename "
    }
    append command " $Xref(outputFile) "
    if $Xref(fillIn) {append command " -fillIn"}
    if $Xref(reusePage) {
        append command " -reuse=page"
    } elseif $Xref(reuse) {
        append command " -reuse"
    }
    set Xref(matchColumn1) [lindex $Xref(matchColumn1) 0]
    set Xref(matchColumn2) [lindex $Xref(matchColumn2) 0]
    if [string length $Xref(matchColumn2)] {
        set matchOpt $Xref(matchColumn1)=$Xref(matchColumn2)
    } else {
        set matchOpt $Xref(matchColumn1)
    }
    if [string length $matchOpt] {
        switch $Xref(matchOption) {
            match {
                append command " -match=$matchOpt"
            }
            equate {
                append command " -equate=$matchOpt"
            }
        }
    }
    foreach name {take leave} {
        set dataList [join [os editstring "1000%/,/ /1000(a%/  / /)" $Xref(${name}ColumnList)]]
        if [llength $dataList] {
            if ![regexp {,} $Xref(${name}ColumnList)] {
                append command " -${name}=[join $dataList ,]"
            } else {
                append command " -${name}=$dataList"
            }
        }
    }
    foreach type {Column Parameter} {
        set oldList [join [os editstring "1000%/,/ /1000(a%/  / /)" $Xref(rename${type}List)]]
        set newList [join [os editstring "1000%/,/ /1000(a%/  / /)" $Xref(new${type}List)]]
        if [llength $oldList] {
            if {[llength $oldList]!=[llength $newList]} {
                setStatus "The new $type list and the rename $type list must be the same length"
                return
            }
            set renameList ""
            foreach old $oldList new $newList {
                lappend renameList $old=$new
            }
            append command " -rename=$type,[join $renameList ,]"
        }
        set Xref(edit${type}) [lindex $Xref(edit${type}) 0]
        set Xref(edit${type}Command) [lindex $Xref(edit${type}Command) 0]
        if [string length $Xref(edit${type})] {
            if ![string length $Xref(edit${type}Command)] {
                setStatus "edit command is not provided."
                return
            }
            append command " -edit=$type,[string trim $Xref(edit${type})],[string trim $Xref(edit${type}Command)]"
        }
    }
    set transferList [join [os editstring "1000%/,/ /1000(a%/  / /)" $Xref(transferParameterList)]]
    if [llength $transferList] {
        append command " -transfer=parameter,[join $transferList ,]"
    }
    APSAddToRecentFileList -filename $Xref(inputFile)
    APSAddToRecentFileList -filename $Xref(outputFile)
    
    setStatus "$command"
    if [catch {eval exec $command} result] {
	setStatus "$result"
	return
    }
    setStatus "Done."
}

proc AddSelectOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}
    global Select
    
    set Select(secondInput) ""
    set Select(matchOption) match
    set Select(matchColumns) ""
    set Select(equateColumns) ""
    set Select(reuse) 1
    set Select(reusePage) 0
    set Select(invert) 0
    APSLabeledEntry .input2 -parent $parent -label "Second input filename:" \
      -width 70 -commandButton 1 -textVariable Select(secondInput) \
      -contextHelp "enter the second input file for sddsselect"
    APSFrame .opt -parent $parent -label "Options"
    set w $parent.opt.frame
    $w configure -relief flat -bd 0
    APSRadioButtonFrame .opt -parent $w -label "" -buttonList {match equate} -packOption "-side left" \
      -variable Select(matchOption) -valueList {match equate} -orientation horizontal \
      -contextHelp "select match for matching string columns or equate for matching numeric columns."
    APSCheckButtonFrame .check -parent $w -label "  " -orientation horizontal -packOption "-side left" \
      -buttonList {"reuse  " "-reuse=page  " "invert  " } \
      -variableList {Select(reuse) Select(reusePage) Select(invert)} \
      -contextHelp "reuse specifies that rows of <input2> may be reused, i.e., matched with more than one row of <input1>.  
      Also, -reuse=page specifies  that only the first page of <input2> is used.
      invert specifies that only nomatched rows are to be kept."
    APSFrame .match -parent $parent -label "Match/Equate Columns"
    set w $parent.match.frame
    $w configure -relief flat -bd 0
    makeVariableFrame .match1 -parent $w -label "Column1:" -width 60 \
	-arrayName Select -slot matchColumn1 -single 1 -filenameVariable Select(inputFile) -contextHelp \
	"Entry a match column in the input file."
    makeVariableFrame .match2 -parent $w -label "Column2:" -width 60 \
      -arrayName Select -slot matchColumn2 -single 1 -filenameVariable Select(secondInput) -contextHelp \
      "Entry a match column in the second input file."

    set Select(outputFile) ""
    APSLabeledEntry .output -parent $parent -label "Output file:     " -width 70 \
	-commandButton 1 -textVariable Select(outputFile) 
    APSButton .run -parent $parent -text "Run" -command "RunSelect"
}

proc RunSelect {args} {
    global Select
    
    if ![string length $Select(inputFile)] {
        setStatus "Supply an input filename"
        return
    }
    if ![string length $Select(secondInput)] {
        setStatus "Supply a second input filename"
        return
    }
    if ![string length $Select(outputFile)] {
        setStatus "Supply a output filename"
        return
    }
    set command "sddsselect $Select(inputFile) $Select(secondInput) $Select(outputFile)"
    set Select(matchColumn1) [string trim $Select(matchColumn1)]
    set Select(matchColumn2) [string trim $Select(matchColumn2)]
    if ![string length $Select(matchColumn1)] {
        setStatus "Supply the match columns"
        return
    }
    if [string length $Select(matchColumn2)] {
        set matchOpt $Select(matchColumn1)=$Select(matchColumn2)
    } else {
        set matchOpt $Select(matchColumn1)
    }
    switch $Select(matchOption) {
        match {
            append command " -match=$matchOpt"
        }
        equate {
            append command " -equate=$matchOpt"
        }
    }
    if $Select(reusePage) {
        append command " -reuse=page"
    } elseif $Select(reuse) {
        append command " -reuse"
    }
    if $Select(invert) {
        append command " -invert"
    }
    setStatus "$command"
    if [catch {eval exec $command} result] {
	setStatus "$result"
	return
    }
    setStatus "Done."
}

proc AddDeleteRenameOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}
    
    global DeleteRename DeleteRenameNameList
    APSFrame .opts -parent $parent -label "Options"
    set w $parent.opts.frame
    $w configure -relief flat -bd 0
    set tabList {column parameter}
    set tabWidgetList [APSTabFrame .opt -parent $w -labelList $tabList -width 800 -height 270]
    set Column [lindex $tabWidgetList 0]
    set Parameter [lindex $tabWidgetList 1]
    foreach type {Column Parameter} name {column parameter} par {0 1} {
        set DeleteRename(rename${type}List) ""
        lappend DeleteRenameNameList rename${type}List
        makeVariableFrame .rename -parent [set $type] -label "Rename ${name}(s):" -width 50  \
          -arrayName DeleteRename -slot rename${type}List -single 0 -filenameVariable DeleteRename(inputFile) -parameter $par \
          -contextHelp \
          "Entry a a comma- or space-separated list of ${name}s for renaming"
        set DeleteRename(new${type}List) ""
        lappend DeleteRenameNameList new${type}List
        APSLabeledEntry .new -parent [set $type].rename.frame -label "New $name name(s):" \
          -width 60 -textVariable DeleteRename(new${type}List) \
          -contextHelp \
          "Entry a a comma- or space-separated list of new $name names for the rename ${name}s"
        
        set DeleteRename(delete${type}List) ""
        set DeleteRename(retain${type}List) ""
        lappend DeleteRenameNameList delete${type}List
        lappend DeleteRenameNameList retain${type}List
        makeVariableFrame .delete -parent [set $type] -label "Delete ${name}(s):" -width 50  \
          -arrayName DeleteRename -slot delete${type}List -single 0 -filenameVariable DeleteRename(inputFile) -parameter $par \
          -contextHelp \
          "Entry a a comma- or space-separated list of ${name}s for deleting"
        makeVariableFrame .retain -parent [set $type] -label "Retain ${name}(s):" -width 50  \
          -arrayName DeleteRename -slot retain${type}List -single 0 -filenameVariable DeleteRename(inputFile) -parameter $par \
          -contextHelp \
          "Entry a a comma- or space-separated list of ${name}s for deleting"

        set DeleteRename(edit${type}) ""
        set DeleteRename(edit${type}Command) ""
        lappend DeleteRenameNameList edit${type}
        lappend DeleteRenameNameList edit${type}Command
        APSFrame .edit -parent [set $type]
        set w [set $type].edit.frame
        APSLabeledEntry .edit -parent $w -label "Edit ${name} name:" \
          -textVariable DeleteRename(edit${type}) \
          -width 55 -contextHelp "enter the wildcard string for editting ${name}(s)"
        APSLabeledEntry .com -parent $w -label "  Edit command:" \
          -width 55 -textVariable DeleteRename(edit${type}Command) \
          -contextHelp \
          "enter the edit command"
    }
    lappend DeleteRenameNameList inputFile
    lappend DeleteRenameNameList outputFile
    APSLabeledEntry .output -parent $parent -label "Output file: " -width 90 \
	-commandButton 1 -textVariable DeleteRename(outputFile) 

    APSButton .run -parent $parent -text "Run" -command "RunDeleteRename"
    
}

proc RunDeleteRename {args} {
    global DeleteRename DeleteRenameNameList
    
    foreach name $DeleteRenameNameList {
        set DeleteRename($name) [string trim [string trimright $DeleteRename($name)]]
    }
    if ![string length $DeleteRename(inputFile)] {
        setStatus "Supply an input file."
        return
    }
    if ![string length $DeleteRename(outputFile)] {
        setStatus "Supply an output file"
        return
    }
    set command "sddsconvert $DeleteRename(inputFile) $DeleteRename(outputFile)"
    foreach type {Column Parameter} {
        set oldList [join [os editstring "1000%/,/ /1000(a%/  / /)" $DeleteRename(rename${type}List)]]
        set newList [join [os editstring "1000%/,/ /1000(a%/  / /)" $DeleteRename(new${type}List)]]
        if [llength $oldList] {
            if {[llength $newList]!=[llength $oldList]} {
                setStatus "The rename $type list and the new $type list must be the same length."
                return
            }
            set renameList ""
            foreach old $oldList new $newList {
                lappend renameList ${old}=${new}
            }
            append command " -rename=$type,[join $renameList ,]"
        }
        foreach nm {delete retain} {
            set dataList [join [os editstring "1000%/,/ /1000(a%/  / /)" $DeleteRename(${nm}${type}List)]]
            if [llength $dataList] {
                append command " -$nm=$type,[join $dataList ,]"
            }
        }
        set DeleteRename(edit${type}) [lindex $DeleteRename(edit${type}) 0]
        set DeleteRename(edit${type}Command) [lindex $DeleteRename(edit${type}Command) 0]
        if [string length $DeleteRename(edit${type})] {
            if ![string length $DeleteRename(edit${type}Command)] {
                setStatus "please supply the edit command for $type"
                return
            }
            append command " -edit=$type,$DeleteRename(edit${type}),$DeleteRename(edit${type}Command)"
        }
    }
    setStatus "$command"
    if [catch {eval exec $command} result] {
	setStatus "$result"
	return
    }
    setStatus "Done."
}

proc AddConvertOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}
    
    global Convert ConvertNameList
    
    
    set Convert(acceptAllNames) 0
    set Convert(recover) 0
    set Convert(recoverClip) 0
    APSCheckButtonFrame .check -parent $parent -orientation horizontal -label "" \
      -buttonList {"acceptAllNames  " "recover  " "recover=clip  "} \
      -variableList {Convert(acceptAllNames) Convert(recover) Convert(recoverClip)} \
      -contextHelp "acceptAllNames -- force the SDDS library to accept element names that have spaces and other normally unacceptable characters.\n recover -- sddsconvert  tries to save as much data from the corrupted page as it can\n recover=clip, all data from a corrupted page is ignored."
    
    APSFrame .type -parent $parent
    set w $parent.type.frame
    $w configure -bd 0 -relief flat
    set Convert(type) ""
    set Convert(linesPerRow) ""
    set commandList [list "APSDisableWidget $parent.type.frame.lines" "APSEnableWidget $parent.type.frame.lines" "APSDisableWidget $parent.type.frame.lines"]
    APSRadioButtonFrame .type -parent $w -packOption "-side left" -orientation horizontal \
      -buttonList {binary ascii default} -valueList {-binary -ascii ""} \
      -variable Convert(type) -label "Convert-to file type" \
      -commandList $commandList \
      -contextHelp "select binary or ascii to convert the input file into binary, ascii or select default to keep the input file type." 
    APSLabeledEntry .lines -parent $w -packOption "-side right" \
      -label "      Lines per row:" -width 30 -textVariable Convert(linesPerRow) \
      -contextHelp "Sets the number of lines of text output per row of the tabular data, for ASCII output only."
    APSDisableWidget $parent.type.frame.lines
    APSFrame .fromto -parent $parent
    set fromto $parent.fromto.frame
    $fromto configure -bd 0 -relief flat
    set Convert(fromPage) ""
    set Convert(toPage) ""
    makeVariableFrame .from -parent $fromto -label "From page:" -width 20 -packOption "-side left" \
      -arrayName Convert -slot fromPage -page 1 -single 1 -filenameVariable Convert(inputFile) -contextHelp \
      "Entry a page number to convert the file from."
    makeVariableFrame .to -parent $fromto -label "  To page:" -width 20 -packOption "-side right" \
      -arrayName Convert -slot toPage -page 1 -single 1 -filenameVariable Convert(inputFile) -contextHelp \
      "Entry a page number to convert the file to."
    set Convert(removePages) ""
    set Convert(keepPages) ""
    makeVariableFrame .removepage -parent $parent -label "Remove pages: " -width 60 \
      -arrayName Convert -slot removePages -page 1 -single 0 -filenameVariable Convert(inputFile) -contextHelp \
      "Entry a comma- or space-separated list of page numbers to be removed."
    makeVariableFrame .keeppage -parent $parent -label "Keep pages:   " -width 60 \
      -arrayName Convert -slot keepPages -page 1 -single 0 -filenameVariable Convert(inputFile) -contextHelp \
      "Entry a comma- or space-separated list of page numbers to keep."
    
    APSLabeledEntry .output -parent $parent -label "Output file: " -width 90 \
	-commandButton 1 -textVariable Convert(outputFile) 

    APSButton .run -parent $parent -text "Run" -command "RunConvert"
}

proc RunConvert {args} {
    global Convert
    foreach name {inputFile outputFile} {
        set Convert($name) [string trim [string trimright $Convert($name)]]
    }
    if ![string length $Convert(inputFile)] {
        setStatus "Supply an input file."
        return
    }
    if ![string length $Convert(outputFile)] {
        setStatus "Supply an output file"
        return
    }
    
    set command "sddsconvert $Convert(inputFile) $Convert(outputFile) $Convert(type)"
    if {$Convert(type)=="-ascii"} {
        set Convert(linesPerRow) [lindex $Convert(linesPerRow) 0]
        if [string length $Convert(linesPerRow)] {
            append command " -linesPerRow=$Convert(linesPerRow)"
        }
    }
    foreach name {fromPage toPage} {
        set Convert($name) [lindex $Convert($name) 0]
        if [string length $Convert($name)] {
            append command " -$name=$Convert($name)"
        }
    }
    foreach name {removePages keepPages} {
        set pageList [join [os editstring "1000%/,/ /1000(a%/  / /)" $Convert($name)]]
        if [llength $pageList] {
            append command " -$name=[join $pageList ,]"
        }
    }
    if $Convert(acceptAllNames) {append command " -acceptAllNames"}
    if $Convert(recoverClip) {
        append command " -recover=clip"
    } elseif $Convert(recover) {
        append command " -recover"
    }
    
    setStatus "$command"
    if [catch {eval exec $command} result] {
	setStatus "$result"
	return
    }
    setStatus "Done."
}

proc FillTabFrame {widget args} {
    set type ""
    global filename
    APSStrictParseArguments {type}
    global $type
    set ${type}(inputFile) $filename
    APSLabeledEntry .file -parent $widget -textVariable ${type}(inputFile) \
      -width 90 -commandButton 1 -label "Input filename: "
    catch {Add${type}Options -parent $widget} result
}

# Xref Select Convert(Rename/Delete) Convert(Ascii/Binary/PageSelect/Recover)
set tabLabelList [list Sort Filter Combine Collapse Expand Xref Select DeleteRename Convert]
set tabDescriptionList [list "Sort data by columns or parameters" \
                          "Filter data by columns or parameters" \
                          "Combine multiple data sets and merge pages" \
                          "Abstract parameters into a new table" \
                          "Turn each row into a page (opposite of collapse)" \
                          "Creates a new data set by adding selected rows from one data set to another data set" \
                          "Excludes or includes rows from one file based on the presence of matching data in another" \
                          "Converts SDDS files through renaming or deleting columns, parameters or arrays." \
                          "Converts SDDS files between ASCII and binary, selects pages and recovers from corrupted files."]
set commandList ""
foreach label $tabLabelList description $tabDescriptionList {
    lappend commandList "setStatusNoDate \"$label: $description\""
}
set tabWidgetList [APSTabFrame .main -parent .userFrame \
		       -labelList $tabLabelList -width 800 -height 550 \
		       -commandList $commandList]
foreach tabWidget $tabWidgetList type $tabLabelList {
    FillTabFrame $tabWidget -type $type
}

