// ----------------------------------------------------------------------------
// Copyright (C) 2014
//              David Freese, W1HKJ
//
// This file is part of flrig.
//
// flrig 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 3 of the License, or
// (at your option) any later version.
//
// flrig 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 this program.  If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------

static bool have_not_initialized = true;

#if 0
// Information:
dspserver config file: ~/dspserver.conf
dspserver log file: ???
#endif

#include <string>
#include <cstdio>
#include <cstdlib>
#include <cerrno>

#include <FL/Fl_Secret_Input.H>

#include "socket_io.h"

#include "HPSDR.h"
#include "HPDSP.h"
// #include "HPEXTRAS.h"
// #include "HPPREFS.h"

#include "debug.h"
#include "support.h"

#define TAB_XMITOS	1
#define TAB_NOTCH	1
#define BTN_TEST	1
#define SLDR_TEST	0

#define XMIT_SUPPORT	1

#define STATUS_BAR	1

// Record args for possible restart during configuration.
int __argc;
char** __argv;

// Adjust offset between hydra transmitter and hydra receiver(s).
#define HYDRA_FREQUENCY_OFFSET	1

#define DO_INITIALIZATION	0

#if 1
static bool	master_is_set = false;
static int	master_set_clear = 0;
static int	msg_cnt = 0;
#endif

// NEW_WIDGET
//   add callback, get_/set_/_label/_value to HPSDR.h
//   add DECLARE/INIT to HPPREFS.h

#define BAND_SELECT_COLOR	215 //175 //FL_YELLOW

// Used for c callbacks from fltk land to access member functions/data.
static RIG_HPSDR* mySelf;

// Shrink the physical layout of the table.
#define CBPtr	(Fl_Callback*)&RIG_HPSDR
#define ALIGN_R		1
#define ALIGN_B		2
#define ALIGN_T		3

#if 0
static Fl_Wheel_Value_Slider *sldrAbc = (Fl_Wheel_Value_Slider *)0;
static sldrCfg sldrAbcCfg("", "", CBPtr::cb_sldrAbc,
			   &progStatus.abc, 0, 100, 1);
  { (WPP)&sldrAbc,		SLDR_X,		NXT_R,		SLDR_W,	BTN_H(1), (BCP)&sldrAbcCfg,		sldrType },
#endif

// Existing buttons:
static btnLCfg btnLPreampCfg("PreAmp", "", NULL, &progStatus.preamp);

// Additional sliders:
static Fl_Wheel_Value_Slider *sldrSubRxGain = (Fl_Wheel_Value_Slider *)0;
static sldrCfg sldrSubRxGainCfg("SubRx", "Sub rx output gain", CBPtr::cb_sldrSubRxGain,
			   &progStatus.subrx_gain, 0, 100, 1);

static Fl_Wheel_Value_Slider *sldrAttenuator = (Fl_Wheel_Value_Slider *)0;
static sldrCfg sldrAttenuatorCfg("", "Attenuation value", CBPtr::cb_sldrAtten,
				 &progStatus.attenuation, 0, 31, 1);

#if SLDR_TEST
static Fl_Wheel_Value_Slider *sldrTest = (Fl_Wheel_Value_Slider *)0;
static sldrCfg sldrTestCfg("Test", "Easter Egg Up/Down", CBPtr::cb_sldrTest,
			   &progStatus.testslider, 0, 100, 1);
#endif // SLDR_TEST

#if STATUS_BAR
static Fl_Box *StatusBar = (Fl_Box *)0;
static Fl_Button *btnPStatusClear = (Fl_Button *)0;
static btnPCfg btnPStatusClearCfg(
		"Clear", "clear status bar",
		CBPtr::cb_status_clear, NULL);
#endif // STATUS_BAR

// Additional combos:
static string _name = "";
static string _passwd = "";
static Fl_Button *btnPMaster = (Fl_Button *)0;
static btnPCfg btnPMasterCfg(
		"Master", "Request master status",
		CBPtr::cb_master, NULL);
static Fl_Input2 *inpAMasterName = (Fl_Input2 *)0;
static inpACfg inpAMasterNameCfg(
		"Call", "Enter dspserver callsign",
		CBPtr::cb_master_name, NULL, ALIGN_R);
static Fl_Secret_Input *inpSMasterPasswd = (Fl_Secret_Input *)0;
static inpSCfg inpSMasterPasswdCfg(
		"Passwd", "Enter dspserver password",
		CBPtr::cb_master_passwd, NULL, ALIGN_R);
#if XMIT_SUPPORT
static Fl_ComboBox *cmboAlexAtt = (Fl_ComboBox *)0;
static const char* cmboAlexAttChoices[] = { "0 dB", "10 dB", "20 dB", "30 dB", NULL };
static cmboCfg cmboAlexAttCfg(
		"Alex Atten", "set Alex attenuator",
		CBPtr::cb_alexatt, NULL, //&progStatus.alexatt,
		ALIGN_T, cmboAlexAttChoices);

static Fl_ComboBox *cmboTxAnt = (Fl_ComboBox *)0;
static const char* cmboTxAntChoices[] = { "ANT 1", "ANT 2", "ANT 3", NULL };
static cmboCfg cmboTxAntCfg(
		"Tx Antenna", "select Tx antenna",
		CBPtr::cb_txant, NULL, //&progStatus.txant,
		ALIGN_T, cmboTxAntChoices);

static Fl_ComboBox *cmboRxAnt = (Fl_ComboBox *)0;
static const char* cmboRxAntChoices[] = { "NONE", "EXT 1", "EXT 2", "XVTR", NULL };
static cmboCfg cmboRxAntCfg(
		"Rx Antenna", "select Rx antenna",
		CBPtr::cb_rxant, NULL, //&progStatus.rxant,
		ALIGN_T, cmboRxAntChoices);


static Fl_ComboBox *cmboHPFilt = (Fl_ComboBox *)0;
static const char* cmboHPFiltChoices[] = { "ByPass", "1.5MHz", "6.5MHz", "9.5MHz", "13MHz", "20MHz", "6M LNA", NULL };
static cmboCfg cmboHPFiltCfg(
		"Rx HPFilter", "select Rx highpass filter",
		CBPtr::cb_hpfilt, NULL, //&progStatus.hpfilt,
		ALIGN_T, cmboHPFiltChoices);

static Fl_ComboBox *cmboLPFilt = (Fl_ComboBox *)0;
static const char* cmboLPFiltChoices[] = { "160M", "80M", "60/40M", "30/20M", "17/15M", "12/10M", "6M", NULL };
static cmboCfg cmboLPFiltCfg(
		"Tx LPFilter", "select Tx lowpass filter",
		CBPtr::cb_lpfilt, NULL, //&progStatus.lpfilt,
		ALIGN_T, cmboLPFiltChoices);
#endif // XMIT_SUPPORT

static Fl_Button *btnPFilter = (Fl_Button *)0;
static btnPCfg btnPFilterCfg(
		"Filter", "Set filter",
		CBPtr::cb_filter);
static Fl_Int_Input *inpNFilter_low = (Fl_Int_Input *)0;
static inpNCfg inpNFilter_lowCfg(
		"Low", "",
		CBPtr::cb_filter_low, &progStatus.filter_low, ALIGN_R);
static Fl_Int_Input *inpNFilter_high = (Fl_Int_Input *)0;
static inpNCfg inpNFilter_highCfg(
		"High", "",
		CBPtr::cb_filter_high, &progStatus.filter_high, ALIGN_R);

static Fl_Light_Button *btnLSpectrum = (Fl_Light_Button *)0;
static btnLCfg btnLSpectrumCfg(
		"Spectrum", "Spectrum On/Off",
		CBPtr::cb_spectrum, &progStatus.spectrum);
static Fl_Int_Input *inpNSpectrum_fps = (Fl_Int_Input *)0;
static inpNCfg inpNSpectrum_fpsCfg(
		"Fps", "Spectrum frames per second",
		CBPtr::cb_spectrum_fps, &progStatus.spectrum_fps, ALIGN_R);
static Fl_Int_Input *inpNSpectrum_samples = (Fl_Int_Input *)0;
static inpNCfg inpNSpectrum_samplesCfg(
		"Samples", "Spectrum samples",
		CBPtr::cb_spectrum_samples, &progStatus.spectrum_samples, ALIGN_R);

static Fl_Button *btnPAudio = (Fl_Button *)0;
static btnPCfg btnPAudioCfg(
		"Audio\nStream", "Reset Audio Streaming",
		CBPtr::cb_audio, &progStatus.audio);
static Fl_Int_Input *inpNAudioBuf = (Fl_Int_Input *)0;
static inpNCfg inpNAudioBufCfg(
		"Buffer", "Audio buffer size",
		CBPtr::cb_audio_buf, &progStatus.audio_buf_size,
		ALIGN_T);
static Fl_ComboBox *cmboAudioRate = (Fl_ComboBox *)0;
static const char* cmboAudioRateChoices[] = { "8000", "48000", NULL };
static cmboCfg cmboAudioRateCfg(
		"Rate", "Audio sample rate",
		CBPtr::cb_audio_rate, &progStatus.audio_rate,
		ALIGN_T, cmboAudioRateChoices);
static Fl_ComboBox *cmboAudioChnls = (Fl_ComboBox *)0;
static const char* cmboAudioChnlsChoices[] = { "Mono", "Stereo", NULL };
static cmboCfg cmboAudioChnlsCfg(
		"Mono/Stereo", "Set number of channels",
		CBPtr::cb_audio_chnls, &progStatus.audio_channels,
		ALIGN_T, cmboAudioChnlsChoices);
static Fl_ComboBox *cmboAudioMicEnc = (Fl_ComboBox *)0;
static const char* cmboAudioMicEncChoices[] = { "Alaw", "Codec2", NULL };
static cmboCfg cmboAudioMicEncCfg(
		"Codec", "Change mic encoding",
		CBPtr::cb_audio_mic_enc, &progStatus.audio_mic_encode,
		ALIGN_T, cmboAudioMicEncChoices);

#if TAB_NOTCH
static int notchVfo = 0;
static int notchIndex = 0;
static string notchBandwidth = "500";
static string notchOffset = "1000";
static Fl_Button *btnPNotch = (Fl_Button *)0;
static btnPCfg btnPNotchCfg(
		"Set\nNotch", "Set notch value",
		CBPtr::cb_notch, NULL);
static Fl_ComboBox *cmboNotchVfo = (Fl_ComboBox *)0;
static const char* cmboNotchVfoChoices[] = { "vfoA", "vfoB", NULL };
static cmboCfg cmboNotchVfoCfg(
		"Vfo", "Select vfo",
		CBPtr::cb_notch_vfo, &notchVfo,
		ALIGN_T, cmboNotchVfoChoices);
