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

#package require dp
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)]
APSDebugPath

set CVSRevisionAuthor "\$Revision: 1.1 $ \$Author: soliday $"

set fileSelListDir $env(HOME)
set CountSliders 0
set sliderCount 0
set frameCount 0
set pvnameList ""
set delSlidersList ""

#
# Procedure "move_ball" is invoke by clicking with Button-1
# when a mouse pointer is over chosen slider.
#
proc move_ball {win number x y} {
     global xB$number yB$number xValue$number yValue$number
     global StepEquivalent$number xLabel$number yLabel$number
     set xValue [expr (($x-65) * [set StepEquivalent$number])]
     set yValue [expr ((65-$y) * [set StepEquivalent$number])]
     set xTmpValue [set xValue$number]
     set yTmpValue [set yValue$number]

     catch {CheckNewValuesAgainstLimits $xValue $yValue $number} result
     if $result {
         bell
         SetMainStatus "The new value is outside of the limit set.\nAction suspended."
         return
     } 
     set xValue$number $xValue
     set yValue$number $yValue

     set valList [list xValue$number yValue$number]
     if [pv putw $valList] {
         set xValue$number $xTmpValue
         set yValue$number $yTmpValue
         bell
         SetMainStatus "Problem with setting new values."
	 return
     }

     set xB$number $x
     set yB$number $y

     if {$x < 5 || $y < 5 || $x > 125 || $y > 125} {
         return
     }
     $win coords ball$number \
        [expr $x-3] [expr $y-3] \
        [expr $x+3] [expr $y+3]
     $win coords ballextY$number $x $y 65 $y
     $win coords ballextX$number $x $y $x 65
     update
}

#
# Procedure "move_ball_byStep" is invoke when mouse pointer is 
# over a chosen slider and arrow keys are used.
#
proc move_ball_byStep {win number arrow} {
     global xB$number yB$number xValue$number yValue$number
     global xHigh$number yHigh$number xLow$number yLow$number
     global StepEquivalent$number xStep$number yStep$number

     set x [set xB$number]
     set y [set yB$number]
     set coordList [$win coords ball]
     switch $arrow {
        1 {set y [expr $y-[set yStep$number]]}
        2 {set y [expr $y+[set yStep$number]]}
        3 {set x [expr $x-[set xStep$number]]}
        4 {set x [expr $x+[set xStep$number]]}
     }
     if {$y < 5} {set y 5}
     if {$y > 125} {set y 125}
     if {$x < 5} {set x 5}
     if {$x > 125} {set x 125}
     set xValue [expr (($x-65) * [set StepEquivalent$number])]
     set yValue [expr ((65-$y) * [set StepEquivalent$number])]

     set limitFlag 0
     if {[set xHigh$number] < $xValue} {
          set xValue [set xHigh$number]
          set x [expr (($xValue / [set StepEquivalent$number]) + 65)]
	  set limitFlag 1
     }
     if {[set xLow$number] > $xValue} {
          set xValue [set xLow$number]
	  set x [expr (($xValue / [set StepEquivalent$number]) + 65)]
	  set limitFlag 1
     }
     if {[set yHigh$number] < $yValue} {
          set yValue [set yHigh$number]
	  set y [expr (65 - ($yValue / [set StepEquivalent$number]))]
	  set limitFlag 1
     }
     if {[set yLow$number] > $yValue} {
          set yValue [set yLow$number]
          set y [expr (65 - ($yValue / [set StepEquivalent$number]))]
	  set limitFlag 1
     }
     if $limitFlag {
         bell
         SetMainStatus "The new value was outside of the limit set."
     } 
     set xTmpValue [set xValue$number]
     set yTmpValue [set yValue$number]
     set xValue$number $xValue
     set yValue$number $yValue

     set valList [list xValue$number yValue$number]
     if [pv putw $valList] {
         set xValue$number $xTmpValue
         set yValue$number $yTmpValue
         bell
         SetMainStatus "Problem with setting new values."
	 return
     }

     set xB$number $x
     set yB$number $y
     $win coords ballextY$number $x $y 65 $y
     $win coords ballextX$number $x $y $x 65

     $win coords ball$number \
        [expr ($x-3)] [expr $y-3] \
        [expr $x+3] [expr $y+3]
}

proc CheckNewValuesAgainstLimits {xValue yValue number} {
     global xHigh$number yHigh$number xLow$number yLow$number
     set return 0
     if {[set xHigh$number] < $xValue || [set xLow$number] > $xValue} {
          set return 1
     }
     if {[set yHigh$number] < $yValue || [set yLow$number] > $yValue} {
          set return 1
     }
     return $return
}

#
# Procedure "SetupManager" controls a sliders setup in the slider
# container. There are just six sliders allowed per a row. If the number
# of sliders in the row excesses six a new row will be assigned. 
#
proc SetupManager {number} {
     global pvScroll pvScrollFrame frameCount FrameArray CountSliders
     global inSliderCount
     set count [expr (($number - 1) % 6)]
     set prevFrame 0

     if !$count {
         set inSliderCount 0
         incr frameCount
         pack [frame $pvScroll.mainFrame$frameCount -relief flat -bd 2] \
               -side top -fill both -expand yes
     }
     incr inSliderCount
     set FrameArray($frameCount) $inSliderCount
     set mainWidget $pvScroll.mainFrame$frameCount
     MakeSlider $mainWidget $number

     if $prevFrame!=$frameCount {
	APSScrollAdjust $pvScrollFrame -numVisible 2
     }
     set prevFrame $frameCount
     incr CountSliders
     if $CountSliders==1 {
	.menu.savRes configure -state normal
        .userFrame.minimax.title.listButton configure -state normal
     }
}

