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

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "Spin"
#pragma resource "*.dfm"
TForm1 *Form1;

//////////////////////////////////////////////////////////////////////////////////////////////
// Critical Section class
//
clsCritical::clsCritical(CRITICAL_SECTION *cs, bool createUnlocked, bool lockRecursively)
{
	m_objpCS = cs;
	m_dwLocked = -1;
	m_bDoRecursive = lockRecursively;
	m_dwOwnerThread = GetCurrentThreadId();

	if(!createUnlocked)
		Enter();
}	

clsCritical::~clsCritical()
{
	int iFail = (int)0x80000000;

	while(m_dwLocked >= 0)
		if(Leave() == iFail) 
			break;
}

int clsCritical::Enter()
{
	if(m_dwOwnerThread != GetCurrentThreadId())
		throw "class clsCritical: Thread cross-over error. ";

	try
	{
		if(m_bDoRecursive || (m_dwLocked == -1))
		{
			EnterCriticalSection(m_objpCS);
			InterlockedIncrement(&m_dwLocked);
		}
		return m_dwLocked;
	}
	catch(...)
	{	
		return 0x80000000;
	}
}

int clsCritical::Leave()
{
	if(m_dwOwnerThread != GetCurrentThreadId())
		throw "class clsCritical: Thread cross-over error. ";

	try
	{
		if(m_dwLocked >= 0)
		{
			LeaveCriticalSection(m_objpCS);
			InterlockedDecrement(&m_dwLocked);
			return m_dwLocked;
		}
		return -1;
	}
	catch(...)
	{
		return 0x80000000;
	}
}

bool clsCritical::IsLocked()
{
	return (m_dwLocked > -1);
}

int clsCritical::GetRecursionCount()
{
	return m_dwLocked;
}

//////////////////////////////////////////////////////////////////////////////////////////////
// MessageStatus class
//
MessageStatus::MessageStatus(TPCANMsg canMsg, TPCANTimestamp canTimestamp, int listIndex)
{
    m_Msg = canMsg;
    m_TimeStamp = canTimestamp;
    m_oldTimeStamp = canTimestamp;
    m_iIndex = listIndex;
	m_Count = 1;
	m_bShowPeriod = true;
	m_bWasChanged = false;
}

void MessageStatus::Update(TPCANMsg canMsg, TPCANTimestamp canTimestamp)
{
	m_Msg = canMsg;
	m_oldTimeStamp = m_TimeStamp;
	m_TimeStamp = canTimestamp;
	m_bWasChanged = true;
	m_Count += 1;
}

AnsiString MessageStatus::GetTypeString()
{
	AnsiString strTemp;

	// Add the new ListView Item with the type of the message
	//
	if((m_Msg.MSGTYPE & PCAN_MESSAGE_EXTENDED) != 0)
		strTemp = "EXTENDED";
	else
		strTemp = "STANDARD";

	if((m_Msg.MSGTYPE & PCAN_MESSAGE_RTR) == PCAN_MESSAGE_RTR)
		strTemp = (strTemp + "/RTR");

	return strTemp;
}

AnsiString MessageStatus::GetIdString()
{
	// We format the ID of the message and show it
	//
	if((m_Msg.MSGTYPE & PCAN_MESSAGE_EXTENDED) != 0)
		return IntToHex((int)m_Msg.ID,8) + "h";
	else
		return IntToHex((int)m_Msg.ID,3) + "h";
}

AnsiString MessageStatus::GetDataString()
{
	AnsiString strTemp;

	strTemp = "";

	// We format the data of the message. Each data is one
	// byte of Hexadecimal data
	//
	if((m_Msg.MSGTYPE & PCAN_MESSAGE_RTR) == PCAN_MESSAGE_RTR)
		return "Remote Request";
	else
		for(int i=0; i< m_Msg.LEN; i++)
			strTemp = (strTemp + IntToHex(m_Msg.DATA[i],2) + " ");

	return strTemp;
}

AnsiString MessageStatus::GetTimeString()
{
	double fTime;

	// Update Rcv Time information
	//
	fTime = m_TimeStamp.millis + (m_TimeStamp.micros / 1000.0);
	if(m_bShowPeriod)
		fTime -= (m_oldTimeStamp.millis + (m_oldTimeStamp.micros / 1000.0));
	return AnsiString::Format("%.1f", ARRAYOFCONST((fTime)));
}

void MessageStatus::SetShowingPeriod(bool value)
{
    if (m_bShowPeriod ^ value)
    {
		m_bShowPeriod = value;
		m_bWasChanged = true;
	}
}
//////////////////////////////////////////////////////////////////////////////////////////////

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
	// Initialize all
	//
	InitializeControls();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
	// Release Hardware if needed
	//
	if(btnRelease->Enabled)
		btnRelease->Click();

	// Close the event
	CloseHandle(m_hEvent);
			
	//Free Ressources
	//
	delete m_objPCANBasic;

	// (Protected environment)
	//
	{
		clsCritical locker(m_objpCS);
		
		while(m_LastMsgsList->Count)
		{
			delete m_LastMsgsList->First();
			m_LastMsgsList->Delete(0);
		}
		delete m_LastMsgsList;
	}

	// Uninitialize the Critical Section
	//
	FinalizeProtection();
}
//---------------------------------------------------------------------------

