###################################################################
# This file is part of tk545, a control program for the
# Japan Radio Corp. NRD-545 receiver.
# 
#    Copyright (C) 2001, 2002, Bob Parnass
# 
# tk545 is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 2 of the License,
# or (at your option) any later version.
# 
# tk545 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with tk545; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# 02111-1307  USA
###################################################################



##########################################################
# Check if the configuration file exists.
# If it exits, return 1.
#
# Otherwise, prompt the user to select the
# serial port.
##########################################################

proc FirstTimeCheck { Rcfile } \
{
	global AboutMsg
	global GlobalParam
	global Libdir
	global tcl_platform

	if { [file readable $Rcfile] == 1 } \
		{
		return 0
		}

	tk_dialog .about "About tk545" \
		$AboutMsg info 0 OK

	# No readable config file found.
	# Treat this as the first time the user has run the program.

	# Create a new window with radio buttions and
	# an entry field so user can designate the proper
	# serial port.

	set msg "Please identify the serial port to which\n"
	set msg [append msg "your NRD-545 receiver is connected."]

	toplevel .serialport
	set sp .serialport

	label $sp.intro -text $msg

	frame $sp.rbframe
	set fr $sp.rbframe

	if { $tcl_platform(platform) == "windows" } \
		{
		# For Windows.
		radiobutton $fr.com1 -text COM1: -variable port \
			-value {COM1:}
		radiobutton $fr.com2 -text COM2: -variable port \
			-value {COM2:} 
		radiobutton $fr.com3 -text COM3: -variable port \
			-value {COM3:} 
		radiobutton $fr.com4 -text COM4: -variable port \
			-value {COM4:} 

		pack $fr.com1 $fr.com2 $fr.com3 $fr.com4 \
			-side top -padx 3 -pady 3 -anchor w

		} \
	else \
		{
		# For unix, mac, etc..
		radiobutton $fr.s0 -text /dev/ttyS0 -variable port \
			-value {/dev/ttyS0} 
		radiobutton $fr.s1 -text /dev/ttyS1 -variable port \
			-value {/dev/ttyS1} 
		radiobutton $fr.s2 -text /dev/ttyS2 -variable port \
			-value {/dev/ttyS2} 
		radiobutton $fr.s3 -text /dev/ttyS3 -variable port \
			-value {/dev/ttyS3} 
		radiobutton $fr.s4 -text /dev/ttyS4 -variable port \
			-value {/dev/ttyS4} 
		radiobutton $fr.s5 -text /dev/ttyS5 -variable port \
			-value {/dev/ttyS5} 

		pack \
			$fr.s0 $fr.s1 $fr.s2 $fr.s3 $fr.s4 $fr.s5 \
			-side top -padx 3 -pady 3 -anchor w

		}

	radiobutton $fr.other -text "other (enter below)" \
		-variable port \
		-value other

	entry $fr.ent -width 30 -textvariable otherport

	pack $fr.other $fr.ent \
		-side top -padx 3 -pady 3 -anchor w

	button $sp.ok -text "OK" \
		-command \
			{ \
			global GlobalParam

			if {$port == "other"} \
				{
				set GlobalParam(Device) $otherport
				} \
			else \
				{
				set GlobalParam(Device) $port
				}
			}

	button $sp.exit -text "Exit" \
		-command { exit }

	pack $sp.intro -side top -padx 3 -pady 3
	pack $fr -side top -padx 3 -pady 3
	pack $sp.ok $sp.exit -side left -padx 3 -pady 3 -expand true



	bind $fr.ent <Key-Return> \
		{
		global GlobalParam
		set GlobalParam(Device) $otherport
		}

	wm title $sp "Select serial port"
	wm protocol $sp WM_DELETE_WINDOW {exit}

	set errorflag true

	while { $errorflag == "true" } \
		{
		tkwait variable GlobalParam(Device)

		if { $tcl_platform(platform) != "unix" } \
			{
			set errorflag false
			break
			}

		# The following tests do not work properly
		# in Windows. That is why we won't perform
		# the serial port tests when running Windows version.

		if { ([file readable $GlobalParam(Device)] != 1) \
			|| ([file writable $GlobalParam(Device)] != 1)}\
			{
			# Device must be readable, writable.

			bell
			tk_dialog .badport "Serial port problem" \
				"Serial port problem" error 0 OK
			} \
		else \
			{
			set errorflag false
			}
		}

	destroy $sp
	return 1
}

##########################################################
# ExitApplication
#
# This procedure can do any cleanup necessary before
# exiting the program.
#
# Save the setup info.
# Disable computer control of the radio, then quit.
##########################################################
proc ExitApplication { } \
{
	SaveSetup
	SaveLabel
	DisableCControl
	exit
}


##########################################################
# NoExitApplication
#
# This procedure prevents the user from
# killing the window.
##########################################################
proc NoExitApplication { } \
{

	set response [tk_dialog .quitit "Exit?" \
		"Do not close this window." \
		warning 0 OK ]

	return
}
###################################################################
#
# Create one reception mode selection radiobutton widget
# named 'w', with text label 't' and value 'val'.
#
###################################################################
proc MkModeSel { w t val } \
{
	global Bwpot
	global ModeLabel
	global CurrentFreq
	global GlobalParam
	global RMode

	radiobutton $w -text $t -variable GlobalParam(Mode) \
		-value $val -command {\
			SetMode

			set tmp $RMode($GlobalParam(Mode))
			set tmp [string toupper $tmp]
			$ModeLabel configure -text $tmp

			if {$GlobalParam(TieBWtoMode) == "on"} \
				{
				SetBWm inter
				set tmp [GetBW]
				$Bwpot set $tmp
				set GlobalParam(BandWidth) $tmp
				}\
			else \
				{
				SetBW $GlobalParam(BandWidth)
				set tmp [GetBW]
				$Bwpot set $tmp
				}
			Add2History
			}
	return
}

###################################################################
# Create a radiobutton widget for each reception mode.
###################################################################
proc MkModeSwitch { f } \
{
	global Mode;
	frame $f -relief groove -borderwidth 3

	MkModeSel $f.am  "AM" $Mode(am)
	MkModeSel $f.eccsu  "ECCS/U" $Mode(eccs/u)
	MkModeSel $f.eccsl  "ECCS/L" $Mode(eccs/l)
	MkModeSel $f.ams  "AM Stereo" $Mode(ams)
	MkModeSel $f.usb  "USB" $Mode(usb)
	MkModeSel $f.lsb  "LSB" $Mode(lsb)
	MkModeSel $f.cw   "CW" $Mode(cw)
	MkModeSel $f.rtty "RTTY" $Mode(rtty)
	MkModeSel $f.fm   "FM" $Mode(fm)
	MkModeSel $f.wfm  "WFM" $Mode(wfm)


	pack \
		$f.am $f.eccsu $f.eccsl $f.ams \
		$f.usb $f.lsb $f.cw $f.rtty \
		$f.fm $f.wfm \
		-side top -anchor w

	SetMode
	return $f
}
##########################################################
#
# Read .csv input file.
# Massage contents of each line.
# Insert the info into the channel selector listbox.
#
##########################################################

proc ReadMemFile { } \
{
	global MemAgc
	global CancelXfer
	global MemCh
	global Cht
	global Chvector
	global MemFreq
	global GlobalParam
	global MemLabel
	global MemMode
	global MemSel


#	for {set i 0} {$i < 1000} {incr i} \
#		{
#		set MemCh($i) 0
#		}

	set sid [open $GlobalParam(Ifilename) "r"]

	set line ""
	set i 0

	# Read entire .csv file at one time.
	set allchannels [read $sid]

	# For each line in the .csv file.
	foreach line [split $allchannels "\n" ] \
		{
		update
		# if {$CancelXfer} then {break}
		# puts $line

		incr i
		if { $i > 1 } then\
			{
			# Delete double quote characters.
			regsub -all "\"" $line "" bline
			set line $bline

			if {$line == ""} then {continue}
		
			set fault [ParseCsvLine $line]
			if { $fault != "" } \
				{
				set msg [GenErrMsg $line $fault]
				set response [tk_dialog .badcsv \
					"Error in file" \
					$msg error 0 OK Exit]
				if {$response == 0} then {continue} \
				else {DisableCControl; exit}
				}

			set mlist [split $bline ","]
			set ch [lindex $mlist 0]

			if {$ch != ""} then \
				{

				# Add frequency and label
				# to a cache

				Add2LabelCache \
					$MemFreq($ch) \
					$MemLabel($ch)
				}
			}
		}
	FormatChannelList
	close $sid

	wm title $Cht [ Basename $GlobalParam(Ifilename) ]
	return
}

proc MkNRSel { w t val } \
{

	radiobutton $w -text $t -variable WhichNR \
		-value $val -command "SetNRBC"
	return
}

proc FormatChannelList { } \
{
	global Chvector
	global MemCh MemFreq MemMode MemSel MemAgc MemLabel
	global NBanks
	global NChanPerBank
	global GlobalParam

	set sep "---------------------------------------------------"

	set prevbn -1
	set ch -1
	set Chvector ""

	for {set bn 0} {$bn < $NBanks} {incr bn} \
		{
		for {set i 0} {$i < $NChanPerBank} {incr i} \
			{
			incr ch
			if {$bn != $prevbn} \
				{
				set prevbn $bn
				lappend Chvector $sep
				}

			if {[info exists MemFreq($ch)] == 0} \
				{
				continue
				} \
			elseif {$MemFreq($ch) < .000001} \
				{
				continue
				}

			set s [format "%3d %11.6f %-6s %3.1f %-5s %-s" \
				$ch $MemFreq($ch) \
				$MemMode($ch) \
				$MemAgc($ch) \
				$MemSel($ch) \
				$MemLabel($ch)  ]

			lappend Chvector $s
			}
		}

	return
}

##########################################################
# Creat a Noise Reduction/Beat Canceler set of radiobuttons
##########################################################

proc MkNRSwitch { f } \
{

	MkNRSel $f.off "Off" 0
	MkNRSel $f.nr "Noise reduction" 1
	MkNRSel $f.bc "Beat canceler" 2

	pack \
		$f.off $f.nr $f.bc \
		-side top -anchor w

	return
}

proc InitNRSwitch { f } \
{
	global WhichNR
	set WhichNR 0

	SetNRBC

	$f.off invoke
	$f.nr deselect 
	$f.bc deselect 

	return
}
###################################################################
# Parse one line of a .csv file and extract the fields.
#
# Returns:
#	""	-line passes parsing tests
#	error	-line contains an error, this is the reason
###################################################################

proc ParseCsvLine { line } \
{
	global Mode
	global MemCh MemFreq MemMode MemSel MemAgc MemLabel

	# Skip empty lines.
	if {$line == ""} {return "Empty line."}
	set mlist [split $line ","]

	set n [llength $mlist] 
	set m [ expr {6 - $n} ]

	# Add empty fields to the end of the line
	# if there are too few fields.

	for {set i 0} {$i < $m} {incr i} \
		{
		append line ","
		}

	set mlist [split $line ","]


	set frq [lindex $mlist 1]
	if { [string compare $frq ""] == 0 } {return "No frequency."}
	if { $frq <= 0 } then {return "Invalid frequency."}


	set ch [lindex $mlist 0]
	set ch [string trimleft $ch 0]
	if { $ch == "" } then \
		{
		set ch 0
		}
	if { $ch < 0 || $ch > 999 } {return "Channel must be 0 - 999."}
	set MemFreq($ch) [FormatFreq $frq ]

	set mode [lindex $mlist 2]
	if {[ info exists Mode($mode) ] == 0 } \
		{return "No such mode."}
	set MemMode($ch) $mode

	set sel [lindex $mlist 3]
	# Default selectivity
	if { $sel == "" } {set sel "inter"}

	if { $sel != "wide" && $sel != "inter" && $sel != "narr"  } \
		{return "Selectivity must be wide, inter, or narr."}
	set MemSel($ch) $sel

	set agc [lindex $mlist 4]

	# Default AGC
	if {$agc == ""} {set agc 1.0}
	if { $agc < .01 || $agc > 5.1 } {return "AGC must be .01 - 5.1"}
	set MemAgc($ch) $agc

	set MemLabel($ch) [lindex $mlist 5]

	set MemCh($ch) 1
	return ""
}


proc GenErrMsg { s cause } \
{
	global GlobalParam

	set msg ""
	append msg "The following line in file\n"
	append msg "$GlobalParam(Ifilename) "
	append msg "contains an error:\n\n"
	append msg "$s\n\n"
	if {$cause != ""} \
		{
		append msg "$cause\n\n"
		}
	append msg "Continue or exit the program?"

	return $msg
}