proc MakeSlider {widget number} {
     global xLabel$number yLabel$number pvScroll pvScrollFrame
     global xB$number yB$number xCoord$number yCoord$number
     global SliderStateArray CanvasArray apsContextHelp
     global xValue$number yValue$number

     set xCoord$number 0
     set yCoord$number 0
     set xB$number 65
     set yB$number 65

     set valList [list xValue$number yValue$number]
     set varList [list [set xLabel$number] [set yLabel$number]]
     pv linkw $valList $varList

     set u $widget.frame$number
     pack [frame $u -relief raised -bd 2] \
        -side left -fill none -expand no

     pack [label $u.xPVLabel -text [set xLabel$number] \
           -font {courier 10 bold}] -fill none -expand false
     set apsContextHelp($u.xPVLabel) "A name of a horizontal PV."
     pack [entry $u.xEntry -relief sunken -width 14 \
           -textvariable xValue$number -state disabled -bg gray80] -fill none -expand false
     set apsContextHelp($u.xEntry) "Displays a value of a horizontal PV."
     pack [label $u.yPVLabel -text [set yLabel$number] \
           -font {courier 10 bold}] -fill none -expand false
     set apsContextHelp($u.yPVLabel) "A name of a vertical PV."
     pack [entry $u.yEntry -relief sunken -width 14 \
           -textvariable yValue$number -state disabled -bg gray80] -fill none -expand false
     set apsContextHelp($u.yEntry) "Displays a value of a vertical PV."

     pack [frame $u.frameC -relief raised -bd 2] \
        -side bottom -fill both -expand yes

     set c $u.frameC
     canvas $c.c$number -width 130 -height 130 
     pack $c.c$number
     set apsContextHelp($c.c$number) "This slider controls \"[set xLabel$number]\" as\
         horizontal PV and \"[set yLabel$number]\" as a vertical PV."
     set CanvasArray($number) $c.c$number

     $c.c$number create rectangle 5 5 125 125 -width 1 -outline red
     $c.c$number create rectangle 20 20 110 110 -width 1 -outline red
     $c.c$number create rectangle 35 35 95 95 -width 1 -outline red
     $c.c$number create rectangle 50 50 80 80 -width 1 -outline red

     $c.c$number create line 65 5 65 125 -width 2 -fill red -arrow first
     $c.c$number create line 5 65 125 65 -width 2 -fill red -arrow last
     $c.c$number create line 65 65 125 5 -width 1 -fill red
     $c.c$number create line 5 5 65 65 -width 1 -fill red
     $c.c$number create line 5 125 65 65 -width 1 -fill red
     $c.c$number create line 65 65 125 125 -width 1 -fill red

     $c.c$number create text 12 53 -anchor n -text -1 -fill red \
        -font -*-courier-medium-r-normal--10-140-*
     $c.c$number create text 118 53 -anchor n -text +1 -fill red \
        -font -*-courier-medium-r-normal--10-140-*

     $c.c$number create text 55 8 -anchor n -text +1 -fill red \
        -font -*-courier-medium-r-normal--10-140-*
     $c.c$number create text 55 115 -anchor n -text -1 -fill red \
        -font -*-courier-medium-r-normal--10-140-*

     $c.c$number create text 75 5 -anchor n -text Y -fill red \
        -font -*-times-medium-i-normal--10-140-*
     $c.c$number create text 120 70 -anchor n -text X -fill red \
        -font -*-times-medium-i-normal--10-140-*

     $c.c$number create line 65 65 65 65 -width 2 \
        -fill black -stipple gray12 -tag ballextX$number
     $c.c$number create line 65 65 65 65 -width 2 \
        -fill black -stipple gray12 -tag ballextY$number
     $c.c$number create oval 62 62 68 68 -fill black -tags ball$number

     bind $c.c$number <Button-3> "displaySetup $u $c.c$number $number"
     bind $c.c$number <Leave> "SwitchBindingAreaOff"
     set SliderStateArray($number) disable
     tkwait visibility $c.c$number
}

proc SwitchBindingAreaOn {slider number} {
     bind all <Up> "move_ball_byStep $slider $number 1"
     bind all <Down> "move_ball_byStep $slider $number 2"
     bind all <Left> "move_ball_byStep $slider $number 3"
     bind all <Right> "move_ball_byStep $slider $number 4"
}

proc SwitchBindingAreaOff {} {
     bind all <Up> ""
     bind all <Down> ""
     bind all <Left> ""
     bind all <Right> ""
}

#
# Procedure "Disable" is invoked as a result of command
# from the "Slider Setup" display.
#
proc Disable {d canvas number} {
     global SliderStateArray
     bind $canvas <Enter> "SwitchBindingAreaOff"
     bind $canvas <Button-1> ""

     $d.buttonRow.disable2.button configure -state disable
     $d.buttonRow.enable2.button configure -state normal
     set SliderStateArray($number) disabled
     set revList ""
     set tempList [split $canvas .]
     for {set i 1} {$i <= 8} {incr i} {
          lappend revList [lindex $tempList $i]
     }
     set tempFrame .[join $revList .]
     $tempFrame.xEntry configure -bg gray80
     $tempFrame.yEntry configure -bg gray80
}

#
# Procedure "Enable" is invoked as a result of command
# from the "Slider Setup" display.
#
proc Enable {d canvas number} {
     global SliderStateArray
     bind $canvas <Enter> "SwitchBindingAreaOn $canvas $number"
     bind $canvas <Button-1> "move_ball $canvas $number %x %y"

     $d.buttonRow.enable2.button configure -state disabled
     $d.buttonRow.disable2.button configure -state normal
     set SliderStateArray($number) normal
     set revList ""
     set tempList [split $canvas .]
     for {set i 1} {$i <= 8} {incr i} {
          lappend revList [lindex $tempList $i]
     }
     set tempFrame .[join $revList .]
     $tempFrame.xEntry configure -bg white
     $tempFrame.yEntry configure -bg white
}

