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

# $Log: not supported by cvs2svn $
# Revision 1.56  2010/09/17 15:03:53  soliday
# Updated so that the scrolled status uses a slightly more obscure variable
# name. A problem was noticed when searching columns when one of the columns
# had the name 'status'
#
# Revision 1.55  2009/08/12 20:59:22  borland
# Fixed a long-standing typo.
#
# Revision 1.54  2008/05/21 18:17:46  shang
# added mouse wheel support to column and parameter data.
#
# Revision 1.53  2006/10/24 18:00:41  soliday
# Added support for unsigned shorts and unsigned longs.
#
# Revision 1.52  2006/06/20 16:14:10  soliday
# Make the scrolled status smaller.
#
# Revision 1.51  2006/02/23 15:47:17  soliday
# Updated because some string trimimng statements were not trimming the strings
# exactly as the author intended.
#
# Revision 1.50  2005/11/28 19:53:50  emery
# Changed a comment.
#
# Revision 1.49  2005/10/27 15:49:27  soliday
# Changed the tcl_precision from 15 to 16 to better get better accuracy floating
# point numbers. 17 is the maximum Tcl will allow you to use but there are
# issues with setting it to this amount. Also it appears that sddsprocess
# and sddsconvert use 16 digit precision as well.
#
# Revision 1.48  2005/08/09 14:04:24  jiaox
# Added column breaks to page selection menu so that files with large number of pages can be displayed properly.
#
# Revision 1.47  2004/04/29 19:59:40  soliday
# Fixed another issue with Tktable 2.8. New row numbers were not displayed when
# new rows were added.
#
# Revision 1.46  2004/04/16 14:53:44  soliday
# Modified to work with Tktable 2.8
#
# Revision 1.45  2003/10/20 18:29:53  soliday
# Fixed a bug when saving an alterned a fixed value parameter.
#
# Revision 1.44  2003/08/25 20:59:12  soliday
# Changed tcl_percision to 15
#
# Revision 1.43  2003/08/25 20:55:44  soliday
# Changed the tcl_percision level to 16.
#
# Revision 1.42  2003/06/04 17:35:12  soliday
# Fixed a problem with the middle mouse button.
#
# Revision 1.41  2003/05/07 21:30:12  soliday
# Fixed a problem when running tcl 8.3.5
#
# Revision 1.40  2002/10/11 14:46:44  soliday
# Changed the tktables to double buffered draw mode
#
# Revision 1.39  2002/10/10 14:48:19  soliday
# Fixed a bug calling PerformSearch
#
# Revision 1.38  2002/09/18 21:14:51  soliday
# Added more changes due to newer version of Tktable
#
# Revision 1.37  2002/09/18 03:13:05  soliday
# More of the same changes
#
# Revision 1.36  2002/09/18 03:11:59  soliday
# Changed to work with tcl/tk 8.4.0 version
#
# Revision 1.35  2001/08/28 23:58:53  shang
# added matchAll and matchAny to search feature
#
# Revision 1.34  2001/07/30 22:11:42  shang
# fix search parameter feature problem
#
# Revision 1.33  2000/10/27 00:25:43  emery
# corrected typo.
#
# Revision 1.32  2000/08/30 21:43:45  soliday
# Fixed pasting with mouse when editing large string.
#
# Revision 1.31  2000/08/30 21:00:44  soliday
# The edit string command now warns the user if a cell is not selected.
#
# Revision 1.30  2000/08/30 19:42:39  soliday
# The os editstring command is limited to 1024 characters. When larger strings are
# used the command line program editstring is used.
#
# Revision 1.29  2000/05/18 17:10:30  emery
# Indented code
#
# Revision 1.28  2000/04/24 13:59:16  borland
# Per D. Blachowicz: print feature added.
#
# Revision 1.27  2000/03/30 21:13:15  borland
# Per D. Blachowicz: speeded up multi-row deletion from search dialog.
# Fixed row position from search dialog with long files.
#
# Revision 1.25  2000/01/26 20:13:56  borland
# Edit string problem (with spaces) fixed by D. Blachowicz.
#
# Revision 1.24  2000/01/07 21:33:21  borland
# New version per D. Blachowicz.  Overwrote Emery's indentation of the previous
# version.
#
# Revision 1.22  1999/12/10 17:38:40  borland
# Per D. Blachowicz: added extended length entry box for editing really long
# entries.
#
# Revision 1.21  1999/11/18 18:04:47  soliday
# Added the Go To Page option inside the Edit menu.
#
# 
#

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)]
if [info exists env(OAG_TOP_DIR)] {
    set auto_path [linsert $auto_path 0  $env(OAG_TOP_DIR)/oag/apps/lib/$env(HOST_ARCH)]
}
   
set apsttk 1
set CVSRevisionAuthor "\$Revision: 1.57 $ \$Author: soliday $"
if {[info exists tcl_pkgPath]} {
    set tableLib [file join [lindex $tcl_pkgPath 0] Tktable2.11 libTktable2.11.so]
    if {($tcl_platform(os) == "Linux") && [file exists $tableLib]} {
        load $tableLib TkTable
    } else {
        package require Tktable
    }
} else {
    package require Tktable
}
set workingDir [pwd] 
set inputFile ""
set inputID ""
set validData 0
set cellIndex {}
set cellInput {}
set selectType ""
set previousRow 0
set previousColumn 0
set copyBase ""
set changeInActive 0
set currentCell ""
set searchValue ""
set gotoPage 1
set tkPriv(tableDeleteRows) ""

set optionTag(type) type
set optionTag(symbol) symbol
set optionTag(units) units
set optionTag(description) description
set optionTag(format_string) formatString
set optionTag(fixed_value) fixedValue
set optionTag(field_length) fieldLength
set optionTag(group_name) groupName 
set optionTag(dimenions) dimensions

# 50 MB/page
set largeFileTrigger 50000000

#
# openFile --
#
# This procedure looks for a file, check if the file is sdds style, 
# reads all elements of the file and stores them in data structure
# variables, invokes a spreadsheet setup and its initialization
#
# Arguments:
# fileName - type string - local variable - name of the file to be open,
# inputFile - type string - global variable - name of the open file 
#             displayed in "Now editing" output,  
# workingDir - type string - global variable - initially a user directory,
#             switched to inputFile directory,              
# validData - type integer - global variable - boolean value indicating
#             if some data structure may be alredy present in the memory
#             during the current run of the application,
# table1 - type array - global variable - a data structure
#             containing all attributes for parameters table,
# table2 - type array - global variable - a data structure
#             containing all attributes for column table,
# parametersArray - type array - global variable - a data structure which
#             stores indices of parameter table,
# columnsArray - type array - global variable - a data structure which
#             stores indices of column table,
# undoArray - type array - global variable - a data structure which  
#             stores indices of all selected cells in the column table,
# tkPriv - type array - global variable - a data structure which  
#             stores all information needed for the spreadsheet operations,
# numRows - type integer - global variable - initially a number of rows
#             present in the inputFile, switched to the number of rows in 
#             the spreadsheet.    

set numPages 0
proc openFile {args} {
    set fileName ""
    if [APSStrictParseArguments {fileName}] {
        return -code error "openFile: bad arguments"
    }
    global workingDir validData hideColumns hideParameters
    global inputFile numRows
    global table1 table2 parametersArray columnsArray undoArray tkPriv 
    global descriptionInfo cellIndex cellInput
    global hideList
    global dataArray
    global currentPageIndex currentPage numPages pageRowCount previousCurrentPage
    global defaultFile
    set currentPageIndex 0
    set previousCurrentPage 1 
    set currentPage 1
    set descriptionInfo ""
    set userChoice ""
    set hideList ""
    set defaultFile ""
    set questionFlag 0
    if {![string length $fileName]} {
        global hideList
        set fileName [tk_getOpenFile -initialdir $workingDir]
        update idletasks
        set questionFlag 1
    }

    if ![string length $fileName] {
        return
    }
    if ![file exists $fileName] {
        APSSetVarAndUpdate ScrolledStatus "File $fileName does not exist."
        bell
        return
    }
    APSSetVarAndUpdate ScrolledStatus "[clock format [clock seconds] -format %H:%M:%S] Opening file..."

    if $validData {
        set inputFile ""
        foreach variable {undoArray parametersArray columnsArray dataArray} {
            if [info exists $variable] {                         
                unset $variable
            }
        }
        set validData 0
    }

    EliminateMajorWidgets

    foreach ss [list .parameterPane.parTop .parameterPane.parameters \
                  .columnPane.colLabel .columnPane.columns] {
        if [winfo exists $ss] {
            pack unpack $ss
        }
    }

    if [catch {exec sddscheck $fileName -printErrors} result] {
        APSSetVarAndUpdate ScrolledStatus "$result"
        return
    }
    if {$result != "ok"} {
        APSSetVarAndUpdate ScrolledStatus "Problems with file: $result"
        return	
    }
    set workingDir [file dirname $fileName]
    set fileSize [file size $fileName]
    global largeFileTrigger
    set pages [exec sdds2stream -npage=bare $fileName]
    if {[expr ($fileSize/(1.0*$pages)) > $largeFileTrigger]} {
        if {[APSMultipleChoice [APSUniqueName .] \
               -question "$fileName appears to have very large pages.  Do you really want to edit it?" \
               -labelList {Yes No} -returnList {Yes No}] == "No"} {
            return
        }
    }
    if [catch {sdds load $fileName dataArray} result] {
        APSSetVarAndUpdate ScrolledStatus "Error opening $fileName: $result"
        return
    }
    
    if $questionFlag {
        bell    
        catch {APSMultipleChoice [APSUniqueName .] \
                 -name "Protect Entities" \
                 -question "Do you want to protect any entities?" \
                 -labelList {"Yes-Columns" "Yes-Parameters" "Yes-Both" "No-None"} \
                 -returnList {Columns Parameters Both None}} userChoice
    }
    
    if [string length $userChoice] {
        switch $userChoice {
            Columns {
                set hideList ""
                DisplayColumnParameterList $dataArray(ColumnNames) Columns
                tkwait variable hideList
                foreach el $hideList {
                    lappend hideColumns $el
                }
            }
            Parameters {
                set hideList ""
                DisplayColumnParameterList $dataArray(ParameterNames) Parameters
                tkwait variable hideList
                foreach el $hideList {
                    lappend hideParameters $el
                }
            }
            Both {
                set hideList ""
                DisplayColumnParameterList $dataArray(ColumnNames) Columns
                tkwait variable hideList
                foreach el $hideList {
                    lappend hideColumns $el
                }
                set hideList ""
                DisplayColumnParameterList $dataArray(ParameterNames) Parameters
                tkwait variable hideList
                foreach el $hideList {
                    lappend hideParameters $el
                }
            }
            None {
            }
            default {
                APSSetVarAndUpdate ScrolledStatus "Error getting a user answer to protect entities: $userChoice."
                return  
            }
        }
    }
    if {[info exists dataArray(Description.Contents)]} {
        set dataArray(Description.Contents) [string trim $dataArray(Description.Contents) \{\}]
    }
    if {[info exists dataArray(Description.Text)]} {
        set dataArray(Description.Text) [string trim $dataArray(Description.Text) \{\}]
    }

    MakeSpreadsheet  

    set table1(array) parametersArray
    set table2(array) columnsArray

    OpenTablesForCurrentPage 0

    set inputFile $fileName

    APSSetVarAndUpdate ScrolledStatus "File read successfully."
    if {$numPages == 1} { 
        set status1 "$numPages page"
    } else {
        set status1 "$numPages pages"
    }
    APSSetVarAndUpdate ScrolledStatus "$status1 \
       \n[llength $dataArray(ParameterNames)] parameters \
       \n[llength $dataArray(ColumnNames)] columns with $numRows rows in page 1 \
       \n[clock format [clock seconds] -format %H:%M:%S] File is open. \
       \nFor usage information, please refer to\
       \"Guidelines\" in the \"Info\" sub menu."     
}


proc ChangePage {args} {
    set saveCurrPage 0
    APSStrictParseArguments {saveCurrPage}
    global validData numRows apsContextHelp tkPriv
    global dataArray currentPage currentPageIndex previousCurrentPage
    global table1 table2 hideColumns hideParameters
    global numPages pageRowCount parametersArray columnsArray

    if !$saveCurrPage {
        if {$previousCurrentPage == $currentPage} {
            return
        }
    }
    if !$validData {
        APSSetVarAndUpdate ScrolledStatus "No data."
        set currentPage $previousCurrentPage
        return
    }
    if !$saveCurrPage {
        APSSetVarAndUpdate ScrolledStatus "Switching pages..."
    }
    set columnNum [llength $dataArray(ColumnNames)]
    set parameterNum [llength $dataArray(ParameterNames)]
    if $parameterNum {
        for {set p 0} {$p < $parameterNum} {incr p} {
            set parameter [.parameterPane.parameters.t get $p,0]
            set parValue [.parameterPane.parameters.t get $p,1]
            array set infoArray $dataArray(ParameterInfo.$parameter)
            if {($infoArray(type) != "SDDS_STRING") && ($infoArray(type) != "SDDS_CHARACTER")} {
                if {[llength $parValue] != 1} {
                    set zeroParFlag 0
                    if [catch {DisplayMessage2 parameter $parameter $p} zeroParFlag] {
                        APSSetVarAndUpdate ScrolledStatus $zeroParFlag
                        set currentPage $previousCurrentPage
                        return
                    } else {
                        if $zeroParFlag {
                            APSSetVarAndUpdate ScrolledStatus "\nSwitching pages canceled."
                            set currentPage $previousCurrentPage
                            return			    
                        }
                        set parValue 0
                    }
                }
            }
            set dataArray(Parameter.$parameter) [lreplace $dataArray(Parameter.$parameter) $currentPageIndex $currentPageIndex $parValue]
        }
    }

    if $columnNum {
        set tempRowList ""
        set deleteFlag 0
        for {set c 1} {$c <= $columnNum} {incr c} {
            set column [.columnPane.columns.t get 0,$c]
            set data ""
            array set infoArray $dataArray(ColumnInfo.$column)
            if {($infoArray(type) != "SDDS_STRING") && ($infoArray(type) != "SDDS_CHARACTER")} {
                for {set row 1} {$row <= $numRows} {incr row} {
                    if [llength $tkPriv(tableDeleteRows)] {
                        if {[lsearch $tkPriv(tableDeleteRows) $row] > -1} {
                            set deleteFlag 1
                        }
                    }
                    if !$deleteFlag {
                        set colValue [.columnPane.columns.t get $row,$c]
                        if {[llength $colValue] != 1} {
                            set zeroColFlag 0
                            if [catch {DisplayMessage2 column $column $row} zeroColFlag] {
                                APSSetVarAndUpdate ScrolledStatus "$zeroColFlag"
                                set currentPage $previousCurrentPage
                                return
                            } else {
                                if $zeroColFlag {
                                    APSSetVarAndUpdate ScrolledStatus "\nSwitching pages canceled."
                                    set currentPage $previousCurrentPage
                                    return			    
                                }
                            }
                            set colValue 0
                        }
                        lappend data $colValue
                    }
                    set deleteFlag 0
                }
            } else {
                for {set row 1} {$row <= $numRows} {incr row} {
                    if [llength $tkPriv(tableDeleteRows)] {
                        if {[lsearch $tkPriv(tableDeleteRows) $row] > -1} {
                            set deleteFlag 1
                        }
                    }
                    if !$deleteFlag {
                        set colValue [.columnPane.columns.t get $row,$c]
                        lappend data $colValue
                    }
                    set deleteFlag 0
                }
            }
            set dataArray(Column.$column) [lreplace $dataArray(Column.$column) $currentPageIndex $currentPageIndex $data]
        }
    }

    if $saveCurrPage {return}

    set currentPageIndex [expr $currentPage - 1]
    if $validData {
        foreach variable {undoArray parametersArray columnsArray} {
            if [info exists $variable] {                         
                unset $variable
            }
        }
        set validData 0
    }
    EliminateMajorWidgets
    foreach ss [list .parameterPane.parTop .parameterPane.parameters \
                  .columnPane.colLabel .columnPane.columns] {
        if [winfo exists $ss] {
            pack unpack $ss
        }
    }
    MakeSpreadsheet 

    OpenTablesForCurrentPage $currentPageIndex

    set previousCurrentPage $currentPage
    APSSetVarAndUpdate ScrolledStatus "Done"
    bell
}

proc OpenTablesForCurrentPage {curPageIndex} {
    global validData numRows dataArray currentPage
    global table1 table2 hideColumns hideParameters
    global numPages pageRowCount parametersArray columnsArray
    set index1 -1
    set parameterWidth 0
    set parameterDataWidth 0
    if [llength $hideParameters] {
        $table1(table) tag configure disabled -state disabled
    }
    set numPages 0
    foreach parameter $dataArray(ParameterNames) {
        set indexH -1
        if [llength $hideParameters] {
            set indexH [lsearch -glob $hideParameters $parameter]
        } 
        incr index1
        if {$indexH >= 0} {
            $table1(table) tag row disabled $index1
        } 

        set l [string length $parameter]
        if {$parameterWidth < $l} {
            set parameterWidth $l
        }
        set $table1(array)($index1,0) $parameter
        
        set temp [lindex $dataArray(Parameter.$parameter) $curPageIndex]
        set lData [string length $temp]
        if {$parameterDataWidth < $lData} {
            set parameterDataWidth $lData
        }
        set $table1(array)($index1,1) [string trim $temp \{\}]
        if {$numPages == 0} {
            set numPages [llength $dataArray(Parameter.$parameter)]
        }
    }
    if [llength $dataArray(ParameterNames)] {    
        $table1(table) width 0 [expr 2 + $parameterWidth]
        $table1(table) width 1 [expr 2 + $parameterDataWidth]
    }
    incr index1
    if {$index1 > 6} {
        set height1 6
    } else {
        set height1 $index1
    }
    $table1(table) configure -rows $index1 -height $height1 
    if {[llength $dataArray(ColumnNames)]} {
        set column [lindex $dataArray(ColumnNames) 0]
        set index 0
        foreach page $dataArray(Column.$column) {
            set pageRowCount($index) [llength $page]
            incr index
        }
    } else {
        set pageRowCount($curPageIndex) 0
    }
    set numRows $pageRowCount($curPageIndex)
    if {$numRows > 0} {
        for {set rowIndex 1} {$rowIndex <= $numRows} {incr rowIndex} {
            set $table2(array)($rowIndex,0) $rowIndex
        }
        if {[string length $numRows] > 11} {
            set widthCol0 [expr 2 + [string length $numRows]]
        } else {
            set widthCol0 13
        }
        $table2(table) width 0 $widthCol0
    }
    set colIndex 0
    if [llength $hideColumns] {
        $table2(table) tag configure disabled -state disabled
    }
    foreach column $dataArray(ColumnNames) {
        incr colIndex
        set indexH -1
        if [llength $hideColumns] {
            set indexH [lsearch -glob $hideColumns $column]
        } 
        if {$indexH >= 0} {
            $table2(table) tag col disabled $colIndex
        } 
        set $table2(array)(0,$colIndex) $column
        $table2(table) width $colIndex [expr 2 + [string length $column]]
        set width [string length $column]
        if {$numRows > 0} {
            set rowIndex 0
            foreach item [lindex $dataArray(Column.$column) $curPageIndex] {
                if {$width < [string length [set columnsArray([incr rowIndex],$colIndex) $item]]} {
                    set width [string length $item]
                }
            }
            $table2(table) width $colIndex [expr 2 + $width]
        }
        if {$numPages == 0} {
            set numPages [llength $dataArray(Column.$column)]
        }
    }
    if {![llength $dataArray(ColumnNames)]} {
        for {set index 0} {$index < $numPages} {incr index} {
            set pageRowCount($index) 0
        }
    }

    .parameterPane.parTop.page.menu delete 0 end
    for {set i 1} {$i <= $numPages} {incr i} {
        if { [ expr { $i%20 } ] == 1 } {
            .parameterPane.parTop.page.menu add radiobutton -label $i -variable currentPage -command ChangePage -columnbreak 1
        } else { .parameterPane.parTop.page.menu add radiobutton -label $i -variable currentPage -command ChangePage }
    }
    if {$numRows > 20} {
        set height2 20
    } else {
        set height2 [expr $numRows + 1]
    }
    $table2(table) configure -cols [expr $colIndex + 1] \
      -rows [expr $numRows + 1] -height $height2
    update

    APSSetVarAndUpdate ScrolledStatus "Page number [expr ($curPageIndex + 1)] contains $numRows rows."

    # This is an initialization of the spreadsheet functions and variables 
    $table1(table) activate 0,1
    set t2 $table2(table)
    focus $t2
    $t2 activate 1,1
    $t2 selection clear all
    $t2 selection set active
    $t2 selection anchor active
    $t2 see active
    set i [$t2 index active]
    updateInputBox
    set tkPriv(tableIndex) ""
    set tkPriv(tablePrev) $i
    set tkPriv(tableIndex) $i
    # end of the initialization
    
    foreach butt [list .menu.edit .menu.search .menu.editCom .menu.info] {
        $butt configure -state normal
    }

    set validData 1
}

#
# DisplayColumnParameterList
#
# This procedure invokes a display of all parameters or
# all columns or both read from the edited file.
#
# Arguments:
# aList - type list - a list of chosen entities (parameters or columns)
# factor - type string - a name of the entity
#

proc DisplayColumnParameterList {aList factor} {
    global variable hideFactor
    set hideFactor $factor
    if [winfo exists .hide$factor] {
        destroy .hide$factor
    }
    set variable ""
    APSScrolledListWindow .hide$factor \
      -name "Protection Choices" -label "$factor choices" \
      -itemList $aList \
      -selectionVar $variable \
      -callback ProvideHideLists
}

proc ProvideHideLists {list} {
    global hideList
    set hideList ""

    foreach elem $list {
        lappend hideList $elem
    }
}

# MakeInfoText --
#
# This procedure may be invoked by two of menu buttons:
# 1. "Attributes" in the "Edit" menu for parameters and columns -
#     in this case an edit form is provided to change characteristic
#     of the chosen entity. It is not avaliable when the entity is 
#     protected.
# 2. "Info" for "Parameters" and "Columns" - displays just 
#     characteristic of the entity without possibility to change it.
#     It is avaliable even when the entity is protected.
#

proc MakeInfoText {infoElement display} {
    global dataArray
    switch $infoElement {
        parameter {
            if ![llength $dataArray(ParameterNames)] {
                APSSetVarAndUpdate ScrolledStatus "\nNo parameter exists!"
                bell
                return
            }
            set t .parameterPane.parameters.t
            set cell [$t index active]
            set row [lindex [split $cell ,] 0]
            set parameterName [$t get $row,0]
            set winName "Parameter -> $parameterName"
            set attName $parameterName
            set position $row,0
            
            array set infoArray $dataArray(ParameterInfo.$parameterName)
        }
        column {
            set t .columnPane.columns.t
            set cell [$t index active]
            set col [lindex [split $cell ,] 1]
            if ![llength $dataArray(ColumnNames)] {
                APSSetVarAndUpdate ScrolledStatus "\nNo column exists!"
                bell
                return
            }
            if !$col {
                APSSetVarAndUpdate ScrolledStatus "\nSelect column!"
                bell
                return
            }
            set columnName [$t get 0,$col]
            set winName "Column -> $columnName"
            set attName $columnName
            set position 0,$col

            array set infoArray $dataArray(ColumnInfo.$columnName) 
        }
        default {
            APSSetVarAndUpdate ScrolledStatus "MakeInfoText: Not valid table object $infoElement. Should be: parameter or column."
            bell
            return
        }
    }
    if [winfo exist .infowind] {
        destroy .infowind
    }   

    switch $display {
        edit {
            if [$t tag includes disabled $cell] {
                APSSetVarAndUpdate ScrolledStatus "\n$winName is protected. Not able to edit its attributes."
                bell
                return
            }
            set okCommand "UpdateAttributes $infoElement $attName $position"
            APSDialogBox .infowind -name Info_Edit -width 40 \
              -cancelCommand "" -okCommand $okCommand -contextHelp \
              "Information window containing $winName attributes."
            set parent .infowind.userFrame
            pack [ttk::label ${parent}.infoLabel -text "Attributes of $winName" \
                    -font TkHeadingFont -anchor center] -fill both -expand false
            foreach elem [array names infoArray] {
                global $elem
                set $elem $infoArray($elem)
                APSLabeledEntry .$elem -parent $parent -label "$elem: " \
                  -textVariable $elem -width 30 -contextHelp ""
            }
        }
        info {
            set text ""
            set text "$winName\n\n"
            foreach elem [array names infoArray] {
                set text "$text$elem = $infoArray($elem)\n"
            }

            APSInfoWindow .infowind -name Info -infoMessage $text \
              -contextHelp "Information window containing $winName attributes."
        }
    }
}

#
# UpdateAttributes --
# 
# This a subprocedure for MakeInfoText procedure when an edit mode
# is invoked.
#

