//---------------------------------------------------------------------------
/*
Remote Train Control Program for Windows

© Copyright 2015 by Mark DiVecchio

    This file is part of Remote Train Control.

    Remote Train Control 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.

    Remote Train Control 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 Remote Train Control.  If not, see <http://www.gnu.org/licenses/>.

*/
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Train_Control.h"
#include "Conv_Mode.h"
#include "Setup_Control.h"
#include "Debug_Controls.h"
#include "DCS_Setup_Controls.h"
#include "Comm_Thread.h"
#include "Clipbrd.hpp"

#include <stdio.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "_GClass"
#pragma link "AbCBitBt"
#pragma link "HideButton"
#pragma link "AbVSlide"
#pragma link "AbNumEdit"
#pragma link "AbSwitch"
#pragma link "AbLED"
#pragma link "ColorButton"
#pragma resource "*.dfm"

TConvMode *ConvMode;	// NOTE: this is public static variable, you can never use
				// this when you create multiple instances of a Form
//---------------------------------------------------------------------------
extern TDCS_Setup * ptrDCS[TIUMAX];			// DCS Setup Pointers, 5 TIU - indexed by TIUNo-1

extern TCommThread * ptrTIUCommThread;				// pointer to TIU communications thread
//---------------------------------------------------------------------------
__fastcall TConvMode::TConvMode(TComponent* Owner)
	: TForm(Owner)
{
assert(false);	// never called since I used the overloaded constructor below
}
//---------------------------------------------------------------------------
__fastcall TConvMode::TConvMode(TComponent* Owner, int Remote, int Engine, int TIU, int Channel)
    : TForm(Owner)
{
// grab the Remote, Engine and TIU numbers
RemoteNo = Remote;
assert(RemoteNo >= 0 && RemoteNo <= REMOTEMAX);
EngineNo =  Engine;
TIUNo = TIU;
ChannelNo = Channel;	// Channel is 1-2
assert(ChannelNo >= 1 && ChannelNo <= CONVMAX);

LocalTransparencyOverride = false;
//
Conventional_Controls_Label->Caption = "Track Controls for TIU " + String(TIUNo) + " - Var " +
	String(ChannelNo);
Caption = Conventional_Controls_Label->Caption;
JumptoMinRequest->Checked = false;	// clear request min voltage on next train movement
//
// Required for multiple monitors and save/restore position/size
Position=poDesigned;
DefaultMonitor=dmDesktop;
//
// read Preferences from registry if they exist
TRegIniFile * const RegFile = new TRegIniFile(MainForm->RegFileRootKey);
assert (RegFile != NULL);
__try {
    // read window position
    Top = RegFile->ReadInteger("Conventional", "Top" + String(TIUNo) + String(ChannelNo), Top);
    Left = RegFile->ReadInteger("Conventional", "Left" + String(TIUNo) + String(ChannelNo), Left);
    // if using multiple monitors and one was removed, pull the window
    // back onto the remaining monitor
    int MC = Screen->MonitorCount;
    if (MC == 1) {
        if (Left < 0 ) Left = 0;
        int Wth = Screen->Monitors[0]->Width;
		if (Left > Wth) Left -= Wth;
        if (Top < 0 ) Top = 0;
        int Hth = Screen->Monitors[0]->Height;
        if (Top > Hth) Top -= Hth;
        }
	// Conventional mode setup loaded on a per TIU and per Channel basis
	MaxVoltageSpin->Value = RegFile->ReadInteger("Conventional", "MaxVoltage" + String(TIUNo) + String(ChannelNo), MaxVoltageSpin->Value * 10.0) / 10.0;
	StartVoltageSpin->Value = RegFile->ReadInteger("Conventional", "StartVoltage" + String(TIUNo) + String(ChannelNo), StartVoltageSpin->Value * 10.0) / 10.0;
	InputVoltageSpin->Value = RegFile->ReadInteger("Conventional", "InputVoltage" + String(TIUNo) + String(ChannelNo), InputVoltageSpin->Value * 10.0) / 10.0;
	// status of Fixed/Var Switch  : VarXModeSwitch
    //MainForm->SetSwitch(VarXModeSwitch, RegFile->ReadInteger("TIU", "Var" + String(ChannelNo) + "ModeSwitch" + String(TIUNo), VarXModeSwitch->Checked & 0x01));
    // Set variable mode -- ALWAYS starts in variable mode
    MainForm->SetSwitch(VarXModeSwitch, false) ;
    // status of Signal On/Off Switch
    MainForm->SetSwitch(VarXSignal, RegFile->ReadInteger("TIU", "Var" + String(ChannelNo) + "Signal" + String(TIUNo), VarXSignal->Checked & 0x01));
    }
__finally {
    // Deallocate RegFile
	delete RegFile;
    }

if (Setup->DisableEmergencyStopButton->Checked) {
	ESTOP->Visible = false;
    ESTOP->Enabled = false;
    ESTOP->Hint = "Disabled in Setup";
    ESTOP->Cursor = crNo;
    }

// Set Maximum Voltage
VoltageSlider->SignalSettings->DigitalTo = MaxVoltageSpin->Value * 10;
VoltageSlider->ChangeSmall = 5;
VoltageSlider->ChangeLarge = 10;
VoltageSlider->SignalSettings->ValueTo = MaxVoltageSpin->Value;
VoltageSlider->ScaleSettings->Steps = MaxVoltageSpin->Value / 2;
//
// bug in the TAbVSlider causes it to start out at 10
// this sets the slider to zero without sending "tv" command
void __fastcall (__closure *VoltageSliderOnValueChange)(TObject *Sender);
VoltageSliderOnValueChange = VoltageSlider->OnValueChange;
VoltageSlider->OnValueChange = NULL;
VoltageSlider->Value = 0;
VoltageSlider->OnValueChange = VoltageSliderOnValueChange;
//

JumptoMinRequest->Checked = true;	// request min voltage on next train movement
// but make it visible only in debug mode
if (DEBUGGING) {
	JumptoMinRequest->Visible = true;
	}
}
//---------------------------------------------------------------------------