#
# Procedure "CancelDialogBox" is invoked as a result of command
# from the "Slider Setup" display.
#
proc CancelDialogBox {state canvas number} {
     global SliderStateArray

     if [string compare $state $SliderStateArray($number)]!=0 {
	if [string compare $state normal]==0 {
	    bind $canvas <Enter> "SwitchBindingAreaOn $canvas $number"
	    bind $canvas <Button-1> "move_ball $canvas $number %x %y"
	    set bg white
	} else {
	    bind $canvas <Enter> "SwitchBindingAreaOff"
	    bind $canvas <Button-1> ""
	    set bg gray80
	}
	set SliderStateArray($number) $state
	set revList ""
	set tempList [split $canvas .]
	for {set i 1} {$i <= 8} {incr i} {
	    lappend revList [lindex $tempList $i]
	}
	set tempFrame .[join $revList .]
	$tempFrame.xEntry configure -bg $bg
	$tempFrame.yEntry configure -bg $bg
     }
     destroy .dialogF
}

proc displaySetup {frame canvas number} {
     global xLabel$number yLabel$number xUnitStep$number yUnitStep$number
     global xHigh$number yHigh$number xLow$number yLow$number
     global xValue$number yValue$number xLocal$number yLocal$number
     global SliderStateArray LimitsSnapshot apsContextHelpEnable
     set state $SliderStateArray($number)

     if $apsContextHelpEnable {return}
     if [winfo exists .dialogF] {
         destroy .dialogF
     }

     foreach limit [list xHigh$number yHigh$number xLow$number yLow$number\
                         xUnitStep$number yUnitStep$number] {
        set LimitsSnapshot($limit) [set $limit]
     }

     APSFrame .dialogF -name "Slider Setup" -geometry [APSGeometryRightRelative $frame]
     set d .dialogF.frame.dialog
     set cancelCommand "CancelDialogBox $state $canvas $number"
     set okCommand "[list destroy .dialogF]; RetrieveSliderSetup $number"
     set commandDisable "Disable $d $canvas $number"
     set commandEnable "Enable $d $canvas $number"

     APSDialogBox .dialog -parent .dialogF.frame -name "Slider Setup" \
        -cancelCommand $cancelCommand -okCommand $okCommand -contextHelp \
         "A display of a current setup for a related slider with option to\
          change some values."
     APSDialogBoxAddButton .disable2 -parent $d \
        -text Disable -command $commandDisable -contextHelp "Disables\
        a related slider from any changes."
     APSDialogBoxAddButton .enable2 -parent $d \
        -text Enable -command $commandEnable -contextHelp "Switching\
        a related slider to an operational mode."
     APSDialogBoxAddButton .delete2 -parent $d \
        -text Delete -command "DeleteSlider $frame $number" -contextHelp\
         "Erases a related slider from a current set. A confirmation\
          dialog will follow."
     if [string compare $SliderStateArray($number) normal]==0 {
         $d.buttonRow.enable2.button configure -state disable
         $d.buttonRow.disable2.button configure -state normal
     } else {
         $d.buttonRow.enable2.button configure -state normal
         $d.buttonRow.disable2.button configure -state disable
     }

     set parent .dialogF.frame.dialog.userFrame
     APSLabeledOutput .xCoord2 -parent $parent \
	-label "X-horizontal PV" -textVariable xLabel$number -width 20 \
	-contextHelp "A name of a horizontal PV from a related slider." 
     APSLabeledOutput .xLocal2 -parent $parent \
	-label "Saved-X-local-val" -textVariable xLocal$number -width 20 \
	-contextHelp "A value of related horizontal PV which was saved by \
         an operator locally. An initial local value is equal to the corresponding\
         PV value at an inception time." 
     
     APSLabeledOutput .yCoord2 -parent $parent \
	-label "V-vertical PV" -textVariable yLabel$number -width 20 \
	-contextHelp "A name of a vertical PV from a related slider." 
     APSLabeledOutput .yLocal2 -parent $parent \
	-label "Saved-V-local-val" -textVariable yLocal$number -width 20 \
	-contextHelp "A value of related vertical PV which was saved by \
         an operator locally. An initial local value is equal to the corresponding\
         PV value at an inception time." 
     APSLabeledEntry .xStep2 -parent $parent \
	-label "X step" -textVariable xUnitStep$number -width 20 \
	-contextHelp "A value of a delta for horizontal PV." 
     APSLabeledEntry .yStep2 -parent $parent \
	-label "V step" -textVariable yUnitStep$number -width 20 \
	-contextHelp "A value of a delta for vertical PV." 
     APSLabeledEntry .xHigh -parent $parent \
	-label "X high limit" -textVariable xHigh$number -width 20 \
	-contextHelp "A high limit for a horizontal PV." 
     APSLabeledEntry .xLow -parent $parent \
	-label "X low limit" -textVariable xLow$number -width 20 \
	-contextHelp "A low limit for a horizontal PV." 
     APSLabeledEntry .yHigh -parent $parent \
	-label "V high limit" -textVariable yHigh$number -width 20 \
	-contextHelp "A high limit for a vertical PV." 
     APSLabeledEntry .yLow -parent $parent \
	-label "V low limit" -textVariable yLow$number -width 20 \
	-contextHelp "A low limit for a vertical PV." 

     pack [frame $parent.frameButt -relief flat -bd 2] \
        -side bottom -fill both -expand yes
     APSButton .saveLoc -parent $parent.frameButt -packOption "-side left" \
        -text "Save-Local-Val's" -command "SaveCommand $number" -contextHelp\
         "Saves current values of both PV's as local."
     APSButton .restorLoc -parent $parent.frameButt -packOption "-side right" \
        -text "Restore-Local-Val's" -command "RestoreCommand $number" \
        -contextHelp "Restores both PV's to previously saved local values."
}

