/*
	Copyright (c) 2024 PTC Inc. and/or Its Subsidiary Companies. All Rights Reserved.
*/


#include <ProToolkit.h>
#include <ProObjects.h>
#include <ProUIDashboard.h>
#include <ProUIPushbutton.h>
#include <ProUILayout.h>
#include <ProUITab.h>
#include <ProUIList.h>
#include <ProUIOptionmenu.h>
#include <ProUITable.h>
#include <ProUITree.h>
#include <ProUtil.h>
#include <ProMdl.h>
#include <ProParamval.h>

#include <PTUDFExamples.h>
#include <PTUDFExDashboardStructs.h>
#include <ProTKRunTime.h>

#define PTUDFEX_DB_MAIN_PAGE_NAME "PTUDFExDBMainPage"
#define PTUDFEX_DB_INSTANCE_PAGE_NAME "PTUDFExDBInstancePage"
#define PTUDFEX_DB_SELECTOR_PAGE_NAME "PTUDFExDBSelectorPage"
#define PTUDFEX_DB_PARAM_PAGE_NAME "PTUDFExDBParamPage"
#define PTUDFEX_DB_DIM_PAGE_NAME "PTUDFExDBDimPage"
#define PTUDFEX_DB_PROPERTY_PAGE_NAME "PTUDFExDBPropertyPage"

#define PT_UDF_EX_TREE_ROOT_NODE "RootNode"
#define PT_UDF_EX_SELECTED_NODE_TYPE "Selected"
#define PT_UDF_EX_UNSELECTED_NODE_TYPE "Unselected"


#define PTUDFEX_OK_PUSHBTN "udf_ok_btn"
#define PTUDFEX_REPEAT_PUSHBTN "udf_repeat_btn"

typedef struct
{
	ProUIDashboardPage page;
	char* device_name;
	ProUIDashboardPage main_page; /* The main page handle */
	char* main_device_name;  /* The device name for the main page / Pro/E window */
	PTUDFExUdfdata* db_udf_data;
} PTUDFExtUdfDBPageData;

typedef enum
{
	PTUDFEX_UI_LAYOUT,
	PTUDFEX_UI_TAB,
	PTUDFEX_UI_PUSHBUTTON,
	PTUDFEX_UI_LIST,
	PTUDFEX_UI_LABEL
} PTUDFExUIType;

static wchar_t** s_mru_udf_files = NULL;
static wchar_t** s_mru_udf_filepaths = NULL;
static int s_num_mru_udfs = 0;


/*======================================================================================================*\
FUNCTION: PTUDFExMRUMenuUpdate
PURPOSE:  Update contents of the UDF dashboard MRU list.
RETURNS:  Index of the menu item to select.
\*======================================================================================================*/
static int PTUDFExMRUMenuUpdate (char* device_name, 
									  char* menu_name,
									  wchar_t* file_name, /* If NULL, we are updating the list without selecting any new items */
									  wchar_t* file_path)
{
	char** names;	
	int i, result = -1;
	wchar_t** tmp_files;
	wchar_t** tmp_filepaths;
	int file_name_len;
	int file_path_len;

/*------------------------------------------------------------------------------------------------------*\
	Add the input item to the list. 
\*------------------------------------------------------------------------------------------------------*/
	if (file_name != NULL || file_path != NULL)
	{
		for (i = 0; i < s_num_mru_udfs; i ++)
		{
			ProWstringCompare (s_mru_udf_files [i], file_name, PRO_VALUE_UNUSED, &result);
			if (result == 0)
				break;
		}
/*------------------------------------------------------------------------------------------------------*\
	Item is already in the list.
\*------------------------------------------------------------------------------------------------------*/
		if (result == 0)
			return (i);

		tmp_files = (wchar_t**) calloc (s_num_mru_udfs + 1, sizeof (wchar_t*));
		tmp_filepaths = (wchar_t**) calloc (s_num_mru_udfs + 1, sizeof (wchar_t*));

		for (i = 0; i < s_num_mru_udfs; i ++)
		{
			tmp_files[i] = s_mru_udf_files [i];
			tmp_filepaths [i] = s_mru_udf_filepaths [i];
		}
		ProWstringLengthGet (file_name, &file_name_len);
		ProWstringLengthGet (file_path, &file_path_len);
		tmp_files [s_num_mru_udfs] = (wchar_t*) calloc (file_name_len + 1, sizeof (wchar_t));
		tmp_filepaths [s_num_mru_udfs] = (wchar_t*) calloc (file_path_len + 1, sizeof (wchar_t));
		ProWstringCopy (file_name, tmp_files [s_num_mru_udfs], PRO_VALUE_UNUSED);
		ProWstringCopy (file_path, tmp_filepaths [s_num_mru_udfs], PRO_VALUE_UNUSED);

		if (s_num_mru_udfs != 0)
		{
			free (s_mru_udf_files);
			free (s_mru_udf_filepaths);
		}
		s_mru_udf_files = tmp_files;
		s_mru_udf_filepaths = tmp_filepaths;
		s_num_mru_udfs ++;
	}

	if (s_num_mru_udfs == 0)
		return (-1);

/*------------------------------------------------------------------------------------------------------*\
	Update the optionmenu properties with the items.
\*------------------------------------------------------------------------------------------------------*/
	names = (char**) calloc (s_num_mru_udfs, sizeof (char*));

	for (i = 0; i < s_num_mru_udfs; i ++)
	{
		names[i] = (char*) calloc (2, sizeof (char));
		ProTKSprintf (names [i], "%d", i);
	}

	status = ProUIOptionmenuNamesSet (device_name, menu_name, s_num_mru_udfs, names);
	PT_TEST_LOG_SUCC ("ProUIOptionmenuNamesSet()");

	status = ProUIOptionmenuLabelsSet (device_name, menu_name, s_num_mru_udfs, s_mru_udf_files);
	PT_TEST_LOG_SUCC ("ProUIOptionmenuLabelsSet()");

	status = ProUIOptionmenuItemhelptextSet (device_name, menu_name, s_num_mru_udfs, s_mru_udf_filepaths);
	PT_TEST_LOG_SUCC ("ProUIOptionmenuItemhelptextSet()");

	status = ProUIOptionmenuEnable (device_name, menu_name);
	PT_TEST_LOG_SUCC ("ProUIOptionmenuEnable()");

	for (i = 0; i < s_num_mru_udfs; i ++)
	{
		free (names[i]); 
	}
	free (names);
	
	return (s_num_mru_udfs - 1);
}

/*======================================================================================================*\
FUNCTION: PTUDFExGetOwnerString
PURPOSE:  Get the string label for a variable parameter.
\*======================================================================================================*/
static ProError PTUDFExGetOwnerString (int id, ProType type, ProLine label)
{
	wchar_t* type_label = L"";
	char c_id_buf [20];
	wchar_t id_buf [20];
	switch (type)
	{
	case PRO_ANNOTATION_ELEM:
		type_label = L"Annotation Element";
		break;
	case PRO_FEATURE:
		type_label = L"Feature";
		break;
	}
	
	ProTKSprintf (c_id_buf, " id %d", id);
	ProStringToWstring (id_buf, c_id_buf);

	ProWstringCopy (type_label, label, PRO_VALUE_UNUSED);
	ProWstringConcatenate (id_buf, label, PRO_VALUE_UNUSED);

	return (PRO_TK_NO_ERROR);
}