__fastcall TConvMode::~TConvMode(void)
{
if (MainForm->bSaveRegistryInfo) {
    // write preferences to the Registry
    TRegIniFile * const RegFile = new TRegIniFile(MainForm->RegFileRootKey);
    assert (RegFile != NULL);
    __try {
        // save window position
        RegFile->WriteInteger("Conventional", "Top" + String(TIUNo) + String(ChannelNo), Top > 0 ? Top : 0);
        RegFile->WriteInteger("Conventional", "Left" + String(TIUNo) + String(ChannelNo), Left);
        // Conventional mode setup loaded on a per TIU and per Channel basis
        RegFile->WriteInteger("Conventional", "MaxVoltage" + String(TIUNo) + String(ChannelNo), MaxVoltageSpin->Value * 10.0);
        RegFile->WriteInteger("Conventional", "StartVoltage" + String(TIUNo) + String(ChannelNo), StartVoltageSpin->Value * 10.0);
        RegFile->WriteInteger("Conventional", "InputVoltage" + String(TIUNo) + String(ChannelNo), InputVoltageSpin->Value * 10.0);
        // status of Fixed/Var switch : VarXModeSwitch
        RegFile->WriteInteger("TIU", "Var" + String(ChannelNo) + "ModeSwitch" + String(TIUNo), VarXModeSwitch->Checked);
        // status of Signal On/Off
        RegFile->WriteInteger("TIU", "Var" + String(ChannelNo) + "Signal" + String(TIUNo), VarXSignal->Checked);
        }
    __finally {
        // Deallocate RegFile
        delete RegFile;
        }
    }
assert(ConvMode == NULL); 	// this form must never be auto created
}
//---------------------------------------------------------------------------
/*
FDL	Direction Lock
FSC	Speed Control on /off
FFR	Factory reset
TZV	Set Track Voltage to zero volts
MPG	PS1 Program mode
MTV	Track Voltage Menu
MIN	Set track voltage to minimum volts
PS1	PS1 Engine Start Up
*/
//---------------------------------------------------------------------------

