//---------------------------------------------------------------------------
/*
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

// RTC Near Field Communications
#include "RTCMegaMaster.h"
#include "RTC_NFC.h"

#include "Engine_Commands.h"
#include "Train_Control.h"
#include "AIU_Control.h"
#include "Op_Control.h"
#include "Setup_Control.h"
#include "Debug_Controls.h"
#include "Comm_Thread.h"
#include "DL_Thread.h"
#include "Clipbrd.hpp"
#include "Watchdog_Thread.h"

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

//TAIUControl *AIUControl;	// NOTE: this is public static variable, you can never use
							// this when you create multiple instances of a Form

extern TCommThread * ptrTIUCommThread;				// pointer to TIU communications thread
extern TDispatch_List_Thread * ptrDL_Thread;		// pointer to Dispatch List Thread
extern int AIUCount[TIUMAX];						// Number of AIU connected to each TIU
extern bool TIUSelection[TIUMAX];                  // Flags to indicate that a TIU is present

//---------------------------------------------------------------------------
__fastcall TAIUControl::TAIUControl(TComponent* Owner)
	: TForm(Owner)
{
assert(false);	// never called since I used the overloaded constructor below
}
//---------------------------------------------------------------------------
__fastcall TAIUControl::TAIUControl(TComponent* Owner, int Remote, int Engine, int TIU, int AIU)
	: TForm(Owner)
{
// grab the Remote, Engine, TIU and AIU numbers
RemoteNo = Remote;
assert(RemoteNo >= 0 && RemoteNo <= REMOTEMAX);
EngineNo =  Engine;		                        // Engine Number needed in AIU commands but is ignored by the TIU
assert(EngineNo >= 0 && EngineNo <= LASHUPMAX);	// 0 factory reset, 1-99, 100 "all engine operation, 101-120 LashUps
TIUNo = TIU;
assert(TIUNo >= 1 && TIUNo <= TIUMAX);
AIUNo = AIU;
assert(AIUNo >= 1 && AIUNo <= AIUMAX);
Accessory_Controls_Label->Caption = "AIU Controls for TIU " + String(TIUNo) +
	" AIU " + String(AIUNo);
this->Caption = Accessory_Controls_Label->Caption;

LocalTransparencyOverride = false;

// Fill the AIU Data Grid from a disk file
// Titles and Acc/Switch Name- needed when the grid is first created
AccData->Rows[ACC_LABEL_ROW]->CommaText = "Channel, \"Accessory Name\", \"State\", \"Switch Name\", \"State\"";
assert(AccData->Rows[ACC_LABEL_ROW]->Count == ACC_COUNT);
for (int i = 1; i < AccData->RowCount ; i++) {
	AccData->Cells [ACC_NUM] [i] = String(i);
    AccData->Cells [ACC_NAME] [i] = "*";
    AccData->Cells [ACC_STATE] [i] = "0"; 	// value is 1 or 0 : On or Off - default 0
    AccData->Cells [SW_NAME] [i] = "*";
    AccData->Cells [SW_STATE] [i] = "1";	// value is 1 or 0 : Normal - Reverse - default 1
	}
ACCDataFile = String(MainForm->cStartDir) + "\\" + "AccData" + String(TIUNo) + String(AIUNo) + ".ini";
TStringList * AccDat = new TStringList();
assert (AccDat != NULL);
AccDat->Sorted = false;
bACCSWDataCorrupt = false;
bACCSWDataLoaded = false;
__try {
   	try {
    	if (FileExists(ACCDataFile)) {
			// read it
	    	AccDat->LoadFromFile(ACCDataFile);
	    	// transfer data into Grid
			AccData->Rows[ACC_LABEL_ROW]->CommaText = AccDat->Strings[ACC_LABEL_ROW];
            assert(AccData->Rows[ACC_LABEL_ROW]->Count == ACC_COUNT);
	    	for (int ACCNum = ACC_TOPROW ; ACCNum < AccData->RowCount ; ACCNum++) {
				AccData->Rows[ACCNum]->CommaText = AccDat->Strings[ACCNum];
                assert(AccData->Rows[ACCNum]->Count == ACC_COUNT);
                // Convert old field values "OFF"/"ON" & "DIVERGE"/"STRAIGHT" to "0" and "1"
                if (AccData->Cells[SW_STATE][ACCNum].UpperCase() == "STRAIGHT") AccData->Cells[SW_STATE][ACCNum] = "1";	// Normal/Closed
                if (AccData->Cells[ACC_STATE][ACCNum].UpperCase() == "OFF") AccData->Cells[ACC_STATE][ACCNum] = "0";	// Off
                if (AccData->Cells[ACC_STATE][ACCNum] == "*") AccData->Cells[ACC_STATE][ACCNum] = "0";	// Off
                // Validate Data
                int field = ValidateACCDat(ACCNum);
                if (field != -1) throw((ACCNum * 10) + field);
	    		}
            }
        }
    catch (int field) {
    	MainForm->DebugMessage("Saved Accessory/Switch Data File Field (" + String(field) + ") is Corrupt : " + ACCDataFile);
    	bACCSWDataCorrupt = true;
    	MainForm->CaughtErrors->Value ++;
    	}
    catch ( ... ){
    	MainForm->DebugMessage("Saved Accessory/Switch File is Corrupt : " + ACCDataFile);
    	bACCSWDataCorrupt = true;
    	MainForm->CaughtErrors->Value ++;
    	}
    }
    __finally {
		delete AccDat;
    	bACCSWDataLoaded = true;
    	}

SelectedSw->Tag = -1;
SelectedAcc->Tag = -1;

// read preferences fro, the Registry
RegFileRootKey = MainForm->RegFileRootKey;
TRegIniFile * const RegFile = new TRegIniFile(RegFileRootKey);
assert(RegFile != NULL);
__try {
	//  restore selected cell in AccData
	AccData->Row = RegFile->ReadInteger("AIU", "AIUSelRow" + String(TIUNo) + String(AIUNo), AccData->Row);
	AccData->Col = RegFile->ReadInteger("AIU", "AIUSelCol" + String(TIUNo) + String(AIUNo), AccData->Col);
	}
__finally {
	// Deallocate RegFile
	delete RegFile;
	}

// Images
MainForm->OnOffImageList->GetBitmap(IMAGE_OFF, ImageOff->Picture->Bitmap);
MainForm->OnOffImageList->GetBitmap(IMAGE_ON, ImageOn->Picture->Bitmap);
MainForm->OnOffImageList->GetBitmap(IMAGE_STRAIGHT, ImageStraight->Picture->Bitmap);
MainForm->OnOffImageList->GetBitmap(IMAGE_DIVERGE, ImageDiverge->Picture->Bitmap);
// Use alternate words for Normal and Reverse if given in the  config.lua file
if (Setup->SwitchStraight != "Normal") {
    StraightLabel->Caption = StringReplace(StraightLabel->Caption, "Normal", Setup->SwitchStraight, TReplaceFlags());
	SwStraightButton->Caption  = StringReplace(SwStraightButton->Caption, "Normal", Setup->SwitchStraight, TReplaceFlags());
	SwStraightButton->Hint   = StringReplace(SwStraightButton->Hint, "Normal", Setup->SwitchStraight, TReplaceFlags());
	}
if (Setup->SwitchDiverge != "Reverse") {
    DivergeLabel->Caption = StringReplace(DivergeLabel->Caption, "Reverse", Setup->SwitchDiverge, TReplaceFlags());
    SwDivergeButton->Caption = StringReplace(SwDivergeButton->Caption, "Reverse", Setup->SwitchDiverge, TReplaceFlags());
    SwDivergeButton->Hint = StringReplace(SwDivergeButton->Hint, "Reverse", Setup->SwitchDiverge, TReplaceFlags());
	}
}
//---------------------------------------------------------------------------
__fastcall TAIUControl::~TAIUControl(void)
{
SaveACCDat();

if (MainForm->bSaveRegistryInfo) {
    // write preferences to the Registry
    TRegIniFile * const RegFile = new TRegIniFile(RegFileRootKey);
    assert(RegFile != NULL);
	__try {
		// Save selected cell in AccData
		RegFile->WriteInteger("AIU", "AIUSelRow" + String(TIUNo) + String(AIUNo), AccData->Row);
		RegFile->WriteInteger("AIU", "AIUSelCol" + String(TIUNo) + String(AIUNo), AccData->Col);
		}
	__finally {
        // Deallocate RegFile
        delete RegFile;
        }
    }

}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::SaveACCDat(void)
{
// Save AIU Data to disk with overwrite
if (bACCSWDataLoaded && !bACCSWDataCorrupt) { // don't save if we could not read it
	TStringList * ACCDat = new TStringList();
	assert (ACCDat != NULL);
	ACCDat->Sorted = false;
	__try {
    	try {
			// transfer data from Grid
            assert(AccData->Rows[ACC_LABEL_ROW]->Count == ACC_COUNT);
            ACCDat->Add(AccData->Rows[ACC_LABEL_ROW]->CommaText);
			for (int ACCNum = ACC_TOPROW ; ACCNum < AccData->RowCount ; ACCNum++) {
            	// Validate Data
                const int field = ValidateACCDat(ACCNum);
				if (field != -1) throw((ACCNum * 10) + field);
                assert(AccData->Rows[ACCNum]->Count == ACC_COUNT);
                // data is valid so move it into the ACCDat Object
			   	ACCDat->Add(AccData->Rows[ACCNum]->CommaText);
		   		}
			// write it
			ACCDat->SaveToFile(ACCDataFile);
   			if (DEBUGGING) DB->Debug_Print("Accessory/Switch Data Saved to " + ACCDataFile);
            }
        catch (int field) {
        	MainForm->DebugMessage("Edited Accessory/Switch Data Field (" + String(field) + ") is Corrupt - Not Saved : " + ACCDataFile);
        	bACCSWDataCorrupt = true;
        	MainForm->CaughtErrors->Value ++;
        	}
        catch (...) {
        	MainForm->DebugMessage("Edited Accessory/Switch Data is Corrupt - Not Saved : " + ACCDataFile);
        	bACCSWDataCorrupt = true;
        	MainForm->CaughtErrors->Value ++;
        	}
        }
    __finally {
		delete ACCDat;
        }
    }
}
//---------------------------------------------------------------------------
int __fastcall TAIUControl::ValidateACCDat(int ACCNum)
{
int val;
// Index
val = AccData->Cells[ACC_NUM][ACCNum].ToInt();
if (val != ACCNum) return(ACC_NUM);
// ACC Name
val = AccData->Cells[ACC_NAME][ACCNum].Length();
if (val == 0) return(ACC_NAME);
if ((val == 1) && (AccData->Cells[ACC_NAME][ACCNum] != "*")) return(ACC_NAME);
// ACC State
val = AccData->Cells[ACC_STATE][ACCNum].Length();
if (val == 0) return(ACC_STATE);
if ((val == 1) && (AccData->Cells[ACC_STATE][ACCNum] != "0") && (AccData->Cells[ACC_STATE][ACCNum] != "1")) return(ACC_STATE);
if (val > 1 ) return(ACC_STATE);
// SW Name
val = AccData->Cells[SW_NAME][ACCNum].Length();
if (val == 0) return(SW_NAME);
if ((val == 1) && (AccData->Cells[SW_NAME][ACCNum] != "*")) return(SW_NAME);
// SW State
val = AccData->Cells[SW_STATE][ACCNum].Length();
if (val == 0) return(SW_STATE);
if ((val == 1) && (AccData->Cells[SW_STATE][ACCNum] != "0") && (AccData->Cells[SW_STATE][ACCNum] != "1")) return(SW_STATE);
if (val > 1 ) return(ACC_STATE);
//
return -1;  	// valid
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccDataDrawCell(TObject *Sender, int ACol, int ARow,
      TRect &Rect, TGridDrawState State)
{
Graphics::TBitmap * pBitmap;
//TCanvas *pCanvas;
const int HOffset = 16, VOffset = 4;   // default text offset width
// Selected Acc or Switch
if( State.Contains(gdSelected) ) {
   	AccData->Canvas->Font->Color = clWhite;
	AccData->Canvas->Brush->Color = clBlue;
	} else {
	AccData->Canvas->Font->Color = clBlack;
	AccData->Canvas->Brush->Color = clWhite;
	}

if (ARow != ACC_LABEL_ROW) {
	if (ACol == ACC_STATE || ACol == SW_STATE) {
		AccData->Canvas->FillRect(Rect); // clear the rectangle
		pBitmap = new Graphics::TBitmap;
		assert(pBitmap != NULL);	// make sure constructor worked
		assert(MainForm->OnOffImageList->Count == 4);
        }

// Acc State ON or OFF
	if (ACol == ACC_STATE) {
    	if (AccData->Cells[ACol][ARow] == "1") {	// On
            MainForm->OnOffImageList->GetBitmap(IMAGE_ON, pBitmap);
			}
        if (AccData->Cells[ACol][ARow] == "0") {	// Off
            MainForm->OnOffImageList->GetBitmap(IMAGE_OFF, pBitmap);
			}
        if (AccData->Cells[ACC_NAME][ARow] != "*")
	        AccData->Canvas->BrushCopy(Bounds(Rect.Left + HOffset, Rect.Top + VOffset, pBitmap->Width, pBitmap->Height), pBitmap, Bounds(0, 0, pBitmap->Width, pBitmap->Height), clRed); // render bitmap
        if (pBitmap) delete pBitmap;
        return;
        }

// Switch State Normal or Reverse
	if (ACol == SW_STATE) {
    	if (AccData->Cells[ACol][ARow] == "1") {	// Normal
            MainForm->OnOffImageList->GetBitmap(IMAGE_STRAIGHT, pBitmap);
			}
        if (AccData->Cells[ACol][ARow] == "0")  {	// Reverse
            MainForm->OnOffImageList->GetBitmap(IMAGE_DIVERGE, pBitmap);
			}
        if (AccData->Cells[SW_NAME][ARow] != "*")
	        AccData->Canvas->BrushCopy(Bounds(Rect.Left + HOffset, Rect.Top + VOffset, pBitmap->Width, pBitmap->Height), pBitmap, Bounds(0, 0, pBitmap->Width, pBitmap->Height), clRed); // render bitmap
        if (pBitmap) delete pBitmap;
        return;
        }
    }

// Fill the Rectangle with Text (the name of the Accessory or Switch)
AccData->Canvas->FillRect(Rect);
// draw the text
String text = AccData->Cells[ACol][ARow];
AccData->Canvas->TextRect(Rect, Rect.Left, Rect.Top, text);
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccDataKeyDown(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
if (!MainForm->IsTIUConnected()){
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }
if (AccData->Col == ACC_STATE || AccData->Col == SW_STATE) return;
// if ENTER is pressed, select the Accessory or Switch unless the entry is empty
if (AccData->Cells[AccData->Col][AccData->Row] == "*") return;

if (Key == VK_RETURN) {
	AccDataClick(Sender);
    }
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccDataMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
int ARow, ACol;
// Respond to both left and right buttons // if (Button != mbLeft) return;	// only respond to left button
AccData->MouseToCell(X, Y,  ACol, ARow);
if (Button == mbRight) {
    AccData->Row = ARow;
    AccData->Col = ACol;
    }
// Row 1 through row 11 selects Accessory/Switch 1 through 10
if (ARow == ACC_LABEL_ROW ) return;	// ignore row 0
// if it's in either of the State Columns or the number column, ignore it
if (ACol == ACC_STATE || ACol == SW_STATE || ACol == ACC_NUM) return;
if (ARow == -1 || ACol == -1) return;	// click out of bounds
// Popup the PopupMenu
if (Button == mbRight) {
	AccData->PopupMenu->Popup(Left + X, Top + Y);
    }
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::EditACCSwNameClick(TObject *Sender)
{
// if it's in either of the State Columns or the number column, don't edit anything
if (AccData->Col == ACC_STATE || AccData->Col == SW_STATE || AccData->Col == ACC_NUM) return;
const String NameofAcc = AccData->Cells[AccData->Col][AccData->Row];
// Edit Accessory/Switch Name
String NewName = NameofAcc;
bool retvb = InputQuery("Edit Name", "New Accessory or Switch Name:", NewName);
if (!retvb) return;					// nochange in InputQuery box
if (NewName == NameofAcc) return;	// not changed

// must be more than 1 character
if (NewName.Length() == 1) {
	MainForm->DebugMessagePopup("Warning", "New Accessory/Switch Name must be more than one character");
    return;	// no change
    }
//
AccData->Cells[AccData->Col][AccData->Row] = NewName;
if (AccData->Col == ACC_NAME && AccData->Cells[ACC_STATE][AccData->Row] == "*")
	AccData->Cells[ACC_STATE][AccData->Row] = "0";	// Off
if (AccData->Col == SW_NAME && AccData->Cells[SW_STATE][AccData->Row] == "*")
	AccData->Cells[SW_STATE][AccData->Row] = "1";	// Normal
if ((AccData->Row == SelectedAcc->Tag) && (AccData->Col == ACC_NAME)) {
	SelectedAcc->Text = NewName;
    }
if ((AccData->Row == SelectedSw->Tag) && (AccData->Col == SW_NAME)) {
	SelectedSw->Text = NewName;
    }
AccData->Invalidate();	// redraw the Grid
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccActivateButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// Start Accessory
// Send CM_ACT message to ourselves which will perform this function
(void) PostMessage(Handle, CM_ACT, true, NULL);	// true = button down
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccActivateButtonMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// Stop Accessory
// Send CM_ACT message to ourselves which will perform this function
(void) PostMessage(Handle, CM_ACT, false, NULL);	// false = button up
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::CMAct(TMessage &Message)
{
//
const bool Dn = Message.WParam;		// set button state from Word Parameter
TShiftState Shift;					// need to fake this to call function below
static bool SaveAllCheckBox = false;
Shift << ssLeft;
if (Dn) {	// true = button down
    SaveAllCheckBox = AllToggleSwitch->Checked;
    if (AllToggleSwitch->Checked == false && SelectedAcc->Tag == -1) {	// no ACC selected
   	    MainForm->DebugMessagePopup("Warning", "Select an Accessory or 'ALL' before pressing [Activate]");
    	return;
	    }
	// begin Activate
    WatchdogToggleSwitch->Checked = false;
    if (DEBUGGING) DB->Debug_Print("CMAct: Button Down");
    AccOnButtonMouseDown(NULL, (TMouseButton) NULL, Shift, 0, 0);	// function does not make use of any parameter
    }
if (!Dn) {	// false = button up
    AllToggleSwitch->Checked = SaveAllCheckBox;
    SaveAllCheckBox = false;
    if (AllToggleSwitch->Checked == false && SelectedAcc->Tag == -1) {	// no ACC selected
   	    MainForm->DebugMessagePopup("Warning", "Select an Accessory or 'ALL' before pressing [Activate]");
    	return;
	    }
	// end Activate
    WatchdogToggleSwitch->Checked = false;
    if (DEBUGGING) DB->Debug_Print("CMAct: Button Up");
    AccOffButtonMouseDown(NULL, (TMouseButton) NULL, Shift, 0, 0);	// function does not make use of any parameter
	}
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccOnButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// if TIU Port is not connected fail
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }

if (AllToggleSwitch->Checked) {
/* Wrong way
    // send all listed accessories ON command like MakeItSo();
    const TCursor OldCursor = Screen->Cursor; 	// save cursor
    Screen->Cursor = crHourGlass;
    // Loop through all of the Accessories and set the Accy to On
    const int SavedRow = AccData->Row;
    const int SavedCol = AccData->Col;
    AccData->Col = ACC_NAME;
    for (int iChan = 1 ; iChan <= CHANMAX ; iChan++) {
        if (AccData->Cells[AccData->Col][iChan] == "*") continue;
        AccData->Row = iChan;
        AccData->Cells[ACC_STATE][iChan] = "1";	// On
        AccData->Repaint();
        // Send Acc On command - this command has a two digit operand
        const int Num = (iChan - 1) + ((AIUNo - 1) * CHANMAX);
        // Convert value into chars
        char str[15];
        (void)snprintf(str, sizeof str, ACC_ON, Num);
        (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK, NULL, NULL, NULL);
        MainForm->Delay(200);
        }
    AccData->Row = SavedRow;
    AccData->Col = SavedCol;
	Screen->Cursor = OldCursor;
	// reset ALL check box
	AllCheckBox->Checked = false;
	return;
*/
    // Aa
    char * ACCYOn[AIUMAX] = {"Aa000000000003FF",
                         "Aa000000000FFC00",
                         "Aa0000003FF00000",
                         "Aa0000FFC0000000",
                         "Aa03FF0000000000"};

    // send all accessories on command to all AIU on all TIU
    for (int iTIU = 1 ; iTIU <= TIUMAX ; iTIU++) {
        if (TIUSelection[iTIU - 1] == false) continue;
        if (AIUCount[iTIU - 1] == 0) continue;
        for (int iAIU = 1 ; iAIU <= AIUCount[iTIU - 1] ; iAIU++) {
            (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, ACCYOn[iAIU - 1], iTIU, RemoteNo, 11, AIU_LONG_WAIT, RECORD_OK, NULL);
            // Here we set the accessory state to on
            if (iTIU == TIUNo && iAIU == AIUNo) {
                for (int iChan = 1 ; iChan <= CHANMAX ; iChan++) {
                    AccData->Cells[ACC_STATE][iChan] = "1";	// On
                    }
                }
            }
        }
    AccData->Repaint();
	// reset ALL toggle switch
	AllToggleSwitch->Checked = false;
    return;
    }
