/*
PROGRAM NAME	TallyInfoEdit.DLL

USED BY			BBC Academy Studio A/CTA installation

VERSION			1.0
DATE			7th February 2015
PROGRAMMER		Andy Woodhouse

Overview
========
The function of this module is to allow a user to edit the "stable" data information stored in the Tally Infodriver.

The items that can be edited are:
1)	Matrix destination id to mixer input map
2)	Studio A Control Room UMD Address per matrix destination
3)	CTA UMD address per video destination
4)	CTA UMD address per audio destination
5)	Matrix Source Primary Tally map.

*/


#include <windows.h>
#include <stdio.h>
#include <bncs_string.h>
#include <bncs_config.h>
#include "TallyInfoEdit.h"

#define MENU	1
#define MENU_NAME "Menu_Bar.bncs_ui"
#define MIXERINPUT 2
#define MIXERINPUT_NAME "Mixer_Inputs.bncs_ui"
#define STAUMDADDR 3
#define STAUMDADDR_NAME "StudioA_UMD.bncs_ui"
#define CTAVIDUMD 4
#define CTAVIDUMD_NAME "CTA_Video_UMD.bncs_ui"
#define CTAAUDUMD 5
#define CTAAUDUMD_NAME "CTA_Audio_UMD.bncs_ui"
#define PRIMARYTALLY 6
#define PRIMARYTALLY_NAME "PrimarySource.bncs_ui"

#define TIMER_MIXER_IN	1
#define MIXER_ACCEPT_TIMEOUT 20

#define GROUPDESELECT "WNGroupDeselected" 
#define GROUPSELECT "WNGroupSelected"
#define SOURCEDESELECT "WNSourceDeselected"
#define SOURCESELECTED "WNSourceSelected"
#define TAKEHIGH "WN_TakeHigh"
#define TAKELOW "WN_TakeLow"

// Definitions for the Infodriver slots
#define MIXERINFIRST 101
#define MIXERINLAST	136
#define STA_UMD_ADDR 301
#define CTA_VID_UMD_ADDR 501
#define CTA_AUD_UMD_ADDR 631
#define MASTER_SOURCE 701

// Make our class visible to the outside world
EXPORT_BNCS_SCRIPT(TallyInfoEdit)

// ++++++++++++++++++++++++++++++++++++++++++++ //
// Constructor - equivalent to ApplCore STARTUP //
// ++++++++++++++++++++++++++++++++++++++++++++ //
TallyInfoEdit::TallyInfoEdit( bncs_client_callback * parent, const char * path ) : bncs_script_helper( parent, path )
{
	// Initialise variables.
	cta_sdi_id = 0;
	cta_audio_id = 0;
	tally_info_id = 0;
	currentPanel = 0;

	dest_block = 0;
	dest_offset = 0;
	dest_index = 0;
	mtx_index = 0;
	mixer_input = 0;
	mix_in_timeout = 0;
	StAPad = 0;
	CtAVPad = 0;
	CtAAPad = 0;
	StA_UMD_Index = 0;
	StA_UMD_Addr = 0;
	CTA_VUMD_Index = 0;
	CTA_VUMD_Addr = 0;
	CTA_AUMD_Index = 0;
	CTA_AUMD_Addr = 0;

	dest_selected = false;
	dest_deselect = false;
	mix_in_selected = false;
	accept_active = false;

	// Initialise the arrays
	for (int i = 0; i <= SDI_MTX_SIZE; i++) sta_umd_addresses[i] = 0;
	for (int i = 0; i <= SDI_MTX_SIZE; i++) cta_Vumd_addresses[i] = 0;
	for (int i = 0; i <= SDI_MTX_SIZE; i++) primary_tally[i] = 0;
	for (int i = 0; i <= AUDIO_MTX_SIZE; i++) cta_Aumd_addresses[i] = 0;

	// Prepare panels for display
	panelInit(MENU, MENU_NAME);
	panelInit(MIXERINPUT, MIXERINPUT_NAME);
	panelInit(STAUMDADDR, STAUMDADDR_NAME);
	panelInit(CTAVIDUMD, CTAVIDUMD_NAME);
	panelInit(CTAAUDUMD, CTAAUDUMD_NAME);
	panelInit(PRIMARYTALLY, PRIMARYTALLY_NAME);
	
	// Find the BNCS driver numbers for the relevant routers
	getDev("CTA SDI Router", &cta_sdi_id);
	getDev("CTA Audio Router", &cta_audio_id);
	getDev("CTA Tally Infodriver", &tally_info_id);

	// Set up the MixerInput panel
	showSources(1);							// Select destination block 1
	controlDisable(MIXERINPUT, "Assign");	// Disable the Assign button

	loadSDI_Dest();			// Set up the Studio A UMD address panel
	loadCTASDI_Dest();		// Set up the CTA Video UMD address panel
	loadCTAAudio_Dest();	// Set up the CTA Audio UMD address panel
	loadPrimaryTallyData();	// Set up the lists of names on the two listboxes of Primary Tally Panel

	// Display the menu panel
	panelShow(MENU, MENU_NAME);

	// Select a main panel display.
	showMyPanel(MIXERINPUT);
	textPut("statesheet", SOURCESELECTED, MENU, "MixerInput");

	// Register for revertives from BNCS drivers
	infoRegister(tally_info_id, MIXERINFIRST, MASTER_SOURCE + SDI_MTX_SIZE - 1);

	// Poll for initial data
	infoPoll(tally_info_id, MIXERINFIRST, MIXERINLAST);								// Destinations that feed mixer inputs
	infoPoll(tally_info_id, STA_UMD_ADDR, STA_UMD_ADDR + SDI_MTX_SIZE - 1);			// Studio A UMD address per destination
	infoPoll(tally_info_id, CTA_VID_UMD_ADDR, CTA_VID_UMD_ADDR + SDI_MTX_SIZE - 1);	// CTA Video UMD address per destination
	infoPoll(tally_info_id, CTA_AUD_UMD_ADDR, CTA_AUD_UMD_ADDR+AUDIO_MTX_SIZE - 1);	// CTA Audio UMD address per destination
	infoPoll(tally_info_id, MASTER_SOURCE, MASTER_SOURCE + SDI_MTX_SIZE - 1);		// Master source tally list.
}