proc UpdateAttributes {elem elemName pos} {
    global description name field_length symbol type units format_string  
    global fixed_value
    global dataArray

    if {[lsearch [list SDDS_SHORT SDDS_USHORT SDDS_LONG64 SDDS_ULONG64 SDDS_LONG SDDS_ULONG SDDS_FLOAT SDDS_DOUBLE SDDS_LONGDOUBLE\
                    SDDS_CHARACTER SDDS_STRING] $type] < 0} {
        APSSetVarAndUpdate ScrolledStatus "\nInvalid type $type. Changes not saved."
        bell
        return
    }
    if {[llength $name] != 1} {
        APSSetVarAndUpdate ScrolledStatus "\nInvalid Name $name. Changes not saved."
        bell
        return
    }

    switch $elem {
        column {
            if {[string compare $elemName $name]!=0 && [lsearch -exact $dataArray(ColumnNames) $name]>=0} {
                APSSetVarAndUpdate ScrolledStatus "\nA column with the name $name already exists.\
                                            Changes not saved."
                bell
                return
            } 
            set i [lsearch -exact $dataArray(ColumnNames) $elemName]
            set dataArray(ColumnNames) [lreplace $dataArray(ColumnNames) $i $i $name]
            unset dataArray(ColumnInfo.$elemName)
            set dataArray(ColumnInfo.$name) ""
            foreach e {description name field_length symbol type units format_string} {
                lappend dataArray(ColumnInfo.$name) $e
                lappend dataArray(ColumnInfo.$name) [set $e]
            }
            if {$name != $elemName} {
                set dataArray(Column.$name) $dataArray(Column.$elemName)
                unset dataArray(Column.$elemName)
            }
            .columnPane.columns.t set $pos $name
        }
        parameter {
            if {[string compare $elemName $name]!=0 && [lsearch -exact $dataArray(ParameterNames) $name] >= 0} {
                APSSetVarAndUpdate ScrolledStatus "\nA parameter with the name $name already exists.\
                                            Changes not saved."
                bell
                return
            }
            set i [lsearch -exact $dataArray(ParameterNames) $elemName]
            set dataArray(ParameterNames) [lreplace $dataArray(ParameterNames) $i $i $name]
            unset dataArray(ParameterInfo.$elemName)
            set dataArray(ParameterInfo.$name) ""
            foreach e {description name fixed_value symbol type units format_string} {
                lappend dataArray(ParameterInfo.$name) $e
                lappend dataArray(ParameterInfo.$name) [set $e]
            }
            if {$name != $elemName} {
                set dataArray(Parameter.$name) $dataArray(Parameter.$elemName)
                unset dataArray(Parameter.$elemName)
            }
            .parameterPane.parameters.t set $pos $name
            LookForSearchBox parameter
        }
    }
}

#
# MakeSpreadsheetButtons --
# 
# This procedure creates tool bar with the most needed functions
# for a spreadsheet.
#
# Arguments:
# .columnPane.columns.t - name of the column widget table
#

proc MakeSpreadsheetButtons {} {
    global apsContextHelp
    set t .columnPane.columns.t
    APSFrame .toolBar -parent .columnPane -width 100 -height 5 \
      -packOption " -side top -fill both -expand false" -relief sunken\
      -contextHelp "Toolbar representing most of the spreadsheet functions.\
         \nFor more information about how to use the displayed functions\
         go to \"Info\" button in the menu-bar and look for \"Guidelines\".\
         A scrolled text window will appear with detailed information.\n"

    set ws .columnPane.toolBar.frame
    set bw 5

    ttk::style configure Flat.TButton -relief flat -padding 0 -borderwidth 0 -shiftrelief 0 -focusthickness 0

    ttk::button $ws.copy -text "Copy" -style Flat.TButton -command "tk_tableCopy1 $t"
    ttk::button $ws.paste -text "Paste" -style Flat.TButton -command "tk_tablePaste1 $t"
    ttk::button $ws.cut -text "Cut" -style Flat.TButton -command "tk_tableCut1 $t"
    ttk::button $ws.undo -text "Undo" -style Flat.TButton -command "tk_tableUndo $t"
    set apsContextHelp($ws.copy) "Invokes a function which copies inserts from allselected cells."
    set apsContextHelp($ws.paste) "Invokes a function which pastes the copied inserts into selected cells."
    set apsContextHelp($ws.cut) "Invokes a function which clears all selected cells from any inserts."
    set apsContextHelp($ws.undo) "Invokes a function which rereads the value of the active cell from the spreadsheet, discarding any latest editing that have been performed on the cell during the same cell activation."
    pack $ws.copy $ws.paste $ws.cut $ws.undo -side left

    ttk::button $ws.moveL -text "<-" -style Flat.TButton -command "tkTableMoveCell1 $t  0 -1" -width 5
    ttk::button $ws.moveR -text "->" -style Flat.TButton -command "tkTableMoveCell1 $t  0  1" -width 5
    ttk::button $ws.moveLPage -text "<<-" -style Flat.TButton -command [list $t xview scroll -1 pages] -width 6
    ttk::button $ws.moveRPage -text "->>" -style Flat.TButton -command [list $t xview scroll 1 pages] -width 6
    set apsContextHelp($ws.moveL) "Moves cursor to a previous cell in a row and activates that cell."
    set apsContextHelp($ws.moveR) "Moves cursor to a next cell in a row and activates that cell."
    set apsContextHelp($ws.moveLPage) "Scrolls the table horizontally to a previous page."
    set apsContextHelp($ws.moveRPage) "Scrolls the table horizontally to a next page."
    pack $ws.moveL $ws.moveR $ws.moveLPage $ws.moveRPage -side left

    ttk::button $ws.moveD -text "Down" -style Flat.TButton -command "tkTableMoveCell1 $t  1  0"
    ttk::button $ws.moveU -text "Up" -style Flat.TButton -command "tkTableMoveCell1 $t -1  0"
    ttk::button $ws.moveDPage -text "PgDown" -style Flat.TButton -command [list $t yview scroll 1 pages]
    ttk::button $ws.moveUPage -text "PgUp" -style Flat.TButton -command [list $t yview scroll -1 pages]
    set apsContextHelp($ws.moveD) "Moves cursor to a next cell in a column and activates that cell."
    set apsContextHelp($ws.moveU) "Moves cursor to a previous cell in a column and activates that cell."
    set apsContextHelp($ws.moveDPage) "Scrolls the table vertically to a next page."
    set apsContextHelp($ws.moveUPage) "Scrolls the table vertically to a previous page."
    pack $ws.moveD $ws.moveU $ws.moveDPage $ws.moveUPage -side left

    ttk::button $ws.moveH -text "Home" -style Flat.TButton -command "$t see origin"
    ttk::button $ws.moveE -text "End" -style Flat.TButton -command "$t see end"
    set apsContextHelp($ws.moveH) "Home moves the table to have the origin in view. It does not move an activation of a cell."
    set apsContextHelp($ws.moveE) "End moves the table to have the end cell in view. It does not move an activation of a cell."
    pack $ws.moveH $ws.moveE -side left

}

proc MakeEntryBar {} {
    global cellIndex cellInput apsContextHelp 

    set p .columnPane.entryFrame
    pack [ttk::frame $p -width 100 -height 5 -relief flat]\
      -side top -fill x -expand no

    pack [ttk::entry $p.cellIndex -width 10 -textvariable cellIndex \
            -state disabled] -side left -fill x -expand no
    set apsContextHelp($p.cellIndex) "Displays an index of an active cell in the\
                                       column table."
    ttk::style configure Short.TButton -focusthickness 0
    pack [ttk::button $p.buttonC -text C -command ClearEntry -width 3 -style Short.TButton] -side left -expand no
    set apsContextHelp($p.buttonC) "Invokes Clear procedure for an entry box\
         and an active cell as a synchronized function. The button becomes disabled\
         when an active cell is in a title row or a title column."
    pack [ttk::button $p.buttonE -text E -command EnterEntry -width 3 -style Short.TButton] -side left -expand no
    set apsContextHelp($p.buttonE) "Invokes Entry procedure for an active cell, inserts\
         entry box input into the cell. The button becomes disabled\
         when an active cell is in a title row or a title column."
    pack [ttk::entry $p.cellInput -width 80 -textvariable cellInput]\
      -side left -fill x -expand yes
    set apsContextHelp($p.cellInput) "Entry box provided for editing of a input for\
         an active cell. When entry is processed in the cell the entry box is updated\
         simultaneously. The box becomes disabled when an active cell is in a title row\
         or a title column."
    bind $p.cellInput <Return> {EnterEntry}
    bind $p.cellInput <KeyRelease> {
        if [winfo exists .editLargeString] {
            set t .editLargeString.userFrame.largeText.text
            $t delete 1.0 end
            $t insert end $cellInput
        }    
    }
}

proc ClearEntry {} {
    global cellInput

    SetUndoArrayForEntryBox
    set cellInput ""
    update
    .columnPane.columns.t set active $cellInput

    if [winfo exists .editLargeString] {
        set t .editLargeString.userFrame.largeText.text
        $t delete 1.0 end
    }    
}

proc EnterEntry {} {
    global cellInput

    SetUndoArrayForEntryBox
    .columnPane.columns.t set active $cellInput
}

proc SetUndoArrayForEntryBox {} {
    global cellIndex tkPriv undoArray

    UnsetUndoArray
    set tkPriv(tableIndex) $cellIndex
    set undoArray($cellIndex) [.columnPane.columns.t get $cellIndex]
}

# 
# ChangeMousePointer --
# 
# This procedure changes a display of the mouse cursor
#
# Arguments:
# c - type string - local variable - a name of the widget
# m - type string - local variable - a name of the cursor
#

proc ChangeMousePointer {c m} {
    set mouse $m

    $c config -cursor $mouse
}

#
# MakeSpreadsheet --
# This procedure builds parameter and column tables and
# calls for binding for those tables.
#
# Arguments:
# table1, table2, validData, parametersArray, columnsArray - global variable
#

proc MakeSpreadsheet {} {
    global table1 table2 validData currentPage

    ttk::panedwindow .userFrame.pane -orient vertical
    pack .userFrame.pane -fill both -expand true
    ttk::frame .parameterPane
    ttk::frame .columnPane
    .userFrame.pane add .parameterPane
    .userFrame.pane add .columnPane

    pack [ttk::frame .parameterPane.parTop] -fill both -expand false
    pack [ttk::label .parameterPane.parTop.pageLabel -text "       Page:" -font TkHeadingFont]\
      -side left
    tk_optionMenu .parameterPane.parTop.page currentPage $currentPage
    pack .parameterPane.parTop.page -side left
    pack [ttk::label .parameterPane.parTop.parLabel -text "Parameters" -font TkHeadingFont -anchor center]\
      -fill both -expand true -side left
    ttk::style configure Header.TRadiobutton -font TkHeadingFont
    pack [ttk::radiobutton .parameterPane.parTop.binaryMode \
            -text Binary \
            -variable dataArray(Layout.DataMode.Mode) \
            -value binary -style Header.TRadiobutton] -side right
    pack [ttk::radiobutton .parameterPane.parTop.asciiMode \
            -text Ascii \
            -variable dataArray(Layout.DataMode.Mode) \
            -value ascii -style Header.TRadiobutton] -side right -padx 5
    
    pack [ttk::frame .parameterPane.parameters -borderwidth 2 -relief ridge] -fill both -expand true
    pack [ttk::label .columnPane.colLabel -text "Columns" -font TkHeadingFont -anchor center]\
      -fill both -expand false

    MakeSpreadsheetButtons
    MakeEntryBar

    pack [ttk::frame .columnPane.columns -borderwidth 2 -relief ridge] -fill both -expand true

    array set table1 {
        rows	5
        cols	2
        table	.parameterPane.parameters.t
        array	parametersArray
    }
    array set table2 {
        rows	20
        cols	8
        table	.columnPane.columns.t
        array	columnsArray
    }
    global $table1(array) $table2(array) apsContextHelp

    table $table1(table) -rows $table1(rows) -cols $table1(cols) \
      -variable $table1(array) \
      -width 6 -height 1 \
      -titlerows 0 -titlecols 1 \
      -roworigin 0 -colorigin 0 \
      -yscrollcommand {.parameterPane.parameters.sy set} \
      -xscrollcommand {.parameterPane.parameters.sx set} \
      -rowtagcommand rowProc -coltagcommand colProc \
      -colstretchmode last -rowstretchmode none \
      -selectmode extended -anchor w \
      -resizeborders col -drawmode slow -font TkFixedFont

    $table1(table) tag configure active -foreground black -background white

    set apsContextHelp(.parameterPane.parameters.t) "Parameters Table -\
         \na set of parameters with its names and values. \n\n- To change value of\
         a parameter just go directly to its value's cell in the table. \n\n- To insert or delete\
         a parameter go to the button \"Edit\" in the menu. \n\n- To access information\
         about  attributes of a selected parameter go to the button \"Info\".\n"

    ttk::scrollbar .parameterPane.parameters.sy -command [list $table1(table) yview]
    ttk::scrollbar .parameterPane.parameters.sx -command [list $table1(table) xview] -orient horizontal

    grid $table1(table) .parameterPane.parameters.sy -sticky news
    grid .parameterPane.parameters.sx -sticky news
    grid columnconfig .parameterPane.parameters 0 -weight 1
    grid rowconfig .parameterPane.parameters 0 -weight 100
    grid rowconfig .parameterPane.parameters 1 -weight 1


    table $table2(table) -rows $table2(rows) -cols $table2(cols) \
      -variable $table2(array) \
      -width 6 -height 6 \
      -titlerows 1 -titlecols 1 \
      -roworigin 0 -colorigin 0 \
      -yscrollcommand {.columnPane.columns.sy set} \
      -xscrollcommand {.columnPane.columns.sx set} \
      -rowtagcommand rowProc -coltagcommand colProc \
      -colstretchmode none -rowstretchmode none \
      -selectmode extended -anchor w \
      -resizeborders col -drawmode slow -font TkFixedFont
    
    $table2(table) tag configure active -foreground black -background white

    set apsContextHelp(.columnPane.columns.t) "Columns Table - Spreadsheet\
         \na set of columns with all data setup in rows in order. \n\n- To change value of\
         a column in a specific row just go directly to a corresponding cell in the table. \
         \n\n- To insert or delete a column go to the button \"Edit\" in the menu. \
         \n\n- To access information about  attributes of a selected column go to\
         the button \"Info\". \n\n- For more information about how to use the spreadsheet\
         go to \"Info\" button in the menu-bar and look for \"Guidelines\". A scrolled text\
	 window will appear with detailed information.\n"


    ttk::scrollbar .columnPane.columns.sy -command [list $table2(table) yview]
    ttk::scrollbar .columnPane.columns.sx -command [list $table2(table) xview] -orient horizontal
    
   
    grid $table2(table) .columnPane.columns.sy -sticky news
    grid .columnPane.columns.sx -sticky news
    grid columnconfig .columnPane.columns 0 -weight 1
    grid rowconfig .columnPane.columns 0 -weight 100
    grid rowconfig .columnPane.columns 1 -weight 1


    SpreadsheetBind
    if {[llength [info command tkCancelRepeat]]} {
        tkCancelRepeat
    } else {
        tk::CancelRepeat
    }
    set $table2(array)(0,0) "Rows\\Col's"
    set validData 1
}

#
# SpreadsheetBind --
#
# This procedure invokes binding for column table.
#
# Arguments:
# table1 - type array - global variable
# table2 - type array - global variable
#

proc SpreadsheetBind {} {
    global table1 table2 

    bind all <Tab> {}
    bind all <Shift-Tab> {}
    bind $table2(table) <Tab> {tkTableMoveCell1 %W  0  1}
    bind $table2(table) <Shift-Tab> {tkTableMoveCell1 %W  0 -1}

    bind Table <Control-Up>		{ }
    bind Table <Control-Down>		{ }
    bind $table2(table) <Control-equal>	{tk::table::ChangeWidth %W active  1; break}
    bind $table2(table) <Control-minus>	{tk::table::ChangeWidth %W active -1; break}

    bind Table <ButtonRelease-2> {
        if {!$tkPriv(mouseMoved)} {tk_tablePaste2 %W [%W index @%x,%y]}
        break
    }
    bind $table1(table) <Control-Left>	{
        set parEditCell [%W index active]
        set disabledPar [%W tag includes disabled $parEditCell]
        if !$disabledPar {
            set parCol [lindex [split $parEditCell ,] 1]
            if $parCol>0 {
                %W icursor [expr {[%W icursor]-1}]
            }
        }
        break
    }
    bind $table1(table) <Control-Right> {
        set parEditCell [%W index active]
        set disabledPar [%W tag includes disabled $parEditCell]
        if !$disabledPar {
            set parCol [lindex [split $parEditCell ,] 1]
            if $parCol>0 {
                %W icursor [expr {[%W icursor]+1}]
            }
        }
        break
    }

    bind $table2(table) <Control-Left>	{
        global disabledIndex
        set editCell [%W index active]
        if !$disabledIndex {
            set rowC [lindex [split $editCell ,] 0]
            set colC [lindex [split $editCell ,] 1]
            if {$rowC > 0 && $colC > 0} {
                %W icursor [expr [%W icursor] - 1]
            }
        }
        break
    }
    bind $table2(table) <Control-Right> {
        global disabledIndex
        set editCell [%W index active]
        if !$disabledIndex {
            set rowC [lindex [split $editCell ,] 0]
            set colC [lindex [split $editCell ,] 1]
            if {$rowC > 0 && $colC > 0} {
                %W icursor [expr [%W icursor] + 1]
            }
        }
        break
    }
    bind $table2(table) <Up>			{tkTableMoveCell1 %W -1  0; break}
    bind $table2(table) <Down>		        {tkTableMoveCell1 %W  1  0; break}
    bind $table2(table) <Left>		        {tkTableMoveCell1 %W  0 -1; break}
    bind $table2(table) <Right>		{tkTableMoveCell1 %W  0  1; break}
    bind $table2(table) <Return> {tk::table::MoveCell %W 1 0; updateInputBox; break}
    bind $table2(table) <ButtonRelease-2> {    
        if {!$tkPriv(mouseMoved)} { tk_tablePaste1 %W }
        break
    }     
    bind $table2(table) <Delete> {
        global cellIndex

        DoChangeInActive

        %W delete active insert
        .columnPane.columns.t activate $cellIndex
        .columnPane.columns.t see active
        updateInputBox
        break
    }
    bind $table2(table) <Control-Delete> {
        global cellIndex
        DoChangeInActive
        %W delete active insert end
        .columnPane.columns.t activate $cellIndex
        .columnPane.columns.t see active
        updateInputBox
        break
    }
    bind $table2(table) <Shift-Up>		{tkTableExtendSelect1 %W -1  0; break}
    bind $table2(table) <Shift-Down>		{tkTableExtendSelect1 %W  1  0; break}
    bind $table2(table) <Shift-Left>		{tkTableExtendSelect1 %W  0 -1; break}
    bind $table2(table) <Shift-Right>	        {tkTableExtendSelect1 %W  0  1; break}

    bind Table <<Copy>> {break}
    bind Table <<Cut>>	{break}
    bind Table <<Paste>> {break}
    bind $table2(table) <<Copy>> {tk_tableCopy1 %W; break}
    bind $table2(table) <<Cut>>	{tk_tableCut1 %W; break}
    bind $table2(table) <<Paste>>	{tk_tablePaste1 %W; break}
    bind $table1(table) <<Copy>> {tk_tableCopy2 %W; break}

    ##   binding with <<Undo>> does not work
    ##   button "Undo" reacts more as a key item not as a function button
    bind $table2(table) <KeyPress> {
        global cellIndex

        ## %K returns F14 for "Undo" button
        if {[string compare %K F14]==0} {
            tk_tableUndo $table2(table)
            return
        }
        ## To protect against inserting an empty string by "Tab" key
        if {[string compare %K Tab]==0} {
            return
        }

        DoChangeInActive

        tk::table::Insert %W %A
        .columnPane.columns.t activate $cellIndex
        .columnPane.columns.t see active
        updateInputBox
        break
    }
    bind $table2(table) <Control-Home> {
        ControlHomeEnd %W origin
        break
    }
    bind $table2(table) <Control-End> {
        ControlHomeEnd %W end
        break
    }
    bind $table2(table) <BackSpace> {
        global cellIndex

        DoChangeInActive

        set tkPriv(junk) [%W icursor]
        if {[string compare {} $tkPriv(junk)] && $tkPriv(junk)} {
            %W delete active [expr {$tkPriv(junk)-1}]
        }
        .columnPane.columns.t activate $cellIndex
        .columnPane.columns.t see active
        updateInputBox
        break
    }
    bind $table2(table) <Control-1>	{tkTableBeginToggle1 %W [%W index @%x,%y]; break}
    bind $table2(table) <Control-B1-Motion> {%W selection clear all; break}
    bind $table2(table) <B1-Motion> {
        global currentCell tkPriv
        # If we already had motion, or we moved more than 1 pixel,
        # then we start the Motion routine

        if {$tkPriv(mouseMoved) || abs(%x-$tkPriv(x))>1 || abs(%y-$tkPriv(y))>1} {
            set tkPriv(mouseMoved) 1
        }
        set actionCell [%W index @%x,%y]
        if {$tkPriv(mouseMoved)} {
            if [string compare $actionCell $currentCell]!=0 {
                set currentCell $actionCell
                tkTableMotion1 %W $currentCell
            }
        }
        if {[llength [info command tkCancelRepeat]]} {
            tkCancelRepeat
        } else {
            tk::CancelRepeat
        }
        break
    }

    ## bind $table2(table) <1>
    doBindingForButtonN1     

    bind $table2(table) <ButtonRelease-1> {
        global tkPriv
        if {[winfo exists %W]} {
            if {[llength [info command tkCancelRepeat]]} {
                tkCancelRepeat
            } else {
                tk::CancelRepeat
            }
            %W activate @%x,%y
            updateInputBox
        }
        break
    }
    bind $table2(table) <Prior> {
        %W yview scroll -1 pages
        break
    }
    bind $table2(table) <Next> {
        %W yview scroll  1 pages
        break
    }
    ## Key events

    if {[string comp {} [info command event]]} {
        tkTableClipboardKeysyms1 <Copy> <Cut> <Paste>
    } else {
        tkTableClipboardKeysyms1 Control-c Control-x Control-v
    }
    #support the mouse wheel
    bind $table2(table) <Button-4> { $table2(table) yview scroll -5 units }
    bind $table2(table) <Button-5> { $table2(table) yview scroll +5 units }
    
    bind $table2(table) <MouseWheel> {
      if { %D < 0} {
          $table2(table) yview scroll +5 units
         } else {
          $table2(table) yview scroll -5 units
         }
    }

    bind $table1(table) <Button-4> { $table1(table) yview scroll -5 units }
    bind $table1(table) <Button-5> { $table1(table) yview scroll +5 units }
    
    bind $table1(table) <MouseWheel> {
      if { %D < 0} {
          $table1(table) yview scroll +5 units
         } else {
          $table1(table) yview scroll -5 units
         }
    }
} 

#
# ControlHomeEnd --
# 
# This procedure invokes selection of "Home" or "End" with
# an implementation of all necessary for this spreadsheet procedures.
#
# Arguments:
# t - a table widget
# corner - home or end 

proc ControlHomeEnd {t corner} {
    global tkPriv undoArray
    UnsetUndoArray
    $t selection clear all
    $t activate $corner
    $t selection set active
    $t see active
    updateInputBox
    set tkPriv(tableIndex) [$t index active]
    set undoArray($tkPriv(tableIndex)) [$t get active]
}

#
# DoChangeInActive --
#
# This procedure is invoked any time an active cell is moved.
# It reads an actual value of the cell during its activation
# mostly for "Undo" function purposes.
#

proc DoChangeInActive {} {
    global cellIndex changeInActive undoArray tkPriv
    
    if !$changeInActive {
        if [info exists undoArray] {
            foreach u [array names undoArray] {
                unset undoArray($u)
            }
            unset undoArray
        }
        set undoArray($cellIndex) [.columnPane.columns.t get $cellIndex]
        set changeInActive 1
    }     
}

set tkPriv(mouseMoved) 0
proc doBindingForButtonN1 {} {
    global table2 tkPriv
    bind $table2(table) <1> {
        set tkPriv(tableIndex) ""
        array set tkPriv {x %x y %y}
        set tkPriv(mouseMoved) 0
        
        if {[winfo exists %W]} {
            tkTableBeginSelect1 %W [%W index @%x,%y]
            focus %W
        }
        break
    }
}

# tkTableClipboardKeysyms1 --
# This procedure is invoked to identify the keys that correspond to
# the "copy", "cut", and "paste" functions for the clipboard.
#
# Arguments:
# copy -	Name of the key (keysym name plus modifiers, if any,
#		such as "Meta-y") used for the copy operation.
# cut -		Name of the key used for the cut operation.
# paste -	Name of the key used for the paste operation.

proc tkTableClipboardKeysyms1 {copy cut paste} {
    global table1 table2 ScrolledStatus
    bind Table <$copy>	{break}
    bind Table <$cut>	{break}
    bind Table <$paste>	{break}
    bind $table2(table) <$copy>	{tk_tableCopy1 %W; break}
    bind $table2(table) <$cut>	{tk_tableCut1 %W; break}
    bind $table2(table) <$paste>	{tk_tablePaste1 %W; break}
    bind $table1(table) <$copy>	{tk_tableCopy2 %W; break}
    bind $table1(table) <$cut>	{
        set selectList [%W cursel]
        catch {
            set disabledFlag 0
            foreach selEl $selectList {
                set disabledPar [%W tag includes disabled $selEl]
                if !$disabledPar {
                    set actCol [lindex [split $selEl ,] 1]
                    if $actCol {
                        %W set $selEl {}
                    }
                } else {
                    set disabledFlag 1
                }
            }
            if $disabledFlag {
                bell
                set ScrolledStatus "\nSelection contained protected parameters.\
                            \nThose ones are not changed."
                update
            }
            %W selection clear all
        }
        break
    }
    bind $table1(table) <$paste> {tk_tablePaste2 %W; break}
}