##########################################################
# Contruct the top row of pulldown menus
##########################################################
proc MakeMenuBar { f } \
{
	global AboutMsg
	global Device
	global FileTypes
	global GlobalParam
	global Pgm
	global Version

	# File pull down menu
	frame $f -relief groove -borderwidth 3

	menubutton $f.file -text "File" -menu $f.file.m \
		-underline 0
	menubutton $f.view -text "View" -menu $f.view.m \
		-underline 0
	menubutton $f.tweaks -text "Tweaks" -menu $f.tweaks.m \
		-underline 0
	menubutton $f.options -text "Options" -menu $f.options.m \
		-underline 0
	menubutton $f.rtty -text "RTTY" -menu $f.rtty.m \
		-underline 3
	menubutton $f.presets -text "Presets" -menu $f.presets.m \
		-underline 0
	menubutton $f.log -text "Log" -menu $f.log.m \
		-underline 0
	menubutton $f.radio -text "Radio" -menu $f.radio.m \
		-underline 0
	menubutton $f.help -text "Help" -menu $f.help.m \
		-underline 0
	
	
	menu $f.view.m
	AddView $f.view.m

	menu $f.options.m
	AddOptions $f.options.m

	set hint ""
	set hint [append hint "To tear off the Options menu, "]
	set hint [append hint "click on Options, "]
	set hint [append hint "then click on the dotted line."]
	balloonhelp_for $f.options $hint

	menu $f.tweaks.m
	AddTweaks $f.tweaks.m

	set hint ""
	set hint [append hint "To tear off the Tweaks menu, "]
	set hint [append hint "click on Tweaks, "]
	set hint [append hint "then click on the dotted line."]
	balloonhelp_for $f.tweaks $hint

	
	menu $f.help.m
	$f.help.m add command -label "Tcl info" \
		-underline 0 \
		-command { \
			tk_dialog .about "Tcl info" \
				[HelpTclInfo] info 0 OK
			}

	$f.help.m add command -label "License" \
		-underline 0 \
		-command { \
			set helpfile [format "%s/COPYING" $Libdir ]
			set win [textdisplay_create "Notice"]
			textdisplay_file $win $helpfile
			}
	
	$f.help.m add command -label "About tk545" \
		-underline 0 \
		-command { \
			tk_dialog .about "About tk545" \
				$AboutMsg info 0 OK
			}
	
	menu $f.file.m -tearoff no
	AddFile $f

	menu $f.radio.m -tearoff no
	AddRadio $f.radio.m

	menu $f.rtty.m
	AddRtty $f.rtty.m

	menu $f.presets.m
	AddPresets $f.presets.m

	menu $f.log.m
	AddLog $f.log.m

	set hint ""
	set hint [append hint "Quickly tune the radio "]
	set hint [append hint "to preset frequencies."]
	balloonhelp_for $f.presets $hint

	pack $f.file $f.view $f.tweaks $f.options \
		$f.rtty $f.presets $f.log $f.radio -side left
	pack $f.help -side right
	
	update
	return $f
}


####################################################################
# Freq Display
####################################################################
proc FreqDisplay { f } \
{
	global AMLockLabel
	global ChanLabel
	global CurrentFreq
	global DisplayFontSize
	global GlobalParam
	global Label
	global LabelLabel
	global MeterFrame
	global ModeLabel
	global Readout
	global StereoLabel


	frame $f -relief flat -background black -borderwidth 0
	set lf [frame $f.lf -relief flat -background black \
		-borderwidth 0]

	set MeterFrame $f.meter
	MakeMeter


	set lw 9

	set ChanLabel $lf.chan
	label $ChanLabel -text "VFO" -borderwidth 3 -width $lw \
		-justify left \
		-background black -foreground yellow


	set ModeLabel $lf.mode
	label $ModeLabel -text "    " -borderwidth 3 -width $lw \
		-justify left \
		-background black -foreground orange

	set AMLockLabel $lf.ecsslock
	label $AMLockLabel -text "AM locked" -borderwidth 3 \
		-justify left \
		-width $lw \
		-background black -foreground black

	set StereoLabel $lf.stereo
	label $StereoLabel -text "Stereo" -borderwidth 3 \
		-justify left \
		-width $lw \
		-background black -foreground black

	set LabelLabel $lf.label
	label $LabelLabel -text "" -borderwidth 3 \
		-justify left \
		-width $lw \
		-background black -foreground white

	pack $ChanLabel \
		$ModeLabel \
		$AMLockLabel \
		$StereoLabel \
		$LabelLabel \
		-anchor w -side top -expand y

		# -font {Courier $DisplayFontSize bold} 

	entry $f.display -width 12 \
		-font $DisplayFontSize \
		-textvariable CurrentFreq \
		-relief flat \
		-borderwidth 0 \
		-background black \
		-foreground yellow


	set Readout $f.display
	
	label $f.units -text "MHz" -borderwidth 3

	set hint ""
	set hint [append hint "You can enter a new frequency\n"]
	set hint [append hint "by using the mouse and keyboard\n"]
	set hint [append hint "to edit this field.\n"]
	set hint [append hint "Numbers ending in k are interpreted "]
	set hint [append hint "in kHz."]
	balloonhelp_for $f.display $hint

	set CurrentFreq [FormatFreq "0"]
	pack $f.meter -pady 3 -padx 10 -side left
	pack $lf -padx 4 -side left
	# pack $f.chan -padx 4 -side left
	pack $f.display -padx 10 -side left
	pack $f.units -padx 4 -side left
	
	bind $f.display <Key-Return> \
		{

		# Trim leading spaces or zeros.
		set tmp $CurrentFreq
		regsub {^[ 0]*} $tmp "" CurrentFreq

		set units mhz

		if { [regexp {[mM]$} $CurrentFreq] } \
			{
			# Ends in m or M so number is MHz
			set units mhz 
			regsub {[mM]$} $CurrentFreq {} CurrentFreq
			} \
		elseif { [regexp {[kK]$} $CurrentFreq] } \
			{
			# Ends in k or K so number is kHz
			set units khz
			regsub {[kK]$} $CurrentFreq "" CurrentFreq
			}

		if { [CheckFreqValid $CurrentFreq $units] } then \
			{
			if { $units == "khz" } \
				{
				set CurrentFreq [ expr { double($CurrentFreq) \
					/ double(1000) } ]
				}
			set CurrentFreq [FormatFreq $CurrentFreq ]

			SetSlideRuleDial
			set hz [expr {$CurrentFreq * 1000000}]
			set hz [expr {round($hz)}]
			SetFreq $hz
			set GlobalParam(PreviousFreq) $CurrentFreq

			# Update BW slider to reflect the
			# radio's current selectivity.
			set tmp [GetBW]
			$Bwpot set $tmp
			set GlobalParam(BandWidth) $tmp
			Add2History


			# Get and display frequency alpha label
			# from label cache.
			UpdLabel
	
			} \
		else \
			{
			# Invalid frequency.
			bell
			set CurrentFreq $GlobalParam(PreviousFreq)
			set CurrentFreq [FormatFreq $CurrentFreq ]
			}
		}
	return $f
}
####################################################################
# Noise Reduction and Beat Canceler controls
####################################################################

proc NoiseReducer { mf }\
{
	global GlobalParam

	frame $mf -relief groove -borderwidth 3
	frame $mf.noiser -relief sunken -borderwidth 3
	set nr $mf.noiser
	
	frame $nr.nrrb -relief sunken -borderwidth 3
	set nrrbf $nr.nrrb
	pack $nrrbf -side top
	
	checkbutton $nr.lenhance -text "NR hold" \
		-variable line_enhance \
		-onvalue "0" \
		-offvalue "1" \
		-command { SetLE $line_enhance }
	
	pack $nr.lenhance -side top
	SetLE 1
	
	
	# Noise reduction/beat canceler
	MkNRSwitch $nrrbf
	InitNRSwitch $nrrbf
	
	scale $nr.level -from 0 -to 255 -label "NR Level" \
		-variable GlobalParam(NRlevel) \
		-orient horizontal  -command SetNRLevel
	$nr.level set $GlobalParam(NRlevel)
	
	scale $nr.bclevel -from 0 -to 255 -label "BC Level" \
		-variable GlobalParam(BClevel) \
		-orient horizontal  -command SetBC
	$nr.bclevel set $GlobalParam(BClevel)
	
	pack $nr.level $nr.bclevel -side top
	pack $nr
	
	
	return $mf
}

####################################################################
# Noise Noise Blanker controls
####################################################################

proc NoiseBlanker { f }\
{
	global GlobalParam

	frame $f -relief groove -borderwidth 3
	frame $f.nbbut -relief sunken -borderwidth 3
	MkNBSwitch  $f.nbbut
	InitNBSwitch $f.nbbut


	scale $f.nblevel -from 0 -to 255 -label "NB Level" \
		-variable GlobalParam(NBlevel) \
		-orient horizontal  -command SetNBlevel
	$f.nblevel set $GlobalParam(NBlevel)

	pack $f.nbbut -side top -fill x
	pack $f.nblevel -side top -fill x

	return $f
}


proc MkNBSel { w t val } \
{

	radiobutton $w -text $t -variable WhichNB \
		-value $val -command "SetNB"
	return
}


##########################################################
# Creat a Noise Blanker set of radiobuttons
##########################################################

proc MkNBSwitch { f } \
{

	MkNBSel $f.off "Off" 0
	MkNBSel $f.nb1 "Noise blanker 1" 1
	MkNBSel $f.nb2 "Noise blanker 2" 2

	pack \
		$f.off $f.nb1 $f.nb2 \
		-side top -anchor w

	return $f
}


proc InitNBSwitch { f } \
{
	global WhichNB
	set WhichNB 0
	SetNB


	$f.off invoke
	$f.nb1 deselect 
	$f.nb2 deselect 


	return
}

####################################################################
# Make two rows of potentiometers
####################################################################

proc MakePots { f } \
{
	global Bwpot
	global GlobalParam

	frame $f -relief flat -borderwidth 3
	frame $f.basic -relief flat -borderwidth 3
	frame $f.adv -relief flat -borderwidth 3
	set p1 $f.basic
	set p2 $f.adv
	
	
	scale $p1.af -from 0 -to 250 -label "Volume" \
		-variable GlobalParam(Volume) \
		-resolution 5 \
		-orient horizontal  -command SetAF -troughcolor green 
	$p1.af set $GlobalParam(Volume)
	
	scale $p1.tone -from 0 -to 250 -label "Tone" \
		-variable GlobalParam(Tone) \
		-resolution 10 \
		-orient horizontal  -command SetTone
	$p1.tone set $GlobalParam(Tone)
	
	scale $p1.squelch -from 0 -to 250 -label "Squelch" \
		-orient horizontal  -command SetSquelch \
		-troughcolor yellow
	$p1.squelch set 0
	
	scale $p1.agc -from 40 -to 5100 -label "AGC (ms)" \
		-orient horizontal  -command SetAGC \
		-resolution 20 \
		-variable GlobalParam(AGCms)
	$p1.agc set $GlobalParam(AGCms)
	
	scale $p2.rf -from 0 -to 255 -label "RF Gain" \
		-resolution 5 \
		-orient horizontal  -command SetRF
	$p2.rf set 255
	
	scale $p2.bw -from 10 -to 9999 -label "IF Bandwidth (Hz)" \
		-resolution 10 \
		-variable GlobalParam(BandWidth) \
		-orient horizontal  -command SetBW -troughcolor orange
	$p2.bw set $GlobalParam(BandWidth)
	set Bwpot $p2.bw
	
	
	scale $p2.pbs -from -2200 -to 2200 -label "Pass Band Shift (Hz)" \
		-resolution 50 \
		-variable GlobalParam(PassBandShift) \
		-orient horizontal  -command SetPBS
	# $p2.pbs set 0


	

	pack $p1.af $p1.tone $p1.squelch $p1.agc -side left \
		-fill x -expand y -padx 3
	pack $p2.rf $p2.bw $p2.pbs -side left -fill x -expand y \
		-padx 3
	pack $p1 -side top -fill x
	pack $p2 -side top -fill x

	return $f
}


proc MkNotchSel { w t val } \
{

	radiobutton $w -text $t -variable WhichNotch \
		-value $val -command "SetNotch"
	return
}


##########################################################
# Creat a Notch set of radiobuttons
##########################################################

proc MkNotchSwitch { f } \
{
	frame $f -relief sunken -borderwidth 3

	MkNotchSel $f.off "Off" 0
	MkNotchSel $f.notch1 "Notch on" 1
	MkNotchSel $f.notch2 "Notch tracking on" 2

	pack \
		$f.off $f.notch1 $f.notch2 \
		-side top -anchor w

	InitNotchSel $f

	return $f
}

####################################################################
# Initialize notch filter to off.
####################################################################

proc InitNotchSel { f } \
{
	global WhichNotch
	set WhichNotch 0
	SetNotch


	$f.off invoke
	$f.notch1 deselect 
	$f.notch2 deselect 

	return
}

####################################################################
# Make IF notch filter frame
####################################################################
proc MakeNotchFrame { f } \
{
	frame $f -relief groove -borderwidth 3
	# frame $f.notchswitch -relief groove -borderwidth 3
	
	set fns [ MkNotchSwitch $f.fns ]
	
	scale $f.notchfreq -from -2500 -to 2500 -label "Notch (Hz)" \
		-resolution 10 \
		-orient horizontal  -command SetNotchFreq
	$f.notchfreq set 0

	pack $fns $f.notchfreq -side top -fill x

	return $f
}


###################################################################
# Check to see if someone is running another
# copy of  this program.
#
# Code Snippit by DJ Eaton.
# Ben Mesander helped with Mac OS X compatibility.
#
# Warning: works in Linux and Mac OS X only, not Solaris
#
###################################################################
proc CheckForDup { } \
{
	global argv0
	global Pgm

	set filename [lindex [split $argv0 "/"] end]
	set ppid [lindex \
		[exec ps xwww \| grep -i wish \| grep $filename] 0]

	if {$ppid != [pid] } \
		{
		puts stderr "$Pgm: A copy of this program is already running.\n"
		exit 1
		}
	unset ppid filename
	return;
}


###################################################################
# Change the current frequency by adding 'delta' MHz.
###################################################################
proc Qsy { delta } \
{
	global ChanLabel
	global CurrentFreq
	global GlobalParam

	# We are deviating off a channel so wipe out the
	# channel number label.

	$ChanLabel configure -text "VFO"

	set CurrentFreq [expr {$CurrentFreq + $delta}]
	if { [CheckFreqValid $CurrentFreq mhz] } then \
		{
		set CurrentFreq [FormatFreq $CurrentFreq ]
		set newfreq [expr {$CurrentFreq * 1000000}]
		set newfreq [expr {round($newfreq)}]
		SetFreq $newfreq
		set GlobalParam(PreviousFreq) $CurrentFreq
		update
		SetSlideRuleDial
		} \
	else \
		{
		# Invalid frequency.
		bell
		set CurrentFreq [FormatFreq $GlobalParam(PreviousFreq)]
		}
	UpdLabel
	return

}
###################################################################
# Scan a range of memory channels
###################################################################