void TForm1::InitializeControls(void)
{
	// Initialize the Critical Section
	//
	InitializeProtection();

	// Creates an array with all possible PCAN-Channels
	//
	m_HandlesArray[0] = PCAN_ISABUS1;
	m_HandlesArray[1] = PCAN_ISABUS2;
	m_HandlesArray[2] = PCAN_ISABUS3;
	m_HandlesArray[3] = PCAN_ISABUS4;
	m_HandlesArray[4] = PCAN_ISABUS5;
	m_HandlesArray[5] = PCAN_ISABUS6;
	m_HandlesArray[6] = PCAN_ISABUS7;
	m_HandlesArray[7] = PCAN_ISABUS8;
	m_HandlesArray[8] = PCAN_DNGBUS1;
	m_HandlesArray[9] = PCAN_PCIBUS1;
	m_HandlesArray[10] = PCAN_PCIBUS2;
	m_HandlesArray[11] = PCAN_PCIBUS3;
	m_HandlesArray[12] = PCAN_PCIBUS4;
	m_HandlesArray[13] = PCAN_PCIBUS5;
	m_HandlesArray[14] = PCAN_PCIBUS6;
	m_HandlesArray[15] = PCAN_PCIBUS7;
	m_HandlesArray[16] = PCAN_PCIBUS8;
	m_HandlesArray[17] = PCAN_USBBUS1;
	m_HandlesArray[18] = PCAN_USBBUS2;
	m_HandlesArray[19] = PCAN_USBBUS3;
	m_HandlesArray[20] = PCAN_USBBUS4;
	m_HandlesArray[21] = PCAN_USBBUS5;
	m_HandlesArray[22] = PCAN_USBBUS6;
	m_HandlesArray[23] = PCAN_USBBUS7;
	m_HandlesArray[24] = PCAN_USBBUS8;
	m_HandlesArray[25] = PCAN_PCCBUS1;
	m_HandlesArray[26] = PCAN_PCCBUS2;

	// We set the variable for the current
	// PCAN Basic Class instance to use it
	//
	m_objPCANBasic = new PCANBasicClass();

	// Create a list to store the displayed messages
	//
	m_LastMsgsList = new TList();

	// Create Event to use Received-event
	//
	m_hEvent = CreateEvent(NULL, FALSE, FALSE, "");

	// Set the Read-thread variable to null
	//
	m_hThread = NULL;

	// We set the variable to know which reading mode is
	// currently selected (Timer by default)
	//
	m_ActiveReadingMode = 0;

	// Prepares the PCAN-Basic's debug-Log file
	//
	FillComboBoxData();

	// Init log parameters
	ConfigureLogFile();

	// Set default connection status
	//
	SetConnectionStatus(false);
}
//---------------------------------------------------------------------------
void TForm1::InitializeProtection()
{
	m_objpCS = new CRITICAL_SECTION();
	InitializeCriticalSection(m_objpCS);
}

//---------------------------------------------------------------------------
void TForm1::FinalizeProtection()
{
	try
	{
		DeleteCriticalSection(m_objpCS);
		delete m_objpCS;
		m_objpCS = NULL;
	}
	catch(...)
	{
		throw;
	}
}
//---------------------------------------------------------------------------

void TForm1::FillComboBoxData()
{
	// Channels will be check
	//
	btnHwRefresh->Click();

	// TPCANBaudrate
	//
	cbbBaudrates->ItemIndex =2; // 500 K
	cbbBaudratesChange(cbbBaudrates);

	// Hardware Type for no plugAndplay hardware
	//
	cbbHwType->ItemIndex = 0;
	cbbHwTypeChange(cbbHwType);

	// Interrupt for no plugAndplay hardware
	//
	cbbInterrupt->ItemIndex = 0;

	// IO Port for no plugAndplay hardware
	//
	cbbIO->ItemIndex = 0;

	// Parameters for GetValue and SetValue function calls
	//
	cbbParameter->ItemIndex = 0;
	cbbParameterChange(cbbParameter);
}
//---------------------------------------------------------------------------

AnsiString TForm1::FormatChannelName(TPCANHandle handle)
{
	AnsiString strName;
	BYTE byChannel;

	// Gets the owner device and channel for a
	// PCAN-Basic handle
	//
	byChannel = handle;
	byChannel &= 0xF;

	// Constructs the PCAN-Basic Channel name and return it
	//
	strName = GetTPCANHandleName(handle);
	return Format("%s %d (%Xh)", ARRAYOFCONST((strName, byChannel, handle)));
}
//---------------------------------------------------------------------------

AnsiString TForm1::GetTPCANHandleName(TPCANHandle handle)
{
	AnsiString result = "PCAN_NONE";

	switch(handle)
	{
		case PCAN_ISABUS1:
		case PCAN_ISABUS2:
		case PCAN_ISABUS3:
		case PCAN_ISABUS4:
		case PCAN_ISABUS5:
		case PCAN_ISABUS6:
		case PCAN_ISABUS7:
		case PCAN_ISABUS8:
			result = "PCAN_ISA";
			break;

		case PCAN_DNGBUS1:
			result = "PCAN_DNG";
			break;

		case PCAN_PCIBUS1:
		case PCAN_PCIBUS2:
		case PCAN_PCIBUS3:
		case PCAN_PCIBUS4:
		case PCAN_PCIBUS5:
		case PCAN_PCIBUS6:
		case PCAN_PCIBUS7:
		case PCAN_PCIBUS8:
			result = "PCAN_PCI";
			break;

		case PCAN_USBBUS1:
		case PCAN_USBBUS2:
		case PCAN_USBBUS3:
		case PCAN_USBBUS4:
		case PCAN_USBBUS5:
		case PCAN_USBBUS6:
		case PCAN_USBBUS7:
		case PCAN_USBBUS8:
			result = "PCAN_USB";
			break;

		case PCAN_PCCBUS1:
		case PCAN_PCCBUS2:
			result = "PCAN_PCC";
			break;
	}
	return result;
}
//---------------------------------------------------------------------------

void TForm1::ConfigureLogFile()
{
	int iBuffer;

	// Sets the mask to catch all events
	//
	iBuffer = LOG_FUNCTION_ENTRY | LOG_FUNCTION_LEAVE | LOG_FUNCTION_PARAMETERS |
		LOG_FUNCTION_READ | LOG_FUNCTION_WRITE;

	// Configures the log file.
	// NOTE: The Log capability is to be used with the NONEBUS Handle. Other handle than this will
	// cause the function fail.
	//
	m_objPCANBasic->SetValue(PCAN_NONEBUS, PCAN_LOG_CONFIGURE, (void*)&iBuffer, sizeof(iBuffer));
}
//---------------------------------------------------------------------------