# tk_tablePaste2 --
# This procedure pastes the contents of the clipboard to the specified
# cell (active by default) in a parameters table widget only. 
#
# Arguments:
# w -		Name of a table widget.
# cell -	Cell to start pasting in.

proc tk_tablePaste2 {w {cell {}}} {
    if {[string compare {} $cell]} {
        if {[catch {selection get -displayof $w} data]} return
    } else {
        if {[catch {selection get -displayof $w -selection CLIPBOARD} data]} {
            return
        }
        set cell active
    }
    tk_tablePasteHandler2 $w [$w index $cell] $data
    if {[$w cget -state] == "normal"} {focus $w}
}

# tk_tablePasteHandler2 --
# This procedure handles how data is pasted into the parameters table widget only.
# This handles data in the default table selection form.
# This allows pasting into all cells, except those with -state disabled.
#
# Arguments:
# w -		Name of a table widget.
# cell -	Cell to start pasting in.

proc tk_tablePasteHandler2 {w cell data} {
    global ScrolledStatus
    set rows	[expr {[$w cget -rows]-[$w cget -roworigin]}]
    set cols	[expr {[$w cget -cols]-[$w cget -colorigin]}]
    set r	[$w index $cell row]
    set c	[$w index $cell col]
    set rsep	[$w cget -rowseparator]
    set csep	[$w cget -colseparator]
    ## Assume separate rows are split by row separator if specified
    ## If you were to want multi-character row separators, you would need:
    # regsub -all $rsep $data <newline> data
    # set data [join $data <newline>]
    if {[string comp {} $rsep]} { set data [split $data $rsep] }
    set row $r
    set disabledFlag 0
    foreach line $data {
        if {$row > $rows} break
        set col	$c
        ## Assume separate cols are split by col separator if specified
        ## Unless a -separator was specified
        if {[string comp {} $csep]} { set line [split $line $csep] }
        ## If you were to want multi-character col separators, you would need:
        # regsub -all $csep $line <newline> line
        # set line [join $line <newline>]
        foreach item $line {
            if {$col > $cols} break
            if $col {
                set disabledPar [$w tag includes disabled $row,$col]
                if !$disabledPar {
                    $w set $row,$col $item
                } else {
                    set disabledFlag 1
                }
            }
            incr col
        }
        incr row
    }
    if $disabledFlag {
        bell
        set ScrolledStatus "\nSelection contained protected parameters.\
                   \nThose ones are not changed."
        update
    }
}

proc tk_tablePasteHandler3 {w cell data} {
    #
    # Don't allow pasting into the title cells
    #
    #if {[$w tag includes title $cell]} {
    #    return
    #}
    set rows	[expr {[$w cget -rows]-[$w cget -roworigin]}]
    set cols	[expr {[$w cget -cols]-[$w cget -colorigin]}]
    set r	[$w index $cell row]
    set c	[$w index $cell col]
    set rsep	[$w cget -rowseparator]
    set csep	[$w cget -colseparator]
    ## Assume separate rows are split by row separator if specified
    ## If you were to want multi-character row separators, you would need:
    # regsub -all $rsep $data <newline> data
    # set data [join $data <newline>]
    if {[string compare {} $rsep]} { set data [split $data $rsep] }
    set row	$r
    foreach line $data {
        if {$row > $rows} break
        set col	$c
        ## Assume separate cols are split by col separator if specified
        ## Unless a -separator was specified
        if {[string compare {} $csep]} { set line [split $line $csep] }
        ## If you were to want multi-character col separators, you would need:
        # regsub -all $csep $line <newline> line
        # set line [join $line <newline>]
        foreach item $line {
            if {$col > $cols} break
            $w set $row,$col $item
            incr col
        }
        incr row
    }
}

proc tk_tableCopy2 w {
    global tkPriv 
    set tkPriv(tableIndex) [$w cursel]

    if [llength $tkPriv(tableIndex)] {
        clipboard clear -displayof $w
        clipboard append -displayof $w [selection get -displayof $w]
    }

}

# tk_tableCopy1 --
# This procedure invokes CollectSelectedValuesForCopy sub-procedure
# which copies the selection from a column table widget into the
# copyArray data structure for possible usage in Paste procedure. 
#
# Arguments:
# w -		Name of a table widget.

proc tk_tableCopy1 w {
    global tkPriv 
    set tkPriv(tableIndex) [$w cursel]

    if [llength $tkPriv(tableIndex)] {
        clipboard clear -displayof $w
        clipboard append -displayof $w [selection get -displayof $w]
        if [catch {CollectSelectedValuesForCopy $w} result] {
            APSSetVarAndUpdate ScrolledStatus "tk_tableCopy1 $result"
        }
    }

}

proc CollectSelectedValuesForCopy {w} {
    global copyArray tkPriv copyBase
    set t .columnPane.columns.t
    if [info exists copyArray] {
        foreach u [array names copyArray] {
            unset copyArray($u)
        }
        unset copyArray
    }
    set copyBase [lindex $tkPriv(tableIndex) 0]
    foreach cell $tkPriv(tableIndex) {
        set fillout [$t get $cell]
        if [string length $fillout] {
            set copyArray($cell) $fillout
        } else {
            set copyArray($cell) {}
        }
    }
    APSSetVarAndUpdate ScrolledStatus "Selected cells are copied."
}

# tk_tableCut1 --
# This procedure copies the selection from a column table widget into the
# undoArray data structure, then deletes the selection (if it exists in the given
# widget).
#
# Arguments:
# w -		Name of a table widget.

proc tk_tableCut1 w {
    global tkPriv
    set tkPriv(tableIndex) [$w cursel]

    if [llength $tkPriv(tableIndex)] {
        catch {
            if [$w tag exists disabled] {
                if [catch {CollectSelectedValuesForUndo_2 $w} result] {
                    APSSetVarAndUpdate ScrolledStatus "tk_tableCut1 $result"
                }
            } else {
                if [catch {CollectSelectedValuesForUndo_1 $w} result] {
                    APSSetVarAndUpdate ScrolledStatus "tk_tableCut1 $result"
                }
                $w cursel {}
            }
            $w selection clear all
            updateInputBox
        } result
        APSSetVarAndUpdate ScrolledStatus "$result"
    }
}

proc CollectSelectedValuesForUndo_1 {w} {
    global undoArray tkPriv

    UnsetUndoArray

    foreach cell $tkPriv(tableIndex) {
        set fillout [$w get $cell]
        if [string length $fillout] {
            set undoArray($cell) $fillout
        } else {
            set undoArray($cell) {}
        }
    }
}

proc CollectSelectedValuesForUndo_2 {w} {
    global undoArray tkPriv ScrolledStatus

    UnsetUndoArray
    set disabledFlag 0
    foreach cell $tkPriv(tableIndex) {
        if ![$w tag includes disabled $cell] {
            set fillout [$w get $cell]
            if [string length $fillout] {
                set undoArray($cell) $fillout
            } else {
                set undoArray($cell) {}
            }
            $w set $cell {}
        } else {
            set disabledFlag 1
        }
    }
    if $disabledFlag {
        bell
        set ScrolledStatus "\nSelection contained protected cells.\
                    \nThose ones are not changed."
        update
    }
}

# tkTableExtendSelect1 --
#
# Does nothing unless we're in extended selection mode; in this
# case it moves the location cursor (active element) by the specified
# number of cells, and extends the selection to that point.
#
# Arguments:
# w - The table widget.
# x - +1 to move down one cell, -1 to move up one cell.
# y - +1 to move right one cell, -1 to move left one cell.

proc tkTableExtendSelect1 {w x y} {
    if {[string compare extended [$w cget -selectmode]] ||
        [catch {$w index active row} r]} return
    set c [$w index active col]
    $w activate [incr r $x],[incr c $y]
    $w see active
    updateInputBox

    tkTableMotion1 $w [$w index active]
}

# tk_tablePaste1 --
# This procedure pastes the contents of the copyArray to the active
# cell in a table widget. If the copyArray contains more than one 
# input the active cell is the base for the transfer. 
#
# Arguments:
# w -	      Name of a table widget.
# copyArray - global array - contains all values assign to indexes
#             of copied cells.
# copyBase - an index of the closest to the origin cell.

proc tk_tablePaste1 {w} {
    global copyArray tkPriv copyBase numRows 
    global undoArray ScrolledStatus
    global dataArray
    set numCols [llength $dataArray(ColumnNames)]
    $w selection clear all

    if [llength $tkPriv(tableIndex)] {
        set tkPriv(tableIndex) ""
    }

    UnsetUndoArray
    set copyArraySize [array size copyArray]

    set cAct [$w index active col]
    set rAct [$w index active row]
    set elAct [$w index active]
    set disabledFlag 0
    if {!$rAct && $copyArraySize==1} {
        if [$w tag includes disabled active] {
            APSSetVarAndUpdate ScrolledStatus "\nThe column \"[$w get active]\" is disabled."
            bell
            return
        }
        set singleInput $copyArray([array names copyArray])
        for {set actColRow 1} {$actColRow <= $numRows} {incr actColRow} {
            set actColCell $actColRow,$cAct
            lappend tkPriv(tableIndex) $actColCell
            set fillout [$w get $actColCell]
            if [string length $fillout] {
                set undoArray($actColCell) $fillout
            } else {
                set undoArray($actColCell) {}
            }
            $w set $actColCell $singleInput
        }
    } else {
        foreach e [list cAct rAct] {
            if ![set $e] {set $e 1}
        }
        set cBase [$w index $copyBase col]
        set rBase [$w index $copyBase row]
        set cTransf [expr $cAct - $cBase]
        set rTransf [expr $rAct - $rBase]
        
        if $copyArraySize {
            foreach name [array names copyArray] {
                set cNam [$w index $name col]
                set rNam [$w index $name row]
                set cNameCalc [expr $cNam + $cTransf]
                set rNameCalc [expr $rNam + $rTransf]
                set nameCalc $rNameCalc,$cNameCalc

                if ![$w tag includes disabled $nameCalc] {
                    if {$rNameCalc <= $numRows && $cNameCalc <= $numCols} {
                        lappend tkPriv(tableIndex) $nameCalc
                        set fillout [$w get $nameCalc]
                        if [string length $fillout] {
                            set undoArray($nameCalc) $fillout
                        } else {
                            set undoArray($nameCalc) {}
                        }
                        $w set $nameCalc $copyArray($name)
                        $w selection set $nameCalc
                        $w selection anchor $nameCalc
                    }
                } else {
                    set disabledFlag 1
                }
            }
            if [llength $tkPriv(tableIndex)]>1 {
                set tkPriv(tableIndex) [lsort -increasing $tkPriv(tableIndex)]
            }
        }
    }
    updateInputBox
    if $disabledFlag {
        bell
        set ScrolledStatus "\nSelection contained protected cells.\
                    \nThose ones are not changed."
        update
    }
}

# tkTableMotion1 --
#
# This procedure is called to process mouse motion events while
# button 1 is down. It may move or extend the selection, depending
# on the table's selection mode.
#
# Arguments:
# w	- The table widget.
# el	- The element under the pointer (must be in row,col form).

proc tkTableMotion1 {w el} {
    global tkPriv 
    if {![info exists tkPriv(tablePrev)]} {
        set tkPriv(tablePrev) $el
        return
    }

    if {[string match $tkPriv(tablePrev) $el]} return
    scan $tkPriv(tablePrev) %d,%d r c
    scan $el %d,%d elr elc
    if {[$w tag includes title $el]} {
        if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {
            ## We're in a column header
            if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {
                ## We're in the topleft title area
                $w selection clear anchor end
            } else {
                $w selection clear anchor [$w index end row],$c
            }
            $w selection set anchor [$w index end row],$elc
        } else {
            ## We're in a row header
            $w selection clear anchor $r,[$w index end col]
            $w selection set anchor $elr,[$w index end col]
        }
    } else {
        $w selection clear anchor $tkPriv(tablePrev)
        $w selection set anchor $el
    }
    set tkPriv(tablePrev) $el
    $w activate $el
    updateInputBox
}

# tkTableBeginToggle1 --
#
# This procedure is typically invoked on control-button-1 presses. It
# begins the process of toggling a selection in the table. Its
# exact behavior depends on the selection mode currently in effect
# for the table; see the Motif documentation for details.
#
# Arguments:
# w - The table widget.
# el - The element for the selection operation (typically the
# one under the pointer). Must be in numerical form.

proc tkTableBeginToggle1 {w el} {
    global tkPriv table2
    bind $table2(table) <1> {break}
    if {[string match extended [$w cget -selectmode]]} {
        set tkPriv(tablePrev) $el
        $w selection anchor $el
        if {[$w tag includes title $el]} {
            scan $el %d,%d r c
            if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {
                ## We're in a column header
                if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {
                    ## We're in the topleft title area
                    APSSetVarAndUpdate ScrolledStatus "Selecting all cells..."
                    set end end
                } else {
                    APSSetVarAndUpdate ScrolledStatus "Selecting column [$w get $el]..."
                    set end [$w index end row],$c
                    for {set i 1} {$i <= [$w index end row]} {incr i} {
                        set cell $i,$c
                        if {[lsearch -exact $tkPriv(tableIndex) $cell]<0} {
                            lappend tkPriv(tableIndex) $cell
                        }
                    }
                }
            } else {
                ## We're in a row header
                APSSetVarAndUpdate ScrolledStatus "Selecting row [$w get $el]..."
                set end $r,[$w index end col]
                for {set j 1} {$j <= [$w index end col]} {incr j} {
                    set cell $r,$j
                    if {[lsearch -exact $tkPriv(tableIndex) $cell]<0} {
                        lappend tkPriv(tableIndex) $cell
                    }
                }
            }
            APSSetVarAndUpdate ScrolledStatus "Selection done."
        } else {
            ## We're in a non-title cell
            set end $el
            if {[lsearch -exact $tkPriv(tableIndex) $el]<0} {
                lappend tkPriv(tableIndex) $el
            } else {
                set ind [lsearch $tkPriv(tableIndex) $el]
                set tkPriv(tableIndex) [lreplace $tkPriv(tableIndex) $ind $ind]
            }
        }
        if {[$w selection includes  $end]} {
            $w selection clear $el $end
        } else {
            $w selection set   $el $end
        }
    }
    doBindingForButtonN1
}

# tkTableBeginSelect1 --
#
# This procedure is typically invoked on button-1 presses. It begins
# the process of making a selection in the table. Its exact behavior
# depends on the selection mode currently in effect for the table;
# see the Motif documentation for details. Also, it introduces 
# an undoArray.
#
# Arguments:
# w	- The table widget.
# el	- The element for the selection operation (typically the
#	one under the pointer).  Must be in row,col form.

proc tkTableBeginSelect1 {w el} {
    global tkPriv columnArray changeInActive
    set changeInActive 0
    if {[scan $el %d,%d r c] != 2} return
    if {[string compare [$w cget -selectmode] extended]==0} {
        $w selection clear all
        $w selection anchor $el
        $w activate $el
        $w see active
        if {[$w tag includes title $el]} {
            if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {
                ## We're in a column header
                if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {
                    $w selection set origin end
                    set tkPriv(tableIndex) ""			
                } else {
                    $w selection set $el [$w index end row],$c
                    set tkPriv(tableIndex) ""			
                }
            } else {
                ## We're in a row header
                $w selection set $el $r,[$w index end col]
                set tkPriv(tableIndex) ""			
            }
        } else {
            lappend tkPriv(tableIndex) $el
            $w selection set $el
        }
        set tkPriv(tablePrev) $el
    }
}

#
# tk_tableUndo --
#
# This procedure rereads all indices in selected cells
#
# Arguments:
# t - The table widget.
#

proc tk_tableUndo {t} {
    global undoArray numRows tkPriv changeInActive
    set dataList ""
    set undoRow $numRows
    if ![array size undoArray] {
        return
    }
    foreach undoCell [array names undoArray] {
        if [info exists undoArray($undoCell)] {
            $t set $undoCell $undoArray($undoCell)
        }
    }
    unset tkPriv(tableIndex)
    UnsetUndoArray

    $t selection clear all
    $t selection set active
    $t see active
    set i [$t index active]
    set tkPriv(tableIndex) $i
    updateInputBox
    set changeInActive 0
}

proc UnsetUndoArray {} {
    global undoArray
    if [info exists undoArray] {
        foreach u [array names undoArray] {
            unset undoArray($u)
        }
        unset undoArray
    }
} 

# tkTableMoveCell1 --
#
# Moves the location cursor (active element) by the specified number
# of cells and changes the selection if we're in browse or extended
# selection mode.  If the new cell is "hidden", we skip to the next
# visible cell if possible, otherwise just abort.
#
# Arguments:
# w - The table widget.
# x - +1 to move down one cell, -1 to move up one cell.
# y - +1 to move right one cell, -1 to move left one cell.

proc tkTableMoveCell1 {w x y} {
    global tkPriv changeInActive
    set changeInActive 0

    if {[catch {$w index active row} r]} return
    set c [$w index active col]
    set cell [$w index [incr r $x],[incr c $y]]
    while {[string compare [set true [$w hidden $cell]] {}]} {
        # The cell is in some way hidden
        if {[string compare $true [$w index active]]} {
            # The span cell wasn't the previous cell, so go to that
            set cell $true
            break
        }
        if {$x > 0} {incr r} elseif {$x < 0} {incr r -1}
        if {$y > 0} {incr c} elseif {$y < 0} {incr c -1}
        if {[string compare $cell [$w index $r,$c]]} {
            set cell [$w index $r,$c]
        } else {
            # We couldn't find a non-hidden cell, just don't move
            return
        }
    }
    $w activate $cell
    $w see active
    switch [$w cget -selectmode] {
        browse {
            $w selection clear all
            $w selection set active
        }
        extended {
            $w selection clear all
            $w selection set active
            $w selection anchor active
            set tkPriv(tablePrev) [$w index active]
        }
    }

    unset tkPriv(tableIndex)
    set i [$w index active]
    set tkPriv(tableIndex) $i
    updateInputBox
}

proc updateInputBox {} {
    global cellIndex cellInput disabledIndex tkPriv
    set w .columnPane.columns.t
    set cellIndex [$w index active]
    set disabledIndex [$w tag includes disabled $cellIndex]
    scan $cellIndex %d,%d r c
    if {!$r || !$c || $disabledIndex} {
        .columnPane.entryFrame.cellInput configure -state disabled
        .columnPane.entryFrame.buttonC configure -state disabled
        .columnPane.entryFrame.buttonE configure -state disabled
        if [winfo exists .editLargeString] {
            .editLargeString.userFrame.largeText.text configure -state disabled
        } 
    } else {
        .columnPane.entryFrame.cellInput configure -state normal
        .columnPane.entryFrame.buttonC configure -state normal
        .columnPane.entryFrame.buttonE configure -state normal
        if [winfo exists .editLargeString] {
            .editLargeString.userFrame.largeText.text configure -state normal
        } 
    }
    set cellInput [$w get $cellIndex]
    set tkPriv(tableIndex) $cellIndex
    focus $w
    if [winfo exists .editLargeString] {
        set t .editLargeString.userFrame.largeText.text
        $t delete 1.0 end
        $t insert end $cellInput
    }    
    update
}

#
# DeleteUserChoice --
#
# This procedure recognizes call from a user to delete entity
# transfered in the call, confirms the call for deleting and 
# invokes a procedure adequate to the passed entity.
#
# Attributes:
# editVar - type string - a name of the entity to be deleted.
#

proc DeleteUserChoice {editVar} {
    if [string compare $editVar row]==0 {
        set editorVar "selected row(s)"
    } else {
        set editorVar $editVar
    }
    catch {APSMultipleChoice [APSUniqueName .] -name Confirmation \
             -question "Do you really want to delete a $editorVar?" \
             -labelList {Yes No} -returnList {Yes No}} userChoice
    switch $userChoice {
        Yes {
        }
        No {
            return
        }
    }

    set table .columnPane.columns.t
    set colI [lindex [split [$table index active] ,] 1]
    switch $editVar {
        parameter {DeleteParameter}
        column {DeleteColumn $table $colI}
        row {DeleteRow $table}
        page {DeletePage}
        default {
            APSSetVarAndUpdate ScrolledStatus "Unrecognized entity $editVar."
            return
        }
    }
}

proc DeletePage {} {
    global validData dataArray currentPage currentPageIndex numPages pageRowCount
    global previousCurrentPage
    if !$validData {
        APSSetVarAndUpdate ScrolledStatus "No data."
        return
    }
    if {[llength $dataArray(ArrayNames)]} {
        APSSetVarAndUpdate ScrolledStatus "Currently unable to delete pages with sdds files containing arrays"
        return
    }
    if {$numPages == 1} {
        APSSetVarAndUpdate ScrolledStatus "This is the only page. Deleting canceled."
        return
    }
    APSSetVarAndUpdate ScrolledStatus "Deleting page $currentPage"
    if {$currentPage == $numPages} {
        incr currentPage -1
        ChangePage
        set skip 1
    } else {
        incr currentPage 1
        ChangePage
        incr currentPage -1
        set currentPageIndex [expr $currentPage - 1]
        set previousCurrentPage $currentPage
        set skip 0
    }

    foreach parameter $dataArray(ParameterNames) {
        set dataArray(Parameter.$parameter) [lreplace $dataArray(Parameter.$parameter) $currentPage $currentPage]
    }
    foreach column $dataArray(ColumnNames) {
        set dataArray(Column.$column) [lreplace $dataArray(Column.$column) $currentPage $currentPage]
    }
    if {!$skip} {
        for {set i $currentPage} {$i < $numPages} {incr i 1} {
            set pageRowCount([expr $i - 1]) $pageRowCount($i)
        }
    }
    incr numPages -1
    .parameterPane.parTop.page.menu delete $numPages
}

#
# DeleteParameter --
#
# This procedure invokes a deleting of the chosen parameter
#
# Attributes:
# dataArray - parameters' data structures,
# active - key word - indicating an active cell.
#

proc DeleteParameter {} {
    global dataArray
    if ![llength $dataArray(ParameterNames)] {
        APSSetVarAndUpdate ScrolledStatus "No parameter exists to be deleted!"
        bell
        return
    }
    
    set t .parameterPane.parameters.t
    if [$t tag includes disabled active] {
        APSSetVarAndUpdate ScrolledStatus "This parameter is protected."
        bell
        return
    }
    set row [lindex [split [$t index active] ,] 0]
    set parameterName [$t get $row,0]
    $t delete rows -holdtags $row 1
    set rowNumber [expr [llength $dataArray(ParameterNames)] - 1]
    if {$rowNumber > 6} {
        set height2 6
    } else {
        set height2 $rowNumber
    }
    $t configure -height $height2
    
    if [string length $parameterName] {
        set i [lsearch -exact $dataArray(ParameterNames) $parameterName]
        set dataArray(ParameterNames) [lreplace $dataArray(ParameterNames) $i $i]
        unset dataArray(ParameterInfo.$parameterName)
        unset dataArray(Parameter.$parameterName)
    }
    APSSetVarAndUpdate ScrolledStatus "Parameter $parameterName is deleted."
    
    LookForSearchBox parameter
}

#
# DeleteColumn --
#
# This procedure invokes a deleting of the chosen column.
#
# Attributes:
# t - type string - name of a column table.
# col - type integer - number of the chosen column to delete.
# dataArray - columns' data structures. 
#

proc DeleteColumn {t col} {
    global dataArray matchEntity
    
    if ![llength $dataArray(ColumnNames)] {
        APSSetVarAndUpdate ScrolledStatus "No column exists to be deleted!"
        bell
        return
    }
    if !$col {
        APSSetVarAndUpdate ScrolledStatus "\nYou cannot delete title column!"
        bell
        return
    }
    if [$t tag includes disabled active] {
        APSSetVarAndUpdate ScrolledStatus "This column is protected."
        bell
        return
    }
    set listLength [llength $dataArray(ColumnNames)]
    for {set i $listLength} {$i > $col} {incr i -1} {
        set colWidth([expr $i - 1]) [$t width $i]
    }
    
    set columnName [$t get 0,$col]
    $t delete cols -holdtags -holdwindows $col 1
    if [string length $columnName] {
        set i [lsearch -exact $dataArray(ColumnNames) $columnName]
        set dataArray(ColumnNames) [lreplace $dataArray(ColumnNames) $i $i]
        unset dataArray(ColumnInfo.$columnName)
        unset dataArray(Column.$columnName)
    }
    
    for {set i $col} {$i < $listLength} {incr i} {
        $t width $i $colWidth($i)
    }
    foreach win {.matchW .searchBox} {
        if {[winfo exist $win] && [string compare $matchEntity column]==0} {
            destroy $win
        }
    }
    APSSetVarAndUpdate ScrolledStatus "Column $columnName is deleted."
}

#
# DeleteRow --
#
# This procedure invokes a deleting of a row from columns' table.
#
# Attributes:
# t - type string - name of a columns' table.
# row - type integer - number of the row to be deleted in the columns' table.
# numRows - type integer - global variable - indicating a number of rows 
#           in a columns' table.
#