proc ScanMemory { first last } \
{
	global MemCh
	global ChanLabel
	global CurrentFreq
	global MemFreq
	global GlobalParam
	global ScanFlag

	if { ( [info exists MemCh ] == 0 ) \
		|| ( [info exists MemCh($first) ] == 0) \
		|| ( [info exists MemCh($last) ] == 0) } \
		{
		return
		}

	while { ($ScanFlag > 0) && [info exists MemCh] } \
		{
		for {set i $first} { $i <= $last && $ScanFlag > 0} \
			{incr i} \
			{
			if { ([info exists MemCh($i)] == 0) \
				|| ($MemCh($i) <= 0)} \
				{
				# Nothing programmed in this channel.
				continue
				}
			SetLChannel $i
			set tmp [format "Ch %3d" $i]
			$ChanLabel configure -text $tmp

			set tmp [ InsertCommas $MemFreq($i) ]
			set CurrentFreq $tmp

			SetSlideRuleDial

			set GlobalParam(PreviousFreq) $tmp
			Idle 500
			while { [set sq [ReadSquelchStatus]] > 0 } \
				{
				# Squelch open, presume signal present.
				if {$GlobalParam(Debug) > 0 }\
					{
					Tattle "sq $sq"
					}
				# Interruptible delay
				Idle 4000
				}
			if {$GlobalParam(Debug) > 0 }\
				{
				Tattle "sq $sq out of loop"
				}
			}
		}
	return
}

###################################################################
# Search between lower and upper frequency limits
###################################################################
proc Search { l u } \
{
	global CurrentFreq
	global GlobalParam
	global PreviousFreq

	set GlobalParam(SearchFlag) 1

	set ts [expr { 1000 * $GlobalParam(SearchStep) } ]
	SetTS $ts

	# If current frequency is outside the limits,
	# set it to the lower limit.

	if { ($CurrentFreq < $l) || ($CurrentFreq > $u) } \
		{
		set CurrentFreq [FormatFreq $l ]
		set hz [expr {$CurrentFreq * 1000000}]
		set hz [expr {round($hz)}]
		SetFreq $hz
		set GlobalParam(PreviousFreq) $CurrentFreq
		SetSlideRuleDial
		}

	waiter $GlobalParam(SlewSpeed)
	Idle 200

	set updown up
 
	if { $updown == "down" } \
		{
		set amount [ expr { -1 * $ts } ]
		} \
	else \
		{
		set amount $ts
		}

	while { $GlobalParam(SearchFlag) } \
		{
		SendArrow $updown
		AlterDisplay $amount
		
		waiter $GlobalParam(SlewSpeed)
		Idle 200

		while { [set sq [ReadSquelchStatus]] > 0 } \
			{
			# Squelch open, presume signal present.
			if {$GlobalParam(Debug) > 0 }\
				{
				Tattle "sq $sq"
				}
			# Interruptible delay
			Idle 4000
			}
		if {$GlobalParam(Debug) > 0 }\
			{
			Tattle "sq $sq out of loop"
			}
		if {$CurrentFreq >= $u } \
			{
			set CurrentFreq [FormatFreq $l ]
			set hz [expr {$CurrentFreq * 1000000}]
			set hz [expr {round($hz)}]
			SetFreq $hz
			set GlobalParam(PreviousFreq) $CurrentFreq
			SetSlideRuleDial
#			puts stderr "resetting cur freq to $CurrentFreq"
			}
		}

	return
}

###################################################################
# Create memory scan widgets. 
###################################################################
proc MakeMemScanFrame { f }\
{
	global MemCh
	global ChanLabel
	global GlobalParam

	frame $f -relief groove -borderwidth 3
	label $f.firstl -text "First ch" -borderwidth 3
	entry $f.first -width 4 \
		-textvariable firstch \
		-background white 

	label $f.lastl -text "Last ch" -borderwidth 3
	entry $f.last -width 4 \
		-textvariable lastch \
		-background white 

	button $f.memscanstart -text "Scan" \
		-command {\
			global GlobalParam
			if {$GlobalParam(Ifilename) != ""} \
				{
				if { ([info exists MemCh ]) } \
					{
					set ScanFlag 1;

					ScanMemory $firstch $lastch
					} \
				else \
					{
					CannotScan
					}
				} \
			else \
				{
				CannotScan
				}
			}


	button $f.memscanstop -text "Stop" \
		-command {\
			set ScanFlag 0
			}

	pack $f.firstl \
		$f.first \
		$f.lastl \
		$f.last \
		$f.memscanstart \
		$f.memscanstop \
		-side top -padx 3 -fill x
	return $f
}

###################################################################
# Validate frequency search limits
#
# Returns:
#	0	- limits invalid
#	1	- limits valid
###################################################################
proc CheckLimitsValid { l u } \
{
	if { [CheckFreqValid  $l mhz] == 0 } \
		{
		return 0
		} \
	elseif { [CheckFreqValid  $u mhz] == 0 } \
		{
		return 0
		} \
	elseif { $l >= $u } \
		{
		return 0
		} \
	else \
		{
		return 1
		}
}

###################################################################
# Create search widgets. 
###################################################################
proc MakeSearchFrame { f }\
{
	global ChanLabel
	global GlobalParam

	frame $f -relief groove -borderwidth 3
	label $f.lowerl -text "Lower freq MHz" -borderwidth 3
	entry $f.lower -width 12 \
		-textvariable lowerfreq \
		-background white 

	# Set default value
	if {$GlobalParam(LowerLimit) > 0} \
		{
		$f.lower insert 0 $GlobalParam(LowerLimit)
		}

	label $f.upperl -text "Upper freq MHz" -borderwidth 3
	entry $f.upper -width 12 \
		-textvariable upperfreq \
		-background white 

	# Set default value
	if {$GlobalParam(UpperLimit) > 0} \
		{
		$f.upper insert 0 $GlobalParam(UpperLimit)
		}


	label $f.stepl -text "Step size kHz" -borderwidth 3
	tk_optionMenu $f.stepmenu GlobalParam(SearchStep) 10 1 .1 .01

	button $f.searchstart -text "Search" \
		-command {\
			if { [CheckLimitsValid \
				$lowerfreq $upperfreq] } \
				{
				set GlobalParam(LowerLimit) $lowerfreq
				set GlobalParam(UpperLimit) $upperfreq

				$ChanLabel configure -text "SEARCH"
				Search $lowerfreq $upperfreq
				} \
			else \
				{
				tk_dialog .error "tk545" \
					"Search limit error" error 0 OK
				}
			}

	button $f.searchstop -text "Stop" \
		-command {\
			set GlobalParam(SearchFlag) 0 
			$ChanLabel configure -text "VFO"
			}


	pack $f.lowerl \
		$f.lower \
		$f.upperl \
		$f.upper \
		$f.stepl \
		$f.stepmenu \
		$f.searchstart \
		$f.searchstop \
		-side top -padx 3 -fill x
	return $f
}

###################################################################
# Set radio to logical channel.
#
# The proc send commands to the radio to set
# the frequency, mode, bandwidth, and AGC decay time
# according to the settings for channel "ch" in
# our global arrays.
#
# We will drive the radio with these logical channel
# settings from our software arrays instead of relying
# on the contents of what is actually programmed
# in the radio's memory channels.
###################################################################

proc SetLChannel { ch } \
{
	global MemAgc
	global MemCh
	global CurrentFreq
	global MemFreq
	global GlobalParam
	global Mode
	global MemMode
	global ModeLabel
	global RMode
	global MemSel

	set freq $MemFreq($ch)

	# Trim leading spaces and zeroes.
	regsub {^[0 ]*} $freq "" tmp
	set freq $tmp

	if { [CheckFreqValid $freq mhz] } then \
		{
		set CurrentFreq [FormatFreq $freq ]
		set hz [expr {$freq * 1000000}]
		set hz [expr {round($hz)}]
		SetFreq $hz
		set GlobalParam(PreviousFreq) $CurrentFreq

		set tmp $MemMode($ch)
		set GlobalParam(Mode) $Mode($tmp)

		# Update the mode label on the display.
		set tmp $RMode($GlobalParam(Mode))
		set tmp [string toupper $tmp]
		$ModeLabel configure -text $tmp

		SetMode

		SetBWm $MemSel($ch)
		set GlobalParam(AGCms) [expr {1000 * $MemAgc($ch)}]
		SetAGC $GlobalParam(AGCms)
		} \
	else \
		{
		# Invalid frequency.
		bell
		set CurrentFreq $GlobalParam(PreviousFreq)
		set CurrentFreq [FormatFreq $CurrentFreq ]
		}

	# SetChannel $ch
	return
}


##########################################################
# Make channel list frame
#
# Inputs:
#	f
#	filename	-name of file to read
#			-if it is the null string, prompt
#			the user for the file name.
##########################################################

proc MakeChannelListFrame { f filename }\
{
	global Chb
	global GlobalParam
	global Chvector
	global FileTypes
	global Mimage


	if {$filename == ""} \
		{
		set GlobalParam(Ifilename) [ Mytk_getOpenFile $f \
			$GlobalParam(MemoryFileDir) \
			"tk545" $FileTypes ]
		} \
	else \
		{
		set GlobalParam(Ifilename) $filename
		}

#	set GlobalParam(Ifilename) [tk_getOpenFile -parent $f \
#		-initialdir $GlobalParam(MemoryFileDir) \
#		-title "tk545" -filetypes $FileTypes]

	if {$GlobalParam(Ifilename) == ""} then {return ""}
	set Chvector ""
	ReadMemFile
	
	set GlobalParam(MemoryFileDir) \
		[ Dirname $GlobalParam(Ifilename) ]

	ShowChannels $f
	set GlobalParam(Populated) 1

	set Mimage ""
	return $f.lch
}

##########################################################
# Show memory channels in a window.
##########################################################

proc ShowChannels { f } \
{
	global Bwpot
	global ChanLabel
	global Chb
	global CurrentFreq
	global MemFreq
	global GlobalParam
	global Chvector
	global FileTypes
	global Home
	global Label
	global LabelLabel

	catch {destroy $f.lch}

	FormatChannelList

	set Chb [ List_channels $f.lch $Chvector 40 ]
	wm deiconify $f
	$Chb activate 1
	pack $f.lch -side top
	# pack $f -side top
	
	# Tune radio to given memory channel when user clicks
	# mouse on channel selector listbox entry.
	
	bind $Chb <ButtonRelease-1> \
		{
		set chann [ global Chb; ChSelected $Chb ]

		if {$chann >= 0} \
			{
			# Tune radio to the channel.
			SetLChannel $chann
	
			# Update the channel label display.
			set tmp [FormatChan $chann]
			if {$GlobalParam(Debug) > 0 }\
				{
				;# puts stderr "chann $chann , tmp $tmp"
				}
	
			$ChanLabel configure -text $tmp
			set tmp [ InsertCommas $MemFreq($chann) ]
	
	
			# Update the frequency display.
			set CurrentFreq $tmp
			set GlobalParam(PreviousFreq) $tmp
			SetSlideRuleDial
	
			# Update BW slider to reflect the radio's current
			# selectivity.
			set tmp [GetBW]
			$Bwpot set $tmp
			set GlobalParam(BandWidth) $tmp
			Add2History
	
	
			# Get and display frequency alpha label
			# from label cache.
			UpdLabel
			}
		}

	return $f.lch
}

###################################################################
# Create a new history window and populate it with
# frequencies we've visited, mode and timestamp.
###################################################################

proc RefreshHistory {} \
{
	global GlobalParam
	global HistoryFreq
	global HistoryLabel
	global HistoryMode
	global HistoryTime
	global HistVector

	set HistVector ""
	for {set i 0} {$i < 1000} {incr i} \
		{
		if {[info exists HistoryTime($i)] == 0} {break}
		if {$HistoryTime($i) <= 0} {break}

		set s [format "%3d %11.6f %-6s %-8s %s" \
			$i \
			$HistoryFreq($i) \
			$HistoryMode($i) \
			$HistoryLabel($i) \
			$HistoryTime($i)]
		lappend HistVector $s
		}

	set f .hist
	catch {destroy $f}
	toplevel $f
	wm title $f "tk545 Session History"

	if {$GlobalParam(ViewHistory) == "off"} \
		{
		# Hide window because user does not want to view it.
		wm iconify $f
		}

	MakeHistoryListFrame $f
	return
}

##########################################################
# Make session history list frame
##########################################################

proc MakeHistoryListFrame { f }\
{
	global Bwpot
	global ChanLabel
	global Histb
	global CurrentFreq
	global MemFreq
	global GlobalParam
	global HistVector
	global ModeLabel
	global RMode

	
	# RefreshHistory

	set Histb [ List_channels $f.hch $HistVector 10 ]
	$Histb activate 1
	pack $f.hch -side top
	
	# Tune radio to given history freq entry
	# and set the mode when user clicks
	# the mouse button on history selector listbox entry.
	
	bind $Histb <ButtonRelease-1> \
		{
		set chann [ global Histb; ChSelected $Histb ]

		$ChanLabel configure -text VFO

		# Update the frequency display.
		set CurrentFreq $HistoryFreq($chann)
		set GlobalParam(PreviousFreq) $CurrentFreq

		# Get and display frequency alpha label
		# from label cache.
		SetSlideRuleDial


		UpdLabel

		# Tune radio to the channel.
                set hz [expr {$CurrentFreq * 1000000}]
                set hz [expr {round($hz)}]
                SetFreq $hz

		set tmp $HistoryMode($chann)
		set GlobalParam(Mode) $Mode($tmp)

		# Update the mode label on the display.
		set tmp $RMode($GlobalParam(Mode))
		set tmp [string toupper $tmp]
		$ModeLabel configure -text $tmp

		SetMode

		# Update bandwidth slider to reflect radio bw
		set tmp [GetBW]
		$Bwpot set $tmp
		set GlobalParam(BandWidth) $tmp

		}

	return $f.hch
}