//-----------------------------------
// AeXX
if (SelectedAcc->Tag == -1) {	// no ACC selected
   	MainForm->DebugMessagePopup("Warning", "Select an Accessory or 'ALL' before pressing [On]");
	return;
	}
//
SelectedAcc->Color = clLime;
AccData->Cells[ACC_STATE][SelectedAcc->Tag] = "1";	// On
// Send Acc On command - this command has a two digit operand
const int Num = (SelectedAcc->Tag - 1) + ((AIUNo - 1) * 10);
// Convert value into chars
char str[15];
(void)snprintf(str, sizeof str, ACC_ON, Num);
(void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK, NULL, NULL, NULL);
//
if (WatchdogToggleSwitch->Checked) {    // Send a watchdog signal
    const TCursor OldCursor = Screen->Cursor; 	// save cursor
    Screen->Cursor = crHourGlass;
    // Send an "I0" command here - used to keep newly powered on engines in Command Mode
    MainForm->Delay(WATCHDOGWAIT);    // by experimentation, I found that a short delay after the accessory command works best
    // this sends an "I0" and updates the engine list and active engine counts
    MainForm->FindEngines(TIUNo);
    WatchdogToggleSwitch->Checked = false;
    Screen->Cursor = OldCursor;
    }
return;
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccOffButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// if TIU Port is not connected fail
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }

if (AllToggleSwitch->Checked) {
/* Wrong Way
    // send all accessories OFF command like MakeItSo()
    const TCursor OldCursor = Screen->Cursor; 	// save cursor
    Screen->Cursor = crHourGlass;
    // Loop through all of the Accessories and set the Accy to Off
    const int SavedRow = AccData->Row;
    const int SavedCol = AccData->Col;
    AccData->Col = ACC_NAME;
    for (int iAcc = 1 ; iAcc <= CHANMAX ; iAcc++) {
        if (AccData->Cells[AccData->Col][iAcc] == "*") continue;
        AccData->Row = iAcc;
        AccData->Cells[ACC_STATE][iAcc] = "0";	// Off
        AccData->Repaint();
        // Send Acc Off command - this command has a two digit operand
        const int Num = (iAcc - 1) + ((AIUNo - 1) * CHANMAX);
        // Convert value into chars
        char str[15];
        (void)snprintf(str, sizeof str, ACC_OFF, Num);
        (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK, NULL, NULL, NULL);
        MainForm->Delay(200);
        }
    AccData->Row = SavedRow;
    AccData->Col = SavedCol;
	Screen->Cursor = OldCursor;
	// reset ALL check box
	AllCheckBox->Checked = false;
	return;
*/
    // Aa
    // send all accessories off command to all TIU that have AIU attached
    for (int iTIU = 1 ; iTIU <= TIUMAX ; iTIU++) {
        if (TIUSelection[iTIU - 1] == false) continue;
        if (AIUCount[iTIU - 1] == 0) continue;
        (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, "Aa00000000000000", iTIU, RemoteNo, 11, AIU_LONG_WAIT, RECORD_OK, NULL);
        for (int iAIU = 1 ; iAIU <= AIUCount[iTIU - 1] ; iAIU++) {
            // Here we set the accessory state to off
            if (iTIU == TIUNo && iAIU == AIUNo) {
                for (int iChan = 1 ; iChan <= CHANMAX ; iChan++) {
                    AccData->Cells[ACC_STATE][iChan] = "0";	// Off
                    }
                }
            }
        }
    AccData->Repaint();
	// reset ALL toggle switch
	AllToggleSwitch->Checked = false;
	return;
    }