proc DeleteRow {t} {
    global numRows tkPriv ScrolledStatus dataArray
    global currentPage previousCurrentPage 
    set numbColumns [llength $dataArray(ColumnNames)]
    if $numbColumns<=10 {set clusterSwitch 50}
    if {10 < $numbColumns && $numbColumns <= 15} {set clusterSwitch 45}
    if {15 < $numbColumns && $numbColumns <= 20} {set clusterSwitch 40}
    if {20 < $numbColumns && $numbColumns <= 25} {set clusterSwitch 35}
    if {25 < $numbColumns && $numbColumns <= 30} {set clusterSwitch 30}
    if {30 < $numbColumns && $numbColumns <= 35} {set clusterSwitch 25}
    if {35 < $numbColumns && $numbColumns <= 40} {set clusterSwitch 20}
    if 40<$numbColumns {set clusterSwitch 15}

    if {![info exists numRows] || ![string length $numRows]} {
        APSSetVarAndUpdate ScrolledStatus "\nNo more rows to delete!"
        bell
        return
    } 

    set ScrolledStatus "\n[clock format [clock seconds] -format %H:%M:%S] Deleting row(s)..."
    update
    set delRows 0
    set rowList ""
    set newList ""
    set prevRow -1
    set rowCount 0
    set tkPriv(tableIndex) [$t cursel]
    set tkPriv(tableDeleteRows) ""
    set row1 -1
    if [llength $tkPriv(tableIndex)] {
        foreach cell $tkPriv(tableIndex) {
            set row2 [lindex [split $cell ,] 0]
            if $row1!=$row2 {
                lappend tkPriv(tableDeleteRows) $row2
                set row1 $row2
            }
        }
    }

    foreach row $tkPriv(tableDeleteRows) { 
        if !$row {
            set ScrolledStatus "\nYou cannot delete title row!"
            update
            bell
        } else {
            if {[expr $prevRow + 1] != $row} {
                if [llength $newList] {
                    lappend rowList [list $newList]
                    set newList ""
                }
            }
            lappend newList $row
            set prevRow $row
        }
        incr rowCount
    }
    if !$rowCount {
        set ScrolledStatus "\nRow is not selected."
        update
        bell
        return
    }

    if [llength $newList] {
        lappend rowList [list $newList]
        set newList ""
    }
    # puts stderr "Number of clusters [llength $rowList]"

    if {[llength $rowList] < $clusterSwitch} {
        foreach elem $rowList {
            set elem [string trim $elem \{\}]
            set elemLength [llength $elem]
            set row0 [lindex $elem 0]
            if $delRows {
                set row0 [expr $row0 - $delRows]
            }
            $t delete rows -keeptitles -holdtags $row0 $elemLength
            incr numRows -$elemLength
            set delRows [expr $delRows + $elemLength]
            set ScrolledStatus "Row(s) $elem deleted."
            update
        }
    } else {
        ChangePage -saveCurrPage 1
        set currentPageIndex [expr $currentPage - 1]
        EliminateMajorWidgets
        foreach ss [list .parameterPane.parTop .parameterPane.parameters \
                      .columnPane.colLabel .columnPane.columns] {
            if [winfo exists $ss] {
                pack unpack $ss
            }
        }
        MakeSpreadsheet 

        OpenTablesForCurrentPage $currentPageIndex

        set previousCurrentPage $currentPage
    }
    if {$numRows > 20} {
        set height2 20
    } else {
        set height2 [expr $numRows + 1]
    }
    $t configure -height $height2
    set ScrolledStatus "[llength $tkPriv(tableDeleteRows)] rows deleted. \
         \n[clock format [clock seconds] -format %H:%M:%S] Deleting done."
    update
    set tkPriv(tableIndex) ""
    set tkPriv(tableDeleteRows) ""
    LookForSearchBox column
}

#
# InsertUserChoice --
# 
# This procedure invokes a call for inserting an entity
# which name was passed by the call.
# 
# Attributes:
# editVar - type string - name of the entity to be inserted
#

proc InsertUserChoice {editVar} {
    set table .columnPane.columns.t
    set rowI [lindex [split [$table index active] ,] 0]
    set colI [lindex [split [$table index active] ,] 1]
    switch $editVar {
        parameter {InsertParameter}
        column {InsertColumn $table $colI}
        row {InsertRowQuestion $table $rowI}
        page {InsertPage}
        default {
            APSSetVarAndUpdate ScrolledStatus "Unrecognized entity $editVar."
            return
        }
    }
}

proc InsertPage {args} {
    set copy 0
    APSStrictParseArguments {copy}
    global validData dataArray currentPage currentPageIndex numPages pageRowCount
    if !$validData {
        APSSetVarAndUpdate ScrolledStatus "No data."
        return
    }
    if {[llength $dataArray(ArrayNames)]} {
        APSSetVarAndUpdate ScrolledStatus "Currently unable to insert pages with sdds files containing arrays"
        return
    }
    APSSetVarAndUpdate ScrolledStatus "Inserting page after current page..."
    if {$copy} {
        foreach parameter $dataArray(ParameterNames) {
            set dataArray(Parameter.$parameter) [linsert $dataArray(Parameter.$parameter)\
                                                   $currentPage [lindex $dataArray(Parameter.$parameter) $currentPageIndex]]
        }
        foreach column $dataArray(ColumnNames) {
            set dataArray(Column.$column) [linsert $dataArray(Column.$column) $currentPage\
                                             [lindex $dataArray(Column.$column) $currentPageIndex]]
        }
    } else {
        foreach parameter $dataArray(ParameterNames) {
            set dataArray(Parameter.$parameter) [linsert $dataArray(Parameter.$parameter)\
                                                   $currentPage {}]
        }
        foreach column $dataArray(ColumnNames) {
            set dataArray(Column.$column) [linsert $dataArray(Column.$column) $currentPage {}]
        }
    }
    for {set i $numPages} {$i > $currentPage} {incr i -1} {
        set pageRowCount($i) $pageRowCount([expr $i - 1])
    }
    if {$copy} {
        set pageRowCount($currentPage) $pageRowCount($currentPageIndex)
    } else {
        set pageRowCount($currentPage) 0
    }
    incr numPages
    .parameterPane.parTop.page.menu add radiobutton -label $numPages -variable currentPage\
      -command ChangePage
    APSSetVarAndUpdate ScrolledStatus "Done"
}

#
# InsertParameter --
#
# This procedure invokes a display of a form for a new parameter.
# 
# Attributes:
# fixedValue - type string - global variable which contains 
# parameters data.
#  

proc InsertParameter {} {
    global fixedValue

    if [winfo exists .newEntityBox] {
        destroy .newEntityBox
    }

    ResetEntries

    set okCommand SetParameter
    set cancelCommand "APSSetVarAndUpdate ScrolledStatus \
                       {Input of new parameter is canceled.}"
    APSDialogBox .newEntityBox -name "New Parameter" -width 80 \
      -cancelCommand $cancelCommand -okCommand $okCommand \
      -contextHelp "This is a form for a new parameter to be inserted in\
         the parameter table."
    setLabelEntryWidgets single .newEntityBox.userFrame
    APSLabeledEntry .parFixVal -parent .newEntityBox.userFrame -label "Parameter fixed value: " \
      -textVariable fixedValue -width 80 -contextHelp \
      "Fixed value allows specification of the constant value for a given parameter."
}

#
# SetParameter --
# 
# This procedure invokes an actual setup of the parameter with all
# its characteristic.
#
# Attributes:
# dataArray - parameters' data structures,
# name,symbol,units,description,formatString,type,fixedValue -
#    variables containing a characteristic of the parameter.
#

proc SetParameter {} {
    global name symbol units description formatString type fixedValue
    global dataArray numPages

    if {[llength $name] != 1} {
        APSSetVarAndUpdate ScrolledStatus "\nInvalid parameter name is provided.\
             Parameter is not saved."
        bell
        return
    }
    if {[lsearch -exact $dataArray(ParameterNames) $name]>=0} {
        APSSetVarAndUpdate ScrolledStatus "\nParameter $name already exists!\
             New input is not saved."
        bell
        return
    }

    set dataArray(Parameter.$name) [APSReplicateItem -item "" -number $numPages]
    set dataArray(ParameterInfo.$name) ""
    set t .parameterPane.parameters.t

    if ![llength $dataArray(ParameterNames)] {
        $t delete rows -keeptitles -holdtags 0 1
    }
    set attrList1 [list name symbol units description format_string type fixed_value]
    set optionList1 [list name symbol units description formatString type fixedValue]
    foreach attr $attrList1 opt $optionList1 {
        lappend dataArray(ParameterInfo.$name) $attr
        lappend dataArray(ParameterInfo.$name) [set $opt]
    }
    lappend dataArray(ParameterNames) $name

    set t .parameterPane.parameters.t
    set rowNumber [llength $dataArray(ParameterNames)]
    set row [expr $rowNumber - 1]
    $t insert rows -keeptitles -holdtags $row 1

    $t set ${rowNumber},0 $name

    $t set ${rowNumber},1 $fixedValue
    if {$rowNumber > 6} {
        set height1 6
    } else {
        set height1 $rowNumber
    }
    $t configure -height $height1
    $t yview moveto 1.00
    LookForSearchBox parameter
}

# 
# InsertColumn --
# 
# This procedure invokes a display of a form for a new column.
#
# Attributes:
# fieldLength - type integer - global variable specifies the number 
# of characters occupied by the data for the column.
#

proc InsertColumn {t col} {
    global fieldLength

    if [winfo exists .newFileBox] {
        destroy .newFileBox
    }

    ResetEntries

    set okCommand "SetColumn $t $col"
    set cancelCommand "APSSetVarAndUpdate ScrolledStatus \
                        {Input of new column is canceled.}"
    APSDialogBox .newEntityBox -name "New Column" -width 80 \
      -cancelCommand $cancelCommand -okCommand $okCommand \
      -contextHelp "This is a form for a new column to be\
         inserted in the column table."
    setLabelEntryWidgets single .newEntityBox.userFrame
    APSLabeledEntry .colField -parent .newEntityBox.userFrame -label "Column field length: " \
      -textVariable fieldLength -width 80 -contextHelp "Field length specifies \
         the number of characters occupied by the data for the column.  If zero, \
         the data is assumed to be bounded by whitespace characters.  If negative, \
         the absolute value is taken as the field length, but leading and trailing \
         whitespace character will be deleted from string data."   
}

#
# SetColumn --
#
# This procedure invokes an actual setup of the column with all
# its characteristic.
#
# Attributes:
# dataArray - column's data structures.
# name,symbol,units,description,formatString,type,fieldLength -
#    variables containing a characteristic of the column.
#

proc SetColumn {t col} {
    global name symbol units description formatString type fieldLength
    global dataArray numPages pageRowCount

    if {[llength $name] != 1} {
        APSSetVarAndUpdate ScrolledStatus "\nInvalid column name is provided. \
             Column is not saved."
        bell
        return
    }
    if {[lsearch -exact $dataArray(ColumnNames) $name]>=0} {
        APSSetVarAndUpdate ScrolledStatus "\nColumn $name already exists!\
             New input is not saved."
        bell
        return
    }

    set dataArray(Column.$name) ""
    for {set i 0} {$i < $numPages} {incr i} {
        lappend dataArray(Column.$name) [APSReplicateItem -item "" -number $pageRowCount($i)]
    }
    set dataArray(ColumnInfo.$name) ""
    set attrList2 [list name symbol units description format_string type field_length]     
    set optionList2 [list name symbol units description formatString type fieldLength]
    foreach attr $attrList2 opt $optionList2 {
        lappend dataArray(ColumnInfo.$name) $attr
        lappend dataArray(ColumnInfo.$name) [set $opt]
    }

    set listLength [llength $dataArray(ColumnNames)]
    for {set i $listLength} {$i > $col} {incr i -1} {
        set colWidth([expr $i + 1]) [$t width $i]
    }
    set colNumber [expr $col + 1] 
    $t insert cols -holdtags $col 1
    set newWidth [expr [string length $name] + 2]
    $t width $colNumber $newWidth

    tk_tablePasteHandler3 $t 0,${colNumber} $name

    lappend dataArray(ColumnNames) $name
    set endOfList [llength $dataArray(ColumnNames)]

    for {set i [expr $col + 2]} {$i <= $endOfList} {incr i} {
        $t width $i $colWidth($i)
    }

    if [array size colWidth] {
        unset colWidth
    }

    for {set i $listLength} {$i > $col} {incr i -1} {
        set j [expr $i - 1]
        set dataArray(ColumnNames) [lreplace $dataArray(ColumnNames) $i $i [lindex $dataArray(ColumnNames) $j]]
    }

    set dataArray(ColumnNames) [lreplace $dataArray(ColumnNames) $col $col $name]
}

proc GoToPage {} {
    global numPages
    if [winfo exists .goToPage] {
        destroy .goToPage
    }
    APSDialogBox .goToPage -name "sddsEdit:Go To Page" \
      -contextHelp "Dialog box for sddsEdit: Go To Page" \
      -okCommand {
          if {($gotoPage > 0) && ($gotoPage <= $numPages)} {
              set currentPage $gotoPage
              ChangePage
          }
      } \
      -cancelCommand "APSSetVarAndUpdate ScrolledStatus {Go to page was canceled.}; return"
    APSLabeledEntry .page -parent .goToPage.userFrame \
      -packOption "-side top -fill x" -label "Page number from 1 to $numPages"\
      -textVariable gotoPage -width 10 -type integer \
      -contextHelp "Enter the page number to go to."
    
}

proc InsertRowQuestion {t row} {
    global newRows

    if [winfo exists .insertRow] {
        destroy .insertRow
    }
    APSDialogBox .insertRow -name "sddsEdit:Insert Row" \
      -contextHelp "Dialog box for sddsEdit: Insert Row(s)." \
      -okCommand "InsertRow $t $row" \
      -cancelCommand "APSSetVarAndUpdate ScrolledStatus {Insert Row was canceled.}; return"

    APSLabeledEntry .newRows -parent .insertRow.userFrame \
      -packOption "-side top -fill x" -label "Number of new rows: "\
      -textVariable newRows -width 10 -type integer \
      -contextHelp "Insert the number of new rows."
}

#
# InsertRow --
#
# This procedure inserts a new row right under the active cell in 
# a column table, assigns row number in order.
#
# Attributes:
# t - type string - a name of the columns' table,
# row - type integer - a number of the row after which a new row
#    is going to be inserted.
# 

proc InsertRow {t row} {
    global numRows newRows

    #     if !$newRows {
    #          set newRows 1
    #     }

    $t insert rows -keeptitles -holdtags $row $newRows

    for {set n 1} {$n <= $newRows} {incr n} {
        incr numRows
        # Original tk_tablePasteHandler procedure from the tkTable library
        tk_tablePasteHandler3 $t ${numRows},0 $numRows
    }
    if {$numRows > 20} {
        set height2 20
    } else {
        set height2 [expr $numRows + 1]
    }
    $t configure -height $height2
    .userFrame.pane forget .columnPane
    .userFrame.pane add .columnPane
}

#
# Starting new sdds file part
#
proc startNewFile {} {
    global workingDir fileName w entity pCount cCount aCount position
    global validData inputFile newStatus snapEntity snapFlag pushButton
    global parameterArray columnArray arrayArray parList colList arrList 
    global parameterCountList columnCountList arrayCountList name
    global dataArray
    
    if $validData {
        set inputFile ""
        set validData 0
        foreach variable {fileName parameterArray columnArray arrayArray \
                            parameterCountList columnCountList arrayCountList dataArray} {
            if [info exists $variable] {
                unset $variable
            }
        }
        EliminateMajorWidgets
    }

    foreach butt [list .menu.edit .menu.search .menu.editCom .menu.info] {
        $butt configure -state disabled 
    }

    APSSetVarAndUpdate ScrolledStatus "New file mode..."

    set pCount 0
    set cCount 0
    set aCount 0
    set parameterCountList ""
    set columnCountList ""
    set arrayCountList ""
    set parList [list fieldLength dimensions groupName]
    set colList [list fixedValue dimensions groupName]
    set arrList "fixedValue"
    set snapFlag 0
    set pushButton ""
    set position 0
    set name ""

    set okCommand "SavePreviousEntry; set validData 1; [set pushButton ""]; \
                    setNewFile"
    set cancelCommand "set validData 1; set pushButton {}; \
                        APSSetVarAndUpdate ScrolledStatus {New file mode is canceled.}; \
                        destroy .newFileBox"
    APSDialogBox .newFileBox -name "New file dialog" -width 80 \
      -cancelCommand $cancelCommand -okCommand $okCommand \
      -contextHelp "This widget is a tool to set a header of new sdds file. By pressing OK button creates new sdds file from the previous entries and stores it in the chosen directory. For a row editing refer to \042Open\042 option from the menu."

    APSDialogBoxAddButton .minus1 -parent .newFileBox -text "-1" -width 4 -packOption "-side left -pady 1" \
      -command "DisplayEntity -1" -contextHelp \
      "Looking for the previously stored active entity one step backward in a time."
    APSDialogBoxAddButton .plus1 -parent .newFileBox -text "+1" -width 4 -packOption "-side left -pady 1" \
      -command "DisplayEntity +1" -contextHelp \
      "Looking for the previously stored active entity one step forward in a time."
    APSDialogBoxAddButton .minus5 -parent .newFileBox -text "-5" -width 4 -packOption "-side left -pady 1" \
      -command "DisplayEntity -5" -contextHelp \
      "Looking for the previously stored active entity five steps backward in a time."
    APSDialogBoxAddButton .plus5 -parent .newFileBox -text "+5" -width 4 -packOption "-side left -pady 1" \
      -command "DisplayEntity +5" -contextHelp \
      "Looking for the previously stored active entity five steps forward in a time."
    APSDialogBoxAddButton .search -parent .newFileBox -text "Search" \
      -command Search -contextHelp "Looking for an entity by the name in the active entity stock." 
    APSDialogBoxAddButton .delete -parent .newFileBox -text "Delete Entry" \
      -command DeleteEntity -contextHelp "Looking for an entity by the name in the active entity stock and if found deleting the entry."
    APSDialogBoxAddButton .newEnt -parent .newFileBox -text "New Entry" \
      -command newEntriesSlot -contextHelp "Creates new entries slot for an active entity."

    set w .newFileBox.userFrame
    set newStatus "Ready..."
    APSScrolledStatus .newStatus -parent $w -textVariable newStatus -width 80 -height 4 -packOption "-side top -fill x -expand false"
    
    set fileName ""
    APSLabeledEntry .file -parent $w -label "Enter new file name: " \
      -textVariable fileName -width 80 -contextHelp \
      "Enter the name of the file to which to write the new data."
    APSLabeledEntry .direct -parent $w -label "Set into directory: " \
      -textVariable workingDir -width 80 -contextHelp \
      "Enter a directory where you want to store new file."

    set entity "parameter"
    set snapEntity "parameter"
    APSRadioButtonFrame .entity -parent $w -label "Entity Entries:               " \
      -variable entity -buttonList {parameter column} \
      -valueList {parameter column} \
      -orientation horizontal -commandList {ParameterForm ColumnForm} \
      -contextHelp "Choose an entity you want to add to the file."

    ParameterForm

    APSFrame .entityCount -parent $w -label "Entities Count:" \
      -packOption "-side bottom"
    set f2 $w.entityCount.frame
    APSLabeledOutput .pCount -parent $f2 -label "Number of entered parameters" \
      -packOption "-side left -fill x" -textVariable pCount -width 5 \
      -contextHelp "Display for a number of stored parameters."
    APSLabeledOutput .cCount -parent $f2 -label "Number of entered columns" \
      -packOption "-side left -fill x" -textVariable cCount -width 5 \
      -contextHelp "Display for a number of stored columns."
    APSLabeledOutput .aCount -parent $f2 -label "Number of entered arrays" \
      -packOption "-side right -fill x" -textVariable aCount -width 5 \
      -contextHelp "Display for a number of stored arrays"

    if [winfo exists .newFileBox] {
        wm protocol .newFileBox WM_DELETE_WINDOW { \
                                                     set validData 1; unset pushButton; destroy .newFileBox; \
                                                     APSSetVarAndUpdate ScrolledStatus {New file mode is canceled.}
        }
    }

    setResetButtons 0
    update
}

proc invokeSwapFrame {} {
    global w f3
    if [winfo exists $w.swap] {
        destroy $w.swap
    }
    APSFrame .swap -parent $w -packOption "-side top"
    set f3 $w.swap.frame
}

proc EntityInitialization {} {
    global entity pushButton snapFlag name snapEntity
    global parameterArray columnArray arrayArray f3

    if ![array size ${entity}Array] {
        setResetButtons 0
    } else {
        setResetButtons 1
    }

    if {![array size ${snapEntity}Array] && [string length $name]} {
        if [catch {saveEntityEntries $snapEntity} result] {
            APSSetVarAndUpdate newStatus "$result"
            return
        }
        set snapFlag 0
    } 
    if {$snapFlag && [array size ${snapEntity}Array]} {
        SavePreviousEntry
    }

    invokeSwapFrame
    ResetEntries
    setLabelEntryWidgets multi $f3
    set snapFlag 1
}

proc ParameterForm {} {
    global fixedValue f3 parameterArray parameterCountList 
    global snapEntity entity position snapshotArray name pushButton

    EntityInitialization
    APSLabeledEntry .parFixVal -parent $f3 -label "Parameter fixed value: " \
      -textVariable fixedValue -width 80 -contextHelp \
      "Fixed value allows specification of the constant value for a given parameter."
    if [array size parameterArray] {
        set position [llength $parameterCountList]
        set lastEntry [lindex $parameterCountList [expr [llength $parameterCountList] - 1]]
        EditEntity parameter $lastEntry
        set snapshotArray($entity) $name
    } else {
        set position 0
    }
    set pushButton "Radio"
    SetCounts
    set snapEntity $entity
    update
}

proc ColumnForm {} {
    global fieldLength f3 columnArray columnCountList pushButton
    global snapEntity entity position snapshotArray name

    EntityInitialization
    APSLabeledEntry .colField -parent $f3 -label "Column field length:    " \
      -textVariable fieldLength -width 80 -contextHelp "Field length specifies the number of characters occupied by the data for the column.  If zero, the data is assumed to be bounded by whitespace characters.  If negative, the absolute value is taken as the field length, but leading and trailing whitespace character will be deleted from string data."   
    if [array size columnArray] {
        set position [llength $columnCountList]
        set lastEntry [lindex $columnCountList [expr [llength $columnCountList] - 1]]
        EditEntity column $lastEntry
        set snapshotArray($entity) $name
    } else {
        set position 0
    }
    set pushButton "Radio"
    SetCounts
    set snapEntity $entity 
    update
}

proc ArrayForm {} {
    global fieldLength dimensions groupName f3 
    global arrayArray arrayCountList snapshotArray name
    global snapEntity entity position pushButton

    EntityInitialization
    APSLabeledEntry .arrGroup -parent $f3 -label "Array group name: " \
      -textVariable groupName -width 80 -contextHelp "Array group name allows specification of a string giving the name of the array group to which the array belongs; such strings may be defined by the user to indicate that different arrays are related."
    APSLabeledEntry .arrField -parent $f3 -label "Array field length: " \
      -textVariable fieldLength -width 80 -contextHelp "Field length specifies the number of characters occupied by the data for the column.  If zero, the data is assumed to be bounded by whitespace characters.  If negative, the absolute value is taken as the field length, but leading and trailing whitespace character will be deleted from string data." 
    APSLabeledEntry .arrDimen -parent $f3 -label "Array dimensions: " \
      -textVariable dimensions -width 80 -contextHelp \
      "Array dimensions field gives the number of dimensions in the array."
    if [array size arrayArray] {
        set position [llength $arrayCountList]
        set lastEntry [lindex $arrayCountList [expr [llength $arrayCountList] - 1]]
        EditEntity array $lastEntry
        set snapshotArray($entity) $name
    } else {
        set position 0
    }
    set pushButton "Radio"
    SetCounts
    set snapEntity $entity
    update
}

proc SetCounts {} {
    global pCount cCount aCount parameterArray columnArray arrayArray
    set pCount [array size parameterArray]
    set cCount [array size columnArray]
    set aCount [array size arrayArray]
    update
}

proc EditEntity {editEnt elem} {
    global parameterArray columnArray arrayArray optionTag
    global name symbol units description formatString type
    global fixedValue fieldLength dimensions groupName
    global parList colList arrList position snapshotArray
    global parameterCountList columnCountList arrayCountList
    
    if [array size ${editEnt}Array] {
        if {[string length $elem] && [string compare $elem Null] != 0} {
            set optionList ""
            set name $elem
            array set optionArray [set ${editEnt}Array($elem)]
            foreach arr [array names optionArray] {
                set opt [string trimleft $arr -]
                lappend optionList $opt
                set $opt $optionArray($arr)
            }
            foreach tag [array names optionTag] {
                if {[lsearch -exact $optionList $tag] < 0} {
                    switch $editEnt {
                        parameter {
                            if {[lsearch -exact $parList $tag] < 0} {
                                set $tag ""
                            }
                        }
                        column {
                            if {[lsearch -exact $colList $tag] < 0} {
                                set $tag ""
                            }
                        }
                        array {
                            if {[lsearch -exact $arrList $tag] < 0} {
                                set $tag ""
                            }
                        }
                        default {
                            APSAlertBox [APSUniqueName .] -errorMessage "Invalid entity: $editEnt"
                            return
                        }
                    }
                }
            }
            set position [expr [lsearch -exact [set ${editEnt}CountList] $elem] + 1]
            set snapshotArray($editEnt) $elem
            SetCounts
            update
        }
    }
}

proc ResetEntries {} {
    global name symbol units description formatString type
    global fieldLength dimensions groupName fixedValue
    set name ""
    set symbol "" 
    set units "" 
    set description ""
    set formatString ""
    set type "SDDS_STRING"
    set fieldLength 0
    set dimensions 1
    set groupName ""
    set fixedValue ""
}