static Fl_ComboBox *cmboNotchIndex = (Fl_ComboBox *)0;
static const char* cmboNotchIndexChoices[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", NULL };
static cmboCfg cmboNotchIndexCfg(
		"Filter", "Select filter index",
		CBPtr::cb_notch_index, &notchIndex,
		ALIGN_T, cmboNotchIndexChoices);
static Fl_Int_Input *inpNNotchBandwidth = (Fl_Int_Input *)0;
static inpNCfg inpNNotchBandwidthCfg(
		"Bandwidth", "Notch bandwidth",
		CBPtr::cb_notch_bandwidth, &notchBandwidth,
		ALIGN_T);
static Fl_Int_Input *inpNNotchOffset = (Fl_Int_Input *)0;
static inpNCfg inpNNotchOffsetCfg(
		"Offset", "Notch offset",
		CBPtr::cb_notch_offset, &notchOffset,
		ALIGN_T);
#endif // TAB_NOTCH

static int notchEnableVfo = 0;
static int notchEnableIndex = 0;
static int notchEnableValue = 0;
static Fl_Button *btnPNotchEnable = (Fl_Button *)0;
static btnPCfg btnPNotchEnableCfg(
		"Enable\nDisable", "Enable/disable notch",
		CBPtr::cb_notchenable, NULL);
static Fl_ComboBox *cmboNotchEnableVfo = (Fl_ComboBox *)0;
static const char* cmboNotchEnableVfoChoices[] = { "vfoA", "vfoB", NULL };
static cmboCfg cmboNotchEnableVfoCfg(
		"Vfo", "Select vfo",
		CBPtr::cb_notchenable_vfo, &notchEnableVfo,
		ALIGN_T, cmboNotchEnableVfoChoices);
static Fl_ComboBox *cmboNotchEnableIndex = (Fl_ComboBox *)0;
static const char* cmboNotchEnableIndexChoices[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "All", NULL };
static cmboCfg cmboNotchEnableIndexCfg(
		"Filter", "Select filter index",
		CBPtr::cb_notchenable_index, &notchEnableIndex,
		ALIGN_T, cmboNotchEnableIndexChoices);
static Fl_ComboBox *cmboNotchEnableValue = (Fl_ComboBox *)0;
static const char* cmboNotchEnableValueChoices[] = { "Disable", "Enable", NULL };
static cmboCfg cmboNotchEnableValueCfg(
		"Enable", "Enable/Disable",
		CBPtr::cb_notchenable_value, &notchEnableValue,
		ALIGN_T, cmboNotchEnableValueChoices);

static Fl_ComboBox *cmboRxEncoding = (Fl_ComboBox *)0;
static const char* cmboRxEncodingChoices[] = { "Alaw", "Pcm","Codec2", NULL };
static cmboCfg cmboRxEncodingCfg(
		"Rx encode", "Change Rx encoding",
		CBPtr::cb_rx_encoding, &progStatus.rx_encoding,
		ALIGN_T, cmboRxEncodingChoices);
static Fl_ComboBox *cmboRxAGC = (Fl_ComboBox *)0;
static const char* cmboRxAGCChoices[] = { "Off", "Long", "Slow", "Med", "Fast", NULL };
static cmboCfg cmboRxAGCCfg(
		"Rx AGC", "Change Rx AGC",
		CBPtr::cb_rx_agc, &progStatus.rx_agc,
		ALIGN_T, cmboRxAGCChoices);
static Fl_Int_Input *inpNFixedAGC = (Fl_Int_Input *)0;
static inpNCfg inpNFixedAGCCfg(
		"Fixed AGC", "Set fixed AGC",
		CBPtr::cb_fixed_agc, &progStatus.fixed_agc,
		ALIGN_T);

// Hydra support
static Fl_Wheel_Value_Slider *sldrHzhos = (Fl_Wheel_Value_Slider *)0;
static sldrCfg sldrHzhosCfg("Adjust\nOffset", "Adjust hydra xmit/rcv offset in Hz",
			    CBPtr::cb_sldrHzhos,
			    &progStatus.hz_hos, -50, 50, 1);

#if TAB_XMITOS
static Fl_Button *btnPHydraOS = (Fl_Button *)0;
static btnPCfg btnPHydraOSCfg(
		"Set Hydra\nXmit Offset", "Set xmit/rcv frequency offset",
		CBPtr::cb_hydraOS);

static Fl_Int_Input *inpNHydraDwf = (Fl_Int_Input *)0;
static inpNCfg inpNHydraDwfCfg(
		"Dial Freq", "Input dial frequency",
		CBPtr::cb_hydra_dwf, &progStatus.hdial_freq, ALIGN_T);
static Fl_Int_Input *inpNHydraXwf = (Fl_Int_Input *)0;
static inpNCfg inpNHydraXwfCfg(
		"XmitWf", "Input transmitter wf cursor",
		CBPtr::cb_hydra_xwf, NULL, ALIGN_T);
static Fl_Int_Input *inpNHydraRwf = (Fl_Int_Input *)0;
static inpNCfg inpNHydraRwfCfg(
		"RcvWf", "Input receiver wf cursor",
		CBPtr::cb_hydra_rwf, NULL, ALIGN_T);
static Fl_Int_Input *inpNHydraPwf = (Fl_Int_Input *)0;
static inpNCfg inpNHydraPwfCfg(
		"Ppm Offset", "Xmit-Rcv PPM Offset",
		CBPtr::cb_hydra_pwf, NULL/*&progStatus.ppm_hos*/, ALIGN_T);
#endif // TAB_XMITOS

// Additional buttons:
static Fl_Light_Button *btnLSetAnf = (Fl_Light_Button *)0;
static btnLCfg btnLSetAnfCfg(
		"AutoNF", "Auto NF On/Off",
		CBPtr::cb_SetAnf, &progStatus.anf);

#if BTN_TEST
static Fl_Light_Button *btnLTestbutton = (Fl_Light_Button *)0;
static btnLCfg btnLTestbuttonCfg(
		"Test",  "Easter Egg On/Off",
		CBPtr::cb_Testbutton, &progStatus.testbutton);
#endif // BTN_TEST

static Fl_Light_Button *btnLDither = (Fl_Light_Button *)0;
static btnLCfg btnLDitherCfg(
		"Dither", "Dither On/Off",
		CBPtr::cb_Dither, &progStatus.dither);
static Fl_Light_Button *btnLRandom = (Fl_Light_Button *)0;
static btnLCfg btnLRandomCfg(
		"Random", "Random On/Off",
		CBPtr::cb_Random, &progStatus.random);
static Fl_Light_Button *btnLRxDCB = (Fl_Light_Button *)0;
static btnLCfg btnLRxDCBCfg(
		"RxDCB", "Rx DC block On/Off",
		CBPtr::cb_Rxdcb, &progStatus.rxdcb);
static Fl_Light_Button *btnLTxDCB = (Fl_Light_Button *)0;
static btnLCfg btnLTxDCBCfg(
		"TxDCB", "Tx DC block On/Off",
		CBPtr::cb_Txdcb, &progStatus.txdcb);
static Fl_Light_Button *btnLPwsmode = (Fl_Light_Button *)0;
static btnLCfg btnLPwsmodeCfg(
		"Pwsmode", "Pwsmode On/Off",
		CBPtr::cb_Pwsmode, &progStatus.pwsmode);

static Fl_Light_Button *btnLSetIQenable = (Fl_Light_Button *)0;
static btnLCfg btnLSetIQenableCfg(
		"IQEnable", "IQ enable On/Off",
		CBPtr::cb_SetIQenable, &progStatus.iqenable);
static Fl_Light_Button *btnLSdrom = (Fl_Light_Button *)0;
static btnLCfg btnLSdromCfg(
		"Sdrom", "Sdrom On/Off",
		CBPtr::cb_Sdrom, &progStatus.sdrom);
static Fl_Light_Button *btnLSpectrumPPhase = (Fl_Light_Button *)0;
static btnLCfg btnLSpectrumPPhaseCfg(
		"PolyPhase", "SpectrumPolyphase On/Off",
		CBPtr::cb_SpectrumPPhase, &progStatus.spectrumpolyphase);
static Fl_Light_Button *btnLRxSquelch = (Fl_Light_Button *)0;
static btnLCfg btnLRxSquelchCfg(
		"RxSq", "Rx Squelch On/Off",
		CBPtr::cb_RxSquelch, &progStatus.squelchstate);
static Fl_Light_Button *btnLSubRxSquelch = (Fl_Light_Button *)0;
static btnLCfg btnLSubRxSquelchCfg(
		"SubRxSq", "Sub Rx Squelch On/Off",
		CBPtr::cb_SubRxSquelch, &progStatus.subrxquelchstate);

// NEW_WIDGET ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


//
// Additional tabs.
//

// Hermes tab 
Fl_Group *genericTab1;
Hspinner *spnr_hermes;
Fl_Check_Button *btn_hermes;

#if TAB_XMITOS
// Xmit-Rcv offset tab.
Fl_Group *genericTab2;
#endif // TAB_XMITOS

#if TAB_NOTCH
// 
Fl_Group *genericTab3;
#endif // TAB_NOTCH

const char HPSDRname_[] = "HPSDR";

const char *HPSDRmodes_[] = {
  "LSB", "USB", "DSB", "CWL", "CWU", "FM", "AM", "DIGU", "SPEC", "DIGL", "SAM", "DRM", NULL
};

const int modenbr[] = {
   LSB_,  USB_,  DSB_,  CWL_,  CWU_,  FM_,  AM_,  DIGU_,  SPEC_,  DIGL_,  SAM_,  DRM_
};

static const char HPSDR_mode_type[] = {
    'L',   'U',   'U',   'L',   'U',  'U',  'U',    'U',    'U',    'L',   'U',   'U'
};

static const char *HPSDR_widths[] = {
   "50",  "100",  "150",  "200",  "250",  "300",  "350",  "400",  "450",  "500",
  "550",  "600",  "650",  "700",  "750",  "800",  "850",  "900",  "950", "1000",
 "1100", "1200", "1300", "1400", "1500", "1600", "1700", "1800", "1900", "2000",
 "2200", "2400", "2600", "2800", "3000", "3200", "3400", "3600", "5000", "10000",
 NULL};

/// XXX ??? static int def_mode_width[] = { 34, 34, 15, 37, 37, 34, 15, 34 };

// I can't believe this really works...
// main window geometry
#define P_BASE		25
#define S_BASE		125
#define R_H		20

// tab geometry
#define TP_BASE		2
#define TS_BASE		277

static int _y_ = S_BASE;
static int _x_ = P_BASE;

#define BTN_X		2
#define BTN_N		50
#define BTN_W		84
#define BTN_H(X)	(X*R_H)
#define BTN_C		100
#define SLDR_X		54
#define SLDR_W		368
#define CHAR_W		8
#define	CB2_W		80
#define	CB4_W		84
#define	CB5_W		84

#define BTN_GAP		1
#define H_GAP		4
#define NXT_R		(_y_ += R_H)
#define NXT_R2		(_y_ += (2*R_H))
#define NEW_R		(_y_ += (R_H+H_GAP))
#define NEW_R2		(_y_ += ((2*R_H)+H_GAP))
#define SAME_R		(_y_)
#define SAME_R2		(_y_+R_H)
#define SAME_POS	(_x_)
#define FIRST_POS	(_x_ = 1)
#define NEXT_POS	(_x_ += (BTN_W+BTN_GAP))
#define LAST_POS	75
#define CB2A		(_x_ += (BTN_W+CHAR_W))
#define CB2B(X)		(_x_ += (CB2_W+BTN_GAP)+((X+1)*CHAR_W))
#define CB4		(_x_ = (BTN_W+BTN_GAP))
#define CB4A		(_x_ += (BTN_W+BTN_GAP))
#define CB4B(X)		(_x_ += (CB4_W+1)+((X)*CHAR_W))
#define CB4C(X)		(_x_ += (CB4_W+1)+((X)*CHAR_W))
#define CB4D(X)		(_x_ += (CB4_W+1)+((X)*CHAR_W))

#define CB5X		(_x_ = (BTN_GAP))
#define CB5A		(_x_ = (BTN_W+BTN_GAP))
#define CB5B(X)		(_x_ += (CB5_W+1)+((X)*CHAR_W))
#define CB5C(X)		(_x_ += (CB5_W+1)+((X)*CHAR_W))
#define CB5D(X)		(_x_ += (CB5_W+1)+((X)*CHAR_W))
#define CB5E(X)		(_x_ += (CB5_W+1)+((X)*CHAR_W))

#define RESET_S_BASE	(_y_ = TS_BASE)
#define RESET_P_BASE	(_x_ = TP_BASE)

typedef Fl_Widget**	WPP;
typedef baseCfg*	CP;

typedef enum {
	nilType,
	btnPType,	// plain button
	btnLType,	// lighted button
	sldrType,	// slider
	inpNType,	// numeric input
	inpAType,	// alpha input
	inpSType,	// "secret" input
	cmboType	// choice input
} widgetType;

static GUI2 hpsdr_widgetPtrs[] = {
#if STATUS_BAR
  { (WPP)&btnPStatusClear, 	BTN_X,		SAME_R,		BTN_W,	BTN_H(1), (CP)&btnPStatusClearCfg,	btnPType },
#endif

// SLIDERS
  { (WPP)&btnVol,		BTN_X,		NEW_R,		BTN_N,	BTN_H(1), NULL,				btnLType },
  { (WPP)&sldrVOLUME,		SLDR_X,		SAME_R,		SLDR_W,	BTN_H(1), NULL,				sldrType },
  { (WPP)&sldrSubRxGain,	SLDR_X,		NXT_R,		SLDR_W,	BTN_H(1), (CP)&sldrSubRxGainCfg,	sldrType },

  { (WPP)&btnAttenuator,  	BTN_X,		NXT_R,		BTN_N,	BTN_H(1), NULL,				btnLType },
  { (WPP)&sldrAttenuator,	SLDR_X,		SAME_R,		SLDR_W,	BTN_H(1), (CP)&sldrAttenuatorCfg,	sldrType },

  { (WPP)&sldrRFGAIN,	 	SLDR_X,		NEW_R,		SLDR_W,	BTN_H(1), NULL,				sldrType },
  { (WPP)&sldrMICGAIN,		SLDR_X,		NXT_R,		SLDR_W,	BTN_H(1), NULL,				sldrType },
  { (WPP)&sldrPOWER,  		SLDR_X,		NXT_R,		SLDR_W,	BTN_H(1), NULL,				sldrType },
  { (WPP)&sldrHzhos,		SLDR_X,		NEW_R,		SLDR_W,	BTN_H(1), (CP)&sldrHzhosCfg,		sldrType },
#if SLDR_TEST
  { (WPP)&sldrTest,		SLDR_X,		NXT_R,		SLDR_W,	BTN_H(1), (CP)&sldrTestCfg,		sldrType },
#endif // SLDR_TEST

  // NEW_WIDGET complete row, slider ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  // COMBOS
  { (WPP)&btnPMaster,  		FIRST_POS,	NEW_R,		BTN_W,	BTN_H(1), (CP)&btnPMasterCfg,		btnPType },
  { (WPP)&inpAMasterName,	CB2A,		SAME_R,		CB2_W,	BTN_H(1), (CP)&inpAMasterNameCfg,	inpAType },
  { (WPP)&inpSMasterPasswd,	CB2B(9),	SAME_R,		CB2_W,	BTN_H(1), (CP)&inpSMasterPasswdCfg,	inpSType },

#if XMIT_SUPPORT
  { (WPP)&cmboAlexAtt,		CB5X,		NEW_R2,		CB4_W,	BTN_H(1), (CP)&cmboAlexAttCfg,		cmboType },
  { (WPP)&cmboTxAnt,		CB5A,		SAME_R,		CB4_W,	BTN_H(1), (CP)&cmboTxAntCfg,		cmboType },
  { (WPP)&cmboRxAnt,		CB5B(0),	SAME_R,		CB5_W,	BTN_H(1), (CP)&cmboRxAntCfg,		cmboType },
  { (WPP)&cmboHPFilt,		CB5C(0),	SAME_R,		CB5_W,	BTN_H(1), (CP)&cmboHPFiltCfg,		cmboType },
  { (WPP)&cmboLPFilt,		CB5D(0),	SAME_R,		CB5_W,	BTN_H(1), (CP)&cmboLPFiltCfg,		cmboType },
#endif // XMIT_SUPPORT

  { (WPP)&btnPFilter,  		FIRST_POS,	NEW_R,		BTN_W,	BTN_H(1), (CP)&btnPFilterCfg,		btnPType },
  { (WPP)&inpNFilter_low,	CB2A,		SAME_R,		CB2_W,	BTN_H(1), (CP)&inpNFilter_lowCfg,	inpNType },
  { (WPP)&inpNFilter_high,	CB2B(9),	SAME_R,		CB2_W,	BTN_H(1), (CP)&inpNFilter_highCfg,	inpNType },

  { (WPP)&btnLSpectrum,  	FIRST_POS,	NEW_R,		BTN_W,	BTN_H(1), (CP)&btnLSpectrumCfg,		btnLType },
  { (WPP)&inpNSpectrum_fps,	CB2A,		SAME_R,		CB2_W,	BTN_H(1), (CP)&inpNSpectrum_fpsCfg,	inpNType },
  { (WPP)&inpNSpectrum_samples,	CB2B(9),	SAME_R,		CB2_W,	BTN_H(1), (CP)&inpNSpectrum_samplesCfg, inpNType },

  { (WPP)&btnPAudio,	  	FIRST_POS,	NEW_R,		BTN_W,	BTN_H(2), (CP)&btnPAudioCfg,		btnPType },
  { (WPP)&inpNAudioBuf,		CB4A,		SAME_R2,	CB4_W,	BTN_H(1), (CP)&inpNAudioBufCfg,		inpNType },
  { (WPP)&cmboAudioRate,	CB4B(0),	SAME_R2,	CB4_W,	BTN_H(1), (CP)&cmboAudioRateCfg,	cmboType },
  { (WPP)&cmboAudioChnls,	CB4C(0),	SAME_R2,	CB4_W,	BTN_H(1), (CP)&cmboAudioChnlsCfg,	cmboType },
  { (WPP)&cmboAudioMicEnc,	CB4D(0),	SAME_R2,	CB4_W,	BTN_H(1), (CP)&cmboAudioMicEncCfg,	cmboType },

#if !TAB_NOTCH
  { (WPP)&btnPNotch,	  	FIRST_POS,	NEW_R2,		BTN_W,	BTN_H(2), (CP)&btnPNotchCfg,		btnPType },
  { (WPP)&cmboNotchVfo,		CB4A,		SAME_R2,	CB4_W,	BTN_H(1), (CP)&cmboNotchVfoCfg,		cmboType },
  { (WPP)&cmboNotchIndex,	CB4B(0),	SAME_R2,	CB4_W,	BTN_H(1), (CP)&cmboNotchIndexCfg,	cmboType },
  { (WPP)&inpNNotchBandwidth,	CB4C(0),	SAME_R2,	CB4_W,	BTN_H(1), (CP)&inpNNotchBandwidthCfg,	inpNType },
  { (WPP)&inpNNotchOffset,	CB4D(0),	SAME_R2,	CB4_W,	BTN_H(1), (CP)&inpNNotchOffsetCfg,	inpNType },
#endif // !TAB_NOTCH

  { (WPP)&btnPNotchEnable,  	FIRST_POS,	NXT_R2,		BTN_W,	BTN_H(2), (CP)&btnPNotchEnableCfg,	btnPType },
  { (WPP)&cmboNotchEnableVfo,	CB4A,		SAME_R2,	CB4_W,	BTN_H(1), (CP)&cmboNotchEnableVfoCfg,	cmboType },
  { (WPP)&cmboNotchEnableIndex,	CB4B(0),	SAME_R2,	CB4_W,	BTN_H(1), (CP)&cmboNotchEnableIndexCfg,	cmboType },
  { (WPP)&cmboNotchEnableValue,	CB4B(0),	SAME_R2,	CB4_W,	BTN_H(1), (CP)&cmboNotchEnableValueCfg,	cmboType },

  { (WPP)&cmboRxEncoding,	CB5A,		NEW_R2+R_H,	CB4_W,	BTN_H(1), (CP)&cmboRxEncodingCfg,	cmboType },
  { (WPP)&cmboRxAGC,		CB5B(0),	SAME_R2,	CB5_W,	BTN_H(1), (CP)&cmboRxAGCCfg,		cmboType },
  { (WPP)&inpNFixedAGC,		CB5C(0),	SAME_R2,	CB5_W,	BTN_H(1), (CP)&inpNFixedAGCCfg,		inpNType },

#if !TAB_XMITOS
  { (WPP)&btnPHydraOS,	  	FIRST_POS,	NEW_R2,		BTN_W,	BTN_H(2), (CP)&btnPHydraOSCfg,		btnPType },
  { (WPP)&inpNHydraDwf,		CB4A,		SAME_R2,	CB4_W,	BTN_H(1), (CP)&inpNHydraDwfCfg,		inpNType },
  { (WPP)&inpNHydraXwf,		CB4A,		SAME_R2,	CB4_W,	BTN_H(1), (CP)&inpNHydraXwfCfg,		inpNType },
  { (WPP)&inpNHydraRwf,		CB4A,		SAME_R2,	CB4_W,	BTN_H(1), (CP)&inpNHydraRwfCfg,		inpNType },
#endif // !TAB_XMITOS

  // NEW_WIDGET complete row, combo, etc. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  // BUTTONS
  { (WPP)&btnPreamp,		FIRST_POS,	NEW_R2,		BTN_W,	BTN_H(1), (CP)&btnLPreampCfg,		btnLType },
  { (WPP)&btnNOISE,		NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), NULL,				btnLType },
  { (WPP)&btnNR,		NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), NULL,				btnLType },
  { (WPP)&btnLSetAnf,		NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), (CP)&btnLSetAnfCfg,		btnLType },