proc MakeUpDownFrame { g } \
{
	frame $g -borderwidth 3 -relief groove
	frame $g.but -borderwidth 3 -relief groove
	set f $g.but

	foreach inc { 10k 9k 5k 1k 100 10 1 } \
		{
		frame $f.f$inc -borderwidth 3 -relief flat
		}

	button $f.f10k.down10k	-text "-10 kHz" -command {Qsy -0.01}
	button $f.f9k.down9k	-text "-9 kHz" -command {Qsy -0.009}
	button $f.f5k.down5k	-text "-5 kHz" -command {Qsy -0.005}
	button $f.f1k.down1k	-text "-1 kHz" -command {Qsy -0.001}
	button $f.f100.down100	-text "-100" -command {Qsy -0.0001}
	button $f.f10.down10	-text "-10" -command {Qsy -0.00001}
	button $f.f1.down1	-text "-1" -command {Qsy -0.000001}
	button $f.f1.up1	-text "+1" -command {Qsy 0.000001}
	button $f.f10.up10	-text "+10" -command {Qsy 0.00001}
	button $f.f100.up100	-text "+100" -command {Qsy 0.0001}
	button $f.f1k.up1k	-text "+1 kHz" -command {Qsy 0.001}
	button $f.f5k.up5k	-text "+5 kHz" -command {Qsy 0.005}
	button $f.f9k.up9k	-text "+9 kHz" -command {Qsy 0.009}
	button $f.f10k.up10k	-text "+10 kHz" -command {Qsy 0.01}


	foreach inc { 10k 9k 5k 1k 100 10 1 } \
		{
		foreach direction { up down } \
			{
			# Pack each pair of up/down buttons.
			pack $f.f$inc.$direction$inc \
				-side top -fill x
			}
		}

	foreach inc {1 10 100 1k 5k 9k 10k } \
		{
		pack $f.f$inc -side right ;# -fill x -expand y
		}

	label $g.lab -text "Single Step"
	# pack $g.lab -padx 3 -pady 3

	pack $f -side top
	return $g
}

##########################################################
# Make the frequency display impossible to read.
# Alter the channel label to show "VFO" instead.
##########################################################
proc BlankDisplay { } \
{
	global ChanLabel

	.freqdisplay.display config -foreground black
	$ChanLabel configure -text "VFO"
	return
}


##########################################################
# Make the frequency display possible to read.
##########################################################
proc RestoreDisplay { } \
{
	.freqdisplay.display config -foreground yellow
	return
}
###################################################################
# Start and stop frequency slewing (autotune).
###################################################################

proc SlewCB { amt updown } \
{
	global GlobalParam

	if { $GlobalParam(Slewing) == 1 } \
		{
		# We are currently slewing and the user
		# wants to stop.

		set GlobalParam(Slewing) 0
		IncFreq 0
		UpdDisplay
		SetSlideRuleDial
		return
		}

	# We are not slewing and the user
	# wants to start slewing.

	set GlobalParam(Slewing) 1
	StartSlew $amt $updown

	return
}

proc MakeSlewFrame { g } \
{
	global GlobalParam

	frame $g -borderwidth 3 -relief groove
	frame $g.but -borderwidth 3 -relief groove
	set f $g.but


	set slew_text(down1000)  "----"
	set slew_text(down100) "---"
	set slew_text(down10)  "--"
	set slew_text(down1)   "-"

	set slew_text(up1000)  "++++"
	set slew_text(up100) "+++"
	set slew_text(up10)  "++"
	set slew_text(up1)   "+"


	foreach inc {1 10 100 1000 } \
		{
		# Create one frame for each pair of slew buttons.

		frame $f.f$inc -borderwidth 3

		foreach updown { up down }\
			{
			button $f.f$inc.$updown \
				-width 4 \
				-text $slew_text($updown$inc) \
				-command \
					"
					global inc
					global updown
					SlewCB $inc $updown
					"

			pack $f.f$inc.$updown -side top -fill x
			}

		pack $f.f$inc -side right -fill x
		}

	# label $g.lab -text "Tune"
	# pack $g.lab -padx 3 -pady 3

	pack $f -side top
	return $g
}


###################################################################
# Read the frequency from the receiver and update the
# display widget to reflect the receiver's true frequency.
###################################################################
proc UpdDisplay {} \
{
	global CurrentFreq
	global GlobalParam
	global Label
	global LabelLabel

	set CurrentFreq [ReadFreq]
	if { [CheckFreqValid $CurrentFreq mhz] } then \
		{
		set hz [expr {$CurrentFreq * 1000000}]
		set hz [expr {round($hz)}]
		# puts "$CurrentFreq $hz"
		set CurrentFreq [FormatFreq $CurrentFreq ]
		set GlobalParam(PreviousFreq) $CurrentFreq

		# Get and display frequency alpha label
		# from label cache.
		UpdLabel
		} \
	else \
		{
		# Invalid frequency.
		bell
		set CurrentFreq $GlobalParam(PreviousFreq)
		set CurrentFreq [FormatFreq $CurrentFreq ]
		}

	# RestoreDisplay
	return
}


proc AlterDisplay { deltahz } \
{
	global CurrentFreq

	set ad [ expr {abs($deltahz)} ]

	# Force the least significant display digits to zero,
	# depending on the step size.

	if { $ad == 1000 }\
		{
		regsub "...$" $CurrentFreq "000" CurrentFreq
		} \
	elseif { $ad == 100 }\
		{
		regsub "..$" $CurrentFreq "00" CurrentFreq
		} \
	elseif { $ad == 10 }\
		{
		regsub ".$" $CurrentFreq "0" CurrentFreq
		}

	set hz [expr {$CurrentFreq * 1000000}]
	set hz [expr {$hz + $deltahz}]
	set CurrentFreq [expr {$hz / 1000000}]

	if { [CheckFreqValid $CurrentFreq mhz] } then \
		{
		set CurrentFreq [FormatFreq $CurrentFreq ]
		set GlobalParam(PreviousFreq) $CurrentFreq

		# Update the slide rule dial every 1 kHz
		if { [regexp {000$} $CurrentFreq] } \
			{
			SetSlideRuleDial
			}
		} \
	else \
		{
		# Invalid frequency.
		bell
		set CurrentFreq $GlobalParam(PreviousFreq)
		set CurrentFreq [FormatFreq $CurrentFreq ]
		}

	# RestoreDisplay
	return
}


##########################################################
# Add entries to File menu.
##########################################################
proc AddFile { f } \
{

	$f.file.m add command -label "Open ..." \
		-underline 0 \
		-command {global Cht
			global GlobalParam
			# wm deiconify $Cht;
			set fr_table [MakeChannelListFrame $Cht ""]; \
			if { $GlobalParam(Ifilename) != ""} \
				{
				pack $fr_table
				SetWinTitle
				}
			}


	$f.file.m add command -label "Save As ..." \
		-underline 0 \
		-command { \
			SaveSetup
			SaveTemplate . 1
			SaveLabel
			}

	
#	$f.file.m add command -label "Save setup" \
#		-underline 0 \
#		-command {SaveSetup; SaveLabel}

	$f.file.m add command -label "Exit" \
		-underline 1 \
		-command {ExitApplication; exit }


	return
}
	