//-----------------------------------
// AfXX
if (SelectedAcc->Tag == -1) {	// no ACC selected
   	MainForm->DebugMessagePopup("Warning", "Select an Accessory or 'ALL' before pressing [Off]");
	return;
    }
SelectedAcc->Color = clRed;
AccData->Cells[ACC_STATE][SelectedAcc->Tag] = "0";	// Off
// Send Acc On command
const int Num = (SelectedAcc->Tag - 1) + ((AIUNo - 1) * 10);
// Convert value into chars - this command has a two digit operand
char str[15];
(void)snprintf(str, sizeof str, ACC_OFF, Num);
(void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK, NULL, NULL, NULL);
WatchdogToggleSwitch->Checked = false;
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::SwStraightButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// if TIU Port is not connected fail
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }
if (AllToggleSwitch->Checked) {
/* Wrong Way
    // send ALL switches NORMAL command like MakeItSo()
    const TCursor OldCursor = Screen->Cursor; 	// save cursor
    Screen->Cursor = crHourGlass;
    const int SavedRow = AccData->Row;
    const int SavedCol = AccData->Col;
    // Loop through all of the switches and set each one to Normal
    AccData->Col = SW_NAME;
    for (int iSw = 1 ; iSw <= 10 ; iSw++) {
        if (AccData->Cells[AccData->Col][iSw] == "*") continue;
        AccData->Row = iSw;
        AccData->Cells[SW_STATE][iSw] = "1";	// On
        AccData->Repaint();
        // Send Sw command - this command has a two digit operand
        const int Num = (iSw - 1) + ((AIUNo - 1) * 10);
        // Convert value into chars
        char str[15];
        (void)snprintf(str, sizeof str, SWITCH_NORMAL, Num);	// Normal
        (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK, NULL, NULL, NULL);
		// the AIU takes about 1 second to energize the switch but we don't have to wait for it
		MainForm->Delay(200);
		}
	AccData->Row = SavedRow;
    AccData->Col = SavedCol;
	Screen->Cursor = OldCursor;
	// reset ALL check box
	AllCheckBox->Checked = false;
	return;
*/
    // Ab
    char * SWNormal[AIUMAX] = {"Ab00000000000000000000055555",
                        "Ab00000000000000005555500000",
                        "Ab00000000000555550000000000",
                        "Ab00000055555000000000000000",
                        "Ab05555500000000000000000000"};

    // send all switches Normal command to all AIU on all TIU
    for (int iTIU = 1 ; iTIU <= TIUMAX ; iTIU++) {
        if (TIUSelection[iTIU - 1] == false) continue;
        if (AIUCount[iTIU - 1] == 0) continue;
        for (int iAIU = 1 ; iAIU <= AIUCount[iTIU - 1] ; iAIU++) {
            (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, SWNormal[iAIU - 1], iTIU, RemoteNo, 11, AIU_LONG_WAIT, RECORD_OK, NULL);
            // Here we set the switch state to normal
            if (iTIU == TIUNo && iAIU == AIUNo) {
                for (int iChan = 1 ; iChan <= CHANMAX ; iChan++) {
                    AccData->Cells[SW_STATE][iChan] = "1";	// Normal
                    }
                }
            }
        }
    AccData->Repaint();
	// reset ALL toggle switch
	AllToggleSwitch->Checked = false;
	return;
    }

