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

© Copyright 2019 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/>.

*/
// Home Page : http://www.silogic.com/trains/RTC_Running.html
//---------------------------------------------------------------------------
#include <vcl.h>
#include <tchar.h>
#pragma hdrstop

#include "Route_Scene_Thread.h"
#include "Route_Scene.h"
#include "Train_Control.h"
#include "Setup_Control.h"
#include "Debug_Controls.h"
#include "Route_Scene_Help.h"
#include "Comm_Thread.h"
#include "Clipbrd.hpp"
#include "RouteScenePopOutList.h"

#include <stdio.h>

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "SyncListBox"
#pragma resource "*.dfm"

TRouteScene *RouteScene;

extern bool SuperTIUMode, TIUSuperMode[TIUMAX];         // Flags to indicate if Super TIU Mode is active
extern bool TIUSelection[TIUMAX];                  // Flags to indicate that a TIU is present
extern TCommThread * ptrTIUCommThread;			// Pointer to TIU communications thread

//---------------------------------------------------------------------------
__fastcall TRouteScene::TRouteScene(TComponent* Owner)
	: TForm(Owner)
{
RemoteNo = MainForm->RemoteNo;
assert(RemoteNo >= 0 && RemoteNo <= REMOTEMAX);

if (SuperTIUMode) {
     RS_Controls_Label->Caption = RS_Controls_Label->Caption + " SuperTIUMode ";
     }
this->Caption = RS_Controls_Label->Caption;

LocalTransparencyOverride = false;
//
// setup RouteScene default
RSNo = RSSpin->ValueAsInt; 	// always start with RouteScene 1

LoadRSData(RSData);	// Load RS data for the first time
ReloadButtonClick(NULL);	// fill the Switch/Accessory list for the first time
 //
RSData->DefaultColWidth = 30;	// default
RSData->ColWidths[RS_NAME] = RSData->Width - RSData->DefaultColWidth;	// lots of space for the RouteScene name
RSData->ColWidths[RS_LIST] = 100;	// lots of space for the 2nd Parameter but it's not visible

RSPageControl->ActivePage = SelectRSSheet;
RSData->Row = RSNo;
RSUpDown->Position = RSUPDOWNLIMIT - RSNo;
// set the Route/Scene Name into the Control
NameofRS->Text = RSData->Cells[RS_NAME][RSNo];

if (MainForm->IsTIUConnected()) {
    RSActivateButton->Enabled = true;
    RSResetButton->Enabled = true;
    AllSwNormalButton->Enabled = true;
    AllAccyOnButton->Enabled = true;
    AllAccyOffButton->Enabled = true;    
	} else {
    RSActivateButton->Enabled = false;
    RSResetButton->Enabled = false;
    AllSwNormalButton->Enabled = false;
    AllAccyOnButton->Enabled = false;
    AllAccyOffButton->Enabled = false;    
    }

if (RSData->Cells[RS_NAME][RSData->Row] == "*") {
	// PopUp Menu
	CreateNewRouteScene->Enabled = true;
    EditRouteScene->Enabled = false;
    DeleteRouteScene->Enabled = false;
    //Buttons
    EditRS->Enabled = false;
    DeleteRS->Enabled = false;
    CreateRS->Enabled = true;
    // clear the switch/accessory list
    RSinSelectedListBox->Clear();
	} else {
    // PopUp Menu
	CreateNewRouteScene->Enabled = false;
    EditRouteScene->Enabled = true;
    DeleteRouteScene->Enabled = true;
    // Buttons
    EditRS->Enabled = true;
    DeleteRS->Enabled = true;
    CreateRS->Enabled = false;
    // Fill the Route/Scene Switch/Accessory List (it might be empty)
	LoadSwitchAccessoryData(RSData->Row, RSinSelectedListBox);
    }
// make the edit tab invisible
CreateRSSheet->TabVisible = false;
//
// make the debug fields visible
if (DEBUGGING) {
	SelectedSwAcc->Visible = true;
	SwitchAccessoryListBox->Height = 250;
	SWACCinSelectedRSListBox->Height = 250;
//    DebugPanel->Visible = true;	// turned on when user selects a Switch/Accessory
    }
// 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);
MainForm->OnOffImageList->GetBitmap(IMAGE_OFF, ImageOff1->Picture->Bitmap);
MainForm->OnOffImageList->GetBitmap(IMAGE_ON, ImageOn1->Picture->Bitmap);
MainForm->OnOffImageList->GetBitmap(IMAGE_STRAIGHT, ImageStraight1->Picture->Bitmap);
MainForm->OnOffImageList->GetBitmap(IMAGE_DIVERGE, ImageDiverge1->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());
    StraightLabel1->Caption = StringReplace(StraightLabel1->Caption, "Normal", Setup->SwitchStraight, TReplaceFlags());
	}
if (Setup->SwitchDiverge != "Reverse") {
    DivergeLabel->Caption = StringReplace(DivergeLabel->Caption, "Reverse", Setup->SwitchDiverge, TReplaceFlags());
    DivergeLabel1->Caption = StringReplace(DivergeLabel1->Caption, "Reverse", Setup->SwitchDiverge, TReplaceFlags());
	}
}
//---------------------------------------------------------------------------

__fastcall TRouteScene::~TRouteScene(void)
{
SaveRSData(RSData);
}
//---------------------------------------------------------------------------

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