#if BTN_TEST
  { (WPP)&btnLTestbutton,	NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), (CP)&btnLTestbuttonCfg,	btnLType },
#endif // BTN_TEST

  { (WPP)&btnLDither,		FIRST_POS,	NXT_R,		BTN_W,	BTN_H(1), (CP)&btnLDitherCfg,		btnLType },
  { (WPP)&btnLRandom,		NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), (CP)&btnLRandomCfg,		btnLType },
  { (WPP)&btnLRxDCB,		NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), (CP)&btnLRxDCBCfg,		btnLType },
  { (WPP)&btnLTxDCB,		NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), (CP)&btnLTxDCBCfg,		btnLType },
  { (WPP)&btnLPwsmode,		NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), (CP)&btnLPwsmodeCfg,		btnLType },

  { (WPP)&btnLSetIQenable,	FIRST_POS,	NXT_R,		BTN_W,	BTN_H(1), (CP)&btnLSetIQenableCfg,	btnLType },
  { (WPP)&btnLSdrom,		NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), (CP)&btnLSdromCfg,		btnLType },
  { (WPP)&btnLSpectrumPPhase,	NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), (CP)&btnLSpectrumPPhaseCfg,	btnLType },
  { (WPP)&btnLRxSquelch,	NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), (CP)&btnLRxSquelchCfg,	btnLType },
  { (WPP)&btnLSubRxSquelch,	NEXT_POS,	SAME_R,		BTN_W,	BTN_H(1), (CP)&btnLSubRxSquelchCfg,	btnLType },

  // NEW_WIDGET buttons  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  { (WPP)NULL,			0,		0,		0,	0,	  NULL,				nilType }
};

// IMPORTANT: we MUST capture current value of _y_ before doing the tab geometry.
static int additional_height = _y_;

#if TAB_XMITOS
#define INP_N		52
#define INP_M		80
#define INP_W		88
#define INP4A		(_x_ += (BTN_W+BTN_GAP))
#define INP4B		(_x_ += (INP_W+BTN_GAP))
#define INP4C		(_x_ += (INP_N+BTN_GAP))
#define	INP_X		(140)
static GUI2 tab2Ptrs[] = {
  { (WPP)&btnPHydraOS,	  	RESET_P_BASE,	RESET_S_BASE,	BTN_W,	BTN_H(2), (CP)&btnPHydraOSCfg,		btnPType },
  { (WPP)&inpNHydraDwf,		INP4A,		SAME_R2,	INP_W,	BTN_H(1), (CP)&inpNHydraDwfCfg,		inpNType },
  { (WPP)&inpNHydraXwf,		INP4B,		SAME_R2,	INP_N,	BTN_H(1), (CP)&inpNHydraXwfCfg,		inpNType },
  { (WPP)&inpNHydraRwf,		INP4C,		SAME_R2,	INP_N,	BTN_H(1), (CP)&inpNHydraRwfCfg,		inpNType },
  { (WPP)&inpNHydraPwf,		INP4C,		SAME_R2,	INP_X,	BTN_H(1), (CP)&inpNHydraPwfCfg,		inpNType },
  { (WPP)NULL,			0,		0,		0,	0,	  NULL,				nilType }
};
#endif // TAB_XMITOS

#if TAB_NOTCH
static GUI2 tab3Ptrs[] = {
  { (WPP)&btnPNotch,	  	RESET_P_BASE,	RESET_S_BASE,	BTN_W,	BTN_H(2), (CP)&btnPNotchCfg,		btnPType },
  { (WPP)&cmboNotchVfo,		CB4A,		SAME_R2,	CB4_W,	BTN_H(1), (CP)&cmboNotchVfoCfg,		cmboType },
  { (WPP)&cmboNotchIndex,	CB4B(0),	SAME_R2,	CB4_W,	BTN_H(1), (CP)&cmboNotchIndexCfg,	cmboType },
  { (WPP)&inpNNotchBandwidth,	CB4C(0),	SAME_R2,	INP_M,	BTN_H(1), (CP)&inpNNotchBandwidthCfg,	inpNType },
  { (WPP)&inpNNotchOffset,	CB4D(0),	SAME_R2,	INP_M,	BTN_H(1), (CP)&inpNNotchOffsetCfg,	inpNType },
  { (WPP)NULL,			0,		0,		0,	0,	  NULL,				nilType }
};
#endif // TAB_NOTCH

typedef enum {
	nilPref,
	boolPref,
	intPref,
	stringPref,
	floatPref,
	doublePref
} prefType;

struct pref {
	void* prefPtr;
	string name;
	prefType type;
};

static struct pref prefs[] = {
  { &progStatus.dither,			"dither",		boolPref   },
  { &progStatus.random,			"random",		boolPref   },
  { &progStatus.attenuation,		"attenuation",		intPref    },
  { &progStatus.rxdcb,			"rxdcb",		boolPref   },
  { &progStatus.txdcb,			"txdcb",		boolPref   },
  { &progStatus.pwsmode,		"pwsmode",		boolPref   },
  { &progStatus.anf,			"anf",			boolPref   },
  { &progStatus.iqenable,		"iqenable",		boolPref   },
  { &progStatus.sdrom,			"sdrom",		boolPref   },
  { &progStatus.spectrumpolyphase,	"spectrumpolyphase",	boolPref   },
  { &progStatus.squelchstate,		"squelchstate",		boolPref   },
  { &progStatus.subrxquelchstate,	"subrxquelchstate",	boolPref   },
  { &progStatus.testbutton,		"testbutton",		boolPref   },
  { &progStatus.subrx_gain,		"subrx_gain",		intPref    },
  { &progStatus.testslider,		"testslider",		intPref    },
  { &progStatus.spnr_hermes,		"spnr_hermes",		intPref    },
  { &progStatus.btn_hermes,		"btn_hermes",		boolPref   },
  { &progStatus.filter_low,		"filter_low",		stringPref },
  { &progStatus.filter_high,		"filter_high",		stringPref },
  { &progStatus.spectrum,		"spectrum",		boolPref   },
  { &progStatus.spectrum_fps,		"spectrum_fps",		stringPref },
  { &progStatus.spectrum_samples,	"spectrum_samples",	stringPref },
  { &progStatus.audio,			"audio",		boolPref   },
  { &progStatus.audio_buf_size,		"audio_buf_size",	stringPref },
  { &progStatus.audio_rate,		"audio_rate",		intPref    },
  { &progStatus.audio_channels,		"audio_channels",	intPref    },
  { &progStatus.audio_mic_encode,	"audio_mic_encode",	intPref    },
  { &progStatus.rx_encoding,		"rx_encoding",		intPref    },
  { &progStatus.rx_agc,			"rx_agc",		intPref    },
  { &progStatus.fixed_agc,		"fixed_agc",		stringPref },
  { &progStatus.ppm_hos,		"ppm_hos",		doublePref },
  { &progStatus.hz_hos,			"hz_hos",		intPref    },
  { &progStatus.hdial_freq,		"hdial_freq",		stringPref },