proc DeleteSlider {frame number} {
     global pvnameList delSlidersList CountSliders sliderCount
     global xLabel$number yLabel$number pvScroll frameCount
     global FrameArray 

     catch {APSMultipleChoice [APSUniqueName .] -name Confirmation \
	       -question "Do you really want to delete the slider with PV's\
                         \"[set xLabel$number]\" & \"[set yLabel$number]\"?" \
	       -labelList {Yes No} -returnList {Yes No}} userChoice
     switch $userChoice {
	Yes {
	}
	No {
	    return
	}
     }

     destroy .dialogF

     foreach pv [list [set xLabel$number] [set yLabel$number]] {
        set index [lsearch -exact $pvnameList $pv]
        set pvnameList [lreplace $pvnameList $index $index]
     }

     set CountSliders [expr ($CountSliders -1)]
     if {$number==$sliderCount} {
         set sliderCount [expr ($sliderCount -1)]
     } else {
         lappend delSlidersList $number
     }
     if [winfo exists $frame] {
         destroy $frame
     }
     set tempList [split $frame .]
     set frameNumber [APSStringTrimLeft [lindex $tempList 7] mainFrame]
     set tmpCount $FrameArray($frameNumber)
     incr tmpCount -1
     if !$tmpCount { 
         destroy $pvScroll.mainFrame$frameNumber
     }
     set FrameArray($frameNumber) $tmpCount
     if !$CountSliders {
         set sliderCount 0
         set delSlidersList ""
         set frameCount 0
         .menu.savRes configure -state disabled 
     }
     
     bell
     SetMainStatus "Slider with PV's \"[set xLabel$number]\"\
                    & \"[set yLabel$number]\" is deleted."
}

#
# Procedure "SaveCommand" saves current PV's values from
# the related slider as local values.
#
proc SaveCommand {number} {
     global xLocal$number xValue$number yLocal$number yValue$number
     set xLocal$number [set xValue$number]
     set yLocal$number [set yValue$number]
}

#
# Procedure "RestoreCommand" restores previously saved local values
# to PV's from the related slider.
#
proc RestoreCommand {number} {
     global xLocal$number xValue$number yLocal$number yValue$number
     set xTmpValue [set xValue$number]
     set yTmpValue [set yValue$number]
     set xValue$number [set xLocal$number]
     set yValue$number [set yLocal$number]
     set valList [list xValue$number yValue$number]
     if [pv putw $valList] {
         set xValue$number $xTmpValue
         set yValue$number $yTmpValue
         bell
         SetMainStatus "Problem with setting new values."
	 return
     }
     SetSliderRange $number
} 

#
# Procedure "RetrieveSliderSetup" is invoked by "OK" command
# from the "Slider Setup" display and corrects the slider setup
# according to done changes.
#
proc RetrieveSliderSetup {number} {
     global xHigh$number yHigh$number xLow$number yLow$number
     global LimitsSnapshot xUnitStep$number yUnitStep$number
     set changeFlag 0
     foreach limit [list xHigh$number yHigh$number xLow$number yLow$number\
                         xUnitStep$number yUnitStep$number] {
        if {$LimitsSnapshot($limit)!=[set $limit]} {
            set changeFlag 1
        }
     } 
     if $changeFlag {
           SetSliderRange $number
     }
}

proc addNewSlider {} {
     global sliderCount
     set okCommand {ProceedWithPVEntry $sliderCount}
     set cancelCommand [list incr sliderCount -1]

     if [winfo exists .pv] {
         incr sliderCount -1
         destroy .pv
     }
     incr sliderCount
     APSDialogBox .pv -name "Add Slider Box" -width 60 \
        -cancelCommand $cancelCommand -okCommand $okCommand \
        -contextHelp "Press OK button to add a slider."
     global xLabel$sliderCount yLabel$sliderCount
     APSLabeledEntry .xCoord1 -parent .pv.userFrame \
	-label "X-horizontal PV" -textVariable xLabel$sliderCount -width 20 \
	-contextHelp "Enter a name of a horizontal PV." 
     APSLabeledEntry .yCoord1 -parent .pv.userFrame \
	-label "V-vertical PV" -textVariable yLabel$sliderCount -width 20 \
	-contextHelp "Enter a name of a vertical PV."
     global xUnitStep$sliderCount yUnitStep$sliderCount 
     set xUnitStep$sliderCount 0
     set yUnitStep$sliderCount 0
     APSLabeledEntry .xStep1 -parent .pv.userFrame \
	-label "X step" -textVariable xUnitStep$sliderCount -width 20 \
	-contextHelp "Enter a value of a delta for horizontal PV. \
                      It is 0 PV's units by default." 
     APSLabeledEntry .yStep1 -parent .pv.userFrame \
	-label "V step" -textVariable yUnitStep$sliderCount -width 20 \
	-contextHelp "Enter a value of a delta for vertical PV. \
                      It is 0 PV's units by default."
}