proc setLabelEntryWidgets {module parent} {
    global name symbol units description formatString type 
    global position entity

    APSLabeledEntry .eName -parent $parent -label "Entity name: " \
      -textVariable name -width 80
    APSLabeledEntry .eSymbol -parent $parent -label "Entity symbol: " \
      -textVariable symbol -width 80
    APSLabeledEntry .eUnits -parent $parent -label "Entity units: " \
      -textVariable units -width 80
    APSLabeledEntry .eDescr -parent $parent -label "Entity description: " \
      -textVariable description -width 80
    APSLabeledEntry .eFormat -parent $parent -label "Entity format string: " \
      -textVariable formatString -width 80
    APSRadioButtonFrame .eType -parent $parent -label "Entity type:                 " \
      -variable type -buttonList {int64 uint64 int32 uint32 int16 uint16 float double "long double" character string} \
      -valueList {SDDS_LONG64 SDDS_ULONG64 SDDS_LONG SDDS_ULONG SDDS_SHORT SDDS_USHORT SDDS_FLOAT SDDS_DOUBLE SDDS_LONGDOUBLE SDDS_CHARACTER SDDS_STRING} \
      -orientation horizontal 
    if [string compare $module multi]==0 {
        APSFrame .positionFrame -parent $parent -packOption "-side bottom"
        APSLabeledOutput .position -parent ${parent}.positionFrame.frame \
          -label "Position in the $entity list" \
          -textVariable position -width 5 \
          -contextHelp "Shows a number of the display position in the current entity list."
    }
    update
}

proc saveEntityEntries {saveEnt} {
    switch $saveEnt {
        parameter -
        column -
        array {
            saveEntries $saveEnt
        }
        default {
            APSAlertBox [APSUniqueName .] -errorMessage "Invalid entity: $saveEnt"
            return
        }
    }
    SetCounts
}

proc saveEntries {ent} {
    global ${ent}Array name newStatus overWrite
    set overWrite 0

    if [array size ${ent}Array] {
        foreach elem [array names ${ent}Array] {
            if {[string compare $elem $name] == 0} {
                catch {APSMultipleChoice [APSUniqueName .] \
                         -question "This $elem alredy exists. What do you want to do?" \
                         -labelList {Overwrite Cancel} \
                         -returnList {Overwrite Cancel}} elemChoice

                switch $elemChoice {
                    Overwrite {
                        unset ${ent}Array($elem)
                        set overWrite 1
                    }
                    Cancel {
                        return
                    }
                    default {
                        APSAlertBox [APSUniqueName .] -errorMessage "Unknown return value: $elemChoice"
                        return
                    }
                }
            }
        }
    }
    if [catch {setEntity $ent} result] {
        APSAlertBox [APSUniqueName .] -errorMessage $result
        return
    }
    set newStatus "$ent $name is saved."
    update
}

proc setEntity {Entity} {
    global name type optionTag ${Entity}Array overWrite
    global parList colList arrList
    global parameterCountList columnCountList arrayCountList

    if {[string length $name] && [string length $type] && \
          [string compare $name Null] != 0 && [string compare $type Null] != 0} {
        set ${Entity}Array($name) ""
        foreach arr [array names optionTag] {
            global $optionTag($arr)
            set flag 0
            switch $Entity {
                parameter {
                    if {[lsearch -exact $parList $optionTag($arr)] < 0} {
                        set flag 1
                    }
                }
                column {
                    if {[lsearch -exact $colList $optionTag($arr)] < 0} {
                        set flag 1
                    }
                }
                array {
                    if {[lsearch -exact $arrList $optionTag($arr)] < 0} {
                        set flag 1
                    }
                }
                default {
                    APSAlertBox [APSUniqueName .] -errorMessage "Invalid entity: $editEnt"
                    return
                }
            }
            if $flag {
                if {[string length [set $optionTag($arr)]] && \
                      [string compare [set $optionTag($arr)] Null] != 0} {
                    lappend ${Entity}Array($name) "-$optionTag($arr)"
                    lappend ${Entity}Array($name) "[set $optionTag($arr)]"
                }
            }
        } 
        if !$overWrite {
            lappend ${Entity}CountList $name
            set overWrite 1
        }
        return -code ok
    } else {
        return -code error "Name and type fields must be supplied"
    }
}

proc SavePreviousEntry {} {
    global snapEntity pushButton snapshotArray name overWrite
    global parameterArray columnArray arrayArray snapFlag position
    global parameterCountList columnCountList arrayCountList

    if $snapFlag {
        if ![string length $name] {
            SetCounts
            incr position -1
            return
        }

        if {[string compare $pushButton NewEntry] == 0} {
            saveEntityEntries $snapEntity
            set snapshotArray($snapEntity) $name
        }
        if {[string compare $pushButton LookEntry] == 0 || \
              [string compare $pushButton Search] == 0 || \
              [string compare $pushButton Radio] == 0} {
            if [array size ${snapEntity}Array] {
                set nameSnap $snapshotArray($snapEntity)
                unset ${snapEntity}Array($nameSnap)
                set index [lsearch [set ${snapEntity}CountList] $nameSnap]
                set ${snapEntity}CountList [lreplace [set ${snapEntity}CountList] $index $index $name]
                set overWrite 1
                if [catch {setEntity $snapEntity} result] {
                    APSAlertBox [APSUniqueName .] -errorMessage $result
                    return -code error "$result"
                } else {
                    return -code ok
                }
            }
        }
    }
    set snapFlag 1
}

proc DisplayEntity {step} {
    global entity name newStatus pushButton overWrite
    global parameterArray columnArray arrayArray snapshotArray
    global parameterCountList columnCountList arrayCountList 

    if {![string length $name] && [string compare $pushButton LookEntry] != 0} {
        EditEntity $entity [lindex [set ${entity}CountList] \
                              [expr [llength [set ${entity}CountList]] - 1]]
        set newStatus "Last entry has not contained a name. Not saved."
        set pushButton "LookEntry"
        bell
        return
    }
    if [catch {SavePreviousEntry} result] {
        APSAlertBox [APSUniqueName .] -errorMessage $result
    } else {
        if {!$overWrite && [string compare $pushButton NewEntry] == 0} {
            EditEntity $entity [lindex [set ${entity}CountList] \
                                  [expr [llength [set ${entity}CountList]] - 1]]
            set pushButton "LookEntry"
            return
        }
    }

    if [array size ${entity}Array] {
        foreach nam [array names ${entity}Array] {
            if {[string compare $nam $name] == 0} {
                set j [lsearch -exact [set ${entity}CountList] $nam]
            }
        }
    } else {
        set newStatus "$entity is not found."
        bell
        return
    }

    if {[expr abs($step) >= [array size ${entity}Array]]} {
        if {$step >= [array size ${entity}Array]} {
            EditEntity $entity [lindex [set ${entity}CountList] \
                                  [expr [llength [set ${entity}CountList]] - 1]]
        } 
        if {$step <= [array size ${entity}Array]} {    
            EditEntity $entity [lindex [set ${entity}CountList] 0]
        }
        bell
        return
    }

    if {$j < 0} {
        set newStatus "$entity $name is not found."
        EditEntity $entity $nam
        return
    }

    switch -- $step {
        -1 -
        -5 {
            set stm [string trimleft $step -]
            if {[expr $j - $stm] < 0} {
                bell
                EditEntity $entity [lindex [set ${entity}CountList] 0]
                set pushButton "LookEntry"
                return
            }
            set newName [lindex [set ${entity}CountList] [expr $j - $stm]]
        }
        +1 -
        +5 {
            set stp [string trimleft $step +]
            if {[expr $j + $stp] >= [array size ${entity}Array]} {
                bell
                EditEntity $entity [lindex [set ${entity}CountList] \
                                      [expr [llength [set ${entity}CountList]] - 1]]
                set pushButton "LookEntry"
                return
            }
            set newName [lindex [set ${entity}CountList] [expr $j + $stp]]
        }
    }

    EditEntity $entity $newName
    set pushButton "LookEntry"
    set snapshotArray($entity) $name
}

proc newEntriesSlot {} {
    global entity position pCount cCount aCount pushButton 
    global parameterCountList columnCountList arrayCountList
    global parameterArray columnArray arrayArray name

    if [catch {SavePreviousEntry} result] {
        APSAlertBox [APSUniqueName .] -errorMessage $result
    }
    
    if {![array size ${entity}Array] && [string length $name]} {
        if [catch {saveEntityEntries $entity} result] {
            APSSetVarAndUpdate newStatus "$result"
            return
        }
    }
    ResetEntries
    setResetButtons 1

    if [llength ${entity}CountList] {
        set position [expr [llength [set ${entity}CountList]] + 1]
    } else {
        set position 1
    }
    switch $entity {
        parameter {set pCount [expr [array size parameterArray] + 1]}
        column {set cCount [expr [array size columnArray] + 1]}
        array {set aCount [expr [array size arrayArray] + 1]}
        default {
            APSAlertBox [APSUniqueName .] -errorMessage "Invalid entity: $entity."
            return
        }
    }
    set pushButton "NewEntry"
    update
}

proc Search {} {
    global newStatus entity name parameterArray columnArray arrayArray
    global parameterCountList columnCountList arrayCountList 
    global pushButton position

    if [array size ${entity}Array] {
        set flag 0
        foreach arr [array names ${entity}Array] {
            if {[string compare $arr $name] == 0} {
                EditEntity $entity $arr 
                set newStatus "$entity $name is found."
                set flag 1
            }
        }
        if !$flag {
            set newStatus "$entity $name is not found."
            set lastEntry [lindex [set ${entity}CountList] [expr [llength [set ${entity}CountList]] - 1]]
            EditEntity $entity $lastEntry
        }
    } else {
        set newStatus "$entity $name is not found."
        set position 0
    }
    update
    set pushButton "Search"
}

proc DeleteEntity {} {
    global entity newStatus name parameterArray columnArray arrayArray
    global parameterCountList columnCountList arrayCountList pushButton
    global position
    set nameE $name

    if [array size ${entity}Array] {
        set flag 0
        foreach arr [array names ${entity}Array] {
            if {[string compare $arr $name] == 0} {
                unset ${entity}Array($arr)
                set delName [lsearch -exact [set ${entity}CountList] $arr]
                set ${entity}CountList [lreplace [set ${entity}CountList] $delName $delName]
                set flag 1
            }
        }
        if {$flag && [array size ${entity}Array]} {
            set lastEntry [lindex [set ${entity}CountList] [expr [llength [set ${entity}CountList]] - 1]]
            EditEntity $entity $lastEntry
            set newStatus "$entity $nameE is deleted."
            return
        } elseif {$flag == 0} {
            set newStatus "$entity $name is not found."
            set lastEntry [lindex [set ${entity}CountList] [expr [llength [set ${entity}CountList]] - 1]]
            EditEntity $entity $lastEntry
            return
        } elseif {$flag && ![array size ${entity}Array]} {
            set newStatus "$entity $nameE is deleted. No more entries for $entity."
            set pushButton "Delete"
            set name ""
            switch $entity {
                parameter {
                    ParameterForm
                }
                column {
                    ColumnForm
                }
                array {
                    ArrayForm
                }
                default {
                    APSAlertBox [APSUniqueName .] -errorMessage "Invalid entity: $entity."
                    return
                }
            }
        } else {
            continue
        }
    } else {
        set newStatus "$entity $name is not found."
        set position 0
    }
    SetCounts
    set pushButton "Delete"
    update
}

#
# Allows saving the data into a named file.
#

proc setNewFile {} {
    global columnArray parameterArray arrayArray validData 
    global fileName workingDir
    
    if {[array size columnArray] || [array size parameterArray] || [array size arrayArray]} {
        set validData 1
    }
    if !$validData {
        APSSetVarAndUpdate newStatus "No data."
        return
    }
    if [catch {CheckOutputFile -fileName $workingDir/$fileName} result] {
        APSAlertBox [APSUniqueName .] -errorMessage "$result"
        return
    }
}

proc CheckOutputFile {args} {
    set fileName ""
    APSStrictParseArguments {fileName}

    if ![string length $fileName] {
        set outputFile \
          [APSInfoDialog [APSUniqueName .] -name "Output file dialog" \
             -width 40 -infoMessage "Output filename: " \
             -contextHelp "Enter the name of the file to which to write the data." \
             -default ""]
    } else {
        set outputFile $fileName
    }
    if [file exists $outputFile] {
        catch {APSMultipleChoice [APSUniqueName .] \
                 -question "File $outputFile exists. What do you want to do?" \
                 -labelList {"New filename" Overwrite Cancel} \
                 -returnList {Newfilename Overwrite Cancel}} choice
        
        switch $choice {
            Newfilename {
                CheckOutputFile 
            }
            Overwrite {
                catch {file copy -force -- $outputFile $outputFile.bck}
                if [catch {saveNewFile -outputFile $outputFile} result] {
                    APSAlertBox [APSUniqueName .] -errorMessage "$result"
                    return
                }
            }
            Cancel {
                APSSetVarAndUpdate newStatus "New file aborted."
                return
            }
            default {
                APSSetVarAndUpdate newStatus "Unknown return value: $choice"
                return
            }
        }
    } else {
        if [catch {saveNewFile -outputFile $outputFile} result] {
            APSAlertBox [APSUniqueName .] -errorMessage "$result"
            return
        }
    }
}

proc saveNewFile {args} {
    set outputFile ""
    APSStrictParseArguments {outputFile}
    global optionTag parameterArray columnArray arrayArray
    global parameterCountList columnCountList arrayCountList

    APSSetVarAndUpdate ScrolledStatus "Saving data..."
    if [string length $outputFile] {
        if [catch {sdds open $outputFile w SDDS_BINARY} fid] {
            APSSetVarAndUpdate ScrolledStatus "$fid"
            return
        }
        foreach parameter $parameterCountList {
            set optionList $parameterArray($parameter)
            if [catch {eval sdds defineParameter $fid $parameter $optionList} result] {
                APSSetVarAndUpdate ScrolledStatus "$result"
                return
            }
            unset optionList
        }

        foreach column $columnCountList {
            set optionList $columnArray($column)
            if [catch {eval sdds defineColumn $fid $column $optionList} result] {
                APSSetVarAndUpdate ScrolledStatus "$result"
                return
            }
            unset optionList
        }
        
        foreach arr $arrayCountList {
            set optionList $arrayArray($arr)
            if [catch {eval sdds defineArray $fid $arr $optionList} result] {
                APSSetVarAndUpdate ScrolledStatus "$result"
                return
            }
            unset optionList
        }

        if {[catch {sdds writeLayout $fid} result] || \
              [catch {sdds startPage $fid 0} result]} {
            APSSetVarAndUpdate ScrolledStatus "$result"
            return
        }

        if {[catch {sdds writePage $fid} result] || \
              [catch {sdds close $fid} result]} {
            APSSetVarAndUpdate ScrolledStatus "$result"
            return
        }

        APSSetVarAndUpdate ScrolledStatus "Data saved to $outputFile. \
                                   \nDone with the new file mode."

        if [catch {openFile -fileName $outputFile} result] {
            APSSetVarAndUpdate ScrolledStatus "$result"
        }
        
    } else {
        APSSetVarAndUpdate ScrolledStatus "Ready..."
    }        
}

proc setResetButtons {state} {
    if $state {
        APSEnableButton .newFileBox.buttonRow.minus1.button
        APSEnableButton .newFileBox.buttonRow.plus1.button
        APSEnableButton .newFileBox.buttonRow.minus5.button
        APSEnableButton .newFileBox.buttonRow.plus5.button
        APSEnableButton .newFileBox.buttonRow.search.button
        APSEnableButton .newFileBox.buttonRow.delete.button
    } else {
        APSDisableButton .newFileBox.buttonRow.minus1.button
        APSDisableButton .newFileBox.buttonRow.plus1.button
        APSDisableButton .newFileBox.buttonRow.minus5.button
        APSDisableButton .newFileBox.buttonRow.plus5.button
        APSDisableButton .newFileBox.buttonRow.search.button
        APSDisableButton .newFileBox.buttonRow.delete.button
    }
}

proc userOption {} {
    global saveFlag
    if $saveFlag {
        exit
    } 
    if [catch {APSMultipleChoice [APSUniqueName .] \
                 -question "All new changes done in sddsEdit will not be saved. \
                    Do you really want to do it?" \
                 -labelList {Yes No} -returnList {Yes No}} userChoice] {
        APSSetVarAndUpdate ScrolledStatus "$userChoice"
    } else {
        switch $userChoice {
            Yes {
                exit
            }
            No {
                return
            }
        }
    }
}

proc saveFile {} {
    global validData inputFile
    if !$validData {
        APSSetVarAndUpdate ScrolledStatus "No data."
        return
    }
    saveFileAs -fileName $inputFile
}

#
# Allows saving the data into a named file.
#
proc saveFileAs {args} {
    set fileName ""
    APSStrictParseArguments {fileName}
    global validData optionTag numRows numPages
    global descriptionInfo workingDir inputFile
    global dataArray currentPage pageRowCount currentPageIndex
    global defaultFile

    set description ""
    set text ""
    set contents ""
    set tmpFile ""

    if !$validData {
        APSSetVarAndUpdate ScrolledStatus "No data."
        return
    }

    if ![string length $fileName] {
            set fileName [tk_getSaveFile -initialdir $workingDir -initialfile [file tail $inputFile] -parent .]
        if ![string length $fileName] {return} 
        set defaultFile $fileName
    } 
    set outputFile $fileName

    APSSetVarAndUpdate ScrolledStatus "Saving data..."
    set columnNum [llength $dataArray(ColumnNames)]
    APSSetVarAndUpdate ScrolledStatus "$numRows rows and $columnNum columns are present." 

    if [file exists $outputFile] {
        set isIndexedLink 0
        if {[string compare [file type $outputFile] link] == 0} {
            set linkFile [file readlink $outputFile]
            set directory [file dirname $linkFile]
            if {[string compare $directory .]==0} {
                set directory [file dirname $outputFile]
            }
            set name [file tail $linkFile] 
            set linkname $outputFile

            APSSetVarAndUpdate ScrolledStatus "$linkname is a link to $linkFile file."

            if {[string last . $name] > [string last - $name]} {
                set separator "."
            } else {
                set separator "-"
            }
            if [catch {APSNextGenerationedName -name $name -separator $separator \
                         -newFile 1 -directory $directory} genName] {
                #Not an indexed link
                set outputFile [APSResolveLink $outputFile]
                set inputFile $outputFile
            } else {
                set isIndexedLink 1
                set outputFile $directory/$genName
                APSSetVarAndUpdate ScrolledStatus "The new generationed file \
		    $directory/$genName was created."
            }
        } 
        if {$isIndexedLink == 0} {
            global warnOnOverwrite requireBackup
            if {($warnOnOverwrite == 1) && ([string compare $inputFile $outputFile]==0)} {
                bell
                set choice [tk_messageBox -title "Overwrite Requested" -message "Are you sure you which to overwrite the previous \"$outputFile\" file?" -type yesno]
                if {$choice == "no"} {
                    APSSetVarAndUpdate ScrolledStatus "\nSaving of \"$outputFile\" file aborted."
                    return
                }
                if {$requireBackup} {
                    set i 0
                    while {[file exists ${inputFile}.bak[format %03d $i]]} {
                        incr i
                    }
                    file rename ${inputFile} ${inputFile}.bak[format %03d $i]
                }
                APSSetVarAndUpdate ScrolledStatus "\nOverwriting \"$outputFile\" file."
            }
            if {!$requireBackup} {
                set tmpFile [APSTmpDir]/[APSTmpString]
                file copy $outputFile $tmpFile
            }
        }
    }
    set dir [file dirname $outputFile]
    set file [file rootname $outputFile]
    if {![string length $dir] || [string compare $dir .]==0} {
        set dir $workingDir
    }

    if [catch {SetDataArrayForOutput} result] {
        APSSetVarAndUpdate ScrolledStatus "Error setting dataArray : $result"
        bell
        return
    }
    
    if [catch {sdds save $outputFile dataArray} result] {
        APSSetVarAndUpdate ScrolledStatus "Error saving data : $result"
        bell
        return
    }
    if [file exists $tmpFile] {
        if [file exists ${outputFile}~] {
            file delete -force ${outputFile}~
        }

        file copy $tmpFile ${outputFile}~
        file delete -force $tmpFile
    }

    if {[string compare [file type $fileName] link] == 0} {
        if {[file exists $linkname] && [catch {file delete -- $linkname} result]} {
            APSSetVarAndUpdate ScrolledStatus "Unable to remove existing $linkname: $result"
        }
        if [file exists $outputFile] {
            if [catch {exec ln -s $outputFile $linkname} result] {
                APSSetVarAndUpdate ScrolledStatus "$result"
                return
            }
        }
        APSSetVarAndUpdate ScrolledStatus "Data saved to $linkname"
        set inputFile $linkname
    } else {
        APSSetVarAndUpdate ScrolledStatus "Data saved to $outputFile"
        set inputFile $outputFile
    }

}

proc ExportFile {} {
    global validData ScrolledStatus dataArray
    set tmpFile [APSTmpDir]/[APSTmpString].sddsEdit

    if !$validData {
        APSSetVarAndUpdate ScrolledStatus "No data."
        return
    }
    if [catch {SetDataArrayForOutput} result] {
        APSSetVarAndUpdate ScrolledStatus "Error setting dataArray : $result"
        bell
        return
    }

    if [catch {sdds save $tmpFile dataArray} result] {
        APSSetVarAndUpdate ScrolledStatus "Error saving data : $result"
        bell
        return
    }
    eval exec sddsExportData -dataFileList $tmpFile &
}

proc SetDataArrayForOutput {} {
    global dataArray ScrolledStatus currentPage numPages numRows
    global pageRowCount currentPageIndex
    set parameterNum [llength $dataArray(ParameterNames)]
    set columnNum [llength $dataArray(ColumnNames)]

    if $parameterNum {
        for {set p 0} {$p < $parameterNum} {incr p} {
            set parameter [.parameterPane.parameters.t get $p,0]
            set parValue [.parameterPane.parameters.t get $p,1]
            array set infoArray $dataArray(ParameterInfo.$parameter)
            if {($infoArray(type) != "SDDS_STRING") && ($infoArray(type) != "SDDS_CHARACTER")} {
                if {[llength $parValue] != 1} {
                    set zeroParFlag 0
                    if [catch {DisplayMessage parameter $parameter $p} zeroParFlag] {
                        APSSetVarAndUpdate ScrolledStatus $zeroParFlag
                        return
                    } else {
                        if $zeroParFlag {
                            APSSetVarAndUpdate ScrolledStatus "\nSaving of the file is canceled."
                            bell
                            return			    
                        }
                        set parValue 0
                    }
                }
            }
            if {([llength $infoArray(fixed_value)]) && ($infoArray(fixed_value) != $parValue)} {
                set infoArray(fixed_value) ""
                set dataArray(ParameterInfo.$parameter) [array get infoArray]
            }
            set dataArray(Parameter.$parameter) [lreplace $dataArray(Parameter.$parameter) $currentPageIndex $currentPageIndex $parValue]
        }
    }

    if $columnNum {
        for {set c 1} {$c <= $columnNum} {incr c} {
            set column [.columnPane.columns.t get 0,$c]
            set data ""
            array set infoArray $dataArray(ColumnInfo.$column)
            if {($infoArray(type) != "SDDS_STRING") && ($infoArray(type) != "SDDS_CHARACTER")} {
                for {set row 1} {$row <= $numRows} {incr row} {
                    set colValue [.columnPane.columns.t get $row,$c]
                    if {[llength $colValue] != 1} {
                        set zeroColFlag 0
                        if [catch {DisplayMessage column $column $row} zeroColFlag] {
                            APSSetVarAndUpdate ScrolledStatus "$zeroColFlag"
                            return
                        } else {
                            if $zeroColFlag {
                                APSSetVarAndUpdate ScrolledStatus "\nSaving of the file is canceled."
                                bell
                                return
                            }
                        }
                        set colValue 0
                    }
                    lappend data $colValue
                }
            } else {
                for {set row 1} {$row <= $numRows} {incr row} {
                    set colValue [.columnPane.columns.t get $row,$c]
                    lappend data $colValue
                }
            }
            set dataArray(Column.$column) [lreplace $dataArray(Column.$column) $currentPageIndex $currentPageIndex $data]
        }
    }
}

proc DisplayMessage {entity name row} {
    bell
    set userChoice [APSMultipleChoice [APSUniqueName .] \
                      -question "The $entity \"${name}\" cell in\nrow \#$row is not valid.\
                   \nIf you want to assign zero to the\ncell - choose \"SetZero\".\
                   \nIf you want to cancel the saving of the\nfile - choose \"Cancel\"." \
                      -labelList {SetZero Cancel} -returnList {0 1}]
    return $userChoice
}

proc DisplayMessage2 {entity name row} {
    bell
    set userChoice [APSMultipleChoice [APSUniqueName .] \
                      -question "The $entity \"${name}\" cell in\nrow \#$row is not valid.\
                   \nIf you want to assign zero to the\ncell - choose \"SetZero\".\
                   \nIf you want to cancel the switch page\noperation - choose \"Cancel\"." \
                      -labelList {SetZero Cancel} -returnList {0 1}]
    return $userChoice
}

#
# MakeGuidelines --
#
# This procedure provides an information display for a user.
#