  // NEW_PREF  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  { NULL,				"",			nilPref    }
};

// Dsp server ports start @ 8000
#define DSP_PORT_BASE	8000

// length of command buffers are fixed:
#define SEND_BUFFER_SIZE 64

#define FREQUENCY_MAX	60000000		// 60.0MHz max, 0MHz min assumed
#define SDR_FILT_LO	0
#define SDR_FILT_HI	7000

#define USE_PCM		1			// XXX till we make PCM/ALAW programmatic
#if USE_PCM
#define MAGIC_SIZE	(2096 * 2)		// 2 bytes per sample
#else
#define MAGIC_SIZE	(2096)			// 1 byte per sample
#endif // USE_PCM

// Throttle initialization commands comming down from base controller.
// Theory is that radio (FlRadio, QtRadio, etc.) should do the initialization.
static bool enable_commands = false;

//=============================================================================

RIG_HPSDR::RIG_HPSDR()
{
	mySelf = this;

	name_ = HPSDRname_;

	sdr_filt_lo = SDR_FILT_LO;
	sdr_filt_hi = SDR_FILT_HI;

	modes_ = HPSDRmodes_;
	bandwidths_ = HPSDR_widths;

	widgetPtrs = hpsdr_widgetPtrs;

	def_mode = modeA = 1;
	def_bw = bwA = 38;	// 3600?
	can_change_alt_vfo =

	// Enable/disable buttons and sliders,
	//  in general, _control refers to a button.
	// These are disabled:
	has_a2b =
	has_alc_control =
	has_auto_notch =
	has_attenuator_control =
	has_bfo =
	has_cw_vol =
	has_data_port =
	has_dsp_controls =
	has_get_info =
	has_ifshift_control =
	has_micgain_control =
	has_mic_line_control =
	has_noise_reduction =
	has_noise_reduction_control =
	has_power_control =
	has_power_out =
	has_ptt_control =
	has_rit =
	has_smeter =
	has_special =
	has_sql_control =
	has_swr_control =
	has_tune_control =
	has_xit =
	has_cw_keyer =
	has_cw_qsk =
	has_cw_spot =
	has_cw_spot_tone =
	has_cw_weight =
	has_cw_wpm =
	has_vox_anti =
	has_vox_gain =
	has_vox_hang =
	has_vox_on_dataport =
	has_vox_onoff =
	has_compON =
	has_compression =
	has_vfo_adj =
	has_bpf_center =
	has_xcvr_auto_on_off =
	has_nb_level =
	has_agc_level =
			false;

	// These are enabled:
	has_preamp_control =
	has_attenuator_control =

	has_band_selection =
	has_bandwidth_control =

	// XXX should be has_xxx_control
	has_dither =
	has_random =

	has_extras =
	has_mode_control =

	has_noise_reduction_control =

	has_split =
	has_split_AB =
	has_volume_control =

	has_power_control =
				 true;

	// Frequency display.
	precision = 1;
	ndigits = 8;

	if_shift_min = 400;
	if_shift_max = 2600;
	if_shift_step = 10;
	if_shift_mid = 1500;
}

// Fill the progStatus structure with values from HPSDR.prefs file.
// Called from within status.cxx:
// XXX we need to add a "default value" field to the prefs table.
void RIG_HPSDR::spref_get(Fl_Preferences &spref)
{
	char defbuffer[200];
	int i = 0;

	for (int x = 0; prefs[x].prefPtr; ++x) {
		switch (prefs[x].type) {
		case boolPref:
			if (spref.get(prefs[x].name.c_str(), i, i))
				*((bool *)prefs[x].prefPtr) = i;
			break;
		case intPref:
			spref.get(prefs[x].name.c_str(),
				*((int *)prefs[x].prefPtr), *((int *)prefs[x].prefPtr));
			break;
		case floatPref:
			spref.get(prefs[x].name.c_str(),
				*((float *)prefs[x].prefPtr), *((float *)prefs[x].prefPtr));
			break;
		case doublePref:
			spref.get(prefs[x].name.c_str(),
				  *((double *)prefs[x].prefPtr), *((double *)prefs[x].prefPtr));
			break;
		case stringPref:
			spref.get(prefs[x].name.c_str(), defbuffer, "", 199);
			*((string *)prefs[x].prefPtr) = defbuffer;
			break;
		default:
			fprintf(stderr, "**************** broken prefStatus table!\n");
			exit(-1);
		}
	}
}

// Fill the HPSDR.prefs file with values from the progStatus structure.
// Called from within status.cxx:
void RIG_HPSDR::spref_set(Fl_Preferences &spref)
{
	for (int x = 0; prefs[x].prefPtr; ++x) {
		switch (prefs[x].type) {
		case boolPref:
			spref.set(prefs[x].name.c_str(), *((bool *)prefs[x].prefPtr));
			break;
		case intPref:
			spref.set(prefs[x].name.c_str(), *((int *)prefs[x].prefPtr));
			break;
		case floatPref:
			spref.set(prefs[x].name.c_str(), *((float *)prefs[x].prefPtr));
			break;
		case doublePref:
			spref.set(prefs[x].name.c_str(), *((double *)prefs[x].prefPtr), 9);
			break;
		case stringPref:
			spref.set(prefs[x].name.c_str(), (*(string *)prefs[x].prefPtr).c_str());
			break;
		default:
			fprintf(stderr, "**************** broken prefStatus table!\n");
			exit(-1);
		}
	}
}