void __fastcall TConvMode::HideButtonClick(TObject *Sender)
{
Hide();	      // Same as setting Visible to false	
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::ESTOPClick(TObject *Sender)
{
// emergency stop
#ifdef BUTTONMETHOD
MainForm->EmergencyStopAllEngines();	// Stop Engines and take down windows
#else
not used
// Send CM_EMERGENCYSTOP message to MainForm
(void) PostMessage(MainForm->Handle, CM_EMERGENCYSTOP, 0, 0);
#endif	
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::InputVoltageSpinChange(TObject *Sender)
{
/*
if (InputVoltageSpin->Value < MaxVoltageSpin->Value)
	MaxVoltageSpin->Value = InputVoltageSpin->Value;
*/
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::MaxVoltageSpinValueChanged(TObject *Sender)
{
VoltageSlider->SignalSettings->DigitalTo = MaxVoltageSpin->Value * 10;
VoltageSlider->SignalSettings->ValueTo = MaxVoltageSpin->Value;
VoltageSlider->ScaleSettings->Steps = MaxVoltageSpin->Value / 2;

if ((VoltageSlider->Value >= 0.1) && (VoltageSlider->Value > MaxVoltageSpin->Value)) {
	// lower the voltage
	SetVoltageSliderValue(MaxVoltageSpin->Value);	// caues voltage to be lowered to new Max and
    												// command to the TIU
    }

// Adjust the maximum on the Start Voltage Spinner
StartVoltageSpin->MaxValue = MaxVoltageSpin->Value;
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::PFAButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// Start PFA
char str[15];
(void)snprintf(str, sizeof str, "tp%d", ChannelNo-1);
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::WhistleButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// Start whistle
#ifdef BUTTONMETHOD
char str[15];
(void)snprintf(str, sizeof str, "tw%d1", ChannelNo-1);
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
#else
not used
// Send CM_WHISTLE message to ourselves which will perform this function
(void) PostMessage(Handle, CM_WHISTLE, true, NULL);	// true = button down
#endif
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::WhistleButtonMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// End Whistle
#ifdef BUTTONMETHOD
char str[15];
(void)snprintf(str, sizeof str, "tw%d0", ChannelNo-1);
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL);
#else
not used
// Send CM_WHISTLE message to ourselves which will perform this function
(void) PostMessage(Handle, CM_WHISTLE, false, NULL);	// false = button up
#endif}
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::CMWhistle(TMessage &Message)
{
//
if (DEBUGGING) DB->Debug_Print("CMWhistle");
const bool Dn = Message.WParam;		// set button state from Word Parameter
if (Dn) {
	// start whistle
	char str[15];
	(void)snprintf(str, sizeof str, "tw%d1", ChannelNo-1);
	if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
    }
if (!Dn) {
	// stop whistle
	char str[15];
	(void)snprintf(str, sizeof str, "tw%d0", ChannelNo-1);
	if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
	}
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::VarXModeSwitchStatusChanged(TObject *Sender)
{
// FXD Mode Switch
char str[15];
if (VarXModeSwitch->Checked) {
	// Fixed Mode on
	SetVoltageSliderValue(0.0);	// causes the 'tv' command to be sent
    if (ptrDCS[TIUNo - 1]) {	// reflect the change on to any open DCS (Signal Setup) Mode window
    	if (ChannelNo == 1) {
	        ptrDCS[TIUNo - 1]->Var1ModeSwitch->Checked = true;
            } else {
	        ptrDCS[TIUNo - 1]->Var2ModeSwitch->Checked = true;
            }
    	} else {
	        (void)snprintf(str, sizeof str, "Ob%d0", ChannelNo - 1);
		    (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, VERY_LONG_WAIT, RECORD_OK, NULL, NULL, NULL);
        }
    // Disable Controls
    VoltageSlider->Enabled = false;
    BrakeButton->Enabled = false;
    BoostButton->Enabled = false;
    DirButton->Enabled = false;
    ZeroButton->Enabled = false;
    WhistleButton->Enabled = false;
    BellButton->Enabled = false;
    CPLRFButton->Enabled = false;
    CPLRRButton->Enabled = false;
    PFAButton->Enabled = false;
    MinVoltageButton->Enabled = false;
    SpeedControlButton->Enabled = false;
    DirLockButton->Enabled = false;
    FactoryResetButton->Enabled = false;
    PS1_Start_Button->Enabled = false;
    } else {
    // Variable Mode on
    if (ptrDCS[TIUNo - 1]) {	// reflect the change on to any open DCS (Signal Setup) Mode window
    	if (ChannelNo == 1) {
	        ptrDCS[TIUNo - 1]->Var1ModeSwitch->Checked = false;
            } else {
	        ptrDCS[TIUNo - 1]->Var2ModeSwitch->Checked = false;
            }
    	} else {
	        (void)snprintf(str, sizeof str, "Ob%d1", ChannelNo - 1);
		    (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, VERY_LONG_WAIT, RECORD_OK, NULL, NULL, NULL);
        }
        // Enable Controls
	SetVoltageSliderValue(0.0);	// causes the 'tv' command to be sent
    VoltageSlider->Enabled = true;
    BrakeButton->Enabled = true;
    BoostButton->Enabled = true;
    DirButton->Enabled = true;
    ZeroButton->Enabled = true;
    WhistleButton->Enabled = true;
    BellButton->Enabled = true;
    CPLRFButton->Enabled = true;
    CPLRRButton->Enabled = true;
    PFAButton->Enabled = false;
    MinVoltageButton->Enabled = true;
    SpeedControlButton->Enabled = true;
    DirLockButton->Enabled = true;
    FactoryResetButton->Enabled = true;
    PS1_Start_Button->Enabled = true;
    }
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::VarXSignalStatusChanged(TObject *Sender)
{
// Signal Switch
char str[15];
if (VarXSignal->Checked) {
	// Signal on
    if (ptrDCS[TIUNo - 1]) {	// reflect the change on to any open DCS (Signal Setup) Mode window
    	if (ChannelNo == 1) {
	        ptrDCS[TIUNo - 1]->Var1Signal->Checked = true;
            } else {
	        ptrDCS[TIUNo - 1]->Var2Signal->Checked = true;
            }
    	} else {
            (void)snprintf(str, sizeof str, "Od%d1", ChannelNo == 1 ? 0 : 3);
		    (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, VERY_LONG_WAIT, RECORD_OK, NULL, NULL, NULL);
        }
    } else {
    // Signal off
    if (ptrDCS[TIUNo - 1]) {	// reflect the change on to any open DCS (Signal Setup) Mode window
    	if (ChannelNo == 1) {
	        ptrDCS[TIUNo - 1]->Var1Signal->Checked = false;
            } else {
	        ptrDCS[TIUNo - 1]->Var2Signal->Checked = false;
            }
    	} else {
	        (void)snprintf(str, sizeof str, "Od%d0", ChannelNo == 1 ? 0 : 3);
		    (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, VERY_LONG_WAIT, RECORD_OK, NULL, NULL, NULL);
        }
    }
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::PS1_Start_ButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// PS1 Startup
const TCursor OldCursor = Screen->Cursor; 	// save cursor
Screen->Cursor = crHourGlass;
//Remote sets to 22v
VoltageSlider->Value = 22.0;
MainForm->Delay(2000);
// Remote sets to 8v
VoltageSlider->Value = 8.0;
Screen->Cursor = OldCursor;
VoltageSlider->SetFocus();	// focus to speed slider
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::PS1_Start_ButtonMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
//VoltageSlider->SetFocus();	// focus to speed slider
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::FactoryResetButtonClick(TObject *Sender)
{
// Factory Reset   with confirmation
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }
// Factory Reset
String FactoryResetMsg = "Are you sure you want to Factory Reset all of the engines on the track?";
const int retv = Application->MessageBox(FactoryResetMsg.c_str(),	TEXT("Warning"), MB_YESNO | MB_ICONQUESTION);
if (retv == IDYES) {
	char str[15];
	(void)snprintf(str, sizeof str, "ta%d", ChannelNo-1);
	if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, VERY_LONG_WAIT * 3, RECORD_OK, NULL, NULL, NULL);
	}
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::DirButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// DIR button pressed
#ifdef BUTTONMETHOD
char str[15];
(void)snprintf(str, sizeof str, "td%d0", ChannelNo-1);
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
#else
not used
// Send CM_DIR message to ourselves which will perform this function
(void) PostMessage(Handle, CM_DIR, true, NULL);	// true = button down
#endif
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::DirButtonMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// DIR button released
#ifdef BUTTONMETHOD
char str[15];
(void)snprintf(str, sizeof str, "td%d1", ChannelNo-1);
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
#else
not used
// Send CM_DIR message to ourselves which will perform this function
(void) PostMessage(Handle, CM_DIR, false, NULL);	// false = button up
#endif
VoltageSlider->SetFocus();	// focus to speed slider
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::CMDir(TMessage &Message)
{
//
char str[15];
if (DEBUGGING) DB->Debug_Print("CMDir");
bool Dn = Message.WParam;		// set button state from Word Parameter
if (Dn) {
	// DIR button pressed
	(void)snprintf(str, sizeof str, "td%d0", ChannelNo-1);
    }
if (!Dn) {
	// DIR button released
	(void)snprintf(str, sizeof str, "td%d1", ChannelNo-1);
	}
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::DirLockButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// Toggle FDL - Dir lock
char str[15];
(void)snprintf(str, sizeof str, "tl%d", ChannelNo-1);
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, VERY_LONG_WAIT * 3, RECORD_OK, NULL, NULL, NULL);
VoltageSlider->SetFocus();	// focus to speed slider
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::SpeedControlButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// Toggle FSC - Speed Control
char str[15];
(void)snprintf(str, sizeof str, "tm%d", ChannelNo-1);
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, VERY_LONG_WAIT * 3, RECORD_OK, NULL, NULL, NULL);
VoltageSlider->SetFocus();	// focus to speed slider
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::StartVoltageSpinChange(TObject *Sender)
{
/*
// Change Start Up Voltage
if (VoltageSlider->Value < StartVoltageSpin->Value) {
	// lower the voltage unless it's already zero
    if (VoltageSlider->Value > 0.5) {
        SetVoltageSliderValue(StartVoltageSpin->Value);	// causes command to be sent to the TIU
        }
    }
*/
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::MinVoltageButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// Set to Minimum Voltage
SetVoltageSliderValue(StartVoltageSpin->Value);	// causes min voltage command to be sent to TIU
VoltageSlider->SetFocus();	// focus to speed slider
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::BellButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// One press turns on the Bell, second press turns off the Bell
#ifdef BUTTONMETHOD
// Indicate Button Down
char str[15];
(void)snprintf(str, sizeof str, "tb%d1", ChannelNo-1);
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
#else
not used
// Send CM_BELL message to ourselves which will perform this function
(void) PostMessage(Handle, CM_BELL, true, NULL);	// true = button down
#endif
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::BellButtonMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
#ifdef BUTTONMETHOD
// Indicate Button Up
char str[15];
(void)snprintf(str, sizeof str, "tb%d0", ChannelNo-1);
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
#else
not used
// Send CM_BELL message to ourselves which will perform this function
(void) PostMessage(Handle, CM_BELL, false, NULL);	// false = button up
#endif
VoltageSlider->SetFocus();	// focus to speed slider
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::CMBell(TMessage &Message)
{
//
char str[15];
if (DEBUGGING) DB->Debug_Print("CMBell");
bool Dn = Message.WParam;		// set button state from Word Parameter
if (Dn) {
	// Indicate Bell button down
	(void)snprintf(str, sizeof str, "tb%d1", ChannelNo-1);
    }
if (!Dn) {
	// Indicate Bell button up
	(void)snprintf(str, sizeof str, "tb%d0", ChannelNo-1);
	}
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
}

//---------------------------------------------------------------------------

void __fastcall TConvMode::SetVoltageSliderValue(float Voltage)
{
VoltageSlider->Value = Voltage;
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::CPLRFButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// CPLR-F
// Send CM_CPRLF message to ourselves which will perform this function
(void) PostMessage(Handle, CM_CPLRF, NULL, NULL);
VoltageSlider->SetFocus();	// focus to speed slider
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::CMCPLRF(TMessage &Message)
{
// Uncouple Front
char str[15];
(void)snprintf(str, sizeof str, "tf%d", ChannelNo-1);
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::CPLRRButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// CPLR-R
// Send CM_CPRLR message to ourselves which will perform this function
(void) PostMessage(Handle, CM_CPLRR, NULL, NULL);
VoltageSlider->SetFocus();	// focus to speed slider
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::CMCPLRR(TMessage &Message)
{
// Uncouple Rear
char str[15];
(void)snprintf(str, sizeof str, "tr%d", ChannelNo-1);
if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL, NULL, NULL);
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::FormKeyDown(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
// Make sure KeyPreview is true for this form
assert(KeyPreview);
// Pass function keys to the MainForm, handle rest of the keys here.
switch (Key) {
    case VK_F1:
    case VK_F2:
    case VK_F3:
    case VK_F4:
    case VK_F5:
    case VK_F6:
    case VK_F7:
    case VK_F8:
    case VK_F9:
    case VK_F10:
    case VK_F11:
    case VK_F12:
		MainForm->FormKeyDown(Sender, Key, Shift);
        Key = 0;	// handled
		break;
	}	
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::VoltageSliderValueChange(TObject *Sender)
{
// Voltage on Variable Channel 1
// Send speed command based on value of slider
// 0v goes up to start voltage
// start voltage goes down to 0v
//

static float LastSentVoltageValue;
//
if (DEBUGGING) DB->Debug_Print("VoltageSliderValueChange : " + String(VoltageSlider->Value));
// https://stackoverflow.com/questions/31649315/c-builder-button-click-function-reentry
LOCKEM()

LastSentVoltageValue = VoltageSlider->Value;	// save the current setting voltage

__try {
	// if the voltage is just coming off of 0, raise it to the Start Voltage
	if (JumptoMinRequest->Checked && (VoltageSlider->Value < StartVoltageSpin->Value)) {
	    VoltageSlider->Value = StartVoltageSpin->Value;
        LastSentVoltageValue = StartVoltageSpin->Value;
		}

	float fVoltage = VoltageSlider->Value;	// Indicated voltage
    fVoltage = fVoltage * (22.0 / InputVoltageSpin->Value);	// Normalized Voltage
    const int iVoltage = (fVoltage * 10) + 0.5; 	// voltage * 10 then rounded to nearest .1
	// Convert value into chars
	char str[10];
	(void) snprintf(str, sizeof str, "tv%d%03d", ChannelNo - 1, iVoltage);
	if (!VarXModeSwitch->Checked) (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, LONG_WAIT, RECORD_OK, NULL, NULL, NULL);

	if (fVoltage > 0) {
	    JumptoMinRequest->Checked = false; 	// no Min voltage on next train movement
	    }

	// if the speed is set to zero, request sounds on next move off zero
	if (fVoltage <= 0.01) {
		JumptoMinRequest->Checked = true;	// request Min Voltage on next train movement
	    }
    }
__finally {
    }

#ifdef NOTNEEDED
// Check for slider movement while setting voltage
if (LastSentVoltageValue != VoltageSlider->Value) {
    if (DEBUGGING) DB->Debug_Print("VoltageSliderValueChange Speed Changed on Exit : " + String(VoltageSlider->Value) +
    	" from " + String(LastSentVoltageValue));
    VoltageSliderValueChange(Sender);	// call me again to process the changed speed value
	}
#endif
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::ZeroButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// Stop the Engine by setting voltage to zero
// speed 0;
SetVoltageSliderValue(0.0);	// causes the 'tv' command to be sent
VoltageSlider->SetFocus();	// focus to speed slider
JumptoMinRequest->Checked = true;	// request Min voltage on next train movement	
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::BoostButtonStatusChanged(TObject *Sender)
{
// Boost
// Increase Voltage by 0.5 volt

if (BoostButton->Checked) {
	if (VoltageSlider->Value == VoltageSlider->SignalSettings->ValueTo) return;	// no effect when max
    VoltageSlider->Value += 0.5;
	}
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::BrakeButtonStatusChanged(TObject *Sender)
{
// Brake
// Decrease Voltage by 0.5 volt

if (BrakeButton->Checked) {
	if (VoltageSlider->Value == 0) return;	// no effect when min
    VoltageSlider->Value -= 0.5;
	}
}
//---------------------------------------------------------------------------
// Workaround for SetLayeredWindowAttributes() to make transparent windows
extern lpfnSetLayeredWindowAttributes m_pSetLayeredWindowAttributes;
//---------------------------------------------------------------------------

void __fastcall TConvMode::FormActivate(TObject *Sender)
{
#if __BORLANDC__ >= 0x720      // CB10.1 only
AlphaBlend = false; // Remove Transparency
#else
// Remove WS_EX_LAYERED from this window styles
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) & ~WS_EX_LAYERED);
// Ask the window and its children to repaint
//Repaint();
#endif
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::FormDeactivate(TObject *Sender)
{
if (Setup->TransparentSpin->Value == Setup->TransparentSpin->MaxValue) return;
if (LocalTransparencyOverride) return;
#if __BORLANDC__ >= 0x720      // CB10.1 only
AlphaBlendValue = (255 * Setup->TransparentSpin->Value) / 100;
AlphaBlend = true;  // Add Transparency
#else
// Set WS_EX_LAYERED on this window
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) | WS_EX_LAYERED);
//SetLayeredWindowAttributes
m_pSetLayeredWindowAttributes(Handle, NULL, (255 * Setup->TransparentSpin->Value) / 100, LWA_ALPHA);
#endif
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::Conventional_Controls_LabelClick(
      TObject *Sender)
{
LocalTransparencyOverride = !LocalTransparencyOverride;	// Toggle transparency locally
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::InputVoltageSpinValueChanged(TObject *Sender)
{
if (InputVoltageSpin->Value < MaxVoltageSpin->Value)
	MaxVoltageSpin->Value = InputVoltageSpin->Value;
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::StartVoltageSpinValueChanged(TObject *Sender)
{
// Change Start Up Voltage
if (VoltageSlider->Value < StartVoltageSpin->Value) {
	// lower the voltage unless it's already zero
    if (VoltageSlider->Value > 0.5) {
        SetVoltageSliderValue(StartVoltageSpin->Value);	// causes command to be sent to the TIU
        }
    }	
}
//---------------------------------------------------------------------------

void __fastcall TConvMode::CopyrightClick(TObject *Sender)
{
// Copy the Form to the clipboard.
// In BCB4.0, there is no SetSize method associated with TBitmap
// so we have to use Width and Height separately.
//
// see:
// https://stackoverflow.com/questions/3190777/how-do-i-copy-a-form-as-an-image-to-the-clipboard
//
Graphics::TBitmap * bm;
bm = new Graphics::TBitmap;
try
	{
    #if __BORLANDC__ >= 0x720
	// Defined for CB10.1 C++ programs that use the VCL.
	bm->SetSize(ClientWidth, ClientHeight);
    #else
    bm->Width = ClientWidth;
    bm->Height = ClientHeight;
    #endif
	BitBlt(bm->Canvas->Handle, 0, 0, ClientWidth, ClientHeight, Canvas->Handle, 0, 0, SRCCOPY);
    Clipboard()->Assign(bm);
    }
__finally
	{
	delete bm;
    }    
}
//---------------------------------------------------------------------------