void TForm1::SetConnectionStatus(bool bConnected)
{
	// Buttons
	//
	btnInit->Enabled = !bConnected;
	btnHwRefresh->Enabled = !bConnected;
	btnRelease->Enabled = bConnected;
	btnFilterApply->Enabled = bConnected;
	btnFilterQuery->Enabled = bConnected;
	btnWrite->Enabled = bConnected;
	btnRead->Enabled = (bConnected && rdbManual->Checked);
	btnParameterGet->Enabled = bConnected;
	btnParameterSet->Enabled = bConnected;
	btnGetVersions->Enabled = bConnected;
	btnStatus->Enabled = bConnected;
	btnReset->Enabled = bConnected;

	// ComboBoxs
	//
	cbbBaudrates->Enabled = !bConnected;
	cbbChannel->Enabled = !bConnected;
	cbbHwType->Enabled = !bConnected;
	cbbIO->Enabled = !bConnected;
	cbbInterrupt->Enabled = !bConnected;

	// Hardware configuration and read mode
	//
	if (!bConnected)
		cbbChannelChange(cbbChannel);
	else
		ReadingModeChanged();

	// Display messages in grid
	//
	tmrDisplay->Enabled = bConnected;
}
//---------------------------------------------------------------------------

AnsiString TForm1::GetFormatedError(TPCANStatus error)
{
	TPCANStatus status;
	char buffer[256];
	AnsiString result;

	memset(buffer,'\0',256);
	// Gets the text using the GetErrorText API function
	// If the function success, the translated error is returned. If it fails,
	// a text describing the current error is returned.
	//
	status = m_objPCANBasic->GetErrorText(error, 0, buffer);
	if(status != PCAN_ERROR_OK)
		result = Format("An error ocurred. Error-code's text (%Xh) couldn't be retrieved", ARRAYOFCONST((error)));
	else
		result = buffer;
	return result;
}
//---------------------------------------------------------------------------

void TForm1::IncludeTextMessage(AnsiString strMsg)
{
	// Inserts a message in the Info box and focuses it
	//
	lbxInfo->Items->Add(strMsg);
	lbxInfo->ItemIndex = lbxInfo->Items->Count-1;
}
//---------------------------------------------------------------------------

bool TForm1::GetFilterStatus(int* status)
{
	TPCANStatus stsResult;

	// Tries to get the status of the filter for the current connected hardware
	//
	stsResult = m_objPCANBasic->GetValue(m_PcanHandle, PCAN_MESSAGE_FILTER, (void*)status, sizeof(int));

	// If it fails, a error message is shown
	//
	if (stsResult != PCAN_ERROR_OK)
	{
		::MessageBox(NULL, GetFormatedError(stsResult).c_str(), "Error!",MB_ICONERROR);
		return false;
	}
	return true;
}
//---------------------------------------------------------------------------

void TForm1::InsertMsgEntry(TPCANMsg NewMsg, TPCANTimestamp timeStamp)
{
	MessageStatus *msgStsCurrentMsg;
	TListItem *CurrentItem;

	// (Protected environment)
	//
	{
		clsCritical locker(m_objpCS);

		// We add this status in the last message list
		//
		msgStsCurrentMsg = new MessageStatus(NewMsg, timeStamp, lstMessages->Items->Count);
		m_LastMsgsList->Add(msgStsCurrentMsg);

		// Add the new ListView Item with the Type of the message
		//
		CurrentItem = lstMessages->Items->Add();
		CurrentItem->Caption = msgStsCurrentMsg->TypeString;

		// We set the ID of the message
		//
		CurrentItem->SubItems->Add(msgStsCurrentMsg->IdString);
		// We set the length of the Message
		//
		CurrentItem->SubItems->Add(IntToStr(NewMsg.LEN));
		// We set the data of the message.
		//
		CurrentItem->SubItems->Add(msgStsCurrentMsg->DataString);
		// we set the message count message (this is the First, so count is 1)
		//
		CurrentItem->SubItems->Add(IntToStr(msgStsCurrentMsg->Count));
		// Add timestamp information
		//
		CurrentItem->SubItems->Add(msgStsCurrentMsg->TimeString);
	}
}
//---------------------------------------------------------------------------

void TForm1::DisplayMessages()
{
	TListItem *CurrentItem;
	MessageStatus *msgStatus;

    // We search if a message (Same ID and Type) is 
    // already received or if this is a new message
	// (in a protected environment)
	//
	{
		clsCritical locker(m_objpCS);
		for(int i=0; i < m_LastMsgsList->Count; i++)
		{
			msgStatus = (MessageStatus*)m_LastMsgsList->Items[i];

			if (msgStatus->MarkedAsUpdated)
			{
				msgStatus->MarkedAsUpdated = false;
				CurrentItem = lstMessages->Items->Item[msgStatus->Position];

				CurrentItem->SubItems->Strings[1] = IntToStr(msgStatus->CANMsg.LEN);
				CurrentItem->SubItems->Strings[2] = msgStatus->DataString;
				CurrentItem->SubItems->Strings[3] = IntToStr(msgStatus->Count);
				CurrentItem->SubItems->Strings[4] = msgStatus->TimeString;
			}
		}
	}
}
//---------------------------------------------------------------------------

void TForm1::ReadMessage()
{

	TPCANMsg CANMsg;
	TPCANTimestamp CANTimeStamp;
	TPCANStatus stsResult;

	// We read at least one time the queue looking for messages.
	// If a message is found, we look again trying to find more.
	// If the queue is empty or an error occurr, we get out from
	// the dowhile statement.
	//
	do
	{
		// We execute the "Read" function of the PCANBasic
		//
		stsResult = m_objPCANBasic->Read(m_PcanHandle, &CANMsg, &CANTimeStamp);

		// A message was received
		// We process the message(s)
		//
		if (stsResult == PCAN_ERROR_OK)
			ProcessMessage(CANMsg, CANTimeStamp);
	} while (btnRelease->Enabled && (!(stsResult & PCAN_ERROR_QRCVEMPTY)));
}
//---------------------------------------------------------------------------

void TForm1::ProcessMessage(TPCANMsg theMsg, TPCANTimestamp itsTimeStamp)
{
	MessageStatus *msg;

    // We search if a message (Same ID and Type) is 
    // already received or if this is a new message
	// (in a protected environment)
	//
	{
		clsCritical locker(m_objpCS);
		for(int i=0; i < m_LastMsgsList->Count; i++)
		{
			msg = (MessageStatus*)m_LastMsgsList->Items[i];
			if((msg->CANMsg.ID == theMsg.ID) && (msg->CANMsg.MSGTYPE == theMsg.MSGTYPE))
			{
				// Modify the message and exit
				//
				msg->Update(theMsg, itsTimeStamp);
				return;
			}
		}
		// Message not found. It will created
		//
		InsertMsgEntry(theMsg, itsTimeStamp);
	}
}
//---------------------------------------------------------------------------