// 
// Called from within support/support.cxx:
void RIG_HPSDR::initXcvrTab()
{
	SELRIG_TRACK("");

#if TAB_XMITOS
	// Xmit-Rcv offset tab.
	tabsGeneric->insert(*genericTab2, 1);
#endif // TAB_XMITOS
#if TAB_NOTCH
	// 
	tabsGeneric->insert(*genericTab3, 2);
#endif // TAB_NOTCH

	// Hermes tab.
	tabsGeneric->insert(*genericTab1, 3);

	spnr_hermes->minimum(1);
	spnr_hermes->maximum(15);
	spnr_hermes->step(1);
#if 0
	progStatus.spnr_hermes = selrig->getHermes();
#endif
	spnr_hermes->value(progStatus.spnr_hermes);
	spnr_hermes->show();

	btn_hermes->value(progStatus.btn_hermes);
	btn_hermes->show();

	genericTab1->deactivate();
	spnr_hermes->deactivate();
	btn_hermes->deactivate();

// NEW_TAB ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}

//
// Called from within UI/ui_small.cxx:
void RIG_HPSDR::adjust_small_main_group(int w, int h, int &aw, int &ah)
{
	aw = w;
	ah = h + (additional_height - 240);

#if 0
	fprintf(stderr, "******************************************************************\n");
	fprintf(stderr, "*** additional_height: %d, h: %d\n", additional_height, ah);
	fprintf(stderr, "******************************************************************\n");
#endif
}

//
// Called from within UI/ui_small.cxx:
void RIG_HPSDR::addto_small_main_group()
{
	SELRIG_TRACK("");

#if STATUS_BAR
// XXX make a boxCfg function and place this dirctly in the table.
#define STATUS_XP	90
#define STATUS_YP	125
#define STATUS_W	332
	StatusBar = new Fl_Box(BTN_W + 6, 125, STATUS_W, R_H, "");
	StatusBar->box(FL_DOWN_BOX);
	StatusBar->color(FL_BACKGROUND2_COLOR);
	StatusBar->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
#endif // STATUS_BAR

	addto_group(hpsdr_widgetPtrs);

	have_not_initialized = false;
}

// 
void RIG_HPSDR::addto_group(GUI2* wP)
{
	for (int x = 0; wP[x].W != NULL; ++x) {
		if (wP[x].C) {
			(wP[x].C)->initialize(&(wP[x]));
		}
	}
}

//
// Called from within UI/ui_small.cxx:
void RIG_HPSDR::add_tab1()
{
	SELRIG_TRACK("");

	tabsGeneric->remove(*genericTab1);

	genericTab1 = new Fl_Group(0, 271, 425, 49, "Hermes");
	genericTab1->hide();

	spnr_hermes = new Hspinner(24, 279, 96, 22, "Hermes");
	spnr_hermes->tooltip("Hermes Spinner");
	spnr_hermes->type(1);
	spnr_hermes->callback((Fl_Callback*)cb_spnr_hermes);
	spnr_hermes->align(Fl_Align(34));
	spnr_hermes->value(progStatus.spnr_hermes);

	btn_hermes = new Fl_Check_Button(325, 280, 95, 15, "Hermes");
	btn_hermes->tooltip("Hermes Button");
	btn_hermes->down_box(FL_DOWN_BOX);
	btn_hermes->callback((Fl_Callback*)cb_btn_hermes);
	btn_hermes->value(progStatus.btn_hermes);

#if TAB_XMITOS
	// Xmit-Rcv offset tab.
	tabsGeneric->remove(*genericTab2);
	genericTab2 = new Fl_Group(0, 271, 425, 49, "XmitOS");
	addto_group(tab2Ptrs);
	char p[40];
	sprintf(p, "%3.9e", progStatus.ppm_hos);
	inpNHydraPwf->value(p);
#endif // TAB_XMITOS
#if TAB_NOTCH
	// 
	tabsGeneric->remove(*genericTab3);
	genericTab3 = new Fl_Group(0, 271, 425, 49, "Notch");
	addto_group(tab3Ptrs);
#endif // TAB_NOTCH

	// Set custom colors for the bands tab.
	btnBandSelect_1->color((Fl_Color)BAND_SELECT_COLOR);
	btnBandSelect_1->label("160m");
	btnBandSelect_1->tooltip("1.8-2.0MHz");
	btnBandSelect_2->color((Fl_Color)BAND_SELECT_COLOR);
	btnBandSelect_2->label("80m");
	btnBandSelect_2->tooltip("3.5-4.0MHz");
	btnBandSelect_3->color((Fl_Color)BAND_SELECT_COLOR);
	btnBandSelect_3->label("40m");
	btnBandSelect_3->tooltip("7.0-7.3MHz");
	btnBandSelect_4->color((Fl_Color)BAND_SELECT_COLOR);
	btnBandSelect_4->label("30m");
	btnBandSelect_4->tooltip("10.1-10.15MHz");
	btnBandSelect_5->color((Fl_Color)BAND_SELECT_COLOR);
	btnBandSelect_5->label("20m");
	btnBandSelect_5->tooltip("14.0-14.350MHz");
	btnBandSelect_6->color((Fl_Color)BAND_SELECT_COLOR);
	btnBandSelect_6->label("17m");
	btnBandSelect_6->tooltip("18.068-18.168MHz");
	btnBandSelect_7->color((Fl_Color)BAND_SELECT_COLOR);
	btnBandSelect_7->label("15m");
	btnBandSelect_7->tooltip("21.0-21.45MHz");
	btnBandSelect_8->color((Fl_Color)BAND_SELECT_COLOR);
	btnBandSelect_8->label("12m");
	btnBandSelect_8->tooltip("24.89-24.99MHz");
	btnBandSelect_9->color((Fl_Color)BAND_SELECT_COLOR);
	btnBandSelect_9->label("10m");
	btnBandSelect_9->tooltip("28.0-29.7MHz");
	btnBandSelect_10->color((Fl_Color)BAND_SELECT_COLOR);
	btnBandSelect_10->label("6m");
	btnBandSelect_10->tooltip("50.0-54.0MHz");
	btnBandSelect_11->color((Fl_Color)BAND_SELECT_COLOR);
	opSelect60->color((Fl_Color)BAND_SELECT_COLOR);
}

//
// Called from within support/dialogs.cxx
// Called from within support/status.cxx:
void RIG_HPSDR::setColors(Fl_Color &btn_lt_color, Fl_Color &btn_slider, Fl_Color &bg_slider)
{
	for (int x = 0; hpsdr_widgetPtrs[x].W != NULL; ++x) {
		if (hpsdr_widgetPtrs[x].C) {
			switch(hpsdr_widgetPtrs[x].T) {
			case btnPType:
				(*((Fl_Button**)hpsdr_widgetPtrs[x].W))->selection_color(btn_lt_color);
				break;
			case btnLType:
				(*((Fl_Light_Button**)hpsdr_widgetPtrs[x].W))->selection_color(btn_lt_color);
				break;
			case sldrType:
				(*((Fl_Wheel_Value_Slider**)hpsdr_widgetPtrs[x].W))->color(bg_slider);
				(*((Fl_Wheel_Value_Slider**)hpsdr_widgetPtrs[x].W))->selection_color(btn_slider);
				break;
			}
		}
	}
}

//
// Called from within support/status.cxx:
void RIG_HPSDR::hide()
{
	for (int x = 0; hpsdr_widgetPtrs[x].W != NULL; ++x) {
#if 0
	  if (*hpsdr_widgetPtrs[x].W == btnPreamp)
	  	continue;
#endif
		(*hpsdr_widgetPtrs[x].W)->hide();
	}
}

//
// Called from within support/status.cxx:
bool RIG_HPSDR::adjust_small_uiA(int* y)
{
	if (progStatus.schema == 1) {	// "Small sliders" button checked in "Config->UI",
		return (false);		//   so we don't display our custom widgets.
	}

	int i = 0;
	while (widgetPtrs[i].W != NULL) {
#if 0
		fprintf(stderr, "w: %p *%p, x: %d, y: %d, w: %d, h: %d\n",
			widgetPtrs[i].W, *widgetPtrs[i].W,
			widgetPtrs[i].x, widgetPtrs[i].y,
			widgetPtrs[i].w, widgetPtrs[i].h);
#endif // 0
#if 0
		switch(hpsdr_widgetPtrs[i].T) {
		case btnPType: fprintf(stderr, "found a plain button\n"); break;
		case btnLType: fprintf(stderr, "found a light button\n"); break;
		case sldrType: fprintf(stderr, "found a slider\n"); break;
		case inpNType: fprintf(stderr, "found an numeric input box\n"); break;
		case inpAType: fprintf(stderr, "found an alpha input box\n"); break;
		case inpSType: fprintf(stderr, "found an secret input box\n"); break;
		case cmboType: fprintf(stderr, "found a comboBox\n"); break;
		default: fprintf(stderr, "found I know not what...\n"); break;
		}
#endif // 0
		(*widgetPtrs[i].W)->resize(widgetPtrs[i].x, widgetPtrs[i].y,
					   widgetPtrs[i].w, widgetPtrs[i].h);
		(*widgetPtrs[i].W)->show();
		(*widgetPtrs[i].W)->redraw();
		if (widgetPtrs[i].y > *y)
			*y = widgetPtrs[i].y;
		i++;
	}
	if (has_data_port) {
		sldrMICGAIN->label("");
		sldrMICGAIN->redraw_label();
	}

	return (true);
}

//
// Called from within support/status.cxx:
bool RIG_HPSDR::adjust_small_uiB(int* y)
{
	btn_show_controls->position( btn_show_controls->x(), *y );

	// Override the default behaviour.
	return (true);
}

#include "icons.h"
void RIG_HPSDR::initialize()
{
  //fprintf(stderr, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> initialize()\n");

	// If we have never created our custom widgets we need to restart.
	if (have_not_initialized == true) {
		fl_choice2("Confirm Re-start", NULL, "Yes", NULL);

		name_ = HPSDRname_;	// get new name into lastXcvrUsed string
		shutdown();		// save HPSDR.prefs
		execv(__argv[0], __argv);
	}

	// We derive the receiver in use from the tcp port# of the dsp server.
	sdr_receiver = atoi(progStatus.tcpip_port.c_str()) - DSP_PORT_BASE;

	char msg[80];
#if 1
	sprintf(msg, "Engage 'Master' button before proceeding");
	master_set_clear = statusWindow(msg, 2);
#else
	sprintf(msg, "XML-RPC %s:%s", progStatus.server_addr.c_str(), progStatus.server_port.c_str());
	statusWindow(msg);
#endif

	// We include the receiver # in the title.
	char name[strlen(HPSDRname_) + 3];
	snprintf(name, strlen(HPSDRname_) + 3, "%s-%d", HPSDRname_, sdr_receiver);
	mainwindow->label(name);
	freqA = progStatus.freq_A;
	freqB = progStatus.freq_B;
	sdr_frequency = freqA;
#if defined(HYDRA_FREQUENCY_OFFSET)
	LOG_QUIET("hydra frequency offset:: ppm: %1.20lf, hz: %d",
		 progStatus.ppm_hos, progStatus.hz_hos);
	// Dummy set to align to the hydra xmt/rcv offset.
	set_vfoA(get_vfoA());
#endif // HYDRA_FREQUENCY_OFFSET

	Open();

	// No support in the wide UI for the extra buttons/sliders.
	btn_wide_ui->deactivate();
	sldrMICGAIN->deactivate();
	sldrPOWER->deactivate();
#if 0
	// Hide until we tackle transmit
	btnPTT->hide();
#endif
}

void RIG_HPSDR::post_initialize()
{
  //fprintf(stderr, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>post initialize()\n");
#if 0
	// an example of how we could use 1 callback for multiple buttons:
	btnLDither->argument((long)(&progStatus.random));
#endif
	// XXX set_split(split);
	set_split(true); // normal ops
	btnSplit->value(true);
#if 0
	// XXX what should these do?
	enable_bandselect_btn(12, false);
	enable_bandselect_btn(13, true);
#endif

	// Now we can do commands again.
	enable_commands = true;
}

int RIG_HPSDR::Open(int freq)
{
	Close();					// just in case?

	try {
		// open a socket to the dsp server
		device_fd = open_sdr();

		// Preliminary open for discovery.
		if (freq == 0)
			return (0);

		// Allow commands during our startup.
		enable_commands = true;
		char cmdbuf[SEND_BUFFER_SIZE];
		sprintf(cmdbuf, "*hardware?");
		Write_cmd(cmdbuf);
		sprintf(cmdbuf, "q-version");
		Write_cmd(cmdbuf);

		sprintf(cmdbuf, "q-loffset");
		Write_cmd(cmdbuf);
		sprintf(cmdbuf, "q-protocol3");
		Write_cmd(cmdbuf);
		sprintf(cmdbuf, "q-master");
		Write_cmd(cmdbuf);

		Write_cmd("q-info", true);	// update info

#if DO_INITIALIZATION
		// Set filter params with data from progStatus.
		set_filter();

		// Set filter params with data from progStatus.
		set_rx_encoding(progStatus.rx_encoding);
		cmboRxEncoding->index(progStatus.rx_encoding);
		// XXX this seems to be necessary, why?
		set_spectrum(progStatus.spectrum);		// allows btn to reflect change
		btnLSpectrum->value(progStatus.spectrum);

		// XXX we can't stop it, and must start it,
		// XXX  so progStatus.audio is kinda' irrelevant...
		set_audio(progStatus.audio);
		btnPAudio->value(progStatus.audio);

		fprintf(stderr, "* HPSDR ************************************************************\n");
		fprintf(stderr, "Frequency: %8d, Audio Gain: %3d\n", sdr_frequency, progStatus.volume);
		fprintf(stderr, "********************************************************************\n");
#endif // DO_INITIALIZATION

		// Prevent initialization commands from base controller.
		enable_commands = false;
	}
	catch (...) {
		fprintf(stderr, "Open caught a COLD!\n");
		throw;
	}

	return 0;
}

void RIG_HPSDR::Close()
{
	close_sdr();

	device_fd = -1;
}

  /** from FL copy_label()  documentation:
      Sets the current label. 
      Unlike label(), this method allocates a copy of the label 
      string instead of using the original string pointer.
      The internal copy will automatically be freed whenever you assign
      a new label or when the widget is destroyed.
  */
int RIG_HPSDR::statusWindow(const char* msg, int level)
{
	fprintf(stderr, "\n*** %s ***\n\n", msg);
#if STATUS_BAR
	StatusBar->copy_label(msg);
	switch (level) {
		default:
		case 0:
		StatusBar->color(FL_BACKGROUND2_COLOR);
		break;
	case 1:
		StatusBar->color(FL_YELLOW);
		break;
	case 2:
		StatusBar->color(FL_RED);
		break;
	}
#endif // STATUS_BAR
#if 1
	return (++msg_cnt);
#endif
}

int RIG_HPSDR::open_sdr()
{
	try {
		if (tcpip == 0) {
		  statusWindow("Warning: tcp NOT connected", 2);
			return (0);
		}
		return (tcpip->fd());
	}
	catch (...) {
		fprintf(stderr, ">>> DSP server @ ip address %s, port %s for receiver #%d is not available.\n",
			  progStatus.tcpip_addr.c_str(), progStatus.tcpip_port.c_str(), sdr_receiver);
		fprintf(stderr, ">>>   Hardware must be powered & connected.\n");
		fprintf(stderr, ">>>   Alex hpsdr server and dspserver must be running.\n");
		fprintf(stderr, ">>>   If ip addr: '%s' is incorrect,\n"
				"\t\tedit [config-dir]/HPSDR.prefs:\n",
			progStatus.tcpip_addr.c_str());
#if 0
		// XXX this is all we can do at this point...
		exit(-1);
#else
		// XXX need to fix the routines that use the socket to act gracefully first...
		fprintf(stderr, ">>> attempting to continue without DSP server...\n");
		statusWindow("Warning: cannot connect to dspserver");
		return (-1);
#endif
	}
}

void RIG_HPSDR::close_sdr()
{
}

int RIG_HPSDR::Parse_cmd(char *s)
{
  fprintf(stderr, "Pc: %s\n", s);
	char delim = ';';
	char *str1, *token, *saveptr1;
	int j;

	for (j = 1, str1 = s; ; j++, str1 = NULL) {
	  token = strtok_r(str1, (char *)&delim, &saveptr1);
	  if (token == NULL)
	  	break;
	  fprintf(stderr, ">>>>>>>>>>>>>>>>%d: %s\n", j, token);
	  /// XXX update other fields
	  if (j == 4)
	  	sdr_frequency = atoi(token);
	}

	return (0);
}

int RIG_HPSDR::Write_cmd(const char *buf, bool answer)
{
	// We don't want to do base controller init of hardware.
	if (!enable_commands)
		return (0);

	// Not very secure attempt to hide passwd.
	char hidden[80];
	if (strncmp(buf, "setmaster", 9) == 0) {
		int s = 2;
		int x;
		for (x = 0; buf[x]; ++x) {
			hidden[x] = (s > 0) ? buf[x] : '*';
			if (buf[x] == ' ') --s;
		}
		hidden[x] = '\0';
		fprintf(stderr, "wc: %s\n", hidden);
	}
	else
		fprintf(stderr, "wc: %s\n", buf);

	if (!tcpip) return (0);

	uint8_t buff[MAGIC_SIZE];
	int retval = tcpip->send(buf, SEND_BUFFER_SIZE);
	///fprintf(stderr, "wc: %s (%s)\n", buf, answer ? "true" : "false");

	// answer:
	// 01234567891123456789212345678931234567894
	// q-info:s;1;f;14080000;m;7;z;0;l;0;r;7000;
	if (answer == true) {
#if 0
		int ret = tcpip->recv(buff, MAGIC_SIZE);
		fprintf(stderr, "answer length: %d\n", ret);
#else
		memset(buff, 0, 20);
		tcpip->recv(buff, MAGIC_SIZE);
#endif
		if (buff[0] == '4') {
			int end = ((buff[1] - '0') * 10) + (buff[2] - '0') + 3;
			uint8_t b = buff[end];
			buff[end] = '\0';
			fprintf(stderr, "answer: %s\n", &buff[3]);
			if (strncmp((const char *)buf, "q-info", 6) == 0)
				Parse_cmd((char *)&buff[3+7]);
			buff[end] = b;
		}
		else {
		  for (int x = 0; x < 10; ++x)
		  	fprintf(stderr, " 0x%02d", buff[x]);
		  fprintf(stderr, "\n");
		}
	}
#if 0
	/// XXX XXX XXX crap is continually coming at us, what to do? XXX XXX XXX
	else {
		int ret = tcpip->recv(buff, MAGIC_SIZE);
		fprintf(stderr, "more: %d\n", ret);
	}
#endif

	return retval;
}


////////////////////////////////////////////////////////////////////////////////////
// Support for base controls.
//

// 
long RIG_HPSDR::get_vfoA()
{
#if 0
	Write_cmd("q-info", true);	// update info
#else
	/// XXX freqA = sdr_frequency;
	return (freqA);
#endif
}

void RIG_HPSDR::set_vfoA(long freq)
{
	char cmdbuf[SEND_BUFFER_SIZE];
	freqA = freq;

#if defined(HYDRA_FREQUENCY_OFFSET)
	freq += ((double)freq * progStatus.ppm_hos);
	freq += progStatus.hz_hos;
	fprintf(stderr, ">>>> setting offset frequency of %ld from %ld\n", freq, freqA);
#endif // HYDRA_FREQUENCY_OFFSET

	sprintf(cmdbuf, "setFrequency %ld", freq);
	Write_cmd(cmdbuf, false);
}

long RIG_HPSDR::get_vfoB()
{
#if 0
	Write_cmd("q-info", true);	// update info
#else
	/// XXX freqB = sdr_frequency;
	return (freqB);
#endif
}

void RIG_HPSDR::set_vfoB(long freq)
{
	freqB = freq;
	///fprintf(stderr, "set_vfoB(%ld)\n", freqB);
#if 0
	// as we don't have a traditional vfoA/vfoB situation,
	// we provide this function to allow flrig to adjust
	// the display, which can be used for a quick frequency
	// change via the A/B button.  But we don't actually
	// set the frequency of the hardware...
	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "setFrequency %d", (int)freqB);
Sset_f	Write_cmd(cmdbuf, false);
#endif
}