// +++++++++++++++++++++++++++++++++++++++++++++ //
// Destructor - equivalent to ApplCore CLOSEDOWN //
// +++++++++++++++++++++++++++++++++++++++++++++ //
TallyInfoEdit::~TallyInfoEdit()
{
	infoUnregister(tally_info_id);
}


// ++++++++++++++++++++++++++++++++++++++++++++++ //
// All button pushes and notifications come here. //
// ++++++++++++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::buttonCallback(buttonNotify *b)
{
	if (b->panel() == MENU)
	{
		processMenuButton(b);	// Add action code here
		return;
	}

	if (b->panel() == MIXERINPUT)
	{
		processMixerInButton(b);
		return;
	}

	if (b->panel() == STAUMDADDR)
	{
		process_StA_UMD_button(b);
		return;
	}

	if (b->panel() == CTAVIDUMD)
	{
		process_CTA_VidUMD_button(b);
		return;
	}

	if (b->panel() == CTAAUDUMD)
	{
		process_CTA_AudUMD_button(b);
		return;
	}

	if (b->panel() == PRIMARYTALLY)
	{
		process_TallySet(b);
		return;
	}

}




// +++++++++++++++++++++++++ //
// All revertives come here. //
// +++++++++++++++++++++++++ //
int TallyInfoEdit::revertiveCallback( revertiveNotify * r )
{

	if (r->device() == tally_info_id)
	{
		// Matrix destination to mixer input is stores 101 to 136
		if ((r->index() >= MIXERINFIRST) && (r->index() <= MIXERINLAST))
		{
			// Show the current destination data on the tally displays of panel MIXERINPUT
			textPut("text", r->sInfo(), MIXERINPUT, bncs_string("Src_%1").arg(r->index() - MIXERINFIRST + 1));
			return 0;
		}

		// Address of Studio A UMD displays per matrix destination
		if ((r->index() >= STA_UMD_ADDR) && (r->index() <= (STA_UMD_ADDR + SDI_MTX_SIZE - 1)))
		{
			// 
			sta_umd_addresses[r->index() - STA_UMD_ADDR + 1] = r->sInfo().toInt();	// Update the array of values
			if ((r->index() - STA_UMD_ADDR + 1) == StA_UMD_Index)
			{
				// Current revertive is in use on the StA UMD address panel. Update the display.
				textPut("text", r->sInfo(), STAUMDADDR, "UMD_ID");
			}
			return 0;
		}

		// Address of CTA UMD displays per matrix destination
		if ((r->index() >= CTA_VID_UMD_ADDR) && (r->index() <= (CTA_VID_UMD_ADDR + SDI_MTX_SIZE - 1)))
		{
			//
			cta_Vumd_addresses[r->index() - CTA_VID_UMD_ADDR + 1] = r->sInfo().toInt();	// Update the array of values
			if ((r->index() - CTA_VID_UMD_ADDR + 1) == CTA_VUMD_Index)
			{
				// Current revertive is in use on the StA UMD address panel. Update the display.
				textPut("text", r->sInfo(), CTAVIDUMD, "UMD_ID");
			}
			return 0;
		}

		// Address of CTA Audio displays per matrix destination
		if ((r->index() >= CTA_AUD_UMD_ADDR) && (r->index() <= (CTA_AUD_UMD_ADDR + AUDIO_MTX_SIZE - 1)))
		{
			//
			cta_Aumd_addresses[r->index() - CTA_AUD_UMD_ADDR + 1] = r->sInfo().toInt();	// Update the array of values
			if ((r->index() - CTA_AUD_UMD_ADDR + 1) == CTA_AUMD_Index)
			{
				// Current revertive is in use on the StA UMD address panel. Update the display.
				textPut("text", r->sInfo(), CTAAUDUMD, "UMD_ID");
			}
			return 0;
		}

		// Master source to tally per matrix source
		if ((r->index() >= MASTER_SOURCE) && (r->index() <= (MASTER_SOURCE + SDI_MTX_SIZE - 1)))
		{
			//
			primary_tally[r->index() - MASTER_SOURCE + 1] = r->sInfo().toInt();	// Update the array of values
			if ((r->index() - MASTER_SOURCE + 1) == Tally_Index)
			{
				// Revertive is selected on the Tally list display
				textPut("text", r->sInfo(), PRIMARYTALLY, "Tally_ID");
			}
			return 0;
		}

	}	// End of if (r->device() == tally_info_id)
	
	return 0;
}