##########################################################
# Add widgets to the view menu
##########################################################
proc AddView { m } \
{
	global GlobalParam
	global MeterFrame
	global Pri
	global Sec
	global SlideRule

	# Change font.

	if {$GlobalParam(Font) == ""} \
		{
		set msg "Change Font"
		} \
	else \
		{
		set msg [format "Change Font (%s)" $GlobalParam(Font)]
		}

	$m add command -label $msg -command \
		{
		set ft [font_select]
		if {$ft != ""} \
			{
			set GlobalParam(Font) $ft

			set msg "The change will take effect next "
			set msg [append msg "time you start tk545."]

			tk_dialog .wcf "Change Appearance" \
				$msg info 0 OK
			}
		}

	$m add command -label "Restore Original Font" -command \
		{
		set GlobalParam(Font) ""
		set msg "The change will take effect next "
		set msg [append msg "time you start tk545."]

		tk_dialog .wcf "Change Appearance" $msg info 0 OK
		}

	$m add separator

	$m add command -label "Change Panel Color" -command \
		{
		set col [tk_chooseColor -initialcolor #d9d9d9]
		if {$col != ""} \
			{
			set GlobalParam(BackGroundColor) $col

			set msg "The change will take effect next "
			set msg [append msg "time you start tk545."]

			tk_dialog .wcf "Change Appearance" \
				$msg info 0 OK
			}
		}

	$m add command -label "Change Lettering Color" -command \
		{
		set col [tk_chooseColor -initialcolor black]
		if {$col != ""} \
			{
			set GlobalParam(ForeGroundColor) $col

			set msg "The change will take effect next "
			set msg [append msg "time you start tk545."]

			tk_dialog .wcf "Change Appearance" \
				$msg info 0 OK
			}
		}

	$m add command -label "Change Slider Trough Color" -command \
		{
		set col [tk_chooseColor -initialcolor #c3c3c3]
		if {$col != ""} \
			{
			set GlobalParam(TroughColor) $col

			set msg "The change will take effect next "
			set msg [append msg "time you start tk545."]

			tk_dialog .wcf "Change Appearance" \
				$msg info 0 OK
			}
		}

	$m add separator

	# Helpful tips balloons

	$m add  checkbutton \
		-label "Balloon Help Windows" \
                -variable GlobalParam(BalloonHelpWindows) \
                -onvalue on \
                -offvalue off 


	# Helpful frequency usage note

	$m add  checkbutton \
		-label "Frequency Usage Note" \
                -variable GlobalParam(ViewNote) \
                -onvalue on \
                -offvalue off \
		-command \
			{
			set msg "The change will take effect next "
			set msg [append msg "time you start tk8500."]

			tk_dialog .wslew "Change appearance" \
				$msg info 0 OK
			}


	# Set unshift on space

	$m add  checkbutton \
		-label "Session History Window" \
                -variable GlobalParam(ViewHistory) \
                -onvalue on \
                -offvalue off \
                -command \
			{
			if {$GlobalParam(ViewHistory) == "on"} \
				{
				catch {wm deiconify .hist}
				} \
			else \
				{
				catch {wm iconify .hist}
				}
			}


	$m add  checkbutton \
		-label "Slide Rule Dial" \
                -variable GlobalParam(ViewSlideRuleDial) \
                -onvalue on \
                -offvalue off \
                -command \
			{
			set msg "The change will take effect next "
			set msg [append msg "time you start tk545."]

			tk_dialog .wcf "Change Appearance" \
				$msg info 0 OK
			}


	$m add  checkbutton \
		-label "S-Meter and Indicators" \
                -variable GlobalParam(SmeterEnable) \
                -onvalue on \
                -offvalue off \
                -command \
			{
			if {$GlobalParam(SmeterEnable) == "on"} \
				{
				$MeterFrame.c configure \
					-background white
				} \
			else \
				{
				$MeterFrame.c configure \
					-background black
				}
			}
	return
}

##########################################################
# Add widgets to the Options menu
##########################################################
proc AddOptions { m } \
{
	global GlobalParam


	# Set scan autostop

	$m add  checkbutton \
		-label "Scan auto stop" \
                -variable GlobalParam(ScanAutoStop) \
                -onvalue on \
                -offvalue off \
                -command {global GlobalParam; \
			SetScanAutoStop $GlobalParam(ScanAutoStop) }

	SetScanAutoStop $GlobalParam(ScanAutoStop)

	# Set scan skip

	$m add  checkbutton \
		-label "Skip empty channels during scanning" \
                -variable GlobalParam(SkipEmptyCh) \
                -onvalue on \
                -offvalue off \
                -command {global GlobalParam
			SetSkipEmptyCh $GlobalParam(SkipEmptyCh) }

	SetSkipEmptyCh $GlobalParam(SkipEmptyCh)

	$m add separator

	$m add  radiobutton \
		-label "Slew speed slow" \
		-variable GlobalParam(SlewSpeed) \
		-value 500


	$m add  radiobutton \
		-label "Slew speed fast" \
		-variable GlobalParam(SlewSpeed) \
		-value 100

	$m add separator

	$m add  radiobutton \
		-label "Timer relay off" \
		-variable GlobalParam(TimerRelay) \
		-value off -command {ConfigureTimerRelay off}

	$m add  radiobutton \
		-label "Timer relay on" \
		-variable GlobalParam(TimerRelay) \
		-value on -command {ConfigureTimerRelay on}

	$m add  radiobutton \
		-label "Timer relay controlled by squelch" \
		-variable GlobalParam(TimerRelay) \
		-value squelch -command {ConfigureTimerRelay squelch}


	$m add separator

	# Set colon blinking 

	$m add  checkbutton \
		-label "Blinking colon on clock display" \
                -variable GlobalParam(BlinkingColon) \
                -onvalue on \
                -offvalue off \
                -command {global GlobalParam
			SetBlinkingColon $GlobalParam(BlinkingColon) }

	SetBlinkingColon $GlobalParam(BlinkingColon)


	# Set display lighting 

	$m add  checkbutton \
		-label "Dim display lighting" \
                -variable GlobalParam(Lighting) \
                -onvalue dim \
                -offvalue bright \
                -command {global GlobalParam
			SetLighting $GlobalParam(Lighting) }

	SetLighting $GlobalParam(Lighting)



	# Option to tie bandwidth to mode switch

	$m add  checkbutton \
		-label "Tie Bandwidth to Mode" \
                -variable GlobalParam(TieBWtoMode) \
                -onvalue  on \
                -offvalue off \
                -command {\
			global GlobalParam
			if {$GlobalParam(TieBWtoMode) == "on"} \
				{
				SetBWm inter
				set tmp [GetBW]
				$Bwpot set $tmp
				set GlobalParam(BandWidth) $tmp
				}
			}



	return $m
}


##########################################################
# Add option widgets to the RTTY menu
##########################################################
proc AddRtty { m } \
{
	global GlobalParam


	# Set unshift on space

	$m add  checkbutton \
		-label "Unshift on space" \
                -variable GlobalParam(UnshiftOnSpace) \
                -onvalue on \
                -offvalue off \
                -command {global GlobalParam; \
			SetUnshiftOnSpace $GlobalParam(UnshiftOnSpace) }

	SetUnshiftOnSpace $GlobalParam(UnshiftOnSpace)


	# Set polarity

	$m add  checkbutton \
		-label "Reverse polarity" \
                -variable GlobalParam(RttyPolarity) \
                -onvalue reverse \
                -offvalue normal \
                -command {global GlobalParam; \
			SetRttyPolarity $GlobalParam(RttyPolarity) }

	SetRttyPolarity $GlobalParam(RttyPolarity)


	$m add separator

	$m add  radiobutton \
		-label "Display space upon error" \
		-variable GlobalParam(SpaceOnError) \
		-value space -command {SetSpaceOnError space}

	$m add  radiobutton \
		-label "Display * upon error" \
		-variable GlobalParam(SpaceOnError) \
		-value asterisk -command {SetSpaceOnError asterisk}

	return $m
}


##########################################################
# Add option widgets to the Presets menu
##########################################################
proc AddPresets { m } \
{
	global CurrentFreq
	global GlobalParam
	global PreviousFreq
	global PresetFreq PresetLabel PresetMode PresetBW
	global Psi

	set n [array size PresetFreq]

	# For each preset frequency.

	for { set Psi 0 } { $Psi < $n } { incr Psi } \
		{

		set cmd [format "$m add command "]
		set cmd [append cmd "\-label {$PresetLabel($Psi)} "]
		set cmd [append cmd "\-command {GoToPreset $Psi}"]
		eval $cmd
		}
	return $m
}

proc GoToPreset { i } \
{
	global Bwpot
	global CurrentFreq
	global GlobalParam
	global Mode
	global ModeLabel
	global RMode
	global PreviousFreq
	global PresetFreq PresetLabel PresetMode PresetBW

	set CurrentFreq [FormatFreq $PresetFreq($i) ]
	set hz [expr {$CurrentFreq * 1000000}]
	set hz [expr {round($hz)}]
	SetFreq $hz
	set GlobalParam(PreviousFreq) $CurrentFreq

	set m $PresetMode($i)
	set GlobalParam(Mode) $Mode($m)
	SetMode
	set tmp $RMode($GlobalParam(Mode))
	set tmp [string toupper $tmp]
	$ModeLabel configure -text $tmp

	SetSlideRuleDial

	set GlobalParam(BandWidth) $PresetBW($i)
	$Bwpot set $GlobalParam(BandWidth)
	SetBW $GlobalParam(BandWidth)

	return
}

##########################################################
# Add Tweak widgets to the Tweaks menu
##########################################################
proc AddTweaks { m } \
{
	global GlobalParam

	# Enable/disable attenuator

	$m add  checkbutton \
		-label "Attenuator" \
                -variable GlobalParam(Attenuator) \
                -onvalue on \
                -offvalue off \
                -command {global GlobalParam
			SetAttenuator $GlobalParam(Attenuator)
			}

	SetAttenuator $GlobalParam(Attenuator)

	$m add  checkbutton \
		-label "Front End Filter" \
                -variable GlobalParam(BPFenable) \
                -onvalue on \
                -offvalue off \
                -command {global GlobalParam
			SetBPF $GlobalParam(BPFenable) }

	SetBPF $GlobalParam(BPFenable)

	# Set IF filter shape

	$m add  checkbutton \
		-label "IF filter sharp" \
                -variable GlobalParam(FilterShape) \
                -onvalue  on \
                -offvalue off \
                -command {global GlobalParam
			SetFilterShape $GlobalParam(FilterShape) }

	SetFilterShape $GlobalParam(FilterShape)



	# Lock/unlock the radio panel to permit local radio tuning.

	$m add  checkbutton \
		-label "Panel Lock" \
                -variable GlobalParam(PanelLock) \
                -onvalue  on \
                -offvalue off \
                -command {global GlobalParam
			SetPanelLock $GlobalParam(PanelLock) }

	SetPanelLock $GlobalParam(PanelLock)


	$m add command -label "Check for duplicate frequencies" \
		-command { CkDuplicate }


	$m add command -label "Sort Memory Channels ..." \
		-command { MakeSortFrame }


	$m add command -label "Clear All Memory Channels ..." \
		-command { ClearAllChannels }


	return $m
}


##########################################################
# Add choices to the Radio menu
##########################################################
proc AddRadio { m } \
{
	global GlobalParam
	global Libdir
	global Pri

	$m add command -label "Read from radio ..." \
		-command { \
			Radio2File $Pri.mainframe
			update
			}
	
	$m add command -label "Write to radio ..." \
		-command { \
			File2Radio $Pri.mainframe
			update
			}
	
	$m add separator

	$m add command -label "Clear ALL memory channels" \
		-command { \
			set response [tk_dialog .clearall \
				"Clear all memories?" \
				"Clear ALL memory channels?" warning \
				0 Cancel Clear]

			if {$response > 0} then {ClearAllMem}
			update
			}
	
	$m add separator

	$m add command -label "Set radio clock using GMT" \
		-command { SyncClock 1 }

	$m add command -label "Set radio clock using local time zone" \
		-command { SyncClock 0 }

	$m add separator

	$m add  checkbutton \
		-label "Debug" \
                -variable GlobalParam(Debug) \
                -onvalue "1" \
                -offvalue "0"

	return $m
}

proc MakeWaitWindow {f cnflag color} \
{
	global CancelXfer

	set CancelXfer 0

	frame $f
	button $f.cancel -text Cancel -command {\
		global CancelXfer; set CancelXfer 1; puts "Canceled"}

	gauge_create $f.g PaleGreen
	option add *Gauge.borderWidth 2 widgetDefault
	option add *Gauge.relief sunken widgetDefault
	option add *Gauge.length 300 widgetDefault
	option add *Gauge.color gray widgetDefault

	pack $f.g -expand yes -fill both \
		-padx 10 -pady 10

	if {$cnflag} \
		{
		pack $f.cancel -side top -padx 3 -pady 3
		}

	

	pack $f
	return $f.g
}

##########################################################
# Copy from radio to .csv file
##########################################################
proc Radio2File { f }\
{
	global Cht
	global FileTypes
	global GlobalParam
	global Home
	global Ofilename
	global Mimage


	set Ofilename [ Mytk_getSaveFile $f \
		$GlobalParam(MemoryFileDir) \
		.csv \
		"Copy from NRD-545 to file" \
		$FileTypes]

#	set Ofilename [tk_getSaveFile -parent $f \
#		-initialdir $GlobalParam(MemoryFileDir) \
#		-defaultextension .csv \
#		-title "Copy from NRD-545 to file" \
#		-filetypes $FileTypes]


	if {$Ofilename != ""} \
		{
		# puts "going to read from radio into $Ofilename"
	
		set GlobalParam(MemoryFileDir) \
			[ Dirname $GlobalParam(Ifilename) ]

		toplevel .pbw
		wm title .pbw "Reading NRD-545"
		grab set .pbw
		set p [MakeWaitWindow .pbw.g 0 PaleGreen]
	
		set memorydata [ReadMem $p]
		Internal2Csv $memorydata

		# Zap the progress bar.
		# destroy $p
		grab release .pbw
		destroy .pbw

		tk_dialog .belch "Read NRD-545" \
			"Transfer Complete" info 0 OK

		set Mimage ""

		set fr_table [MakeChannelListFrame $Cht $Ofilename]; \
		if { $GlobalParam(Ifilename) != ""} \
			{
			pack $fr_table
			SetWinTitle
			}
		return
		}
}


proc Internal2Csv { data } \
{
	global Ofilename
	global RBw
	global RMode

	set fid [open $Ofilename "w"]

	# Write first line as the field names.

	puts $fid [format "Ch,MHz,Mode,Select.,AGC"]

	set memlist [split $data "\n"]
	set nlines [llength $memlist]
	incr nlines -1
	for {set i 0} {$i < $nlines} {incr i} \
		{
		set line [lindex $memlist $i]
		if {[string index $line 0] == "V"}\
			{
			continue
			}

		set ch [string range $line 1 3]
		if {$ch == "000"}\
			{
			set ch 0 
			} \
		else \
			{
			# regsub "^0*" $ch "" ch
			set ch [string trimleft $ch 0]
			}
		if {$ch == ""}\
			{
			continue
			}

		set freq [string range $line 7 16]
		if {$freq == "0000000000"}\
			{
			continue
			}
		# regsub "^0*" $freq "" freq
		set freq [string trimleft $freq 0]

		if {$freq == ""}\
			{
			continue
			}
		set freq [ format "%.6f" $freq ]
		set freq [ expr $freq / 1000000 ]

		set bw $RBw([string range $line 5 5])
		set mode $RMode([string range $line 6 6])
		set agc [string range $line 17 19]
		# regsub "^0*" $agc "" agc
		set agc [string trimleft $agc 0]
		set agc [ expr $agc * .02 ]
		# puts "ch $ch is $freq, bw $bw, mode $mode, agc $agc"
		puts $fid [format "%s,%.6f,%s,%s,%.1f" \
			$ch $freq $mode $bw $agc]

		}
	close $fid
	return
}


##########################################################
# Copy from a .csv file to radio
##########################################################
proc File2Radio { f }\
{
	global FileTypes
	global GlobalParam
	global Cht;


	if {$GlobalParam(Ifilename) == ""} \
		{
		# Prompt for file name and read the file.
		set fr_table [MakeChannelListFrame $Cht ""]
		if {$GlobalParam(Ifilename) == ""} then {return}
		pack $fr_table
		}


	if {$GlobalParam(Ifilename) != ""} \
		{

		# Create a new window with a progress gauge
		# and prevent user from accessing main window
		# during the data transfer.

		toplevel .pbw
		wm title .pbw "Writing to NRD-545"
		grab set .pbw

		set p [MakeWaitWindow .pbw.g 1 PaleGreen]
	
		Memories2Radio $p

		# Zap the progress bar window.
		grab release .pbw
		destroy .pbw

#			# warning: Font is ugly
#			tk_messageBox -icon info \
#				-message "Transfer Complete"

		tk_dialog .wpbr "Write to NRD-545" \
			"Transfer Complete" info 0 OK

		return
		}
}
##########################################################
# Format and write memory data to the radio.
# Data has previously been stored in global arrays.
##########################################################
proc Memories2Radio { g } \
{
	global Bw
	global CancelXfer
	global MemCh
	global MemAgc
	global MemFreq
	global MemLabel
	global MemMode
	global MemSel
	global Mode
	global Sid


	set chlist [ array names MemCh ]
	set chlist [ lsort -integer $chlist ]

	set n [ llength $chlist ]
	set i 0

	foreach ch $chlist \
		{
		update
		if {$CancelXfer} then {break}
		if {$ch == "" } then {continue}
		if { ([info exists MemCh($ch)] == 0) \
			|| ($MemCh($ch) != 1) } \
			{
			# no such channel
			continue
			}
		set ich [ PadLeft0 3 $ch ]

		set freq [ expr 1000000 * $MemFreq($ch) ]
		set freq [ expr round($freq) ]
		set freq [ PadLeft0 10 $freq ]

		set mode $MemMode($ch)
		set mode $Mode($mode)

		set agc $MemAgc($ch)
		set agc [expr round($agc * 50) ]
		set agc [PadLeft0 3 $agc]

		set label $MemLabel($ch)
		set sel $MemSel($ch)
		set sel $Bw($sel)

		set s [format "K%s0%s%s%s%s" \
			$ich $sel $mode $freq $agc]

		# Write memory channel info to radio.
		SendCmd $Sid $s
		incr i
		set pc [ expr $i * 100 / $n ]
		gauge_value $g $pc
		}

	return
}

###################################################################
# Read S meter value from radio and update the S meter widget.
# Check AM sync lock status.
#
# This sequence repeats continuously whenever a callback
# is not running.
###################################################################
proc PollSmeter {} \
{
	global AMLockLabel
	global StereoLabel
	global GlobalParam

	waiter 100

	while {1} \
		{
		update
		waiter 100

		if {$GlobalParam(SmeterEnable) == "off"} \
			{
			set GlobalParam(Smeter) 0
			updateMeter $GlobalParam(Smeter)
			$AMLockLabel configure -foreground black
			$StereoLabel configure -foreground black
			continue
			} 
		set GlobalParam(Smeter) [GetSmeter]
		updateMeter $GlobalParam(Smeter)


                if { [ReadAMdetection] == "Locked" } \
			{
			$AMLockLabel configure -foreground PaleGreen
			} \
		else \
			{
			$AMLockLabel configure -foreground black
			}

                if { [ReadStereoDetect] == "stereo" } \
			{
			$StereoLabel configure -foreground red
			} \
		else \
			{
			$StereoLabel configure -foreground black
			}

		}
}


###################################################################
# Meter widget adapted from
# Bob Techentin's code.
###################################################################
proc updateMeter {v} \
{
	global MeterFrame

	# set meterht 75
	set meterht 50
	set min 0
	set max 255.0
 
	set pos [expr {$v / $max}]
	set x [expr {$meterht - $meterht * .9*(cos($pos*3.14))}]
	set y [expr {$meterht - $meterht * .9*(sin($pos*3.14))}]

	$MeterFrame.c coords meter $meterht $meterht $x $y
}
###################################################################
# Create a meter widget
#
# Variables:
#	max 	-the highest amplitude of what we will measure
#	meterht	-meter height in pixels
###################################################################
proc MakeMeter { } \
{
	global MeterFrame
	global Sunits2Db

	set max 255.0
	set meterht 50
	# set meterht 75
	set meterwidth [expr {$meterht * 2.0}]

	set f $MeterFrame
	frame $f -relief sunken -borderwidth 10 

	set meterhtplus [expr {$meterht * 1.1}]
	grid [canvas $f.c -width $meterwidth -height $meterhtplus \
		-borderwidth 1 -relief sunken -background white]

	# Calculate endpoints for the arc.

	set m10 [expr {$meterht * .1}]
	set m190 [expr {$meterwidth - $m10}]

	$f.c create arc $m10 $m10 $m190 $m190 \
		-extent 160 \
		-start 10 -style arc -outline black -width 2

	$f.c create line $meterht $meterht $m10 $meterht \
		-tag meter -width 2 \
		-fill red

	# Draw meter calibrations.
	set ndivs [ expr {$max / 24} ]
	for {set i 0} {$i <= $ndivs} {incr i}\
		{
		if {$i == 0} {continue}
		set pos [expr {$i * 24 / $max}]

		set x [expr {$meterht - $meterht \
			* .75 *(cos($pos*3.14))}]
		set y [expr {$meterht - $meterht \
			* .75 *(sin($pos*3.14))}]

		# Scale is 0 - 10 units.

		$f.c create text $x $y -text $i \
			-justify center \
			-fill black \
			-font {Courier 10 normal}
		}
	set ylogo [expr {.7 * $meterht}]
	$f.c create text $meterht $ylogo -text "tk545" \
		-justify center \
		-fill blue \
		-font {Courier 10 bold}
	return $f
}

###################################################################
# Create a Keypad.
# This is based on a calculator program by Richard Suchenwirth.
###################################################################
proc MakeKeyPad {f} \
{
	global Enw

	frame $f -relief raised -borderwidth 10
	set Enw [entry $f.e -width 12 -textvar e \
		-just right -background white]
	grid $Enw -columnspan 3 
	bind $f.e <Return> {Ekey mhz}


	set hint ""
	set hint [append hint "You can enter a new frequency\n"]
	set hint [append hint "(in kHz or MHz) by using\n"]
	set hint [append hint "the simulated keypad."]
	set hint [append hint "Syn resyncronizes the display\n"]
	set hint [append hint "when the front panel is unlocked."]
	balloonhelp_for $f $hint


	set n 0
	foreach row {
		{7 8 9 }
		{4 5 6 }
		{1 2 3 }
		{C 0 . }
		{ MHz kHz Syn }
		} {
		foreach key $row {
			switch -- $key {
				MHz   {set cmd {Ekey mhz}}
				kHz   {set cmd {Ekey khz}}
				C       {set cmd {set clear 1; set e ""}}
				Syn    {set cmd {UpdDisplay}}
				default {set cmd "Hit $f $key"}
				}
			lappend keys [button $f.[incr n] \
				-text $key \
				-command $cmd]
				# -width 3 
			}
		eval grid $keys -sticky we -padx 1 -pady 1
		set keys [list]
		}

	return $f
}

proc Ekey { units }\
{
	global Enw
	global Bwpot
	global CurrentFreq
	global GlobalParam
	global Label
	global LabelLabel
	global Readout

	# Calculate the length of string entered.
	set elen [ string length $::e ]
	set tmp $::e


	# Trim leading spaces or zeros.
	regsub {^[ 0]*} $::e "" $tmp
	set CurrentFreq $tmp



	if { [CheckFreqValid $CurrentFreq $units] } then \
		{
		if { $units == "khz" } \
			{
			set CurrentFreq [ expr { double($CurrentFreq) \
				/ double(1000) } ]
			}
		set CurrentFreq [FormatFreq $CurrentFreq ]
		set hz [expr {$CurrentFreq * 1000000}]
		set hz [expr {round($hz)}]
		SetFreq $hz
		set GlobalParam(PreviousFreq) $CurrentFreq

		SetSlideRuleDial
		Add2History

		# Update BW slider to reflect the
		# radio's current selectivity.
		set tmp [GetBW]
		$Bwpot set $tmp
		set GlobalParam(BandWidth) $tmp


		# Get and display frequency alpha label
		# from label cache.
		UpdLabel
		} \
	else \
		{
		# Invalid frequency.
		bell
		set CurrentFreq $GlobalParam(PreviousFreq)
		set CurrentFreq [FormatFreq $CurrentFreq ]
		}

	# Clear the string entered in the local entry box.
	$Enw delete 0 $elen
	return
 }
 
proc Hit {f key} \
{
	if $::clear {
		set ::e ""
		if ![regexp {[0-9().]} $key] {set ::e $::res}
		$f.e config -fg black
		$f.e icursor end
		set ::clear 0
		}
	$f.e insert end $key
}
set clear 0

###################################################################
# Return 1 if frequency is in range 0 - 2000 exclusive.
###################################################################
proc FreqInRange { f units } \
{
	if {$units == "mhz" } \
		{
		if { $f > 0 && $f < 2000.0 } \
			{
			return 1
			}
		} \
	elseif {$units == "khz" } \
		{
		if { $f > 0 && $f < 2000000.0 } \
			{
			return 1
			}
		}
	return 0
}

###################################################################
# Return 1 if string 's' is a valid frequency.
# Return 0 otherwise.
#
# Units should be khz or mhz
###################################################################
proc CheckFreqValid { s units }\
{
	if {$s == ""} then {return 0}

	# Check for non-digit and non decimal point chars.
	set rc [regexp {^[0-9.]*$} $s]
	if {$rc == 0} then {return 0}


	# All digits.
	set rc [regexp {^[0-9]*$} $s]
	if {$rc == 1} \
		{
		return [FreqInRange $s $units]
		}

	if {$s == "."} then {return 0}

	# Check for Two or more decimal points
	set tmp $s
	set tmp [split $s "."]
	set n [llength $tmp]
	if { $n >= 3 } then {return 0}
	
	return [FreqInRange $s $units]
}


###################################################################
# Format a channel label
###################################################################
proc FormatChan { ch } \
{
	set tmp [format "Ch %3d" $ch ]
	return $tmp
}

###################################################################
# Format a frequency into the proper
# floating point representation for display, i.e.,
#	4 digits . 6 digits
###################################################################
proc FormatFreq { freq } \
{
	set tmp [format "%11.6f" $freq ]
	return $tmp
}

###################################################################
# Wait for a period of milliseconds.  Meanwhile, poll the S-meter
# and check the AM sync lock status.
###################################################################
proc Idle { ms } \
{
	global AMLockLabel
	global GlobalParam
	global StereoLabel

	set n [expr {$ms / 100} ]

	for {set i 0} {$i < $n} {incr i} \
		{
		waiter 100
		update

		if {$GlobalParam(SmeterEnable) == "off"} \
			{
			set val 0
			updateMeter $val
			$AMLockLabel configure -foreground black
			$StereoLabel configure -foreground red
			continue
			}

		set GlobalParam(Smeter) [GetSmeter]
		updateMeter $GlobalParam(Smeter)


                if { [ReadAMdetection] == "Locked" } \
			{
			$AMLockLabel configure -foreground PaleGreen
			} \
		else \
			{
			$AMLockLabel configure -foreground black
			}


                if { [ReadStereoDetect] == "stereo" } \
			{
			$StereoLabel configure -foreground red
			} \
		else \
			{
			$StereoLabel configure -foreground black
			}

		}
	return
}

###################################################################
# Set default receiver parameters
###################################################################
proc SetUp { } \
{
	global env
	global GlobalParam
	global RootDir
	global tcl_platform

	if { [regexp "Darwin" $tcl_platform(os) ] } \
		{
		# For Mac OS X.
		set RootDir ":"
		} \
	else \
		{
		set RootDir "/"
		}

	set GlobalParam(Debug) 0
	# set GlobalParam(Device) /dev/ttyS1
	set GlobalParam(Ifilename) {}
	set GlobalParam(MemoryFileDir) $RootDir
	set GlobalParam(PreviousFreq) 0.0

	return
}


###################################################################
# Start or stop frequency slewing (autotune).
#
# Inputs:
#	ts	-frequency increment in kHz
#	updown	-direction up or down
###################################################################
proc StartSlew { ts updown }\
{
	global GlobalParam

	set GlobalParam(Slewing) 1;
	SetTS $ts
 
	if { $updown == "down" } \
		{
		set amount [ expr { -1 * $ts } ]
		} \
	else \
		{
		set amount $ts
		}

	while { $GlobalParam(Slewing) } \
		{
		SendArrow $updown
		AlterDisplay $amount
		
		waiter $GlobalParam(SlewSpeed)
		}

	return
}

###################################################################
# Explain to user that he cannot scan memory channels
# until he opens a memory data file.
###################################################################
proc CannotScan {} \
{
	set msg ""
	set msg [append msg "You must open a memory data "]
	set msg [append msg "file before scanning."]

	tk_dialog .noscan "Memory scan" $msg warning 0 OK

	return
}

###################################################################
# SyncClock
#
# Set the NRD-545's clock using the computer's time.
#
# Inputs:
#	gmtflag:
#
#	0 - use local time
#	1 - use Greenwich Mean Time 
#
###################################################################
proc SyncClock { gmtflag } \
{
	set nsec [clock seconds]

	set time_now [clock format $nsec -format {%H%M} -gmt $gmtflag]

	SetClock $time_now
	return
}

###################################################################
# Add a frequency and mode to the history list and
# timestamp the entry.
#
# Each entry is assigned a unique, sequential number, starting
# at 0.  Once the history list is full, we discard the oldest
# entry and move the remaining entries down one slot.
###################################################################

proc Add2History { } \
{
	global CurrentFreq
	global GlobalParam
	global HistoryTime
	global HistoryFreq
	global HistoryIdx
	global HistoryLabel
	global HistoryMode
	global Label
	global RMode

	# Maximum number of history entries - 1
	set n 99

	if {[ info exists HistoryIdx ] == 0 } \
		{
		set HistoryIdx -1
		}

	incr HistoryIdx

	# If list is full...

	if { $HistoryIdx > $n} \
		{
		# Overlay the oldest entry and move each
		# remaining entry down one slot.

		for {set i 0} { $i < $n} {incr i} \
			{
			set ip1 [expr {$i + 1}]
			set HistoryTime($i) $HistoryTime($ip1)
			set HistoryFreq($i) $HistoryFreq($ip1)
			set HistoryLabel($i) $HistoryLabel($ip1)
			set HistoryMode($i) $HistoryMode($ip1)
			}
		set HistoryIdx $n
		}
	set seconds [clock seconds]
	set HistoryTime($HistoryIdx) [clock format $seconds \
		-format {%T}]
	set HistoryFreq($HistoryIdx) $CurrentFreq

	set f [string trimleft $CurrentFreq " "]
	if { [info exists Label($f)] } \
		{
		set HistoryLabel($HistoryIdx) \
			[string range $Label($f) 0 8]
		} \
	else \
		{
		set HistoryLabel($HistoryIdx) \
			[string range "          " 0 8]
		}


	set mode $GlobalParam(Mode)
	set HistoryMode($HistoryIdx) $RMode($mode)

	RefreshHistory

	return
}


###################################################################
# Create a label to hold a comment about the user of this
# frequency.
###################################################################

proc MakeNote { f }\
{
	global NoteLabel
	global CurrentFreq
	global GlobalParam

	global SlideRule
	frame $f -background ""

	set NoteLabel $f.comment
	label $NoteLabel -text " " -borderwidth 3 \
		-justify left \
		-font "Helvetica 28 normal" \
		-background black -foreground yellow


	if {$GlobalParam(ViewNote) == "on"} \
		{
		pack $NoteLabel -padx 0 -pady 0 -expand yes -fill x
		}

	return $f
}


###################################################################
# Create an analog style slide rule dial using a scale widget.
# The scale and reading is adjusted by the SetSlideRuleDial
# procedure as the user tunes the radio.
###################################################################

proc MakeSlideRule { f }\
{
	global CurrentFreq
	global DialFreq
	global GlobalParam
	global SlideRule
	frame $f -background ""

	scale $f.d -from 0 -to 30 -showvalue yes \
		-orient horizontal \
		-width 6 \
		-sliderlength 6 -resolution 1 \
		-tickinterval 10 \
		-troughcolor white \
		-background black -foreground white \
		-activebackground red \
		-variable DialFreq \
		-state disabled

	set SlideRule $f.d

	if {$GlobalParam(ViewSlideRuleDial) == "on"} \
		{
		pack $f.d -padx 0 -pady 0 -expand yes -fill x
		}

	return $f
}
###################################################################
# Update the slide rule tuning dial widget to show the
# frequency according to the global variable CurrentFreq.
###################################################################

proc SetSlideRuleDial {} \
{
	global CurrentFreq
	global DialFreq
	global SlideRule

	set lowlim [expr {floor($CurrentFreq)}]
	set hilim  [expr {ceil($CurrentFreq)}]

#	set lowlim [expr {100 * [expr {$CurrentFreq - 0.5}] }]
#	set lowlim [expr {floor($lowlim)/100}]
#	set hilim  [expr {100 * [expr {$CurrentFreq + 0.5}] }]
#	set hilim  [expr {ceil($hilim)/100}]


	if {$hilim == $lowlim} \
		{
		# We are tuned to a band edge.
		set hilim [ expr {$lowlim + 1.0}]
		}


	$SlideRule configure -from $lowlim -to $hilim \
		-showvalue yes -tickinterval .1 \
		-state disabled \
		-resolution .001 

	$SlideRule set $CurrentFreq
	set DialFreq $CurrentFreq

	update

	return
}

###################################################################
# This proc is not used currently.
###################################################################
proc TuneBySlideRule {f} \
{
	set CurrentFreq [FormatFreq $f ]
	set hz [expr {$CurrentFreq * 1000000}]
	set hz [expr {round($hz)}]
	SetFreq $hz
	set GlobalParam(PreviousFreq) $CurrentFreq

	UpdDisplay
	update
	return
}

##########################################################
# Add widgets to the Log menu
##########################################################
proc AddLog { m } \
{
	global GlobalParam
	global Lid
	global Lfilename

	set Lfilename ""

	set msg "Log frequencies in a file"

	$m add checkbutton \
		-label $msg \
                -variable GlobalParam(AutoLogging) \
                -onvalue on \
                -offvalue off \
                -command \
			{
			global Lfilename
			if {$GlobalParam(AutoLogging) == "on"} \
				{
				if {$Lfilename == ""} \
					{
					InitLogFile
					}
				}
			}

	$m add command -label "Log the current frequency and time." \
		-command \
			{
			if { $Lfilename == "" } \
				{
				InitLogFile
				}
			LogTransmission
			}

	return $m
}

###################################################################
# Prompt user for the name of a file to create for logging
# active frequencies.
# Open the file.
###################################################################

proc InitLogFile { } \
{
	global FileTypes
	global GlobalParam
	global Lfilename
	global Lid
	global Pri



	set Lfilename [ Mytk_getSaveFile $Pri \
		$GlobalParam(LogFileDir) \
		.csv \
		"Create a log file" \
		$FileTypes ]

#	set Lfilename [tk_getSaveFile \
#		-parent $Pri \
#		-initialdir $GlobalParam(LogFileDir) \
#		-defaultextension .csv \
#		-title "Create a log file" \
#		-filetypes $FileTypes]

	if { [string length $Lfilename] != 0 } \
		{
		set GlobalParam(LogFileDir) \
			[ Dirname $Lfilename ]

		if { [catch {open $Lfilename {WRONLY APPEND CREAT} } \
			Lid] } \
			{
			bell
			tk_dialog .logfilerror "Log file error" \
				"Cannot create log file." error 0 OK
			}
		}
	return
}

###################################################################
# Append a line in csv format to the log file.
#
# Note:
#	The current value of the S-meter will be used,
#	so be sure to read the S-meter from the radio
#	while on the current frequency before calling this
#	procedure.
###################################################################

proc LogTransmission { } \
{
	global CurrentFreq
	global GlobalParam
	global Label
	global Lid
	global Lfilename
	global RMode

	if { $Lfilename == "" } {return}


	set f [string trimleft $CurrentFreq " "]

	set label [GetComment]

	set seconds [clock seconds]
	set time [clock format $seconds -format {%T}]
	set date [clock format $seconds -format {%Y/%m/%d}]

	regsub -all "\"" $label "" label

	# Scale the signal strength reading from 0 - 256
	# to 0 - 10.

	set smeter [expr { $GlobalParam(Smeter) / 25.6 } ]

	set mode $GlobalParam(Mode)
	set mode $RMode($mode)

	set s [format "%s,%s,%s,%s,%.2f,\"%s\"" \
		$date $time $f \
		$mode $smeter $label]

	if { [catch {puts $Lid $s}] } \
		{
		bell
		tk_dialog .logfilerror "Log file error" \
			"Cannot write to log file." error 0 OK
		} \
	else \
		{
		catch {flush $Lid}
		}
	return
}

###################################################################
# Popup a window to ask user for a comment for the log.
###################################################################
proc GetComment { } \
{
	global AboutMsg
	global Lcomment
	global GlobalParam
	global Libdir

	set c ""

	# Create a new window with radio buttions and
	# an entry field so user can designate the proper
	# serial port.

	set msg "Enter an optional comment for this log entry.\n"

	toplevel .logpopup
	set sp .logpopup

	label $sp.intro -text $msg
	label $sp.cmt -text "Comment:" 
	entry $sp.ent -width 50 -textvariable Lcomment

	button $sp.ok -text "OK" \
		-command \
			{ \
			global Lcomment
			set c $Lcomment
			}

	pack $sp.intro $sp.ent \
		-side top -padx 3 -pady 3 -anchor w

	pack $sp.ok -side top -padx 3 -pady 3 -expand true



	bind $sp.ent <Key-Return> \
		{
		global Lcomment
		set c $Lcomment
		}

	wm title $sp "Enter comment for the log"
	wm protocol $sp WM_DELETE_WINDOW {exit}

	tkwait variable c

	set c $Lcomment
	set Lcomment ""

	destroy $sp

	# Translate funny characters to spaces.

	regsub -all {[^a-zA-Z0-9:;.-]} $c " " c
	return $c
}

###################################################################
# Dialog to open a file for reading.
#
# Note:
#	I placed a wrapper around the built in Tk routine
#	because the MacOS X version
#	does not handle the initialdir parameter properly.
###################################################################
proc Mytk_getOpenFile {parent initialdir title types} \
{
	global tcl_platform

	if { [regexp "Darwin" $tcl_platform(os) ] } \
		{
		# For Mac OS X.
		set initialdir ":"
		}

	set code [tk_getOpenFile -parent $parent \
		-initialdir $initialdir \
		-title $title -filetypes $types]

	return $code
}



###################################################################
# Dialog to open a file for writing.
#
# Note:
#	I placed a wrapper around the built in Tk routine
#	because the MacOS X version
#	does not handle the initialdir parameter properly.
###################################################################
proc Mytk_getSaveFile {f initialdir defaultextension title types} \
{
	global tcl_platform

	if { [regexp "Darwin" $tcl_platform(os) ] } \
		{
		# For Mac OS X.
		set initialdir ":"
		}

	set code [tk_getSaveFile -parent $f \
		-initialdir $initialdir \
		-defaultextension $defaultextension \
		-title $title \
		-filetypes $types]

	return $code
}


##########################################################
#
# Scroll_Set manages optional scrollbars.
#
# From "Practical Programming in Tcl and Tk,"
# second edition, by Brent B. Welch.
# Example 27-2
#
##########################################################

proc Scroll_Set {scrollbar geoCmd offset size} {
	if {$offset != 0.0 || $size != 1.0} {
		eval $geoCmd;# Make sure it is visible
		$scrollbar set $offset $size
	} else {
		set manager [lindex $geoCmd 0]
		$manager forget $scrollbar								;# hide it
	}
}


##########################################################
#
# Listbox with optional scrollbars.
#
#
# Inputs: basename of configuration file
#
# From "Practical Programming in Tcl and Tk,"
# second edition, by Brent B. Welch.
# Example 27-3
#
##########################################################

proc Scrolled_Listbox { f args } {
	frame $f
	listbox $f.list \
		-font {courier 12} \
		-xscrollcommand [list Scroll_Set $f.xscroll \
			[list grid $f.xscroll -row 1 -column 0 -sticky we]] \
		-yscrollcommand [list Scroll_Set $f.yscroll \
			[list grid $f.yscroll -row 0 -column 1 -sticky ns]]
	eval {$f.list configure} $args
	scrollbar $f.xscroll -orient horizontal \
		-command [list $f.list xview]
	scrollbar $f.yscroll -orient vertical \
		-command [list $f.list yview]
	grid $f.list $f.yscroll -sticky news
	grid $f.xscroll -sticky news

	grid rowconfigure $f 0 -weight 1
	grid columnconfigure $f 0 -weight 1

	return $f.list
}


##########################################################
#
# Channel Listbox with optional scrollbars.
#
#
# This is modified version of Example 29-1.
# From "Practical Programming in Tcl and Tk,"
# second edition, by Brent B. Welch.
#
# This proc prevents huge listbox windows on MacOS X.
# A bug in the version of Tk for MacOS X prevents the user
# from being able to resize windows larger than the screen.
##########################################################
proc List_channels { parent values height } \
{
	global tcl_platform

	if { [regexp "Darwin" $tcl_platform(os) ] \
		&& (($height == 0) || ($height > 30)) }\
		{
		# Limit the height for Mac OS X.
		set height 30
		}

	frame $parent
	set choices [Scrolled_Listbox $parent.choices \
		-width 0 -height $height ]

	# Insert all the choices
	foreach x $values \
		{
		$choices insert end $x
		}

	pack $parent.choices -side left
	return "$choices"
}

##########################################################
#
# Return the channel selected from the channel selector listbox
#
##########################################################

proc ChSelected { w } \
{
	set line [ ListSelected $w ]

	if {[string compare -nocase -length 1 $line "-"] == 0} \
		{
		return -1
		}
	set line [string trimleft $line " "]
	regsub " .*" $line "" ch
	return "$ch"
}

##########################################################
# Begin creating the graphical user interface widgets.
##########################################################
proc MakeGui { } \
{
	global Cht
	global Chvector
	global HistVector
	global Mc
	global Pri
	global Sec
	global GlobalParam

	
	SetAppearance

	set Pri .primary
	# toplevel $Pri
	frame $Pri
	# wm title $Pri "tk545 Main Controls"
	wm title . "tk545 Main Controls"

	############################################################
	# Menu bar along the top edge.
	############################################################
	set fr_menubar [MakeMenuBar $Pri.mb]
	set mf [frame $Pri.mainframe]
	frame $mf.chtable
	set Cht $mf.chtable
	
	############################################################
	# Freq Display and tuning buttons.
	############################################################
	set fr_freqdisplay [FreqDisplay $Pri.freqdisplay]
	set fr_comment [MakeNote $Pri.comment]
	
	set fr_sliderule [MakeSlideRule $Pri.dial]
	set fr_buttons [frame $Pri.fbuttons]
	set fr_updown [MakeUpDownFrame $fr_buttons.updown]
	balloonhelp_for $fr_updown \
		"Click and release a button\nto change the frequency."
	
	set fr_vet [frame $fr_buttons.vertbar \
		-borderwidth 6 -relief groove]
	
	set fr_slew [MakeSlewFrame $fr_buttons.slew]
	balloonhelp_for $fr_slew \
		"Press once to start autotune.\nPress again to stop."
	
	pack $fr_slew -side left -fill x -expand y -padx 3
	pack $fr_vet -side left -fill x -expand y -padx 6
	pack $fr_updown -side left -fill x -expand y -padx 3
	
	############################################################
	# Potentiometer (scale) controls
	############################################################
	frame $mf.row2 -relief groove -borderwidth 3
	set row2 $mf.row2
	
	set fr_pots [ MakePots $row2.potentiometers]
	set fr_kp [MakeKeyPad $row2.kp ]
	
	pack $fr_pots -side left -fill x -expand y -padx 3 -pady 3
	pack $fr_kp -side left -fill x -expand y -padx 1 -pady 1
	
	############################################################
	# Secondary controls window
	############################################################
	set Sec .controls
	toplevel $Sec
	set ctls $Sec.ctls
	frame $ctls -relief groove
	
	set fr_mod [MkModeSwitch $ctls.mode]
	SetMode
	
	set $ctls.notch [ MakeNotchFrame $ctls.notch]
	NoiseReducer $ctls.nr
	NoiseBlanker $ctls.nb
	
	set Mc .mc
	set Cht $Mc

	if { [catch {toplevel $Mc}] == 0 } \
		{
		wm iconify $Cht
		}
	
	# Prevent user from closing the channel list window unless
	# he elects to exit the entire program.
	
	wm protocol $Cht WM_DELETE_WINDOW {ExitApplication; exit}
	wm title $Cht "tk545 Memory Channels"
	
	
	set $ctls.scan [ MakeMemScanFrame $ctls.scan]
	set $ctls.search [ MakeSearchFrame $ctls.search]
	
	
	set Chvector ""
	
	pack $fr_menubar -side top -fill x -pady 3
	pack $fr_freqdisplay -side top -fill x
	pack $fr_comment -side top -fill x -expand y
	pack $fr_sliderule -side top -fill x -expand y
	pack $fr_buttons -side top -fill x -expand y
	
	
	pack $mf.row2 -side top -fill x -expand y -padx 3 -pady 3
	pack $ctls.mode -side left -anchor n -padx 3 -pady 3
	pack $ctls.notch -side left -anchor n -padx 3 -pady 3
	pack $ctls.nr -side left -anchor n -padx 3 -pady 3
	pack $ctls.nb -side left -anchor n -padx 3 -pady 3
	pack $ctls.scan -side left -anchor n -padx 3 -pady 3
	pack $ctls.search -side left -anchor n -padx 3 -pady 3
	
	pack $ctls -side top -fill both -expand true -padx 3 -pady 3
	pack $Pri.mainframe -side top -fill both -expand true
	pack $Pri -side top -fill both -expand true
	
	# Zap the session history.
	set HistVector ""
	
	update idletasks
	
	############################################################
	#  Ask the window manager to catch the delete window
	#  event.
	############################################################
	wm protocol . WM_DELETE_WINDOW {ExitApplication; exit}
	
	wm protocol $Sec WM_DELETE_WINDOW {ExitApplication; exit}
	wm title $Sec "tk545 Secondary Controls"
	
	set x [winfo width $ctls.mode]
	set x [expr $x + 14]
	
	# Prevent user from overshrinking or expanding controls window.
	wm minsize $Sec $x [winfo height $Sec]
	# wm maxsize $Sec [winfo width $Sec] [winfo height $Sec]
	
	EnableCControl
	SetPower on
	SetPanelLock on
	set GlobalParam(PanelLock) on
	DecodeRtty off
	
	# Configure the step sizes in the radio's setup so our search
	# step sizes work ok.
	
	SetDefaultSteps
	
	# Update the frequency display widget with the radio's
	# frequency.
	
	UpdDisplay
	SetSlideRuleDial
	
	
	# Prevent user from shrinking or expanding main window.
	wm minsize . [winfo width .] [winfo height .]
	wm maxsize . [winfo width .] [winfo height .]

	return
}

###################################################################
# Alter color and font appearance based on user preferences.
###################################################################
proc SetAppearance { } \
{
	global GlobalParam

	if {$GlobalParam(Font) != "" } \
		{
		# Designate a custom font for most widgets.
		option add *font $GlobalParam(Font)
		}

	if {$GlobalParam(BackGroundColor) != "" } \
		{
		# Designate a custom background color for most widgets.
		option add *background $GlobalParam(BackGroundColor)
		}

	if {$GlobalParam(ForeGroundColor) != "" } \
		{
		# Designate a custom foreground color for most widgets.
		option add *foreground $GlobalParam(ForeGroundColor)
		}

	if {$GlobalParam(TroughColor) != "" } \
		{
		# Designate a custom slider trough color
		# for most scale widgets.
		option add *troughColor $GlobalParam(TroughColor)
		}

	return
}


###################################################################
# 
# Define receiver parameters before we read the
# global parameter configuration file in case they are missing
# from the configuration file.
# This avoids a tcl error if we tried to refer to an
# undefined variable.
#
# These initial definitions will be overridden with
# definitions from the configuration file.
#
###################################################################

proc PresetGlobals { } \
{
	global GlobalParam
	global Mode
	global Rcfile
	global RootDir
	global tcl_platform


	set GlobalParam(AGCms) 2000
	set GlobalParam(Attenuator) off
	set GlobalParam(AutoLogging) off
	set GlobalParam(BalloonHelpWindows) on
	set GlobalParam(BackGroundColor) ""
	set GlobalParam(BandWidth) 8000
	set GlobalParam(BClevel) 128
	set GlobalParam(BlinkingColon) on
	set GlobalParam(Debug) 0
	set GlobalParam(BPFenable) on
	set GlobalParam(FilterShape) on
	set GlobalParam(Font) ""
	set GlobalParam(ForeGroundColor) ""
	set GlobalParam(Ifilename) {}
	set GlobalParam(Lighting) dim
	set GlobalParam(LogFileDir) $RootDir
	set GlobalParam(LowerLimit) 0
	set GlobalParam(MemoryFileDir) $RootDir
	set GlobalParam(Mode) $Mode(am)
	set GlobalParam(NBlevel) 50
	set GlobalParam(NRlevel) 50
	set GlobalParam(PassBandShift) 0
	set GlobalParam(Populated) 0
	set GlobalParam(PowerSupply) on
	set GlobalParam(PreviousFreq) 10
	set GlobalParam(RttyPolarity) normal
	set GlobalParam(ScanAutoStop) on
	set GlobalParam(SearchFlag) 0
	set GlobalParam(SearchStep) 1
	set GlobalParam(SkipEmptyCh) on
	set GlobalParam(Slewing) 0
	set GlobalParam(SlewSpeed) 100
	set GlobalParam(Smeter) 0
	set GlobalParam(SortBank) 0
	set GlobalParam(SortType) label
	set GlobalParam(SmeterEnable) on
	set GlobalParam(SpaceOnError) asterisk
	set GlobalParam(TieBWtoMode) off
	set GlobalParam(TimerRelay) off
	set GlobalParam(Tone) 130
	set GlobalParam(TroughColor) ""
	set GlobalParam(UnshiftOnSpace) on
	set GlobalParam(UpperLimit) 0
	set GlobalParam(ViewHistory) off
	set GlobalParam(ViewNote) on
	set GlobalParam(ViewSlideRuleDial) on
	set GlobalParam(Volume) 50

	return
}


###################################################################
# Set global variables after reading the global
# configuration file so these settings override
# whatever values were in the configuration file.
###################################################################

proc OverrideGlobals { } \
{
	global env
	global GlobalParam
	global RootDir
	global tcl_platform


	# Note on MacOS X:
	# The initial directory passed to the file chooser widget.
	# The problem here is that osx's tcl is utterly busted.
	# The _only_ pathname it accepts is ':' - no other ones work.
	# Now this isn't as bad as you might think because
	# the native macos file selector widget persistantly
	# remembers the last place you opened/saved a file
	# for a particular application. So the logic to
	# remember this is simply redundant on macos anyway...
	# Presumably they'll fix this someday and we can take
	# out the hack.
	# - Ben Mesander

	if { [regexp "Darwin" $tcl_platform(os) ] } \
		{
		# kluge for MacOS X.

		set GlobalParam(LogFileDir) $RootDir
		set GlobalParam(MemoryFileDir) $RootDir

		if {$GlobalParam(Ifilename) != ""} \
			{
			set GlobalParam(Ifilename) $RootDir
			}
		}

	set GlobalParam(PanelLock) off
	return
}



###################################################################
# Read frequencies/labels from the label file.
#
# Strip off comments.
# Strip out blank and empty lines.
#
# Remaining lines should be of the form:
#
# freq=label
###################################################################

proc ReadLabel { } \
{
	global env
	global Label
	global LabelFile
	global Mode
	global RootDir


	if [ catch { open $LabelFile "r"} fid] \
		{
		# error
		# Tattle "Cannot open $LabelFile for reading."
		return
		} 


	# For each line in the file.

	while { [gets $fid rline] >= 0 } \
		{
		set line $rline

		# Discard comments.
		regsub {#.*} $line "" line

		# Skip blank line.
		if { [regexp {^ *$} $line] } \
			{
			continue
			}

		set line [string trimleft $line " "]

		# Valid parameter line must be of the form:
		# Fieldname=value

		set plist [ split $line "=" ]
		set n [llength $plist]

		set msg [format "Error in label file %s,\n" $LabelFile]
		set msg [append msg [format "in this line:\n%s" $rline]]

		if {$n != 2} \
			{
			tk_dialog .error "tk545" \
				$msg error 0 OK

			exit 1
			}
		set field [ lindex $plist 0 ]
		set value [ lindex $plist 1 ]
		set Label($field) $value
		}


	close $fid
	return
}

###################################################################
# Save freq/label correspondence in the label file.
###################################################################

proc SaveLabel { } \
{
	global env
	global GlobalParam
	global LabelFile
	global Label
	global Version

	if [ catch { open $LabelFile "w"} fid] \
		{
		# error
		tk_dialog .error "tk545" \
			"Cannot save labels in file $LabelFile" \
			error 0 OK

		return
		} 

	puts $fid "# tk545 label file, Version $Version"

	set a [array names Label]
	set a [ lsort -dictionary $a ]

	foreach x $a \
		{
		puts $fid "$x=$Label($x)"
		}

	close $fid
	return
}


###################################################################
# Add a frequency and label to a global cache.
###################################################################
proc Add2LabelCache { f label } \
{
	global Label

	set f [string trimleft $f " "]

	if { ($label != "") && ($f != "") && ($f > 0) } \
		{
		set f [format "%.6f" $f]
		set Label($f) $label
		}


	return
}

###################################################################
# Update the label indicator using the label cache.
###################################################################
proc UpdLabel { } \
{
	global CurrentFreq
	global Label
	global LabelLabel
	global Note
	global NoteLabel

	# Get and display frequency alpha label
	# from label cache.

	set f [string trimleft $CurrentFreq " "]
	if { [info exists Label($f)] } \
		{
		set tmp [string range $Label($f) 0 8]
		$LabelLabel configure \
			-text $tmp
		$NoteLabel configure -text $Label($f)
		} \
	else \
		{
		$LabelLabel configure -text " "
		$NoteLabel configure -text " "
		}

	return
}



######################################################################
#					Bob Parnass
#					DATE:
#
# USAGE:	SortaBank first last
#
# INPUTS:
#		first	-starting channel to sort
#		last	-ending channel to sort
#
# RETURNS:
#		0	-ok
#		-1	-error
#
#
# PURPOSE:	Sort a range of memory channels based on frequency.
#
# DESCRIPTION:
#
######################################################################
proc SortaBank { first last } \
{
	global GlobalParam
	global MemCh
	global MemAgc
	global MemFreq
	global MemLabel
	global MemMode
	global MemSel

	# global MemPopulated


	if {$GlobalParam(Populated) == 0} \
		{
		set msg "You must open a memory channel file\n"
		append msg " before sorting channels.\n"

		tk_dialog .belch "Sort channels" \
			$msg info 0 OK
		return -1
		}

	if {$GlobalParam(SortType) == "freq"} \
		{
		set inlist [Bank2List MemFreq $first $last]
		set vorder [SortFreqList $inlist]
		} \
	else \
		{
		set inlist [Bank2List MemLabel $first $last]
		set vorder [SortLabelList $inlist]
		}

	set inlist [Bank2List MemFreq $first $last]
	set slist [ReorderList $inlist $vorder]
	for {set i $first; set j 0} {$i <= $last} {incr i; incr j} \
		{
		set MemFreq($i) [lindex $slist $j]
		}

	set inlist [Bank2List MemMode $first $last]
	set slist [ReorderList $inlist $vorder]
	for {set i $first; set j 0} {$i <= $last} {incr i; incr j} \
		{
		set MemMode($i) [lindex $slist $j]
		if {$MemMode($i) == ""} \
			{
			set MemMode($i) AM
			}
		}

	set inlist [Bank2List MemLabel $first $last]
	set slist [ReorderList $inlist $vorder]
	for {set i $first; set j 0} {$i <= $last} {incr i; incr j} \
		{
		set MemLabel($i) [lindex $slist $j]
		}


	set inlist [Bank2List MemAgc $first $last]
	set slist [ReorderList $inlist $vorder]
	for {set i $first; set j 0} {$i <= $last} {incr i; incr j} \
		{
		set MemAgc($i) [lindex $slist $j]
		}


	set inlist [Bank2List MemCh $first $last]
	set slist [ReorderList $inlist $vorder]
	for {set i $first; set j 0} {$i <= $last} {incr i; incr j} \
		{
		set MemCh($i) [lindex $slist $j]
		}


	set inlist [Bank2List MemSel $first $last]
	set slist [ReorderList $inlist $vorder]
	for {set i $first; set j 0} {$i <= $last} {incr i; incr j} \
		{
		set MemSel($i) [lindex $slist $j]
		}

	return 0
}


###################################################################
# Set title of the main window so it contains the
# current channel csv file name.
###################################################################
proc SetWinTitle { } \
{
	global GlobalParam

	if { ( [info exists GlobalParam(Ifilename)] == 0 ) \
		|| ($GlobalParam(Ifilename) == "") } \
		{
		set filename untitled.csv
		} \
	else \
		{
		set filename $GlobalParam(Ifilename)
		}

	set s [format "%s - tk545" $filename]
	wm title . $s

	return
}


##########################################################
# Write data to csv file.
##########################################################
proc SaveTemplate { f asflag } \
{
	global FileTypes
	global MemCh MemFreq MemMode MemSel MemAgc MemLabel
	global GlobalParam

	global NBanks
	global NChanPerBank
	global Populated



	if {$GlobalParam(Populated) == 0} \
		{
		set msg "You must first open a file or read from"
		append msg " the radio before saving it in a"
		append msg " file."
		append msg " (Use the Radio menu for reading"
		append msg " from the radio.)"

		tk_dialog .error "No template data" \
			$msg error 0 OK
		return
		}


	set filename $GlobalParam(Ifilename)

	if { ($GlobalParam(Ifilename) == "") \
		|| ($asflag) } \
		{
		set filename \
			[Mytk_getSaveFile $f \
			$GlobalParam(MemoryFileDir) \
			.csv \
			"Save memory channel data to file" \
			$FileTypes]
		}



	if { $filename != "" }\
		{
		set GlobalParam(Ifilename) $filename
		SetWinTitle

		set GlobalParam(MemoryFileDir) \
			[ Dirname $GlobalParam(Ifilename) ]

		set fid [open $GlobalParam(Ifilename) "w"]
		# puts -nonewline $fid $Mimage

		puts $fid "Ch,MHz,Mode,Select,AGC,Label"

		set ch -1
		for {set bn 0} {$bn < $NBanks} {incr bn} \
			{
			for {set i 0} {$i < $NChanPerBank} {incr i} \
				{
				incr ch
				if {[info exists MemFreq($ch)] \
					== 0} \
					{
					continue
					}
				set freq [string trimleft \
					$MemFreq($ch) " "]

				if {$freq <= .000001} \
					{
					continue
					}
				set s ""
				append s "$ch,"
				append s "$freq,"
				append s "\"$MemMode($ch)\","
				append s "\"$MemSel($ch)\","
				append s "$MemAgc($ch),"

				if {$MemLabel($ch) != ""} \
					{
					append s "\"$MemLabel($ch)\""
					}

				puts $fid $s
				}
			}

		close $fid
		set TemplateSavedFlag yes
		}

	return
}

###################################################################
#
# Clear all memory channels by zeroing the proper arrays,
# but confirm this with the user first.
#
###################################################################
proc ClearAllChannels { } \
{
	global Cht
	global GlobalParam
	global Mimage
	global MemCh
	global MemFreq
	global NBanks
	global NChanPerBank
	global VNChanPerBank


	if { [info exists Mimage] == 0 } \
		{
		# No image to write.
		set msg "You must first read data from"
		append msg " the radio or open a file before"
		append msg " clearing memories."

		tk_dialog .error "Clear all channels" $msg error 0 OK
		return
		}


	set msg "Warning: This operation will clear all "
	append msg "memory channels."

	set result [tk_dialog .clearall "Warning" \
		$msg warning 0 Cancel "Clear Memories" ]

	if {$result == 0} {return}


	for {set bn 0} {$bn < $NBanks} {incr bn} \
		{
		set ch [expr {$bn * $VNChanPerBank}]
		for {set i 0} {$i < $NChanPerBank} {incr i} \
			{
			set MemFreq($ch) 0
			set MemCh($ch) 0
			incr ch
			}
		}
	ShowChannels $Cht
	return

}