/*======================================================================================================*\
FUNCTION: PTUDFExPromptLabelUpdate
PURPOSE:  Update sensitivity of item from dashboard page.
\*======================================================================================================*/
static ProError PTUDFExPromptLabelUpdate (ProUIDashboardPage page, char* device_name,
												char* page_c_name, wchar_t* prompt)
{
	char* component_name;
	
	status = ProUIDashboardpageComponentnameGet (page, page_c_name, &component_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

	if (status != PRO_TK_NO_ERROR)
		return (PRO_TK_E_NOT_FOUND);

	ProUILabelTextSet (device_name, component_name, prompt);

	return (PRO_TK_NO_ERROR);
}


/*======================================================================================================*\
FUNCTION: PTUDFExSetPageComponentSensitivity
PURPOSE:  Update sensitivity of item from dashboard page.
\*======================================================================================================*/
static ProError PTUDFExSetPageComponentSensitivity (ProUIDashboardPage page, char* device_name,
												char* page_c_name, ProBoolean sensitivity,
												PTUDFExUIType type)
{
	char* component_name;
	
	status = ProUIDashboardpageComponentnameGet (page, page_c_name, &component_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

	if (status != PRO_TK_NO_ERROR)
		return (PRO_TK_E_NOT_FOUND);

	switch (type)
	{
	case PTUDFEX_UI_LAYOUT:
		if (sensitivity)
			ProUILayoutEnable (device_name, component_name);
		else
			ProUILayoutDisable (device_name, component_name);
		break;
	case PTUDFEX_UI_TAB:
		if (sensitivity)
			ProUITabEnable (device_name, component_name);
		else
			ProUITabDisable (device_name, component_name);
		break;
	case PTUDFEX_UI_PUSHBUTTON:
		if (sensitivity)
			ProUIPushbuttonEnable (device_name, component_name);
		else
			ProUIPushbuttonDisable (device_name, component_name);
		break;
	case PTUDFEX_UI_LIST:
		if (sensitivity)
			ProUIListEnable (device_name, component_name);
		else
			ProUIListDisable (device_name, component_name);
		break;
	case PTUDFEX_UI_LABEL:
		if (sensitivity)
			ProUILabelEnable (device_name, component_name);
		else
			ProUILabelDisable (device_name, component_name);
		break;
	}

	ProStringFree (component_name);

	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExOKBtnUpdate
PURPOSE:  Update display of the OK button based on current UDF data.
\*======================================================================================================*/
static ProError PTUDFExOKBtnUpdate (PTUDFExtUdfDBPageData* data)
{
	int i;
	ProBoolean all_refs_ok = PRO_B_TRUE;

	for (i = 0; i < data->db_udf_data->num_references; i++)
	{
		if (data->db_udf_data->reference_array [i].sel_ref == NULL)
		{
			all_refs_ok = PRO_B_FALSE;
			break;
		}
	}

	PTUDFExSetPageComponentSensitivity (data->main_page, data->main_device_name, PTUDFEX_OK_PUSHBTN, all_refs_ok, PTUDFEX_UI_PUSHBUTTON);
	PTUDFExSetPageComponentSensitivity (data->main_page, data->main_device_name, PTUDFEX_REPEAT_PUSHBTN, all_refs_ok, PTUDFEX_UI_PUSHBUTTON);

	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExUpdateReferencesUI
PURPOSE:  Update display related to references needed by the current selected UDF.
\*======================================================================================================*/
static void PTUDFExUpdateReferencesUI (PTUDFExtUdfDBPageData* data)
{
	int size = data->db_udf_data->num_references;
	int i;
	char* tab_name;
	char* db_tab_name;
	char* tab_layout_name;
	char* coll_label_name_1;
	char* coll_label_name_2;
	char* coll_label_name_3;
	ProUIDashboard dashboard;
	ProUIDashboardPage selector_page;

	status = ProUIDashboardpageComponentnameGet (data->page, "udf_ex_ref_tab", &db_tab_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

/*-----------------------------------------------------------------------------------------------------*\
Set the dashboard to show the reference inputs that matches the number of references for the item
\*-----------------------------------------------------------------------------------------------------*/
	
	switch (size)
	{
	case 1:
		tab_name = "udf_ex_ref_tab_1";
		coll_label_name_1 = "udf_ex_ref_1_label_1";
		coll_label_name_2 = NULL;
		coll_label_name_3 = NULL;
		break;
	case 2:
		tab_name = "udf_ex_ref_tab_2";
		coll_label_name_1 = "udf_ex_ref_2_label_1";
		coll_label_name_2 = "udf_ex_ref_2_label_2";
		coll_label_name_3 = NULL;
		break;
	case 3:
	default:
		tab_name = "udf_ex_ref_tab_3";
		coll_label_name_1 = "udf_ex_ref_3_label_1";
		coll_label_name_2 = "udf_ex_ref_3_label_2";
		coll_label_name_3 = "udf_ex_ref_3_label_3";
		break;
	}
	
/*-----------------------------------------------------------------------------------------------------*\
Set the main page to show the correct layout (containing 1, 2, or 3 collectors)
\*-----------------------------------------------------------------------------------------------------*/
	status = ProUIDashboardpageComponentnameGet (data->page, tab_name, &tab_layout_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

	status = ProUITabSelectednamesSet (data->device_name, db_tab_name, 1, &tab_layout_name);
	PT_TEST_LOG_SUCC ("ProUITabSelectednamesSet()");

	ProStringFree (db_tab_name);
	ProStringFree (tab_layout_name);

	PTUDFExPromptLabelUpdate (data->page, data->device_name, coll_label_name_1, data->db_udf_data->reference_array[0].prompt);
	if (coll_label_name_2 != NULL)
		PTUDFExPromptLabelUpdate (data->page, data->device_name, coll_label_name_2, data->db_udf_data->reference_array[1].prompt);
	if (coll_label_name_3 != NULL)
		PTUDFExPromptLabelUpdate (data->page, data->device_name, coll_label_name_3, data->db_udf_data->reference_array[2].prompt);

/*-----------------------------------------------------------------------------------------------------*\
If the number of references > 3, make the References tab RED to ensure user knows to enter it to set
the references not shown on the main page.
\*-----------------------------------------------------------------------------------------------------*/
	status = ProUIDashboardpageDashboardGet (data->page, &dashboard);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageDashboardGet()");

	status = ProUIDashboardPageGet (dashboard, PTUDFEX_DB_SELECTOR_PAGE_NAME, &selector_page);
	PT_TEST_LOG_SUCC ("ProUIDashboardPageGet()");

	status = ProUIDashboardpageForegroundcolorSet (selector_page, (size > 3 ? PRO_UI_COLOR_RED : PRO_UI_COLOR_WINDOW_TEXT ));
	PT_TEST_LOG_SUCC ("ProUIDashboardpageForegroundcolorSet()");
}

/*======================================================================================================*\
FUNCTION: PTUDFExUpdate
PURPOSE:  Update display and data based on the current selected UDF.
\*======================================================================================================*/
static void PTUDFExUpdate (PTUDFExtUdfDBPageData* data)
{
	int length;

/*------------------------------------------------------------------------------------------------------*\
	If no UDF is loaded, deactive most of the dashboard
\*------------------------------------------------------------------------------------------------------*/
	ProWstringLengthGet (data->db_udf_data->gph_name, &length);
	if (length <= 0)
	{
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, PTUDFEX_OK_PUSHBTN, PRO_B_FALSE, PTUDFEX_UI_PUSHBUTTON);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, PTUDFEX_REPEAT_PUSHBTN, PRO_B_FALSE, PTUDFEX_UI_PUSHBUTTON);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_tab", PRO_B_FALSE, PTUDFEX_UI_TAB);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_1_label_1", PRO_B_FALSE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_1_coll_1", PRO_B_FALSE, PTUDFEX_UI_LIST);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_2_label_1", PRO_B_FALSE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_2_coll_1", PRO_B_FALSE, PTUDFEX_UI_LIST);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_2_label_2", PRO_B_FALSE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_2_coll_2", PRO_B_FALSE, PTUDFEX_UI_LIST);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_label_1", PRO_B_FALSE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_coll_1", PRO_B_FALSE, PTUDFEX_UI_LIST);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_label_2", PRO_B_FALSE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_coll_2", PRO_B_FALSE, PTUDFEX_UI_LIST);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_label_3", PRO_B_FALSE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_coll_3", PRO_B_FALSE, PTUDFEX_UI_LIST);
	}
/*------------------------------------------------------------------------------------------------------*\
	Activate the dashboard components
\*------------------------------------------------------------------------------------------------------*/
	else
	{	
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_1_label_1", PRO_B_TRUE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_1_coll_1", PRO_B_TRUE, PTUDFEX_UI_LIST);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_2_label_1", PRO_B_TRUE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_2_coll_1", PRO_B_TRUE, PTUDFEX_UI_LIST);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_2_label_2", PRO_B_TRUE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_2_coll_2", PRO_B_TRUE, PTUDFEX_UI_LIST);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_label_1", PRO_B_TRUE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_coll_1", PRO_B_TRUE, PTUDFEX_UI_LIST);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_label_2", PRO_B_TRUE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_coll_2", PRO_B_TRUE, PTUDFEX_UI_LIST);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_label_3", PRO_B_TRUE, PTUDFEX_UI_LABEL);
		PTUDFExSetPageComponentSensitivity (data->page, data->device_name, "udf_ex_ref_3_coll_3", PRO_B_TRUE, PTUDFEX_UI_LIST);

/*------------------------------------------------------------------------------------------------------*\
	Update the required references UIs
\*------------------------------------------------------------------------------------------------------*/
		if (data->db_udf_data->reference_array == NULL)
		{
			PTUDFExUpdateReferences (data->db_udf_data);
			PTUDFExUpdateReferencesUI (data);
			PTUDFExOKBtnUpdate (data);
		}

/*------------------------------------------------------------------------------------------------------*\
	Update the available variable parameters
\*------------------------------------------------------------------------------------------------------*/
		if (data->db_udf_data->varparam_array == NULL)
		{
			PTUDFExUpdateVarparams (data->db_udf_data);
		}

/*------------------------------------------------------------------------------------------------------*\
	Update the available variable dimensions
\*------------------------------------------------------------------------------------------------------*/
		if (data->db_udf_data->vardim_array == NULL)
		{
			PTUDFExUpdateVardims (data->db_udf_data);
		}

/*------------------------------------------------------------------------------------------------------*\
	Update the available instances
\*------------------------------------------------------------------------------------------------------*/
		if (data->db_udf_data->instance_names == NULL)
		{
			PTUDFExUpdateInstances (data->db_udf_data);
		}
	}
}

typedef struct
{
	ProType type;
	const char* filter_string;
	const char* string;
} TypeEntry;

static TypeEntry s_type_entries[] = {
	{PRO_CSYS, "csys", "Coordinate system"},
	{PRO_SURFACE, "surface,datum", "Surface"},
	{PRO_AXIS, "axis", "Axis"},
	{PRO_POINT, "point", "Point"},
	{PRO_FEATURE, "feature,membfeat", "Feature"},
	{PRO_EDGE, "edge", "Edge"},
	{PRO_EDGE_START, "edge_end", "End of edge"},
	{PRO_EDGE_END, "edge_end", "End of edge"},
	{PRO_CURVE, "curve,comp_crv", "Curve"},
	{PRO_CRV_START, "curve_end", "End of curve"},
	{PRO_CRV_END, "curve_end", "End of curve"},
	{PRO_QUILT, "quilt", "Quilt"},
	{PRO_DIMENSION, "dimension", "Dimension"},
	{PRO_REF_DIMENSION, "ref_dim", "Ref dimension"},
	{PRO_GTOL, "gtol", "Gtol"},
	{PRO_SYMBOL_INSTANCE, "symbol_3d", "Symbol"},
	{PRO_SURF_FIN, "surffin_3d", "Surface finish"},
	{PRO_NOTE, "note_3d", "Note"},
	{PRO_PART, "prt_or_asm", "Part"},
	{PRO_ASSEMBLY, "prt_or_asm", "Part"},
	{PRO_ANNOTATION_ELEM, "annot_elem", "Annotation element"},
	{PRO_BODY, "3d_body", "Solid Body"}
};

static int s_num_type_entries = sizeof (s_type_entries) / sizeof (TypeEntry);


/*======================================================================================================*\
FUNCTION: PTUDFExStringLookup
PURPOSE: Look up the label text string for a particular item type.
\*======================================================================================================*/
static const char* PTUDFExStringLookup (ProType type)
{
	int i;
	for (i = 0; i < s_num_type_entries; i++)
	{
		if (s_type_entries [i].type == type)
			return (s_type_entries [i].string);
	}
	return "";
}


/*======================================================================================================*\
FUNCTION: PTUDFExFilterStringLookup
PURPOSE: Look up the ProSelect filter string for a particular item type.
\*======================================================================================================*/
static const char* PTUDFExFilterStringLookup (ProType type)
{
	int i;
	for (i = 0; i < s_num_type_entries; i++)
	{
		if (s_type_entries [i].type == type)
			return (s_type_entries [i].filter_string);
	}
	return "";
}

/*======================================================================================================*\
FUNCTION: PTUDFExGetComponentIndexNo
PURPOSE: Action function to prompt for selection when user clicks on the collector.
\*======================================================================================================*/
static int PTUDFExGetComponentIndexNo (char* component)
{
	int comp_len = strlen (component);

	if (isdigit (component [comp_len - 2]))
		return (atoi (&component [comp_len -2]));
	return (atoi (&component [comp_len -1]));
}