proc ProceedWithPVEntry {number} {
     global xLabel$number yLabel$number
     global sliderCount pvnameList 
     global xHigh$number yHigh$number xLow$number yLow$number
     set message ""
     set tempList ""

     SetMainStatus "Setting new slider..."
     
     foreach pv [list [set xLabel$number] [set yLabel$number]] {
        if ![string length $pv] {
             set message "No entry submited."
        }
        if {[lsearch $pvnameList $pv]>=0 || [lsearch $tempList $pv]>=0} {
            set message "Entry \($pv\) already exists."
        }
	if ![string length $message] {
	     lappend tempList $pv
        } else {
	    bell
            SetMainStatus $message
	    incr sliderCount -1
	    return
	} 
     }

     if [catch {SearchPVName -pvNames $tempList -number $number} result] {
	 incr sliderCount -1
	 SetMainStatus "SearchPVName: $result. Setting is stopped."
	 bell
	 return
     }

     set xRoot [os editstring S?/./10D [set xLabel$number]]
     set yRoot [os editstring S?/./10D [set yLabel$number]]
     if [catch {eval exec cavget -list=$xRoot.DRVH} xHigh$number] {
         set message [set xHigh$number]
     }
     if [catch {eval exec cavget -list=$xRoot.DRVL} xLow$number] {
         set message [set xLow$number]
     } 
     if [catch {eval exec cavget -list=$yRoot.DRVH} yHigh$number] {
         set message [set yHigh$number]
     }
     if [catch {eval exec cavget -list=$yRoot.DRVL} yLow$number] {
         set message [set yLow$number]
     } 
     if [string length $message] {
	  bell
          SetMainStatus "Problem reading high-low limits: $message."
	  incr sliderCount -1
	  return
     }

     SetupManager $number

     set flag 0
     foreach limit [list xHigh$number xLow$number] {
        if {[string compare [set $limit] "?"]==0} {
	     set $limit 0
	     if !$flag {
                  bell
                  SetMainStatus "Cannot read limits for [set xLabel$number]. \
			         \n         Limits set to zero."
		  set flag 1
	     }
        }
     }
     set flag 0
     foreach limit [list yHigh$number yLow$number] {
        if {[string compare [set $limit] "?"]==0} {
	     set $limit 0
	     if !$flag {
		  bell
		  SetMainStatus "Cannot read limits for [set yLabel$number]. \
			         \n         Limits set to zero."
		  set flag 1
	     }
	}
     }

     lappend pvnameList [set xLabel$number]
     lappend pvnameList [set yLabel$number]

     SetSliderRange $number
     SetMainStatus "Done." 
}

proc SearchPVName {args} {
     set pvNames ""
     set number 0

     if [APSStrictParseArguments {pvNames number}] {
         return -code error "SearchPVName: bad arguments"
     }
     global xLabel$number yLabel$number xValue$number yValue$number

     set pvString [join $pvNames ,]

     if [catch {eval exec cavget -list=$pvString -pend=30 \
		  -labeled {{-delim= }}} dataValue] {
         return -code error "$dataValue"
     }
     if [llength $dataValue] {
         array set valueNow $dataValue

	 set xValue$number $valueNow([set xLabel$number])
         if [string compare [set xValue$number] ?]==0 {
             return -code error "Cannot read value of [set xLabel$number]!"
         }
	 set yValue$number $valueNow([set yLabel$number])
         if [string compare [set yValue$number] ?]==0 {
             return -code error "Cannot read value of [set yLabel$number]!"
         }
         update

         foreach val [list xInitial$number xGlobal$number xLocal$number] {
            global $val
            set $val [set xValue$number]
         }
         foreach val [list yInitial$number yGlobal$number yLocal$number] {
            global $val
            set $val [set yValue$number]
         }
     }
}

proc SetSliderRange {number} {
     global xValue$number yValue$number CanvasArray
     global xHigh$number yHigh$number xLow$number yLow$number
     global xB$number yB$number xCoord$number yCoord$number
     global StepEquivalent$number xStep$number yStep$number
     global xUnitStep$number yUnitStep$number

##   This part is just to correct limits
     if {[set xLow$number] > [set xValue$number]} {
          set xLow$number [set xValue$number]
     }
     if {[set xHigh$number] < [set xValue$number]} {
          set xHigh$number [set xValue$number]
     }
     if {[set yLow$number] > [set yValue$number]} {
          set yLow$number [set yValue$number]
     }
     if {[set yHigh$number] < [set yValue$number]} {
          set yHigh$number [set yValue$number]
     }

##   This part is to set scale for each variable
     if {[set xValue$number]==0 && [set yValue$number]==0 && \
         [set xLow$number]==0 && [set xHigh$number]==0 && \
	 [set yLow$number]==0 && [set yHigh$number]==0} {
          set ScaleFactor 1
     } else {
          set ScaleFactor [expr abs([set xHigh$number])]
          set abs [expr abs([set xLow$number])]
          if {$ScaleFactor < $abs} {
              set ScaleFactor $abs
          }
          set abs [expr abs([set yHigh$number])]
          if {$ScaleFactor < $abs} {
              set ScaleFactor $abs
          }
          set abs [expr abs([set yLow$number])]
          if {$ScaleFactor < $abs} {
              set ScaleFactor $abs
          }
     }
     
     set StepEquivalent$number [expr (double($ScaleFactor) / 60)]
     set x [expr ((double([set xValue$number]) / [set StepEquivalent$number]) + 65)]
     set y [expr (65 - (double([set yValue$number]) / [set StepEquivalent$number]))]
     set xB$number $x
     set yB$number $y

     if {$x < 5 || $y < 5 || $x > 125 || $y > 125} {
         return
     }
     $CanvasArray($number) coords ball$number \
        [expr $x-3] [expr $y-3] \
        [expr $x+3] [expr $y+3]
     $CanvasArray($number) coords ballextY$number $x $y 65 $y
     $CanvasArray($number) coords ballextX$number $x $y $x 65
     set xStep$number [expr ([set xUnitStep$number] / [set StepEquivalent$number])]
     set yStep$number [expr ([set yUnitStep$number] / [set StepEquivalent$number])]
     update
}

proc SaveGlobalValues {} {
     global sliderCount delSlidersList
     for {set i 1} {$i<=$sliderCount} {incr i} {
          if [lsearch -exact $delSlidersList $i]<0 {
	      global xGlobal$i yGlobal$i xValue$i yValue$i
	      set xGlobal$i [set xValue$i]
	      set yGlobal$i [set yValue$i]
	  }
     }
     SetMainStatus "Global values are saved."
}