void __fastcall TRouteScene::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 TRouteScene::LoadRSData(TStringGrid * RSD)
{
// Fill the Route/Scene Data Grid from a disk file
// sets bRSDataLoaded to true

// Loaded once at the beginning of the run
// and saved once at the end of the run
assert(RSD != NULL);
if(bRSDataLoaded) return;	// if already loaded then do nothing
//
const String RSDataFile = String(MainForm->cStartDir) + "\\" + RS_FILENAME;
//
// Fill the Route/Scene Data Grid from a disk file
//  The Index parameter is the number of the row, where the value of the first row in the grid is zero.
//  The Index parameter is the number of the column, where the value of the first column in the grid is zero.

bRSDataCorrupt = false;
bRSDataLoaded = false;

try {
    TStringList * ptrRSDat = new TStringList();
    assert (ptrRSDat != NULL);	    // make sure that construction succeeded
    ptrRSDat->Sorted = false;
    #if __BORLANDC__ >= 0x720
    ptrRSDat->Duplicates = System::Classes::dupIgnore;	// needed because of an ambiguity in Emabarcadero CB10.1
    #else
    ptrRSDat->Duplicates = dupIgnore;
    #endif
	try {
	   	if (FileExists(RSDataFile)) {
  			// read it
   	    	ptrRSDat->LoadFromFile(RSDataFile);
            RSD->Rows[RS_LABEL_ROW]->CommaText = ptrRSDat->Strings[RS_LABEL_ROW];
            assert(RSD->Rows[RS_LABEL_ROW]->Count == RSDAT_COUNT);

   	    	// transfer data into TStringGrid from the TStringList
   	    	for (int RSNum = RS_TOPROW ; RSNum < RSD->RowCount ; RSNum++) {
	    		RSD->Rows[RSNum]->CommaText = ptrRSDat->Strings[RSNum];
                assert(RSD->Rows[RSNum]->Count == RSDAT_COUNT);
                // Validate Data
                const int field = ValidateRSData(RSD, RSNum);
				if (field != -1) throw((RSNum * 10) + field);
   	    		}
   	    	} else {
           	// RS Data Files does not exist so create an empty TStringGrid
   			// Titles and RS Numbers- needed when the grid is first created
   			RSD->Rows[RS_LABEL_ROW]->CommaText = RSDAT_TITLE;
            assert(RSD->Rows[RS_LABEL_ROW]->Count == RSDAT_COUNT);
   			for (int RSNum = RS_TOPROW; RSNum < RSD->RowCount ; RSNum++) {
       			// Insert RouteScene Number and extra field, RouteScene Name is "*"
       			RSD->Rows[RSNum]->CommaText = String(RSNum) + RSDAT_INIT;
                assert(RSD->Rows[RSNum]->Count == RSDAT_COUNT);
   				}
           	}
        if (ptrRSDat) {
            delete ptrRSDat;
            }
       	}
    catch (int field) {
    	ShowMessage("Saved Route/Scene Data File Field (" + String(field) + ") is Corrupt : " + RSDataFile);
       	bRSDataCorrupt = true;	// loaded but corrupt
       	MainForm->CaughtErrors->Value ++;
       	}

    catch ( ... ){
       	ShowMessage("Saved Route/Scene Data File is Corrupt : " + RSDataFile);
       	bRSDataCorrupt = true;	// loaded but corrupt
       	MainForm->CaughtErrors->Value ++;
       	}
    }
__finally {
   	bRSDataLoaded = true;	// loaded
    }
    
// read Preferences from registry if they exist
RegFileRootKey =  MainForm->RegFileRootKey;
TRegIniFile * const RegFile = new TRegIniFile(RegFileRootKey);
assert(RegFile != NULL);
try {
	// RSNo loaded on system wide basis
	RSNo = RegFile->ReadInteger("Options", "RouteScene", RSNo);
    }
__finally {
	// Deallocate RegFile
	delete RegFile;
    }
assert(RSNo >= 1 && RSNo <= RSMAX);	// RS Numbers only

// Make sure the selected RS is visible in the middle row if possible
if ((RSNo) < 3)
	RSD->TopRow = 1;
    else
	RSD->TopRow = RSNo - 2;
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::SaveRSData(TStringGrid * RSD)
{
// save all Route/Scene specific data
assert(RSD != NULL);

assert(bRSDataLoaded);
// if the Route/Scene Data is not loaded then do nothing.
if (!bRSDataLoaded) return;

const String RSDataFile = String(MainForm->cStartDir) + "\\" + RS_FILENAME;
if (MainForm->bSaveRegistryInfo) {
    // write preferences to the Registry
    TRegIniFile * const RegFile = new TRegIniFile(RegFileRootKey);
    assert(RegFile != NULL);
    try {
        // RSNo saved on a per system basis
        RegFile->WriteInteger("Options", "RouteScene", RSNo);
        }
    __finally {
        // Deallocate RegFile
        delete RegFile;
        }
    }
//
if (bRSDataLoaded && !bRSDataCorrupt) { // don't save if it's not loaded or if we could not read it
	try {
        TStringList * ptrRSDat = new TStringList();   // deleted in MainForm->CloseAllWindows()
        assert (ptrRSDat != NULL);	    // make sure that construction succeeded
        ptrRSDat->Sorted = false;
        #if __BORLANDC__ >= 0x720
        ptrRSDat->Duplicates = System::Classes::dupIgnore;	// needed because of an ambiguity in Emabarcadero CB10.1
        #else
        ptrRSDat->Duplicates = dupIgnore;
        #endif
    	try {
			// Save RouteScene Data to disk with overwrite
            assert(RSD->Rows[RS_LABEL_ROW]->Count == RSDAT_COUNT);
        	// transfer data from TStringGrid to TStringList
            ptrRSDat->Add(RSD->Rows[RS_LABEL_ROW]->CommaText);
	   		for (int RSNum = RS_TOPROW ; RSNum < RSD->RowCount ; RSNum++) {
            	// Validate Data
                const int field = ValidateRSData(RSD, RSNum);
				if (field != -1) throw((RSNum * 10) + field);
                assert(RSD->Rows[RSNum]->Count == RSDAT_COUNT);
                // Route/Scene data is valid so move it into the RSDat Object
                // transfer data from TSringGrid to TStringList
   				ptrRSDat->Add(RSD->Rows[RSNum]->CommaText);
   				}
	   		// write it
   			ptrRSDat->SaveToFile(RSDataFile);
			if (DEBUGGINGX(5)) DB->Debug_Print("Route/Scene Data Saved to " + RSDataFile);
            if (ptrRSDat) {
                delete ptrRSDat;
                }
			}
        catch (int field) {
           	ShowMessage("Edited Route/Scene Data Field (" + String(field) + ") is Corrupt - Not Saved : " + RSDataFile);
           	bRSDataCorrupt = true;
           	MainForm->CaughtErrors->Value ++;
           	}
        catch (...) {
           	ShowMessage("Edited Route/Scene Data is Corrupt - Not Saved : " + RSDataFile);
       		bRSDataCorrupt = true;
       		MainForm->CaughtErrors->Value ++;
           	}
    	}
    	__finally {
		if (bRSDataCorrupt) {
			bRSDataLoaded = false;  // indicate data should be reloaded
			} else {
			bRSDataLoaded = true; 	// the Route/Scene data remains in place
			}
    	}
    }
}
//---------------------------------------------------------------------------

int __fastcall TRouteScene::ValidateRSData(TStringGrid * RSD, int RSNum)
{
// This function must be called within a try{} block because the ToInt()
// function will throw an error if it cannot parse an integer
//
int val;
// Index
val = RSD->Cells[RS_NUM][RSNum].ToInt();
if (val != RSNum) return(RS_NUM);
// RS Name
val = RSD->Cells[RS_NAME][RSNum].Length();
if (val == 0) return(RS_NAME);
if ((val == 1) && (RSD->Cells[RS_NAME][RSNum] != "*")) return(RS_NAME);
// RS List
val = RSD->Cells[RS_LIST][RSNum].Length();
if (val == 0) return(RS_LIST);
if ((val == 1) && (RSD->Cells[RS_LIST][RSNum] != " ")) return(RS_LIST);
//
return -1;  	// valid
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::CancelRSButtonClick(TObject *Sender)
{
// Clear the Selected Switch/Accessory List
SWACCinSelectedRSListBox->Clear();
RSName->Text = "";
//Return to Select/Activate/Delete Tab
CreateRSSheet->TabVisible = false;
RSPageControl->ActivePage = SelectRSSheet;
RSData->SetFocus();
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::CreateRSClick(TObject *Sender)
{
// Create New Route/Scene
// check for blank line
if (RSData->Cells[RS_NAME][RSNo] != "*") {
	ShowMessage("Select an unused Route/Scene Number before creating a new one");
    return;
    }
// Reload the Switch/Accessory List as it might have changed
ReloadButtonClick(ReloadButton);
// clear the Selected Switch/Accessory List
SWACCinSelectedRSListBox->Clear();
RSName->Text = "";

if (SwitchAccessoryListBox->Items->Count == 0) {
	ShowMessage("You must have defined Accessories/Switches using the AIU Control window before you can create a new Route/Scene");
	return;	// No Accessories or Switches
    }

// switch to the Create Route/Scene sheet
SaveRSButton->Caption = "Save As New";
CreateRSSheet->TabVisible = true;
RSPageControl->ActivePage = CreateRSSheet;
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::EditRSClick(TObject *Sender)
{
// Edit Route/Scene
// check for blank line
if (RSData->Cells[RS_NAME][RSNo] == "*") {
	ShowMessage("Select an existing Route/Scene Number before editing");
    return;
    }
// Reload the Switch/Accessory List  as it might have changed
ReloadButtonClick(ReloadButton);

SWACCinSelectedRSListBox->Items->Text =  RSinSelectedListBox->Items->Text;
RSName->Text = NameofRS->Text;

// switch to the Edit Route/Scene sheet
CreateRSSheet->TabVisible = true;
SaveRSButton->Caption = "Save Edits";
RSPageControl->ActivePage = CreateRSSheet;
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::DeleteRSClick(TObject *Sender)
{
// Delete Route/Scene
// check for blank line
if (RSData->Cells[RS_NAME][RSNo] == "*") {
	ShowMessage("Select an existing Route/Scene Number before trying to delete one");
    return;
    }

// Break up with confirmation - don't need to be connected to just delete
const String AskString = "Do you really want to delete Route/Scene " + String(RSNo) +  " - " +
	RSData->Cells[RS_NAME][RSNo] + "?";
const int retv = Application->MessageBox(AskString.c_str(), TEXT("Confirmation"), MB_YESNO | MB_ICONQUESTION);
if (retv == IDNO) return;	// No - get out
// Delete it
RSData->Cells[RS_NAME][RSNo] = "*";
RSData->Cells[RS_LIST][RSNo] = " ";
NameofRS->Text = "";
RSinSelectedListBox->Clear();
//
CreateRS->Enabled = true;
EditRS->Enabled = false;

// Save the new Route/Scene List and refresh the PopOut List
SaveRSData(RSData);
PopoutRouteSceneList->RefreshClick(NULL);

char RSNum[10];
snprintf(RSNum, sizeof RSNum, "%03d", RSNo);
const String FileName = String(MainForm->cStartDir) + "\\" + "SwitchAccessory" + String(RSNum) + ".ini";
if (!FileExists(FileName)) {
	ShowMessage("Route/Scene File does not Exist: " + FileName);
    return;
    }
DeleteFile(FileName);
if (DEBUGGING) DB->Debug_Print("Switch/Accessory File " + FileName + " Deleted");
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::RSDataDrawCell(TObject *Sender, int ACol,
      int ARow, TRect &Rect, TGridDrawState State)
{
// Draw the contents of each cell
RSData->Canvas->Font->Size = 12;
RSData->Canvas->Font->Style = RSData->Canvas->Font->Style >> fsItalic;
// if the cell is selected, hightlight it
if( State.Contains(gdSelected) ) {
    RSData->Canvas->Font->Color = clWhite;
	RSData->Canvas->Brush->Color = clBlue;	// Blue on White
	} else {
	RSData->Canvas->Font->Color = clBlack;
	RSData->Canvas->Brush->Color = clWhite;	// White on Black
	}
// move a little to the right 
Rect.Left = Rect.Left + LISTLEFTOFFSET;
// fill the rectangle
RSData->Canvas->FillRect(Rect);
// draw the text
String text = RSData->Cells[ACol][ARow];
RSData->Canvas->TextRect(Rect, Rect.Left, Rect.Top, text);
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::SaveRSButtonClick(TObject *Sender)
{
// STEP 3 : Save Route/Scene
if (SWACCinSelectedRSListBox->Items->Count == 0) {
	ShowMessage("A Route/Scene must contain at least one Accessory or Switch");
    return;	// continue editing
    }
if (RSName->Text.IsEmpty()) {
	ShowMessage("    Please Name the Route/Scene (Step 2)    ");
    return;	// continue editing
    }
// must be more than 1 character
if (RSName->Text.Length() <= 1) {
	ShowMessage("New Route/Scene Name must be more than one character");
    return;	// continue editing
    }
// Add the Route/Scene name to the TStringGrid table (max length 48 characters)
String Name48 = RSName->Text;
Name48.SetLength(48);
RSName->Text = Name48;
RSData->Cells[RS_NAME][RSNo] = RSName->Text;
RSData->Cells[RS_LIST][RSNo] = " ";

// Save the new Route/Scene List and refresh the PopOut List
SaveRSData(RSData);
PopoutRouteSceneList->RefreshClick(NULL);

SaveSwitchAccessoryData(RSNo, SWACCinSelectedRSListBox);	// save the RouteScene Switch/Accessory List

RSinSelectedListBox->Items->Text =  SWACCinSelectedRSListBox->Items->Text;

//Return to Select Route/Scene Tab
RSDataClick(Sender);
CreateRSSheet->TabVisible = false;
RSPageControl->ActivePage = SelectRSSheet;
RSData->SetFocus();
//
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::RSDataClick(TObject *Sender)
{
if (RSData->Row == -1 || RSData->Col == -1) {
	RSData->Row = RS_TOPROW;  	// ignore out of bounds (if possible)
    RSUpDown->Position = RSUPDOWNLIMIT - RS_TOPROW;
    }

// Set the RS Number into the Control
RSSpin->ValueAsInt = RSData->Row;

if (PopoutRouteSceneList && (PopoutRouteSceneList->RSData->Row != RSData->Row)) {   // reflect the change onto the popout route/scene list
    PopoutRouteSceneList->RSData->Row = RSData->Row;
    }

// Set the RS Number into the working variable
RSNo = RSData->Row;
// set the RS Name into the Control
NameofRS->Text = RSData->Cells[RS_NAME][RSNo];

RSActivateButton->Enabled = false;
RSResetButton->Enabled = false;

if (RSData->Cells[RS_NAME][RSData->Row] == "*") {
	// PopUp Menu
	CreateNewRouteScene->Enabled = true;
    EditRouteScene->Enabled = false;
    DeleteRouteScene->Enabled = false;
    //Buttons
    EditRS->Enabled = false;
    DeleteRS->Enabled = false;
    CreateRS->Enabled = true;
    // clear the switch/accessory list
    RSinSelectedListBox->Clear();
	} else {
    // PopUp Menu
    CreateNewRouteScene->Enabled = false;
    EditRouteScene->Enabled = true;
    DeleteRouteScene->Enabled = true;
    // Buttons
    EditRS->Enabled = true;
    DeleteRS->Enabled = true;
    CreateRS->Enabled = false;
    // if the port is connected, enabled the buttons
	if (MainForm->IsTIUConnected()) {
    	RSActivateButton->Enabled = true;
    	RSResetButton->Enabled = true;
        AllSwNormalButton->Enabled = true;
        AllAccyOnButton->Enabled = true;
        AllAccyOffButton->Enabled = true;
        }
    // Fill the Route/Scene Switch/Accessory List (it might be empty)
	LoadSwitchAccessoryData(RSData->Row, RSinSelectedListBox);
    }
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::LoadSwitchAccessoryData(int RSNo, TSyncListBox *SAList)
{
// Then clear the Destination list boxes
SAList->Clear();
char RSNum[10];
snprintf(RSNum, sizeof RSNum, "%03d", RSNo);
const String FileName = String(MainForm->cStartDir) + "\\" + "SwitchAccessory" + String(RSNum) + ".ini";
if (!FileExists(FileName)) {
	ShowMessage("Route/Scene File does not Exist: " + FileName);
    return;
    }
SAList->Items->LoadFromFile(FileName);
if (DEBUGGINGX(5)) DB->Debug_Print("Switch/Accessory Data Loaded from " + FileName);
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::SaveSwitchAccessoryData(int RSNo, TSyncListBox *SAList)
{
char RSNum[10];
snprintf(RSNum, sizeof RSNum, "%03d", RSNo);
const String FileName = String(MainForm->cStartDir) + "\\" + "SwitchAccessory" + String(RSNum) + ".ini";
SAList->Items->SaveToFile(FileName);
if (DEBUGGINGX(5)) DB->Debug_Print("Switch/Accessory Data Saved to " + FileName);
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::RSSpinValueChanged(TObject *Sender)
{
// this routine is called whenever something or someone changes RSNo->Value
assert(RSNo >= 1 && RSNo <= RSMAX);

if (DEBUGGING) DB->Debug_Print("RS Number changed from " + String(RSNo) + " to " + String(RSSpin->ValueAsInt));

RSNo = RSSpin->ValueAsInt;

if (RSNo != 0) {
	NameofRS->Text = RSData->Cells[RS_NAME][RSNo];
  	// Highlight the RS in the StringGrid
	RSData->Col = RS_NAME;
	RSData->Row = RSNo;
    RSUpDown->Position = RSUPDOWNLIMIT - RSNo;
    }
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::ReloadButtonClick(TObject *Sender)
{
// read up all of the AccDataXY.ini files
SwitchAccessoryListBox->Sorted = true;
SwitchAccessoryListBox->Clear();
AccDataFileCounter = 0;
TSearchRec sr;        // for FindFirst and FindNext

const int iAttributes = 0;  // Any normal file

const String FindString = String(MainForm->cStartDir) + "\\" + "AccData*.ini";
if (FindFirst(FindString, iAttributes, sr) == 0){
    // Add Switch/Accessory data for first file
    ProcessReload(sr);
    AccDataFileCounter++;
    while (FindNext(sr) == 0){
    	// Add Switch/Accessory data for remaining files
	    ProcessReload(sr);
       	AccDataFileCounter++;
    	}
    }
FindClose(sr);	// release resources
if (DEBUGGINGX(5)) DB->Debug_Print("Switch/Accessory Files Processed: " + String(AccDataFileCounter));
// TODO : this message came out on startup since now the Route/Scene window is auto create.
//if (SwitchAccessoryListBox->Items->Count == 0) {
//	ShowMessage("Use the AIU Control window to create Acessories or Switches before trying to create a Route or Scene");	// No Accessories or Switches
//	}
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::ProcessReload(TSearchRec sr)
{
// load an AccDataXX.ini file
const TCursor OldCursor = Screen->Cursor; 	// save cursor
Screen->Cursor = crHourGlass;

const String FileName = String(MainForm->cStartDir) + "\\" + sr.Name;
//
TStringList * const RSDat = new TStringList();	// deleted in finally{}
assert (RSDat != NULL);		// make sure that construction succeeded
RSDat->Sorted = false;
#if __BORLANDC__ >= 0x720
RSDat->Duplicates = System::Classes::dupIgnore;	// needed because of an ambiguity in Emabarcadero CB10.1
#else
RSDat->Duplicates = dupIgnore;
#endif
//
TStringList * TSL = new TStringList();	// deleted in finally{}
assert (TSL != NULL);	// make sure that construction succeeded
TSL->Sorted = false;
#if __BORLANDC__ >= 0x720
TSL->Duplicates = System::Classes::dupIgnore;	// needed because of an ambiguity in Emabarcadero CB10.1
#else
TSL->Duplicates = dupIgnore;
#endif
//
try {
   	try {
    	if (FileExists(FileName)) { // example: AccData11.ini
            int TIU, AIU;
			if (_stscanf(sr.Name.c_str(),TEXT("AccData%1d%1d"), &TIU, &AIU) != 2) {
               ShowMessage("Saved Accessory/Switch Filename is Corrupt : " + FileName);
               }
			// read it
	    	RSDat->LoadFromFile(FileName);
            assert(RSDat->Count == 11);
	    	// transfer data into List
			//SwitchAccessoryListBox->Items->Append(RSDat->Strings[RS_LABEL_ROW]);
	    	for (int RSNum = 1 ; RSNum < RSDat->Count ; RSNum++) {	// starting at second line in the file
	            // #,AccName,State,SwName,State
                TSL->Clear();
                TSL->CommaText = RSDat->Strings[RSNum];
                assert(TSL->Count == RSCONTROL_COUNT);
                const int Channel = TSL->Strings[0].ToInt();    // this may throw an EConvertError exception
                const String Acc = TSL->Strings[1];
                const String AccDef = TSL->Strings[2];
                const String Sww = TSL->Strings[3];
                const String SwwDef = TSL->Strings[4];

                //int Cmd = (Channel - 1) + ((AIU - 1) * 10);
                if (Acc.SubString(1,1) != "*") {
                    TCHAR CT[64];
                    const int DefSetting = (AccDef == "0") ? 0:1;
                    _sntprintf(CT, sizeof CT, _TEXT("%-50s\t%d,%d,%d,%d,%d,%d"), Acc.c_str(), 0, Channel, AIU, TIU, DefSetting, DefSetting);	// Off = 0   On = 1
					SwitchAccessoryListBox->Items->Append(CT);
					}
				if (Sww.SubString(1,1) != "*") {
					TCHAR CT[64];
					const int DefSetting = (SwwDef == "0") ? 0:1;
                    _sntprintf(CT, sizeof CT, _TEXT("%-50s\t%d,%d,%d,%d,%d,%d"), Sww.c_str(), 1, Channel, AIU, TIU, DefSetting, DefSetting);	// reverse = 0   Normal = 1
					SwitchAccessoryListBox->Items->Append(CT);
					// Scan the string just created and make sure it has 7 valid fields
					TCHAR SW[50];
					int type, Chan, AIUNo, TIUNo, Setting;
					const int retv = _stscanf(CT, _TEXT("%[ -~]%d,%d,%d,%d,%d,%d"), SW, &type, &Chan, &AIUNo, &TIUNo, &DefSetting, &Setting);
                    if (retv != 7) {
                    	ShowMessage("Saved Accessory/Switch Format is Corrupt : " + FileName);
                        }
                    }
	    		}
            if (DEBUGGINGX(5)) DB->Debug_Print("Switch/Accessory File loaded : " + FileName);
            }
        }
    catch (int field) {
	    MainForm->DebugMessage("Saved Accessory/Switch Data File Field (" + String(field) + ") is Corrupt : " + FileName);
    	MainForm->CaughtErrors->Value ++;
    	}
    catch ( ... ){
	    MainForm->DebugMessage("Saved Accessory/Switch File is Corrupt : " + FileName);
    	MainForm->CaughtErrors->Value ++;
    	}
    }
    __finally {
		delete RSDat;		// a const object
		delete TSL;
		TSL = NULL;
        Screen->Cursor = OldCursor;
    	}
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::CopySelectedList(TListBox *SrcLB, TSyncListBox *DestLB, bool ClearDest)
{
if (SrcLB->SelCount == 0) {
	//nothing selected
    return;
    }
if (ClearDest) {
 	DestLB->Clear();
    }
// copy selected items from source listbox
for (int Index = 0; Index < SrcLB->Items->Count; ++Index) {
	if (SrcLB->Selected[Index]) {
		String NewItem = SrcLB->Items->Strings[Index];
        // Get setting of switch/accessory for this route/scene
        TCHAR SW[50];
        int type, Chan, AIUNo, TIUNo, DefSetting, Setting;
        const int retv = _stscanf(NewItem.c_str(),_TEXT("%[ -~]%d,%d,%d,%d,%d,%d"), SW, &type, &Chan, &AIUNo, &TIUNo, &DefSetting, &Setting);
        if (retv != 7) {
        	ShowMessage("Selected Switch/Accessory List Format is Corrupt for item " + String(Index));
            }
        // display new item in debug window
        if (DEBUGGINGX(5)) SelectedSwAcc->Text = NewItem;
    	// find out whether the new string already exists in the Selected list
    	// If if does, we will not add it
    	if(DestLB->Items->IndexOf(NewItem) == -1) {
    	    // add the new item to the end of the list
    	    DestLB->Items->Add(NewItem);
		    }
    	SrcLB->Selected[Index] = false;	// deselect Switch/Accessory
    	} // end if
	} // end for
} // end CopySelectedList
//---------------------------------------------------------------------------

void __fastcall TRouteScene::SelectButtonClick(TObject *Sender)
{
// STEP 1 : Select Switch/Accessory
if (SwitchAccessoryListBox->SelCount == 0) {
	ShowMessage("Select at least one Acessory or Switch");
    return;
    }
//
CopySelectedList(SwitchAccessoryListBox, SWACCinSelectedRSListBox, false);	// dont' clear destination
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::UnSelectButtonClick(TObject *Sender)
{
if (SWACCinSelectedRSListBox->SelCount != 0 && SWACCinSelectedRSListBox->Items->Count != 0) {
	// Delete selected Switch/Accessories (start at end of list)
	// go from last item to item 0
	for (int Index = (SWACCinSelectedRSListBox->Items->Count-1) ; Index >= 0 ; Index--) {
	    if (SWACCinSelectedRSListBox->Selected[Index]) {
        	SWACCinSelectedRSListBox->Items->Delete(Index);
            }
		}
	} else {
    // list empty
	return;
    }
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::SwitchAccessoryListBoxClick(
      TObject *Sender)
{
	SelectedSwAcc->Text = SwitchAccessoryListBox->Items->Strings[SwitchAccessoryListBox->ItemIndex];
    int type, Chan, AIUNo, TIUNo;
	const int retv = _stscanf(SelectedSwAcc->Text.c_str(),TEXT("%*[ -~]%d,%d,%d,%d,%*d,%*d"), &type, &Chan, &AIUNo, &TIUNo);
    assert(retv == 4); 	// make sure 4 paramters were found
    assert(Chan <= CHANMAX);	// check the limits on the four parameters
    assert(type <= TYPEMAX);
    assert(TIUNo <= TIUMAX);
    assert(AIUNo <= AIUMAX);
    TIUNumber->Caption = "TIU " + String(TIUNo);
    AIUNumber->Caption = "AIU " + String(AIUNo);
    if (type == TYPEACCY) {
	    ChannelNumber->Caption = "Accy Channel " + String(Chan);
        } else {
	    ChannelNumber->Caption = "Sw Channel " + String(Chan);
        }
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::SWACCinSelectedRSListBoxDrawItem(
      TWinControl *Control, int Index, TRect &Rect, TOwnerDrawState State)
{
// NOTE: this DrawItem routine is used by two different ListBoxes
int HOffset = 2;   // default text offset width
int VOffset = 2;	// default ICON offset vertical
// note that we draw on the listbox’s canvas, not on the form
TCanvas *pCanvas = ((TListBox *)Control)->Canvas;
pCanvas->FillRect(Rect); // clear the rectangle

Graphics::TBitmap * pBitmap = new Graphics::TBitmap;
assert(pBitmap != NULL);	// make sure constructor worked
const int count =  MainForm->OnOffImageList->Count;
assert(count == 4);

int type, Setting;
const int retv = _stscanf(((TListBox *)Control)->Items->Strings[Index].c_str(),TEXT("%*[ -~]%d,%*d,%*d,%*d,%*d,%d"), &type, &Setting);
assert(retv == 2);

if (type == 0)	// Accessory
	MainForm->OnOffImageList->GetBitmap(IMAGE_OFF + Setting, pBitmap);
	else	// type == 1  Switch
    MainForm->OnOffImageList->GetBitmap(IMAGE_DIVERGE + Setting, pBitmap);

if (pBitmap) {
    pCanvas->BrushCopy(Bounds(Rect.Left + HOffset, Rect.Top + VOffset, pBitmap->Width, pBitmap->Height), pBitmap, Bounds(0, 0, pBitmap->Width, pBitmap->Height), clRed); // render bitmap
    HOffset += pBitmap->Width + 4;   // add four pixels between bitmap and text
	}
// display the text
pCanvas->TextOut(Rect.Left + HOffset, Rect.Top, ((TListBox *)Control)->Items->Strings[Index]);
delete pBitmap;	// free the memory
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::SWACCinSelectedRSListBoxClick(
      TObject *Sender)
{
	SelectedSwAcc->Text = SWACCinSelectedRSListBox->Items->Strings[SWACCinSelectedRSListBox->ItemIndex];
    int type, Chan, AIUNo, TIUNo;
	const int retv = _stscanf(SelectedSwAcc->Text.c_str(),TEXT("%*[ -~]%d,%d,%d,%d,%*d,%*d"), &type, &Chan, &AIUNo, &TIUNo);
	assert(retv == 4); 	// make sure 4 paramters were found
    assert(Chan <= CHANMAX);	// check the limits on the four parameters
    assert(type <= TYPEMAX);
    assert(TIUNo <= TIUMAX);
    assert(AIUNo <= AIUMAX);
    TIUNumber->Caption = "TIU " + String(TIUNo);
    AIUNumber->Caption = "AIU " + String(AIUNo);
    if (type == TYPEACCY) {
	    ChannelNumber->Caption = "Accy Channel " + String(Chan);
        } else {
	    ChannelNumber->Caption = "Sw Channel " + String(Chan);
        }
    }
//---------------------------------------------------------------------------

void __fastcall TRouteScene::SWACCinSelectedRSListBoxDblClick(
      TObject *Sender)
{
// Toggle the setting
const int Index = SWACCinSelectedRSListBox->ItemIndex;
// get last character
String Str;
Str = SWACCinSelectedRSListBox->Items->Strings[Index];
int Len = Str.Length();
String Setting = Str.SubString(Len, 1);
// toggle last character and put it back into the string
Setting = (Setting == "1") ? "0" : "1";
Str = Str.Delete(Len, 1);	// remove last character
Len = Str.Length();
Str.Insert(Setting, Len+1);	// Add new last character
SWACCinSelectedRSListBox->Items->Strings[Index] = Str;
// redraw the list
SWACCinSelectedRSListBox->Invalidate();
SWACCinSelectedRSListBoxClick(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::RSResetButtonClick(TObject *Sender)
{
// return all Switches/Accessories in Route/Scene to their default positions
if (RSData->Cells[RS_NAME][RSNo] == "*") {
	ShowMessage("Select a Route/Scene before trying to Reset it");
    return;
    }
// if Port A is not connected fail
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }

const TCursor OldCursor = Screen->Cursor; 	// save cursor
Screen->Cursor = crHourGlass;

char RSNum[10];
snprintf(RSNum, sizeof RSNum, "%03d", RSNo);
const String FileName = String(MainForm->cStartDir) + "\\" + "SwitchAccessory" + String(RSNum) + ".ini";
if (!FileExists(FileName)) {
	ShowMessage("Route/Scene File does not Exist: " + FileName);
    return;
    }
// start the route/scene thread - FreeOnTerminate is set true so we don't need to
// worry about deleting the Thread
if (DEBUGGING) DB->Debug_Print("Starting Route/Scene Thread for : " + FileName);

(void) new TRouteSceneThread(false, this, Handle, RemoteNo, FileName, RSNo, RSRESET);

Screen->Cursor = OldCursor;
return;
}

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

void __fastcall TRouteScene::RSActivateButtonClick(TObject *Sender)
{
// set all Switches/Accessories in Route/Scene to their Setting positions
if (RSData->Cells[RS_NAME][RSNo] == "*") {
	ShowMessage("Select a Route/Scene before trying to Activate it");
    return;
    }
// if Port A is not connected fail
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }
    
const TCursor OldCursor = Screen->Cursor; 	// save cursor
Screen->Cursor = crHourGlass;

char RSNum[10];
snprintf(RSNum, sizeof RSNum, "%03d", RSNo);
const String FileName = String(MainForm->cStartDir) + "\\" + "SwitchAccessory" + String(RSNum) + ".ini";
if (!FileExists(FileName)) {
	ShowMessage("Route/Scene File does not Exist: " + FileName);
    return;
    }
// start the route/scene thread - FreeOnTerminate is set true so we don't need to
// worry about deleting the Thread
if (DEBUGGING) DB->Debug_Print("Starting Route/Scene Thread for : " + FileName);

(void) new TRouteSceneThread(false, this, Handle, RemoteNo, FileName, RSNo, RSACTIVATE);

Screen->Cursor = OldCursor;
return;
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::Help1Click(TObject *Sender)
{
// Help window - I use the IDE defined pointer so this Form will be
// automatically de-allocated when the program is terminated.
if (RTCRSHelp == NULL) {
	RTCRSHelp = new TRTCRSHelp(Application);
	assert(RTCRSHelp != NULL);		// make sure that construction worked
    }
RTCRSHelp->Show();		// show Help window
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::RSDataKeyDown(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
// if ENTER is pressed, Activate the Route/Scene if it's present
if (Key == VK_RETURN) {
	if (RSData->Cells[RS_NAME][RSNo] != "*") {
		RSActivateButtonClick(RSActivateButton);
   		} else {
	    // create a new Route/Scene
	    CreateRSClick(CreateRS);
	    }
    }
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::RSDataDblClick(TObject *Sender)
{
// Activate the Route/Scene or if field is empty, Edit Route/Scene Name
if (!MainForm->IsTIUConnected()){
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }
// double clicking in column header or row header is ignored
if (RSData->Col == RS_NUM || RSData->Row == RS_LABEL_ROW) return;

// Route/Scene Name column double clicked
if (RSData->Col == RS_NAME) {
	if (RSData->Cells[RS_NAME][RSData->Row] == "*") {
	    CreateRSClick(CreateRS);  // Create New Route/Scene name
        return;
    	}
    }
// otherwise, activate the Route/Scene
RSActivateButtonClick(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::RSUpDownClick(TObject *Sender,
      TUDBtnType Button)
{
// A bug in BCB4.0 - when this routine is called, the value of Position has
// already changed. Correct operation in CB10.1 "use the OnClick event handler
//to respond to changes just before the value of Position is changed."
if (Button == Comctrls::btPrev) {
    if (RSData->Row < (RSUPDOWNLIMIT - 1))
    	RSData->Row = RSUPDOWNLIMIT - RSUpDown->Position + UPDOWNBUGFIX;
	}
if (Button == Comctrls::btNext) {
    if (RSData->Row > 1 )
    	RSData->Row = RSUPDOWNLIMIT - RSUpDown->Position - UPDOWNBUGFIX;
	}
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::RestoreScrollBarsforRouteScenelist1Click(
      TObject *Sender)
{
RSData->ScrollBars = ssVertical;	
}
//---------------------------------------------------------------------------
// Workaround for SetLayeredWindowAttributes() to make transparent windows
extern lpfnSetLayeredWindowAttributes m_pSetLayeredWindowAttributes;
//---------------------------------------------------------------------------

void __fastcall TRouteScene::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()) {
    RSActivateButton->Enabled = true;
    RSResetButton->Enabled = true;
    AllSwNormalButton->Enabled = true;
    AllAccyOnButton->Enabled = true;
    AllAccyOffButton->Enabled = true;
	} else {
    RSActivateButton->Enabled = false;
    RSResetButton->Enabled = false;
    AllSwNormalButton->Enabled = false;
    AllAccyOnButton->Enabled = false;
    AllAccyOffButton->Enabled = false;
    }
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::FormDeactivate(TObject *Sender)
{
if (Setup->TransparentSpin->Value == Setup->TransparentSpin->MaxValue) return;	// Off
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 TRouteScene::RS_Controls_LabelClick(TObject *Sender)
{
LocalTransparencyOverride = !LocalTransparencyOverride;	// Toggle transparency locally
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::FormShow(TObject *Sender)
{
//
// Configuration
//
if ((MainForm->M != NULL) && lua_getglobal(MainForm->M, "Route_Scene_List_Title") == LUA_TSTRING) {
    Route_Scene_List_Label->Caption = lua_tolstring(MainForm->M, -1, NULL);
    }
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::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 TRouteScene::AllSwNormalButtonClick(TObject *Sender)
{
// if Port A is not connected fail
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }
const TCursor OldCursor = Screen->Cursor; 	// save cursor
Screen->Cursor = crHourGlass;

char RSNum[10];
snprintf(RSNum, sizeof RSNum, "%03d", RSNo);
const String FileName = String(MainForm->cStartDir) + "\\" + "SwitchAccessory" + String(RSNum) + ".ini";
if (!FileExists(FileName)) {
	ShowMessage("Route/Scene File does not Exist: " + FileName);
    return;
    }

// start the route/scene thread - FreeOnTerminate is set true so we don't need to
// worry about deleting the Thread
if (DEBUGGING) DB->Debug_Print("Starting Route/Scene Thread for All Switches Normal");

(void) new TRouteSceneThread(false, this, Handle, RemoteNo, FileName, RSNo, SETALLSWNORMAL);

Screen->Cursor = OldCursor;
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::AllAccyOnButtonClick(TObject *Sender)
{
// if Port A is not connected fail
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }
const TCursor OldCursor = Screen->Cursor; 	// save cursor
Screen->Cursor = crHourGlass;

char RSNum[10];
snprintf(RSNum, sizeof RSNum, "%03d", RSNo);
const String FileName = String(MainForm->cStartDir) + "\\" + "SwitchAccessory" + String(RSNum) + ".ini";
if (!FileExists(FileName)) {
	ShowMessage("Route/Scene File does not Exist: " + FileName);
    return;
    }

// start the route/scene thread - FreeOnTerminate is set true so we don't need to
// worry about deleting the Thread
if (DEBUGGING) DB->Debug_Print("Starting Route/Scene Thread for All Accy ON");

(void) new TRouteSceneThread(false, this, Handle, RemoteNo, FileName, RSNo, SETALLACCYON);

Screen->Cursor = OldCursor;    
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::AllAccyOffButtonClick(TObject *Sender)
{
// if Port A is not connected fail
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }
const TCursor OldCursor = Screen->Cursor; 	// save cursor
Screen->Cursor = crHourGlass;

char RSNum[10];
snprintf(RSNum, sizeof RSNum, "%03d", RSNo);
const String FileName = String(MainForm->cStartDir) + "\\" + "SwitchAccessory" + String(RSNum) + ".ini";
if (!FileExists(FileName)) {
	ShowMessage("Route/Scene File does not Exist: " + FileName);
    return;
    }
    
// start the route/scene thread - FreeOnTerminate is set true so we don't need to
// worry about deleting the Thread
if (DEBUGGING) DB->Debug_Print("Starting Route/Scene Thread for All Accy OFF");

(void) new TRouteSceneThread(false, this, Handle, RemoteNo, FileName, RSNo, SETALLACCYOFF);

Screen->Cursor = OldCursor;
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::SaveEditsButtonClick(TObject *Sender)
{
// save edits to Route/Scene List
SaveRSData(RSData);
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::OpenPopoutRouteSceneList1Click(
      TObject *Sender)
{
// Open the Route/Scene Popout
PopoutRouteSceneList->RefreshClick(NULL);
//if (PopoutRouteSceneList == NULL) return;
//// cannot assign a TStringGrid to a TStringGrid : PopoutRouteSceneList->RSData->Assign(RSData);
//for (int I = 0; I < PopoutRouteSceneList->RSData->ColCount; I++)
//    PopoutRouteSceneList->RSData->Cols[I]->Assign(RSData->Cols[I]);
//PopoutRouteSceneList->RSData->Row = RSData->Row;
//PopoutRouteSceneList->RSData->Col = RSData->Col;
//PopoutRouteSceneList->Route_Scene_List_Label->Caption = Route_Scene_List_Label->Caption;
PopoutRouteSceneList->Visible = true;
}
//---------------------------------------------------------------------------

void __fastcall TRouteScene::RSDataMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
int ARow, ACol;
if (Button == mbRight || Button == mbMiddle) {
	RSData->MouseToCell(X, Y, ACol, ARow);
    RSData->Row = ARow;
	RSData->Col = ACol;
    }

RSDataClick(Sender);

// Popup the PopupMenu
if (Button == mbRight) {
	RSData->PopupMenu->Popup(Left + X, Top + Y);
    }
// Pop Out the Engine List
if (Button == mbMiddle) {
    PopoutRouteSceneList->RefreshClick(NULL);
    //// cannot assign a TStringGrid to a TStringGrid : PopOutRouteSceneList->RSData->Assign(RSData);
    //for (int I = 0; I < PopoutRouteSceneList->RSData->ColCount; I++)
    //    PopoutRouteSceneList->RSData->Cols[I]->Assign(RSData->Cols[I]);
    //PopoutRouteSceneList->RSData->Row = ARow;
	//PopoutRouteSceneList->RSData->Col = ACol;
    //PopoutRouteSceneList->Route_Scene_List_Label->Caption = Route_Scene_List_Label->Caption;
	PopoutRouteSceneList->Visible = true;
    }
}
//---------------------------------------------------------------------------