/*======================================================================================================*\
FUNCTION: PTUDFExCollectorSelect
PURPOSE:  Utility to gather a selection needed by a collector.
\*======================================================================================================*/
static ProError PTUDFExCollectorSelect (ProType item_type, ProSelection* selected_item)
{
	const char* filter_string = PTUDFExFilterStringLookup (item_type);
	ProSelection tmp_sel;
	ProSelection* sel_array;
	int num_sels;

	status = ProSelect (filter_string, 1, NULL, NULL, NULL, NULL, &sel_array, &num_sels);
	PT_TEST_LOG ("ProSelect()", status, status != PRO_TK_NO_ERROR && status != PRO_TK_USER_ABORT);

	if (status != PRO_TK_NO_ERROR || num_sels <= 0)
		return (status);

	status = ProSelectionCopy (sel_array [0], &tmp_sel);
	PT_TEST_LOG_SUCC ("ProSelectionCopy()");

	*selected_item = tmp_sel;

	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExCollectorStringGet
PURPOSE:  Get the descriptive string for a given reference.
\*======================================================================================================*/
static ProError PTUDFExCollectorStringGet (ProSelection selection, ProLine string)
{
	char line [PRO_LINE_SIZE];
	ProModelitem sel_item;

	status = ProSelectionModelitemGet (selection, &sel_item);
	PT_TEST_LOG_SUCC ("ProSelectionModelitemGet()");

	ProTKSprintf (line, "%s id %d", PTUDFExStringLookup (sel_item.type), sel_item.id);
	ProStringToWstring (string, line);

	return (PRO_TK_NO_ERROR);
}


/*======================================================================================================*\
FUNCTION: PTUDFExCollectorStringUpdate
PURPOSE:  Set the descriptive string for the current collector contents.
\*======================================================================================================*/
static void PTUDFExCollectorStringUpdate (ProUIDashboardPage page, char* device, char* component, PTUDFExReference* reference_info)
{
	ProLine string;
	wchar_t* list_label_ptr;
	char* real_component = component;
	char* empty_image_ptr = "";
	char* red_dot_image_ptr = "red_dot_14x14";

	
	if (page != NULL)
	{
		status = ProUIDashboardpageComponentnameGet (page, component, &real_component);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");
	}

/*------------------------------------------------------------------------------------------------------*\
	If no item has been selected, show the collector prompt including the missing red dot
\*------------------------------------------------------------------------------------------------------*/
	if (reference_info->sel_ref == NULL)
	{
		ProStringToWstring (string, "Select...");

		status = ProUIListItemimageSet (device, real_component, 1, &red_dot_image_ptr);
		PT_TEST_LOG_SUCC ("ProUIListItemimageSet()");
	}
/*------------------------------------------------------------------------------------------------------*\
	Else, show the current item's details
\*------------------------------------------------------------------------------------------------------*/
	else
	{
		PTUDFExCollectorStringGet (reference_info->sel_ref, string);

		status = ProUIListItemimageSet (device, real_component, 1, &empty_image_ptr);
		PT_TEST_LOG_SUCC ("ProUIListItemimageSet()");
	}

	list_label_ptr = &string [0];

	status = ProUIListLabelsSet (device, real_component, 1, &list_label_ptr);
	PT_TEST_LOG_SUCC ("ProUIListLabelsSet()");

	if (page != NULL)
		ProStringFree (real_component);
}

/*======================================================================================================*\
FUNCTION: PTUDFExCollectorStringsUpdate
PURPOSE:  Set the descriptive string for the current collector contents.
\*======================================================================================================*/
static void PTUDFExCollectorStringsUpdate (PTUDFExtUdfDBPageData* db_data)
{
	int num_references = db_data->db_udf_data->num_references;

	switch (num_references)
	{
	case 1:
		PTUDFExCollectorStringUpdate (db_data->main_page, db_data->main_device_name, "udf_ex_ref_1_coll_1", &db_data->db_udf_data->reference_array[0]);
		break;
	case 2:
		PTUDFExCollectorStringUpdate (db_data->main_page, db_data->main_device_name, "udf_ex_ref_2_coll_1", &db_data->db_udf_data->reference_array[0]);
		PTUDFExCollectorStringUpdate (db_data->main_page, db_data->main_device_name, "udf_ex_ref_2_coll_2", &db_data->db_udf_data->reference_array[1]);
		break;
	case 3:
	default:
		PTUDFExCollectorStringUpdate (db_data->main_page, db_data->main_device_name, "udf_ex_ref_3_coll_1", &db_data->db_udf_data->reference_array[0]);
		PTUDFExCollectorStringUpdate (db_data->main_page, db_data->main_device_name, "udf_ex_ref_3_coll_2", &db_data->db_udf_data->reference_array[1]);
		PTUDFExCollectorStringUpdate (db_data->main_page, db_data->main_device_name, "udf_ex_ref_3_coll_3", &db_data->db_udf_data->reference_array[2]);
		break;
	}
}


/*======================================================================================================*\
FUNCTION: PTUDFExClearReferenceHighlights
PURPOSE:  Clear any and all highlighted references.
\*======================================================================================================*/
static void PTUDFExClearReferenceHighlights (PTUDFExtUdfDBPageData* db_data, int current_collector)
{
	int i;

	for (i = 0; i < db_data->db_udf_data->num_references; i++)
	{
/*------------------------------------------------------------------------------------------------------*\
	Don't unhighlight the current collector's reference since it will be rehighlighted later
\*------------------------------------------------------------------------------------------------------*/
		if (i == current_collector)
			continue;

		if (db_data->db_udf_data->reference_array[i].sel_ref != NULL)
			ProSelectionUnhighlight (db_data->db_udf_data->reference_array[i].sel_ref);
	}
}


/*======================================================================================================*\
FUNCTION: PTUDFExCollectorAction
PURPOSE: Action function to prompt for selection when user clicks on the collector.
\*======================================================================================================*/
static void PTUDFExCollectorAction (char* device, char* component, ProAppData data)
{
	int reference_no = PTUDFExGetComponentIndexNo (component);
	int reference_index = reference_no - 1;
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) data;
	ProType type = db_data->db_udf_data->reference_array [reference_index].type;
	ProSelection tmp_sel;
	
/*------------------------------------------------------------------------------------------------------*\
	Clear any other highlighted references
\*------------------------------------------------------------------------------------------------------*/
	PTUDFExClearReferenceHighlights (db_data, reference_index);

/*------------------------------------------------------------------------------------------------------*\
	Highlight this collector's reference, if it already has one selected
\*------------------------------------------------------------------------------------------------------*/
	if (db_data->db_udf_data->reference_array [reference_index].sel_ref != NULL)
			ProSelectionHighlight (db_data->db_udf_data->reference_array [reference_index].sel_ref, PRO_COLOR_HIGHLITE);

/*------------------------------------------------------------------------------------------------------*\
	Prompt for selection
\*------------------------------------------------------------------------------------------------------*/
	status = PTUDFExCollectorSelect (type, &tmp_sel);

	if (status == PRO_TK_NO_ERROR)
	{
/*------------------------------------------------------------------------------------------------------*\
	Store the newly selected item
\*------------------------------------------------------------------------------------------------------*/
		if (db_data->db_udf_data->reference_array [reference_index].sel_ref != NULL)
		{
				ProSelectionUnhighlight (db_data->db_udf_data->reference_array [reference_index].sel_ref);
				ProSelectionFree (&db_data->db_udf_data->reference_array [reference_index].sel_ref);
		}

		db_data->db_udf_data->reference_array [reference_index].sel_ref = tmp_sel;

/*------------------------------------------------------------------------------------------------------*\
	Update the collector and the dashboard UI
\*------------------------------------------------------------------------------------------------------*/
		PTUDFExCollectorStringUpdate (NULL, device, component, &db_data->db_udf_data->reference_array [reference_index]);

		PTUDFExOKBtnUpdate (db_data);
	}
}

/*======================================================================================================*\
FUNCTION: PTUDFExCollectorFocusoutAction
PURPOSE: Action function to prompt for selection when user clicks on the collector.
\*======================================================================================================*/
static void PTUDFExClearActiveSelectionUI ()
{
}

/*======================================================================================================*\
FUNCTION: PTUDFExCollectorFocusoutAction
PURPOSE: Action function to prompt for selection when user clicks on the collector.
\*======================================================================================================*/
static void PTUDFExCollectorFocusoutAction (char* device, char* component, ProAppData data)
{
	int comp_len = strlen (component);
	int reference_no = atoi (&component [comp_len -1]);
	int reference_index = reference_no - 1;
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) data;
	char* empty_list_ptr;

	if (db_data->db_udf_data->reference_array [reference_index].sel_ref != NULL)
		ProSelectionUnhighlight (db_data->db_udf_data->reference_array [reference_index].sel_ref);

	status = ProUIListSelectednamesSet (device, component, 0, &empty_list_ptr);
	PT_TEST_LOG_SUCC ("ProUIListSelectednamesSet()");

	PTUDFExClearActiveSelectionUI ();
}

/*======================================================================================================*\
FUNCTION: PTUDFExClearMemory
PURPOSE:  Clear any currently stored memory in the dashboard data
\*======================================================================================================*/
static void PTUDFExClearMemory (PTUDFExtUdfDBPageData* db_data)
{
	if (db_data->db_udf_data->reference_array != NULL)
	{
		int size;
		int i;

		status = ProArraySizeGet (db_data->db_udf_data->reference_array, &size);

		for (i = 0; i < size; i++)
		{
			if (db_data->db_udf_data->reference_array [i].sel_ref != NULL)
				ProSelectionFree (&db_data->db_udf_data->reference_array [i].sel_ref);
		}
		ProArrayFree ((ProArray*) &db_data->db_udf_data->reference_array);
		db_data->db_udf_data->reference_array = NULL;
		db_data->db_udf_data->num_references = 0;
	}

	if (db_data->db_udf_data->vardim_array != NULL)
	{
		ProArrayFree ((ProArray*) &db_data->db_udf_data->vardim_array);
		db_data->db_udf_data->vardim_array = NULL;
		db_data->db_udf_data->num_vardims = 0;
	}

	if (db_data->db_udf_data->varparam_array != NULL)
	{
		ProArrayFree ((ProArray*)&db_data->db_udf_data->varparam_array);
		db_data->db_udf_data->varparam_array = NULL;
		db_data->db_udf_data->num_varparams = 0;
	}

	if (db_data->db_udf_data->instance_names != NULL)
	{
		ProArrayFree ((ProArray*)&db_data->db_udf_data->instance_names);
	}

/*------------------------------------------------------------------------------------------------------*\
	Update the dashboard internal data and UI
\*------------------------------------------------------------------------------------------------------*/
	PTUDFExUpdate (db_data);
}

/*======================================================================================================*\
FUNCTION: PTUDFExMRUMenuAction
PURPOSE:  Action when a UDF is selected from the MRU menu
\*======================================================================================================*/
static void PTUDFExMRUMenuAction (char* device, char* component, ProAppData data)
{
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) data;
	int num_sels;
	char** sels;
	int mru_index;
	ProUdfdata udf_data;
	ProPath udf_file_path;
	wchar_t* udf_file_name_ptr;
	int length;

/*------------------------------------------------------------------------------------------------------*\
	Determine the currently selected UDF
\*------------------------------------------------------------------------------------------------------*/
	status = ProUIOptionmenuSelectednamesGet (device, component, &num_sels, &sels);
	PT_TEST_LOG_SUCC ("ProUIOptionmenuSelectednamesGet()");

	mru_index = atoi (sels [0]);

	ProStringarrayFree (sels, num_sels);

	status = ProUdfdataAlloc (&udf_data);
	PT_TEST_LOG_SUCC ("ProUdfdataAlloc()");

	ProWstringCopy (s_mru_udf_filepaths [mru_index], db_data->db_udf_data->udf_file_path, PRO_VALUE_UNUSED);

	udf_file_name_ptr = s_mru_udf_files [mru_index];
	ProWstringLengthGet (udf_file_name_ptr, &length);
	ProWstringCopy (udf_file_name_ptr, db_data->db_udf_data->gph_name, length - 4); 
	db_data->db_udf_data->gph_name [length - 4] = (wchar_t) 0;

	status = ProUdfdataPathSet (udf_data, db_data->db_udf_data->udf_file_path);
	PT_TEST_LOG_SUCC ("ProUdfdataPathSet()");

	if (db_data->db_udf_data->udf_data != NULL)
	{
		ProUdfdataFree (db_data->db_udf_data->udf_data);
	}
	db_data->db_udf_data->udf_data = udf_data;

	PTUDFExClearMemory (db_data);
}