int RIG_HPSDR::get_modetype(int n)
{
	return (HPSDR_mode_type[n]);
}

// Volume control
void RIG_HPSDR::set_volume_control(int val) 
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "setRXOutputGain %d", val);
	Write_cmd(cmdbuf, false);

	progStatus.volume = val;
}

// 
void RIG_HPSDR::set_modeA(int val)
{
	modeA = val;

	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "setmode %d", modenbr[val]);
	Write_cmd(cmdbuf, false);
}

// 
void RIG_HPSDR::set_modeB(int val)
{
	// XXX do we even pretend to have a vfoB???
	modeB = val;
}

// XXX static int split = 0;
// I think flrig manages this by brute force...
void RIG_HPSDR::set_split(bool val)
{
	  /// XXX	split = val;
#if 0
	if (val)
		fprintf(stderr, "set split ON");
	else
		fprintf(stderr, "set split OFF");
#endif
}

// 
void RIG_HPSDR::set_PTT_control(int ptt)
{
	if (ptt == true)
		Write_cmd("mox on");
	else
		Write_cmd("mox off");
}


////////////////////////////////////////////////////////////////////////////////////
// Additional controls created by HPSDR.
//

#if STATUS_BAR
// 
void RIG_HPSDR::cb_status_clear(Fl_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 316);
		StatusBar->copy_label("");
		StatusBar->color(FL_BACKGROUND2_COLOR);
}
#endif // STATUS_BAR

//
// Sliders.
//

// Transceiver attenuation control.
// Uses base rig attenuator control button.
// Uses HPSDR attenuation value slider.
void RIG_HPSDR::set_attenuator(int val)
{
	progStatus.attenuator = val;	// update storage
	btnAttenuator->value(val);	// update button

	int newVal;
	if (val == 0) {
		newVal = 0;				// zero while button is OFF
		sldrAttenuator->deactivate();
	}
	else {
		newVal = progStatus.attenuation;	// current slider value
		sldrAttenuator->activate();
	}

	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "*setattenuator %d", newVal);
	Write_cmd(cmdbuf, false);
}

void RIG_HPSDR::cb_sldrAtten(Fl_Wheel_Value_Slider* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 301);
		mySelf->set_atten_sldr(sldrAttenuator->value());
}

void RIG_HPSDR::set_atten_sldr(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "*setattenuator %d", val);
	Write_cmd(cmdbuf, false);

	progStatus.attenuation = val;
}

// Sub Rx output gain.
void RIG_HPSDR::cb_sldrSubRxGain(Fl_Wheel_Value_Slider* w, void* p) {
	guard_lock gl_serial(&mutex_serial, 397);
		mySelf->set_subrxgain(w->value());
}

void RIG_HPSDR::set_subrxgain(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "setsubrxoutputgain %d", val);
	Write_cmd(cmdbuf, false);

	progStatus.subrx_gain = val;
}

#if SLDR_TEST
// test slider
void RIG_HPSDR::cb_sldrTest(Fl_Wheel_Value_Slider* w, void* p) {
	guard_lock gl_serial(&mutex_serial, 397);
		mySelf->set_testslider(w->value());
}

void RIG_HPSDR::set_testslider(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "testslider %d", val);
	Write_cmd(cmdbuf, false);

	progStatus.testslider = val;
}
#endif // SLDR_TEST

//
// Combo controls.
//

// 
void RIG_HPSDR::cb_master(Fl_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 316);
		mySelf->set_master(btnPMaster->value());
}

void RIG_HPSDR::set_master(int val)
{
#define DEFAULT_MASTER_NAME	"flrig"
#define DEFAULT_MASTER_PWORD	"TXALL"

	char cmdbuf[SEND_BUFFER_SIZE];
	if (_name.empty()) {				// assume that passwd is TXALL,
		_name = DEFAULT_MASTER_NAME;
		inpAMasterName->value(DEFAULT_MASTER_NAME);
	}
	if (_passwd.empty()) {				// give dspserver something it likes.
		_passwd = DEFAULT_MASTER_PWORD;
		inpSMasterPasswd->value(DEFAULT_MASTER_PWORD);
	}

	sprintf(cmdbuf, "setmaster %s %s", _name.c_str(), _passwd.c_str());

	Write_cmd(cmdbuf);
#if 1
	master_is_set = true;
	if (msg_cnt == master_set_clear) {
 #if 1
		StatusBar->copy_label("");
		StatusBar->color(FL_BACKGROUND2_COLOR);
 #else
		// XXX why not?
		cb_status_clear(NULL, NULL);
 #endif
		master_set_clear = 0;
	}
#endif
}

// 
void RIG_HPSDR::cb_master_name(Fl_Input2* o, void*)
{
	guard_lock gl_serial(&mutex_serial, 317);	// XXX is this lock necessary?
		mySelf->set_master_name(o->value());
}

void RIG_HPSDR::set_master_name(string name)
{
	_name = name;
}

// 
void RIG_HPSDR::cb_master_passwd(Fl_Input2* o, void*)
{
	guard_lock gl_serial(&mutex_serial, 318);	// XXX is this lock necessary?
		mySelf->set_master_passwd(o->value());
}

void RIG_HPSDR::set_master_passwd(string passwd)
{
	_passwd = passwd;
}

#if XMIT_SUPPORT
// 
void RIG_HPSDR::cb_alexatt(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 324);
		mySelf->set_alexatt(c->index());
}

void RIG_HPSDR::set_alexatt(int att)
{
	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "*alexrxatt %d", att);
	Write_cmd(cmdbuf, false);

	//progStatus.alexatt = att;
}

// 
void RIG_HPSDR::cb_txant(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 324);
		mySelf->set_txant(c->index());
}

void RIG_HPSDR::set_txant(int ant)
{
	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "*alextxrelay %d", ant);
	Write_cmd(cmdbuf, false);

	//progStatus.txant = ant;
}

// 
void RIG_HPSDR::cb_rxant(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 324);
		mySelf->set_rxant(c->index());
}

// XXX WARNING: alex ozy.c:ozy_set_alex_rx_antenna() doesn't set bypass relay!
void RIG_HPSDR::set_rxant(int ant)
{
  fprintf(stderr, "alex ozy.c:ozy_set_alex_rx_antenna() doesn't set bypass relay!\n");
	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "*alexrxantenna %d", ant);
	Write_cmd(cmdbuf, false);

	//progStatus.txant = ant;
}


void RIG_HPSDR::cb_hpfilt(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 324);
		mySelf->set_hpfilt(c->index());
}

void RIG_HPSDR::set_hpfilt(int f)
{
	switch (f) {
	case 0:	 f = 5;  break;
	case 1:	 f = 4;  break;
	case 2:	 f = 3;  break;
	case 3:	 f = 2;  break;
	case 4:	 f = 0;  break;
	case 5:	 f = 1;  break;
	case 6:	 f = 6;  break;
	case 7:
	default:
		return;
	}

	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "*alexhpfilt %d", f);
	Write_cmd(cmdbuf, false);

	//progStatus.hpfilt = f;
}

void RIG_HPSDR::cb_lpfilt(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 324);
		mySelf->set_lpfilt(c->index());
}

void RIG_HPSDR::set_lpfilt(int f)
{
	switch (f) {
	case 0:	 f = 3;  break;
	case 1:	 f = 2;  break;
	case 2:	 f = 1;  break;
	case 3:	 f = 0;  break;
	case 4:	 f = 6;  break;
	case 5:	 f = 5;  break;
	case 6:	 f = 4;  break;
	case 7:
	default:
		return;
	}
	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "*alexlpfilt %d", f);
	Write_cmd(cmdbuf, false);

	//progStatus.lpfilt = f;
}
#endif // XMIT_SUPPORT

// 
void RIG_HPSDR::cb_filter(Fl_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 316);
		mySelf->set_filter();
}

void RIG_HPSDR::set_filter()
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "setfilter %s %s",
		progStatus.filter_low.c_str(),
		progStatus.filter_high.c_str());
	Write_cmd(cmdbuf);
}

// 
void RIG_HPSDR::cb_filter_low(Fl_Int_Input* o, void*)
{
	guard_lock gl_serial(&mutex_serial, 317);	// XXX is this lock necessary?
		mySelf->set_filter_low(o->value());
}

// 
void RIG_HPSDR::cb_filter_high(Fl_Int_Input* o, void*)
{
	guard_lock gl_serial(&mutex_serial, 318);	// XXX is this lock necessary?
		mySelf->set_filter_high(o->value());
}

// 
void RIG_HPSDR::cb_spectrum(Fl_Light_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 316);
		mySelf->set_spectrum(btnLSpectrum->value());
}

void RIG_HPSDR::set_spectrum(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	if (val) {
		sprintf(cmdbuf, "setfps %s %s",
			progStatus.spectrum_samples.c_str(),
			progStatus.spectrum_fps.c_str());
	}
	else {
		sprintf(cmdbuf, "setfps 0 0");
	}
	Write_cmd(cmdbuf);

	progStatus.spectrum = val;
}

// 
void RIG_HPSDR::cb_spectrum_fps(Fl_Int_Input* o, void*)
{
	guard_lock gl_serial(&mutex_serial, 317);	// XXX is this lock necessary?
		mySelf->set_spectrum_fps(o->value());
}

// 
void RIG_HPSDR::cb_spectrum_samples(Fl_Int_Input* o, void*)
{
	guard_lock gl_serial(&mutex_serial, 318);	// XXX is this lock necessary?
		mySelf->set_spectrum_samples(o->value());
}

// 
void RIG_HPSDR::cb_audio(Fl_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 319);
		mySelf->set_audio(btnPAudio->value());
}

// 
// XXX "stopaudiostream" is currently not enabled in dspserver.
// XXX As such this is really a one-shot, not a start-stop.
// So we handle it as an Fl_Button, NOT an Fl_light_Button.
void RIG_HPSDR::set_audio(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "startaudiostream %s %s %d %d", // "startAudioStream 2048 8000 1 0"
		progStatus.audio_buf_size.c_str(), progStatus.audio_rate ? RATE_48000 : RATE_8000,
		progStatus.audio_channels + 1, progStatus.audio_mic_encode);
	Write_cmd(cmdbuf);

#if 0
	// XXX If we ever enable "stopaudiostream" in dspserver we would:
	progStatus.audio = val;
#endif
}

// 
void RIG_HPSDR::cb_audio_buf(Fl_Int_Input *buf, void* p)
{
	guard_lock gl_serial(&mutex_serial, 320);
		mySelf->set_audio_buf(buf->value());
}

// 
void RIG_HPSDR::cb_audio_rate(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 321);
		mySelf->set_audio_rate(c->index());
}

// 
void RIG_HPSDR::cb_audio_chnls(Fl_ComboBox* c, void*)
{
	guard_lock gl_serial(&mutex_serial, 322);
		mySelf->set_audio_chnls(c->index());
}

//
// Hydra support.
//

// hydra hz offset slider
void RIG_HPSDR::cb_sldrHzhos(Fl_Wheel_Value_Slider* w, void* p) {
	guard_lock gl_serial(&mutex_serial, 397);
		mySelf->set_hzhosslider(w->value());
}

void RIG_HPSDR::set_hzhosslider(int val)
{
	progStatus.hz_hos = val;

	// Reset with new offset.
	set_vfoA(freqA);
}

void RIG_HPSDR::cb_hydraOS(Fl_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 319);
		mySelf->set_hydraos(btnPHydraOS->value());
}