proc MakeGuidelines {} {
    foreach window {.infowind .guide} {
        if [winfo exists $window] {
            destroy $window
        }
    }
    toplevel .guide
    APSScrolledText .guideText -parent .guide -width 80 -height 40 \
      -name "Guidelines" -contextHelp "This display provides an information\
         assistance for a user about basic function for the spreadsheet."

    pack [ttk::frame .guide.f -relief flat] -side bottom -anchor c
    APSButton .button1 -parent .guide.f -packOption "-side left" \
      -text "Dismiss" -command "destroy .guide" -contextHelp ""
    .guide.f.button1.button configure -width 10
    APSButton .button2 -parent .guide.f -packOption "-side right" \
      -text "Top" -command ".guide.guideText.text yview moveto -1.00" \
      -contextHelp ""
    .guide.f.button2.button configure -width 10
    wm title .guide "Spreadsheet Guidelines"

    .guide.guideText.text insert end "Table of Contents:\n" title

    .guide.guideText.text insert end \
      "\n                  1. Spreadsheet functions." {subtitle sub1}
    .guide.guideText.text insert end \
      "\n                     - activate, move active cell." {subtitle sub11}
    .guide.guideText.text insert end \
      "\n                     - inside the active cell." {subtitle sub12}
    .guide.guideText.text insert end \
      "\n                     - home & end functions." {subtitle sub13}
    .guide.guideText.text insert end \
      "\n                     - scroll a table by page." {subtitle sub14}
    .guide.guideText.text insert end \
      "\n                     - select, cut, copy, paste, undo." {subtitle sub15}
    .guide.guideText.text insert end \
      "\n                     - resize width of a column." {subtitle sub16} 
    .guide.guideText.text insert end \
      "\n                  2. Using the entry box for editing" {subtitle sub2}
    .guide.guideText.text insert end \
      "\n                  3. Search" {subtitle sub3}
    .guide.guideText.text insert end \
      "\n                     - parameter, parameter data" {subtitle sub31} 
    .guide.guideText.text insert end \
      "\n                     - column data" {subtitle sub32} 
    .guide.guideText.text insert end \
      "\n                  4. Parameters - attributes, insert, delete, info." {subtitle sub4}
    .guide.guideText.text insert end \
      "\n                  5. Columns - attributes, insert, delete, info." {subtitle sub5}
    .guide.guideText.text insert end \
      "\n                  6. Rows - insert, delete." {subtitle sub6}

    .guide.guideText.text insert end \
      "\n                  7. Saving the file - save, saveAs." {subtitle sub7}

    .guide.guideText.text insert end \
      "\n                  8. Edit command" {subtitle sub8}

    .guide.guideText.text insert end "\n\n1. Spreadsheet functions.\n" {start st1}
    .guide.guideText.text insert end "\n     Activate a cell " italic 
    .guide.guideText.text insert end "- click Button-1 in a cell; clicking into\
         already active cell moves                the insertion cursor to the\
         character nearest the mouse.\n"

    .guide.guideText.text insert end "\n     Moving the active cell " italic 
    .guide.guideText.text insert end "- use the left, right, up and down arrows; or\
         use the buttons                     in the toolbar (<-, ->, Down, Up).\n"

    .guide.guideText.text insert end "\n     Backspace key " italic 
    .guide.guideText.text insert end "- deletes the character before the insertion cursor\
         in the active                 cell.\n"

    .guide.guideText.text insert end "\n     Delete key " italic 
    .guide.guideText.text insert end "- deletes the character after the insertion cursor\
         in the active cell.\n"

    .guide.guideText.text insert end "\n     Control-a key " italic 
    .guide.guideText.text insert end "- moves the insertion cursor to the beginning\
         of the active cell.\n"

    .guide.guideText.text insert end "\n     Control-e key " italic 
    .guide.guideText.text insert end "- moves the insertion cursor to the end\
         of the active cell.\n"

    .guide.guideText.text insert end "\n     Control-leftarrow & Control-rightarrow " italic 
    .guide.guideText.text insert end "- moves the insertion cursor within the cell.\n"

    .guide.guideText.text insert end "\n     Home (key or button) " italic 
    .guide.guideText.text insert end "- moves the table to have the origin in view.\n"

    .guide.guideText.text insert end "\n     End (key or button) " italic 
    .guide.guideText.text insert end "- moves the table to have the end cell in view.\n"

    .guide.guideText.text insert end "\n     Control-Home key " italic 
    .guide.guideText.text insert end "- moves the table to have the origin and activates\
         that cell.\n"

    .guide.guideText.text insert end "\n     Control-End key " italic 
    .guide.guideText.text insert end "- moves the table to have the end and activates\
         that cell.\n"

    .guide.guideText.text insert end "\n     Scrolling the table by page " italic 
    .guide.guideText.text insert end "- (<<- ->>) scrolls the table in horizontal\
          direction;                 (PgDown PgUp) as well as (Page Down and Page Up)\
          buttons on the                 keyboard scrolls the table in vertical direction.\n"

    .guide.guideText.text insert end "\n     Select cells " italic 
    .guide.guideText.text insert end "- moving slowly the mouse while Button-1 is pressed\
         or using                      Shift-<arrow> key will stroke out a selection area.\
         A selection of              whole column (row) can be done by activating\
         a title cell of the                column (row).\n"

    .guide.guideText.text insert end "\n     Cut " italic 
    .guide.guideText.text insert end "- deletes the indices from all selected cells and\
         assigns them into the           undoArray (temporary memory) for \"Undo\" purpose;\
         use button in the              toolbar or key on the keyboard.\n"

    .guide.guideText.text insert end "\n     Copy " italic 
    .guide.guideText.text insert end "- sets the indices from all selected cells into the\
         copyArray (temporary           memory) for \"Paste\" purpose; use button in the\
         toolbar or key on the            keyboard.\n"
    
    .guide.guideText.text insert end "\n     Paste " italic 
    .guide.guideText.text insert end "- sets values from the copyArray: -if copyArray\
         contains just one                  element, Paste sets that element into an active\
         cell (if the active             cell is in a column title row, Paste sets the single\
         value to all               cells from the column); -if copyArray contains more then\
         one element,           Paste sets those elements starting from the active cell in\
         the shape            they were copied from; use button in the toolbar or key on\
         the                  keyboard.\n"

    .guide.guideText.text insert end "\n     Undo " italic 
    .guide.guideText.text insert end "- sets all selected cells into their previous\
         value (if they were changed         in the latest operation) just one step\
         back; use button in the toolbar          or key on the keyboard.\n"

    .guide.guideText.text insert end "\n     Resize width of a column " italic 
    .guide.guideText.text insert end "- activate any cell in the column of interest;\
         use                      Control-minus and Control-equals to decrease and\
         increase the                   width; or move the mouse cursor over a border,\
         press Button-3 and               move the mouse.\n"  

    .guide.guideText.text insert end "\n\n2. Using the entry box for editing\n" {start st2}
    .guide.guideText.text insert end "\n        The entry box is synchronized\
         with an active cell. As soon as any               cell from the spreadsheet\
         is activated its value is transferred in to           the box and its index\
         displayed. User may do editing in the active cell         or in the entry box.\
         An editing in the cell is reflected right away in          the box. The entry\
         box has associated two push buttons. \"C\" for Clear           does empty\
	 the inputs. \"E\" for Enter transfers input into the active            cell.\
         Each of above procedures could be reversed by \"Undo\" function. \n"

    .guide.guideText.text insert end "\n\n3. Search\n" {start st3}

    .guide.guideText.text insert end "\n     Parameter, parameter data " italic
    .guide.guideText.text insert end "- in the case of a need to find a parameter or\
         parameter           data in the ocean of a full table go to the \"Search\"\
         button in the              menu bar and look for a \"Parameter\"; when \"Parameter\"\
         is chosen a new           dialog window will be displayed with options to look for\
         a parameter            or parameter data. Choose one of radio buttons and\
         insert a matching            pattern into the corresponding entry widget\
         (all wildcards may be               used). If no parameter is present in the\
         parameter table, the search            will stop and a message will be displayed.\
         Otherwise, three                     situations may happen: i)no match found - an\
         appropriate information            will be displayed in the status window,\
         ii)one match found -                    corresponding cell will be activated and\
         parameter table will scroll            to that cell, iii)multiple match found\
         - a scrolled list will pop up            in a new window, a parameter table\
         location cell will assist                    each of the found elements.\
         A number of matches will be shown on the            bottom. Double click above\
         an element you want to see. The match list           will allow to look through\
         each element. To close the display of the            list click \"Close\".\n"

    .guide.guideText.text insert end "\n     Column data " italic
    .guide.guideText.text insert end "- in the case of a need to find a column element\
         go to the \"Search\"           button in the menu bar and look for a \"Column\";\
         when \"Column\" is                chosen a new dialog window will be displayed\
         with all column names              from a spreadsheet. Choose one of radio\
         buttons and insert a matching           pattern into the corresponding entry\
         widget (all wild cards may be              used). If no column is present in the\
         column table or the table does            not posses any row of data, the\
         search will stop and a message will be          displayed. Otherwise, three\
         situations may happen: i)no match found -           an appropriate information\
         will be displayed in the status window,              ii)one match found -\
         corresponding cell will be activated and column            table will scroll\
         until an active cell is visible, iii)multiple                 match found - a\
         scrolled list will pop up in a new window, a column             table location\
         cell will assist each of the found elements. A number            of matches will\
         be shown on the bottom. This display provides a few             functions: \
         \n            1.Double click above an element you want to see. The column table\
         \n              will scroll to display selected match. The match list will allow\
         \n              to look through each element.\
         \n            2.Multiple selection in the match list and the \"Select\" push button\
         \n              invoked. This action provides a selection of the cells in the\
         \n              column table corresponding to those selected in the match list.\
         \n              The column table will not scroll.\
         \n            3.Multiple selection in the match list and the \"Delete_Row\" push\
         \n              button invoked. This action invokes a deletion of rows which\
         \n              contain the selected matches from the match list. The match list\
         \n              will be updated after each row deletion from the column table.\
         \n          To close the display of the list click \"Close\".\n"

    .guide.guideText.text insert end "\n\n4. Parameters - attributes, insert, delete, info.\n" {start st4}

    .guide.guideText.text insert end "\n     Attributes " italic
    .guide.guideText.text insert end "- in order to change attributes of existing parameter\
         go to the \"Edit\"          button in the menu bar and look for a parameter's\
         submenu;\n         \"Attributes\" invokes a display of \"Info_Edit\" form; the user may\
	 \n         change characteristic of the parameter; all changes will be applied\
         \n         when \"OK\" is activated; this feature is not available for protected\
         \n         parameters (to look at characteristic of protected parameter the user\
         \n         should refer to the \"Info\" in the menu bar section).\n"

    .guide.guideText.text insert end "\n     Insert " italic
    .guide.guideText.text insert end "- in order to add a new parameter go to\
         the \"Edit\" button in the menu bar          and look for a parameter's\
         submenu; \"Insert\" invokes a display of \"New          Parameter\" form\
         (for help with the form go to \"Help\" in the menu bar,           pick\
         contextHelp, move the mouse cursor over each entry and click);            \
	 \"OK\" button activates a setup of the new parameter at the end of\
         the            table (if the end of the parameter table was not visible,\
         it scrolls            the table up to the last parameter).\n"
    
    .guide.guideText.text insert end "\n     Delete " italic
    .guide.guideText.text insert end "- in order to delete a parameter go to\
         a parameter table, select the              parameter, go to\
         the \"Edit\" button in the menu bar and look for a               parameter's\
         submenu; \"Delete\" invokes a display of a \"Confirmation\"             message\
         (if answered \"Yes\" the parameter will be deleted, if \"No\" a            \
         deleting process will be canceled).\n"
    
    .guide.guideText.text insert end "\n     Info " italic
    .guide.guideText.text insert end "- to find a characteristic of a selected parameter\
         go to the \"Info\" button         in the menu bar and look for a \"Parameter\"\
         button; it will activate             a display of the \"Info\" window with all\
         attributes relative to the              selected parameter.\n "

    .guide.guideText.text insert end "\n\n5. Columns - attributes, insert, delete, info.\n" {start st5}

    .guide.guideText.text insert end "\n     Attributes " italic
    .guide.guideText.text insert end "- in order to change attributes of existing column\
         go to the \"Edit\"            button in the menu bar and look for a column's\
         submenu; \"Attributes\"            invokes a display of \"Info_Edit\" form; the user may\
	 change\n        characteristic of the column; all changes will be applied when \"OK\"\
	 \n        is activated; this feature is not available for protected columns (to\
         \n        look at characteristic of protected column the user should refer to the\
         \n        \"Info\" in the menu bar section).\n"


    .guide.guideText.text insert end "\n     Insert " italic
    .guide.guideText.text insert end "- in order to add a new column select a one\
         which is going to be prior to          the input in the column table; next\
         go to the \"Edit\" button in the              menu bar and look for a column's\
         submenu; \"Insert\" invokes a display            of \"New Column\" form\
         (for help with the form go to \"Help\" in the                menu bar, pick\
         contextHelp, move the mouse cursor over each entry and           click);\
	 \"OK\" button activates a setup of the new column.\n"
    
    .guide.guideText.text insert end "\n     Delete " italic
    .guide.guideText.text insert end "- in order to delete a column select one, go to\
         the \"Edit\" button in the          menu bar and look for a column's\
         submenu; \"Delete\" invokes a                    display of a \"Confirmation\"\
         message (if answered \"Yes\" the column will          be deleted, if \"No\" a\
         deleting process will be canceled).                       Remember, \"Undo\"\
         function will not reread the deleted column. The               deletion is a final\
         process.\n"
    
    .guide.guideText.text insert end "\n     Info " italic
    .guide.guideText.text insert end "- to find a characteristic of a selected column\
         go to the \"Info\" button            in the menu bar and look for a \"Column\"\
         button; it will activate                a display of the \"Info\" window with all\
         attributes relative to the              selected column.\n "

    .guide.guideText.text insert end "\n\n6. Rows - insert, delete.\n" {start st6}

    .guide.guideText.text insert end "\n     Insert " italic
    .guide.guideText.text insert end "- in order to add a new row in a column table\
         select a one which is going          to be prior to the input; next\
         go to the \"Edit\" button in the menu              bar and look for a row's\
         submenu; \"Insert\" invokes a display of an              input box for a number\
         of new rows to set; \"OK\" button activates a              setup of the row with a correct\
         row number in the order.\n"
    
    .guide.guideText.text insert end "\n     Delete " italic
    .guide.guideText.text insert end "- in order to delete a row select one or many\
         in the column table; next           go to the \"Edit\" button in the menu bar\
         and look for a row's submenu;           \"Delete\" invokes a display of a\
         \"Confirmation\" message (if answered             \"Yes\" the row will be\
         deleted, if \"No\" a deleting process will be               canceled). Remember, \"Undo\"\
         function will not reread the deleted                row(s). The deletion is a final\
         process.\n"

    .guide.guideText.text insert end "\n\n7. Saving the file.\n" {start st7}
    .guide.guideText.text insert end "\n         In general, there are two cases of saving the\
         edited file:\n"
    .guide.guideText.text insert end "\n     Saving " italic
    .guide.guideText.text insert end "- saving under the same name the file was\
         edited \n          \* initially the file was a regular sdds file - the program\
         will                   save all changes to the file, \n          \* initially the file\
         was a link to a regular sdds file - the                      program will save all\
         changes to that file, initially the file                  was a link to a generationed\
         sdds file - the program will create                a new generationed file\
         (next number in order), save the edited                 file there and reassign\
         the link to new file.\n"
    .guide.guideText.text insert end "\n     SavingAs " italic
    .guide.guideText.text insert end "- saving under a new name;\
         a window will pop-up with the                          input box to provide\
         a new name\n          \* if a new name of the file does not exists in the\
         specified                      directory - the edited file will be saved under\
         that name,\n          \* if a new name of the file is a name of already\
         existing file and                that file is a regular file - a window will\
         pop-up with a                       confirmation question for \"Overwrite\"\
         old file or \"Cancel\" the                  saving procedure,\n          \* if\
         a new name of the file is a name of already existing file and                that file\
         is a link - when the program detects that a new name is               a link\
         to not generationed file it will display a message in the                status\
         window and will stop saving. The user is always able                     to do another\
         saving,\n          \* if a new name of the file is a name of already existing\
         file and                that file is a link - when the program detects that a new\
         name is               a link to generationed file it will create a new\
         generationed                   file (next number in order), save the edited\
         file there and                     reassign the link to new file.\n"

    .guide.guideText.text insert end "\n\n8. Edit command.\n" {start st8}
    .guide.guideText.text insert end "\n  \
         This function applies to a column table only.                                  \
         The editing commands for these programs are composed\
         of a series of             subcommands of the form \[count\]commandLetter\[commandSpecificData\].\
         \n   As indicated, the count and commandSpecificData are optional.\ 
         \n\n   The commands are as follows:\ 

         \n   \[n\]f -- move forward 1 or n characters.\ 
         \n   \[n\]b -- move backward 1 or n characters.\ 
         \n   \[n\]d -- delete the next character or n characters.\ 
         \n   \[n\]F -- move forward 1 or n words.\ 
         \n   \[n\]B -- move backward 1 or n words.\ 
         \n   \[n\]D -- delete the next word or n words.\ 
         \n   a -- Go to the beginning of the string.\ 
         \n   e -- Go to the end of the string.\ 
         \n   \[n\]i-delim-text-delim- -- Insert text, delimited by the character -delim- 1          or n times.\
                      For example, ``i/thisString/'' would insert ``thisString''          once.\ 
         \n   \[n\]s-delim-text-delim- -- Search for text, delimited by the character                -delim- 1\
                    or n times. The position is left at the end of the search             string. -delim-\
                    may be any character except a question mark.\ 
         \n   S-delim-text-delim- -- Search for text, delimited by the character -delim-,         \
                    leaving the position at the start of the search string. -delim- may be         \
                    any nonspace character except a question mark.\ 
         \n   \[n\]s?-delim-text-delim- -- Search for text, delimited by the character              \
                    -delim- 1 or n times. Abort all subsequent editing if the search fails.        \
		    If the search suceeds, leave the position at the end of the search             \
                    string. -delim- may be any nonspace character except a question mark.\ 
         \n   S?-delim-text-delim- -- Search for text, delimited by the character -delim-.        \
                    Abort all subsequent editing if the search fails. If the search                \
                    suceeds, leave the position at the start of the search string. -delim-         \
                    may be any nonspace character except a question mark.\ 
         \n   \[n\]k -- Delete forward from the present position 1 or n characters, placing         \
                    them in the kill buffer.\ 
         \n   \[n\]K -- Delete forward from the present position 1 or n words, placing them         \
                    in the kill buffer.\ 
         \n   zchar -- Delete forward from the present position up to the first occurence         \
                    of the character char, placing the deleted text in the kill buffer.\ 
         \n   \[n\]Zchar -- Delete 1 or n times up to and including the character char,             \
                    placing the deleted text in the kill buffer.\ 
         \n   \[n\]y -- Yank the kill buffer into the string 1 or n times.\ 
         \n   \[n\]%-delim-text1-delim-text2-delim- -- Replace text1 with text2 1 or n times        \
                    starting at the present position. -delim- may be any nonspace                  \
                    character. For example, ``10%/c/C/'' would capitalize the next 10              \
                    occurences of the character 'c'." 
    
    ## Setting a style of fonts.
    .guide.guideText.text tag configure title \
      -font -*-times-bold-r-normal-*-20-120-*
    .guide.guideText.text tag configure subtitle \
      -font -*-times-medium-r-normal-*-18-120-*
    .guide.guideText.text tag configure start \
      -font -*-times-bold-r-normal-*-14-120-*
    .guide.guideText.text tag configure italic \
      -font -*-times-medium-i-normal-*-16-120-*

    .guide.guideText.text configure -state disabled

    MakeGuideBindings
}

proc MakeGuideBindings {} {
    set w .guide.guideText.text
    
    $w tag bind subtitle <Enter> "ChangeMousePointer $w hand2"
    $w tag bind subtitle <Leave> "ChangeMousePointer $w xterm" 

    $w tag bind sub1 <Button-1>  {.guide.guideText.text yview moveto .07}    
    $w tag bind sub11 <Button-1>  {.guide.guideText.text yview moveto .075}    
    $w tag bind sub12 <Button-1>  {.guide.guideText.text yview moveto .10}    
    $w tag bind sub13 <Button-1>  {.guide.guideText.text yview moveto .135}    
    $w tag bind sub14 <Button-1>  {.guide.guideText.text yview moveto .160}    
    $w tag bind sub15 <Button-1>  {.guide.guideText.text yview moveto .165}    
    $w tag bind sub16 <Button-1>  {.guide.guideText.text yview moveto .25}
    $w tag bind sub2 <Button-1>  {.guide.guideText.text yview moveto .27}      
    $w tag bind sub3 <Button-1>  {.guide.guideText.text yview moveto .30}      
    $w tag bind sub31 <Button-1>  {.guide.guideText.text yview moveto .30}      
    $w tag bind sub32 <Button-1>  {.guide.guideText.text yview moveto .36}      
    $w tag bind sub4 <Button-1>  {.guide.guideText.text yview moveto .445}      
    $w tag bind sub5 <Button-1>  {.guide.guideText.text yview moveto .535}      
    $w tag bind sub6 <Button-1>  {.guide.guideText.text yview moveto .63}      
    $w tag bind sub7 <Button-1>  {.guide.guideText.text yview moveto .68}      
    $w tag bind sub8 <Button-1>  {.guide.guideText.text yview moveto .78}      
}

#
# DisplaySearchDialogBox--
#
# This procedure invokes a display of dialog box which
# reads a search pattern and starts a search process by
# pressing \"OK\" button or cancels a search process by
# pressing \"Cancel\" button.
#
# Attributes:
# entity - parameter or column 
#

proc DisplaySearchDialogBox {entity} {
    global searchValue variableList valueList
    global dataArray
    set buttonList ""
    set valueList ""
    set commandList ""
    set variableList ""
    
    foreach win {.matchW .searchBox} {
        if [winfo exist $win] {
            destroy $win
        }
    }
    
    if [string compare $entity parameter]==0 {
        set parameterNum [llength $dataArray(ParameterNames)]
        if !$parameterNum {
            APSSetVarAndUpdate ScrolledStatus "\nNo parameter exists."
            bell
            return
        }
        foreach e $dataArray(ParameterNames) {
            lappend buttonList $e
            lappend valueList $e
            lappend variableList V$e
            lappend commandList "focus .searchBox.userFrame.frame1.frame.e$e"
        }
    }
    if [string compare $entity column]==0 {
        set columnNum [llength $dataArray(ColumnNames)]
        if !$columnNum {
            APSSetVarAndUpdate ScrolledStatus "\nNo column exists."
            bell
            return
        }
        foreach col $dataArray(ColumnNames) {
            lappend buttonList $col
            lappend valueList $col
            lappend variableList V$col
            lappend commandList "focus .searchBox.userFrame.frame1.frame.e$col"
        }
    }
    
    APSWindow .searchBox -name "sddsEdit:search" \
      -contextHelp "Dialog box for sddsEdit search of $entity." \
      -closeButton 0
    #     -okCommand "PerformSearch $entity" \
      #     -cancelCommand "APSSetVarAndUpdate ScrolledStatus {Search was canceled.}; return"
    pack [ttk::frame .searchBox.userFrame.labelFrame -relief flat] \
      -fill x -expand true
    APSDialogBoxAddButton .all -parent .searchBox -text "Match All" \
      -command "PerformSearch $entity 1; destroy .searchBox"
    APSDialogBoxAddButton .any -parent .searchBox -text "Match Any" \
      -command "PerformSearch $entity 0; destroy .searchBox"
    APSDialogBoxAddButton .no -parent .searchBox -text "Cancel" \
      -command "APSSetVarAndUpdate ScrolledStatus {Search was canceled.}; destroy .searchBox"
    set uf .searchBox.userFrame
    #set searchValue [lindex $valueList 0]
    
    APSCheckButtonFrame .radioBut -parent $uf -packOption "-side left" \
      -buttonList $buttonList -variableList $valueList \
      -orientation vertical -label "" -commandList $commandList \
      -relief flat -contextHelp "The set of search categories."

    APSFrame .frame1 -parent $uf -packOption "-side right -fill both -expand true" \
      -orientation vertical -relief flat -contextHelp ""
    set w $uf.frame1.frame
    
    foreach element $valueList elem $variableList {
        global $element $elem
        if ![info exists $elem] {
            set $elem ""
        }
        pack [ttk::entry $w.e$element -width 20 -textvariable $elem] \
          -side top -fill x
    }
    #focus .searchBox.userFrame.frame1.frame.e$searchValue
}

#
# PerformSearch--
# 
# This procedure looks for a matches in a selected table and
# depending on the outcome shows the result.
#
# Attributes:
# entity - parameter or column
#