/*======================================================================================================*\
FUNCTION: PTUDFExFileOpenAction
PURPOSE: Push button action function to select and open a .gph file.
\*======================================================================================================*/
static void PTUDFExFileOpenAction (char* device, char* component, ProAppData data)
{
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) data;
	ProName label;
	ProLine filter_string;
	ProPath udf_file_path;
	ProMdlName name;
	ProMdlExtension ext;
	char* file_name_menu;
	int index;
	char buf [2];
	char* buf_ptr;


/*------------------------------------------------------------------------------------------------------*\
	Prompt for UDF selection
\*------------------------------------------------------------------------------------------------------*/
	ProStringToWstring (label, "Select UDF");
	ProStringToWstring (filter_string, "*.gph");

	status = ProFileMdlnameOpen (label, filter_string, NULL, NULL, NULL, NULL, udf_file_path);
	PT_TEST_LOG ("ProFileMdlnameOpen()", status, status != PRO_TK_NO_ERROR && status != PRO_TK_USER_ABORT);

	if (status == PRO_TK_NO_ERROR)
	{
/*------------------------------------------------------------------------------------------------------*\
	Update the MRU pulldown
\*------------------------------------------------------------------------------------------------------*/
		status = ProFileMdlnameParse (udf_file_path, NULL, name, ext, NULL);
		PT_TEST_LOG_SUCC ("ProFileMdlnameParse()");

		ProWstringConcatenate (L".", name, PRO_VALUE_UNUSED);
		ProWstringConcatenate (ext, name, PRO_VALUE_UNUSED);

		status = ProUIDashboardpageComponentnameGet (db_data->page, "udf_name_menu", &file_name_menu);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

		index = PTUDFExMRUMenuUpdate (db_data->device_name, file_name_menu, name, udf_file_path);

/*------------------------------------------------------------------------------------------------------*\
	Update the dashboard data and UI for the new selected item
\*------------------------------------------------------------------------------------------------------*/
		ProTKSprintf (buf, "%d", index);
		buf_ptr = &buf[0];

		status = ProUIOptionmenuSelectednamesSet (db_data->device_name, file_name_menu, 1, &buf_ptr);
		PT_TEST_LOG_SUCC ("ProUIOptionmenuSelectednamesSet()");

		PTUDFExMRUMenuAction (device, file_name_menu, data);

		ProStringFree (file_name_menu);

	}
}

/*======================================================================================================*\
FUNCTION: PTUDFExPlaceUDFAction
PURPOSE:  UDF placement routine
\*======================================================================================================*/
static void PTUDFExPlaceUDFAction (PTUDFExtUdfDBPageData* db_data)
{ 
	ProUdfdata udf_data;
	ProUdfreference reference_array[40];
	ProUdfvardim vardim_array [40];
	ProUdfvarparam varparam_array [40];
	ProSolid solid;
	ProUdfCreateOption options[] = {PROUDFOPT_EDIT_MENU};
	ProGroup udf;
	int i, length;
	int vardim_array_size = 0;
	

	status = ProMdlCurrentGet ((ProMdl*)&solid);
	PT_TEST_LOG_SUCC ("ProMdlCurrentGet()");

	ProUdfdataAlloc (&udf_data);

	ProWstringLengthGet (db_data->db_udf_data->instance_name, &length);

/*------------------------------------------------------------------------------------------------------*\
	Set UDF name (and instance name)
\*------------------------------------------------------------------------------------------------------*/
	if (length > 0)
	{
		ProUdfdataNameSet (udf_data, db_data->db_udf_data->gph_name, db_data->db_udf_data->instance_name);
		//status = ProUdfdataInstancenameSet (udf_data, db_data->db_udf_data->instance_name);
	}
	else
		ProUdfdataNameSet (udf_data, db_data->db_udf_data->gph_name, NULL);

/*------------------------------------------------------------------------------------------------------*\
	Set some default placement parameters.
\*------------------------------------------------------------------------------------------------------*/
	status = ProUdfdataDimdisplaySet (udf_data, PROUDFDIMDISP_NORMAL);
	PT_TEST_LOG_SUCC ("ProUdfdataDimdisplaySet()");

	status = ProUdfdataScaleSet (udf_data, PROUDFSCALETYPE_SAME_DIMS, 0.0);
	PT_TEST_LOG_SUCC ("ProUdfdataScaleSet()");

/*------------------------------------------------------------------------------------------------------*\
	Set the UDF references
\*------------------------------------------------------------------------------------------------------*/
	for (i = 0; i < db_data->db_udf_data->num_references; i++)
	{
		status = ProUdfreferenceAlloc (db_data->db_udf_data->reference_array [i].prompt,
										db_data->db_udf_data->reference_array [i].sel_ref, PRO_B_FALSE, &reference_array[i]);
		PT_TEST_LOG_SUCC ("ProUdfreferenceAlloc()");

		status = ProUdfdataReferenceAdd (udf_data, reference_array[i]);
		PT_TEST_LOG_SUCC ("ProUdfdataReferenceAdd()");
	}

/*------------------------------------------------------------------------------------------------------*\
	Set the UDF variable dimensions
\*------------------------------------------------------------------------------------------------------*/
	if (db_data->db_udf_data->vardim_array  != NULL)
	{		
		for (i = 0; i < db_data->db_udf_data->num_vardims; i++)
		{
			status = ProUdfvardimAlloc (db_data->db_udf_data->vardim_array[i].dim_symbol,
										db_data->db_udf_data->vardim_array[i].value,
										db_data->db_udf_data->vardim_array[i].type,
										&vardim_array [i]);
			PT_TEST_LOG_SUCC ("ProUdfvardimAlloc()");

			status = ProUdfdataUdfvardimAdd (udf_data, vardim_array[i]);
			PT_TEST_LOG_SUCC ("ProUdfdataUdfvardimAdd()");
		}
	}

/*------------------------------------------------------------------------------------------------------*\
	Set the UDF variable parameters
\*------------------------------------------------------------------------------------------------------*/
	if (db_data->db_udf_data->varparam_array  != NULL)
	{
		ProModelitem item;

		for (i = 0; i < db_data->db_udf_data->num_varparams; i++)
		{
			item.owner = NULL;
			item.id = db_data->db_udf_data->varparam_array[i].owner_id;
			item.type = db_data->db_udf_data->varparam_array[i].owner_type;

			status = ProUdfvarparamAlloc (db_data->db_udf_data->varparam_array[i].param_name,
										&item,
										&varparam_array [i]);
			PT_TEST_LOG_SUCC ("ProUdfvarparamAlloc()");

			status = ProUdfvarparamValueSet (varparam_array [i], &db_data->db_udf_data->varparam_array[i].value);
			PT_TEST_LOG_SUCC ("ProUdfvaparamValueSet()");

			status = ProUdfdataVarparamAdd (udf_data, varparam_array[i]);
			PT_TEST_LOG_SUCC ("ProUdfdataUdfvarparamAdd()");
		}
	}

/*------------------------------------------------------------------------------------------------------*\
	Create the UDF
\*------------------------------------------------------------------------------------------------------*/
	status = ProUdfCreate (solid, udf_data, NULL,
							options, 0, &udf);
	PT_TEST_LOG_SUCC ("ProUdfCreate()");

/*------------------------------------------------------------------------------------------------------*\
	Rename the new UDF group
\*------------------------------------------------------------------------------------------------------*/
	if (db_data->db_udf_data->udf_name_set)
	{
		ProFeature* feats;

		status = ProGroupFeaturesCollect (&udf, &feats);
		PT_TEST_LOG_SUCC ("ProGroupFeaturesCollect()");

		status = ProModelitemNameSet (&feats [0], db_data->db_udf_data->udf_name);
		PT_TEST_LOG_SUCC ("ProModelitemNameSet()");

		ProTreetoolRefresh (solid);

		ProArrayFree ((ProArray*)&feats);
	}

/*------------------------------------------------------------------------------------------------------*\
	Clean up allocated memory
\*------------------------------------------------------------------------------------------------------*/
	for (i = 0; i < db_data->db_udf_data->num_references; i++)
	{
		ProUdfreferenceFree (reference_array [i]);
	}

	if (db_data->db_udf_data->vardim_array  != NULL)
	{		
		for (i = 0; i < db_data->db_udf_data->num_vardims; i++)
		{
			ProUdfvardimFree (vardim_array [i]);
		}
	}

	if (db_data->db_udf_data->varparam_array  != NULL)
	{		
		for (i = 0; i < db_data->db_udf_data->num_varparams; i++)
		{
			ProUdfvarparamFree (varparam_array [i]);
		}
	}

	ProUdfdataFree (udf_data);
}

/*======================================================================================================*\
FUNCTION: PTUDFExOKAction
PURPOSE: Push button action function to finish the UDF placement and close the dashboard.
\*======================================================================================================*/
static void PTUDFExOKAction (char* device, char* component, ProAppData data)
{
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) data;
	ProUIDashboard db;

	PTUDFExPlaceUDFAction (db_data);

	ProUdfdataFree (db_data->db_udf_data->udf_data);
	db_data->db_udf_data->udf_data = NULL;

/*------------------------------------------------------------------------------------------------------*\
	Close the dashboard
\*------------------------------------------------------------------------------------------------------*/
	status = ProUIDashboardpageDashboardGet (db_data->page, &db);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageDashboardGet()");

	status = ProUIDashboardDestroy (db);
	PT_TEST_LOG_SUCC ("ProUIDashboardDestroy()");

	status = ProWindowRepaint (PRO_VALUE_UNUSED);
	PT_TEST_LOG_SUCC ("ProWindowRepaint()");
}

/*======================================================================================================*\
FUNCTION: PTUDFExRepeatAction
PURPOSE: Push button action function to finish the UDF placement and reset the dashboard.
\*======================================================================================================*/
static void PTUDFExRepeatAction (char* device, char* component, ProAppData data)
{
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) data;

	PTUDFExPlaceUDFAction (db_data);

/*------------------------------------------------------------------------------------------------------*\
	Reset the dashboard
\*------------------------------------------------------------------------------------------------------*/
	PTUDFExClearMemory (db_data);

	PTUDFExCollectorStringsUpdate (db_data);

	status = ProWindowRepaint (PRO_VALUE_UNUSED);
	PT_TEST_LOG_SUCC ("ProWindowRepaint()");
}



/*======================================================================================================*\
FUNCTION: PTUDFExCancelAction
PURPOSE: Push button action function to cancel the operartion and close the dashboard.
\*======================================================================================================*/
static void PTUDFExCancelAction (char* device, char* component, ProAppData data)
{
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) data;
	ProUIDashboard db;

	status = ProUIDashboardpageDashboardGet (db_data->page, &db);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageDashboardGet()");

	status = ProUIDashboardDestroy (db);
	PT_TEST_LOG_SUCC ("ProUIDashboardDestroy()");
}