void TForm1::ReadingModeChanged()
{
	if (!btnRelease->Enabled)
		return;

	switch(m_ActiveReadingMode)
	{
		case 0: 	// If active reading mode is By Timer
			// Terminate Read Thread if it exists
			//
			if(m_hThread != NULL)
			{
				TerminateThread(m_hThread, -1000);
				m_hThread = NULL;
			}
			// We start to read
			//
			tmrRead->Enabled = true;
			break;
		case 1: 	// If active reading mode is By Event
			// We stop to read from the CAN queue
			//
			tmrRead->Enabled = false;

			// Create Reading Thread ....
			//
			m_hThread = CreateThread(NULL, NULL, TForm1::CallCANReadThreadFunc, (LPVOID)this, NULL, NULL);

			if(m_hThread == NULL)
				::MessageBox(NULL,"Create CANRead-Thread failed","Error!",MB_ICONERROR);
			break;
		default:	// If active reading mode is Manual
			// Terminate Read Thread if it exists
			//
			if(m_hThread != NULL)
			{
				TerminateThread(m_hThread, -1000);
				m_hThread = NULL;
			}
			// We enable the button for read
			//
			tmrRead->Enabled = false;
			btnRead->Enabled = (btnRelease->Enabled && rdbManual->Checked);
			break;
	}
}
//---------------------------------------------------------------------------

DWORD WINAPI TForm1::CallCANReadThreadFunc(LPVOID lpParam)
{
	// Cast lpParam argument to PCANBasicExampleDlg*
	//
	TForm1* dialog = (TForm1*)lpParam;

	// Call TForm1 Thread member function
	//
	return dialog->CANReadThreadFunc();
}
//---------------------------------------------------------------------------