proc RestoreValues {mode} {
     global sliderCount delSlidersList

     if [string compare $mode global]==0 {
         for {set i 1} {$i<=$sliderCount} {incr i} {
	      if [lsearch -exact $delSlidersList $i]<0 {
                  global xGlobal$i yGlobal$i xValue$i yValue$i
		  set xTmpValue [set xValue$i]
                  set yTmpValue [set yValue$i]
		  set xValue$i [set xGlobal$i]
		  set yValue$i [set yGlobal$i]
                  set valList [list xValue$i yValue$i]
                  if [pv putw $valList] {
                      set xValue$i $xTmpValue
                      set yValue$i $yTmpValue
                      bell
                      SetMainStatus "Problem with setting new values."
	              return
                  }
                  SetSliderRange $i
              }
         }
	 SetMainStatus "Global values are restored."
     }
     if [string compare $mode initial]==0 {
         for {set i 1} {$i<=$sliderCount} {incr i} {
	      if [lsearch -exact $delSlidersList $i]<0 {
                  global xInitial$i yInitial$i xValue$i yValue$i
		  set xTmpValue [set xValue$i]
                  set yTmpValue [set yValue$i]
                  set xValue$i [set xInitial$i]
                  set yValue$i [set yInitial$i]
                  set valList [list xValue$i yValue$i]
                  if [pv putw $valList] {
                      set xValue$i $xTmpValue
                      set yValue$i $yTmpValue
                      bell
                      SetMainStatus "Problem with setting new values."
	              return
                  }

                  SetSliderRange $i
	      }
         }
	 SetMainStatus "Initial values are restored."
     }
}

proc LoadNewConfiguration {} {
     global sliderCount pvScroll FrameArray inSliderCount CanvasArray
     global SliderStateArray LimitsSnapshot delSlidersList CountSliders
     global fileSelListDir inputFile frameCount pvnameList

     foreach f [array names FrameArray] {
        if [winfo exists $pvScroll.mainFrame$frameCount] {
            destroy $pvScroll.mainFrame$frameCount
        }
	set FrameArray($f) ""
     }
     set sliderCount 0
     set frameCount 0
     set inSliderCount 0
     set CountSliders 0
     set pvnameList ""
     set fileSelListDir ""
     set inputFile ""
     for {set i 0} {$i <= $sliderCount} {incr i} {
          foreach v [list xLabel yLabel xB yB xCoord yCoord xValue StepEquivalent \
                     xStep yStep xLow yLow xHigh yHigh xUnitStep yUnitStep xLocal yLocal \
                     xGlobal yGlobal xInitial yInitial] {
             if [info exists ${v}$i] {
                 unset ${v}$i
             }
          }
          foreach limit [list xHigh$i yHigh$i xLow$i yLow$i \
                         xUnitStep$i yUnitStep$i] {
             if [info exists LimitsSnapshot($limit)] {
                 unset LimitsSnapshot($limit)
	     }
          }
          foreach s [list CanvasArray($i) SliderStateArray($i)] {
	     if [info exists $s] {
	         unset $s
	     }
	  }
     }
     set delSlidersList ""
}

proc LoadExistingConfiguration {args} {
    set fileName ""
    set module read
    if [APSStrictParseArguments {fileName module}] {
        SetMainStatus "Wrong argument."
	bell
	return
    }
    global sliderCount fileSelListDir inputFile
    set xNameList ""
    set yNameList ""
    set xDeltaList ""
    set yDeltaList ""
    if ![string length $fileName] {
	set fileName [APSFileSelectDialog .chooseInputFile \
                      -listDir $fileSelListDir]
    }
    if {$fileName==""} { 
        return 
    }
    set fileSelListDir [file dirname $fileName]
    if {![APSCheckSDDSFile -fileName $fileName]} {
        SetMainStatus "$fileName is not an SDDS file."
        return
    }

    set names [APSGetSDDSNames -fileName $fileName -class column]
    if {[llength $names]<1} {
        SetMainStatus "$fileName is not a valid input file."
        return 
    }
    
    SetMainStatus "Loading configuration..."
    bell

    if {[lsearch -exact $names "HorizontalControlName"]>=0} {
	 set xNameList [APSGetSDDSColumn -fileName $fileName -column HorizontalControlName -page 0]
    } else {
         SetMainStatus "$fileName is not a valid input file.\nLoading interrupted."
         return 
    }
    if {[lsearch -exact $names "VerticalControlName"]>=0} {
	 set yNameList [APSGetSDDSColumn -fileName $fileName -column VerticalControlName -page 0]
    } else {
         SetMainStatus "$fileName is not a valid input file.\nLoading interrupted."
         return 
    }
    if {[lsearch -exact $names "HorizontalDelta"]>=0} {
	 set xDeltaList [APSGetSDDSColumn -fileName $fileName -column HorizontalDelta -page 0]
    } else {
         SetMainStatus "$fileName is not a valid input file.\nLoading interrupted."
         return 
    }
    if {[lsearch -exact $names "VerticalDelta"]>=0} {
	 set yDeltaList [APSGetSDDSColumn -fileName $fileName -column VerticalDelta -page 0]
    } else {
         SetMainStatus "$fileName is not a valid input file.\nLoading interrupted."
         return 
    }

    if {[llength $xNameList]==0 || [llength $yNameList]==0 || \
        [llength $xDeltaList]==0 || [llength $yDeltaList]==0} {
	SetMainStatus "$fileName does not have valid data file.\nLoading interrupted."
        return 
    }
    if [string compare $module read]==0 {
        LoadNewConfiguration
    }

    for {set i 0} {$i < [llength $xNameList]} {incr i} {
         incr sliderCount
         global xLabel$sliderCount yLabel$sliderCount 
         global xUnitStep$sliderCount yUnitStep$sliderCount
         set xLabel$sliderCount [lindex $xNameList $i]
	 set yLabel$sliderCount [lindex $yNameList $i]
         set xUnitStep$sliderCount [format %.3g [lindex $xDeltaList $i]]
         set yUnitStep$sliderCount [format %.3g [lindex $yDeltaList $i]]
         ProceedWithPVEntry $sliderCount
    }
    set inputFile $fileName
    bell
    SetMainStatus "Configuration loaded from file $fileName"
}
 