proc PerformSearch {entity match} {
    global numRows variableList searchValue currentPage previousCurrentPage
    global dataArray editCommand valueList
    global matchEntity matchItemList
    set matchEntity $entity
    
    set searchName ""
    set searchValue ""
    APSSetVarAndUpdate ScrolledStatus "Searching $entity..."
    foreach elem $valueList element $variableList {
        global $elem $element
        if [set $elem] {
            lappend searchName $elem
            lappend searchValue $element
            lappend foundpage P$elem
            set P$elem ""
        }
    }
    if {$searchName==""} {
        bell
        APSSetVarAndUpdate ScrolledStatus "No search name is checked!"
        return
    }
    set matchall ""
    if [string compare $entity parameter]==0 {
        set parameterNum [llength $dataArray(ParameterNames)]
        if !$parameterNum {
            APSSetVarAndUpdate ScrolledStatus "No parameters for search!"
            return
        }
        set foundAllPage ""
        set foundAnyPage ""
        set foundAnyName ""
        set foundAnyValue ""
        set name [lindex $searchName 0]
        set pages [llength $dataArray(Parameter.$name)]
        for {set p 0} {$p<$pages} {incr p} {
            set foundall 1
            set page [expr $p+1]
            foreach name $searchName value $searchValue {
                set val [lindex $dataArray(Parameter.$name) $p]
                set i [lsearch -exact $dataArray(ParameterNames) $name]
                global $value
                if ![string match *[set $value]* $val] {
                    set foundall 0
                } else {
                    lappend foundAnyPage "$page"
                    lappend foundAnyName $name
                    lappend foundAnyValue $val
                }
            }
            if $foundall {
                lappend foundAllPage "$page"
            }
        }
        #match==1: match all the searched items; match==0, match any researched item
        if $match {  
            set size [llength $foundAllPage]
        } else {
            set size [llength $foundAnyPage]
        }
        if !$size {
            APSSetVarAndUpdate ScrolledStatus "No matches found!"
            bell
            return
        }
        
        if {$size==1} {
            if $match {
                set page [lindex $foundAllPage 0]
                set p [expr $page -1]
                foreach name $searchName {
                    set value [lindex $dataArray(Parameter.$name) $p] 
                    set matchall ${matchall}${name}=${value},
                }
            } else {
                set page [lindex $foundAnyPage 0]
                set name [lindex $foundAnyValue 0]
                set matchall ${name}=${value}
            }
            APSSetVarAndUpdate ScrolledStatus "$matchall is found at page $page"
            set previousCurrentPage $currentPage
            set currentPage $page
            if {$previousCurrentPage==$currentPage} {
                return
            }
            ChangePage
            set previousCurrentPage $currentPage
            return
        }
        #for size>1, display the matchList
        set matchItemList ""
        if $match {
            set matchall ""
            foreach page $foundAllPage {
                foreach name $searchName {
                    set p [expr $page -1]
                    set val [lindex $dataArray(Parameter.$name) $p]
                    set matchall ${matchall}${name}=${val},
                }
                lappend matchItemList "$matchall@$page"
                set matchall ""
            }
        } else {
            foreach name $foundAnyName val $foundAnyValue page $foundAnyPage {
                lappend matchItemList "${name}=${val}@${page}"
            }
        }
    }
    if [string compare $entity column]==0 {
        if !$numRows {
            APSSetVarAndUpdate ScrolledStatus "\nNo data in the $entity." 
            bell
            return
        }
        set columnNum [llength $dataArray(ColumnNames)]
        set foundAnyRows ""
        set foundAllRows ""
        set foundValue ""
        #j is the row number, i is the column number
        for {set j 1} {$j <= $numRows} {incr j} {
            set foundall 1
            foreach name $searchName value $searchValue {
                global $value
                set index [lsearch -exact $dataArray(ColumnNames) $name]
                if {$index==-1} {
                    APSSetVarAndUpdate ScrolledStatus "$name is not an column name!"
                    bell
                    return
                }
                set i [expr $index +1]
                set cellValue [.columnPane.columns.t get $j,$i]
                if [string match *[set $value]* $cellValue] {
                    lappend foundValue "$name=$cellValue"
                    lappend foundAnyRows "$j,$i"
                } else {
                    set foundall 0
                }
            }
            if $foundall {
                # $i is the last matched column index
                lappend foundAllRows "$j,$i"
            }
        }
        if $match {
            #match all
            set size [llength $foundAllRows]
        } else {
            #match any
            set size [llength $foundAnyRows]
        }
        if !$size {
            APSSetVarAndUpdate ScrolledStatus "\nNo match found for a $entity."
            bell
            return
        } 
        if {$size==1} {
            if $match {
                #only the last matched cell is displayed
                ShowMatch $entity $foundAllRows
            } else {
                ShowMatch $entity $foundAnyRows 
            }
        } 
        set matchItemList ""
        if $match {
            foreach row $foundAllRows {
                set j [lindex [split $row ,] 0]
                set matchall ""
                foreach name $searchName {
                    set i [lsearch -exact $dataArray(ColumnNames) $name]
                    set i [expr $i+1]
                    set cellValue [.columnPane.columns.t get $j,$i]
                    set matchall ${matchall}${name}=${cellValue},
                }
                lappend matchItemList "$matchall@$row"
            }
        } else {
            foreach val $foundValue row $foundAnyRows {
                lappend matchItemList "$val@$row"
            }
        }
    }
    APSWindow .matchW -name "Match List" -contextHelp "This window\
         displays a list of matches for a search procedure. Double click\
         above an element you want to see and a $entity table will show\
         that element. You can look at each element from the list. \
         Use \"Close\" button to exit this window."
    pack [ttk::frame .matchW.userFrame.labelFrame -relief flat] \
      -fill x -expand true
    set f1 .matchW.userFrame.labelFrame
    if ![string compare $entity parameter] {
        set text "ParameterValue@page"
    } else {
        set text "Element@Cell(row,column)"
    }
    pack [ttk::label $f1.l -text $text -font TkHeadingFont] \
      -side top -anchor c
    APSScrolledList .matchL -parent .matchW.userFrame \
      -name "Match List" -itemList $matchItemList -callback MatchCallback \
      -selectMode extended -contextHelp "This scrolled list contains \
         a set of matches for a search procedure. The location cell \
         in a $entity table assists each element separated by \"@\" sign.\
         A number of matches found is displayed on the bottom. \
         Click once for a selection and next double click above an element\
         you want to see and a $entity\
         table will show that element. You can look at each element from\
         the list."
    pack [ttk::frame .matchW.userFrame.numberFrame -relief flat] \
      -fill x -expand true
    set f2 .matchW.userFrame.numberFrame
    pack [ttk::label $f2.l -text "Number of found elements: $size" -font TkHeadingFont] \
      -side top -anchor c
    if [string compare $entity parameter]==0 {
        APSDialogBoxAddButton .mSelect -parent .matchW -text "Goto selected page"\
          -command "GotoSelectedPage" -contextHelp ""
    }
    if [string compare $entity column]==0 {
        if ![info exists editCommand] {
            set editCommand ""
        }
        set selectAllCommand {.matchW.userFrame.matchL.listbox selection set 0 end}
        APSLabeledEntry .editComS -parent .matchW.userFrame -label "Edit Command: "\
          -textVariable editCommand -width 40 -contextHelp "" 
        APSDialogBoxAddButton .mSelect -parent .matchW -text "Select"\
          -command "MakeSelectionFromChosenCells select" -contextHelp ""
        APSDialogBoxAddButton .mSelectAll -parent .matchW -text "Select_All"\
          -command "$selectAllCommand; MakeSelectionFromChosenCells select" -contextHelp ""
        APSDialogBoxAddButton .mDelete -parent .matchW -text "Delete_Row"\
          -command "MakeSelectionFromChosenCells delete" -contextHelp ""
        APSDialogBoxAddButton .editComB -parent .matchW -text "Edit_Comm" \
          -command "MakeSelectionFromChosenCells select; DoEditString" -contextHelp \
          "This button invokes procedure with provided edit command."
    }
    APSSetVarAndUpdate ScrolledStatus "Search is completed."
}

#
# ShowMatch--
#
# This procedure activates a cell with a found match and
# scrolls a relative table to make that cell visible.
#
# Attributes:
# entity - parameter or column,
# cell - a position in the ralative table to be activated.
#

proc ShowMatch {entity cell} {
    global tkPriv numRows
    global dataArray

    set row [lindex [split $cell ,] 0]
    
    if {$entity == "parameter"} {
        set t .parameterPane.${entity}s.t
    } else {
        set t .columnPane.${entity}s.t
    }
    $t activate $cell
    focus $t
    $t selection clear all
    $t selection set active
    $t selection anchor active
    
    if [string compare $entity column]==0 {
        if {$numRows < 800} {set corrector 0.01}
        if {$numRows >= 800 && $numRows < 1000} {set corrector 0.007}
        if {$numRows >= 1000 && $numRows < 3000} {set corrector 0.005}
        if {$numRows >= 3000 && $numRows < 5000} {set corrector 0.002}
        if {$numRows >= 5000 && $numRows < 13000} {set corrector 0.001}
        if {$numRows >= 13000 && $numRows <= 30000} {set corrector 0.0005}
        if {$numRows > 30000} {set corrector 0.0001}
        set tkPriv(tablePrev) [$t index active]
        set tkPriv(tableIndex) ""
        $t see active
        set i [$t index active]
        set tkPriv(tableIndex) $i
        set step [expr (double(${row}.00000 / ${numRows}.00000) - $corrector)]
    } else {
        if {$row && [llength $dataArray(ParameterNames)]>1} {
            set step [expr ${row}.00 / ([llength $dataArray(ParameterNames)].00 - 1.00)]
        } else {
            set step 0
        }
    }
    
    $t yview moveto [expr ($step + (double(1 / $numRows)) * 10)]
    updateInputBox
    #    set element [$t get $cell]
    #    APSSetVarAndUpdate ScrolledStatus "\nElement \"$element\" found in the row $row of\
      #        the $entity table."
    bell 
}

#
# MatchCallback--
#
# This procedure is a call back invoked by double click
# in the match list display.
#
# Attributes:
# item - selected element from the match list,
# doubleClick - boolean value indicating a double click event.
#

proc MatchCallback {item doubleClick} {
    global matchEntity currentPage previousCurrentPage
    if $doubleClick {
        if {[string compare column $matchEntity]==0} {
            set items [split $item @]
            set match [lindex $items 0]
            set cell [lindex $items 1]
            ShowMatch $matchEntity $cell 
        } else {
            set items [split $item @]
            set match [lindex $items 0]
            set page [lindex $items 1]
            set previousCurrentPage $currentPage
            set currentPage $page
            if {$previousCurrentPage==$currentPage} {
                bell
                return
            }
            ChangePage
            set previousCurrentPage $currentPage
        }    
    }
}

proc LookForSearchBox {entity} {
    global matchEntity
    if [winfo exist .matchW] {
        if [string compare $entity $matchEntity]==0 {
            destroy .matchW
            PerformSearch $entity 1
        }
    }
}

proc MakeSelectionFromChosenCells {action} {
    global tkPriv matchItemList matchEntity
    set chosenList [.matchW.userFrame.matchL.listbox curselection]
    if [llength $chosenList] {
        set t .columnPane.columns.t
        focus $t
        $t selection clear all
        set tkPriv(tableIndex) ""
        set firstItem [lindex $matchItemList [lindex $chosenList 0]]
        set firstCell [lindex [split $firstItem @] 1]
        ShowMatch $matchEntity $firstCell 
        if [string compare $action select]==0 {
            foreach chosenItem $chosenList {
                set chosenCell [lindex $matchItemList $chosenItem]
                set cell [lindex [split $chosenCell @] 1]
                $t activate $cell
                $t selection set active
                $t selection anchor active
            }
            updateInputBox
        } else {
            foreach chosenItem $chosenList {
                set chosenCell [lindex $matchItemList $chosenItem]
                set cell [lindex [split $chosenCell @] 1]
                set row [lindex [split $cell ,] 0]
                $t selection set $row,0 $row,[$t index end col]
                $t activate $row,0
                $t selection anchor active
            }
            updateInputBox
            DeleteUserChoice row
        }
    }			
}
proc GotoSelectedPage {} {
    global matchItemList matchEntity currentPage previousCurrentPage
    set chosenList [.matchW.userFrame.matchL.listbox curselection]
    if [llength $chosenList] {
        set firstItem [lindex $matchItemList [lindex $chosenList 0]]
        set i [string first @ $firstItem]
        set previousCurrentPage $currentPage
        set currentPage [string range $firstItem [expr $i+1] [expr $i+1]]
        if {$previousCurrentPage==$currentPage} {
            bell
            return
        }
        ChangePage
        set previousCurrentPage $currentPage
        updateInputBox
    }
}
#
# This procedure allows the user to edit arbitrarily
# long strings from the column table cells. 
#
proc EditLargeEntryString {} {
    global cellInput

    if [winfo exists .editLargeString] {
        APSSetVarAndUpdate ScrolledStatus "Window \"Edit Large Entry String\" already exists."
        bell
        return
    }
    APSDialogBox .editLargeString -name "Edit Large Entry String" -width 100 \
      -contextHelp ""
    destroy .editLargeString.buttonRow.ok.button 
    destroy .editLargeString.buttonRow.cancel.button
    APSDialogBoxAddButton .dismiss -parent .editLargeString -text "Dismiss" \
      -command "destroy .editLargeString" -contextHelp ""
    APSScrolledText .largeText -parent .editLargeString.userFrame -width 100 \
      -height 20 -name LargeString -contextHelp ""

    set t .editLargeString.userFrame.largeText.text
    $t insert end $cellInput

    if [string compare [$t cget -state] normal]==0 {
        bindtags $t "Text . all $t"
        bind $t <KeyRelease> {
            set t .editLargeString.userFrame.largeText.text
            set cellInput [string trimright [$t get 1.0 end] \n]
            update
        }
        event add <<Paste>> <ButtonRelease-2>
        bind $t <<Paste>> {
            set t .editLargeString.userFrame.largeText.text
            set cellInput [string trimright [$t get 1.0 end] \n]
            update
        }
    }
}

proc EditString {} {
    global editCommand
    if ![info exists editCommand] {
        set editCommand ""
    }
    if [winfo exists .editString] {
        destroy .editString
    }
    set okCommand "DoEditString"
    APSDialogBox .editString -name "Edit String" -width 80 \
      -okCommand $okCommand \
      -contextHelp "This widget is a tool to do an automated string editing to all selected cells\
         in the column table only.\
         \nOK button invokes procedure with provided command and closes the dialog window.\
	 \nCancel button cancels the dialog window display."

    APSLabeledEntry .editCom -parent .editString.userFrame -label "Edit Command: "\
      -textVariable editCommand -width 80 -contextHelp "" 

    APSDialogBoxAddButton .apply -parent .editString -text "Apply" \
      -command "DoEditString" -contextHelp \
      "Apply button invokes procedure with provided command (the dialog window stays available)."
}

proc DoEditString {} {
    global editCommand
    set t .columnPane.columns.t
    set nonSelected 1
    foreach selCell [$t cursel] {
        if [$t tag includes disabled $selCell] {
            APSSetVarAndUpdate ScrolledStatus "Selected cell(s) are protected."
            bell
            return
        }
        set nonSelected 0
        set string [$t get $selCell]
        if {[string length $string ] < 900} {
            if [catch {eval os editstring {$editCommand} {$string}} results] {
                APSSetVarAndUpdate ScrolledStatus "DoEditString: $results."
                bell
                return
            } else {
                $t set $selCell [string trim $results \{\}]
            }
        } else {
            if [catch {exec editstring -editcommand=$editCommand $string} results] {
                APSSetVarAndUpdate ScrolledStatus "DoEditString: $results."
                bell
                return
            } else {
                $t set $selCell [string trim $results \{\}]
            }
        }
    }
    if {$nonSelected} {
        APSSetVarAndUpdate ScrolledStatus "No cells are selected"
        bell
    }
}

proc EliminateMajorWidgets {} {
    foreach window {.newFileBox .newEntityBox .infowind .guide .import\
                      .import.userFrame.columns.entries} {
        if [winfo exists $window] {
            destroy $window
        }
    }
    set ww .columnPane.toolBar.frame
    set p .columnPane.entryFrame 
    foreach widget [list .columnPane.toolBar $ww.copy $ww.paste \
                      $ww.cut $ww.undo $ww.moveL $ww.moveR \
                      $ww.moveD $ww.moveU $ww.moveLPage $ww.moveRPage\
                      $ww.moveDPage $ww.moveUPage $ww.moveH $ww.moveE \
                      .parameterPane.parTop .parameterPane.parameters .columnPane.colLabel .columnPane.columns\
                      $p $p.cellIndex $p.buttonC $p.buttonE $p.cellInput .editString .searchBox .parameterPane .columnPane .userFrame.pane] {
        if [winfo exists $widget] {
            destroy $widget
        }
    }
}

proc ImportFile {} {
    global fileType importFile apsContextHelp

    EliminateMajorWidgets
    foreach butt [list .menu.edit .menu.search .menu.editCom .menu.info] {
        $butt configure -state disabled 
    }

    set okCommand ProcessImport
    set cancelCommand [list APSSetVarAndUpdate ScrolledStatus "Importing file is canceled."]
    APSSetVarAndUpdate ScrolledStatus "Importing file..."
    APSDialogBox .import -name "Import File" -width 80 \
      -okCommand $okCommand -cancelCommand $cancelCommand\
      -contextHelp "This widget is a tool to transfer text or spreadsheet data files to\
           sdds files\
         \nOK button invokes procedure with provided command and closes the dialog window.\
	 \nCancel button cancels the dialog window display."

    set fileType Text
    set parent .import.userFrame

    APSRadioButtonFrame .fType -parent $parent -label "Type of File: "\
      -variable fileType -buttonList {Text CSV} -valueList {Text CSV}\
      -orientation horizontal -contextHelp "Provide the type of the import file.\
         \nText - for text data files, \nCSV - for spreadsheet data files."

    APSLabeledEntry .importFile -parent $parent -label "File Name: "\
      -textVariable importFile -width 60 -contextHelp "The name of the file being imported." \
      -fileSelectButton 1
    bind $parent.importFile.entry <Return> {FindImportFile -impFile $importFile}
    #APSDisableButton .import.buttonRow.ok.button 
}

proc AddColumnInfoPart {} {
    global numbOfColumns numbOfLines eType colName enteredColumnsList
    global apsContextHelp
    set colName ""
    set parent .import.userFrame
    pack [ttk::label ${parent}.label1 -text "File Info" -font TkHeadingFont -anchor center]\
      -fill both -expand false
    
    pack [ttk::frame ${parent}.fileSize -borderwidth 2 -relief ridge] -fill both -expand true
    APSLabeledOutput .nOfCol -parent $parent.fileSize -label "Number of Columns: "\
      -packOption "-side left" -textVariable numbOfColumns -width 20 \
      -contextHelp "Displays the number of columns found in the imported file."
    APSLabeledOutput .nOfRow -parent $parent.fileSize -label "Number of Rows: "\
      -packOption "-side right" -textVariable numbOfLines -width 20\
      -contextHelp "Displays the number of rows found in the imported file."

    pack [ttk::label ${parent}.label2 -text "Columns Setup" -font TkHeadingFont -anchor center]\
      -fill both -expand false
    pack [ttk::frame ${parent}.columns -borderwidth 2 -relief ridge] -fill both -expand true
    APSLabeledEntry .colName -parent $parent.columns -label "Column Name: "\
      -textVariable colName -width 60 -contextHelp "Enter a name for each column." 
    set eType double
    APSRadioButtonFrame .eType -parent $parent.columns -label "Column Type: " \
      -variable eType -buttonList {int64 uint64 int32 uint32 int16 uint16 float double "long double" character string} \
      -valueList {long64 ulong64 long ulong short ushort float double longdouble character string} \
      -orientation horizontal -contextHelp "Select a type of data for the\
         corresponding column."

    pack [ttk::frame ${parent}.columns.buttons -borderwidth 2 -relief ridge] -fill both -expand true
    set buttParent ${parent}.columns.buttons
    pack [ttk::button $buttParent.enter -text "Enter" -width 30 \
            -command AddEntryToColumnsList] -side left -expand no
    set apsContextHelp($buttParent.enter) "Proceeds with an insertion of provided\
         attributes (name type) for a column."
    pack [ttk::button $buttParent.delete -text "Delete" -width 30 \
            -command DelEntryFromColumnsList] -side right -expand no
    set apsContextHelp($buttParent.delete) "Proceeds with a deletion of selected\
         column attributes from the column list."

    set enteredColumnsList ""
    APSScrolledList .entries -parent $parent.columns -height 10 \
      -name "Entered Columns" -itemList $enteredColumnsList -selectMode single\
      -contextHelp "Displays the list of names and types in order they are\
         going to be assign to data columns."
}