//-----------------------------------
// Acxx
if (SelectedSw->Tag == -1) {	// no Switch selected
   	MainForm->DebugMessagePopup("Warning", "Select a Switch or 'ALL' before pressing [Normal]");
	return;
    }
SelectedSw->Color = clLime;
AccData->Cells[SW_STATE][SelectedSw->Tag] = "1";	// Normal
// Send Sw Normal command - this command has a two digit operand
const int Num = (SelectedSw->Tag - 1) + ((AIUNo - 1) * 10);
// Convert value into chars - this command has a two digit operand
char str[15];
(void)snprintf(str, sizeof str, SWITCH_NORMAL, Num);
(void)ptrDL_Thread->Send_String_DL_FIFO(0, EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK);
if (Setup->Safety_Mode->Checked) {	// double toggle the switch in Safety_Mode
	char str2[15];
	(void)snprintf(str2, sizeof str2, SWITCH_REVERSE, Num);
	(void)ptrDL_Thread->Send_String_DL_FIFO(1100, EngineNo, str2, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK);
	(void)ptrDL_Thread->Send_String_DL_FIFO(2200, EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK);
	}
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::SwDivergeButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// if TIU Port is not connected fail
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }
if (AllToggleSwitch->Checked) {
/* Wrong Way
	// send ALL switches REVERSE command like MakeItSo()
    const TCursor OldCursor = Screen->Cursor; 	// save cursor
    Screen->Cursor = crHourGlass;
    const int SavedRow = AccData->Row;
    const int SavedCol = AccData->Col;
    // Loop through all of the switches and set each one to Reverse
    AccData->Col = SW_NAME;
    for (int iSw = 1 ; iSw <= 10 ; iSw++) {
        if (AccData->Cells[AccData->Col][iSw] == "*") continue;
        AccData->Row = iSw;
        AccData->Cells[SW_STATE][iSw] = "0";	// Off
        AccData->Repaint();
        // Send Sw command - this command has a two digit operand
        const int Num = (iSw - 1) + ((AIUNo - 1) * 10);
        // Convert value into chars
        char str[15];
        (void)snprintf(str, sizeof str, SWITCH_REVERSE, Num);	// Reverse
        (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK, NULL, NULL, NULL);
		// the AIU takes about 1 second to energize the switch but we don't have to wait for it
		MainForm->Delay(200);
		}
    AccData->Row = SavedRow;
	AccData->Col = SavedCol;
	Screen->Cursor = OldCursor;
	// reset ALL check box
	AllCheckBox->Checked = false;
	return;
*/
    // Ab
    char * SWReverse[AIUMAX] = {"Ab000000000000000000000AAAAA",
                            "Ab0000000000000000AAAAA00000",
                            "Ab00000000000AAAAA0000000000",
                            "Ab000000AAAAA000000000000000",
                            "Ab0AAAAA00000000000000000000"};
                            
    // send all switches Reverse command on all AIU on all TIU
    for (int iTIU = 1 ; iTIU <= TIUMAX ; iTIU++) {
        if (TIUSelection[iTIU - 1] == false) continue;
        if (AIUCount[iTIU - 1] == 0) continue;
        for (int iAIU = 1 ; iAIU <= AIUCount[iTIU - 1] ; iAIU++) {
            (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, SWReverse[iAIU - 1], iTIU, RemoteNo, 11, AIU_LONG_WAIT, RECORD_OK, NULL);
            // Here we set the switch state to reverse
            if (iTIU == TIUNo && iAIU == AIUNo) {
                for (int iChan = 1 ; iChan <= CHANMAX ; iChan++) {
                    AccData->Cells[SW_STATE][iChan] = "0";	// Reverse
                    }
                }
            }
        }
    AccData->Repaint();
	// reset ALL toggle switch
	AllToggleSwitch->Checked = false;
	return;    
    }