/*======================================================================================================*\
FUNCTION: PTUDFExTreeSelectAction
PURPOSE: Action when a node of the selector tree is selected
\*======================================================================================================*/
static void PTUDFExTreeSelectAction (char* device, char* component, ProAppData data)
{
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) data;
	int i;
	char* label;
	int num_sels;
	char** selnames;
	int reference_no = PTUDFExGetComponentIndexNo (component);
	ProSelection tmp_sel;

	PTUDFExClearActiveSelectionUI ();

	status = ProUIDashboardpageComponentnameGet (db_data->page, "pt_udf_ex_selector_label", &label);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageDashboardGet()");

	status = ProUITreeSelectednamesGet (device, component, &num_sels, &selnames);
	PT_TEST_LOG_SUCC ("ProUITreeSelectednamesGet()");

/*------------------------------------------------------------------------------------------------------*\
	Update the selector UI, reference prompt, main page collectors, and OK button based on the latest selection
\*------------------------------------------------------------------------------------------------------*/
	if (num_sels == 1)
	{
		int idx = PTUDFExGetComponentIndexNo (selnames [0]);
		
		PTUDFExClearReferenceHighlights (db_data, idx);

		if (db_data->db_udf_data->reference_array [idx].sel_ref != NULL)
			ProSelectionHighlight (db_data->db_udf_data->reference_array [idx].sel_ref, PRO_COLOR_HIGHLITE);

		status = ProUILabelTextSet (device, label, db_data->db_udf_data->reference_array[idx].prompt);
		PT_TEST_LOG_SUCC ("ProUILabelTextSet()");

		status = PTUDFExCollectorSelect (db_data->db_udf_data->reference_array[idx].type, &tmp_sel);

		if (status == PRO_TK_NO_ERROR)
		{
			ProLine string;

			if (db_data->db_udf_data->reference_array [idx].sel_ref != NULL)
			{
				ProSelectionUnhighlight (db_data->db_udf_data->reference_array [idx].sel_ref);
				ProSelectionFree (&db_data->db_udf_data->reference_array [idx].sel_ref);
			}

			db_data->db_udf_data->reference_array [idx].sel_ref = tmp_sel;

			PTUDFExCollectorStringGet (tmp_sel, string);

			status = ProUITreeNodeTypeSet (device, component, selnames [0], PT_UDF_EX_SELECTED_NODE_TYPE);
			PT_TEST_LOG_SUCC ("ProUITreeNodeTypeSet()");

			status = ProUITreeNodeLabelSet (device, component, selnames [0], string);
			PT_TEST_LOG_SUCC ("ProUITreeNodeLabelSet()");

			PTUDFExCollectorStringsUpdate (db_data);
	
			PTUDFExOKBtnUpdate (db_data);
		}

		ProStringarrayFree (selnames, num_sels);
	}
	else
	{
		status = ProUILabelTextSet (device, label, L"");
		PT_TEST_LOG_SUCC ("ProUILabelTextSet()");
	}
	
	ProStringFree (label);

}