proc FindImportFile {args} {
    set impFile ""
    if [APSStrictParseArguments {impFile}] {
        return -code error "openFile: bad arguments"
    }
    global importFile workingDir fileType numbOfColumns numbOfLines
    if ![string length $impFile] {
        set importFile \
          [APSFileSelectDialog .openDialog -listDir $workingDir \
             -contextHelp "Select a file to Import." \
             -title "Select file to import."]
        update idletasks
    } else {
        set importFile $impFile
    }
    if [file exists $importFile] {
        if [catch {open $importFile r} fid] {
            APSSetVarAndUpdate ScrolledStatus "FindImportFile: $fid"
            return -code error
        }
        set numbOfLines 0
        set firstLine ""
        while {[gets $fid line] >= 0} {
            if !$numbOfLines {
                set firstLine $line
            }
            incr numbOfLines
        }
        close $fid

        if ![string length $firstLine] {
            APSInfoWindow [APSUniqueName .] -name Message -width 30 -infoMessage \
              "First line of the file $importFile is empty. Please, check the file."\
              -contextHelp ""
            bell
            return -code error
        }     
        if {[string compare $fileType CSV] == 0} {
            if {[string first \",\" $firstLine] < 0} {
                APSInfoWindow [APSUniqueName .] -name Message -width 30 -infoMessage \
                  "File $importFile is not a CSV file." -contextHelp ""
                bell
                return -code error
            }
            set editCommand "100%/\",\"/\#/"
            if [catch {APSEditString -editcommand $editCommand $firstLine} newString] {
                APSInfoWindow [APSUniqueName .] -name Message -width 30 -infoMessage \
                  "FindImportFile: $newString." -contextHelp ""
                bell
                return -code error
            } 
            set numbOfColumns [llength [split $newString "\#"]]
        } else {
            if {[string first \t $firstLine] < 0} {
                APSInfoWindow [APSUniqueName .] -name Message -width 30 -infoMessage \
                  "File $importFile is not a Text file." -contextHelp ""
                bell
                return -code error
            }
            set numbOfColumns [llength $firstLine]
        }
        update
        AddColumnInfoPart
        #APSEnableButton .import.buttonRow.ok.button
#        APSDisableButton .import.userFrame.file.find
    } else {
        APSInfoWindow [APSUniqueName .] -name Message -width 30 -infoMessage \
          "File $importFile does not exists." -contextHelp ""
        bell
        return -code error
    }
}

proc AddEntryToColumnsList {} {
    global enteredColumnsList colName eType
    set parent .import.userFrame
    if ![string length $colName] {return}
    lappend enteredColumnsList [list $colName $eType]
    eval {$parent.columns.entries.listbox insert end} [list "$colName $eType"]
}

proc DelEntryFromColumnsList {} {
    global enteredColumnsList
    if ![llength $enteredColumnsList] {return}
    set columnsBox .import.userFrame.columns.entries.listbox
    set selItem [$columnsBox curselection]
    set enteredColumnsList [lreplace $enteredColumnsList $selItem $selItem]
    eval [$columnsBox delete $selItem] 
}

proc ProcessImport {} {
    global importFile enteredColumnsList numbOfColumns numbOfLines
    global fileType
    
    if {[catch {FindImportFile -impFile $importFile} results]} {
        return
    }

    set columnDataList ""

    if {[llength $enteredColumnsList]!=$numbOfColumns} {
        APSInfoWindow [APSUniqueName .] -name Message -width 30 -infoMessage \
          "Number of names of columns does not match number of data columns." \
          -contextHelp ""
        bell
        return
    }     
    if ![llength $enteredColumnsList] {return}     
    if {[string compare $fileType CSV] == 0} {
        lappend columnDataList "-delimiters=start=\",end=\""
        lappend columnDataList "-separator=\\,"
    } else {
        lappend columnDataList "-separator=\t"
    }     
    
    foreach entry $enteredColumnsList {
        lappend columnDataList "-columnData=name=[lindex $entry 0],type=[lindex $entry 1]"
    }

    if [catch {eval exec csv2sdds $importFile ${importFile}.sdds \
                 -maxRows=$numbOfLines $columnDataList} result] {
        APSSetVarAndUpdate ScrolledStatus "ProcessImport: $result"
        return
    } else {
        if [file exists ${importFile}.sdds] {
            APSSetVarAndUpdate ScrolledStatus "Importing file is done."
            openFile -fileName ${importFile}.sdds
        }
    }
}

#
# AddData procedure reads data from the chosen file and
# if everything is correct, the spreadsheet is saved
# in the internal data structure and AddDataProcess procedure
# is invoked. If factor newPage is equal 1 new data will be added
# to new page(s). If factor newPage is equal 0 new data will be 
# added to the currently displayed page.
#
proc AddData {newPage} {
    global validData workingDir addDataArray
    if !$validData {
        bell
        APSSetVarAndUpdate ScrolledStatus "You can use \"Add data...\" feature\
                                    only when file is open."
        return
    }

    set addFileName \
      [APSFileSelectDialog .openDialog -listDir $workingDir \
         -contextHelp "Select a file to edit." \
         -title "Select file to edit." -path [pwd]]

    if ![string length $addFileName] {
        return
    }

    if [catch {exec sddscheck $addFileName -printErrors} result] {
        APSSetVarAndUpdate ScrolledStatus "$result"
        return
    }
    if {$result != "ok"} {
        APSSetVarAndUpdate ScrolledStatus "Problem adding file: $result"
        return	
    }
    set workingDir [file dirname $addFileName]
    set fileSize [file size $addFileName]
    global largeFileTrigger
    set pages [exec sdds2stream -npage=bare $addFileName]
    if {[expr ($fileSize/(1.0*$pages)) > $largeFileTrigger]} {
        if {[APSMultipleChoice [APSUniqueName .] \
               -question "$addFileName appears to have very large pages.  Do you really want to add it?" \
               -labelList {Yes No} -returnList {Yes No}] == "No"} {
            return
        }
    }

    APSSetVarAndUpdate ScrolledStatus "[clock format [clock seconds] -format %H:%M:%S] Adding data..."

    if [info exists addDataArray] {
        unset addDataArray
    }
    if [catch {sdds load $addFileName addDataArray} result] {
        APSSetVarAndUpdate ScrolledStatus "Error opening $addFileName: $result"
        return
    }

    ## ChangePage procedure is called here just to save current display in the 
    ## inside structure.
    ChangePage -saveCurrPage 1

    AddDataProcess $newPage
}

proc AddDataProcess {newPage} {
    global addDataArray dataArray validData numPages
    global currentPage previousCurrentPage undoArray parametersArray columnsArray
    set numbOfAddDataColumns [llength $addDataArray(ColumnNames)]
    set numbOfDataColumns [llength $dataArray(ColumnNames)]
    set numbOfNewColumns 0
    set numbOfNewParameters 0
    
    if $numbOfAddDataColumns {
        set column [lindex $addDataArray(ColumnNames) 0]
        set numbOfAddDataPages [llength $addDataArray(Column.$column)]
        set index 0
        foreach page $addDataArray(Column.$column) {
            set addDataPageRowCount($index) [llength $page]
            incr index
        }
    }
    if {!$newPage && $numbOfAddDataPages > 1} {
        catch {APSMultipleChoice [APSUniqueName .] -name Confirmation \
                 -question "The new data file contains more then one page.\
                        \nDo you want to extract all data to one page?" \
                 -labelList {Yes No} -returnList {Yes No}} userChoice
        switch $userChoice {
            Yes {
            }
            No {
                APSSetVarAndUpdate ScrolledStatus "Adding new data is canceled." 
                return
            }
        }
    }
    set currentPageIndex [expr $currentPage - 1]
    
    if $numbOfDataColumns {
        set column [lindex $dataArray(ColumnNames) 0]
        set numbOfDataPages [llength $dataArray(Column.$column)]
        set index 0
        foreach page $dataArray(Column.$column) {
            set dataPageRowCount($index) [llength $page]
            incr index
        }
    }  
    foreach col $dataArray(ColumnNames) {
        if {[lsearch $addDataArray(ColumnNames) $col] < 0} {
            lappend addDataArray(ColumnNames) $col
            set addDataArray(Column.$col) ""
            array set infoArray $dataArray(ColumnInfo.$col)
            for {set i 0} {$i < $numbOfAddDataPages} {incr i} {
                set dataList ""
                for {set j 0} {$j < $addDataPageRowCount($i)} {incr j} {
                    if {($infoArray(type) != "SDDS_STRING") && ($infoArray(type) != "SDDS_CHARACTER")} {
                        lappend dataList 0
                    } else {
                        lappend dataList ""
                    }
                }
                lappend addDataArray(Column.$col) $dataList
            }
        }
    }
    foreach col $addDataArray(ColumnNames) {
        if {[lsearch $dataArray(ColumnNames) $col] < 0} {
            lappend dataArray(ColumnNames) $col
            set dataArray(Column.$col) ""
            set dataArray(ColumnInfo.$col) $addDataArray(ColumnInfo.$col)
            array set infoArray $addDataArray(ColumnInfo.$col)
            for {set i 0} {$i < $numbOfDataPages} {incr i} {
                set dataList ""
                for {set j 0} {$j < $dataPageRowCount($i)} {incr j} {
                    if {($infoArray(type) != "SDDS_STRING") && ($infoArray(type) != "SDDS_CHARACTER")} {
                        lappend dataList 0
                    } else {
                        lappend dataList ""
                    }
                }
                lappend dataArray(Column.$col) $dataList
            }
            incr numbOfNewColumns
        }
        if $newPage {
            for {set k 0} {$k < $numbOfAddDataPages} {incr k} {
                lappend dataArray(Column.$col) [lindex $addDataArray(Column.$col) $k]
            }
        } else {
            set newItemsList ""
            foreach item [lindex $dataArray(Column.$col) $currentPageIndex] {
                lappend newItemsList $item
            }
            for {set i 0} {$i < $numbOfAddDataPages} {incr i} {
                foreach item [lindex $addDataArray(Column.$col) $i] {
                    lappend newItemsList $item
                }
            }
            set dataArray(Column.$col) [lreplace $dataArray(Column.$col)\
                                          $currentPageIndex $currentPageIndex $newItemsList]
        }     
    }

    APSSetVarAndUpdate ScrolledStatus "[clock format [clock seconds] -format %H:%M:%S]\
		        $numbOfNewColumns new columns were added."

    if $newPage {
        foreach par $dataArray(ParameterNames) {
            if {[lsearch $addDataArray(ParameterNames) $par] < 0} {
                lappend addDataArray(ParameterNames) $par
                set addDataArray(Parameter.$par) ""
                array set infoArray $dataArray(ParameterInfo.$par)
                for {set x 0} {$x < $numbOfAddDataPages} {incr x} {
                    if {($infoArray(type) != "SDDS_STRING") && ($infoArray(type) != "SDDS_CHARACTER")} {
                        lappend addDataArray(Parameter.$par) 0
                    } else {
                        lappend addDataArray(Parameter.$par) ""
                    }
                }
            }
        }
    }
    foreach par $addDataArray(ParameterNames) {
        if {[lsearch $dataArray(ParameterNames) $par] < 0} {
            lappend dataArray(ParameterNames) $par
            set dataArray(Parameter.$par) ""
            set dataArray(ParameterInfo.$par) $addDataArray(ParameterInfo.$par)
            array set infoArray $addDataArray(ParameterInfo.$par)
            for {set x 0} {$x < $numbOfDataPages} {incr x} {
                if {($infoArray(type) != "SDDS_STRING") && ($infoArray(type) != "SDDS_CHARACTER")} {
                    lappend dataArray(Parameter.$par) 0
                } else {
                    lappend dataArray(Parameter.$par) ""
                }
            }
            incr numbOfNewParameters
        }
        if $newPage {
            for {set y 0} {$y < $numbOfAddDataPages} {incr y} {
                lappend dataArray(Parameter.$par) [lindex $addDataArray(Parameter.$par) $y]
            }
        } else {
            set dataArray(Parameter.$par) [lreplace $dataArray(Parameter.$par)\
                                             $currentPageIndex $currentPageIndex\
                                             [lindex $addDataArray(Parameter.$par) 0]]
        }
    }
    APSSetVarAndUpdate ScrolledStatus "[clock format [clock seconds] -format %H:%M:%S]\
	                   $numbOfNewParameters new parameters were added."

    if $validData {
        foreach variable {undoArray parametersArray columnsArray} {
            if [info exists $variable] {                         
                unset $variable
            }
        }
        set validData 0
    }
    EliminateMajorWidgets
    foreach ss [list .parameterPane.parTop .parameterPane.parameters \
                  .columnPane.colLabel .columnPane.columns] {
        if [winfo exists $ss] {
            pack unpack $ss
        }
    }
    MakeSpreadsheet 

    OpenTablesForCurrentPage $currentPageIndex

    set previousCurrentPage $currentPage

    APSSetVarAndUpdate ScrolledStatus "[clock format [clock seconds] -format %H:%M:%S] Done.\
              \nCurrent spredsheet contains: \
              \n                             [llength $dataArray(ColumnNames)] columns\
	      \n                             [llength $dataArray(ParameterNames)] parameters\
	      \n                             $numPages page(s)."
}

proc DisplayPrintDialogBox {} {
    global printerName printMode sddsPage dataArray validData env
    global sddsPage printPage firstPage lastPage

    if !$validData {return}
    if [winfo exists .printBox] {
        destroy .printBox
    }
    if [info exists env(PRINTER)] {
        set printerName $env(PRINTER)
    }

    set okCommand ProceedPrinting
    APSDialogBox .printBox -name "Print Setup" -width 50 \
      -okCommand $okCommand -contextHelp ""
    APSLabeledEntry .printerName -parent .printBox.userFrame -textVariable printerName \
      -width 30 -label "Printer Name: " -contextHelp ""
    pack unpack .printBox.userFrame.printerName.entry
    pack .printBox.userFrame.printerName.entry -side left -fill x -expand yes
    set printMode -r
    APSRadioButtonFrame .printMode -parent .printBox.userFrame -label "Print Mode:    " \
      -variable printMode -buttonList {Landscape Portrait} -valueList {-r -R} \
      -orientation horizontal -contextHelp "" 
    set sddsPage 1
    APSRadioButtonFrame .sddsPage -parent .printBox.userFrame -label "SDDS Page:    " \
      -variable sddsPage -buttonList {Current All} -valueList {1 0} \
      -orientation horizontal -contextHelp ""
    set printPage 0
    APSRadioButtonFrame .printPage -parent .printBox.userFrame -label "Print Page:     " \
      -variable printPage -buttonList {All Select} -valueList {0 1} \
      -orientation horizontal -contextHelp ""

    pack [ttk::frame .printBox.userFrame.select2 -relief ridge] -side top -expand no
    pack [ttk::label .printBox.userFrame.select2.select1 -text "Print Page Selection:" \
            -font TkHeadingFont -anchor center] -fill both -expand false
    set firstPage 1
    set lastPage 1
    APSLabeledEntry .firstPage -parent .printBox.userFrame.select2 -textVariable firstPage \
      -width 15 -label "From : " -packOption "-side left" -contextHelp ""
    APSLabeledEntry .lastPage -parent .printBox.userFrame.select2 -textVariable lastPage \
      -width 15 -label "  To : " -packOption "-side right" -contextHelp ""
    
    if [llength $dataArray(ParameterNames)] {
        pack [ttk::label .printBox.userFrame.parLabel -text "Choose Parameters to Print" \
                -font TkHeadingFont -anchor center] -fill both -expand true -side top
        set parButtonList $dataArray(ParameterNames)
        set parVariableList $dataArray(ParameterNames)
        foreach par $dataArray(ParameterNames) {
            global $par
            set $par 1
        }
        APSCheckButtonFrame .parButts -parent .printBox.userFrame -label "" \
          -limitPerRow 10 -buttonList $parButtonList -variableList $parVariableList \
          -relief sunken -orientation vertical -allNone 1 \
          -contextHelp ""
    }
    if [llength $dataArray(ColumnNames)] {
        if {[llength $dataArray(ColumnNames)] <= 10} {
            set limitPerRow 5
        } elseif {[llength $dataArray(ColumnNames)] <= 30} {
            set limitPerRow 10
        } else {
            set limitPerRow 15
        }
        pack [ttk::label .printBox.userFrame.colLabel -text "Choose Columns to Print" \
                -font TkHeadingFont -anchor center] -fill both -expand true -side top
        set colButtonList $dataArray(ColumnNames)
        set colVariableList $dataArray(ColumnNames)
        foreach col $dataArray(ColumnNames) {
            global $col
            set $col 1
        }
        APSCheckButtonFrame .colButts -parent .printBox.userFrame -label "" \
          -limitPerRow $limitPerRow -buttonList $colButtonList -variableList \
          $colVariableList -relief sunken -orientation vertical -allNone 1 \
          -contextHelp ""
    }
}

proc ProceedPrinting {} {
    global printerName printMode dataArray curPageDataArray
    global currentPage numPages table2 inputFile resultSave env
    global sddsPage printPage firstPage lastPage
    set tmpFileIn [APSTmpDir]/[APSTmpString]
    set tmpFileOut [APSTmpDir]/[APSTmpString]
    set tmpFileMid [APSTmpDir]/[APSTmpString]
    set numbOfColToPrint 0

    ChangePage -saveCurrPage 1

    if $printPage {
        if {[string length $firstPage] && [string length $lastPage]} {
            set begin $firstPage
            set end $lastPage
        } else {
            APSSetVarAndUpdate ScrolledStatus "Print Pages are not selected." 
            bell
            return
        }            
    } else {
        set begin 0
        set end ""
    } 
    
    APSSetVarAndUpdate ScrolledStatus "Printing..."

    set resultSave resultSave
    if {$sddsPage && $numPages > 1} {
        set currentPageIndex [expr $currentPage - 1]
        set curPageDataArray(ColumnNames) $dataArray(ColumnNames)
        foreach col $dataArray(ColumnNames) {
            set curPageDataArray(Column.$col) [list [lindex $dataArray(Column.$col) $currentPageIndex]]
            set curPageDataArray(ColumnInfo.$col) $dataArray(ColumnInfo.$col)
        }
        set curPageDataArray(ParameterNames) $dataArray(ParameterNames)
        foreach par $dataArray(ParameterNames) {
            set curPageDataArray(Parameter.$par) [list [lindex $dataArray(Parameter.$par) $currentPageIndex]]
            set curPageDataArray(ParameterInfo.$par) $dataArray(ParameterInfo.$par)
        }
        if [catch {sdds save $tmpFileIn curPageDataArray} resultSave] {
            APSSetVarAndUpdate ScrolledStatus "Error saving data for printing: $resultSave]"
            bell
            return
        }
    } else {
        if [catch {sdds save $tmpFileIn dataArray} resultSave] {
            APSSetVarAndUpdate ScrolledStatus "Error saving data for printing: $resultSave]"
            bell
            return
        }
    }
    if {[string compare $printMode -r] == 0} {
        set maxPageWidth 122
    } else {
        set maxPageWidth 94
    }

    set parNotToShowList ""
    set colNotToShowList ""
    set PrintParameterList ""
    foreach d $dataArray(ParameterNames) {
        global $d
        if ![set $d] {
            lappend parNotToShowList $d
        } else {
            lappend PrintParameterList "-parameters=${d},endsLine"
        }
    }
    set colCount 1
    set index 0
    if [llength $dataArray(ColumnNames)] {
        set PrintList($index) "-column=PrintOutIndex,label=Row,format=%6ld"
        set globalWidth 8
    }
    foreach d $dataArray(ColumnNames) {
        global $d
        if ![set $d] {
            lappend colNotToShowList $d
        } else {
            array set myarray $dataArray(ColumnInfo.$d)
            set type $myarray(type)
            set colWidth [$table2(table) width $colCount]
            switch $type {
                SDDS_CHARACTER {set format "%c"}
                SDDS_STRING {set format "%${colWidth}s"}
                SDDS_SHORT {set format "%${colWidth}d"}
                SDDS_USHORT {set format "%${colWidth}u"}
                SDDS_LONG {set format "%${colWidth}ld"}
                SDDS_ULONG {set format "%${colWidth}lu"}
                SDDS_LONG64 {set format "%${colWidth}ld"}
                SDDS_ULONG64 {set format "%${colWidth}lu"}
                SDDS_FLOAT {set format "%${colWidth}f"}
                SDDS_DOUBLE {set format "%${colWidth}e"}
                SDDS_LONGDOUBLE {set format "%${colWidth}Le"}
            }
            set globalWidth [expr ($globalWidth + $colWidth) + 2]
            if {$globalWidth > $maxPageWidth} {
                incr index
                set PrintList($index) "-column=PrintOutIndex,label=Row,format=%6ld"
                set globalWidth [expr (8 + $colWidth) + 2]
            }
            lappend PrintList($index) "-column=${d},format=$format"
            incr numbOfColToPrint
        }
        incr colCount
    }
    set listToDelete ""
    if [llength $parNotToShowList] {
        lappend listToDelete "-delete=parameters,[join $parNotToShowList ,]"
    }
    if [llength $colNotToShowList] {
        lappend listToDelete "-delete=columns,[join $colNotToShowList ,]"
    }

    set title "This is a printout for \n\"$inputFile\" file \nfrom the sddsedit spreadsheet."

    if ![string length $printerName] {
        APSSetVarAndUpdate ScrolledStatus "Printer not assigned."
        bell
        return
    }

    if [file exists $tmpFileIn] {
        if [llength $listToDelete] {
            if {[catch {eval exec sddsprocess $tmpFileIn $tmpFileMid \
                          {"-noWarnings"} \
                          $listToDelete \
                          {"-define=column,PrintOutIndex,1 i_row +,type=long"}} result]} {
                APSSetVarAndUpdate ScrolledStatus "1=$result"
                bell
                result
            }
        } else {
            if {[catch {eval exec sddsprocess $tmpFileIn $tmpFileMid \
                          {"-noWarnings"} \
                          {"-define=column,PrintOutIndex,1 i_row +,type=long"}} result]} {
                APSSetVarAndUpdate ScrolledStatus "$result"
                bell
                result
            }
        }
        for {set i 0} {$i <= $index} {incr i} {
            set j [expr $i + 1]
            set header [list |%W|Set $j Page $% of $=]
            if {$numbOfColToPrint || [llength $PrintParameterList]} {
                if {$numbOfColToPrint} {
                    if {[catch {eval exec sddsprintout $tmpFileMid $tmpFileOut \
                                  $PrintParameterList \
                                  $PrintList($i) \
                                  {"-title=[APSMakeSafeQualifierString $title]"}} result]} { 
                        APSSetVarAndUpdate ScrolledStatus "$result"
                    }
                } else {
                    if {[catch {eval exec sddsprintout $tmpFileMid $tmpFileOut \
                                  $PrintParameterList \
                                  {"-title=[APSMakeSafeQualifierString $title]"}} result]} { 
                        APSSetVarAndUpdate ScrolledStatus "$result"
                    }
                }
                if [file exists $tmpFileOut] {
                    if {[catch {eval exec enscript -P${printerName} {"--header=$header"} \
                                  $printMode -a ${begin}-${end} $tmpFileOut} result]} {
                        APSSetVarAndUpdate ScrolledStatus "Set=$j $result"
                    }
                }
            } else {
                APSSetVarAndUpdate ScrolledStatus "No data to print."
            }  
        }
    }
    foreach f [list $tmpFileIn $tmpFileMid $tmpFileOut] {
        if [file exists $f] {
            file delete -force $f
        }
    }
    APSSetVarAndUpdate ScrolledStatus "Printing done." 
}


proc ParseArguments {optlist} {
    upvar args arguments
    set length [llength $arguments]
    set index 0
    set leftovers {}
    while {$index<$length} {
        set arg [lindex $arguments $index]
        if {[string index $arg 0]=="-"} {
            set keywordName [string range $arg 1 end]
            if {[lsearch -exact $optlist $keywordName]!=-1} {
                incr index
                if {$index==$length} {
                    lappend leftovers $arg
                } else {
                    set valueString [lindex $arguments $index]
                    uplevel "set $keywordName {$valueString}"
                    incr index
                }
            } else {
                incr index
                lappend leftovers $arg
            }
        } else {
            lappend leftovers $arg
            incr index
        }
    }
    set arguments [concat $leftovers]
    if {$arguments != ""} {
        set procName [lindex [info level [expr {[info level] - 1}]] 0]
        return -1
    } else {
        return 0
    }
}

APSApplication . -name sddsedit -version $CVSRevisionAuthor \
  -overview "This is an application which helps the user to proceed with\
    editing or creating a sdds file. The editing provides an user friendly\
    spreadsheet system with the most helpful functions available in any other\
    spreadsheet application. Among them the user can find: -inserting & deleting\
    a parameter; -inserting & deleting a column; -inserting & deleting a row.\
    The creating a new sdds file enables the user to set file with any number\
    of parameters and columns. When a new file is created (saved) sddsEdit application\
    switches that file into the \"Open\" mode with the spreadsheet edting."

set ScrolledStatus "Please, go to the menu. \nReady..."
APSScrolledStatus .status -parent .userFrame -textVariable ScrolledStatus -width 90 \
  -height 4 -packOption "-side top -fill x -expand false"
APSLabeledOutput .filename -parent .userFrame -textVariable inputFile -width 80 \
  -label "Now editing: " -packOption "-side top -fill x -expand false" \
  -contextHelp "Displays a directory and a name of a currently edited file."
pack configure .userFrame.filename.entry -side top -fill x -expand false

set args $argv
set fileName ""
set hideColumns ""
set hideParameters ""
set defaultFile ""
set complexAction 0
set warnOnOverwrite 0
set requireBackup 0

if [ParseArguments {fileName hideColumns hideParameters complexAction warnOnOverwrite requireBackup}] {
    if {([llength $args] == 1)} {
        set fileName $args
    } else {
        puts stderr "usage: sddsedit \[<filename>\] \[-fileName <string>\] \[-hideColumns <list>\]\
                 \[-hideParameters <list>\] \[-complexAction <boolean>\]"
        exit 1
    }
}

set newRows 1
set saveFlag 0
if $complexAction {
    .menu.file.menu insert 0 separator
    .menu.file.menu  insert 0 command -label "Print..." -underline 0 \
      -command DisplayPrintDialogBox
    .menu.file.menu insert 0 command -label "Save" -underline 0 \
      -command "saveFile; set saveFlag 1"
    .menu.file.menu delete 4 5
    .menu.file.menu add command -label "Quit" -underline 0       -command userOption
} else {    
    # Add Open... entry to File menu
    .menu.file.menu insert 0 separator
    .menu.file.menu  insert 0 command -label "Print..." -underline 0 \
      -command DisplayPrintDialogBox
    .menu.file.menu insert 0 separator
    .menu.file.menu  insert 0 cascade -label "Save as..." -underline 0 \
      -menu .menu.file.menu.saveAS
    menu .menu.file.menu.saveAS -bg $apsMenuBackground -fg $apsMenuForeground
    .menu.file.menu.saveAS insert 0 command -label "Export file..." -underline 0 \
      -command ExportFile
    .menu.file.menu.saveAS insert 0 command -label "SDDS file..." -underline 0 \
      -command saveFileAs
    .menu.file.menu insert 0 command -label "Save" -underline 0 \
      -command saveFile
    .menu.file.menu insert 0 separator
    .menu.file.menu insert 0 cascade -label "Add data..." -underline 0 \
      -menu .menu.file.menu.addData
    menu .menu.file.menu.addData -bg $apsMenuBackground -fg $apsMenuForeground
    .menu.file.menu.addData insert 0 command -label "As part of current page..." -underline 0 \
      -command "AddData 0"
    .menu.file.menu.addData insert 0 command -label "As new page..." -underline 0 \
      -command "AddData 1"
    .menu.file.menu insert 0 command -label "Import..." -underline 0 \
      -command "ImportFile"
    .menu.file.menu insert 0 command -label "Open..." -underline 0 \
      -command "openFile"
    .menu.file.menu insert 0 command -label "New..." -underline 0 \
      -command startNewFile
}

APSMenubarAddMenu .edit -parent .menu -text Edit
.menu.edit.menu add cascade -label "Parameter" -underline 0 \
  -menu .menu.edit.menu.parameter
.menu.edit.menu add separator
.menu.edit.menu add cascade -label "Column" -underline 0 \
  -menu .menu.edit.menu.column
.menu.edit.menu add separator
.menu.edit.menu add cascade -label "Row" -underline 0 \
  -menu .menu.edit.menu.row
.menu.edit.menu add separator
.menu.edit.menu add cascade -label "Page" -underline 0 \
  -menu .menu.edit.menu.page

foreach casc [list parameter column row page] {
    menu .menu.edit.menu.$casc -bg $apsMenuBackground -fg $apsMenuForeground
    .menu.edit.menu.$casc insert 1 command -label "Delete"  \
      -command "DeleteUserChoice $casc" -underline 0
    .menu.edit.menu.$casc insert 1 command -label "Insert"  \
      -command "InsertUserChoice $casc" -underline 0
    if {$casc == "page"} {
        .menu.edit.menu.$casc insert 1 command -label "Insert and Copy Current Page"  \
          -command "InsertPage -copy 1" -underline 11
        .menu.edit.menu.$casc insert 1 command -label "Go To Page"  \
          -command "GoToPage" -underline 0
    }	  
    if {($casc != "row") && ($casc != "page")} {
        .menu.edit.menu.$casc insert 1 command -label "Attributes"  \
          -command "MakeInfoText $casc edit" -underline 0
    }    
}

APSMenubarAddMenu .search -parent .menu -text Search
.menu.search.menu add command -label "Parameter" -underline 0 \
  -command "DisplaySearchDialogBox parameter"
.menu.search.menu add separator
.menu.search.menu add command -label "Column" -underline 0 \
  -command "DisplaySearchDialogBox column"

APSMenubarAddMenu .editCom -parent .menu -text "Edit_Command"
.menu.editCom.menu add command -label "Edit String" -underline 0 \
  -command "EditString"
.menu.editCom.menu add separator
.menu.editCom.menu add command -label "Edit Large Entry String" -underline 0 \
  -command "EditLargeEntryString"

APSMenubarAddMenu .info -parent .menu -text Info -packOption "-side right"
.menu.info.menu add command -label "Parameter" -underline 0 \
  -command "MakeInfoText parameter info"
.menu.info.menu add command -label "Column" -underline 0 \
  -command "MakeInfoText column info"
.menu.info.menu add command -label "Guidelines" -underline 0 \
  -command "MakeGuidelines"

foreach butt [list .menu.edit .menu.search .menu.editCom .menu.info] {
    $butt configure -state disabled 
}
set tcl_precision 0

if [string length $fileName] {
    openFile -fileName $fileName
}