// +++++++++++++++++++++++++++++++++++++ //
// Database name changes come back here. //
// +++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::databaseCallback( revertiveNotify * r )
{
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// All parent notifications come here - ie when this script is just one component //
// of another dialog then our host might want to tell us things.                  //
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
bncs_string TallyInfoEdit::parentCallback(parentNotify *p)
{
	if( p->command() == "return" )
	{
		if( p->value() == "all" )
		{	// Persisting values for bncs_vis_ed
			bncs_stringlist sl;
			
			sl << bncs_string( "myParam=%1" ).arg( m_myParam );
			
			return sl.toString( '\n' );
		}

		else if( p->value() == "myParam" )
		{	// Specific value being asked for by a textGet
			return( bncs_string( "%1=%2" ).arg( p->value() ).arg( m_myParam ) );
		}

	}
	else if( p->command() == "instance" && p->value() != m_instance )
	{	// Our instance is being set/changed
		m_instance = p->value();
		//Do something instance-change related here
	}

	else if( p->command() == "myParam" )
	{	// Persisted value or 'Command' being set here
		m_myParam = p->value();
	}

	// ***** CONNECTIONS EVENTS HELPER LIST *****
	else if( p->command() == "_events" )
	{	// Helper-list of everything in this component generated by hostNotify's
		bncs_stringlist sl;

		sl << "notify=*";		
		
		return sl.toString( '\n' );
	}

	// ***** CONNECTIONS COMMANDS HELPER LIST *****
	else if( p->command() == "_commands" )
	{	// Helper-list of any commands/parameters you might want to set at run-time
		bncs_stringlist sl;
		
		sl << "myParam=[value]";
		
		return sl.toString( '\n' );
	}

	return "";
}


// +++++++++++++++++++++++ //
// Timer events come here. //
// +++++++++++++++++++++++ //
void TallyInfoEdit::timerCallback(int id)
{
	switch(id)
	{
	case TIMER_MIXER_IN:
		mix_in_timeout--;
		if ((mix_in_timeout & 1) == 0)
			textPut("statesheet", TAKEHIGH, MIXERINPUT, "Assign");
		else
			textPut("statesheet", TAKELOW, MIXERINPUT, "Assign");

		if (mix_in_timeout == 0)
		{
			timerStop(TIMER_MIXER_IN);
			controlDisable(MIXERINPUT, "Assign");
			accept_active = false;
		}
		break;

	default:	// Unhandled timer event
		timerStop(id);
		break;
	}
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////// Callbacks above - Methods below ///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

// +++++++++++++++++++++++++++++++++++++++++++++++++ //
// Display the panel number provided as a parameter. //
// +++++++++++++++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::showMyPanel(int newpanel)
{
	if (currentPanel != 0) panelRemove(currentPanel);	// Remove the existing panel
	currentPanel = 0;

	switch (newpanel)
	{
	case MIXERINPUT:
		panelShow(MIXERINPUT, MIXERINPUT_NAME);
		currentPanel = MIXERINPUT;
		break;

	case STAUMDADDR:
		panelShow(STAUMDADDR, STAUMDADDR_NAME);
		currentPanel = STAUMDADDR;
		break;

	case CTAVIDUMD:
		panelShow(CTAVIDUMD, CTAVIDUMD_NAME);
		currentPanel = CTAVIDUMD;
		break;

	case CTAAUDUMD:
		panelShow(CTAAUDUMD, CTAAUDUMD_NAME);
		currentPanel = CTAAUDUMD;
		break;

	case PRIMARYTALLY:
		panelShow(PRIMARYTALLY, PRIMARYTALLY_NAME);
		currentPanel = PRIMARYTALLY;
		break;

	default:
		break;
	}

}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// showSources(block) is called to set up the source names in the buttons, and to set //
// up variables needed to compute the true source number of a button is selected.     //
// If any source button was selected, the button is deselected. The relevant          //
// destination block button is selected to indicate the current block.                //
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::showSources(int block)
{
	bncs_string dest_name;
	
	if (dest_block != 0)	// See if there is currently an active block button. If so, deselect it.
	{
		textPut("statesheet", GROUPDESELECT, MIXERINPUT, bncs_string("Dest_%1").arg(dest_block));
		dest_block = 0;
	}

	if (dest_selected)		// If there is a source highlight, remove it
	{
		textPut("statesheet", SOURCEDESELECT, MIXERINPUT, bncs_string("Out_%1").arg(dest_index));
		dest_selected = false;
		checkAccept();
	}

	dest_offset = SRCBLOCKSIZE * (block - 1);	// Compute the base address of the block. Values 0, 32, 64, 96

	// Put the highlight on the new block button
	dest_block = block;
	textPut("statesheet", GROUPSELECT, MIXERINPUT, bncs_string("Dest_%1").arg(dest_block));

	// Get the names from SDI router database 3 (short destination names in our system) and put on the buttons
	for (int i = 1; i <= SRCBLOCKSIZE; i++)
	{
		routerName(cta_sdi_id, 3, dest_offset + i, dest_name);
		textPut("text", dest_name, MIXERINPUT, bncs_string("Out_%1").arg(i));
	}
}	// End of showSources(int block)


// ++++++++++++++++++++++++++++++++++++ //
// Load the SDI destination list into   //
// the listbox on UMD assignment panel. //
// ++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::loadSDI_Dest(void)
{
	bncs_string dstName;

	textPut("clear", STAUMDADDR, "MtxDest");
	for (int i = 1; i <= SDI_MTX_SIZE; i++)
	{
		routerName(cta_sdi_id, 1, i, dstName);
		textPut("add", bncs_string("%1 - ").arg(i, '0', 3, 10) + dstName, STAUMDADDR, "MtxDest");
	}
}


// ++++++++++++++++++++++++++++++++++++++ //
// Load the SDI destination list into the //
// listbox on CTA UMD assignment panel.   //
// ++++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::loadCTASDI_Dest(void)
{
	bncs_string dstName;

	textPut("clear", CTAVIDUMD, "MtxDest");
	for (int i = 1; i <= SDI_MTX_SIZE; i++)
	{
		routerName(cta_sdi_id, 1, i, dstName);
		textPut("add", bncs_string("%1 - ").arg(i, '0', 3, 10) + dstName, CTAVIDUMD, "MtxDest");
	}
}

// ++++++++++++++++++++++++++++++++++++++++++ //
// Load the Audio destination list into the   //
// listbox on CTA Audio UMD assignment panel. //
// ++++++++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::loadCTAAudio_Dest(void)
{
	bncs_string dstName;

	textPut("clear", CTAAUDUMD, "MtxDest");
	for (int i = 1; i <= AUDIO_MTX_SIZE; i++)
	{
		routerName(cta_audio_id, 1, i, dstName);
		textPut("add", bncs_string("%1 - ").arg(i, '0', 3, 10) + dstName, CTAAUDUMD, "MtxDest");
	}
}


//++++++++++++++++++++++++++++++++++++++++++++ //
//
// +++++++++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::loadPrimaryTallyData(void)
{
	bncs_string srcName;

	textPut("clear", PRIMARYTALLY, "VidSrc");
	textPut("clear", PRIMARYTALLY, "Tally");
	for (int i = 1; i <= SDI_MTX_SIZE; i++)
	{
		routerName(cta_sdi_id, 0, i, srcName);
		textPut("add", bncs_string("%1 - ").arg(i, '0', 3, 10) + srcName, PRIMARYTALLY, "VidSrc");
		textPut("add", bncs_string("%1 - ").arg(i, '0', 3, 10) + srcName, PRIMARYTALLY, "Tally");
	}
}



// ++++++++++++++++++++ //
// processMenuButton(b)
void TallyInfoEdit::processMenuButton(buttonNotify *b)
{
	// Remove the "old" panel highlight on the menu button
	switch (currentPanel)
	{
	case MIXERINPUT:
		textPut("statesheet", SOURCEDESELECT, MENU, "MixerInput");
		break;

	case STAUMDADDR:
		textPut("statesheet", SOURCEDESELECT,MENU, "StAUMD");
		break;

	case CTAVIDUMD:
		textPut("statesheet", SOURCEDESELECT, MENU, "CTAUMD");
		break;

	case CTAAUDUMD:
		textPut("statesheet", SOURCEDESELECT, MENU, "CTAAUMD");
		break;

	case PRIMARYTALLY:
		textPut("statesheet", SOURCEDESELECT, MENU, "PrimaryMap");
		break;

	default:
		break;
	}

	// Now display the requested panel
	if (b->id() == "MixerInput")
	{
		textPut("statesheet", SOURCESELECTED, MENU, "MixerInput");
		showMyPanel(MIXERINPUT);
		return;
	}

	if (b->id() == "StAUMD")
	{
		textPut("statesheet", SOURCESELECTED, MENU, "StAUMD");
		showMyPanel(STAUMDADDR);
		return;
	}

	if (b->id() == "CTAUMD")
	{
		textPut("statesheet", SOURCESELECTED, MENU, "CTAUMD");
		showMyPanel(CTAVIDUMD);
		return;
	}

	if (b->id() == "CTAAUMD")
	{
		textPut("statesheet", SOURCESELECTED, MENU, "CTAAUMD");
		showMyPanel(CTAAUDUMD);
		return;
	}

	if (b->id() == "PrimaryMap")
	{
		textPut("statesheet", SOURCESELECTED, MENU, "PrimaryMap");
		showMyPanel(PRIMARYTALLY);
		return;
	}

}	// End of function processMenuButton(buttonNotify *b)


// processMixerInButton() processes user button presses on
// the matrix destination to mixer input panel.
void TallyInfoEdit::processMixerInButton(buttonNotify *b)
{
	// Destintion block processing
	if (b->id().startsWith("Dest_"))
	{
		showSources(b->id().firstInt());	// This function does most of the work
		return;
	}

	// Destination selection buttons
	if (b->id().startsWith("Out_"))
	{
		// Start by looking for an existing destination button
		if (dest_deselect)
		{
			textPut("statesheet", SOURCEDESELECT, MIXERINPUT, "Disconnect");
			dest_deselect = false;
		}
		if (dest_selected)
		{
			// Deselect the old button
			textPut("statesheet", SOURCEDESELECT, MIXERINPUT, bncs_string("Out_%1").arg(dest_index));
			dest_selected = false;
			dest_index = 0;
		}

		// Put the new highlight active
		dest_index = b->id().firstInt();
		textPut("statesheet", SOURCESELECTED, MIXERINPUT, bncs_string("Out_%1").arg(dest_index));
		dest_selected = true;
		mtx_index = dest_offset + dest_index;
		checkAccept();
		return;
	}

	// Has user pushed the Not Connected button?
	if (b->id() == "Disconnect")
	{
		if (dest_selected)
		{
			// A selection is active in the individual buttons. Delect them
			textPut("statesheet", SOURCEDESELECT, MIXERINPUT, bncs_string("Out_%1").arg(dest_index));
			dest_selected = false;
			dest_index = 0;
		}

		dest_deselect = true;
		dest_index = 0;
		mtx_index = 0;
		textPut("statesheet", SOURCESELECTED, MIXERINPUT, bncs_string("Disconnect"));
		checkAccept();
	}

	// Has the user pressed a mixer input selection button?
	if (b->id().startsWith("MixIn_"))
	{
		// Clear any existing selection
		if (mix_in_selected)
		{
			textPut("statesheet", SOURCEDESELECT, MIXERINPUT,bncs_string("MixIn_%1").arg(mixer_input));
			mix_in_selected = false;
			mixer_input = 0;
		}

		// Make new selection
		mixer_input = b->id().firstInt();	// Extract matrix input number
		textPut("statesheet", SOURCESELECTED, MIXERINPUT, bncs_string("MixIn_%1").arg(mixer_input));
		mix_in_selected = true;
		checkAccept();
	}

	// Has the user pressed the CANCEL button?
	if (b->id() == "Cancel")
	{
		doMixerInCancel();	// Action the cancel action
		return;
	}	// End of if (b->id() == "Cancel")

	// Has the user presed the ASSIGN button?
	if (b->id() == "Assign")
	{
		// Send the route data to the infodriver slot
		infoWrite(tally_info_id, bncs_string(mtx_index), mixer_input + MIXERINFIRST - 1, true);
		// Clear down button selections.
		doMixerInCancel();		
		return;
	}
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// process_StA_UMD_button(b) is called to process button pushes //
// on the UMD address to matrix destination assignment panel.   //
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::process_StA_UMD_button(buttonNotify *b)
{
	// Test for the listbox changing selection
	if (b->id() == "MtxDest")
	{
		// The text of the entry is returned. The entry is of the form "021 - Mixer Input 1"
		// We need to extract the leading number and the text after the " - ". Number goes into
		// current index variable, name of destination goes into DestName box.
		list_return = b->value();
		StA_UMD_Index = list_return.firstInt();								// Extract the leading number
		dest_name = list_return.right(list_return.length() - 6);			// Trim the leading 6 characters to leave the name
		textPut("text", bncs_string(StA_UMD_Index), STAUMDADDR, "DestID");	// Show the index number
		textPut("text", dest_name, STAUMDADDR, "DestName");					// Show the destination name
		textPut("text", sta_umd_addresses[StA_UMD_Index], STAUMDADDR, "UMD_ID");
		return;
	}

	// Check for a keypad number
	if (b->id().startsWith("Pad_"))
	{
		StAPad *= 10;		// Move the existing digits to the left
		StAPad += b->id().firstInt();
		if (StAPad > 128) StAPad = 128;		// Limit to valid address
		textPut("text", bncs_string(StAPad), STAUMDADDR, "Value");
		return;
	}

	// Check for the keypad clear button
	if (b->id() == "Clear")
	{
		StAPad = 0;
		textPut("text=0", STAUMDADDR, "Value");
		return;
	}

	// Check for the Set button
	if (b->id() == "Set")
	{
		// Send the Keypad value to the slot containing StA_UMD_Index, provided StA_UMD_Index non-zero.
		if (StA_UMD_Index != 0)
		{
			infoWrite(tally_info_id, bncs_string(StAPad), STA_UMD_ADDR - 1 + StA_UMD_Index, true);
			StAPad = 0;
			textPut("text=0", STAUMDADDR, "Value");
			return;
		}
	}

	// Check for the "Set all addr to zero
	if (b->id() == "AllZero")
	{
		for (int i = 0; i < SDI_MTX_SIZE; i++)
		{
			infoWrite(tally_info_id, bncs_string("0"), STA_UMD_ADDR + i, false);
		}
		return;
	}
}	// End of process_StA_UMD_button()



// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// process_CTA_VidUMD_button(b) is called to process button pushes on //
// the CTA Video UMD address to matrix destination assignment panel.  //
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::process_CTA_VidUMD_button(buttonNotify *b)
{
	// Test for the listbox changing selection
	if (b->id() == "MtxDest")
	{
		// The text of the entry is returned. The entry is of the form "021 - Mixer Input 1"
		// We need to extract the leading number and the text after the " - ". Number goes into
		// current index variable, name of destination goes into DestName box.
		list_return = b->value();
		CTA_VUMD_Index = list_return.firstInt();							// Extract the leading number
		dest_name = list_return.right(list_return.length() - 6);			// Trim the leading 6 characters to leave the name
		textPut("text", bncs_string(CTA_VUMD_Index), CTAVIDUMD, "DestID");	// Show the index number
		textPut("text", dest_name, CTAVIDUMD, "DestName");					// Show the destination name
		textPut("text", cta_Vumd_addresses[CTA_VUMD_Index], CTAVIDUMD, "UMD_ID");
		return;
	}

	// Check for a keypad number press
	if (b->id().startsWith("Pad_"))
	{
		CtAVPad *= 10;		// Move the existing digits to the left
		CtAVPad += b->id().firstInt();
		if (CtAVPad > 128) CtAVPad = 128;		// Limit to valid address
		textPut("text", bncs_string(CtAVPad), CTAVIDUMD, "Value");
		return;
	}

	// Check for the keypad clear button
	if (b->id() == "Clear")
	{
		CtAVPad = 0;
		textPut("text=0", CTAVIDUMD, "Value");
		return;
	}

	// Check for the Set button
	if (b->id() == "Set")
	{
		// Send the Keypad value to the slot containing StA_UMD_Index, provided CTA_VUMD_Index non-zero.
		if (CTA_VUMD_Index != 0)
		{
			infoWrite(tally_info_id, bncs_string(CtAVPad), CTA_VID_UMD_ADDR - 1 + CTA_VUMD_Index, true);
			CtAVPad = 0;
			textPut("text=0", CTAVIDUMD, "Value");
			return;
		}
	}

	// Check for the "Set all addr to zero
	if (b->id() == "AllZero")
	{
		for (int i = 0; i < SDI_MTX_SIZE; i++)
		{
			infoWrite(tally_info_id, bncs_string("0"), CTA_VID_UMD_ADDR + i, false);
		}
		return;
	}
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// process_CTA_AudUMD_button(b) is called to process button pushes on //
// the CTA Audio UMD address to matrix destination assignment panel.  //
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::process_CTA_AudUMD_button(buttonNotify *b)
{
	// Test for the listbox changing selection
	if (b->id() == "MtxDest")
	{
		// The text of the entry is returned. The entry is of the form "021 - Mixer Input 1"
		// We need to extract the leading number and the text after the " - ". Number goes into
		// current index variable, name of destination goes into DestName box.
		list_return = b->value();
		CTA_AUMD_Index = list_return.firstInt();							// Extract the leading number
		dest_name = list_return.right(list_return.length() - 6);			// Trim the leading 6 characters to leave the name
		textPut("text", bncs_string(CTA_AUMD_Index), CTAAUDUMD, "DestID");	// Show the index number
		textPut("text", dest_name, CTAAUDUMD, "DestName");					// Show the destination name
		textPut("text", cta_Aumd_addresses[CTA_AUMD_Index], CTAAUDUMD, "UMD_ID");
		return;
	}

	// Check for a keypad number press
	if (b->id().startsWith("Pad_"))
	{
		CtAAPad *= 10;		// Move the existing digits to the left
		CtAAPad += b->id().firstInt();
		if (CtAAPad > 256) CtAAPad = 256;		// Limit to valid address
		textPut("text", bncs_string(CtAAPad), CTAAUDUMD, "Value");
		return;
	}

	// Check for the keypad clear button
	if (b->id() == "Clear")
	{
		CtAAPad = 0;
		textPut("text=0", CTAAUDUMD, "Value");
		return;
	}

	// Check for the Set button
	if (b->id() == "Set")
	{
		// Send the Keypad value to the slot containing StA_UMD_Index, provided CTA_VUMD_Index non-zero.
		if (CTA_AUMD_Index != 0)
		{
			infoWrite(tally_info_id, bncs_string(CtAAPad), CTA_AUD_UMD_ADDR - 1 + CTA_AUMD_Index, true);
			CtAAPad = 0;
			textPut("text=0", CTAAUDUMD, "Value");
			return;
		}
	}

	// Check for the "Set all addr to zero
	if (b->id() == "AllZero")
	{
		for (int i = 0; i < SDI_MTX_SIZE; i++)
		{
			infoWrite(tally_info_id, bncs_string("0"), CTA_AUD_UMD_ADDR + i, false);
		}
		return;
	}
}

void TallyInfoEdit::process_TallySet(buttonNotify *b)
{
	// Test for button push on the Source listbox
	if (b->id() == "VidSrc")
	{
		// The text of the entry is returned. The entry is of the form "021 - Mixer Input 1"
		// We need to extract the leading number and the text after the " - ". Number goes into
		// current index variable, name of destination goes into DestName box.
		list_return = b->value();
		Source_Index = list_return.firstInt();									// Extract the leading number
		textPut("text", bncs_string(Source_Index), PRIMARYTALLY, "Source_ID");	// Show the index number
		textPut("text", primary_tally[Source_Index], PRIMARYTALLY, "Tally_ID");	// Show the current tally selection
		return;
	}

	// Check for a tally source return from tally listbox
	if (b->id() == "Tally")
	{
		list_return = b->value();
		Tally_Index = list_return.firstInt();
		textPut("text", bncs_string(Tally_Index), PRIMARYTALLY, "Tally_ID");
		return;
	}

	if (b->id() == "SetOneToOne")
	{
		for (int i = 1; i <= SDI_MTX_SIZE; i++)
		{
			infoWrite(tally_info_id, bncs_string(i), MASTER_SOURCE + i - 1);
		}
		return;
	}

	if (b->id() == "Set")
	{
		// Update the tally infodriver slot to store the new tally value in the source index slot.
		infoWrite(tally_info_id, bncs_string(Tally_Index), MASTER_SOURCE - 1 + Source_Index, true);
		// Clear the selection in the tally listbox
		textPut("selected=none", PRIMARYTALLY, "Tally");
	}
}


// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// doMixerInCancel() is called to clear down button selections and //
// flags used by the Mixer Input from matrix destination panel.    //
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::doMixerInCancel(void)
{
	// Cancel any timeout process
	if (accept_active)
	{
		// Shut down the timeout process
		timerStop(TIMER_MIXER_IN);
		accept_active = false;
		controlDisable(MIXERINPUT, "Assign");
	}

	if (mix_in_selected)
	{
		// Clear the mixer input button
		mix_in_selected = false;
		textPut("statesheet", SOURCEDESELECT, MIXERINPUT, bncs_string("MixIn_%1").arg(mixer_input));
	}

	if (dest_selected)
	{
		// Clear the matrix destination button.
		dest_selected = false;
		textPut("statesheet", SOURCEDESELECT, MIXERINPUT, bncs_string("Out_%1").arg(dest_index));
	}

	if (dest_deselect)
	{
		// Clear the "Not Connected" button
		dest_deselect = false;
		textPut("statesheet", SOURCEDESELECT, MIXERINPUT, bncs_string("Disconnect"));
	}
}	// End of doMixerInCancel(void)


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// checkAccept() is called to check if there is a matrix destination and a mixer input selected.    //
// If both are selected the ACCEPT button is activated, and the timeout process to press the accept //
// is initiated. If the function is called, and the active flag was set, but is no longer set, the  //
// ACCEPT button is disabled and the timeout timer is stopped.                                      //
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void TallyInfoEdit::checkAccept(void)
{
	if (accept_active)
	{
		// Check if the ACCEPT state should still be active.
		if (!((mix_in_selected) && (dest_selected || dest_deselect)))
		{
			// Enter this code if one or zero required elements now active
			controlDisable(MIXERINPUT, "Assign");
			timerStop(TIMER_MIXER_IN);
			accept_active = false;
			return;
		}
	}

	if ((mix_in_selected) && (dest_selected || dest_deselect))
	{
		// Enable the ACCEPT button, and start the timeout counter
		textPut("statesheet", TAKEHIGH, MIXERINPUT, "Assign");
		controlEnable(MIXERINPUT, "Assign");
		mix_in_timeout = MIXER_ACCEPT_TIMEOUT;		// Initialise the timeout counter
		timerStart(TIMER_MIXER_IN, 500);			// Start the timer
		accept_active = true;
	}

}	// End of checkAccept(void)