//-----------------------------------
// AdXX
if (SelectedSw->Tag == -1) {	// no Switch selected
   	MainForm->DebugMessagePopup("Warning", "Select a Switch or 'ALL' before pressing [Reverse]");
	return;
    }
SelectedSw->Color = clRed;
AccData->Cells[SW_STATE][SelectedSw->Tag] = "0";	// Reverse
// Send Sw Reverse command - this command has a two digit operand
const int Num = (SelectedSw->Tag - 1) + ((AIUNo - 1) * 10);
// Convert value into chars
char str[15];
(void)snprintf(str, sizeof str, SWITCH_REVERSE, Num);
(void)ptrDL_Thread->Send_String_DL_FIFO(0, EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK);
if (Setup->Safety_Mode->Checked) {	// double toggle the switch in Safety_Mode
	char str2[15];
	(void)snprintf(str2, sizeof str2, SWITCH_NORMAL, Num);
	(void)ptrDL_Thread->Send_String_DL_FIFO(1100, EngineNo, str2, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK);
	(void)ptrDL_Thread->Send_String_DL_FIFO(2200, EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK);
	}
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccDataClick(TObject *Sender)
{
// user clicked on a cell in AccData
// This does not cause any action to an Accessory or switch
//if (!MainForm->IsTIUConnected()){
//	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
//	return;
//    }
// clicking in column header or row header is ignored
if (AccData->Col == ACC_NUM || AccData->Row == ACC_LABEL_ROW) return;

// Accessory Name or State column clicked
if (AccData->Col == ACC_NAME || AccData->Col == ACC_STATE) {
	if (AccData->Cells[ACC_NAME][AccData->Row] == "*") {
       	SelectedAcc->Tag = -1;
        SelectedAcc->Text = "Click on an Accessory";
        SelectedAcc->Color = clWindow;
        return;
    	}
	// Select the Accessory on a click
	SelectedAcc->Tag = AccData->Row;
	SelectedAcc->Text = AccData->Cells[ACC_NAME][AccData->Row];
    if (DEBUGGING) DB->Debug_Print("Accessory Selected : " + String(SelectedAcc->Tag));
    SelectedAcc->Tag = AccData->Row; 	// save row number
    if (AccData->Cells[ACC_STATE][SelectedAcc->Tag] == '1') {
        SelectedAcc->Color = clLime;	// On
    	} else {
        SelectedAcc->Color = clRed;		// Off
        }
    return;
	}

// Switch Name or State column clicked
if (AccData->Col == SW_NAME || AccData->Col == SW_STATE) {
	if (AccData->Cells[SW_NAME][AccData->Row] == "*") {
        SelectedSw->Tag = -1;
        SelectedSw->Text = "Click on a Switch Track";
        SelectedSw->Color = clWindow;
        return;
    	}
	// Select the Switch on a click
    SelectedSw->Tag = AccData->Row;
    SelectedSw->Text = AccData->Cells[SW_NAME][AccData->Row];
    if (DEBUGGINGX(6)) DB->Debug_Print("Switch Selected : " + String(SelectedSw->Tag));
    SelectedSw->Tag = AccData->Row; 	// save row number
    if (AccData->Cells[SW_STATE][SelectedSw->Tag] == "1") {
        SelectedSw->Color = clLime;		// Normal
    	} else {
        SelectedSw->Color = clRed;		// Reverse
        }
    return;
    }
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccDataDblClick(TObject *Sender)
{
// toggle the State or if field is empty, Edit the Accessory/Switch Name

// double clicking in column header or row header is ignored
if (AccData->Col == ACC_NUM || AccData->Row == ACC_LABEL_ROW) return;

// Accessory or Switch Name column double clicked
if (AccData->Col == ACC_NAME) {
	if (AccData->Cells[ACC_NAME][AccData->Row] == "*") {
		EditACCSwNameClick(EditACCSwName);
		return;
		}
	}
if (AccData->Col == SW_NAME) {
	if (AccData->Cells[SW_NAME][AccData->Row] == "*") {
    	EditACCSwNameClick(EditACCSwName);
		return;
		}
	}

// to proceed further, we need to be connected to a TIU
if (!MainForm->IsTIUConnected()){
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
	}

// Accessory Name or State column double clicked
if (AccData->Col == ACC_NAME || AccData->Col == ACC_STATE) {
	// to proceed further, we need to have an Accessory selected
	if (SelectedAcc->Tag == -1) {	// no Accessory selected
   		MainForm->DebugMessagePopup("Warning", "Click on an Accessory before trying to toggle the state");
		return;
    	}
	// Toggle the current state
    String CurState = AccData->Cells[ACC_STATE][SelectedAcc->Tag];
    TShiftState t;
    if (CurState == "0") AccOnButtonMouseDown(AccOnButton, (TMouseButton) NULL, t, 0, 0);
	if (CurState == "1") AccOffButtonMouseDown(AccOffButton, (TMouseButton) NULL, t, 0, 0);
	return;
    }
// Switch Name or State column double clicked
if (AccData->Col == SW_NAME || AccData->Col == SW_STATE) {
    // to proceed further, we need to have a switch selected
	if (SelectedSw->Tag == -1) {	// no Switch selected
   		MainForm->DebugMessagePopup("Warning", "Click on an Swtich before trying to toggle the state");
		return;
    	}
	// Toggle the current state
    String CurState = AccData->Cells[SW_STATE][SelectedSw->Tag];
    TShiftState t;
    if (CurState == "0") SwStraightButtonMouseDown(Sender, (TMouseButton) NULL, t, 0, 0);
	if (CurState == "1") SwDivergeButtonMouseDown(Sender, (TMouseButton) NULL, t, 0, 0);
	return;
	}

}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::MakeItSoButtonClick(TObject *Sender)
{
const TCursor OldCursor = Screen->Cursor; 	// save cursor
Screen->Cursor = crHourGlass;
// Loop through all of the Accessories and set the item to its stored state
const int SavedRow = AccData->Row;
const int SavedCol = AccData->Col;
AccData->Col = ACC_NAME;
for (int iAcc = 1 ; iAcc <= CHANMAX ; iAcc++) {
    if (AccData->Cells[AccData->Col][iAcc] == "*") continue;
	AccData->Row = iAcc;
    AccData->Repaint();
    // if ON, send the command to turn on the Accessory
    if (AccData->Cells[ACC_STATE][iAcc] == "1") {	// On
		// Send Acc On command - this command has a two digit operand so can handle 100 Accessories (10 AIU * 10 Acc per AIU)
		const int Num = (iAcc - 1) + ((AIUNo - 1) * CHANMAX);
		// Convert value into chars
		char str[15];
		(void)snprintf(str, sizeof str, ACC_ON, Num);
		(void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK, NULL, NULL, NULL);
	    MainForm->Delay(200);
		}
    // if OFF, send the command to turn of the Accessory
    if (AccData->Cells[ACC_STATE][iAcc] == "0") {	// Off
		// Send Acc Off command - this command has a two digit operand so can handle 100 Accessories (10 AIU * 10 Acc per AIU)
		const int Num = (iAcc - 1) + ((AIUNo - 1) * CHANMAX);
		// Convert value into chars
		char str[15];
		(void)snprintf(str, sizeof str, ACC_OFF, Num);
		(void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK, NULL, NULL, NULL);
	    MainForm->Delay(200);
		}
    }

// Loop through all of the switches and set each one to Normal or Reverse
AccData->Col = SW_NAME;
for (int iSw = 1 ; iSw <= 10 ; iSw++) {
    if (AccData->Cells[AccData->Col][iSw] == "*") continue;
	AccData->Row = iSw;
    AccData->Repaint();
    // Send Sw command - this command has a two digit operand so can handle 10 switches (10 AIU * 10 Switches per AIU)
	const int Num = (iSw - 1) + ((AIUNo - 1) * 10);
	// Convert value into chars
	char str[15];
    if (AccData->Cells[SW_STATE][iSw] == "0") {
		(void)snprintf(str, sizeof str, SWITCH_REVERSE, Num);	// Reverse
        } else {
		(void)snprintf(str, sizeof str, SWITCH_NORMAL, Num);	// Normal
    	}
     (void)ptrTIUCommThread->Send_String_FIFO(EngineNo, str, TIUNo, RemoteNo, 11, AIU_WAIT, RECORD_OK, NULL, NULL, NULL);
	MainForm->Delay(1100);	// the AIU takes about 1 second to energize the switch
    }
AccData->Row = SavedRow;
AccData->Col = SavedCol;
Screen->Cursor = OldCursor;
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::DeleteAccSw(TObject *Sender)
{
// if it's in either of the State Columns or the number column, don't delete anything
if (AccData->Col == ACC_STATE || AccData->Col == SW_STATE || AccData->Col == ACC_NUM) return;
const String NameofAcc = AccData->Cells[AccData->Col][AccData->Row];
if (NameofAcc == "*") return;	// slot already deleted

const String AskString = "Do you really want to delete " + NameofAcc + "?";
int retv = Application->MessageBox(AskString.c_str(), TEXT("Confirmation"), MB_YESNO | MB_ICONQUESTION);
if (retv == IDNO) return;
// else delete it
AccData->Cells[AccData->Col][AccData->Row] = "*";		// name field default
AccData->Cells[AccData->Col + 1][AccData->Row] = "1";	// state field default
if (AccData->Row == SelectedAcc->Tag) {
	SelectedAcc->Text = "Click on an Accessory";
    SelectedAcc->Tag = -1;
    SelectedAcc->Color = clWindow;
    }
if (AccData->Row == SelectedSw->Tag) {
	SelectedSw->Text = "Click on a Switch Track";
    SelectedSw->Tag = -1;
    SelectedSw->Color = clWindow;
    }
}

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

void __fastcall TAIUControl::SaveEditsButtonClick(TObject *Sender)
{
// Save AIU data with overwrite
SaveACCDat();
}
//---------------------------------------------------------------------------
// Workaround for SetLayeredWindowAttributes() to make transparent windows
extern lpfnSetLayeredWindowAttributes m_pSetLayeredWindowAttributes;
//---------------------------------------------------------------------------

void __fastcall TAIUControl::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 TAIUControl::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
// enable or disable buttons depending on TIU connection
if (MainForm->IsTIUConnected()) {
    AccOnButton->Enabled = true;
    AccOffButton->Enabled = true;
    AccActivateButton->Enabled = true;
    SwStraightButton->Enabled = true;
    SwDivergeButton->Enabled = true;
    MakeItSoButton->Enabled = true;
	} else {
    AccOnButton->Enabled = false;
    AccOffButton->Enabled = false;
    AccActivateButton->Enabled = false;
    SwStraightButton->Enabled = false;
    SwDivergeButton->Enabled = false;
    MakeItSoButton->Enabled = false;
    }
}
//---------------------------------------------------------------------------

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

void __fastcall TAIUControl::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;
    }
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::NextTIUClick(TObject *Sender)
{
//
// send a message to MainForm to display the next page of AIU Controls
// Active TIU numbers may not be sequential
#ifdef OLDWAY
int NewTIU = TIUNo;
if (NewTIU == TIUMAX) {
	NewTIU = 1;
    } else {
    NewTIU += 1;
    }
(void) PostMessage(MainForm->Handle, CM_AIU, AIUNo, NewTIU);
#else
int NewTIU = TIUNo;
// Find the next active TIU
for (int iCount = 1 ; iCount < TIUMAX ; iCount++) {
    if (NewTIU == TIUMAX) {
    	NewTIU = 1;
        } else {
        NewTIU += 1;
        }
    if (TIUSelection[NewTIU - 1]) {
        (void) PostMessage(MainForm->Handle, CM_AIU, AIUNo, NewTIU);
        break;
        }
    }
#endif
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::PrevTIUClick(TObject *Sender)
{
//
// send a message to MainForm to display the previous page of AIU Controls
// Active TIU numbers may not be sequential
#ifdef OLDWAY
int NewTIU = TIUNo;
if (NewTIU == 1) {
	NewTIU = TIUMAX;
    } else {
    NewTIU -= 1;
    }
(void) PostMessage(MainForm->Handle, CM_AIU, AIUNo, NewTIU);
#else
int NewTIU = TIUNo;
// Find the next active TIU
for (int iCount = 1 ; iCount < TIUMAX ; iCount++) {
    if (NewTIU == 1) {
        NewTIU = TIUMAX;
        } else {
        NewTIU -= 1;
        }
    if (TIUSelection[NewTIU - 1]) {
        (void) PostMessage(MainForm->Handle, CM_AIU, AIUNo, NewTIU);
        break;
        }
    }
#endif
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::NextAIUClick(TObject *Sender)
{
//
// send a message to MainForm to display the next page of AIU Controls
// AIU's are always sequentially numbered starting at 1 to AIUCount[TIUNo - 1]
int NewAIU = AIUNo;
if (NewAIU == AIUCount[TIUNo - 1]) {
	NewAIU = 1;
    } else {
    NewAIU += 1;
    }
(void) PostMessage(MainForm->Handle, CM_AIU, NewAIU, TIUNo);
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::PrevAIUClick(TObject *Sender)
{
//
// send a message to MainForm to display the previous page of AIU Controls
// AIU's are always sequentially numbered starting at 1 to AIUCount[TIUNo - 1]
int NewAIU = AIUNo;
if (NewAIU == 1) {
	NewAIU = AIUCount[TIUNo - 1];
    } else {
    NewAIU -= 1;
    }
(void) PostMessage(MainForm->Handle, CM_AIU, NewAIU, TIUNo);
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccOnButtonClick(TObject *Sender)
{
//MainForm->DebugMessagePopup("Information", "Acc On Button");
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccOffButtonClick(TObject *Sender)
{
//MainForm->DebugMessagePopup("Information", "Acc Off Button");
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::AccActivateButtonClick(TObject *Sender)
{
//MainForm->DebugMessagePopup("Information", "Acc Activate Button");
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::SwStraightButtonClick(TObject *Sender)
{
//MainForm->DebugMessagePopup("Information", "Switch Normal Button");
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::SwDivergeButtonClick(TObject *Sender)
{
//MainForm->DebugMessagePopup("Information", "Switch Reverse Button");
}
//---------------------------------------------------------------------------

void __fastcall TAIUControl::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;
    case VK_NEXT:
    	// Next AIU
        NextAIUClick(Sender);
    	Key = 0;	// handled
        break;
    case VK_PRIOR:
    	// Previous AIU
        PrevAIUClick(Sender);
    	Key = 0;	// handled
        break;
    case 'O':
    	//MainForm->DebugMessagePopup("Information", "Acc On Button");
        AccOnButtonMouseDown(NULL, (TMouseButton) NULL, Shift, 0, 0);
        Key = 0;	// handled
        break;
    case 'F':
    	//MainForm->DebugMessagePopup("Information", "Acc Off Button");
        AccOffButtonMouseDown(NULL, (TMouseButton) NULL, Shift, 0, 0);
    	Key = 0;	// handled
        break;
    case 'N':
    	//MainForm->DebugMessagePopup("Information", "Switch Normal Button");
        SwStraightButtonMouseDown(NULL, (TMouseButton) NULL, Shift, 0, 0);
    	Key = 0;	// handled
        break;
    case 'R':
    	//MainForm->DebugMessagePopup("Information", "Switch Reverse Button");
        SwDivergeButtonMouseDown(NULL, (TMouseButton) NULL, Shift, 0, 0);
    	Key = 0;	// handled
        break;
	}	
}
//---------------------------------------------------------------------------
// Possible future implementation of the [ALL] switch
// The ALL switch looks at every AIU*.ini file and affects only the accessories
// and switches that are defined.
// This has not been compiled or tested.
/*
        // Initialize the 100 bit string
        OneHundredBitString = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
        // loop through all AIU on one TIU
        // start with LSB of string, which is AIU #1, Channel #1
        // go through the string for 10 channels
        for (int iChan = 1 ; iChan <= 10 ; iChan++) {  // Channels 1 to 10
                               
            // if the channel is defined, set the appropriate bits in the OneHundredBitString
            if (Channel is defined) {
				if (button pushed for Normal) {
                	OneHundredBitString[(100 + 1) - (iChan - 1) * 2] = '1';	// The [] operator assumes a base index of 1.
                	OneHundredBitString[(100 + 1) - (iChan - 1) * 2 - 1] = '0';	// The [] operator assumes a base index of 1.
					} else {
					// button pushed for Reverse
                	OneHundredBitString[(100 + 1) - (iChan - 1) * 2] = '0';	// The [] operator assumes a base index of 1.
	               	OneHundredBitString[(100 + 1) - (iChan - 1) * 2 - 1] = '1';	// The [] operator assumes a base index of 1.
					}
                }
            }

        if (DEBUGGING) DB->Debug_Print("ALL AIU Channels - OneHundredBitString :  " + OneHundredBitString);
        // Count the number of 1 bits on in the Binary string
        int ChanCount = OneBitsinBinaryString(OneHundredBitString);

        DebugMessage("ALL AIU Channels - Found " + String(EngCount) + " Channels(s)");

        // convert the OneHundredBitString into a "Ab" command
        String TwentySixHexString =  "0";	// since only 25 bytes come from the OneHundredBitString, we need to start off with 4 bits of zeros
        for (size_t i = 1 ; i <= 100 ; i = i + 4) {
            // eg: convert each 4 bits "0101" to a hex digit "5" and append onto the TwentySixHexString. The [] operator assumes a base index of 1.
            byte bval = (OneHundredBitString[i + 0] == '1' ? 8 : 0) +
                        (OneHundredBitString[i + 1] == '1' ? 4 : 0) +
                        (OneHundredBitString[i + 2] == '1' ? 2 : 0) +
                        (OneHundredBitString[i + 3] == '1' ? 1 : 0);
            TwentySixHexString += IntToHex(bval, 1);
            }

        assert(TwentySixHexString.Length() == 26);
        // "Ab00000000000000000000000101" each bit counting from the right is a channel
        AbHexString = "Ab" + TwentySixHexString;   // 13 hex digits (26 characters)
        //
*/
//---------------------------------------------------------------------------