DWORD TForm1::CANReadThreadFunc()
{
	TPCANStatus stsResult;
	DWORD result, dwTemp;

	// Sets the handle of the Receive-Event.
	//
	stsResult = m_objPCANBasic->SetValue(m_PcanHandle, PCAN_RECEIVE_EVENT ,&m_hEvent, sizeof(m_hEvent));

	// If it fails, a error message is shown
	//
	if (stsResult != PCAN_ERROR_OK)
	{
		::MessageBox(NULL, GetFormatedError(stsResult).c_str(), "Error!",MB_ICONERROR);
		return 1;
	}

	// While this mode is selected
	//
	while(rdbEvent->Checked)
	{
		//Wait for CAN Data...
		result = WaitForSingleObject(m_hEvent, INFINITE);

		if (result == WAIT_OBJECT_0)
			ReadMessage();
	}

	// Resets the Event-handle configuration
	//
	dwTemp = 0;
	m_objPCANBasic->SetValue(m_PcanHandle, PCAN_RECEIVE_EVENT ,&dwTemp, sizeof(dwTemp));

	return 0;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::tmrReadTimer(TObject *Sender)
{
	ReadMessage();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnHwRefreshClick(TObject *Sender)
{
	int iBuffer;
	TPCANStatus stsResult;

	// Clears the Channel combioBox and fill it againa with 
	// the PCAN-Basic handles for no-Plug&Play hardware and
	// the detected Plug&Play hardware
	//
	cbbChannel->Items->Clear();
	for (int i = 0; i < (int)(sizeof(m_HandlesArray) /sizeof(TPCANHandle)) ; i++)
	{
		// Includes all no-Plug&Play Handles
		//
		if (m_HandlesArray[i] <= PCAN_DNGBUS1)
			cbbChannel->Items->Add(FormatChannelName(m_HandlesArray[i]));
		else
		{
			// Checks for a Plug&Play Handle and, according with the return value, includes it
			// into the list of available hardware channels.
			//
			stsResult = m_objPCANBasic->GetValue((TPCANHandle)m_HandlesArray[i], PCAN_CHANNEL_CONDITION, (void*)&iBuffer, sizeof(iBuffer));
			if ((stsResult == PCAN_ERROR_OK) && (iBuffer == PCAN_CHANNEL_AVAILABLE))
				cbbChannel->Items->Add(FormatChannelName(m_HandlesArray[i]));
		}
	}

	// Select Last One
	//
	cbbChannel->ItemIndex = cbbChannel->Items->Count - 1;
	cbbChannelChange(cbbChannel);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnInitClick(TObject *Sender)
{
	TPCANStatus stsResult;
	int selectedIO;
	int selectedInterrupt;

	// Parse IO and Interrupt
	//
	selectedIO = StrToInt("0x"+cbbIO->Text);
	selectedInterrupt = StrToInt("0x"+cbbInterrupt->Text);

	// Connects a selected PCAN-Basic channel
	//
	stsResult = m_objPCANBasic->Initialize(m_PcanHandle, m_Baudrate, m_HwType, selectedIO, selectedInterrupt);

	if (stsResult != PCAN_ERROR_OK)
		::MessageBox(NULL, GetFormatedError(stsResult).c_str(), "Error!",MB_ICONERROR);

	// Sets the connection status of the main-form
	//
	SetConnectionStatus(stsResult == PCAN_ERROR_OK);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnReleaseClick(TObject *Sender)
{
	// Terminate Read Thread if it exists
	//
	if(m_hThread != NULL)
	{
		TerminateThread(m_hThread, 1000);
		m_hThread = NULL;
	}

	// We stop to read from the CAN queue
	//
	tmrRead->Enabled = false;

	// Releases a current connected PCAN-Basic channel
	//
	m_objPCANBasic->Uninitialize(m_PcanHandle);

	// Sets the connection status of the main-form
	//
	SetConnectionStatus(false);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnFilterApplyClick(TObject *Sender)
{
	int iBuffer;
	AnsiString info;
	TPCANStatus stsResult;

	// Gets the current status of the message filter
	//
	if (!GetFilterStatus(&iBuffer))
		return;

	// Configures the message filter for a custom range of messages
	//
	if (rdbFilterCustom->Checked)
	{
		// The filter must be first closed in order to customize it
		//
		if (iBuffer != PCAN_FILTER_OPEN)
		{
			// Sets the custom filter
			//
			stsResult = m_objPCANBasic->FilterMessages(m_PcanHandle, StrToInt("0x"+txtIdFrom->Text), StrToInt("0x"+txtIdTo->Text), chbFilterExt->Checked ? PCAN_MODE_EXTENDED : PCAN_MODE_STANDARD);
			// If success, an information message is written, if it is not, an error message is shown
			//
			if (stsResult == PCAN_ERROR_OK)
			{
				info = Format("The filter was customized. IDs from {0x%s} to {0x%s} will be received", ARRAYOFCONST((txtIdFrom->Text,txtIdTo->Text)));
				IncludeTextMessage(info);
			}
			else
				::MessageBox(NULL, GetFormatedError(stsResult).c_str(), "Error!",MB_ICONERROR);
		}
		else
			::MessageBox(NULL, "The filter must be closed first in order to be able to customize it", "Attention!",MB_ICONWARNING);

		return;
	}

	// The filter will be full opened or complete closed
	//
	if (rdbFilterClose->Checked)
		iBuffer = PCAN_FILTER_CLOSE;
	else
		iBuffer = PCAN_FILTER_OPEN;

	// The filter is configured
	//
	stsResult = m_objPCANBasic->SetValue(m_PcanHandle, PCAN_MESSAGE_FILTER, (void*)&iBuffer, sizeof(int));

	// If success, an information message is written, if it is not, an error message is shown
	//
	if (stsResult == PCAN_ERROR_OK)
	{
		info = Format("The filter was successfully %s", ARRAYOFCONST((rdbFilterClose->Checked ? "closed." : "opened.")));
		IncludeTextMessage(info);
	}
	else
		::MessageBox(NULL, GetFormatedError(stsResult).c_str(), "Error!",MB_ICONERROR);

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

void __fastcall TForm1::btnFilterQueryClick(TObject *Sender)
{
	int iBuffer;

	// Queries the current status of the message filter
	//
	if (GetFilterStatus(&iBuffer))
	{
		switch(iBuffer)
		{
			// The filter is closed
			//
		case PCAN_FILTER_CLOSE:
			IncludeTextMessage("The Status of the filter is: closed.");
			break;
			// The filter is fully opened
			//
		case PCAN_FILTER_OPEN:
			IncludeTextMessage("The Status of the filter is: full opened.");
			break;
			// The filter is customized
			//
		case PCAN_FILTER_CUSTOM:
			IncludeTextMessage("The Status of the filter is: customized.");
			break;
			// The status of the filter is undefined. (Should never happen)
			//
		default:
			IncludeTextMessage("The Status of the filter is: Invalid.");
			break;
		}
	}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnParameterSetClick(TObject *Sender)
{
	TPCANStatus stsResult;
	unsigned int iBuffer;
	AnsiString info;
	TCHAR szDirectory[MAX_PATH] = "";

	// Sets a PCAN-Basic parameter value
	//
	switch (cbbParameter->ItemIndex)
	{
		// The Device-Number of an USB channel will be set
		//
	case 0:
		iBuffer = StrToInt(txtDeviceId->Text);
		stsResult = m_objPCANBasic->SetValue(m_PcanHandle, PCAN_DEVICE_NUMBER, (void*)&iBuffer, sizeof(iBuffer));
		if(stsResult == PCAN_ERROR_OK)
			IncludeTextMessage("The desired Device-Number was successfully configured");
		break;

		// The 5 Volt Power feature of a PC-card or USB will be set
		//
	case 1:
		iBuffer = rdbParamActive->Checked ? PCAN_PARAMETER_ON : PCAN_PARAMETER_OFF;
		stsResult = m_objPCANBasic->SetValue(m_PcanHandle, PCAN_5VOLTS_POWER, (void*)&iBuffer, sizeof(iBuffer));
		if(stsResult == PCAN_ERROR_OK)
		{
			info = Format("The USB/PC-Card 5 power was successfully %s", ARRAYOFCONST(((iBuffer == PCAN_PARAMETER_ON) ? "activated" : "deactivated")));
			IncludeTextMessage(info);
		}
		break;

		// The feature for automatic reset on BUS-OFF will be set
		//
	case 2:
		iBuffer = rdbParamActive->Checked ? PCAN_PARAMETER_ON : PCAN_PARAMETER_OFF;
		stsResult = m_objPCANBasic->SetValue(m_PcanHandle, PCAN_BUSOFF_AUTORESET, (void*)&iBuffer, sizeof(iBuffer));
		if(stsResult == PCAN_ERROR_OK)
		{
			info = Format("The automatic-reset on BUS-OFF was successfully %s", ARRAYOFCONST(((iBuffer == PCAN_PARAMETER_ON) ? "activated" : "deactivated")));
			IncludeTextMessage(info);
		}
		break;

		// The CAN option "Listen Only" will be set
		//
	case 3:
		iBuffer = rdbParamActive->Checked ? PCAN_PARAMETER_ON : PCAN_PARAMETER_OFF;
		stsResult = m_objPCANBasic->SetValue(m_PcanHandle, PCAN_LISTEN_ONLY, (void*)&iBuffer, sizeof(iBuffer));
		if(stsResult == PCAN_ERROR_OK)
		{
			info = Format("The CAN-option Listen-Only was successfully %s", ARRAYOFCONST(((iBuffer == PCAN_PARAMETER_ON) ? "activated" : "deactivated")));
			IncludeTextMessage(info);
		}
		break;

		// The feature for logging debug-information will be set
		//
	case 4:
		iBuffer = rdbParamActive->Checked ? PCAN_PARAMETER_ON : PCAN_PARAMETER_OFF;
		stsResult = m_objPCANBasic->SetValue(PCAN_NONEBUS, PCAN_LOG_STATUS, (void*)&iBuffer, sizeof(iBuffer));
		if(stsResult == PCAN_ERROR_OK)
		{
			info = Format("The feature for logging debug information was successfully %s", ARRAYOFCONST(((iBuffer == PCAN_PARAMETER_ON) ? "activated" : "deactivated")));
			IncludeTextMessage(info);
			::GetCurrentDirectory(sizeof(szDirectory) - 1, szDirectory);
			info = Format("Log file folder: %s" , ARRAYOFCONST((szDirectory)));
			IncludeTextMessage(info);
		}
		break;

		// The current parameter is invalid
		//
	default:
		stsResult = PCAN_ERROR_UNKNOWN;
		::MessageBox(NULL, "Wrong parameter code.", "Error!",MB_ICONERROR);
		return;
	}

	// If the function fail, an error message is shown
	//
	if(stsResult != PCAN_ERROR_OK)
		::MessageBox(NULL, GetFormatedError(stsResult).c_str(), "Error!",MB_ICONERROR);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnParameterGetClick(TObject *Sender)
{
	TPCANStatus stsResult;
	unsigned int iBuffer;
	AnsiString  info;

	// Sets a PCAN-Basic parameter value
	//
	switch (cbbParameter->ItemIndex)
	{
		// The Device-Number of an USB channel will be get
		//
	case 0:
		stsResult = m_objPCANBasic->GetValue(m_PcanHandle, PCAN_DEVICE_NUMBER, (void*)&iBuffer, sizeof(iBuffer));
		if(stsResult == PCAN_ERROR_OK)
		{
			info = Format("The configured Device-Number is %d", ARRAYOFCONST((iBuffer)));
			IncludeTextMessage(info);
		}
		break;

		// The 5 Volt Power feature of a PC-card or USB will be get
		//
	case 1:
		stsResult = m_objPCANBasic->GetValue(m_PcanHandle, PCAN_5VOLTS_POWER, (void*)&iBuffer, sizeof(iBuffer));
		if(stsResult == PCAN_ERROR_OK)
		{
			info = Format("The 5-Volt Power of the USB/PC-Card is %s", ARRAYOFCONST(((iBuffer == PCAN_PARAMETER_ON) ? "activated" : "deactivated")));
			IncludeTextMessage(info);
		}
		break;

		// The feature for automatic reset on BUS-OFF will be get
		//
	case 2:
		stsResult = m_objPCANBasic->GetValue(m_PcanHandle, PCAN_BUSOFF_AUTORESET, (void*)&iBuffer, sizeof(iBuffer));
		if(stsResult == PCAN_ERROR_OK)
		{
			info = Format("The automatic-reset on BUS-OFF is %s", ARRAYOFCONST(((iBuffer == PCAN_PARAMETER_ON) ? "activated" : "deactivated")));
			IncludeTextMessage(info);
		}
		break;

		// The CAN option "Listen Only" will be get
		//
	case 3:
		stsResult = m_objPCANBasic->GetValue(m_PcanHandle, PCAN_LISTEN_ONLY, (void*)&iBuffer, sizeof(iBuffer));
		if(stsResult == PCAN_ERROR_OK)
		{
			info = Format("The CAN-option Listen-Only is %s", ARRAYOFCONST(((iBuffer == PCAN_PARAMETER_ON) ? "activated" : "deactivated")));
			IncludeTextMessage(info);
		}
		break;

		// The feature for logging debug-information will be get
		//
	case 4:
		stsResult = m_objPCANBasic->GetValue(PCAN_NONEBUS, PCAN_LOG_STATUS, (void*)&iBuffer, sizeof(iBuffer));
		if(stsResult == PCAN_ERROR_OK)
		{
			info = Format("The feature for logging debug information is %s", ARRAYOFCONST(((iBuffer == PCAN_PARAMETER_ON) ? "activated" : "deactivated")));
			IncludeTextMessage(info);
		}
		break;

		// The current parameter is invalid
		//
	default:
		::MessageBox(NULL, "Wrong parameter code.", "Error!",MB_ICONERROR);
		return;
	}

	// If the function fail, an error message is shown
	//
	if(stsResult != PCAN_ERROR_OK)
		::MessageBox(NULL, GetFormatedError(stsResult).c_str(), "Error!",MB_ICONERROR);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnReadClick(TObject *Sender)
{
	TPCANMsg CANMsg;
	TPCANTimestamp CANTimeStamp;
	TPCANStatus stsResult;

	// We execute the "Read" function of the PCANBasic
	//
	stsResult = m_objPCANBasic->Read(m_PcanHandle, &CANMsg, &CANTimeStamp);
	if (stsResult == PCAN_ERROR_OK)
		// We show the received message
		//
		ProcessMessage(CANMsg, CANTimeStamp);
	else
		// If an error occurred, an information message is included
		//
		IncludeTextMessage(GetFormatedError(stsResult));
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnMsgClearClick(TObject *Sender)
{
	// (Protected environment)
	//
	{
		clsCritical locker(m_objpCS);

		// Remove all messages
		//
		lstMessages->Items->Clear();
		while(m_LastMsgsList->Count)
		{
			delete m_LastMsgsList->First();
			m_LastMsgsList->Delete(0);
		}
	}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnWriteClick(TObject *Sender)
{
	TPCANMsg CANMsg;
	TPCANStatus stsResult;

	// We configurate the Message.  The ID (max 0x1FF),
	// Length of the Data, Message Type (Standard in 
	// this example) and die data
	//
	CANMsg.ID = StrToInt("0x"+txtID->Text);
	CANMsg.LEN = (BYTE)nudLength->Position;
	CANMsg.MSGTYPE = (chbExtended->Checked) ? PCAN_MESSAGE_EXTENDED : PCAN_MESSAGE_STANDARD;
	// If a remote frame will be sent, the data bytes are not important.
	//
	if (chbRemote->Checked)
		CANMsg.MSGTYPE = CANMsg.MSGTYPE | PCAN_MESSAGE_RTR;
	else
	{
		// We get so much data as the Len of the message
		//
		CANMsg.DATA[0] = (BYTE)(StrToInt("0x" + txtData0->Text));
		CANMsg.DATA[1] = (BYTE)(StrToInt("0x" + txtData1->Text));
		CANMsg.DATA[2] = (BYTE)(StrToInt("0x" + txtData2->Text));
		CANMsg.DATA[3] = (BYTE)(StrToInt("0x" + txtData3->Text));
		CANMsg.DATA[4] = (BYTE)(StrToInt("0x" + txtData4->Text));
		CANMsg.DATA[5] = (BYTE)(StrToInt("0x" + txtData5->Text));
		CANMsg.DATA[6] = (BYTE)(StrToInt("0x" + txtData6->Text));
		CANMsg.DATA[7] = (BYTE)(StrToInt("0x" + txtData7->Text));
	}

	// The message is sent to the configured hardware
	//
	stsResult = m_objPCANBasic->Write(m_PcanHandle, &CANMsg);

	// The Hardware was successfully sent
	//
	if (stsResult == PCAN_ERROR_OK)
		IncludeTextMessage("Message was successfully SENT");
	// An error occurred.  We show the error.
	//
	else
		::MessageBox(NULL, GetFormatedError(stsResult).c_str(), "Error!",MB_ICONERROR);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnGetVersionsClick(TObject *Sender)
{
	TPCANStatus stsResult;
	char buffer[256];

	AnsiString info;
	int iPos = 0;

	memset(buffer,'\0',256);

	// We get the vesion of the PCAN-Basic API
	//
	stsResult = m_objPCANBasic->GetValue(PCAN_NONEBUS, PCAN_API_VERSION, buffer, 256);
	if (stsResult == PCAN_ERROR_OK)
	{
		info = Format("API Version: %s", ARRAYOFCONST((buffer)));
		IncludeTextMessage(info);
		// We get the driver version of the channel being used
		//
		stsResult = m_objPCANBasic->GetValue(m_PcanHandle, PCAN_CHANNEL_VERSION, buffer, 256);

		if (stsResult == PCAN_ERROR_OK)
		{
			IncludeTextMessage("Channel/Driver Version: ");

			// Because this information contains line control characters (several lines)
			// we split this also in several entries in the Information List-Box
			//			
			info = (AnsiString)buffer;
			while(info != "")
			{
				iPos = info.Pos("\n");
				if(iPos == 0)
					iPos = info.Length();
				IncludeTextMessage("     * " + info.SubString(1, iPos));
				info.Delete(1,iPos);
			}
		}
	}

	// If the function fail, an error message is shown
	//
	if(stsResult != PCAN_ERROR_OK)
		::MessageBox(NULL, GetFormatedError(stsResult).c_str(), "Error!",MB_ICONERROR);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnInfoClearClick(TObject *Sender)
{
	lbxInfo->Clear();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnStatusClick(TObject *Sender)
{
	TPCANStatus status;
	AnsiString errorName;
	AnsiString info;

	// Gets the current BUS status of a PCAN Channel.
	//
	status = m_objPCANBasic->GetStatus(m_PcanHandle);

	// Switch On Error Name
	//
	switch(status)
	{
		case PCAN_ERROR_INITIALIZE:
			errorName = "PCAN_ERROR_INITIALIZE";
			break;

		case PCAN_ERROR_BUSLIGHT:
			errorName = "PCAN_ERROR_BUSLIGHT";
			break;

		case PCAN_ERROR_BUSHEAVY:
			errorName = "PCAN_ERROR_BUSHEAVY";
			break;

		case PCAN_ERROR_BUSOFF:
			errorName = "PCAN_ERROR_BUSOFF";
			break;

		case PCAN_ERROR_OK:
			errorName = "PCAN_ERROR_OK";
			break;

		default:
			errorName = "See Documentation";
			break;
	}

	// Display Message
	//
	info = Format("Status: %s (%Xh)", ARRAYOFCONST((errorName, status)));
	IncludeTextMessage(info);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnResetClick(TObject *Sender)
{
	TPCANStatus stsResult;

	// Resets the receive and transmit queues of a PCAN Channel.
	//
	stsResult = m_objPCANBasic->Reset(m_PcanHandle);

	// If it fails, a error message is shown
	//
	if (stsResult != PCAN_ERROR_OK)
		::MessageBox(NULL, GetFormatedError(stsResult).c_str(), "Error!",MB_ICONERROR);
	else
		IncludeTextMessage("Receive and transmit queues successfully reset");
}
//---------------------------------------------------------------------------

void __fastcall TForm1::cbbChannelChange(TObject *Sender)
{
	bool bNonPnP;
	AnsiString strTemp;

	// Get the handle from the text being shown
	//
	strTemp = cbbChannel->Text;

	strTemp = strTemp.SubString(strTemp.Pos("(") + 1, 2);

	// Determines if the handle belong to a No Plug&Play hardware
	//
	m_PcanHandle = (TPCANHandle)StrToInt("0x"+strTemp);
	bNonPnP = m_PcanHandle <= PCAN_DNGBUS1;

	// Activates/deactivates configuration controls according with the
	// kind of hardware
	//
	cbbIO->Enabled = bNonPnP;
	cbbInterrupt->Enabled = bNonPnP;
	cbbHwType->Enabled = bNonPnP;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::cbbBaudratesChange(TObject *Sender)
{
	// We save the corresponding Baudrate enumeration
	// type value for every selected Baudrate from the
	// list.
	//
	switch(cbbBaudrates->ItemIndex)
	{
	case 0:
		m_Baudrate = PCAN_BAUD_1M;
		break;
	case 1:
		m_Baudrate = PCAN_BAUD_800K;
		break;
	case 2:
		m_Baudrate = PCAN_BAUD_500K;
		break;
	case 3:
		m_Baudrate = PCAN_BAUD_250K;
		break;
	case 4:
		m_Baudrate = PCAN_BAUD_125K;
		break;
	case 5:
		m_Baudrate = PCAN_BAUD_100K;
		break;
	case 6:
		m_Baudrate = PCAN_BAUD_95K;
		break;
	case 7:
		m_Baudrate = PCAN_BAUD_83K;
		break;
	case 8:
		m_Baudrate = PCAN_BAUD_50K;
		break;
	case 9:
		m_Baudrate = PCAN_BAUD_47K;
		break;
	case 10:
		m_Baudrate = PCAN_BAUD_33K;
		break;
	case 11:
		m_Baudrate = PCAN_BAUD_20K;
		break;
	case 12:
		m_Baudrate = PCAN_BAUD_10K;
		break;
	case 13:
		m_Baudrate = PCAN_BAUD_5K;
		break;
	default:
		m_Baudrate = (TPCANBaudrate)0;
		break;
	}	
}
//---------------------------------------------------------------------------

void __fastcall TForm1::cbbHwTypeChange(TObject *Sender)
{
	// Saves the current type for a no-Plug&Play hardware
	//
	switch (cbbHwType->ItemIndex)
	{
	case 0:
		m_HwType = PCAN_TYPE_ISA;
		break;
	case 1:
		m_HwType = PCAN_TYPE_ISA_SJA;
		break;
	case 2:
		m_HwType = PCAN_TYPE_ISA_PHYTEC;
		break;
	case 3:
		m_HwType = PCAN_TYPE_DNG;
		break;
	case 4:
		m_HwType = PCAN_TYPE_DNG_EPP;
		break;
	case 5:
		m_HwType = PCAN_TYPE_DNG_SJA;
		break;
	case 6:
		m_HwType = PCAN_TYPE_DNG_SJA_EPP;
		break;
	}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::cbbParameterChange(TObject *Sender)
{
	// Activates/deactivates controls according with the selected 
	// PCAN-Basic parameter 
	//
	rdbParamActive->Enabled = (cbbParameter->ItemIndex != 0);
	rdbParamInactive->Enabled = rdbParamActive->Enabled;
	txtDeviceId->Enabled = (!rdbParamActive->Enabled);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::chbFilterExtClick(TObject *Sender)
{
	// Updates the from and To ID-text according with the Message type
	//
	txtIdFromExit(txtIdFrom);
	txtIdFromExit(txtIdTo);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::chbExtendedClick(TObject *Sender)
{
	// Updates the ID-text according with the Message type
	//
	txtIDExit(txtID);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::rdbTimerClick(TObject *Sender)
{
	TRadioButton *radioBtn;

	radioBtn = 	(TRadioButton*)Sender;
	if(radioBtn && !radioBtn->Checked)
		return;

	if(radioBtn->Name == "rdbTimer")
		m_ActiveReadingMode = 0;
	if(radioBtn->Name == "rdbEvent")
		m_ActiveReadingMode = 1;
	if(radioBtn->Name == "rdbManual")
		m_ActiveReadingMode = 2;

	ReadingModeChanged();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::nudLengthClick(TObject *Sender, TUDBtnType Button)
{
	TEdit *CurrentTextBox;

	// We enable so much TextBox Data fields as the length of the
    // message will be, that is the value of the UpDown control
    //
    for(int i=0; i< 8; i++)
	{
        CurrentTextBox = (TEdit*)FindComponent("txtData"+IntToStr(i));
        CurrentTextBox->Enabled = (i < nudLength->Position)? true : false;
	}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::txtIdFromExit(TObject *Sender)
{
	unsigned int iTempMax, iTempValue;
	TEdit *IdBox;

	IdBox = (TEdit*)Sender;

	// Calculates the text length and Maximum ID value according
	// with the Frame Type
	//
	IdBox->MaxLength = (chbFilterExt->Checked) ? 8 : 3;
	iTempMax = (chbFilterExt->Checked) ? 0x1FFFFFFF : 0x7FF;

	// Adjusts the ID value to refresh
	//
	iTempValue = StrToInt("0x" + IdBox->Text);
	if(iTempValue < iTempMax)
		iTempMax = iTempValue;

	// Refreshes the ID value
	//
	IdBox->Text = IntToHex((int)iTempMax,IdBox->MaxLength);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::txtIdFromKeyPress(TObject *Sender, char &Key)
{
	// We convert the Character to its Upper case equivalent
	//
	Key = *(UpperCase((AnsiString)Key)).c_str();

	// The Key is the Delete (Backspace) Key
	//
	if(Key == 8)
		return;
	// The Key is a number between 0-9
	//
	if((Key > 47)&&(Key < 58))
		return;
	// The Key is a character between A-F
	//
	if((Key > 64)&&(Key < 71))
		return;

	// Is neither a number nor a character between A(a) and F(f)
	//
	Key = NULL;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::txtDeviceIdKeyPress(TObject *Sender, char &Key)
{
	// We convert the Character to its Upper case equivalent
	//
	Key = *(UpperCase((AnsiString)Key)).c_str();

	// The Key is the Delete (Backspace) Key
	//
	if(Key == 8)
		return;
	// The Key is a number between 0-9
	//
	if((Key > 47)&&(Key < 58))
		return;

	// Is neither a number nor a character between A(a) and F(f)
	//
	Key = NULL;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::txtDeviceIdExit(TObject *Sender)
{
	__int64 iTemp;

	// Checks the range of the given device ID
	//
	iTemp = StrToInt64(txtDeviceId->Text);
	if(iTemp > MAXUINT)
		txtDeviceId->Text = FloatToStr(MAXUINT);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::txtIDExit(TObject *Sender)
{
	unsigned int iTempMax, iTempValue;

	// Calculates the text length and Maximum ID value according
	// with the Frame Type
	//
	txtID->MaxLength = (chbExtended->Checked) ? 8 : 3;
	iTempMax = (chbExtended->Checked) ? 0x1FFFFFFF : 0x7FF;

	// Adjusts the ID value to refresh
	//
	iTempValue = StrToInt("0x" + txtID->Text);
	if(iTempValue < iTempMax)
		iTempMax = iTempValue;

	// Refreshes the ID value
	//
	txtID->Text = IntToHex((int)iTempMax,txtID->MaxLength);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::txtData0Exit(TObject *Sender)
{
	TEdit *CurrentEdit;

	// Checks the length of the given value
	//
	if(String(Sender->ClassName()) == "TEdit"){
		CurrentEdit = (TEdit*)Sender;
		while(CurrentEdit->Text.Length() != 2)
            CurrentEdit->Text = ("0" + CurrentEdit->Text);
    }	
}
//---------------------------------------------------------------------------

void __fastcall TForm1::chbShowPeriodClick(TObject *Sender)
{
	MessageStatus *msg;

	// According with the check-value of this checkbox,
	// the recieved time of a messages will be interpreted as 
	// period (time between the two last messages) or as time-stamp
	// (the elapsed time since windows was started).
	// - (Protected environment)
	//
	{
		clsCritical locker(m_objpCS);

		for(int i=0; i < m_LastMsgsList->Count; i++)
		{
			msg = (MessageStatus*)m_LastMsgsList->Items[i];
			msg->ShowingPeriod =  chbShowPeriod->Checked;
		}
	}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::tmrDisplayTimer(TObject *Sender)
{
	DisplayMessages();
}
//---------------------------------------------------------------------------