proc saveFile {} {
     global CountSliders inputFile
     if !$CountSliders {
         SetMainStatus "No data."
         return
     }
     SaveConfiguration -outputFile $inputFile
}
  
proc SaveConfiguration {args} {
    set outputFile ""
    if [APSStrictParseArguments {outputFile}] {
        SetMainStatus "Wrong argument."
	bell
	return
    }
    global sliderCount CountSliders delSlidersList
    global dataArray inputFile
    set boolean 0

    if {!$CountSliders} {
        SetMainStatus "No sliders exists."
        bell
        return	
    }

    if ![string length $outputFile] {
	set outputFile \
	    [APSInfoDialog [APSUniqueName .] -name "Configuration Output File" \
             -width 60 -infoMessage "Output filename: " \
             -contextHelp "Enter the name of the file to which to write the configuration."]
    } else {
        set boolean 1
    }
    if {[string length $outputFile]==0} {
        SetMainStatus "Saving Configuration is canceled."
        bell
        return
    }
    if {[string first " " $outputFile]!=-1} {
        SetMainStatus "Spaces are not allowed in the filename"
        bell
        return
    }

    if {[file exists $outputFile] && !$boolean} {
        bell
        set ok [APSYesNoPopUp "Delete existing $outputFile?"]
        if {!$ok} {
            SetMainStatus "Supply a new output filename"
            return
        }
    }

    SetMainStatus "Saving Configuration..."
    set inputFile $outputFile
    set xNameList ""
    set yNameList ""
    set xDeltaList ""
    set yDeltaList ""

    for {set i 1} {$i <= $sliderCount} {incr i} {
       global xLabel$i yLabel$i xUnitStep$i yUnitStep$i
       if [lsearch -exact $delSlidersList $i]<0 {
           lappend xNameList [set xLabel$i]
           lappend yNameList [set yLabel$i]
           lappend xDeltaList [set xUnitStep$i]
           lappend yDeltaList [set yUnitStep$i]
       }
    }

    set dataArray(ColumnNames) "HorizontalControlName VerticalControlName HorizontalDelta VerticalDelta"
    set dataArray(ColumnInfo.HorizontalControlName) "type SDDS_STRING"
    set dataArray(ColumnInfo.VerticalControlName) "type SDDS_STRING"
    set dataArray(ColumnInfo.HorizontalDelta) "type SDDS_DOUBLE"
    set dataArray(ColumnInfo.VerticalDelta) "type SDDS_DOUBLE"

    set dataArray(Column.HorizontalControlName) [list $xNameList]
    set dataArray(Column.VerticalControlName) [list $yNameList]
    set dataArray(Column.HorizontalDelta) [list $xDeltaList]
    set dataArray(Column.VerticalDelta) [list $yDeltaList]

    if [catch {sdds save $outputFile dataArray} result] {
        SetMainStatus "Unable to save $outputFile: $result"
	bell
	return
    }    
    SetMainStatus "Configuration is saved into $outputFile."
}

proc DisableSystem {} {
     global sliderCount delSlidersList CanvasArray SliderStateArray

     if !$sliderCount {return}
     destroy .dialogF
     for {set i 1} {$i <= $sliderCount} {incr i} {
          if [lsearch -exact $delSlidersList $i]<0 {
              set canvas $CanvasArray($i)
              bind $canvas <Button-3> ""
	      if [string compare $SliderStateArray($i) normal]==0 { 
                  bind $canvas <Enter> "SwitchBindingAreaOff"
                  bind $canvas <Button-1> ""
	          set revList ""
                  set tempList [split $canvas .]
                  for {set j 1} {$j <= 8} {incr j} {
                       lappend revList [lindex $tempList $j]
                  }
                  set tempFrame .[join $revList .]
                  $tempFrame.xEntry configure -bg gray80
                  $tempFrame.yEntry configure -bg gray80
		  set SliderStateArray($i) disable
	      }
          }
     }
     .menu.savRes configure -state disabled 
     .userFrame.minimax.title.listButton configure -state disable
     bell
     SetMainStatus "System is disabled."     
}

proc EnableSystem {} {
     global sliderCount delSlidersList CanvasArray SliderStateArray CountSliders

     if !$CountSliders {return}
 
     for {set i 1} {$i <= $sliderCount} {incr i} {
          if [lsearch -exact $delSlidersList $i]<0 {
              set canvas $CanvasArray($i)
              bind $canvas <Enter> "SwitchBindingAreaOn $canvas $i"
              bind $canvas <Button-1> "move_ball $canvas $i %x %y"
	      set revList ""
              set tempList [split $canvas .]
              for {set j 1} {$j <= 8} {incr j} {
                   lappend revList [lindex $tempList $j]
              }
              set u .[join $revList .]
              bind $canvas <Button-3> "displaySetup $u $canvas $i"
              $u.xEntry configure -bg white
              $u.yEntry configure -bg white
	      set SliderStateArray($i) normal
          }
     }
     .menu.savRes configure -state normal
     .userFrame.minimax.title.listButton configure -state normal
     bell
     SetMainStatus "System is enabled."     
}

proc ListOfSliders {} {
     global sliderCount delSlidersList
     set listOfSliders ""
     set count 0
     for {set i 1} {$i<=$sliderCount} {incr i} {
          if [lsearch -exact $delSlidersList $i]<0 {
              global xLabel$i yLabel$i
	      incr count
	      lappend listOfSliders "$count [set xLabel$i] [set yLabel$i]"
	  }
     }

     set frame .userFrame.minimax.title.listButton
     APSFrame .list -name "List of Sliders" -geometry [APSGeometryRightRelative $frame]
     APSScrolledList .matchL -parent .list.frame \
	    -name "Match List" -itemList $listOfSliders -callback ScrolledListCallback \
	    -selectMode single -contextHelp "List of sliders in the current setup.\
            Double click over selected slider will scroll the sliders set to the\
            related position."
     pack [frame .list.frame.buttonRow -relief flat] \
	    -fill x -expand true
     set command "destroy .list"
     APSButton .close -parent .list.frame.buttonRow -packOption "-side bottom" \
         -text "Close" -command $command -contextHelp "Closing a list of sliders."
}