void RIG_HPSDR::set_hydraos(int val)
{
#if 1
	progStatus.ppm_hos =
		(get_hdial_freq() / (get_hdial_freq() + (get_hxmit_os() - get_hrcv_os()))) - 1.0;
#else
	fprintf(stderr, "==========================\ncalculate and set ppm_hos:\n");
	int diff = get_hxmit_os() - get_hrcv_os();
	double dial = get_hdial_freq();
	double offset = dial + diff;
	double ppm = (dial / offset) - 1.0;
	fprintf(stderr, "dial: %lf, offset: %lf, diff: %d, ppm_hos: %1.20lf\n", dial, offset, diff, ppm);
	progStatus.ppm_hos = ppm;
#endif // 1

	clear_hxmit_os();
	clear_hrcv_os();
#if TAB_XMITOS
	char p[40];
	sprintf(p, "%3.9e", progStatus.ppm_hos);
	inpNHydraPwf->value(p);
#endif // TAB_XMITOS

#if 0
	int freq = dial + (dial * ppm);
	fprintf(stderr, ">>>> setting imaginary frequency of %d from %lf\n", freq, dial);
	double dial40 = 7070000.0;
	int freq2 = dial40 + (dial40 * ppm);
	fprintf(stderr, ">>>> setting imaginary frequency of %d from %lf\n", freq2, dial40);
#endif // 0

	fprintf(stderr, "new hydra ppm offset: %1.9lf : %3.9e\n",
		progStatus.ppm_hos, progStatus.ppm_hos);
}

// 
void RIG_HPSDR::cb_hydra_dwf(Fl_Int_Input *buf, void* p)
{
	guard_lock gl_serial(&mutex_serial, 320);
		mySelf->set_hdial_freq(buf->value());
}

// 
void RIG_HPSDR::cb_hydra_xwf(Fl_Int_Input *buf, void* p)
{
	guard_lock gl_serial(&mutex_serial, 320);
		mySelf->set_hxmit_os(buf->value());
}

void RIG_HPSDR::clear_hxmit_os()
{
	hxmit_os = "";
	inpNHydraXwf->value("");
}

// 
void RIG_HPSDR::cb_hydra_rwf(Fl_Int_Input *buf, void* p)
{
	guard_lock gl_serial(&mutex_serial, 320);
		mySelf->set_hrcv_os(buf->value());
}

void RIG_HPSDR::clear_hrcv_os()
{
	hrcv_os = "";
	inpNHydraRwf->value("");
}

// 
void RIG_HPSDR::cb_hydra_pwf(Fl_Int_Input *buf, void* p)
{
#if 0
	guard_lock gl_serial(&mutex_serial, 320);
		mySelf->set_ppm_os(strtod(buf->value(), NULL));
#endif
}

// 
void RIG_HPSDR::cb_audio_mic_enc(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 323);
		mySelf->set_audio_mic_enc(c->index());
}

// 
void RIG_HPSDR::cb_rx_encoding(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 324);
		mySelf->set_rx_encoding(c->index());
}

void RIG_HPSDR::set_rx_encoding(int rx_encoding)
{
	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "setencoding %d", rx_encoding);
	Write_cmd(cmdbuf, false);

	progStatus.rx_encoding = rx_encoding;
}

// 
void RIG_HPSDR::cb_rx_agc(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 324);
		mySelf->set_rx_agc(c->index());
}

void RIG_HPSDR::set_rx_agc(int rx_agc)
{
	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "setagc %d", rx_agc);
	Write_cmd(cmdbuf, false);

	progStatus.rx_agc = rx_agc;
}

// 
void RIG_HPSDR::cb_fixed_agc(Fl_Int_Input *buf, void* p)
{
	guard_lock gl_serial(&mutex_serial, 324);
		mySelf->set_fixed_agc(buf->value());
}

void RIG_HPSDR::set_fixed_agc(const char *fixed_agc)
{
	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "setfixedagc %s", fixed_agc);
	Write_cmd(cmdbuf, false);

	progStatus.fixed_agc = fixed_agc;
}

// ghpsdr3-alex/trunk/src/DttSP/sdrexport.h
// 
void RIG_HPSDR::cb_notch(Fl_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 319);
		mySelf->set_notchfilter(btnPNotch->value());
}

void RIG_HPSDR::set_notchfilter(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];
	sprintf(cmdbuf, "setnotchfilter %d %d %s %s",	// vfo[0-1], index[0-8], (float)bandwidth, (float)offset
		notchVfo, notchIndex, notchBandwidth.c_str(), notchOffset.c_str());
	Write_cmd(cmdbuf);

	// XXX set progStatus.XXX
}

// 
void RIG_HPSDR::cb_notch_vfo(Fl_ComboBox* c, void*)
{
	guard_lock gl_serial(&mutex_serial, 322);
		notchVfo = c->index();
}

// 
void RIG_HPSDR::cb_notch_index(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 323);
		notchIndex = c->index();
}

// 
void RIG_HPSDR::cb_notch_bandwidth(Fl_Int_Input *buf, void* p)
{
	guard_lock gl_serial(&mutex_serial, 320);
		notchBandwidth = buf->value();
}

// 
void RIG_HPSDR::cb_notch_offset(Fl_Int_Input *buf, void* p)
{
	guard_lock gl_serial(&mutex_serial, 320);
		notchOffset = buf->value();
}

// 
void RIG_HPSDR::cb_notchenable(Fl_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 319);
		mySelf->set_notchenable(btnPNotchEnable->value());
}

void RIG_HPSDR::set_notchenable(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	if (notchEnableIndex == 9)
		notchEnableIndex = -1;
	sprintf(cmdbuf, "enablenotchfilter %d %d %d",	// vfo[0-1], index[-1(all) or 0-8], 1-0(enable/disable)
		notchEnableVfo, notchEnableIndex, notchEnableValue);
	Write_cmd(cmdbuf);

	// XXX set progStatus.XXX
}

// 
void RIG_HPSDR::cb_notchenable_vfo(Fl_ComboBox* c, void*)
{
	guard_lock gl_serial(&mutex_serial, 322);
		notchEnableVfo = c->index();
}

// 
void RIG_HPSDR::cb_notchenable_index(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 323);
		notchEnableIndex = c->index();
}

// 
void RIG_HPSDR::cb_notchenable_value(Fl_ComboBox* c, void* p)
{
	guard_lock gl_serial(&mutex_serial, 323);
		notchEnableValue = c->index();
}


//
// Buttons.
//

// 
void RIG_HPSDR::set_preamp(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "*preamp %s", val ? "on" : "off");
	Write_cmd(cmdbuf, false);
}

// Noise blanking: true/false.
void RIG_HPSDR::set_noise(bool on)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "setnb %s", on ? "true" : "false");
	Write_cmd(cmdbuf, false);
}

// Noise reduction: true/false.
void RIG_HPSDR::set_noise_reduction(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "setnr %s", val ? "true" : "false");
	Write_cmd(cmdbuf, false);
}

// 
void RIG_HPSDR::cb_SetAnf(Fl_Light_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 309);
		mySelf->set_anf(btnLSetAnf->value());
}

void RIG_HPSDR::set_anf(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "setanf %s", val ? "true" : "false");
	Write_cmd(cmdbuf, false);

	progStatus.anf = val;
}

#if BTN_TEST
// 
void RIG_HPSDR::cb_Testbutton(Fl_Light_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 315);
		mySelf->set_testbutton(btnLTestbutton->value());
}

void RIG_HPSDR::set_testbutton(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "testbutton %s", val ? "true" : "false");
	Write_cmd(cmdbuf, false);

	progStatus.testbutton = val;
}
#endif // BTN_TEST

// Dither.
void RIG_HPSDR::cb_Dither(Fl_Light_Button* w, void* p) {
	guard_lock gl_serial(&mutex_serial, 304);
		mySelf->set_dither(btnLDither->value());
}

void RIG_HPSDR::set_dither(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "*dither %s", val ? "on" : "off");
	Write_cmd(cmdbuf, false);

	progStatus.dither = val;
}

// Random.
void RIG_HPSDR::cb_Random(Fl_Light_Button* w, void* p) {
	guard_lock gl_serial(&mutex_serial, 305);
		mySelf->set_random(btnLRandom->value());
}

void RIG_HPSDR::set_random(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "*random %s", val ? "on" : "off");
	Write_cmd(cmdbuf, false);

	progStatus.random = val;
}

// Rx DC block.
void RIG_HPSDR::cb_Rxdcb(Fl_Light_Button* w, void* p) {
 	guard_lock gl_serial(&mutex_serial, 306);
		mySelf->set_rxdcb(btnLRxDCB->value());
}

void RIG_HPSDR::set_rxdcb(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "SetRXDCBlock %d", val ? 1 : 0);
	Write_cmd(cmdbuf, false);

	progStatus.rxdcb = val;
}

// Tx DC block.
void RIG_HPSDR::cb_Txdcb(Fl_Light_Button* w, void* p) {
	guard_lock gl_serial(&mutex_serial, 307);
		  mySelf->set_txdcb(btnLTxDCB->value());
}

void RIG_HPSDR::set_txdcb(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "SetTXDCBlock %d", val ? 1 : 0);
	Write_cmd(cmdbuf, false);

	progStatus.txdcb = val;
}

// Pwsmode
void RIG_HPSDR::cb_Pwsmode(Fl_Light_Button* w, void* p) {
	guard_lock gl_serial(&mutex_serial, 308);
		mySelf->set_pwsmode(btnLPwsmode->value());
}

void RIG_HPSDR::set_pwsmode(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "SetPWSMode %d", val ? 1 : 0);
	Write_cmd(cmdbuf, false);

	progStatus.pwsmode = val;
}

// 
void RIG_HPSDR::cb_SetIQenable(Fl_Light_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 310);
		mySelf->set_iqenable(btnLSetIQenable->value());
}

void RIG_HPSDR::set_iqenable(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "setiqenable %s", val ? "true" : "false");
	Write_cmd(cmdbuf, false);

	progStatus.iqenable = val;
}

// 
void RIG_HPSDR::cb_Sdrom(Fl_Light_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 311);
		mySelf->set_sdrom(btnLSdrom->value());
}

void RIG_HPSDR::set_sdrom(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "setsdrom %s", val ? "true" : "false");
	Write_cmd(cmdbuf, false);

	progStatus.sdrom = val;
}

// 
void RIG_HPSDR::cb_SpectrumPPhase(Fl_Light_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 312);
		mySelf->set_spectrumpolyphase(btnLSpectrumPPhase->value());
}

void RIG_HPSDR::set_spectrumpolyphase(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "setspectrumpolyphase %s", val ? "true" : "false");
	Write_cmd(cmdbuf, false);

	progStatus.spectrumpolyphase = val;
}

// 
void RIG_HPSDR::cb_RxSquelch(Fl_Light_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 313);
		mySelf->set_squelchstate(btnLRxSquelch->value());
}

void RIG_HPSDR::set_squelchstate(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "setsquelchstate %s", val ? "on" : "off");
	Write_cmd(cmdbuf, false);

	progStatus.squelchstate = val;
}

// 
void RIG_HPSDR::cb_SubRxSquelch(Fl_Light_Button* w, void* p)
{
	guard_lock gl_serial(&mutex_serial, 314);
		mySelf->set_subrxquelchstate(btnLSubRxSquelch->value());
}

void RIG_HPSDR::set_subrxquelchstate(int val)
{
	char cmdbuf[SEND_BUFFER_SIZE];

	sprintf(cmdbuf, "setsubrxquelchstate %s", val ? "on" : "off");
	Write_cmd(cmdbuf, false);

	progStatus.subrxquelchstate = val;
}


//
// Tabs.
//

#define BS_MIN_160	 1800000
#define BS_MAX_160	 2000000
#define BS_MIN_80	 3500000
#define BS_MAX_80	 4000000
#define BS_MIN_40	 7000000
#define BS_MAX_40	 7300000
#define BS_MIN_30	10100000
#define BS_MAX_30	10150000
#define BS_MIN_20	14000000
#define BS_MAX_20	14350000
#define BS_MIN_17	18068000
#define BS_MAX_17	18168000
#define BS_MIN_15	21000000
#define BS_MAX_15	21450000
#define BS_MIN_12	24890000
#define BS_MAX_12	24990000
#define BS_MIN_10	28000000
#define BS_MAX_10	29700000
#define BS_MIN_6	50000000
#define BS_MAX_6	54000000