/*======================================================================================================*\
FUNCTION: PTUDFExPageDataInit
PURPOSE:  Set the data needed elsewhere in the UI action functions
\*======================================================================================================*/
static void PTUDFExPageDataInit (ProUIDashboardPage page, PTUDFExtUdfDBPageData* db_data)
{
	ProUIDashboard db;
	
	db_data->page = page;

	status = ProUIDashboardpageDevicenameGet (page, &db_data->device_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageDevicenameGet()");

	status = ProUIDashboardpageDashboardGet (page, &db);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageDashboardGet()");

	status = ProUIDashboardPageGet (db, PTUDFEX_DB_MAIN_PAGE_NAME, &db_data->main_page);
	PT_TEST_LOG_SUCC ("ProUIDashboardPageGet()");

	status = ProUIDashboardpageDevicenameGet (db_data->main_page, &db_data->main_device_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageDevicenameGet()");
}

/*======================================================================================================*\
FUNCTION: PTUDFExUDFMainPageNotification
PURPOSE: Notification function controlling the page for entering user-controllable parameters.
\*======================================================================================================*/
static ProError PTUDFExUDFMainPageNotification (ProUIDashboardPage page,
										ProUIDashboardPageEventType event_type,
			 							ProAppData appdata)
{
	char* component_name;
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) appdata;
	char buf [2];
	char* buf_ptr;
	int index;

	if (event_type == PRO_UI_DASHBOARD_PAGE_CREATE || event_type == PRO_UI_DASHBOARD_PAGE_SHOW)
	{
		if (db_data->page == NULL)
		{
			PTUDFExPageDataInit (page, db_data);
		}

/*------------------------------------------------------------------------------------------------------*\
	Set action functions on components in the main page
\*------------------------------------------------------------------------------------------------------*/
		status = ProUIDashboardpageComponentnameGet (page, "udf_file_open_btn", &component_name);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

		status = ProUIPushbuttonActivateActionSet (db_data->device_name, component_name, 
													PTUDFExFileOpenAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		ProStringFree (component_name);		

		status = ProUIDashboardpageComponentnameGet (page, PTUDFEX_OK_PUSHBTN, &component_name);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

		status = ProUIPushbuttonActivateActionSet (db_data->device_name, component_name, 
													PTUDFExOKAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		ProStringFree (component_name);	

		
		status = ProUIDashboardpageComponentnameGet (page, "udf_cancel_btn", &component_name);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

		status = ProUIPushbuttonActivateActionSet (db_data->device_name, component_name, 
													PTUDFExCancelAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		ProStringFree (component_name);	

		status = ProUIDashboardpageComponentnameGet (page, PTUDFEX_REPEAT_PUSHBTN, &component_name);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

		status = ProUIPushbuttonActivateActionSet (db_data->device_name, component_name, 
													PTUDFExRepeatAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		ProStringFree (component_name);	

		status = ProUIDashboardpageComponentnameGet (page, "udf_ex_ref_1_coll_1", &component_name);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

		status = ProUIListFocusinActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		status = ProUIListFocusoutActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorFocusoutAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		ProStringFree (component_name);	

		status = ProUIDashboardpageComponentnameGet (page, "udf_ex_ref_2_coll_1", &component_name);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

		status = ProUIListFocusinActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		status = ProUIListFocusoutActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorFocusoutAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		ProStringFree (component_name);	

		status = ProUIDashboardpageComponentnameGet (page, "udf_ex_ref_2_coll_2", &component_name);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

		status = ProUIListFocusinActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		status = ProUIListFocusoutActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorFocusoutAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		ProStringFree (component_name);	

		status = ProUIDashboardpageComponentnameGet (page, "udf_ex_ref_3_coll_1", &component_name);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");


		status = ProUIListFocusinActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		status = ProUIListFocusoutActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorFocusoutAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		ProStringFree (component_name);	

		status = ProUIDashboardpageComponentnameGet (page, "udf_ex_ref_3_coll_2", &component_name);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

		status = ProUIListFocusinActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		status = ProUIListFocusoutActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorFocusoutAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		ProStringFree (component_name);	

		status = ProUIDashboardpageComponentnameGet (page, "udf_ex_ref_3_coll_3", &component_name);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");


		status = ProUIListFocusinActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		status = ProUIListFocusoutActionSet (db_data->device_name, component_name, 
													PTUDFExCollectorFocusoutAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIPushbuttonActivateActionSet()");

		ProStringFree (component_name);	

		status = ProUIDashboardpageComponentnameGet (db_data->page, "udf_name_menu", &component_name);
		PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

		index = PTUDFExMRUMenuUpdate (db_data->device_name, component_name, NULL, NULL);

		status = ProUIOptionmenuSelectActionSet (db_data->device_name, component_name, PTUDFExMRUMenuAction, appdata);
		PT_TEST_LOG_SUCC ("ProUIOptionmenuSelectActionSet()");

		if (index >= 0)
		{
			ProTKSprintf (buf, "%d", index);
			buf_ptr = &buf[0];

			status = ProUIOptionmenuSelectednamesSet (db_data->device_name, component_name, 1, &buf_ptr);
			PT_TEST_LOG_SUCC ("ProUIOptionmenuSelectednamesSet()");

			PTUDFExMRUMenuAction (db_data->device_name, component_name, appdata);
		}
		ProStringFree (component_name);

		PTUDFExUpdate (db_data);
	}
	return (PRO_TK_NO_ERROR);
}


/*======================================================================================================*\
FUNCTION: PTUDFExUDFOpenPageNotification
PURPOSE: Notification function controlling the page for entering user-controllable parameters.
\*======================================================================================================*/
static ProError PTUDFExUDFInstancePageNotification (ProUIDashboardPage page,
										ProUIDashboardPageEventType event_type,
			 							ProAppData appdata)
{
	char* component_name;
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) appdata;
	int num_names, num_labels, index;
	char** names;
	wchar_t** labels;
	int i;

	if (event_type == PRO_UI_DASHBOARD_PAGE_CREATE)
	{
		status = ProUIDashboardpageTitleSet (page, L"Instances");
		PT_TEST_LOG_SUCC ("ProUIDashboardpageTitleSet()");
	}

	status = ProUIDashboardpageComponentnameGet (page, "udf_instance_name_menu", &component_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");


/*------------------------------------------------------------------------------------------------------*\
	Setup the instance name pulldown
\*------------------------------------------------------------------------------------------------------*/
	if (event_type == PRO_UI_DASHBOARD_PAGE_SHOW)
	{
		if (db_data->page == NULL)
		{
			PTUDFExPageDataInit (page, db_data);
		}

		status = ProArraySizeGet (db_data->db_udf_data->instance_names, &num_names);

		if (status != PRO_TK_NO_ERROR || num_names <= 0)
		{
			status = ProUIOptionmenuDisable (db_data->device_name, component_name);
			PT_TEST_LOG_SUCC ("ProUIOptionmenuDisable()");
		}
		else
		{
			status = ProUIOptionmenuEnable (db_data->device_name, component_name);
			PT_TEST_LOG_SUCC ("ProUIOptionmenuEnable()");
		}	
		
		num_names ++;
		names = (char**) calloc (num_names, sizeof (char*));
		labels = (wchar_t**) calloc (num_names, sizeof (wchar_t*));

		for (i = 0; i < num_names; i++)
		{
			names [i] = (char*) calloc (4, sizeof (char));
			ProTKSprintf (names[i], "%d", i);

			labels [i] = (wchar_t*) calloc (PRO_NAME_SIZE, sizeof (wchar_t*));
			if (i == 0)
				ProStringToWstring (labels [i], "<Generic>");
			else
				ProWstringCopy (db_data->db_udf_data->instance_names [i - 1], labels [i], PRO_VALUE_UNUSED);
		}
		status = ProUIOptionmenuNamesSet (db_data->device_name, component_name, num_names, names);
		PT_TEST_LOG_SUCC ("ProUIOptionmenuNamesSet()");

		status = ProUIOptionmenuLabelsSet (db_data->device_name, component_name, num_names, labels);
		PT_TEST_LOG_SUCC ("ProUIOptionmenuLabelsSet()");

		status = ProUIOptionmenuSelectednamesSet (db_data->device_name, component_name, 1, &names [0]);
		PT_TEST_LOG_SUCC ("ProUIOptionmenuSelectednamesSet()");

		for (i = 0; i < num_names; i++)
		{
			free (names [i]);
			free (labels [i]);
		}
		free (names);
		free (labels);
	}
	
/*------------------------------------------------------------------------------------------------------*\
	Transfer the selected instane to the dashboard data and clean up
\*------------------------------------------------------------------------------------------------------*/
	if (event_type == PRO_UI_DASHBOARD_PAGE_HIDE)
	{
		status = ProUIOptionmenuSelectednamesGet (db_data->device_name, component_name, &num_names, &names);
		PT_TEST_LOG_SUCC ("ProUIOptionmenuSelectednamesGet()");

		status = ProUIOptionmenuLabelsGet (db_data->device_name, component_name, &num_labels, &labels);
		PT_TEST_LOG_SUCC ("ProUIOptionmenuLabelsGet()");

		index = atoi (names[0]);

		if (index != 0)
			ProWstringCopy (labels [index], db_data->db_udf_data->instance_name, PRO_VALUE_UNUSED);
		else
			ProStringToWstring (db_data->db_udf_data->instance_name, "");

		ProWindowRefresh (PRO_VALUE_UNUSED);
	}

	ProStringFree (component_name);

	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExUDFDimPageNotification
PURPOSE: Notification function controlling the page for entering user-controllable parameters.
\*======================================================================================================*/
static ProError PTUDFExUDFDimPageNotification (ProUIDashboardPage page,
										ProUIDashboardPageEventType event_type,
			 							ProAppData appdata)
{
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) appdata;
	char* component_name;
	char* input_base_name;

	if (event_type == PRO_UI_DASHBOARD_PAGE_CREATE)
	{
		status = ProUIDashboardpageTitleSet (page, L"Variable Dimensions");
		PT_TEST_LOG_SUCC ("ProUIDashboardpageTitleSet()");
	}

	status = ProUIDashboardpageComponentnameGet (page, "pt_udf_ex_vdim_table", &component_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

	status = ProUIDashboardpageComponentnameGet (page, "pt_udf_ex_vdim_value_base", &input_base_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

/*------------------------------------------------------------------------------------------------------*\
	Setup the variable dimension table
\*------------------------------------------------------------------------------------------------------*/
	if (event_type == PRO_UI_DASHBOARD_PAGE_SHOW)
	{
		int num_dims;
		char** row_names;
		wchar_t* dim_label = L"Dimension";
		wchar_t* param_label = L"Pattern param";
		int i;

		if (db_data->page == NULL)
		{
			PTUDFExPageDataInit (page, db_data);
		}
		
		if (db_data->db_udf_data->vardim_array != NULL)
		{
			num_dims = db_data->db_udf_data->num_vardims;

			row_names = (char**) calloc (num_dims, sizeof (char*));

			for (i = 0; i < num_dims; i++)
			{
				row_names [i] = (char*) calloc (4, sizeof (char));
				ProTKSprintf (row_names [i], "%d", i);
			}

			status = ProUITableRownamesSet (db_data->device_name, component_name, num_dims, row_names);
			PT_TEST_LOG_SUCC ("ProUITableRownamesSet");

			for (i = 0; i < num_dims; i++)
			{
				char input_name [30];

				status = ProUITableCellLabelSet (db_data->device_name, component_name, row_names [i],
												"Prompt", db_data->db_udf_data->vardim_array[i].prompt);
				PT_TEST_LOG_SUCC ("ProUITableCellLabelSet()");

				status = ProUITableCellLabelSet (db_data->device_name, component_name, row_names [i],
												"Type", (db_data->db_udf_data->vardim_array[i].type == PROUDFVARTYPE_DIM ? 
												dim_label : param_label));
				PT_TEST_LOG_SUCC ("ProUITableCellLabelSet()");

				ProTKSprintf (input_name, "input_%s", row_names [i]);

				status = ProUITableCellComponentCopy (db_data->device_name, component_name, row_names [i],
												"Value",
												db_data->device_name, input_base_name, input_name);
				PT_TEST_LOG_SUCC ("ProUITableCellComponentCopy()");

				status = ProUIInputpanelDoubleSet (db_data->device_name, input_name, 
													db_data->db_udf_data->vardim_array[i].value);
				PT_TEST_LOG_SUCC ("ProUIInputpanelDoubleSet()");

				free (row_names [i]);
			}

			free (row_names);
		}
	}

/*------------------------------------------------------------------------------------------------------*\
	Transfer data from the table to the dashboard data, and cleanup
\*------------------------------------------------------------------------------------------------------*/
	if (event_type == PRO_UI_DASHBOARD_PAGE_HIDE)
	{
		char** row_names;
		int n_rows;
		int i;
		char* input_name;

		status = ProUITableRownamesGet (db_data->device_name, component_name,  &n_rows, &row_names);
		PT_TEST_LOG_SUCC ("ProUITableRownamesGet()");

		for (i = 0; i < n_rows; i++)
		{	
			status = ProUITableCellComponentNameGet (db_data->device_name, component_name, row_names [i],
														"Value", &input_name);
			PT_TEST_LOG_SUCC ("ProUITableCellComponentnameGet()");

			status = ProUIInputpanelDoubleGet (db_data->device_name, input_name, 
										&db_data->db_udf_data->vardim_array[i].value);
			PT_TEST_LOG_SUCC ("ProUIInputpanelDoubleGet()");

			status = ProUITableCellComponentDelete (db_data->device_name, component_name, row_names [i],
														"Value");
			PT_TEST_LOG_SUCC ("ProUITableCellComponentDelete()");
			
			ProStringFree (input_name);
		}
		
		status = ProUITableRowsDelete (db_data->device_name, component_name, n_rows, row_names);
		PT_TEST_LOG_SUCC ("ProUITableRowsDelete()");


		ProStringarrayFree (row_names, n_rows);
	}
	
	ProStringFree (component_name);
	ProStringFree (input_base_name);

	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExUDFParamPageNotification
PURPOSE: Notification function controlling the page for entering user reference selections.
\*======================================================================================================*/
static ProError PTUDFExUDFParamPageNotification (ProUIDashboardPage page,
										ProUIDashboardPageEventType event_type,
			 							ProAppData appdata)
{
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) appdata;
	char* component_name;
	char* input_base_name;
	char* bool_base_name;

	if (event_type == PRO_UI_DASHBOARD_PAGE_CREATE)
	{
		status = ProUIDashboardpageTitleSet (page, L"Variable Parameters");
		PT_TEST_LOG_SUCC ("ProUIDashboardpageTitleSet()");
	}
	
	status = ProUIDashboardpageComponentnameGet (page, "pt_udf_ex_param_table", &component_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

	status = ProUIDashboardpageComponentnameGet (page, "pt_udf_ex_vparam_input_base", &input_base_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

	status = ProUIDashboardpageComponentnameGet (page, "pt_udf_ex_vparam_bool_base", &bool_base_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");


/*------------------------------------------------------------------------------------------------------*\
	Setup the variable parameter table
\*------------------------------------------------------------------------------------------------------*/
	if (event_type == PRO_UI_DASHBOARD_PAGE_SHOW)
	{
		int num_params;
		char** row_names;
		ProLine owner_label; 
		ProParamvalueType value_type;
		int i;

		if (db_data->page == NULL)
		{
			PTUDFExPageDataInit (page, db_data);
		}
		
		if (db_data->db_udf_data->varparam_array != NULL)
		{
			num_params = db_data->db_udf_data->num_varparams;

			row_names = (char**) calloc (num_params, sizeof (char*));

			for (i = 0; i < num_params; i++)
			{
				row_names [i] = (char*) calloc (4, sizeof (char));
				ProTKSprintf (row_names [i], "%d", i);
			}

			status = ProUITableRownamesSet (db_data->device_name, component_name, num_params, row_names);
			PT_TEST_LOG_SUCC ("ProUITableRownamesSet");

			for (i = 0; i < num_params; i++)
			{
				char input_name [30];

				status = ProUITableCellLabelSet (db_data->device_name, component_name, row_names [i],
												"Name", db_data->db_udf_data->varparam_array[i].param_name);
				PT_TEST_LOG_SUCC ("ProUITableCellLabelSet()");

				PTUDFExGetOwnerString (db_data->db_udf_data->varparam_array[i].owner_id, db_data->db_udf_data->varparam_array[i].owner_type,
										owner_label);

				status = ProUITableCellLabelSet (db_data->device_name, component_name, row_names [i],
												"Owner", owner_label);
				PT_TEST_LOG_SUCC ("ProUITableCellLabelSet()");

				ProTKSprintf (input_name, "input_%s", row_names [i]);
				value_type = db_data->db_udf_data->varparam_array[i].value.type;

				if (value_type == PRO_PARAM_BOOLEAN)
				{
					char option_value [2];
					char* option_value_ptr = &option_value [0];

					status = ProUITableCellComponentCopy (db_data->device_name, component_name, row_names [i],
												"Value",
												db_data->device_name, bool_base_name, input_name);
					PT_TEST_LOG_SUCC ("ProUITableCellComponentCopy()");

					ProTKSprintf (option_value, "%d", db_data->db_udf_data->varparam_array[i].value.value.l_val);

					status = ProUIOptionmenuSelectednamesSet (db_data->device_name, input_name, 1, &option_value_ptr);
					PT_TEST_LOG_SUCC ("ProUIOptionmenuSelectednamesSet()");
				}
				else
				{
					status = ProUITableCellComponentCopy (db_data->device_name, component_name, row_names [i],
												"Value",
												db_data->device_name, input_base_name, input_name);
					PT_TEST_LOG_SUCC ("ProUITableCellComponentCopy()");

					switch (value_type)
					{
					case PRO_PARAM_INTEGER:
						status = ProUIInputpanelInputtypeSet (db_data->device_name, input_name,
																PROUIINPUTTYPE_INTEGER);
						PT_TEST_LOG_SUCC ("ProUIInputpanelInputtypeSet()");

						status = ProUIInputpanelIntegerSet (db_data->device_name, input_name,
															db_data->db_udf_data->varparam_array[i].value.value.i_val);
						PT_TEST_LOG_SUCC ("ProUIInputpanelIntegerSet()");
						
						break;
					case PRO_PARAM_DOUBLE:
						status = ProUIInputpanelInputtypeSet (db_data->device_name, input_name,
																PROUIINPUTTYPE_DOUBLE);
						PT_TEST_LOG_SUCC ("ProUIInputpanelInputtypeSet()");

						status = ProUIInputpanelDoubleSet (db_data->device_name, input_name,
															db_data->db_udf_data->varparam_array[i].value.value.d_val);
						PT_TEST_LOG_SUCC ("ProUIInputpanelDoubleSet()");

						break;
					case PRO_PARAM_STRING:
						status = ProUIInputpanelInputtypeSet (db_data->device_name, input_name,
																PROUIINPUTTYPE_WSTRING);
						PT_TEST_LOG_SUCC ("ProUIInputpanelInputtypeSet()");

						status = ProUIInputpanelWidestringSet (db_data->device_name, input_name,
															db_data->db_udf_data->varparam_array[i].value.value.s_val);
						PT_TEST_LOG_SUCC ("ProUIInputpanelWidestringSet()");

						break;
					}
				}

				free (row_names [i]);
			}

			free (row_names);
		}
	}

/*------------------------------------------------------------------------------------------------------*\
	Transfer data back into the dashboard data, and cleanup
\*------------------------------------------------------------------------------------------------------*/
	if (event_type == PRO_UI_DASHBOARD_PAGE_HIDE && db_data->db_udf_data->varparam_array != NULL)
	{
		char** row_names;
		int n_rows;
		int i;
		char* input_name;
		char** selnames;
		int n_selnames;
		wchar_t* string_val;

		status = ProUITableRownamesGet (db_data->device_name, component_name,  &n_rows, &row_names);
		PT_TEST_LOG_SUCC ("ProUITableRownamesGet()");

		for (i = 0; i < n_rows; i++)
		{	
			status = ProUITableCellComponentNameGet (db_data->device_name, component_name, row_names [i],
														"Value", &input_name);
			PT_TEST_LOG_SUCC ("ProUITableCellComponentnameGet()");

			switch (db_data->db_udf_data->varparam_array[i].value.type)
			{
			case PRO_PARAM_BOOLEAN:
				status = ProUIOptionmenuSelectednamesGet (db_data->device_name, input_name, &n_selnames, &selnames);
				PT_TEST_LOG_SUCC ("ProUIOptionmenuSelectednamesGet()");

				db_data->db_udf_data->varparam_array[i].value.value.l_val = atoi (selnames [0]);

				ProStringarrayFree (selnames, n_selnames);

				break;
			case PRO_PARAM_INTEGER:
				status = ProUIInputpanelIntegerGet (db_data->device_name, input_name, &db_data->db_udf_data->varparam_array[i].value.value.i_val);
				PT_TEST_LOG_SUCC ("ProUIInputpanelIntegerGet()");

				break;
			case PRO_PARAM_DOUBLE:
				status = ProUIInputpanelDoubleGet (db_data->device_name, input_name, &db_data->db_udf_data->varparam_array[i].value.value.d_val);
				PT_TEST_LOG_SUCC ("ProUIInputpanelDoubleGet()");

				break;

			case PRO_PARAM_STRING:
				status = ProUIInputpanelWidestringGet (db_data->device_name, input_name, &string_val);
				PT_TEST_LOG_SUCC ("ProUIInputpanelWidestringGet()");

				ProWstringCopy (string_val, db_data->db_udf_data->varparam_array[i].value.value.s_val, PRO_VALUE_UNUSED);

				ProWstringFree (string_val);
				break;
			}
			status = ProUITableCellComponentDelete (db_data->device_name, component_name, row_names [i],
														"Value");
			PT_TEST_LOG_SUCC ("ProUITableCellComponentDelete()");

			ProStringFree (input_name);
		}

		status = ProUITableRowsDelete (db_data->device_name, component_name, n_rows, row_names);
		PT_TEST_LOG_SUCC ("ProUITableRowsDelete()");

		ProStringarrayFree (row_names, n_rows);
	}

	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExUDFSelectorPageNotification
PURPOSE: Notification function controlling the page for entering user reference selections.
\*======================================================================================================*/
static ProError PTUDFExUDFSelectorPageNotification (ProUIDashboardPage page,
										ProUIDashboardPageEventType event_type,
			 							ProAppData appdata)
{
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) appdata;
	char* tree_name;

	if (event_type == PRO_UI_DASHBOARD_PAGE_CREATE)
	{
		status = ProUIDashboardpageTitleSet (page, L"References");
		PT_TEST_LOG_SUCC ("ProUIDashboardpageTitleSet()");
	}

	status = ProUIDashboardpageComponentnameGet (page, "pt_udf_ex_selector_tree", &tree_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

/*------------------------------------------------------------------------------------------------------*\
	Setup the reference selector (tree)
\*------------------------------------------------------------------------------------------------------*/
	if (event_type == PRO_UI_DASHBOARD_PAGE_SHOW)
	{
		int i;
		char node_name [30];
		ProLine string;
		char* node_type;
	
		if (db_data->page == NULL)
		{
			PTUDFExPageDataInit (page, db_data);
		}

		status = ProUITreeNodeAdd (db_data->device_name, tree_name, PT_UDF_EX_TREE_ROOT_NODE, L"", NULL, PT_UDF_EX_SELECTED_NODE_TYPE);
		PT_TEST_LOG_SUCC ("ProUITreeNodeAdd");

		for (i = 0; i < db_data->db_udf_data->num_references; i++)
		{
			ProTKSprintf (node_name, "tree_selector_node_%d", i);
			
			ProStringToWstring (string, "Select...");
			node_type = PT_UDF_EX_UNSELECTED_NODE_TYPE;

			if (db_data->db_udf_data->reference_array[i].sel_ref != NULL)
			{
				PTUDFExCollectorStringGet (db_data->db_udf_data->reference_array[i].sel_ref, string);
				node_type = PT_UDF_EX_SELECTED_NODE_TYPE;
			}

			status = ProUITreeNodeAdd (db_data->device_name, tree_name, node_name, string, PT_UDF_EX_TREE_ROOT_NODE, node_type);
			PT_TEST_LOG_SUCC ("ProUITreeNodeAdd");
		}
	
		status = ProUITreeNodeExpand (db_data->device_name, tree_name, PT_UDF_EX_TREE_ROOT_NODE, PRO_B_TRUE);
		PT_TEST_LOG_SUCC ("ProUITreeNodeExpand()");

		status = ProUITreeAllnodesDeselect (db_data->device_name, tree_name);
		PT_TEST_LOG_SUCC ("ProUITreeAllnodesDeselect()");

		status = ProUITreeTreeredrawSet (db_data->device_name, tree_name, PRO_B_TRUE);
		PT_TEST_LOG_SUCC ("ProUITreeTreeredrawSet()");

		status = ProUITreeSelectActionSet (db_data->device_name, tree_name, PTUDFExTreeSelectAction, appdata);
		PT_TEST_LOG_SUCC ("ProUITreeSelectActionSet()");
	}

/*------------------------------------------------------------------------------------------------------*\
	Cleanup the reference selector tree
\*------------------------------------------------------------------------------------------------------*/
	if (event_type == PRO_UI_DASHBOARD_PAGE_HIDE)
	{
		int i, num_nodes = 0 ;
		char** nodes = NULL;

		status = ProUITreeNodesOfTypeGet (db_data->device_name, tree_name, PT_UDF_EX_UNSELECTED_NODE_TYPE, &nodes, &num_nodes);
		PT_TEST_LOG_SUCC ("ProUITreeNodesOfTypeGet()");

		for (i = 0; i < num_nodes; i++)
		{
			status = ProUITreeNodeDelete(db_data->device_name, tree_name, nodes[i]);
			PT_TEST_LOG_SUCC("ProUITreeNodeDelete()");
		}

		if (num_nodes > 0)
		{
			ProStringarrayFree(nodes, num_nodes);
			nodes = NULL;
			num_nodes = 0;
		}

		status = ProUITreeNodesOfTypeGet (db_data->device_name, tree_name, PT_UDF_EX_SELECTED_NODE_TYPE, &nodes, &num_nodes);
		PT_TEST_LOG_SUCC ("ProUITreeNodesOfTypeGet()");

		for (i = 0; (i < num_nodes && nodes != NULL); i++)
		{
			status = ProUITreeNodeDelete (db_data->device_name, tree_name, nodes [i]);
			PT_TEST_LOG_SUCC ("ProUITreeNodeDelete()");
		}
		if (num_nodes > 0)
		{
			ProStringarrayFree(nodes, num_nodes);
			nodes = NULL;
			num_nodes = 0;
		}
	}

	return (PRO_TK_NO_ERROR);
}


/*======================================================================================================*\
FUNCTION: PTUDFExUDFPropertyPageNotification
PURPOSE: Notification function controlling the page for entering UDF properties (name)
\*======================================================================================================*/
static ProError PTUDFExUDFPropertyPageNotification (ProUIDashboardPage page,
										ProUIDashboardPageEventType event_type,
			 							ProAppData appdata)
{
	PTUDFExtUdfDBPageData* db_data = (PTUDFExtUdfDBPageData*) appdata;
	char* input_name;

	if (event_type == PRO_UI_DASHBOARD_PAGE_CREATE)
	{
		status = ProUIDashboardpageTitleSet (page, L"Properties");
		PT_TEST_LOG_SUCC ("ProUIDashboardpageTitleSet()");
	}

	status = ProUIDashboardpageComponentnameGet (page, "pt_udf_ex_group_input", &input_name);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageComponentnameGet()");

/*------------------------------------------------------------------------------------------------------*\
	Setup the new name UI
\*------------------------------------------------------------------------------------------------------*/
	if (event_type == PRO_UI_DASHBOARD_PAGE_SHOW)
	{
		if (db_data->page == NULL)
		{
			PTUDFExPageDataInit (page, db_data);
		}

		if (db_data->db_udf_data->udf_name_set)
		{
			status = ProUIInputpanelValueSet (db_data->device_name, input_name, db_data->db_udf_data->udf_name);
			PT_TEST_LOG_SUCC ("ProUIInputpanelWidestringSet()");
		}
		else
		{
			status = ProUIInputpanelValueSet (db_data->device_name, input_name, L"");
			PT_TEST_LOG_SUCC ("ProUIInputpanelWidestringSet()");
		}
	}


/*------------------------------------------------------------------------------------------------------*\
	Transfer data back into the dashboard data, and cleanup
\*------------------------------------------------------------------------------------------------------*/
	if (event_type == PRO_UI_DASHBOARD_PAGE_HIDE)
	{
		wchar_t* wstring;
		int length;

		status = ProUIInputpanelValueGet (db_data->device_name, input_name, &wstring);
		PT_TEST_LOG_SUCC ("ProUIInputpanelWidestringGet()");

		ProWstringLengthGet (wstring, &length);

		if (length > 0)
		{
			ProWstringCopy (wstring, db_data->db_udf_data->udf_name, PRO_VALUE_UNUSED);
			db_data->db_udf_data->udf_name_set = PRO_B_TRUE;
		}
		else
			db_data->db_udf_data->udf_name_set = PRO_B_FALSE;

		ProWstringFree (wstring);
	}

	ProStringFree (input_name);

	return PRO_TK_NO_ERROR;
}


/*======================================================================================================*\
FUNCTION: PTUDFExUDFMainPageSetup
PURPOSE: Setup the main page for user placement of the UDF.
\*======================================================================================================*/
static ProError PTUDFExUDFMainPageSetup (ProAppData data, ProUIDashboardPage* page_options)
{
	ProUIDashboardPage opts;

	status = ProUIDashboardpageoptionsAlloc (PTUDFEX_DB_MAIN_PAGE_NAME, "pt_udf_ex_main_page",
											data, &opts);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsAlloc()");

	status = 
		ProUIDashboardpageoptionsNotificationSet (opts, (ProUIDashboardpageCallbackFunction)PTUDFExUDFMainPageNotification,
													data);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsNotificationSet()");

	*page_options = opts;

	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExUDFInstancePageSetup
PURPOSE: Setup the page for user selection of the UDF instance.
\*======================================================================================================*/
static ProError PTUDFExUDFInstancePageSetup (ProAppData data, ProUIDashboardPage* page_options)
{
	ProUIDashboardPage opts;

	status = ProUIDashboardpageoptionsAlloc (PTUDFEX_DB_INSTANCE_PAGE_NAME, "pt_udf_ex_instance_page",
											data, &opts);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsAlloc()");

	status = 
		ProUIDashboardpageoptionsNotificationSet (opts, (ProUIDashboardpageCallbackFunction)PTUDFExUDFInstancePageNotification,
													data);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsNotificationSet()");

	*page_options = opts;
	
	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExUDFSelectorPageSetup
PURPOSE: Setup the page for user reference selections.
\*======================================================================================================*/
static ProError PTUDFExUDFSelectorPageSetup (ProAppData data, ProUIDashboardPage* page_options)
{
	ProUIDashboardPage opts;

	status = ProUIDashboardpageoptionsAlloc (PTUDFEX_DB_SELECTOR_PAGE_NAME, "pt_udf_ex_selector_page",
											data, &opts);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsAlloc()");

	status = 
		ProUIDashboardpageoptionsNotificationSet (opts, (ProUIDashboardpageCallbackFunction)PTUDFExUDFSelectorPageNotification,
													data);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsNotificationSet()");

	*page_options = opts;
	
	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExUDFDimPageSetup
PURPOSE: Setup the page for entering variable dimensions.
\*======================================================================================================*/
static ProError PTUDFExUDFDimPageSetup (ProAppData data, ProUIDashboardPage* page_options)
{
	ProUIDashboardPage opts;

	status = ProUIDashboardpageoptionsAlloc (PTUDFEX_DB_DIM_PAGE_NAME, "pt_udf_ex_dim_page",
											data, &opts);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsAlloc()");

	status = 
		ProUIDashboardpageoptionsNotificationSet (opts, (ProUIDashboardpageCallbackFunction)PTUDFExUDFDimPageNotification,
													data);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsNotificationSet()");

	*page_options = opts;
	
	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExUDFParamPageSetup
PURPOSE: Setup the page for entering user-controllable parameters.
\*======================================================================================================*/
static ProError PTUDFExUDFParamPageSetup (ProAppData data, ProUIDashboardPage* page_options)
{
	ProUIDashboardPage opts;

	status = ProUIDashboardpageoptionsAlloc (PTUDFEX_DB_PARAM_PAGE_NAME, "pt_udf_ex_param_page",
											data, &opts);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsAlloc()");

	status = 
		ProUIDashboardpageoptionsNotificationSet (opts, (ProUIDashboardpageCallbackFunction)PTUDFExUDFParamPageNotification,
													data);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsNotificationSet()");

	*page_options = opts;
	
	return (PRO_TK_NO_ERROR);
}


/*======================================================================================================*\
FUNCTION: PTUDFExUDFPropertyPageSetup
PURPOSE: Setup the page for entering UDF properties
\*======================================================================================================*/
static ProError PTUDFExUDFPropertyPageSetup (ProAppData data, ProUIDashboardPage* page_options)
{
	ProUIDashboardPage opts;

	status = ProUIDashboardpageoptionsAlloc (PTUDFEX_DB_PROPERTY_PAGE_NAME, "pt_udf_ex_properties_page",
											data, &opts);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsAlloc()");

	status = 
		ProUIDashboardpageoptionsNotificationSet (opts, (ProUIDashboardpageCallbackFunction)PTUDFExUDFPropertyPageNotification,
													data);
	PT_TEST_LOG_SUCC ("ProUIDashboardpageoptionsNotificationSet()");

	*page_options = opts;
	
	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExUDFDashboardNotification
PURPOSE:  Overall dashboard notification.
\*======================================================================================================*/
static ProError PTUDFExUDFDashboardNotification (ProUIDashboard dashboard,
					       ProUIDashboardEventType event_type,
					       ProAppData appdata)
{
	switch (event_type)
	{
	case PRO_UI_DASHBOARD_CREATE:
/*-----------------------------------------------------------------------------------------------------*\
Set the reference and parameter pages & widgets to be disabled, until the user selects a UDF to place	
\*-----------------------------------------------------------------------------------------------------*/
		status = ProUIDashboardBitmapSet (dashboard, "udf");
		PT_TEST_LOG_SUCC ("ProUIDashboardBitmapSet()");

		break;
	}

	return (PRO_TK_NO_ERROR);
}

/*======================================================================================================*\
FUNCTION: PTUDFExPageDataClear
PURPOSE:  Setup the page data before the dashboard is shown
\*======================================================================================================*/
static void PTUDFExPageDataClear (PTUDFExtUdfDBPageData* db_data, PTUDFExUdfdata* db_udf_data)
{
	db_data->page = NULL;
	db_data->device_name = NULL;
	db_data->main_page = NULL;
	db_data->main_device_name = NULL;
	db_data->db_udf_data = db_udf_data;
}

static ProBoolean s_inUDFdb = PRO_B_FALSE;

/*======================================================================================================*\
FUNCTION: PTUDFExUDFDashboardMain
PURPOSE: Show the dashboard for UDF placement.
\*======================================================================================================*/
int PTUDFExUDFDashboardMain ()
{
	ProUIDashboardShowOptions options;
	ProUIDashboardPageOptions main_page_options;
	ProUIDashboardPageOptions* slideup_panels;
	PTUDFExUdfdata db_udf_data;
	PTUDFExtUdfDBPageData main_page_data;
	PTUDFExtUdfDBPageData instance_page_data;
	PTUDFExtUdfDBPageData selector_page_data;
	PTUDFExtUdfDBPageData param_page_data;
	PTUDFExtUdfDBPageData dim_page_data;
	PTUDFExtUdfDBPageData property_page_data;

	PTUDFExPageDataClear (&main_page_data, &db_udf_data);
	PTUDFExPageDataClear (&instance_page_data, &db_udf_data);
	PTUDFExPageDataClear (&selector_page_data, &db_udf_data);
	PTUDFExPageDataClear (&param_page_data, &db_udf_data);
	PTUDFExPageDataClear (&dim_page_data, &db_udf_data);
	PTUDFExPageDataClear (&property_page_data, &db_udf_data);


/*------------------------------------------------------------------------------------------------------*\
	Setup 1 instance of PTUDFExUdfdata for this instance of the dashboard
\*------------------------------------------------------------------------------------------------------*/
	ProStringToWstring (db_udf_data.udf_file_path, "");
	ProStringToWstring (db_udf_data.gph_name, "");
	db_udf_data.udf_data = NULL;
	db_udf_data.reference_array = NULL;
	db_udf_data.num_references = 0;
	db_udf_data.vardim_array = NULL;
	db_udf_data.num_vardims = 0;
	db_udf_data.varparam_array = NULL;
	db_udf_data.num_varparams = 0;
	db_udf_data.instance_names = NULL;
	db_udf_data.udf_name_set = PRO_B_FALSE;
	ProStringToWstring (db_udf_data.instance_name, "");

	
/*------------------------------------------------------------------------------------------------------*\
	Setup the page details for the main page and slideup panels
\*------------------------------------------------------------------------------------------------------*/
	status = ProArrayAlloc (5, sizeof (ProUIDashboardPageOptions), 1, (ProArray*)&slideup_panels);

	PTUDFExUDFMainPageSetup ((ProAppData)&main_page_data, &main_page_options);

	PTUDFExUDFInstancePageSetup ((ProAppData)&instance_page_data, &slideup_panels [0]);

	PTUDFExUDFSelectorPageSetup ((ProAppData)&selector_page_data, &slideup_panels [1]);

	PTUDFExUDFDimPageSetup ((ProAppData)&dim_page_data, &slideup_panels [2]);

	PTUDFExUDFParamPageSetup((ProAppData)&param_page_data, &slideup_panels [3]);

	PTUDFExUDFPropertyPageSetup((ProAppData)&property_page_data, &slideup_panels [4]);

/*------------------------------------------------------------------------------------------------------*\
	Setup the dashboard for display
\*------------------------------------------------------------------------------------------------------*/
	status = ProUIDashboardshowoptionsAlloc (main_page_options, slideup_panels, (ProAppData)&db_udf_data, &options);
	PT_TEST_LOG_SUCC ("ProUIDashboardshowoptionsAlloc()");

	status = ProUIDashboardshowoptionsNotificationSet (options, (ProUIDashboardCallbackFunction)PTUDFExUDFDashboardNotification, 
														(ProAppData)&db_udf_data);
	PT_TEST_LOG_SUCC ("ProUIDashboardshowoptionsNotificationSet()");
	
	s_inUDFdb = PRO_B_TRUE;

/*------------------------------------------------------------------------------------------------------*\
	Launch the dashboard
\*------------------------------------------------------------------------------------------------------*/
	status = ProUIDashboardShow (options);
	PT_TEST_LOG_SUCC ("ProUIDashboardShow()");

	s_inUDFdb = PRO_B_FALSE;

/*------------------------------------------------------------------------------------------------------*\
	Clean up when finished
\*------------------------------------------------------------------------------------------------------*/
	ProArrayFree ((ProArray*)&slideup_panels);

	ProStringFree (main_page_data.device_name);
	ProStringFree (main_page_data.main_device_name);
	ProStringFree (instance_page_data.device_name);
	ProStringFree (instance_page_data.main_device_name);
	ProStringFree (selector_page_data.device_name);
	ProStringFree (selector_page_data.main_device_name);
	ProStringFree (param_page_data.device_name);
	ProStringFree (param_page_data.main_device_name);
	ProStringFree (dim_page_data.device_name);
	ProStringFree (dim_page_data.main_device_name);
	ProStringFree (property_page_data.device_name);
	ProStringFree (property_page_data.main_device_name);

	return (0);
}
	
/*------------------------------------------------------------------------------------------------------*\
	Access function for the UDF placement UI
\*------------------------------------------------------------------------------------------------------*/
uiCmdAccessState PTUDFExAccessPlaceUDF (uiCmdAccessMode access_mode)
{
	ProMdl mdl;
	ProMdlType type;

	status = ProMdlCurrentGet (&mdl);
	PT_TEST_LOG_SUCC ("ProMdlCurrentGet()");

	if (status == PRO_TK_NO_ERROR)
	{
		status = ProMdlTypeGet (mdl, &type);
		PT_TEST_LOG_SUCC ("ProMdlTypeGet()");

		if (type == PRO_MDL_PART || type == PRO_MDL_ASSEMBLY)
			return (ACCESS_AVAILABLE);
	}
	return (ACCESS_UNAVAILABLE);
}

/*
int PTUDFExUDFDismissCmd ()
{
	return (0);
}

uiCmdAccessState PTUDFExAccessDismissCmd (uiCmdAccessMode access_mode)
{
	if (s_inUDFdb)
		return (ACCESS_AVAILABLE);
	else
		return (ACCESS_REMOVE);
}
*/