proc ScrolledListCallback {item doubleClick} {
     global sliderCount delSlidersList CanvasArray frameCount
     if $doubleClick {
        set slider 0
	set count 0
        set number [lindex $item 0]
        for {set i 1} {$i<=$sliderCount} {incr i} {
             if [lsearch -exact $delSlidersList $i]<0 {
	         incr count
		 if $count==$number {
                    set slider $i
		 }
	     }
        }
        if $slider {
	    set existingFrames 0
	    set frame .userFrame.lines.sw.frame.canvas.frame.mainFrame
            set tempList [split $CanvasArray($slider) .]
            set frameNumber [APSStringTrimLeft [lindex $tempList 7] mainFrame]
            for {set j 1} {$j <= $frameCount} {incr j} {
                 if [winfo exists $frame$j] {
		     incr existingFrames
		 }
	    }
	    set step [expr (($frameNumber.00 / $existingFrames.00) - (1 / $existingFrames.00))]
	    .userFrame.lines.sw.frame.canvas yview moveto $step
        }
     }
}

set args $argv
set fileName ""
if [APSStrictParseArguments {fileName}] {
    puts stderr "usage: twoDSlider \[-fileName <string>\]"
    exit 1
}

APSApplication . -name 2DSlider -version $CVSRevisionAuthor \
  -overview "2DSlider application allows an operator to form a set of sliders\
   when each of them controls two quantities at once. The horizontal dimension\
   of the slider is linked to one PV, and the vertical dimension to another PV.\
   The operator has ability to set a single slider, save/read of configuration\
   sdds files, save/restore of PV values (initial, global, individual),\
   change Step values, change limits for particular PV."

.menu.file.menu insert 1 separator
.menu.file.menu insert 1 command -label "Save As" -underline 0 \
     -command SaveConfiguration
.menu.file.menu insert 1 command -label "Save" -underline 0 \
     -command saveFile
.menu.file.menu insert 1 command -label "Add..." -underline 0 \
     -command "LoadExistingConfiguration -module add"
.menu.file.menu insert 1 command -label "Read..." -underline 0 \
     -command "LoadExistingConfiguration -module read"

APSMenubarAddMenu .savRes -parent .menu -text Save/Restore
.menu.savRes.menu insert 1 command -label "Restore-Initial-Val's..." -underline 0 \
     -command "RestoreValues initial"
.menu.savRes.menu insert 1 command -label "Restore-Global-Val's..." -underline 0 \
     -command "RestoreValues global"
.menu.savRes.menu insert 1 command -label "Save-Global-Val's..." -underline 0 \
     -command SaveGlobalValues

APSMenubarAddMenu .system -parent .menu -text System
.menu.system.menu insert 1 command -label "Disable" -underline 0 \
     -command DisableSystem
.menu.system.menu insert 1 command -label "Enable" -underline 0 \
     -command EnableSystem

APSMenubarAddMenu .addSlid -parent .menu -text "Add-Slider"
.menu.addSlid.menu insert 1 command -label "Add New Slider..." -underline 0 \
     -command addNewSlider

APSMenubarAddMenu .moreLess -parent .menu -text "More/Less"
.menu.moreLess.menu insert 1 command -label "More" -underline 0 \
     -command "pack unpack .userFrame.lines; pack .userFrame.minimax;\
               pack .userFrame.lines -fill x"
.menu.moreLess.menu insert 1 command -label "Less" -underline 0 \
     -command "pack unpack .userFrame.minimax"

set mainStatus "Use \"Add New Slider...\" button to provide input."

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

pack [frame .userFrame.minimax -bd 1 -relief flat -width 90] -side top
APSScrolledStatus .status -parent .userFrame.minimax -textVariable mainStatus -width 90\
   -withButtons 1

set inputFile ""
pack [frame .userFrame.minimax.file -bd 1 -relief flat -width 90] -side top
APSLabeledOutput .filename -parent .userFrame.minimax.file -textVariable inputFile -width 73 \
    -label "Latest config file: " -packOption "-side top -fill x -expand false" \
    -contextHelp "Displays a name of a currently used configuration file."

pack [frame .userFrame.minimax.title -bd 1 -relief flat] -side top -fill x
APSLabeledOutput .label1 -parent .userFrame.minimax.title -packOption "-side left" \
   -label "  Number of Sliders:" -textVariable CountSliders -width 5 \
   -contextHelp "Displays a number of sliders present in the current setup."
.userFrame.minimax.title.label1.label configure -anchor c
pack [label .userFrame.minimax.title.label2 -text "Set of Sliders         " -width 50 -anchor c \
      -font {courier 12 bold}] -side left
pack [button .userFrame.minimax.title.listButton -text "List of Sliders" -command ListOfSliders \
      -highlightthickness 0 -padx 3 -pady 1 -anchor e -state disable\
      -font -adobe-courier-medium-r-normal-*-10-*-*-*-*-*-*-*] -side left 
set apsContextHelp(.userFrame.minimax.title.listButton) "Shows a list of sliders\
    in the current setup."

frame .userFrame.lines  -bd 4  -relief raised
pack .userFrame.lines -side top -fill x
set pvScroll [APSScroll .sw -parent .userFrame.lines -name "Sliders List"]
set pvScrollFrame .userFrame.lines.sw
.userFrame.lines.sw.frame.canvas configure -width 11 -height 10 
set apsContextHelp(.userFrame.lines.sw.frame.canvas) "A container for\
    a set of sliders."

.menu.savRes configure -state disabled 

if [string length $fileName] {
    LoadExistingConfiguration -fileName $fileName
}
set tcl_precision 8