// 
static int current_band = 0;
void RIG_HPSDR::set_band_selection(int v)
{
	// Record current values.
	//fprintf(stderr, "set_band_selection(%d)\n", v);
	int cf = get_vfoA();

	switch(current_band) {
	case 1:
		if ((cf >= BS_MIN_160) && (cf <= BS_MAX_160)) {
			progStatus.m160_freq = freqA;
			progStatus.m160_mode = modeA;
		}
		break;
	case 2:
		if ((cf >= BS_MIN_80) && (cf <= BS_MAX_80)) {
			progStatus.m80_freq = freqA;
			progStatus.m80_mode = modeA;
		}
		break;
	case 3:
		if ((cf >= BS_MIN_40) && (cf <= BS_MAX_40)) {
			progStatus.m40_freq = freqA;
			progStatus.m40_mode = modeA;
		}
		break;
	case 4:
		if ((cf >= BS_MIN_30) && (cf <= BS_MAX_30)) {
			progStatus.m30_freq = freqA;
			progStatus.m30_mode = modeA;
		}
		break;
	case 5:
		if ((cf >= BS_MIN_20) && (cf <= BS_MAX_20)) {
			progStatus.m20_freq = freqA;
			progStatus.m20_mode = modeA;
		}
		break;
	case 6:
		if ((cf >= BS_MIN_17) && (cf <= BS_MAX_17)) {
			progStatus.m17_freq = freqA;
			progStatus.m17_mode = modeA;
		}
		break;
	case 7:
		if ((cf >= BS_MIN_15) && (cf <= BS_MAX_15)) {
			progStatus.m15_freq = freqA;
			progStatus.m15_mode = modeA;
		}
		break;
	case 8:
		if ((cf >= BS_MIN_12) && (cf <= BS_MAX_12)) {
			progStatus.m12_freq = freqA;
			progStatus.m12_mode = modeA;
		}
		break;
	case 9:
		if ((cf >= BS_MIN_10) && (cf <= BS_MAX_10)) {
			progStatus.m10_freq = freqA;
			progStatus.m10_mode = modeA;
		}
		break;
	case 10:
		if ((cf >= BS_MIN_6) && (cf <= BS_MAX_6)) {
			progStatus.m6_freq = freqA;
			progStatus.m6_mode = modeA;
		}
		break;
	case 11:
		progStatus.mgen_freq = freqA;
		progStatus.mgen_mode = modeA;
		break;
	case 0:
		// Band_selection not initialized, nothing to do.
		break;
	default:
		fprintf(stderr, "saving nothing\n");
		break;
	}

	// Use recorded values.
	current_band = v;
	switch(v) {
	case 1:
		set_vfoA(progStatus.m160_freq);
		modeA = progStatus.m160_mode;
		break;
	case 2:
		set_vfoA(progStatus.m80_freq);
		modeA = progStatus.m80_mode;
		break;
	case 3:
		set_vfoA(progStatus.m40_freq);
		modeA = progStatus.m40_mode;
		break;
	case 4:
		set_vfoA(progStatus.m30_freq);
		modeA = progStatus.m30_mode;
		break;
	case 5:
		set_vfoA(progStatus.m20_freq);
		modeA = progStatus.m20_mode;
		break;
	case 6:
		set_vfoA(progStatus.m17_freq);
		modeA = progStatus.m17_mode;
		break;
	case 7:
		set_vfoA(progStatus.m15_freq);
		modeA = progStatus.m15_mode;
		break;
	case 8:
		set_vfoA(progStatus.m12_freq);
		modeA = progStatus.m12_mode;
		break;
	case 9:
		set_vfoA(progStatus.m10_freq);
		modeA = progStatus.m10_mode;
		break;
	case 10:
		set_vfoA(progStatus.m6_freq);
		modeA = progStatus.m6_mode;
		break;
	case 11:
		set_vfoA(progStatus.mgen_freq);
		modeA = progStatus.mgen_mode;
		break;
	case 0:	// FALLTHROUGH
	default:
		break;
	}
}

void RIG_HPSDR::cb_spnr_hermes(Hspinner *w, void *p)
{
	guard_lock gl_serial(&mutex_serial, 302);
	///mySelf->set_spnr_hermes(spnr_hermes->value());
	progStatus.spnr_hermes = spnr_hermes->value();
}

void RIG_HPSDR::cb_btn_hermes(Fl_Check_Button *w, void *p)
{
	guard_lock gl_serial(&mutex_serial, 303);
	///mySelf->set_btn_hermes(btn_hermes->value());
	progStatus.btn_hermes = btn_hermes->value();
}

// NEW_WIDGET cb/set/get  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


void RIG_HPSDR::shutdown()
{
	// Update any required progStatus fields.

	// Save to disk.
	progStatus.saveLastState();
}

// XXX Pass x (ie. the index) instead of Wptr,
// XXX  allowing widget creation to use final w:h geometry.
// XXX This may be better fix for the segfault described below for the cmboCfg object.
#define OFF_SCREEN	500
inline void iAlign(Fl_Widget* w, int align)
{      
	switch(align) {
	case ALIGN_T:
		w->align(Fl_Align(FL_ALIGN_TOP));
		break;
	case ALIGN_B:
		w->align(Fl_Align(FL_ALIGN_BOTTOM));
		break;
	case ALIGN_R:
		w->align(Fl_Align(FL_ALIGN_RIGHT));
		break;
	}
}

// 
void btnPCfg::initialize(GUI2* wP)
{
#if 0
	fprintf(stderr, ">>>>>>>>>>>>>>>>>>> btnP initialize: %p *%p, \"%s\" \"%s\" %p\n",
    		wP->W, *wP->W, _label.c_str(), _tooltip.c_str(), _callback);
#endif
	if (*wP->W == NULL) 
		*wP->W = new Fl_Button(wP->x, wP->y, wP->w, wP->h);
	Fl_Button* w = *(Fl_Button**)wP->W;

	if (!_label.empty()) {
		w->label(_label.c_str());
		w->labelsize(12);
	}
	if (!_tooltip.empty())
		w->tooltip(_tooltip.c_str());
	if (_callback)
		w->callback((Fl_Callback*)_callback);
	if (_valuePtr)
		w->value(*(bool*)_valuePtr);

	w->show();
	w->redraw();
}

// 
void btnLCfg::initialize(GUI2* wP)
{
#if 0
	fprintf(stderr, ">>>>>>>>>>>>>>>>>>> btnL initialize: %p *%p, \"%s\" \"%s\" %p\n",
    		wP->W, *wP->W, _label.c_str(), _tooltip.c_str(), _callback);
#endif
	if (*wP->W == NULL)
		*wP->W = new Fl_Light_Button(wP->x, wP->y, wP->w, wP->h);
	Fl_Light_Button* w = *(Fl_Light_Button**)wP->W;

	if (!_label.empty()) {
		w->label(_label.c_str());
		w->labelsize(12);
	}
	if (!_tooltip.empty())
		w->tooltip(_tooltip.c_str());
	if (_callback)
		w->callback((Fl_Callback*)_callback);
	if (_valuePtr)
		w->value(*(bool*)_valuePtr);

	w->down_box(FL_THIN_DOWN_BOX);

	w->show();
	w->redraw();
}

// 
void sldrCfg::initialize(GUI2* wP)
{
#if 0
	fprintf(stderr, ">>>>>>>>>>>>>>>>>>> sldr initialize: %p *%p, \"%s\" \"%s\" %p\n",
    		wP->W, *wP->W, _label.c_str(), _tooltip.c_str(), _callback);
#endif
	if (*wP->W == NULL)
		*wP->W = new Fl_Wheel_Value_Slider(wP->x, wP->y, wP->w, wP->h);
	Fl_Wheel_Value_Slider* w = *(Fl_Wheel_Value_Slider**)wP->W;

	if (!_label.empty()) {
		w->label(_label.c_str());
		w->labeltype(FL_NORMAL_LABEL);
		w->labelfont(0);
		if (strchr(_label.c_str(), '\n'))
			w->labelsize(10);
		else
			w->labelsize(14);
		w->labelcolor(FL_FOREGROUND_COLOR);
		w->align(Fl_Align(FL_ALIGN_LEFT));
	}
	if (!_tooltip.empty())
		w->tooltip(_tooltip.c_str());
	if (_callback)
		w->callback((Fl_Callback*)_callback);
	if (_valuePtr) {
		w->value(*(int*)_valuePtr);
	}
	if (_step > 0) {
		w->minimum(_min);
		w->maximum(_max);
		w->step(_step);
	}

	w->box(FL_THIN_DOWN_BOX);
	w->textsize(12);
	w->reverse(true);
	w->type(5);
	w->when(FL_WHEN_CHANGED);

	w->show();
	w->redraw();
}

// 
void inpNCfg::initialize(GUI2* wP)
{
#if 0
	fprintf(stderr, ">>>>>>>>>>>>>>>>>>> inpN initialize: %p *%p, \"%s\" \"%s\" %p\n",
    		wP->W, *wP->W, _label.c_str(), _tooltip.c_str(), _callback);
#endif
	if (*wP->W == NULL)
		*wP->W = new Fl_Int_Input(wP->x, wP->y, wP->w, wP->h);
	Fl_Int_Input* w = *(Fl_Int_Input**)wP->W;

	if (!_label.empty()) {
		w->label(_label.c_str());
		iAlign((Fl_Widget*)w, _align);
		w->labeltype(FL_NORMAL_LABEL);
		w->labelfont(0);
		w->labelsize(12);
		w->labelcolor(FL_FOREGROUND_COLOR);
	}
	if (!_tooltip.empty())
		w->tooltip(_tooltip.c_str());
	if (_callback)
		w->callback((Fl_Callback*)_callback);
	if (_valuePtr)
		w->value((*(string*)_valuePtr).c_str());

	w->type(2);
	w->when(FL_WHEN_RELEASE);

	w->show();
	w->redraw();
}

// 
void inpACfg::initialize(GUI2* wP)
{
#if 0
	fprintf(stderr, ">>>>>>>>>>>>>>>>>>> inpA initialize: %p *%p, \"%s\" \"%s\" %p\n",
    		wP->W, *wP->W, _label.c_str(), _tooltip.c_str(), _callback);
#endif
	if (*wP->W == NULL)
		*wP->W = new Fl_Input2(wP->x, wP->y, wP->w, wP->h);
	Fl_Input2* w = *(Fl_Input2**)wP->W;

	if (!_label.empty()) {
		w->label(_label.c_str());
		iAlign((Fl_Widget*)w, _align);
		w->labeltype(FL_NORMAL_LABEL);
		w->labelfont(0);
		w->labelsize(12);
		w->labelcolor(FL_FOREGROUND_COLOR);
	}
	if (!_tooltip.empty())
		w->tooltip(_tooltip.c_str());
	if (_callback)
		w->callback((Fl_Callback*)_callback);
	if (_valuePtr)
		w->value((*(string*)_valuePtr).c_str());

	w->when(FL_WHEN_CHANGED);

	w->show();
	w->redraw();
}

// 
void inpSCfg::initialize(GUI2* wP)
{
#if 0
	fprintf(stderr, ">>>>>>>>>>>>>>>>>>> inpS initialize: %p *%p, \"%s\" \"%s\" %p\n",
    		wP->W, *wP->W, _label.c_str(), _tooltip.c_str(), _callback);
#endif
	if (*wP->W == NULL)
		*wP->W = new Fl_Secret_Input(wP->x, wP->y, wP->w, wP->h);
	Fl_Secret_Input* w = *(Fl_Secret_Input**)wP->W;

	if (!_label.empty()) {
		w->label(_label.c_str());
		iAlign((Fl_Widget*)w, _align);
		w->labeltype(FL_NORMAL_LABEL);
		w->labelfont(0);
		w->labelsize(12);
		w->labelcolor(FL_FOREGROUND_COLOR);
	}
	if (!_tooltip.empty())
		w->tooltip(_tooltip.c_str());
	if (_callback)
		w->callback((Fl_Callback*)_callback);
	if (_valuePtr)
		w->value((*(string*)_valuePtr).c_str());

	w->when(FL_WHEN_CHANGED);

	w->show();
	w->redraw();
}

// 
void cmboCfg::initialize(GUI2* wP)
{
#if 0
	fprintf(stderr, ">>>>>>>>>>>>>>>>>>> cmbo initialize: %p *%p, \"%s\" \"%s\" %p\n",
    		wP->W, *wP->W, _label.c_str(), _tooltip.c_str(), _callback);
#endif
	if (*wP->W == NULL)
		*wP->W = new Fl_ComboBox(wP->x, wP->y, wP->w, wP->h);
	Fl_ComboBox* w = *(Fl_ComboBox**)wP->W;

	if (!_label.empty()) {
		w->label(_label.c_str());
		iAlign((Fl_Widget*)w, _align);
		w->labeltype(FL_NORMAL_LABEL);
		w->labelfont(0);
		w->labelsize(12);
		w->labelcolor(FL_FOREGROUND_COLOR);
	}
	if (!_tooltip.empty())
		w->tooltip(_tooltip.c_str());
	if (_callback)
		w->callback((Fl_Callback*)_callback);

	w->clear();
	if (_choices)
		for (int x = 0; _choices[x]; ++x)
			w->add(_choices[x]);
	if (_valuePtr)
		w->index(*(int*)_valuePtr);
	else
		w->index(0);

	// I think this is necessary as the drop-down damages areas outside its geometry.
	w->box(FL_NO_BOX);

	w->color(FL_BACKGROUND2_COLOR);
	w->selection_color(FL_BACKGROUND_COLOR);
	w->when(FL_WHEN_CHANGED);

	w->end();

	w->show();
	w->redraw();
}


//////////////////////////////////////////////////////////////////////////
// tmp storage:
#if 0

#endif // 0
