/*------------------------------------------------------------------------------*
 * File Name:				 													*
 * Creation: 																	*
 * Purpose: OriginC Source C file												*
 * Copyright (c) ABCD Corp.	2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010		*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 * Iris 12/09/2006 v8.0520 NEED_ARRANGE_MARGIN_EVEN_ONLY_ONE_LAYER				*
 * Hong 12/26/06 QA80-8662 ADD_SECOND_HEIGHT_FOR_LINK_LAYER						*
 * Jasmine 02/08/07 LAYER_SET_LINK_AND_UNIT										*
 * Jasmine 02/28/07 DO_THE_SAME_THING_IN_75										*
 * Jasmine 03/06/07 QA70-9433 CONVERT_LAYER_UNITS								*
 * Jasmine 03/12/07 ADD_CUSTOM_TO_AXIS_SCALE_LINK								*
 * Jasmine 03/15/07 ADD_UNDO_BUTTON												*
 * Jasmine 03/22/07 CHANGE_UNIT_FIRST_WHEN_UNLINK								*
 * Jasmine 03/22/07 ADD_GET_AXIS_SHOW											*
 * Iris 06/14/2007 v8.0640 AVOILD_DUP_PLOT_LEGEND_TEXT							*
 * Jasmine 06/15/07 MODIFICATION_SET_COLOR_FUNCTION								*
 * Jake 07/17/07 GET_LEGEND_MODE_TYPE											*
 * Arvin 07/19/07 USE_STRING_TYPE_LEGEND_DIRECTLY as CP said					*
 * Jake 07/19/07 GET_LEGEND_EXTRA_FORMAT										*
 * Folger 07/24/07 GET_RANGE_CONTENT_FROM_DATAPLOT								*
 * Sim 07-30-2007 SKIP_ERR_BARS_WHEN_UPDATE_ADD_LEGEND							*
 * Folger 08/07/07 MAKE_LAYER_SET_LINK_UNDOABLE									*
 * Folger 08/07/07 MAKE_AXIS_SET_SCALE_UNDOABLE									*
 * Jasmine 08/13/07 MAKE_APPLY_UNDOABLE											*
 * Sandy   06/30/08 ADD_VERTICAL_LABEL_PLOT_FUNCTION							*
 * Kyle 08/15/08 QA80-12022 CHANGE_PROTOTYPE_OF_AXIS_SET_SACLE					*
 * Jasmine 01/07/09 v8.0994b QA80-10257 MAKE_AXIS_OPERATION_UNDOABLE			*
 * Iris 01/07/2009 v8.0994b QA80-12887-P3 FIX_page_add_layer_WITH_TEMPLATE_NOT_KEEP_AXIS_COLOR*
 * Jasmine 01/07/09 v8.0994d QA80-10257 REWRTIE_WITH_APPLY_FORMAT_TO_UNDO		*
 * Kyle 01/08/2009 v8.0994d FIX_MISS_MATCH_TITLE_AND_TICKLABEL					*
 *	Folger 01/12/09 v8.0995c AVOID_MULTIPLE_GET_FORMAT_ALL_TO_SPEED_UP_LAYER_MANAGEMENT_ON_SELECTED_LAYER_CHANGE
 *	Kyle 01/15/2009 REWRITE_AXIS_SET_SHOW_TO_JUST_CALL_GETFORMAT_AND_APPLYFORMAT_ONCE
 * Jasmine 01/15/09 QA80-12968 ADD_OFFSET_BETWEEN_PREV_LAYER					*
 * Jasmine 01/16/09 QA80-12968 NO_OFFSET_BETWEEN_PARENT_LAYER					*
 *	Kyle 01/16/2009 CENTRALIZE_CODE_TO_SET_ALL_AXES_ONCE_FOR_PAGE_ADD_LAYER		*
 *	Kyle 01/24/2009 UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION				*
 *	Kyle 01/24/2009 REMOVE_DUPLICATED_CODE_AND_CALL_FUNCTION_IN_PAGE_UTILS		*
 *	Hong 03/19/09 QA80-13282 DATA_PLOT_SAFE_WAY_TO_GET_FORMAT_TREE				*
 *	Jasmine 03/20/09 CENTRAILIZE_ADD_LAYER_CODE_IN_LAYADD_XF_AND_LAYERMANAGEMENT*
 * Kenny 03/24/2009 QA80-13370 ADD_OFFSET_TO_NEWLY_ADDED_LAYER					*
 *	Jasmine 03/24/09 KEEP_CURRENT_LAYER_SELECTED_AFTER_NEW_LAYER_ADD			*
 *	Jasmine 03/25/09 page_add_layer_SHOULD_NOT_ADD_LEGEND						*
* Kenny 03/30/2009 ADD_FUNC_GET_SET_AXIS_POSITION_IN_LOGICAL_UNITS				*
* Kenny 03/30/2009 IMPROVE_FUNC_AXIS_GET_SHOW									*
* Kenny 03/31/2009 ADD_FUNC_GET_INSET_LAYER_SIZE								*
* Kenny 03/31/2009 NO_OFFSET_TO_NEWLY_ADDED_LAYER_IF_NOT_OVERLAPPING			*
* Kenny 03/31/2009 ADD_PARAM_ACTIVE_NEW_LAYER_TO_FUNC_PAGE_ADD_LAYER			*
* Kenny 04/01/2009 QA80-13370 ADD_FUNC_LINKED_LAYER_GET_OUTMOST_LAYER_AXES		*
* Kenny 04/01/2009 ADD_FUNC_GET_LINKED_LAYER_ANCESTOR							*
* Kenny 04/03/2009 ADD_FUNC_MOVE_LAYER_TO_RANDOM_DIR_POS						*
* Kenny 05/31/2009 ADD_FUNC_GET_PLOT_COLOR_FOR_VERTICAL_CURSOR					*
 *	Folger 07/16/09 INCORRECT_ENGLISH_SOURCE_STRINGS_CAME_INTO_OLOCAL			*
 *	Kenny 07/24/2009 QA80-13992 NEW_XF_FOR_GENERAL_MULTI_AXES_PLOTTING			*
 *	Kenny 07/31/2009 QA80-13992 SUPPORT_GET_PAGE_SIZE_FOR_MULTI_Y_AXES_XF		*
 *	Kenny 08/12/2009 QA80-14100 USE_UNDEFINED_STATUS_FOR_UNCOMMON_PROPERTIES	*
 *	Jasmine 08/18/09 GUI_TO_ADD_2ND_NONLINEAR_AXIS								*
 *	Folger 09/10/09 PEAK_LABEL_PLOT_IN_PA_SHOULD_HAVE_CERTAIN_OFFSET			*
 *	Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
 *	Kyle 03/11/2010 PICK_PEAK_ROI_TOOL_SHARES_UTILS_FUNCTION_WITH_PA			*
 *------------------------------------------------------------------------------*/
 
#include <Origin.h>
#include "graph_utils.h"
#include "okThemeID.h"

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////Moved from page_utils///////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
/// SY 2006-11-06 v8.0505 ADD_SCALER_OBJECT
/// Hong 12/26/06 QA80-8662 ADD_SECOND_HEIGHT_FOR_LINK_LAYER
static bool _link_x_and_position_layer(GraphPage& gp, int nLayer, int nParentLayer, int nHeightChangelayer = -1, int nSecondHeight = 50)
/// end ADD_SECOND_HEIGHT_FOR_LINK_LAYER
{
	GraphLayer gl = gp.Layers(nLayer);
	if(!gl)
		return false;
	/// Hong 12/26/06 QA80-8662 ADD_SECOND_HEIGHT_FOR_LINK_LAYER
	//int nOffsetToMoveUp = nLayer - nParentLayer;
	Tree tr;
	int nTopPos = 0;
	if ( -1 == nHeightChangelayer )
	{
		nTopPos = -100 * (nLayer - nParentLayer);
		tr.Root.Dimension.Height.nVal = 100;
	}
	else
	{
		nTopPos = -100 * (nHeightChangelayer - nParentLayer);
		nTopPos = nTopPos - nSecondHeight * ( nLayer - nHeightChangelayer );
		tr.Root.Dimension.Height.nVal = nSecondHeight;
	}
	tr.Root.Dimension.Units.nVal = M_LINK;
	tr.Root.Dimension.Left.nVal = 0;
    //tr.Root.Dimension.Top.nVal = -100 * nOffsetToMoveUp;
	tr.Root.Dimension.Width.nVal = 100;
	//tr.Root.Dimension.Height.nVal = 100;
	tr.Root.Dimension.Top.nVal = nTopPos;
	/// end ADD_SECOND_HEIGHT_FOR_LINK_LAYER
	tr.Root.Link.LinkTo.nVal = nParentLayer + 1;
	tr.Root.Link.XAxisLink.nVal = 1;
	tr.Root.Link.YAxisLink.nVal = 0;
	int nErr = gl.UpdateThemeIDs(tr.Root);
	if(nErr == 0)
	{
		nErr = gl.ApplyFormat(tr, true, true);
	}
	else
		return false;
	
	return true;
}

//nBaseLayer is the reference and all other layers are assume to be setup to use Layer %, so when base layer is resize, the
// other layers will be adjusted accordingly]
/// Hong 12/26/06 QA80-8662 ADD_SECOND_HEIGHT_FOR_LINK_LAYER
//static bool _adjust_height_for_link_x_layer_group(GraphPage& gp, int nBaseLayer, int nLayers, double dTopMargin, double dBottomMargin)
static bool _adjust_height_for_link_x_layer_group(GraphPage& gp, int nBaseLayer, int nLayers, double dTopMargin, double dBottomMargin, int nHeightChangelayer = -1, int nSecondHeight = 50)
/// end ADD_SECOND_HEIGHT_FOR_LINK_LAYER
{
	if(!gp)
		return false;
	
	GraphLayer gl = gp.Layers(nBaseLayer);
	if(!gl)
		return false;
	
	Tree tr;
	tr.Root.Dimension.Units.nVal = M_PERCENT;	// Set as % Page
	gl.UpdateThemeIDs(tr.Root);
	gl.ApplyFormat(tr, true, true);
	
	tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);
	
	// Get the bottom of the first layer
	double dBottomLine;
	if( 0 > dBottomMargin || dBottomMargin > 100 )
		dBottomLine = tr.Root.Dimension.Top.dVal + tr.Root.Dimension.Height.dVal;
	else
		dBottomLine = 100.00 - dBottomMargin;
	
	double dTopLine = dTopMargin; // % page
	/// Hong 12/26/06 QA80-8662 ADD_SECOND_HEIGHT_FOR_LINK_LAYER
	//double dHeight = (dBottomLine - dTopLine) / nLayers;
	double dHeight;
	if ( -1 != nHeightChangelayer )
	{
		dHeight = (dBottomLine - dTopLine) / ( 0.01 * nSecondHeight * ( nLayers - nHeightChangelayer ) + nHeightChangelayer );			
	}
	else
		dHeight = (dBottomLine - dTopLine) / nLayers;
	/// end ADD_SECOND_HEIGHT_FOR_LINK_LAYER
	tr.Root.Dimension.Height.dVal = dHeight;
	tr.Root.Dimension.Top.dVal = dBottomLine - dHeight;
	
	gl.ApplyFormat(tr, true, true);
	return gp.Refresh(TRUE);
}

// dTopMargin and dBottomMargin as %page unit
bool construct_x_link_layers_using_template_layer(GraphPage& gp, int nLayers, int nBaseLayer, double dTopMargin, double dBottomMargin) // = 2.0, -1000
{
	if(!gp)
		return false;
	
	int nTemplateLayer = nBaseLayer + 1;
	GraphLayer gl = gp.Layers(nTemplateLayer);
	if(!gl)
		return false;
	// first del all extra layers, so gp has only 2 layers
	while(gl = gp.Layers(nTemplateLayer + 1))
		gl.Destroy();
	
	gl = gp.Layers(nTemplateLayer);
	for(int ii = nTemplateLayer + 1; ii < nBaseLayer + nLayers; ii++)
	{
		int nNewLayer = gp.AddLayer(gl, 0);
		_link_x_and_position_layer(gp, nNewLayer, nBaseLayer);
	}
	
	return page_arrange_link_layers(gp, nBaseLayer, -1, dTopMargin, dBottomMargin);
}

//only used in arrange_x_link_layers
static void _config_new_layer(GraphLayer& gl)
{
	if(!gl)
		return;	
		
	axis_set_show(gl, AXIS_TOP, true);
	axis_set_show(gl, AXIS_BOTTOM, false);
	

	axis_set_show(gl, AXIS_LEFT, true);
	axis_set_show(gl, AXIS_RIGHT, true);
	axis_label_set_show(gl, AXIS_RIGHT, false);	
}

bool arrange_x_link_layers(GraphPage& gp, int nLayers, double dTopMargin, double dBottomMargin, int nBaseLayer, bool bReuseExistingLayers)
{
	if(!gp)
		return false;
	
	if(!bReuseExistingLayers)
	{
		GraphLayer gl;
		while(gl = gp.Layers(nBaseLayer + 1))
			gl.Destroy();
	}
	
	while(gp.Layers.Count() < nLayers)
	{
		int nn = gp.AddLayer();
		GraphLayer glNew = gp.Layers(nn);
		_config_new_layer(glNew);
	}
	while(gp.Layers.Count() > nLayers)
	{
		int 	nLastLayer = gp.Layers.Count() - 1;
		gp.Layers(nLastLayer).Destroy();
	}
	
	for(int ii = nBaseLayer + 1; ii < nBaseLayer + nLayers; ii++)
	{
		_link_x_and_position_layer(gp, ii, nBaseLayer);
	}	
	
	return page_arrange_link_layers(gp, nBaseLayer, nBaseLayer + nLayers - 1, dTopMargin, dBottomMargin);
	
}
/// Hong 12/26/06 QA80-8662 ADD_SECOND_HEIGHT_FOR_LINK_LAYER
//bool add_x_link_layer(GraphPage& gp, GraphLayer& gl, int nTemplateLayer, int nBaseLayer)
bool add_x_link_layer(GraphPage& gp, GraphLayer& gl, int nTemplateLayer, int nBaseLayer, int nHeightChangelayer, int nSecondHeight) // -1, 50
/// end ADD_SECOND_HEIGHT_FOR_LINK_LAYER
{
	if(!gp)
		return false;
	
	GraphLayer glTemplate = gp.Layers(nTemplateLayer);
	if(!glTemplate)
		return false;
	GraphLayer glBase = gp.Layers(nBaseLayer);
	if(!glBase)
		return false;
	
	int nNewLayer = gp.AddLayer(glTemplate, 0);
	//_link_x_and_position_layer(gp, nNewLayer, nBaseLayer);
	_link_x_and_position_layer(gp, nNewLayer, nBaseLayer, nHeightChangelayer, nSecondHeight); // Hong 12/26/06 QA80-8662 ADD_SECOND_HEIGHT_FOR_LINK_LAYER
	
	gl = gp.Layers(nNewLayer);
	
	return gl ? TRUE : FALSE;
}

// dTopMargin and dBottomMargin as %page unit
/// Hong 12/26/06 QA80-8662 ADD_SECOND_HEIGHT_FOR_LINK_LAYER
//bool page_arrange_link_layers(GraphPage& gp, int nFirstLayer, int nLastLayer, double dTopMargin, double dBottomMargin) // = 2.0, -1000
bool page_arrange_link_layers(GraphPage& gp, int nFirstLayer, int nLastLayer, double dTopMargin, double dBottomMargin, int nHeightChangelayer, int nSecondHeight) // = 2.0, -1000, -1, 50
/// end ADD_SECOND_HEIGHT_FOR_LINK_LAYER
{
	if(!gp)
		return false;
	if(nLastLayer < 0)
	{
		int nLayers = gp.Layers.Count();
		nLastLayer = nLayers - 1;
	}
	int nLayers = nLastLayer -  nFirstLayer + 1;
	/// Iris 12/09/2006 v8.0520 NEED_ARRANGE_MARGIN_EVEN_ONLY_ONE_LAYER, from CP
	//if( nLayers < 2 ) // At last two layers
		//return false;
	///end NEED_ARRANGE_MARGIN_EVEN_ONLY_ONE_LAYER
	
	/*
	if( 2 == nLayers )
	{
		// check if the 2nd layer is empty.
		GraphLayer gl2nd = gp.Layers(1);
			
		if( 0 == gl2nd.DataPlots.Count() )
		{
			// The 2nd layer is empty and should be removed
			gl2nd.Destroy();
			nLayers--;
		}
	}
	*/
	//return _adjust_height_for_link_x_layer_group(gp, nFirstLayer, nLayers, dTopMargin, dBottomMargin);
	return _adjust_height_for_link_x_layer_group(gp, nFirstLayer, nLayers, dTopMargin, dBottomMargin, nHeightChangelayer, nSecondHeight); /// Hong 12/26/06 QA80-8662 ADD_SECOND_HEIGHT_FOR_LINK_LAYER
}
/// end ADD_SCALER_OBJECT



static string _get_axis_name_from_index(int nAxis)
{
	string	strAxis;
	switch(nAxis)
	{
	case AXIS_BOTTOM:
	case AXIS_TOP:
		strAxis = "X";
		break;
	case AXIS_LEFT:
	case AXIS_RIGHT:
		strAxis = "Y";
		break;
	default:
		strAxis = "";
		break;
	}
	return strAxis;	
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////GraphPage settings//////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
bool	page_zoom(GraphPage& gp)
{
	if(!gp)
		return false;
	return gp.LT_execute("Page.zoomWhole=0;");
}

bool	is_valid_layer_index(GraphPage& gp, int index)
{
	if( gp && index >= 0 && index < gp.Layers.Count() )
		return true;
	return false;
}
///Jasmine 02/08/07 LAYER_SET_LINK_AND_UNIT
///Jasmine 03/12/07 ADD_CUSTOM_TO_AXIS_SCALE_LINK
bool	layer_set_link(GraphLayer& gl, int nParentLayer, int XAxis, int YAxis, int* pnUnit, string strX1, string strX2, string strY1, string strY2)//0,0,0,NULL
{
	if(!gl)
		return false;
	///-----Jasmine 02/09/07 it seems ApplyFormat will make the legend move left, so use LT instead
	GraphPage 	gp;
	gl.GetParent(gp);
	Tree	tr;
	tr.Root.Link.LinkTo.nVal = nParentLayer + 1;
	int nOldLinkTo;
	if(-1 == nParentLayer && layer_get_link(gl, nOldLinkTo) && -1 < nOldLinkTo)//0 offset
	{	///Jasmine 03/22/07 CHANGE_UNIT_FIRST_WHEN_UNLINK
		//if unlink, change unit first, so that when undo it will change back the unit later
		Tree trUnit;
		trUnit.Root.Dimension.Units.nVal = M_PERCENT;
		gl.UpdateThemeIDs(trUnit.Root);
		//------ Folger 08/07/07 MAKE_LAYER_SET_LINK_UNDOABLE
		//gl.ApplyFormat(trUnit, true, true);
		gl.ApplyFormat(trUnit, true, true, true);
		//------ End MAKE_LAYER_SET_LINK_UNDOABLE
	}	///End CHANGE_UNIT_FIRST_WHEN_UNLINK
	if( is_valid_layer_index(gp, nParentLayer) )
	{
		if(2 == XAxis)//Custom
		{
			tr.Root.Link.X1.strVal = strX1;
			tr.Root.Link.X2.strVal = strX2;
		}
		if(2 == YAxis)//Custom
		{
			tr.Root.Link.Y1.strVal = strY1;
			tr.Root.Link.Y2.strVal = strY2;
		}
		tr.Root.Link.XAxisLink.nVal = XAxis;
		tr.Root.Link.YAxisLink.nVal = YAxis;
		if(pnUnit)
			tr.Root.Dimension.Units.nVal = *pnUnit;
	}
	gl.UpdateThemeIDs(tr.Root);
	//------ Folger 08/07/07 MAKE_LAYER_SET_LINK_UNDOABLE
	//return gl.ApplyFormat(tr, true, true);	
	return gl.ApplyFormat(tr, true, true, true);
	//------ End MAKE_LAYER_SET_LINK_UNDOABLE
	/*
	string strLT, strLT2;
	strLT.Format("layer.link = %d; layer.x.link = %d; layer.y.link = %d", nParentLayer+1, XAxis, YAxis);
	if(pnUnit)
	{
		strLT2.Format(";layer.unit = %d", *pnUnit + 1);
		strLT += strLT2;
	}
	return gl.LT_execute(strLT);
	*/
	///-----
}
///------ Folger 01/12/09 v8.0995c AVOID_MULTIPLE_GET_FORMAT_ALL_TO_SPEED_UP_LAYER_MANAGEMENT_ON_SELECTED_LAYER_CHANGE
//bool 	layer_get_link(GraphLayer& gl, int& nParentLayer, int* pXAxis, int* pYAxis, int* pnUnit, string* pX1, string* pX2, string* pY1, string* pY2)//NULL, NULL, NULL
bool 	layer_get_link(GraphLayer& gl, int& nParentLayer, int* pXAxis, int* pYAxis, int* pnUnit, string* pX1, string* pX2, string* pY1, string* pY2, TreeNode& trFmt/* = NULL*/)//NULL, NULL, NULL
///------ End AVOID_MULTIPLE_GET_FORMAT_ALL_TO_SPEED_UP_LAYER_MANAGEMENT_ON_SELECTED_LAYER_CHANGE
{
	if(!gl)
		return false;
	Tree	tr;
	///------ Folger 01/12/09 v8.0995c AVOID_MULTIPLE_GET_FORMAT_ALL_TO_SPEED_UP_LAYER_MANAGEMENT_ON_SELECTED_LAYER_CHANGE
	if ( trFmt )
		tr = trFmt;
	else
	///------ End AVOID_MULTIPLE_GET_FORMAT_ALL_TO_SPEED_UP_LAYER_MANAGEMENT_ON_SELECTED_LAYER_CHANGE
		tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);
	if(!tr.Root.Link.IsValid() || !tr.Root.Link.LinkTo.IsValid())
		return false;
	nParentLayer = tr.Root.Link.LinkTo.nVal - 1;
	if(pXAxis)
	{
		*pXAxis = tr.Root.Link.XAxisLink.IsValid()? tr.Root.Link.XAxisLink.nVal : 0;
		if(2 == *pXAxis)
		{
			if(pX1)
				*pX1 = tr.Root.Link.X1.IsValid()? tr.Root.Link.X1.strVal : "";
			if(pX2)
				*pX2 = tr.Root.Link.X2.IsValid()? tr.Root.Link.X2.strVal : "";
		}
	}
	if(pYAxis)
	{
		*pYAxis = tr.Root.Link.YAxisLink.IsValid()? tr.Root.Link.YAxisLink.nVal : 0;
		if(2 == *pYAxis)
		{
			if(pY1)
				*pY1 = tr.Root.Link.Y1.IsValid()? tr.Root.Link.Y1.strVal : "";
			if(pY2)
				*pY2 = tr.Root.Link.Y2.IsValid()? tr.Root.Link.Y2.strVal : "";
		}
	}
	if(pnUnit)
		*pnUnit = tr.Root.Dimension.Units.nVal;
	return true;
}
///End ADD_CUSTOM_TO_AXIS_SCALE_LINK
///End LAYER_SET_LINK_AND_UNIT

///Kyle 01/24/2009 REMOVE_DUPLICATED_CODE_AND_CALL_FUNCTION_IN_PAGE_UTILS
/////Kyle 01/16/2009 CENTRALIZE_CODE_TO_SET_ALL_AXES_ONCE_FOR_PAGE_ADD_LAYER
//static bool _page_add_layer_set_axes(GraphLayer& gl, const vector<bool>& vbShow)
//{
	//if(!gl)
		//return false;
	//Tree tr;
	//tr = gl.GetFormat(FPB_ALL, FOB_AXIS | FOB_AXIS_LABELS | FOB_LABELS, true, true);
	//TreeNode trAxes = tr.Root.Axes;
	//if(!trAxes)
	//{
		//ASSERT(false);
		//return false;
	//}
	//
	//TreeNode trTicks, trLabels, trTiles;
	//for(int nAxis = 0; nAxis < 4; nAxis++)
	//{
		//bool bShow = vbShow[nAxis];
		//int nDefaultTickStyle = -1;
		//switch(nAxis)
		//{
		//case AXIS_BOTTOM:
			//trTicks = tree_get_node_by_tagname(trAxes, "BottomTicks", true);
			//trLabels = tree_get_node_by_tagname(trAxes, "BottomLabels", true);
			//trTiles = tree_get_node_by_tagname(trAxes, "BottomTitle", true);
			//nDefaultTickStyle = 2;	// out
			//break;
		//
		//case AXIS_LEFT:
			//trTicks = tree_get_node_by_tagname(trAxes, "LeftTicks", true);
			//trLabels = tree_get_node_by_tagname(trAxes, "LeftLabels", true);
			//trTiles = tree_get_node_by_tagname(trAxes, "LeftTitle", true);
			//nDefaultTickStyle = 2;	// out
			//break;
			//
		//case AXIS_TOP:
			//trTicks = tree_get_node_by_tagname(trAxes, "TopTicks", true);
			//trLabels = tree_get_node_by_tagname(trAxes, "TopLabels", true);
			//trTiles = tree_get_node_by_tagname(trAxes, "TopTitle", true);
			//nDefaultTickStyle = 1;	// in
			//break;
			//
		//case AXIS_RIGHT:
			//trTicks = tree_get_node_by_tagname(trAxes, "RightTicks", true);
			//trLabels = tree_get_node_by_tagname(trAxes, "RightLabels", true);
			//trTiles = tree_get_node_by_tagname(trAxes, "RightTitle", true);
			//nDefaultTickStyle = 1;	// in
			//break;
	//
		//default:
			//ASSERT(false);
			//return false;
		//}
		//if(!trTicks || !trLabels || !trTiles)
			//return false;
	//
		//TreeNode trShow;
		//trShow = trTicks.GetNode("Show");
		//if(!trShow)
			//return false;
		//trShow.nVal = bShow;
		//if(bShow)
		//{
			//if(trTicks.Major)
				//trTicks.Major.nVal = nDefaultTickStyle;
			//if(trTicks.Minor)
				//trTicks.Minor.nVal = nDefaultTickStyle;
		//}
		//
		//trShow = trLabels.GetNode("Show");
		//if(!trShow)
			//return false;
		//trShow.nVal = bShow;
		//
		//trShow = trTiles.GetNode("Show");
		//if(!trShow)
			//return false;
		//trShow.nVal = bShow;
	//}
	//
	//return gl.ApplyFormat(tr, true, true, true);
//
//}
/////End CENTRALIZE_CODE_TO_SET_ALL_AXES_ONCE_FOR_PAGE_ADD_LAYER
///End REMOVE_DUPLICATED_CODE_AND_CALL_FUNCTION_IN_PAGE_UTILS

///Jasmine 03/20/09 CENTRAILIZE_ADD_LAYER_CODE_IN_LAYADD_XF_AND_LAYERMANAGEMENT
//	adding_*_layer must be consistent with STR_LAYER_TYPE_LIST_E  
enum{
	adding_normal_layer		=0,
	
	adding_link_layer_begin = adding_normal_layer + 1,
	adding_topx_layer 		= adding_link_layer_begin,
	adding_righty_layer,
	adding_lefty_layer,
	adding_txry_layer,
	adding_bxry_layer,
	adding_inset_layer,
	adding_insetdata_layer,
	adding_noxy_layer,
	adding_link_layer_end 	= adding_noxy_layer,
	
	adding_layer_total
};

#define STR_LAYER_TYPE_LIST_E	_LE("Bottom-X Left-Y|Top-X (Linked Y Scale and Dimension)|Right-Y (Linked X Scale and Dimension)|Left-Y (Linked X Scale and Dimension)|Top-X Right-Y (Linked Dimension)|Bottom-X Right-Y (Linked Dimension)|Inset (Linked Dimension)|Inset With Data (Linked Dimension)|No Axes (Linked XY Scale and Dimension)")

bool AddingLayerType::IsValid(int nAddingType)
{
	string strTypeList = GetTypeList(false);	
	if(strTypeList.GetNumTokens('|') != adding_layer_total)
		return false;
	
	return 0 <= nAddingType && nAddingType < adding_layer_total;
}
string 	AddingLayerType::GetTypeList(bool bLocalizd)
{
	if(bLocalizd)
		///------ Folger 07/16/09 INCORRECT_ENGLISH_SOURCE_STRINGS_CAME_INTO_OLOCAL
		/// should not use _L(LE("...")), that will make string like "etLocalizedEx("...",NULL,1" run into oLocal.txt
		//return _L(STR_LAYER_TYPE_LIST_E);
		return GetLocalized(STR_LAYER_TYPE_LIST_E);
		///------ End INCORRECT_ENGLISH_SOURCE_STRINGS_CAME_INTO_OLOCAL
	
	return STR_LAYER_TYPE_LIST_E;
}

string 	AddingLayerType::GetAddingName(int nAddingType)
{
	ASSERT( IsValid(nAddingType) );
	
	string strTypeList = STR_LAYER_TYPE_LIST_E;
	
	string strName = strTypeList.GetToken(nAddingType, '|');
	if( strName.IsEmpty() )
		return strName;
	
	int nLeftParentheses = strName.Find('(');
	if(nLeftParentheses >= 0)
		strName = strName.Left(nLeftParentheses);
	
	strName.MakeValidCName();
	ASSERT( !strName.IsEmpty() );
	
	return strName;
}	

bool AddingLayerType::GetAxisSettings(int nAddingType, int& bottom, int& left, int& top, int& right, bool& link, int& xaxis, int& yaxis)
{
	ASSERT( IsValid(nAddingType) );
	
	switch(nAddingType)
	{
	case adding_normal_layer:
		bottom 	= left 	= true;
		top 	= right	= false;
		link 	= false;
		xaxis 	= yaxis = LINK_NONE;
		break;
	case adding_topx_layer:
		bottom = left = right = false;
		top	 	= true;
		link 	= true;
		xaxis = LINK_NONE;
		yaxis = LINK_STRAIGHT;
		break;
	case adding_righty_layer:
	case adding_lefty_layer:
		bottom = top = false;
		left	= adding_lefty_layer == nAddingType;
		right 	= adding_righty_layer == nAddingType;
		link 	= true;
		xaxis = LINK_STRAIGHT;
		yaxis = LINK_NONE;
		break;
	case adding_txry_layer:
	case adding_bxry_layer:
		bottom 	= adding_bxry_layer == nAddingType;
		top 	= adding_txry_layer == nAddingType;
		left 	= false;
		right 	= true;
		link 	= true;
		xaxis = yaxis = LINK_NONE;
		break;
	case adding_inset_layer:
	case adding_insetdata_layer:
		bottom 	= left 	= true;
		top 	= right	= false;
		link 	= true;
		xaxis = yaxis = LINK_NONE;		
		break;
	case adding_noxy_layer:
		bottom = left = top = right = false;
		link 	= true;
		xaxis = yaxis = LINK_STRAIGHT;		
		break
		
	default:
		return false;
	}	
	
	return true;	
}

bool AddingLayerType::IsLink(int nAddingType)
{
	ASSERT( IsValid(nAddingType) );
	return adding_link_layer_begin <= nAddingType && nAddingType <= adding_link_layer_end;
}

bool AddingLayerType::HasNoXAxis(int nAddingType)
{
	ASSERT( IsValid(nAddingType) );
	
	bool bHasX = false;
	switch(nAddingType)
	{
	case adding_normal_layer:
	case adding_topx_layer:
	case adding_txry_layer:
	case adding_bxry_layer:
	case adding_inset_layer:
	case adding_insetdata_layer:
		bHasX = true;	
	}
	
	return !bHasX;
}

bool AddingLayerType::HasNoYAxis(int nAddingType)
{
	ASSERT( IsValid(nAddingType) );
	
	bool bHasY = false;
	switch(nAddingType)
	{
	case adding_normal_layer:
	case adding_righty_layer:
	case adding_lefty_layer:
	case adding_txry_layer:
	case adding_bxry_layer:
	case adding_inset_layer:
	case adding_insetdata_layer:
		bHasY = true;	
	}
	
	return !bHasY;
}

bool AddingLayerType::IsInset(int nAddingType)
{
	ASSERT( IsValid(nAddingType) );
	return nAddingType == adding_inset_layer || nAddingType == adding_insetdata_layer;
}

bool AddingLayerType::IsInsetData(int nAddingType)
{
	ASSERT( IsValid(nAddingType) );
	return nAddingType == adding_insetdata_layer;
}

///Kenny 03/31/2009 ADD_FUNC_GET_INSET_LAYER_SIZE
void AddingLayerType::GetInsetLayerSize( int* pnLeft, int* pnTop, int* pnWidth, int* pnHeight )
{
	if (pnLeft)
		*pnLeft		= 75;
	if (pnTop)
		*pnTop		= 0;
	if (pnWidth)
		*pnWidth	= 25;
	if (pnHeight)
		*pnHeight	= 25;
}
///End ADD_FUNC_GET_INSET_LAYER_SIZE
///End CENTRAILIZE_ADD_LAYER_CODE_IN_LAYADD_XF_AND_LAYERMANAGEMENT

///Kenny 03/24/2009 QA80-13370 ADD_OFFSET_TO_NEWLY_ADDED_LAYER
class LayerAxisInfo 
{
public:
	int		nUnits;					// The units type
	//string	strGraphPageName;		// Parent GraphPage's name
	int		nLayerIndex;			// Layer's index number
	double	dLayerPos[TOTAL_POS];	// The position/coordinates of GraphLayer, in M_PIXEL units
	bool	bAxisShow[AXIS_TOTAL];	// The show/hide state of 4 axes, true means shown
	int		nAxisPos[AXIS_TOTAL];	// The position/coordinates of 4 axes, in M_PIXEL units
};

static int _get_layer_axis_info(const GraphLayer& gl, LayerAxisInfo& info)
{
	const int nCurrentUnits = gl.GetPosition(info.dLayerPos);
	if (M_PIXEL != nCurrentUnits && !gl.UnitsConvert(M_PIXEL, info.dLayerPos, nCurrentUnits) )
		return -1;

	if ( !axis_get_show(gl, &info.bAxisShow[AXIS_BOTTOM], &info.bAxisShow[AXIS_LEFT], &info.bAxisShow[AXIS_TOP], &info.bAxisShow[AXIS_RIGHT]) )
		return -2;

	// Note: These axes position are in M_PIXEL units
	_get_axis_position_in_logical_units(gl, info.nAxisPos);

	info.nUnits = M_PIXEL;
/*
	GraphPage gp = gl.GetPage();
	ASSERT(gp);
	info.strGraphPageName = gp.GetName();
*/
	info.nLayerIndex = gl.GetIndex();
	return 0;
}

///Kenny 04/01/2009 ADD_FUNC_GET_LINKED_LAYER_ANCESTOR
int get_linked_layer_ancestor(const GraphPage& gp, const uint nLayerIndex, bool bGetOverlappedLayer/* = false*/)
{
	if (!gp)
		return -1;
	int nAncestorLayer = -1;
	int nChildLayerIndex = nLayerIndex;
	while (true)
	{
		const GraphLayer glChild = gp.Layers(nChildLayerIndex);
		if (!glChild)
			break;
		int nParentLayer = get_linked_parent_layer(glChild);
		if (nParentLayer >= 0)
		{
			if (bGetOverlappedLayer)
			{
				double dLayerPos[TOTAL_POS];
				if ( M_LINK != glChild.GetPosition(dLayerPos) 
					|| 100 != dLayerPos[WIDTH_POS] 
					|| 100 != dLayerPos[HEIGHT_POS] 
					|| 0 != dLayerPos[LEFT_POS] 
					|| 0 != dLayerPos[TOP_POS])
					break;
			}
			nAncestorLayer = nChildLayerIndex = nParentLayer;
		}
		else
			break;
	}
	return nAncestorLayer;
}
///End ADD_FUNC_GET_LINKED_LAYER_ANCESTOR

// Get the layer indices that have the linked relationship and save them into vnLayerFamily
// Return the size of vnLayerFamily
static int _get_linked_layer_family(const GraphPage& gp, const int nTargetLayerIndex, vector<uint>& vnLayerFamily, const bool bUseTargetAsAncestor = true)
{
	// Here's an example of linked layer family
	// The number enclosed with square brackets[] is the layer index
	//      Ancestor-> [1]
	//               /    \
	//             [2]    [4]
	//            /   \
	// Target-> [5]   [9]
	//         /  \     \
	//      [7]   [8]   [10]
	//           /  \
	//         [12]	[13]

	const int nTotalLayerNum = gp.Layers.Count();
	if ( nTotalLayerNum <= 0 || !is_valid_layer_index(gp, nTargetLayerIndex) )
		return 0;

	if ( 1 == nTotalLayerNum )	// No need to do more action
	{
		vnLayerFamily.RemoveAll();
		vnLayerFamily.Add(nTargetLayerIndex);
		return vnLayerFamily.GetSize();
	}

	// 1. Get all layers' parent indices and store them in order
	int nLinkedLayerNum = 1;
	vector<int> vnLayerParent(nTotalLayerNum);
	ASSERT( vnLayerParent.GetSize() == nTotalLayerNum );

	for (int ii = 0; ii < nTotalLayerNum; ++ii)
	{
		const GraphLayer gl = gp.Layers(ii);
		if( (vnLayerParent[ii] = get_linked_parent_layer(gl)) >= 0 )
			++nLinkedLayerNum;
	}
	if (nLinkedLayerNum <= 0)
		return 0;

	// 2. Find the root ancestor layer
	int nAncestorLayer = nTargetLayerIndex;
	if (!bUseTargetAsAncestor)
	{
		while (true)
		{
			int nParentLayer = vnLayerParent[nAncestorLayer];
			if (nParentLayer >= 0)
				nAncestorLayer = nParentLayer;
			else
				break;
		}
	}

	// 3. Find out all the family layers, starts from the root ancestor layer, in level-order
	vnLayerFamily.RemoveAll();
	vnLayerFamily.Add(nAncestorLayer);
	int nLayerFamilyNum = 1;

	vector<uint> vnParentIndices;
	vnParentIndices.Add(nAncestorLayer);
	vector<uint> vnChildrenIndices;
	const uint wBitwiseOption = MATREPL_TEST_EQUAL;

	while (true)
	{
		int nTotalChildrenNum = 0;
		vector<uint> vntmpParentIndices;
		for (int ii = 0; ii < vnParentIndices.GetSize(); ++ii)
		{
			int nChildrenNum = vnLayerParent.Find(wBitwiseOption, vnParentIndices[ii], vnChildrenIndices);
			if (nChildrenNum > 0)
			{
				vntmpParentIndices.Append(vnChildrenIndices);
				nTotalChildrenNum += nChildrenNum;
			}
		}
		if (nTotalChildrenNum > 0)
		{
			vnParentIndices = vntmpParentIndices;
			vnLayerFamily.Append(vnParentIndices);
			nLayerFamilyNum += nTotalChildrenNum;
		}
		else
			break;
	}
	return vnLayerFamily.GetSize();
}

#define IS_X_AXIS_TYPE(nAxisType)	(AXIS_BOTTOM == nAxisType || AXIS_TOP == nAxisType)

// Return true if specified axis matchs all of the following conditions:
// 1) outside the bounding box
// 2) but not outside the range of relevant axis bound(not overlapping) in parallel direction
// nOverBoundAxisType = [output] The axis type that nCheckingAxisType is over bounding
static bool _is_axis_over_bound(const int nCheckingAxisType, const int* pnBoundingBox, const LayerAxisInfo& infoSrc, int& nOverBoundAxisType, int& nParallelOffset)
{
	// Note: if axis overlap(placed at the bounding position) with the box, we still return true
	//
	//                 |
	//                 | <- this Y axis is NOT over bound, since it's not overlapping with the right bound of the box
	//                 |
	//                 |
	//                      <- not overlapping
	//  ++++++++++
	//  +      | + <- pnBoundingBox
	//  +      | +
	//	+      | +     |    <- overlapping
	//  ++++++++++     |
	//         ^       |  <- this Y axis is over bound
	//         |       |     nCheckingAxisType may be either AXIS_LEFT or AXIS_RIGHT, AXIS_RIGHT == nOverBoundAxisType
	//         |
	//     this Y axis is NOT over bound

	/*	Example (nCheckingAxisType == AXIS_RIGHT):
											 |- nParallelOffset
											 V
											| |
									+++++++++ |
									+		+ |
		-1 == nFindDirection <--	+		+ |  --> Find from-left-to-right, 1 == nFindDirection 
									+		+ |
									+++++++++ |
	*/
	// nFindDirection > 0 means find towards right/bottom side, else towards left/top side

	if ( !infoSrc.bAxisShow[nCheckingAxisType] )
		return false;

	const bool bIsCheckingXAxis = IS_X_AXIS_TYPE(nCheckingAxisType);

	const int nAxisPairTypeFirst = bIsCheckingXAxis ? AXIS_TOP : AXIS_LEFT;
	const int nAxisPairTypeSecond = bIsCheckingXAxis ? AXIS_BOTTOM : AXIS_RIGHT;

	nOverBoundAxisType = infoSrc.nAxisPos[nCheckingAxisType] <= pnBoundingBox[nAxisPairTypeFirst] ? nAxisPairTypeFirst : nAxisPairTypeSecond;

	nParallelOffset = infoSrc.nAxisPos[nCheckingAxisType] - pnBoundingBox[nOverBoundAxisType];
	const int nFindDirection = (AXIS_BOTTOM == nOverBoundAxisType || AXIS_RIGHT == nOverBoundAxisType) ? 1 : -1;

	// Checks to see if this axis is outside the bounding box
	if (nParallelOffset * nFindDirection < 0)
		return false;

    //              ++++++++++
	//			    +        +
	//			    +        +   <-- pnBoundingBox
	//			    +        +
	//			    ++++++++++
    //      | <--dLeftBound   | <--dRightBound
    //      |                 |
    //      ++++++++          ++++++++

	if ( bIsCheckingXAxis )	// X Axes
	{
		const double dLeftBound		= pnBoundingBox[AXIS_LEFT] - infoSrc.dLayerPos[WIDTH_POS];
		const double dRightBound	= pnBoundingBox[AXIS_LEFT] + pnBoundingBox[WIDTH_POS];
		if (infoSrc.dLayerPos[LEFT_POS] <= dLeftBound || infoSrc.dLayerPos[LEFT_POS] >= dRightBound)
			return false;
	}
	else	// Y Axes
	{
		const double dTopBound		= pnBoundingBox[AXIS_TOP] - infoSrc.dLayerPos[HEIGHT_POS];
		const double dBottomBound	= pnBoundingBox[AXIS_TOP] + pnBoundingBox[HEIGHT_POS];
		if (infoSrc.dLayerPos[TOP_POS] <= dTopBound || infoSrc.dLayerPos[TOP_POS] >= dBottomBound)
			return false;
	}
	return true;
}
///End ADD_OFFSET_TO_NEWLY_ADDED_LAYER

///Kenny 03/30/2009 ADD_FUNC_GET_SET_AXIS_POSITION_IN_LOGICAL_UNITS

// nAxisType should be: AXIS_BOTTOM/AXIS_LEFT/.../etc
/// Kenny 07/24/2009 QA80-13992 NEW_XF_FOR_GENERAL_MULTI_AXES_PLOTTING, centralize codes for xf plotmaxes
//static AxisObject _get_axis_object(const GraphLayer& gl, const int nAxisType)
AxisObject get_axis_object(const GraphLayer& gl, const int nAxisType)
/// End QA80-13992 NEW_XF_FOR_GENERAL_MULTI_AXES_PLOTTING
{
	AxisObject ao;
	switch (nAxisType)
	{
	case AXIS_BOTTOM:
		ao = gl.XAxis.AxisObjects(AXISOBJPOS_AXIS_FIRST);
		break;
	case AXIS_LEFT:
		ao = gl.YAxis.AxisObjects(AXISOBJPOS_AXIS_FIRST);
		break;
	case AXIS_TOP:
		ao = gl.XAxis.AxisObjects(AXISOBJPOS_AXIS_SECOND);
		break;
	case AXIS_RIGHT:
		ao = gl.YAxis.AxisObjects(AXISOBJPOS_AXIS_SECOND);
		break;
	}
	return ao;
}

// pnAxisPos should be an array for storing four axes's position, pnAxisPos[AXIS_TOTAL]
static int _get_axis_position_in_logical_units(const GraphLayer& gl, int* pnAxisPos)
{
	for (int nAxisType = 0; pnAxisPos && nAxisType < AXIS_TOTAL; ++nAxisType)
	{
		int nAxisPosInPixelUnit;
		/// Kenny 07/24/2009 QA80-13992 NEW_XF_FOR_GENERAL_MULTI_AXES_PLOTTING
		//double dScalePos = _get_axis_object(gl, nAxisType).GetPosition(NULL, NULL, &pnAxisPos[nAxisType]);
		double dScalePos = get_axis_object(gl, nAxisType).GetPosition(NULL, NULL, &pnAxisPos[nAxisType]);
		/// End QA80-13992 NEW_XF_FOR_GENERAL_MULTI_AXES_PLOTTING
	}
	return 0;
}

static int _set_axis_position_in_logical_units(const GraphLayer& gl, const int* pnAxisPos)
{
	for (int nAxisType = 0; pnAxisPos && nAxisType < AXIS_TOTAL; ++nAxisType)
	{
		/// Kenny 07/24/2009 QA80-13992 NEW_XF_FOR_GENERAL_MULTI_AXES_PLOTTING
		//_get_axis_object(gl, nAxisType).SetPosition(AXIS_POS_LOGICAL, pnAxisPos[nAxisType]);
		get_axis_object(gl, nAxisType).SetPosition(AXIS_POS_LOGICAL, pnAxisPos[nAxisType]);
		/// End QA80-13992 NEW_XF_FOR_GENERAL_MULTI_AXES_PLOTTING
	}
	return 0;
}
///End ADD_FUNC_GET_SET_AXIS_POSITION_IN_LOGICAL_UNITS

///Kenny 04/01/2009 QA80-13370 ADD_FUNC_LINKED_LAYER_GET_OUTMOST_LAYER_AXES
// Find the target layer's outmost axes and the layer they belonged to from the linked layer family.
int linked_layer_get_outmost_layer_axes(
	const GraphLayer& gl, 
	const bool* pbGetAxes, 
	int* pnOutmostAxesPos, int* pnOutmostLayerIndices, int* pnOutmostAxesType, 
	const uint nTolerance/* = 0*/)
{
	// 1. Error parameters checking
	if (!gl || !pbGetAxes || !pnOutmostAxesPos || !pnOutmostLayerIndices || !pnOutmostAxesType)
		return -1;

	GraphPage gp = gl.GetPage();
	if (!gp)
		return -2;

	// 2. Get all the layers that are in the same linked layer family
	const int nLayerIndex = gl.GetIndex();
	vector<uint> vnLayerFamily;
	int nFamilyLayerNum = _get_linked_layer_family(gp, nLayerIndex, vnLayerFamily);
	ASSERT( 1 <= nFamilyLayerNum );
	if (nFamilyLayerNum <= 0)
		return -3;

	// 3. Init variables
	LayerAxisInfo infoLayer;
	if( 0 != _get_layer_axis_info(gl, infoLayer) )
		return -4;

	// Default position, that means zero offset, each axis should be placed at bottommost/leftmost/.../etc of the layer it belongs
	int nDefaultPosition[AXIS_TOTAL];
	nDefaultPosition[AXIS_BOTTOM]	= infoLayer.dLayerPos[TOP_POS] + infoLayer.dLayerPos[HEIGHT_POS];
	nDefaultPosition[AXIS_LEFT]		= infoLayer.dLayerPos[LEFT_POS];
	nDefaultPosition[AXIS_TOP]		= infoLayer.dLayerPos[TOP_POS];
	nDefaultPosition[AXIS_RIGHT]	= infoLayer.dLayerPos[LEFT_POS] + infoLayer.dLayerPos[WIDTH_POS];

	// Note: All of these value are in logical units (M_PIXEL)
	int nAxisType;
	for (nAxisType = 0; nAxisType < AXIS_TOTAL; ++nAxisType)
		pnOutmostAxesPos[nAxisType]	= nDefaultPosition[nAxisType];

	bool bDefaultPosHasAxis[AXIS_TOTAL] = {0, 0, 0, 0};

	// 4. Loop through the layer family to find the "outmost" axes
	for (int ii = 0; ii < nFamilyLayerNum; ++ii)
	{
		const GraphLayer glTmp = gp.Layers(vnLayerFamily[ii]);
		ASSERT(glTmp.IsValid());
		if (!glTmp)
			continue;

		LayerAxisInfo infoOther;
		if ( 0 != _get_layer_axis_info(glTmp, infoOther) )
			continue;

		// Check all the four axes
		for (int nCheckingAxisType = 0; nCheckingAxisType < AXIS_TOTAL; ++nCheckingAxisType)
		{
			int nOverBoundAxisType;
			int nParallelOffset;

			bool bIsOverDefaultBound = _is_axis_over_bound(nCheckingAxisType, nDefaultPosition, infoOther, nOverBoundAxisType, nParallelOffset);
			if ( bIsOverDefaultBound && pbGetAxes[nOverBoundAxisType] )
			{
				if ( !bDefaultPosHasAxis[nOverBoundAxisType] && abs(nParallelOffset) <= nTolerance )
					bDefaultPosHasAxis[nOverBoundAxisType] = true;
			}
			else
				continue;
			bool bIsOverUpdatedBound = _is_axis_over_bound(nCheckingAxisType, pnOutmostAxesPos, infoOther, nOverBoundAxisType, nParallelOffset);
			if ( bIsOverUpdatedBound && pbGetAxes[nOverBoundAxisType] )
			{
				pnOutmostAxesPos[nOverBoundAxisType]		= infoOther.nAxisPos[nCheckingAxisType];
				pnOutmostLayerIndices[nOverBoundAxisType]	= infoOther.nLayerIndex;
				pnOutmostAxesType[nOverBoundAxisType]		= nCheckingAxisType;
			}
		}
	}
	for (nAxisType = 0; nAxisType < AXIS_TOTAL; ++nAxisType)
	{
		if ( !pbGetAxes[nAxisType] || !bDefaultPosHasAxis[nAxisType] )
			pnOutmostAxesPos[nAxisType] = pnOutmostLayerIndices[nAxisType] = pnOutmostAxesType[nAxisType] = -1;
	}
	return 0;
}
///End ADD_FUNC_LINKED_LAYER_GET_OUTMOST_LAYER_AXES

///Kenny 03/24/2009 QA80-13370 ADD_OFFSET_TO_NEWLY_ADDED_LAYER
#define DEFAULT_ADD_LAYER_MIN_OFFSET	100
// When adding a GraphLayer, we should place this newly added layer at the same 
// position (and with the same width/height) as the parent layer (nLinkTo).
//
// Then we will loop and check all the linked layer family to 
// see if any axis of them was placed at the default position. If true, an offset 
// towards outside of the parent layer's bounding box will be set to that axis, 
// and we should place that axis at the outmost of the layer. 
// In addition, we will make all of these axes have the same tick value(in/out) with the outmost axes.
// 
// About linked layer family:
// They are the newly added layer's parent and all of its child layers
static int _move_overlapped_link_layer_axes_to_available_pos(
	const GraphPage& gp, 
	const int nNewLayerIndex, 
	const int nLinkTo, 
	const bool bNoOffset = false,	///Kenny 03/31/2009 NO_OFFSET_TO_NEWLY_ADDED_LAYER_IF_NOT_OVERLAPPING
	const uint nMinOffset = DEFAULT_ADD_LAYER_MIN_OFFSET,
	const uint nTolerance = 0)
{
	// 1. Init and check errors
	const bool bValidLayerIndices = is_valid_layer_index(gp, nNewLayerIndex) && is_valid_layer_index(gp, nLinkTo);
	ASSERT(bValidLayerIndices);
	if ( !bValidLayerIndices )
		return -1;

	GraphLayer glNew = gp.Layers(nNewLayerIndex);
	GraphLayer glParent = gp.Layers(nLinkTo);
	if ( !glNew || !glParent )
		return -2;

	double dParentLayerPos[TOTAL_POS];	// We use parent layer's default position as the new layer's position
	int nCurrentUnits = glParent.GetPosition(dParentLayerPos);
	if ( M_PERCENT != nCurrentUnits && !glParent.UnitsConvert(M_PERCENT, dParentLayerPos, nCurrentUnits))
		return -3;

	// 2. Set new layer's position first, since we don't need to give this layer any offset but its axes
	if ( !glNew.SetPosition(dParentLayerPos, M_PERCENT, OCD_UNDO) )
		return -4;

	///Kenny 03/31/2009 NO_OFFSET_TO_NEWLY_ADDED_LAYER_IF_NOT_OVERLAPPING
	if (bNoOffset)	// no more action should be done if bNoOffset is true
		return 0;
	///End NO_OFFSET_TO_NEWLY_ADDED_LAYER_IF_NOT_OVERLAPPING

	LayerAxisInfo infoNewLayer;
	if( 0 != _get_layer_axis_info(glNew, infoNewLayer) )
		return -5;

	int nNewLayerAxisCount;
	for (int nAxisType = 0; nAxisType < AXIS_TOTAL; ++nAxisType)
		nNewLayerAxisCount += infoNewLayer.bAxisShow[nAxisType];
	if (nNewLayerAxisCount <= 0)		// Zero axis? No need to give it offset
		return -6;

	infoNewLayer.nAxisPos[AXIS_BOTTOM]	= infoNewLayer.dLayerPos[TOP_POS] + infoNewLayer.dLayerPos[HEIGHT_POS];
	infoNewLayer.nAxisPos[AXIS_LEFT]	= infoNewLayer.dLayerPos[LEFT_POS];
	infoNewLayer.nAxisPos[AXIS_TOP]		= infoNewLayer.dLayerPos[TOP_POS];
	infoNewLayer.nAxisPos[AXIS_RIGHT]	= infoNewLayer.dLayerPos[LEFT_POS] + infoNewLayer.dLayerPos[WIDTH_POS];

	// 3. Move layer/axes
	///Kenny 04/01/2009 QA80-13370 ADD_FUNC_LINKED_LAYER_GET_OUTMOST_LAYER_AXES
	int nOutmostAxesPos[AXIS_TOTAL];
	int nOutmostLayerIndices[AXIS_TOTAL];
	int nOutmostAxesType[AXIS_TOTAL];
	int nRet = linked_layer_get_outmost_layer_axes(glParent, infoNewLayer.bAxisShow, nOutmostAxesPos, nOutmostLayerIndices, nOutmostAxesType);
	ASSERT(0 == nRet);
	if (0 == nRet)
	{
		for (int nAxisType = 0; nAxisType < AXIS_TOTAL; ++nAxisType)
		{
			if ( !infoNewLayer.bAxisShow[nAxisType] || nOutmostLayerIndices[nAxisType] < 0 )
				continue;

			// Setting Offset
			const int nOffsetDirection = (AXIS_BOTTOM == nAxisType || AXIS_RIGHT == nAxisType) ? 1 : -1;
			infoNewLayer.nAxisPos[nAxisType] = nOutmostAxesPos[nAxisType] + nMinOffset * nOffsetDirection;

			// Set the tick value to the same as the outmost one
			int nOutmostAxisTicksMajorVal, nOutmostAxisTicksMinorVal;
			GraphLayer glOutmost = gp.Layers(nOutmostLayerIndices[nAxisType]);
			if (glOutmost)
			{
				const int nOutmostAxisType = nOutmostAxesType[nAxisType];
				nOutmostAxisTicksMajorVal = axis_get_tick_format(glOutmost, nOutmostAxisType, true);
				nOutmostAxisTicksMinorVal = axis_get_tick_format(glOutmost, nOutmostAxisType, false);
				axis_set_tick_format(glNew, nAxisType, nOutmostAxisTicksMajorVal, true);
				axis_set_tick_format(glNew, nAxisType, nOutmostAxisTicksMinorVal, false);
			}
		}
	}
	else
		return -7;

	// 4. Move each axis to the computed position
	_set_axis_position_in_logical_units(glNew, infoNewLayer.nAxisPos );
	///End ADD_FUNC_LINKED_LAYER_GET_OUTMOST_LAYER_AXES

	return 0;
}
///End ADD_OFFSET_TO_NEWLY_ADDED_LAYER

///Kenny 04/03/2009 ADD_FUNC_MOVE_LAYER_TO_RANDOM_DIR_POS
// nMinOffset is in M_PIXEL unit
static bool _move_layer_to_random_dir_pos(GraphLayer& gl, const uint nMinOffset = DEFAULT_ADD_LAYER_MIN_OFFSET)
{
	double dLayerPos[TOTAL_POS];
	int nCurrentUnits = gl.GetPosition(dLayerPos);
	if ( M_PIXEL != nCurrentUnits && !gl.UnitsConvert(M_PIXEL, dLayerPos, nCurrentUnits))
		return false;

	// Gives this layer an random offset(in both horizontal & vertical directions) to indicate/differ it from linked layer
	srand( GetTickCount() );
	int nHorizontalDirection = (rand() % 2) == 0 ? 1 : -1;
	int nVerticalDirection = (rand() % 2) == 0 ? 1 : -1;

	int nMoreOffset = (0 == nMinOffset ? DEFAULT_ADD_LAYER_MIN_OFFSET : nMinOffset);	// Add more offset to prevent overlapping with last layer
	nMoreOffset = rand() % nMoreOffset;

	dLayerPos[LEFT_POS] += (double)(nMinOffset + nMoreOffset) * nHorizontalDirection;
	dLayerPos[TOP_POS] += (double)(nMinOffset + nMoreOffset) * nVerticalDirection;

	gl.SetPosition(dLayerPos, M_PIXEL, OCD_UNDO);
	return true;
}
///End ADD_FUNC_MOVE_LAYER_TO_RANDOM_DIR_POS

///Jasmine 01/15/09 QA80-12968 ADD_OFFSET_BETWEEN_PREV_LAYER
//----- Iris 02/16/2007 v8.0564 SHOULD_GIVE_GOOD_SIZE_POS_AFTER_ADD
//int	page_add_layer(GraphPage& gp, bool bBottom, bool bLeft, bool bTop, bool bRight, int nLinkTo, int nXAxisLink, int nYAxisLink, int *pnNewLayerSizeUnit, int *pnNewLayerLeft, int *pnNewLayerTop, int *pnNewLayerWidth, int *pnNewLayerHeight, int nTemplateLayer)
/// Iris 01/06/2009 v8.0993e ADD_LAYER_BY_INSET8_OTP_TO_SET_SIZE_ON_AXIS_LABEL
//int	page_add_layer(GraphPage& gp, bool bBottom, bool bLeft, bool bTop, bool bRight, bool bAutoInitSizePosition, int nLinkTo, int nXAxisLink, int nYAxisLink, int *pnNewLayerSizeUnit, int *pnNewLayerLeft, int *pnNewLayerTop, int *pnNewLayerWidth, int *pnNewLayerHeight, int nTemplateLayer)
int	page_add_layer(	GraphPage& gp, bool bBottom, bool bLeft, bool bTop, bool bRight, 
					int nAutoInitSizePosition, /*bool bAutoInitSizePosition,*/ ///Kenny 03/31/2009 NO_OFFSET_TO_NEWLY_ADDED_LAYER_IF_NOT_OVERLAPPING
					bool bActivateNewLayer/* = true*/,	///Kenny 03/31/2009 ADD_PARAM_ACTIVE_NEW_LAYER_TO_FUNC_PAGE_ADD_LAYER
					int nLinkTo, int nXAxisLink, int nYAxisLink, 
					int *pnNewLayerSizeUnit, int *pnNewLayerLeft, int *pnNewLayerTop, int *pnNewLayerWidth, int *pnNewLayerHeight, 
					int nTemplateLayer, LPCSTR lpcszTemplate)
///end ADD_LAYER_BY_INSET8_OTP_TO_SET_SIZE_ON_AXIS_LABEL
//-----
{
	if(!gp)
		return -1;
	
	int nCurrent = page_active_layer_index(gp);	///Jasmine 03/24/09 KEEP_CURRENT_LAYER_SELECTED_AFTER_NEW_LAYER_ADD 
	
	//1. add layer
	GraphLayer	glTemplate;
	if( is_valid_layer_index(gp, nTemplateLayer) )
		glTemplate = gp.Layers(nTemplateLayer);
		
	int			nNewLayer;
	if(glTemplate)
		nNewLayer = gp.AddLayer(glTemplate, DCTRL_ADD_LAYER_UNDOABLE);	///Jasmine 03/15/07 ADD_UNDO_BUTTON
	else
	{
		/// Iris 01/06/2009 v8.0993e ADD_LAYER_BY_INSET8_OTP_TO_SET_SIZE_ON_AXIS_LABEL
		//nNewLayer = gp.AddLayer("", CREATE_UNDOABLE);	///Jasmine 03/15/07 ADD_UNDO_BUTTON
		nNewLayer = gp.AddLayer(NULL, CREATE_UNDOABLE, lpcszTemplate);
		///end ADD_LAYER_BY_INSET8_OTP_TO_SET_SIZE_ON_AXIS_LABEL
	}
	if(nNewLayer < 1)
		return -1;
	
	GraphLayer 	glNew = gp.Layers(nNewLayer);
	glNew.RemoveGraphObject("Legend");///Jasmine 03/25/09 page_add_layer_SHOULD_NOT_ADD_LEGEND
	
	//2. show axes
	///Kyle 01/16/2009 CENTRALIZE_CODE_TO_SET_ALL_AXES_ONCE_FOR_PAGE_ADD_LAYER
	//axis_set_show(glNew, AXIS_BOTTOM, bBottom);
	//axis_set_show(glNew, AXIS_LEFT, bLeft);
	//axis_set_show(glNew, AXIS_TOP, bTop);
	//axis_set_show(glNew, AXIS_RIGHT, bRight);
	///Kyle 01/24/2009 REMOVE_DUPLICATED_CODE_AND_CALL_FUNCTION_IN_PAGE_UTILS
	//vector<bool> vbShow(4);
	//vbShow[AXIS_BOTTOM] = bBottom;
	//vbShow[AXIS_LEFT] = bLeft;
	//vbShow[AXIS_TOP] = bTop;
	//vbShow[AXIS_RIGHT] = bRight;
	//_page_add_layer_set_axes(glNew, vbShow);
	vector<int> vnAxesShow(4), vnTicksShow(4);
	vnAxesShow[AXIS_BOTTOM] = bBottom;
	vnAxesShow[AXIS_LEFT] = bLeft;
	vnAxesShow[AXIS_TOP] = bTop;
	vnAxesShow[AXIS_RIGHT] = bRight;

	vnTicksShow[AXIS_BOTTOM] 	= bBottom ? TICK_OUT : -1;
	vnTicksShow[AXIS_LEFT] 		= bLeft   ? TICK_OUT : -1;
	vnTicksShow[AXIS_TOP] 		= bBottom ? TICK_IN  : -1;
	vnTicksShow[AXIS_RIGHT] 	= bBottom ? TICK_IN  : -1;
	gl_smart_show_object(glNew, vnAxesShow, vnAxesShow, vnAxesShow, vnTicksShow, vnTicksShow);
	///End REMOVE_DUPLICATED_CODE_AND_CALL_FUNCTION_IN_PAGE_UTILS
	///End CENTRALIZE_CODE_TO_SET_ALL_AXES_ONCE_FOR_PAGE_ADD_LAYER
	
	//3. set defaule size & position
	
	///Jasmine 01/16/09 QA80-12968 NO_OFFSET_BETWEEN_PARENT_LAYER
	bool bLinked = false;
	if( -1 != nLinkTo && is_valid_layer_index(gp, nLinkTo) )
		bLinked = true;
	///End NO_OFFSET_BETWEEN_PARENT_LAYER
	
	int	nUnit;
	///Jasmine 03/06/07 QA70-9433 CONVERT_LAYER_UNITS
	//----- Iris 02/16/2007 v8.0564 SHOULD_GIVE_GOOD_SIZE_POS_AFTER_ADD
	//computer a good size and position for new layer and apply
	if (ADD_LAYER_INIT_SIZE_POS_NONE != nAutoInitSizePosition)//if(bAutoInitSizePosition)	///Kenny 03/31/2009 NO_OFFSET_TO_NEWLY_ADDED_LAYER_IF_NOT_OVERLAPPING
	{
		///Kenny 03/24/2009 QA80-13370 ADD_OFFSET_TO_NEWLY_ADDED_LAYER
		////have same size and position as previous layer, with offset
		//
		/////Jasmine 01/16/09 QA80-12968 NO_OFFSET_BETWEEN_PARENT_LAYER
		////GraphLayer glParent = gp.Layers(nNewLayer - 1);
		//int nPrev = nNewLayer - 1;
		//if(bLinked)
			//nPrev = nLinkTo;
		//GraphLayer glParent = gp.Layers(nPrev);
		/////End NO_OFFSET_BETWEEN_PARENT_LAYER
		//
		//if(glParent)
		//{
			//double dPos[TOTAL_POS];
			//if( M_PERCENT != glParent.GetPosition(dPos) )
				//glParent.UnitsConvert(M_PERCENT, dPos);
			//
			/////Jasmine 01/16/09 QA80-12968 NO_OFFSET_BETWEEN_PARENT_LAYER
			////double dOffset = 2.0;
			//double dOffset = bLinked? 0.0 : 2.0;
			/////End NO_OFFSET_BETWEEN_PARENT_LAYER
			//
			//dPos[LEFT_POS] += dOffset;
			//dPos[TOP_POS] += dOffset;
			//
			//nUnit = glNew.GetPosition();
			//if( M_PERCENT != nUnit )
				//glNew.UnitsConvert(nUnit, dPos, M_PERCENT);			
			//glNew.SetPosition(dPos, nUnit, OCD_UNDO);
		//}	
		///Kenny 03/31/2009 NO_OFFSET_TO_NEWLY_ADDED_LAYER_IF_NOT_OVERLAPPING
		//int nRet = _move_overlap_layer_axes_to_available_pos(gp, nNewLayer, bBottom, bLeft, bTop, bRight, nLinkTo);
		if (bLinked)
		{
			bool bIsOverLap = true;
			if ( pnNewLayerSizeUnit && M_LINK == *pnNewLayerSizeUnit && pnNewLayerLeft && pnNewLayerTop && pnNewLayerWidth && pnNewLayerHeight )
			{
				if (0 != *pnNewLayerLeft || 0 != *pnNewLayerTop || 100 != *pnNewLayerWidth || 100 != *pnNewLayerHeight)
					bIsOverLap = false;
			}
			if (bIsOverLap)
			{
				const bool bNoOffset = ADD_LAYER_INIT_SIZE_POS_SAME_AS_PREVIOUS == nAutoInitSizePosition;
				int nRet = _move_overlapped_link_layer_axes_to_available_pos(gp, nNewLayer, nLinkTo, bNoOffset);
			}
		}
		else
		{
			_move_layer_to_random_dir_pos(glNew);
		}
		///End NO_OFFSET_TO_NEWLY_ADDED_LAYER_IF_NOT_OVERLAPPING
		///End ADD_OFFSET_TO_NEWLY_ADDED_LAYER	
	}
	//-----
	
	
	//3. set link
	//note: must set link before set unit since units may be M_LINK
	if( bLinked && !layer_set_link(glNew, nLinkTo, nXAxisLink, nYAxisLink) )
	{
		return -1;
	}
	
	
	//4. specify position and size of newly added layer
	bool bSetPos = false;
	
	if(NULL != pnNewLayerSizeUnit)
	{
		nUnit = *pnNewLayerSizeUnit;
		bSetPos = true;
	}
	if(M_LINK == nUnit && !bLinked) //check if error since UNITS_LAYER only works for linked layer
	{
		error_report("Error since M_LINK only works for linked layer");
		return -1;
	}

	double dPos[TOTAL_POS];
	glNew.GetPosition(dPos);
	glNew.UnitsConvert(nUnit, dPos);
	if(NULL != pnNewLayerLeft)
	{
		dPos[LEFT_POS] = (double)*pnNewLayerLeft;
		bSetPos = true;
	}
	if(NULL != pnNewLayerTop)
	{
		dPos[TOP_POS] = (double)*pnNewLayerTop;
		bSetPos = true;
	}
	if(NULL != pnNewLayerHeight)
	{
		dPos[HEIGHT_POS] = (double)*pnNewLayerHeight;
		bSetPos = true;
	}
	if(NULL != pnNewLayerWidth)
	{
		dPos[WIDTH_POS] = (double)*pnNewLayerWidth;
		bSetPos = true;
	}
	if(bSetPos)
		glNew.SetPosition(dPos, nUnit, OCD_UNDO);
	
	///Kenny 03/31/2009 ADD_PARAM_ACTIVE_NEW_LAYER_TO_FUNC_PAGE_ADD_LAYER
	//page_set_active_layer(gp, nCurrent, false);	///Jasmine 03/24/09 KEEP_CURRENT_LAYER_SELECTED_AFTER_NEW_LAYER_ADD
	page_set_active_layer(gp, bActivateNewLayer ? nNewLayer : nCurrent, false);
	///End ADD_PARAM_ACTIVE_NEW_LAYER_TO_FUNC_PAGE_ADD_LAYER
	
	return nNewLayer;	
}
///End ADD_OFFSET_BETWEEN_PREV_LAYER

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////Layer settings//////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
///Jasmine 03/06/07 QA70-9433 CONVERT_LAYER_UNITS
/*
int		layer_get_unit(GraphLayer& gl)
{	
	if(!gl)
		return false;
	
	Tree 		tr;
	tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);	
	return tr.Root.Dimension.Units.nVal;	
}
bool	layer_set_unit(GraphLayer& gl, int nUnit)
{
	if(!gl)
		return false;
	int nParentLayer;
	layer_get_link(gl, nParentLayer);
	if(nUnit == M_LINK && nParentLayer == -1)
		return false;		
	Tree 	tr;
	tr.Root.Dimension.Units.nVal = nUnit;
	gl.UpdateThemeIDs(tr.Root);
	return gl.ApplyFormat(tr, true, true);
}
*/
///End CONVERT_LAYER_UNITS
string	unit_index_to_str(int unit)
{
	string	str;
	switch(unit)
	{
	case M_PERCENT:
		str = "% of Page";
		break;
	case M_INCH:
		str = "inch";
		break;
	case M_CM:
		str = "cm";
		break;
	case M_MM:
		str = "mm";
		break;
	case M_PIXEL:
		str = "pixel";
		break;
	case M_PT:
		str = "point";
		break;
	case M_LINK:
		str = "% of Linked Layer";
		break;
	default:
		str = "";
		break;
	}
	return str;

}

///Jasmine 03/06/07 QA70-9433 CONVERT_LAYER_UNITS
bool	layer_get_size(GraphLayer& gl, double& dWidth, double& dHeight, int* pUnit)
{
	if(!gl)
		return false;
	/*
	Tree 		tr;
	tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);	
	dWidth = tr.Root.Dimension.Width.dVal;
	dHeight = tr.Root.Dimension.Height.dVal;
	*/
	double dPos[TOTAL_POS];
	int nUnit = gl.GetPosition(dPos);
	dWidth = dPos[WIDTH_POS];
	dHeight = dPos[HEIGHT_POS];
	if(NULL != pUnit)
	{
		*pUnit = nUnit;//layer_get_unit(gl);
	}
	
	return true;
}
/*
static bool _layer_set_size(GraphLayer& gl, double& dSize, bool bIsWidth = true, int* pUnit = NULL)
{
	if(!gl)
		return false;
	
	Tree 	tr;
	if(bIsWidth)
	{
		tr.Root.Dimension.Width.dVal = dSize;
	}
	else
	{
		tr.Root.Dimension.Height.dVal = dSize;
	}
	if(NULL != pUnit)
		tr.Root.Dimension.Units.nVal = *pUnit;
	gl.UpdateThemeIDs(tr.Root);
	gl.ApplyFormat(tr, true, true);
	return true;
}
*/
static bool _layer_set_pos(GraphLayer& gl, int nType, double& dValue, int* pUnit = NULL, bool bRight = false)
{//set one of width, height, left or top
	if(!gl || WIDTH_POS > nType || TOP_POS < nType)
		return false;
	double dPos[TOTAL_POS];
	int nUnit = gl.GetPosition(dPos);
	if(NULL != pUnit)
	{
		nUnit = *pUnit;
		gl.UnitsConvert(nUnit, dPos);
	}
	double dd = dValue;
	if(LEFT_POS == nType && bRight)//set right
		dd -= dPos[WIDTH_POS];
	if(TOP_POS == nType && bRight)//set bottom
		dd -= dPos[HEIGHT_POS];
	dPos[nType] = dd;
	return gl.SetPosition(dPos, nUnit, OCD_UNDO);
}
bool	layer_set_width(GraphLayer& gl, double& dWidth, int* pUnit)
{
	//return _layer_set_size(gl, dWidth, true, pUnit);
	return _layer_set_pos(gl, WIDTH_POS, dWidth, pUnit);
}

bool	layer_set_height(GraphLayer& gl, double& dHeight, int* pUnit)
{
	//return _layer_set_size(gl, dHeight, false, pUnit);
	return _layer_set_pos(gl, HEIGHT_POS, dHeight, pUnit);
}

bool	layer_set_size(GraphLayer& gl, double& dWidth, double& dHeight, int* pUnit)
{
	//layer_set_width(gl, dWidth, pUnit);
	//return layer_set_height(gl, dHeight);
	if(!gl)
		return false;
	double dPos[TOTAL_POS];
	int nUnit = gl.GetPosition(dPos);
	if(NULL != pUnit)
	{
		nUnit = *pUnit;
		gl.UnitsConvert(nUnit, dPos);
	}
	dPos[WIDTH_POS] = dWidth;
	dPos[HEIGHT_POS] = dHeight;
	return gl.SetPosition(dPos, nUnit, OCD_UNDO);	///Jasmine 03/15/07 ADD_UNDO_BUTTON
}


bool	layer_copy_size(GraphLayer& glSource, GraphLayer glDest)
{
	if( !glSource || !glDest)
		return false;
	
	//int			nUnit;
	//double		dWidth, dHeight;
	//if(layer_get_size(glSource, dWidth, dHeight, &nUnit))
		//return layer_set_size(glDest, dWidth, dHeight, &nUnit);
	//return false;
	double dPos[TOTAL_POS], dMyPos[TOTAL_POS];
	int nUnit = glSource.GetPosition(dPos);
	if(M_PERCENT == nUnit || M_LINK == nUnit)
	{//change to absolute value
		glSource.UnitsConvert(M_INCH, dPos);
		nUnit = M_INCH;
	}
	int nMyUnit = glDest.GetPosition(dMyPos);
	if(nMyUnit != nUnit)
		glDest.UnitsConvert(nMyUnit, dPos, nUnit);
	return glDest.SetPosition(dPos, -1, OCD_UNDO);
}

bool	layer_swap_size(GraphLayer& glSource, GraphLayer glDest)
{
	if( !glSource || !glDest)
		return false;
	/*should not change units
	int			nUnitSource;
	double		dWidthSource, dHeightSource;
	layer_get_size(glSource, dWidthSource, dHeightSource, &nUnitSource);
	
	int			nUnitDest;
	double		dWidthDest, dHeightDest;
	layer_get_size(glDest, dWidthDest, dHeightDest, &nUnitDest);

	layer_set_size(glSource, dWidthDest, dHeightDest, &nUnitDest);
	layer_set_size(glDest, dWidthSource, dHeightSource, &nUnitSource);
	*/
	//get original size
	double dPosSource[TOTAL_POS], dPosDest[TOTAL_POS], dPos[TOTAL_POS];
	int nUnitSource = glSource.GetPosition(dPosSource);
	int nUnitDest = glDest.GetPosition(dPosDest);
	//set glSource size
	int nUnit = nUnitDest;
	for(int ii = WIDTH_POS; ii < TOTAL_POS; ii++)
		dPos[ii] = dPosDest[ii];
	if(M_PERCENT == nUnit || M_LINK == nUnit)
	{//change to absolute value
		glDest.UnitsConvert(M_INCH, dPos);
		nUnit = M_INCH;
	}
	glSource.UnitsConvert(nUnitSource, dPos, nUnit);//change to nUnitSource
	dPos[LEFT_POS] = dPosSource[LEFT_POS];//keep position
	dPos[TOP_POS] = dPosSource[TOP_POS];
	glSource.SetPosition(dPos, -1, OCD_UNDO);
	//set glDest size
	nUnit = nUnitSource;
	for(ii = WIDTH_POS; ii < TOTAL_POS; ii++)
		dPos[ii] = dPosSource[ii];
	if(M_PERCENT == nUnit || M_LINK == nUnit)
	{//change to absolute value
		glSource.UnitsConvert(M_INCH, dPos);
		nUnit = M_INCH;
	}
	glDest.UnitsConvert(nUnitDest, dPos, nUnit);//change to nUnitSource
	dPos[LEFT_POS] = dPosDest[LEFT_POS];//keep position
	dPos[TOP_POS] = dPosDest[TOP_POS];
	glDest.SetPosition(dPos, -1, OCD_UNDO);
	return true;
}



bool	layer_get_position(GraphLayer& gl, double* pLeft, double* pTop, double* pRight, double* pBottom, int* pUnit)
{
	if(!gl)
		return false;	
	//Tree 		tr;
	//tr = gl.GetFormat(FPB_DIMENSION, FOB_DIMENSION, true, true);	
	//double		dLeft = tr.Root.Dimension.Left.dVal;
	//double		dTOp = tr.Root.Dimension.Top.dVal;
	double dPos[TOTAL_POS];
	int nUnit = gl.GetPosition(dPos);
	if(NULL != pLeft)
		*pLeft = dPos[LEFT_POS];//dLeft;
	if(NULL != pTop)
		*pTop = dPos[TOP_POS];//dTOp;
	
	if( NULL != pRight || NULL != pBottom)
	{
		//double		dWidth = tr.Root.Dimension.Width.dVal;
		//double		dHeight = tr.Root.Dimension.Height.dVal;
		
		if(NULL != pRight)
			*pRight = dPos[LEFT_POS] + dPos[WIDTH_POS];//dLeft + dWidth;
		if(NULL != pBottom)
			*pBottom = dPos[TOP_POS] + dPos[HEIGHT_POS];//dTOp + dHeight;
	}		

	if(NULL != pUnit)
		*pUnit = nUnit;//layer_get_unit(gl);
	
	
	return true;
}

bool 	layer_set_position(GraphLayer& gl, double dLeft, double dTop, int* pUnit)
{
	if(!gl)
		return false;	
	/*
	Tree 	tr;
	tr.Root.Dimension.Left.dVal = dLeft;	
	tr.Root.Dimension.Top.dVal = dTop;	
	if(NULL != pUnit)
		tr.Root.Dimension.Units.nVal = *pUnit;
	
	gl.UpdateThemeIDs(tr.Root);
	return gl.ApplyFormat(tr, true, true);	
	*/
	double dPos[TOTAL_POS];
	int nUnit = gl.GetPosition(dPos);
	if(NULL != pUnit)
	{
		nUnit = *pUnit;
		gl.UnitsConvert(nUnit, dPos);
	}
	dPos[LEFT_POS] = dLeft;
	dPos[TOP_POS] = dTop;
	return gl.SetPosition(dPos, nUnit, OCD_UNDO);	///Jasmine 03/15/07 ADD_UNDO_BUTTON
}

bool	layer_swap_position(GraphLayer& glSource, GraphLayer glDest)
{
	if( !glSource || !glDest )
		return false;	
	/*
	int			nUnitSource;
	double		dLeftSource, dTopSource;
	layer_get_position(glSource, &dLeftSource, &dTopSource, NULL, NULL, &nUnitSource);
		
	int			nUnitDest;
	double		dLeftDest, dTopDest;
	layer_get_position(glDest, &dLeftDest, &dTopDest, NULL, NULL, &nUnitDest);
	
	layer_set_position(glSource, dLeftDest, dTopDest, &nUnitDest);
	layer_set_position(glDest, dLeftSource, dTopSource, &nUnitSource);
	*/
	double dPosSource[TOTAL_POS], dPosDest[TOTAL_POS], dPos[TOTAL_POS];
	int nUnitSource = glSource.GetPosition(dPosSource);
	int nUnitDest = glDest.GetPosition(dPosDest);
	//set glSource position
	int nUnit = nUnitDest;
	for(int ii = WIDTH_POS; ii < TOTAL_POS; ii++)
		dPos[ii] = dPosDest[ii];
	if(M_PERCENT == nUnit || M_LINK == nUnit)
	{//change to absolute value
		glDest.UnitsConvert(M_INCH, dPos);
		nUnit = M_INCH;
	}
	glSource.UnitsConvert(nUnitSource, dPos, nUnit);//change to nUnitSource
	dPos[WIDTH_POS] = dPosSource[WIDTH_POS];//keep size
	dPos[HEIGHT_POS] = dPosSource[HEIGHT_POS];
	glSource.SetPosition(dPos, -1, OCD_UNDO);	///Jasmine 03/15/07 ADD_UNDO_BUTTON
	//set glDest position
	nUnit = nUnitSource;
	for(ii = WIDTH_POS; ii < TOTAL_POS; ii++)
		dPos[ii] = dPosSource[ii];
	if(M_PERCENT == nUnit || M_LINK == nUnit)
	{//change to absolute value
		glSource.UnitsConvert(M_INCH, dPos);
		nUnit = M_INCH;
	}
	glDest.UnitsConvert(nUnitDest, dPos, nUnit);//change to nUnitSource
	dPos[WIDTH_POS] = dPosDest[WIDTH_POS];//keep size
	dPos[HEIGHT_POS] = dPosDest[HEIGHT_POS];
	glDest.SetPosition(dPos, -1, OCD_UNDO);	///Jasmine 03/15/07 ADD_UNDO_BUTTON
	return true;
}

bool	layer_set_left(GraphLayer& gl, double dLeft, int* pUnit)
{
	/*
	//--- Iris 02/15/2007 v8.0562 dLeft will be negative if layer out of page.
	//if(!gl || dLeft<0 )
	if(!gl)
		//---
		return false;
	
	Tree 	tr;
	tr.Root.Dimension.Left.dVal = dLeft;	
	if(NULL != pUnit)
		tr.Root.Dimension.Units.nVal = *pUnit;
	
	gl.UpdateThemeIDs(tr.Root);	
	return gl.ApplyFormat(tr, true, true);	
	*/
	return _layer_set_pos(gl, LEFT_POS, dLeft, pUnit);
}


bool	layer_set_top(GraphLayer& gl, double dTop, int* pUnit)
{
	/*
	//--- Iris 02/15/2007 v8.0562 dTop will be negative if layer out of page.
	//if(!gl || dTop<0 )
	if( !gl )
	//---
		return false;
	
	Tree 	tr;
	tr.Root.Dimension.Top.dVal = dTop;	
	if(NULL != pUnit)
		tr.Root.Dimension.Units.nVal = *pUnit;
	
	gl.UpdateThemeIDs(tr.Root);	
	return gl.ApplyFormat(tr, true, true);	
	*/
	return _layer_set_pos(gl, TOP_POS, dTop, pUnit);
}

bool	layer_set_right(GraphLayer& gl, double dRight, int* pUnit)
{
	if(!gl)
		return false;
	/*
	double	dWidth, dHeight;
	layer_get_size(gl, dWidth, dHeight);	
	
	return layer_set_left(gl, dRight - dWidth, pUnit);	
	*/
	return _layer_set_pos(gl, LEFT_POS, dRight, pUnit, true);
}

bool	layer_set_bottom(GraphLayer& gl, double dBottom, int* pUnit)
{
	if(!gl)
		return false;
	/*
	double	dWidth, dHeight;
	layer_get_size(gl, dWidth, dHeight);
	
	return layer_set_top(gl, dBottom - dHeight, pUnit);	
	*/
	return _layer_set_pos(gl, TOP_POS, dBottom, pUnit, true);
}

bool	layer_aligns(GraphLayer& glSource, GraphLayer& glDest, int nDirection)
{
	if(!glSource || !glDest)
		return false;
	
	double 	left, top, right, bottom, dSourcePos[TOTAL_POS], dDestPos[TOTAL_POS];
	//layer_get_position(glSource, &left, &top, &right, &bottom);
	//double dSourcePos[TOTAL_POS], dDestPos[TOTAL_POS];
	int nSourceUnit = glSource.GetPosition(dSourcePos);
	if(M_LINK == nSourceUnit)
	{
		glSource.UnitsConvert(M_INCH, dSourcePos, nSourceUnit);
		nSourceUnit = M_INCH;
	}
	int nDestUnit = glDest.GetPosition(dDestPos);
	if(nDestUnit != nSourceUnit)
		glDest.UnitsConvert(nDestUnit, dSourcePos, nSourceUnit);
	/*left = dSourcePos[LEFT_POS];
	top = dSourcePos[TOP_POS];
	right = dSourcePos[LEFT_POS] + dSourcePos[WIDTH_POS];
	bottom = dSourcePos[TOP_POS] + dSourcePos[HEIGHT_POS];*/
	
	switch(nDirection)
	{
	case POS_LEFT:
		//return layer_set_left(glDest, left);
		dDestPos[LEFT_POS] = dSourcePos[LEFT_POS];
		break;
	case POS_TOP:
		//return layer_set_top(glDest, top);
		dDestPos[TOP_POS] = dSourcePos[TOP_POS];
		break;
	case POS_RIGHT:
		//return layer_set_right(glDest, right);
		dDestPos[LEFT_POS] = dSourcePos[LEFT_POS] + dSourcePos[WIDTH_POS] - dDestPos[WIDTH_POS];
		break;
	case POS_BOTTOM:
		//return layer_set_bottom(glDest, bottom);
		dDestPos[TOP_POS] = dSourcePos[TOP_POS] + dSourcePos[HEIGHT_POS] - dDestPos[HEIGHT_POS];
		break;
	}
	return glDest.SetPosition(dDestPos, -1, OCD_UNDO);	///Jasmine 03/15/07 ADD_UNDO_BUTTON
}
///End CONVERT_LAYER_UNITS
bool	layer_zoom(GraphLayer& gl)
{
	if(!gl)
		return false;
	
	GraphPage	gp;
	gl.GetParent(gp);
	
	page_set_active_layer(gp, gl.GetIndex(), false);
	
	return gp.LT_execute("Page.zoomLayer=0;");
	
}

bool	layer_reorder(GraphLayer& gl, int nNewIndex)
{
	if(!gl)
		return false;
	
	int 	nIndex = gl.GetIndex();
	if(nIndex == nNewIndex)
		return true;
	
	GraphPage 	gp;
	gl.GetParent(gp);
	
	string		str;
	str.Format("Page.Reorder(%d, %d);", nNewIndex, nIndex);
	return gp.LT_execute(str);
}

bool	layer_toggle(GraphLayer& gl)
{
	if( !gl )
		return false;	
	
	for(int nAxis = AXIS_X; nAxis < AXIS_LAST_ITEMS; nAxis++)
	{
		 //same as in LayerTools.c, only take care of the first axis
		int		nOneAxis;
		if(AXIS_X == nAxis)
			nOneAxis = AXIS_BOTTOM;
		else if(AXIS_Y == nAxis)
			nOneAxis = AXIS_LEFT;
		else 
			break;
		
		int 	nShow;
		nShow = axis_tick_get_show(gl, nOneAxis);
		if( nShow >= 0)// if -1 = nShow, then this axis is not existing, so cannot set show attribute for it.
			axis_tick_set_show(gl, 	nOneAxis, !nShow);			
		
		nShow = axis_label_get_show(gl, nOneAxis);
		if( nShow >= 0)
		axis_label_set_show(gl, nOneAxis, !nShow);
		
	}	
		
	return true;
}
///Jasmine 03/06/07 QA70-9433 CONVERT_LAYER_UNITS
bool	layer_set_ratio(GraphLayer& gl, double dRatio)
{
	if( !gl || dRatio<=0 || dRatio>100)
		return false;
	//int nUnit = layer_get_unit(gl);
	//if(nUnit == 0 || nUnit == 6)
		//layer_set_unit(gl, M_INCH);
	//double	dWidth, dHeight;
	//layer_get_size(gl, dWidth, dHeight);
	double dPos[TOTAL_POS], dWidth, dHeight;
	int nUnit = gl.GetPosition(dPos);
	bool bConvert;
	if(M_PERCENT == nUnit || M_LINK == nUnit)
		bConvert = gl.UnitsConvert(M_INCH, dPos, nUnit);
	dWidth = dPos[WIDTH_POS];
	dHeight = dPos[HEIGHT_POS];	
	//dWidth = dWidth * dRatio / 100;
	//dHeight = dHeight * dRatio / 100;
	if((dWidth/dHeight) > dRatio)
		dWidth = dHeight * dRatio;
	else if((dWidth/dHeight) < dRatio)
		dHeight = dWidth / dRatio;
	//layer_set_size(gl, dWidth, dHeight);
	//if(nUnit == 0 || nUnit == 6)
		//layer_set_unit(gl, nUnit);
	dPos[WIDTH_POS] = dWidth;
	dPos[HEIGHT_POS] = dHeight;	
	if(bConvert)
		bConvert = gl.UnitsConvert(nUnit, dPos, M_INCH);
	gl.SetPosition(dPos, -1, OCD_UNDO);
	return true;
}
double	layer_get_ratio(GraphLayer& gl)
{
	if(!gl)
		return -1;
	/*int nUnit = layer_get_unit(gl);
	if(nUnit == 0 || nUnit == 6)
		layer_set_unit(gl, M_INCH);
	double	dWidth, dHeight;
	layer_get_size(gl, dWidth, dHeight);
	double dRatio = dWidth/dHeight;
	if(nUnit == 0 || nUnit == 6)
		layer_set_unit(gl, nUnit);
	*/
	double dPos[TOTAL_POS];
	int nUnit = gl.GetPosition(dPos);
	if(M_PERCENT == nUnit || M_LINK == nUnit)
		gl.UnitsConvert(M_INCH, dPos, nUnit);
	double dRatio = dPos[WIDTH_POS]/dPos[HEIGHT_POS];
	return dRatio;
}
///End CONVERT_LAYER_UNITS

bool	layer_set_scale_elements(GraphLayer& gl, int nScaleMode, double dFactor, bool bUndo)// 0, 1, false///Jasmine 08/13/07 MAKE_APPLY_UNDOABLE
{
	Tree tr;
	tr.Root.Display.Scale.nVal = nScaleMode;
	if(1 == nScaleMode)
		tr.Root.Display.FixedFactor.dVal = dFactor;
	int nRet = gl.UpdateThemeIDs(tr.Root);
	nRet = gl.ApplyFormat(tr, true, true, bUndo);	///Jasmine 08/13/07 MAKE_APPLY_UNDOABLE
	gl.LT_execute("layer.fixed = layer.fixed");	//force the layer frame to update
	return nRet;
}
bool	layer_get_scale_elements(GraphLayer& gl, int& nScaleMode, double* pdFactor)// = NULL
{
	if(!gl)
		return false;
	Tree	tr;
	tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);
	TreeNode trScaleMode = tree_get_node_by_tagname(tr, "Scale", true);
	nScaleMode = trScaleMode.IsValid()? trScaleMode.nVal : 0;
	if(1 == nScaleMode && pdFactor)
	{
		TreeNode trFactor = tree_get_node_by_tagname(tr, "FixedFactor", true);
		*pdFactor = trFactor.IsValid()? trFactor.dVal : 1;
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
////////////////////////////Axis settings/////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

bool	has_z_axis(GraphLayer& gl)
{
	if(!gl)
		return false;
	
	Tree 		tr;
	tr = gl.GetFormat(FPB_SCALE, FOB_SCALE, true, true);	

	TreeNode	trAxis = tr.Root.Axes.GetNode("Z");
	if(trAxis)
		return true;
	
	return false;
		
}

//--- Iris 02/15/2007 v8.0563 SUPPORT_GET_Z_SCALE
//bool	axis_get_sacle(GraphLayer& gl, int nAxis, double& dFrom, double& dTo)
bool	axis_get_sacle(GraphLayer& gl, string strAxis, double& dFrom, double& dTo)
//---
{
	if(!gl)
		return false;	
	
	Tree 		tr;
	tr = gl.GetFormat(FPB_SCALE, FOB_SCALE, true, true);	
	
	//--- Iris 02/15/2007 v8.0563 SUPPORT_GET_Z_SCALE
	//TreeNode	trAxis = _get_axis_node(tr, nAxis);	
	TreeNode	trAxis = tr.Root.Axes.GetNode(strAxis);
	//---
	if( !trAxis )
		return false;
	
	dFrom = trAxis.Scale.From.dVal;
	dTo = trAxis.Scale.To.dVal;
	
	return true;
}


//--- Iris 02/15/2007 v8.0563 SUPPORT_GET_Z_SCALE
//bool	axis_set_sacle(GraphLayer& gl, int nAxis, double dFrom, double dTo)
/// Kyle 08/15/08 QA80-12022 CHANGE_PROTOTYPE_OF_AXIS_SET_SACLE
//bool	axis_set_sacle(GraphLayer& gl, string strAxis, double dFrom, double dTo)
bool	axis_set_sacle(GraphLayer& gl, string strAxis, double dFrom, double dTo, double dInc)
/// End CHANGE_PROTOTYPE_OF_AXIS_SET_SACLE
//---
{
	if(!gl)
		return false;	
	
	Tree 		tr;	
	//--- Iris 02/15/2007 v8.0563 SUPPORT_GET_Z_SCALE
	//TreeNode	trAxis = _get_axis_node(tr, nAxis);
	tr.Root.Axis.nVal = 1;//just in order to add Root.Axis node
	TreeNode	trAxis = tr.Root.Axes.AddNode(strAxis);
	//---
	if( !trAxis )
		return false;

	/// Kyle 08/15/08 QA80-12022 CHANGE_PROTOTYPE_OF_AXIS_SET_SACLE
	//trAxis.Scale.From.dVal = dFrom;
	//trAxis.Scale.To.dVal = dTo;
	if(!is_missing_value(dFrom))
		trAxis.Scale.From.dVal = dFrom;
	if(!is_missing_value(dTo))
		trAxis.Scale.To.dVal = dTo;
	if(dInc>0 && !is_missing_value(dInc))
		trAxis.Scale.Value.dVal = dInc;
	/// End CHANGE_PROTOTYPE_OF_AXIS_SET_SACLE
	
	gl.UpdateThemeIDs(tr.Root);
	//------ Folger 08/07/07 MAKE_AXIS_SET_SCALE_UNDOABLE
	//return gl.ApplyFormat(tr, true, true);	
	return gl.ApplyFormat(tr, true, true, true);	
	//------ End MAKE_AXIS_SET_SCALE_UNDOABLE
}
///Jasmine 01/07/09 v8.0994d QA80-10257 REWRTIE_WITH_APPLY_FORMAT_TO_UNDO
//bool	axis_set_scale_type(GraphLayer& gl, string strAxis, int nType)//, bool bXScale)// = true
bool	axis_set_scale_type(GraphLayer& gl, string strAxis, int nType, bool bUndo)//false
{
	/*
	///Jasmine 06/14/07 don't know why if use ApplyFormat, it doesn't set the linked layer's scale
	if(!gl)
		return false;	
	bool bXScale = strAxis.CompareNoCase("Y");
	Scale s;
	if(bXScale)
		s = gl.X;
	else
		s = gl.Y;
	if(!s)
		return false;
	s.Type = nType;
	return true;
	*/
	if(!gl)
		return false;		
	Tree 		tr;
	tr = gl.GetFormat(FPB_SCALE, FOB_SCALE, true, true);
	TreeNode	trAxis = tr.Root.Axes.GetNode(strAxis);
	if( !trAxis )
		return false;
	trAxis.Scale.Type.nVal = nType;
	gl.UpdateThemeIDs(tr.Root);
	return gl.ApplyFormat(tr, true, true, bUndo);	
	
}
///End REWRTIE_WITH_APPLY_FORMAT_TO_UNDO

///Jasmine 03/22/07 ADD_GET_AXIS_SHOW
bool	axis_get_scale_type(GraphLayer& gl, int* pnXScale, int* pnYScale)
{
	if(!gl)
		return false;		
	/*Tree 		tr;
	tr = gl.GetFormat(FPB_SCALE, FOB_SCALE, true, true);
	if(pnXScale)
	{
		TreeNode	trAxis = tr.Root.Axes.GetNode("X");
		if(trAxis)
			*pnXScale = trAxis.Scale.Type? trAxis.Scale.Type.nVal : 0;
	}	
	if(pnYScale)
	{
		TreeNode	trAxis = tr.Root.Axes.GetNode("Y");
		if(trAxis)
			*pnYScale = trAxis.Scale.Type? trAxis.Scale.Type.nVal : 0;
	}*/
	if(pnXScale)
	{
		if(gl.X)
			*pnXScale = gl.X.Type;
	}
	
	if(pnYScale)
	{
		if(gl.Y)
			*pnYScale = gl.Y.Type;
	}
	
	return true;
}
///End ADD_GET_AXIS_SHOW
//--- Iris 02/15/2007 v8.0563 SUPPORT_GET_Z_SCALE
//bool	layer_copy_axis_scales(GraphLayer& glSource, GraphLayer& glDest, int nAxis)
bool	layer_copy_axis_scales(GraphLayer& glSource, GraphLayer& glDest, LPCSTR lpcszAxis)
//---
{
	if( !glSource || !glDest )
		return false;

	//--- Iris 02/15/2007 v8.0563 SUPPORT_GET_Z_SCALE
	/*
	int 	loop = nAxis < 0? AXIS_LAST_ITEMS : 1;
	
	for(int ii=0; ii<loop; ii++)
	{
		if(nAxis < 0)
			nAxis = ii;
		
		double dFrom, dTo;
		axis_get_sacle(glSource, nAxis, dFrom, dTo);
		axis_set_sacle(glDest, nAxis, dFrom, dTo);
	}
	*/
	vector<string> vsAllAxises = {"X", "Y", "Z"};
	if(NULL != lpcszAxis)
	{
		vsAllAxises.SetSize(0);
		vsAllAxises.Add(lpcszAxis);
	}
	
	for(int ii=0; ii<vsAllAxises.GetSize(); ii++)
	{
		double dFrom, dTo;
		axis_get_sacle(glSource, vsAllAxises[ii], dFrom, dTo);
		axis_set_sacle(glDest, vsAllAxises[ii], dFrom, dTo);

	}
	//---
	
	return true;		
}
//------- CPY 5/14/07 BETTER_X_AXIS_HIDE_SHOW_BASED_ON_LAYER_GAP_AND_POSITION
/*
bool 	axis_set_show(GraphLayer& gl, int nAxis, bool bShow, bool bCreateIfNotExist)
{
	if( !gl )
		return false;
	
	axis_tick_set_show(gl, 	nAxis, bShow, bCreateIfNotExist);	
	axis_title_set_show(gl, nAxis, bShow, bCreateIfNotExist);	
	axis_label_set_show(gl, nAxis, bShow, bCreateIfNotExist);
	return false;
}
*/
// I see bCreateIfNotExist is never used for axis_set_show and it is only about the theme tree create, not the actual title/tick create
// so I think it is safe to remove
// nTickLabels < 0 if share bShow, otherwise separate control
bool	axis_set_show(GraphLayer& gl, int nAxis, bool bShow, int nTickLabels, int nTitle)
{
	if( !gl )
		return false;
	bool bShowTickLabels = nTickLabels<0? bShow:nTickLabels;
	bool bShowTitle = nTitle<0? bShow:nTitle;
	
	/// Iris 01/07/2009 v8.0994b QA80-12887-P3 FIX_page_add_layer_WITH_TEMPLATE_NOT_KEEP_AXIS_COLOR
	// to fix: add layer by page_add_layer with one template axis is colorful, after added, axis color of added layer is not colorful.
	/*
	axis_tick_set_show(gl, 	nAxis, bShow);	
	axis_title_set_show(gl, nAxis, bShowTickLabels);	
	axis_label_set_show(gl, nAxis, bShowTitle);
	*/
	///Kyle 01/15/2009 REWRITE_AXIS_SET_SHOW_TO_JUST_CALL_GETFORMAT_AND_APPLYFORMAT_ONCE
	//int nShow;	
	//nShow = axis_tick_get_show(gl, nAxis); // <0 means error return
	//if( nShow >= 0 && nShow != bShow)	
		//axis_tick_set_show(gl, 	nAxis, bShow);	
		//
	//nShow = axis_title_get_show(gl, nAxis); // <0 means error return
	/////Kyle 01/08/2009 v8.0994d FIX_MISS_MATCH_TITLE_AND_TICKLABEL
	////if( nShow >= 0 && nShow != bShow)	
		////axis_title_set_show(gl, nAxis, bShowTickLabels);
	//if( nShow >= 0 && nShow != bShowTitle)
		//axis_title_set_show(gl, nAxis, bShowTitle);
	/////End FIX_MISS_MATCH_TITLE_AND_TICKLABEL
	//
	//nShow = axis_label_get_show(gl, nAxis); // <0 means error return
	/////Kyle 01/08/2009 v8.0994d FIX_MISS_MATCH_TITLE_AND_TICKLABEL
	////if( nShow >= 0 && nShow != bShow)	
	////	axis_label_set_show(gl, nAxis, bShowTitle);
	//if( nShow >= 0 && nShow != bShowTickLabels)
		//axis_label_set_show(gl, nAxis, bShowTickLabels);
	/////End FIX_MISS_MATCH_TITLE_AND_TICKLABEL
	/////end FIX_page_add_layer_WITH_TEMPLATE_NOT_KEEP_AXIS_COLOR
	//
	//return true;
	Tree tr;
	tr = gl.GetFormat(FPB_SHOW, FOB_AXIS | FOB_AXIS_LABELS | FOB_LABELS, true, true);
	TreeNode trAxes = tr.Root.Axes;
	if(!trAxes)
	{
		ASSERT(false);
		return false;
	}
	
	TreeNode trTicks, trLabels, trTiles;
	switch(nAxis)
	{
	case AXIS_BOTTOM:
		trTicks = tree_get_node_by_tagname(trAxes, "BottomTicks", true);
		if(!trTicks)
			trTicks = trAxes.X.Ticks.AddNode("BottomTicks");
		trLabels = tree_get_node_by_tagname(trAxes, "BottomLabels", true);
		if(!trLabels)
			trLabels = trAxes.X.Labels.AddNode("BottomLabels");
		trTiles = tree_get_node_by_tagname(trAxes, "BottomTitle", true);
		if(!trTiles)
			trTiles = trAxes.X.Titles.AddNode("BottomTitle");
		break;
	
	case AXIS_LEFT:
		trTicks = tree_get_node_by_tagname(trAxes, "LeftTicks", true);
		if(!trTicks)
			trTicks = trAxes.Y.Ticks.AddNode("LeftTicks");
		trLabels = tree_get_node_by_tagname(trAxes, "LeftLabels", true);
		if(!trLabels)
			trLabels = trAxes.Y.Labels.AddNode("LeftLabels");
		trTiles = tree_get_node_by_tagname(trAxes, "LeftTitle", true);
		if(!trTiles)
			trTiles = trAxes.Y.Titles.AddNode("LeftTitle");
		break;
		
	case AXIS_TOP:
		trTicks = tree_get_node_by_tagname(trAxes, "TopTicks", true);
		if(!trTicks)
			trTicks = trAxes.X.Ticks.AddNode("TopTicks");
		trLabels = tree_get_node_by_tagname(trAxes, "TopLabels", true);
		if(!trLabels)
			trLabels = trAxes.X.Labels.AddNode("TopLabels");
		trTiles = tree_get_node_by_tagname(trAxes, "TopTitle", true);
		if(!trTiles)
			trTiles = trAxes.X.Titles.AddNode("TopTitle");
		break;
		
	case AXIS_RIGHT:
		trTicks = tree_get_node_by_tagname(trAxes, "RightTicks", true);
		if(!trTicks)
			trTicks = trAxes.Y.Ticks.AddNode("RightTicks");
		trLabels = tree_get_node_by_tagname(trAxes, "RightLabels", true);
		if(!trLabels)
			trLabels = trAxes.Y.Labels.AddNode("RightLabels");
		trTiles = tree_get_node_by_tagname(trAxes, "RightTitle", true);
		if(!trTiles)
			trTiles = trAxes.Y.Titles.AddNode("RightTitle");
		break;

	default:
		ASSERT(false);
		return false;
	}
	ASSERT(trTicks && trLabels && trTiles);

	TreeNode trShow;

	trShow = trTicks.GetNode("Show");
	if(!trShow)
		trShow = trTicks.AddNode("Show");
	trShow.nVal = bShow;
	
	trShow = trLabels.GetNode("Show");
	if(!trShow)
		trShow = trLabels.AddNode("Show");
	trShow.nVal = bShowTickLabels;
	
	trShow = trTiles.GetNode("Show");
	if(!trShow)
		trShow = trTiles.AddNode("Show");
	trShow.nVal = bShowTitle;
	
	gl.UpdateThemeIDs(tr.Root);
	return gl.ApplyFormat(tr, true, true, true);

	///End REWRITE_AXIS_SET_SHOW_TO_JUST_CALL_GETFORMAT_AND_APPLYFORMAT_ONCE
}
//------

///Jasmine 03/22/07 ADD_GET_AXIS_SHOW
bool	axis_get_show(GraphLayer& gl, bool* pbBottom, bool* pbLeft, bool* pbTop, bool* pbRight)
{
	if( !gl )
		return false;
	///Kenny 03/30/2009 IMPROVE_FUNC_AXIS_GET_SHOW
	/*
	Tree 		tr;
	//tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);
	tr = gl.GetFormat(FPB_ALL, FOB_AXIS, true, true);
	if(pbBottom)
	{
		TreeNode	trTick = _get_tick_node(tr, AXIS_BOTTOM);
		if( trTick )
		{
			TreeNode trShow = trTick.GetNode("Show");
			if(trShow)
				*pbBottom = trShow.nVal;
		}	
	}
	if(pbLeft)
	{
		TreeNode	trTick = _get_tick_node(tr, AXIS_LEFT);
		if( trTick )
		{
			TreeNode trShow = trTick.GetNode("Show");
			if(trShow)
				*pbLeft = trShow.nVal;
		}	
	}
	if(pbTop)
	{
		TreeNode	trTick = _get_tick_node(tr, AXIS_TOP);
		if( trTick )
		{
			TreeNode trShow = trTick.GetNode("Show");
			if(trShow)
				*pbTop = trShow.nVal;
		}	
	}
	if(pbRight)
	{
		TreeNode	trTick = _get_tick_node(tr, AXIS_RIGHT);
		if( trTick )
		{
			TreeNode trShow = trTick.GetNode("Show");
			if(trShow)
				*pbRight = trShow.nVal;
		}	
	}
	*/
	/// Kenny 07/24/2009 QA80-13992 NEW_XF_FOR_GENERAL_MULTI_AXES_PLOTTING
	/*
	if(pbBottom)
		*pbBottom	= _get_axis_object(gl, AXIS_BOTTOM).BottomTicks.Show.nVal;
	if(pbLeft)
		*pbLeft		= _get_axis_object(gl, AXIS_LEFT).LeftTicks.Show.nVal;
	if(pbTop)
		*pbTop		= _get_axis_object(gl, AXIS_TOP).TopTicks.Show.nVal;
	if(pbRight)
		*pbRight	= _get_axis_object(gl, AXIS_RIGHT).RightTicks.Show.nVal;
	*/
	if(pbBottom)
		*pbBottom	= get_axis_object(gl, AXIS_BOTTOM).BottomTicks.Show.nVal;
	if(pbLeft)
		*pbLeft		= get_axis_object(gl, AXIS_LEFT).LeftTicks.Show.nVal;
	if(pbTop)
		*pbTop		= get_axis_object(gl, AXIS_TOP).TopTicks.Show.nVal;
	if(pbRight)
		*pbRight	= get_axis_object(gl, AXIS_RIGHT).RightTicks.Show.nVal;
	/// End QA80-13992 NEW_XF_FOR_GENERAL_MULTI_AXES_PLOTTING
	///End IMPROVE_FUNC_AXIS_GET_SHOW
	return true;
}
///End ADD_GET_AXIS_SHOW
static TreeNode _axis_check_get_major_or_minor_tick_node(TreeNode& tr, int nAxis, bool bMajor)
{
	TreeNode 	trTick;
	TreeNode	trTicks = _get_tick_node(tr, nAxis, true);
	if( trTicks )
	{
		string 	str = bMajor? "Major" : "Minor";
		trTick = tree_check_get_node(trTicks, str);
	}
	return trTick;	
}

bool	axis_set_tick_format(GraphLayer& gl, int nAxis, int nFormat, bool bMajor)
{
	if( !gl )
		return false;
	
	Tree 		tr;	
	TreeNode	trTick = _axis_check_get_major_or_minor_tick_node(tr, nAxis, bMajor);
	if( trTick )
	{
		trTick.nVal = nFormat;
		
		gl.UpdateThemeIDs(tr.Root);
		return gl.ApplyFormat(tr, true, true);
	}
	
	return false;
}

int 	axis_get_tick_format(GraphLayer& gl, int nAxis, bool bMajor)
{
	if( !gl )
		return -1;//default
	
	Tree 		tr;
	tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);
	TreeNode	trTick = _axis_check_get_major_or_minor_tick_node(tr, nAxis, bMajor);
	if( trTick )
		return 	trTick.nVal;
	
	return -1;
}

static TreeNode _axis_get_labels_node(TreeNode& tr, int nAxis, bool bCreateIfNotExist = false)
{
	TreeNode	trLabel;
	TreeNode 	trAxis = _get_axis_node(tr, nAxis);
	if( !trAxis )
		return trLabel;
	
	string		str;
	switch(nAxis)
	{
	case AXIS_BOTTOM:
		str = "BottomLabels";
		break;
	case AXIS_TOP:
		str = "TopLabels";
		break;
	case AXIS_LEFT:
		str = "LeftLabels";
		break;
	case AXIS_RIGHT:
		str = "RightLabels";
		break;
	default:
		return trLabel;
	}
	
	trLabel = tree_check_get_node(trAxis, "Labels");
	if(bCreateIfNotExist)
		trLabel = tree_check_get_node(trLabel, str);
	else
		trLabel = trLabel.GetNode(str);
	
	return trLabel;
}

bool	axis_label_set_show(GraphLayer& gl, int nAxis, bool bShow, bool bCreateIfNotAxis)// = true, = true
{
	if( !gl )
		return false;
	
	Tree 		tr;	
	TreeNode	trLabel = _axis_get_labels_node(tr, nAxis, bCreateIfNotAxis);
	if( trLabel )
	{
		TreeNode 	trShow = tree_check_get_node(trLabel, "Show");
		trShow.nVal = bShow;
		
		TreeNode 	trColor = tree_check_get_node(trLabel, "Color");
		trColor.nVal = 0;
		
		gl.UpdateThemeIDs(tr.Root);
		///Jasmine 01/07/09 v8.0994b QA80-10257 MAKE_AXIS_OPERATION_UNDOABLE
		//return gl.ApplyFormat(tr, true, true);
		return gl.ApplyFormat(tr, true, true, true);
		///End MAKE_AXIS_OPERATION_UNDOABLE
	}
	
	return false;	
}

int		axis_label_get_show(GraphLayer& gl, int nAxis)
{
	if( !gl )
		return -1;
	
	Tree 		tr;
	tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);
	
	TreeNode	trLabel = _axis_get_labels_node(tr, nAxis, false);
	if( trLabel )
	{
		TreeNode trShow = trLabel.GetNode("Show");
		if( trShow )
			return trShow.nVal;	
	}

	return -1;
}

///Jasmine 08/18/09 GUI_TO_ADD_2ND_NONLINEAR_AXIS
bool	get_set_axis_label_formula(GraphLayer& gl, int nAxis, string& strFormula, bool bGet/* = true*/)
{
	if( !gl )
		return false;

	string 	strLabel;
	Axis 	axis;
	switch(nAxis)
	{
	case AXIS_BOTTOM:		
	case AXIS_TOP:
		strLabel = AXIS_BOTTOM == nAxis? "BottomLabels" : "TopLabels";
		axis = gl.XAxis;
		break;
		
	case AXIS_LEFT:
	case AXIS_RIGHT:
		strLabel = AXIS_LEFT == nAxis? "LeftLabels" : "RightLabels";
		axis = gl.YAxis;
		break;
		
	default:
		return false;
	}
	
	Tree trFmt;
	trFmt = axis.GetFormat(FPB_ALL, FOB_AXIS_LABELS, TRUE, TRUE);
	//tree structure: trFmt.Root.Labels.LeftLabels.Formula
	TreeNode trLabel = tree_get_node_by_tagname(trFmt, strLabel, true);
	
	string strFormulaNode = "Formula";
	if(bGet)
	{
		strFormula = trLabel.GetNode(strFormulaNode)? trLabel.GetNode(strFormulaNode).strVal : "";
		return true;
	}

	//set
	tree_check_get_node(trLabel, strFormulaNode).strVal = strFormula;
	int iRet = axis.UpdateThemeIDs(trFmt.Root, "Error", "Unknown tag");
	ASSERT(0 == iRet);
	return axis.ApplyFormat(trFmt, TRUE, TRUE, TRUE);
	
}
///End GUI_TO_ADD_2ND_NONLINEAR_AXIS

static TreeNode _get_axis_node(TreeNode& tr, int nAxis)
{
	TreeNode	trAxes;
	string 	strAxis = _get_axis_name_from_index(nAxis);
	if(strAxis.IsEmpty())
		return trAxes;

	TreeNode trN = tree_check_get_node(tr, "Root");
	trN = tree_check_get_node(trN, "Axes");
	trAxes = tree_check_get_node(trN, strAxis);
	return trAxes;	
	
}

static TreeNode	_get_tick_node(TreeNode& tr, int nAxis, bool bCreateIfNotExist = false)
{
	TreeNode 	trTick;
	TreeNode 	trAxis = _get_axis_node(tr, nAxis);
	if( !trAxis )
		return trTick;

	string		str;
	switch(nAxis)
	{
	case AXIS_BOTTOM:
		str = "BottomTicks";
		break;
		case AXIS_TOP:
		str = "TopTicks";
		break;
	case AXIS_LEFT:
		str = "LeftTicks";
		break;
	case AXIS_RIGHT:
		str = "RightTicks";
		break;
	default:
		return trTick;
	}	
	
	TreeNode 	trTicks = tree_check_get_node(trAxis, "Ticks");	
	if(bCreateIfNotExist)
		trTick = tree_check_get_node(trTicks, str);
	else
		trTick = trTicks.GetNode(str);
	
	return trTick;
}

bool	axis_tick_set_show(GraphLayer& gl, int nAxis, bool bShow, bool bCreateIfNotAxis)// = -1, = true, = true
{
	if( !gl )
		return false;
	
	Tree 		tr;	
	TreeNode	trTick = _get_tick_node(tr, nAxis, bCreateIfNotAxis);
	if( trTick )
	{
		TreeNode trShow = tree_check_get_node(trTick, "Show");
		trShow.nVal = bShow;
		
		TreeNode trColor = tree_check_get_node(trTick, "Color");
		trColor.nVal = 0;
		
		gl.UpdateThemeIDs(tr.Root);
		///Jasmine 01/07/09 v8.0994b QA80-10257 MAKE_AXIS_OPERATION_UNDOABLE
		//return gl.ApplyFormat(tr, true, true);
		return gl.ApplyFormat(tr, true, true, true);
		///End MAKE_AXIS_OPERATION_UNDOABLE
	}	

	return false;
}


int		axis_tick_get_show(GraphLayer& gl, int nAxis)// = -1
{
	if( !gl )
		return -1;
	
	Tree 		tr;
	tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);	
	
	TreeNode	trTick = _get_tick_node(tr, nAxis, false);
	if( trTick )
	{
		TreeNode trShow = trTick.GetNode("Show");
		if(trShow)
			return trShow.nVal;
	}	
	return -1;
}


static TreeNode _axis_get_title_node(TreeNode& tr, int nAxis, bool bCreateIfNotExist = false)
{
	TreeNode	trTitle;
	TreeNode 	trAxis = _get_axis_node(tr, nAxis);
	if( !trAxis )
		return trTitle;
	
	string		str;
	switch(nAxis)
	{
	case AXIS_BOTTOM:
		str = "BottomTitle";
		break;
		case AXIS_TOP:
		str = "TopTitle";
		break;
	case AXIS_LEFT:
		str = "LeftTitle";
		break;
	case AXIS_RIGHT:
		str = "RightTitle";
		break;
	default:
		return trTitle;
	}
	
	trTitle = tree_check_get_node(trAxis, "Titles");
	if(bCreateIfNotExist)	
		trTitle = tree_check_get_node(trTitle, str);
	else
		trTitle = trTitle.GetNode(str);
	
	return trTitle;
}
	

bool	axis_title_set_show(GraphLayer& gl, int nAxis, bool bShow, bool bCreateIfNotAxis)// = -1, = true, = true
{
	if( !gl )
		return false;
	
	Tree 		tr;	
	TreeNode	trTitle = _axis_get_title_node(tr, nAxis, bCreateIfNotAxis);
	if( trTitle )
	{
		TreeNode 	trShow = tree_check_get_node(trTitle, "Show");
		trShow.nVal = bShow;
		
		TreeNode 	trColor = tree_check_get_node(trTitle, "Color");
		trColor.nVal = 0; 
		
		gl.UpdateThemeIDs(tr.Root);
		///Jasmine 01/07/09 v8.0994b QA80-10257 MAKE_AXIS_OPERATION_UNDOABLE
		//return gl.ApplyFormat(tr, true, true);
		return gl.ApplyFormat(tr, true, true, true);
		///End MAKE_AXIS_OPERATION_UNDOABLE
	}
	
	return false;
}

int		axis_title_get_show(GraphLayer& gl, int nAxis)// = -1
{
	if( !gl )
		return -1;
	
	Tree 		tr;
	tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);
	
	TreeNode	trTitle = _axis_get_title_node(tr, nAxis, false);
	if( trTitle )
	{
		TreeNode trShow = trTitle.GetNode("Show");
		if( trShow )
			return trShow.nVal;	
	}

	return -1;
}

///Arvin 03/08/07 AXIS_TITILE_SHOULD_ADD_FACTOR_INFO_IN_HISTOGRAM_GRAPH
bool get_axis_title(GraphLayer& gl, string& strTitle, int nAxis)// = AXIS_BOTTOM
{
	if( !gl )
		return false;

	Tree 		tr;
	tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);

	TreeNode	trTitle = _axis_get_title_node(tr, nAxis, false);
	if(trTitle)
	{
		TreeNode trText = trTitle.GetNode("Text");
		if(trText)
		{
			strTitle = trText.strVal;
			return true;
		}
	}
	
	return false;
}

bool set_axis_title(GraphLayer& gl, string& strNewTitle, int nAxis)// = AXIS_BOTTOM
{
	if( !gl )
		return false;
	
	Tree 		tr;
	tr = gl.GetFormat(FPB_ALL, FOB_ALL, true, true);
	
	TreeNode	trTitle = _axis_get_title_node(tr, nAxis, false);
	if(trTitle)
	{
		TreeNode trText = trTitle.GetNode("Text");
		if(trText)
		{
			trText.Text = strNewTitle;
			gl.ApplyFormat(tr, false, true);
			return true;
		}
	}
	
	return false;
}
///END AXIS_TITILE_SHOULD_ADD_FACTOR_INFO_IN_HISTOGRAM_GRAPH

/////////////////////////////////////////////////////////////////////
bool plot_change_to_area_graph(DataPlot& dp, int nColor)
{
	if(!dp)
		return false;

	Tree tr;
	tr.Root.Line.Color.nVal = nColor;
    tr.Root.Line.LineFillArea.nVal = 2; // inclusive, data must be prepared to have proper end points
    tr.Root.Pattern.Border.Style.nVal = 0;
    tr.Root.Pattern.Fill.Pattern.Style.nVal = 0;
    tr.Root.Pattern.Fill.FillColor.nVal = nColor; // make fill area same color as line
	int iRet = dp.UpdateThemeIDs(tr.Root);
	if(iRet >= 0)
	{
		dp.ApplyFormat(tr, TRUE, TRUE);
		return true;
	}
	return false;
}
////////////////////////////////////////////////////////////////////

/// Iris 02/15/2007 v8.0563 Just used in lay xfunctions(laygetsacle, laysetsacle), update axis list to add/remove Z on graph layer if exist Z axis
void update_axis_list(GraphLayer &gl, string &strAxisList)
{
	string	strZ = "Z";
	int		nFind = strAxisList.Find(strZ);
	string	strCopy = strAxisList;
	
	if( has_z_axis(gl) && nFind < 0 )
		strAxisList += "|" + strZ;
	
	if( !has_z_axis(gl) && nFind >= 0)
	{
		strAxisList.Delete(nFind, lstrlen(strZ));
		strAxisList.TrimRight('|');
	}
	
}

bool reorder_graph_layer(GraphPage& gp, const vector<int> vnDest)
{
	if(!gp.IsValid())
		return false;
	int nSize = vnDest.GetSize(), nCount = gp.Layers.Count();
	//string strLT;
	for(int ii = 0; ii < nSize; ii++)
	{//check break link first
		int nLinkTo;
		GraphLayer gl = gp.Layers(vnDest[ii]);
		layer_get_link(gl, nLinkTo);
		vector<uint> vn;
		//if(nLinkTo >= ii)
		if(0 < vnDest.Find(vn, nLinkTo) && vn[0] >= ii)
			layer_set_link(gl, -1);
	}
	for(ii = 0; ii < nSize; ii++)
	{	///Jasmine 03/15/07 ADD_UNDO_BUTTON
		//int m = ii + 1, n = vnDest[ii];
		int m = ii, n = vnDest[ii];
		if(n == m)
			continue;
		if(m > nCount || n > nCount)
			return false;	
		//strLT.Format("page.reorder(%d, %d)", m, n);
		//gp.LT_execute(strLT);
		gp.Reorder(m, n, OCD_UNDO);
		for(int jj = 0; jj < nSize; jj++)
		{
			if(vnDest[jj] >= m && vnDest[jj] < n)
				vnDest[jj]++;
		}
		vnDest[ii] = m;
	}	///End ADD_UNDO_BUTTON
	return true;
}

int graph_get_draw_order(GraphPage& gp)
{
	if(!gp.IsValid())
		return -1;
	string strLT;
	strLT.Format("%s!page.cntrl", gp.GetName());
	double dd;
	LT_get_var(strLT, &dd);
	int nOrder = (int)dd;
	if(0 == nOrder)
		return 0;
	if(4 == nOrder)
		return 1;
	return -1;
}

void graph_set_draw_order(GraphPage& gp, bool bSequence)// = false
{
	if(!gp.IsValid())
		return;
	string strLT;
	strLT.Format("page.cntrl = %d", bSequence?4:0);
	gp.LT_execute(strLT);
}

///Jasmine 01/07/09 v8.0994d QA80-10257 REWRTIE_WITH_APPLY_FORMAT_TO_UNDO
///Jasmine 06/15/07 MODIFICATION_SET_COLOR_FUNCTION
//void fill_layer_background_color(GraphLayer &layer, int nColor, bool bBorder)// = false
//bool fill_layer_background_color(GraphLayer &layer, int* pnColor, int* pnFillColor, int* pnBorderColor)//NULL, NULL
bool fill_layer_background_color(GraphLayer &layer, int* pnColor, int* pnFillColor, int* pnBorderColor, bool bUndo)//NULL, NULL, NULL, false
{
	if(!layer.IsValid())
		return false;
	
	Tree tr;
	tr = layer.GetFormat(FPB_ALL, FOB_ALL, true, true);
	
	if(pnColor)
	{
		//layer.Background.Color.nVal = *pnColor; 
		tr.Root.Background.Color.nVal = *pnColor; 
	}
	
	if(pnFillColor)
	{
		//layer.Background.Fill.Color.nVal = *pnFillColor;
		tr.Root.Background.Fill.Color.nVal = *pnFillColor;
	}
	
	if(pnBorderColor)
	{
		//layer.Background.Border.Color.nVal = *pnBorderColor; 
		//if(!layer.Background.Border.Width.dVal)
			//layer.Background.Border.Width.dVal = 0.5;
		tr.Root.Background.Border.Color.nVal = *pnBorderColor; 
		if(!tr.Root.Background.Border.Width.dVal)
			tr.Root.Background.Border.Width.dVal = 0.5;
	}
	
	int nErr = layer.UpdateThemeIDs(tr.Root);
	if(0 == nErr)
	{
		layer.ApplyFormat(tr, true, true, bUndo);	
		return true;
	}
	
	return false;
}
///End REWRTIE_WITH_APPLY_FORMAT_TO_UNDO
bool get_layer_background_color(GraphLayer &layer, int& nColor, int* pnFillColor, int* pnBorderColor)
{
	if(!layer)
		return false;
	Tree tr;
	///------ Folger 01/12/09 v8.0995c AVOID_MULTIPLE_GET_FORMAT_ALL_TO_SPEED_UP_LAYER_MANAGEMENT_ON_SELECTED_LAYER_CHANGE
	//tr = layer.GetFormat(FPB_ALL, FOB_ALL, true, true);
	tr = layer.GetFormat(FPB_ALL, FOB_BACKGROUND, true, true);
	///------ End AVOID_MULTIPLE_GET_FORMAT_ALL_TO_SPEED_UP_LAYER_MANAGEMENT_ON_SELECTED_LAYER_CHANGE
	TreeNode trBackground = tr.Root.Background;
	nColor = trBackground.Color.nVal;
	if(pnFillColor)
		*pnFillColor = trBackground.Fill.Color.nVal;
	if(pnBorderColor)
		*pnBorderColor = trBackground.Border.Color.nVal; 
	return true;
}

///Jasmine 01/07/09 v8.0994d QA80-10257 REWRTIE_WITH_APPLY_FORMAT_TO_UNDO
//bool set_layer_border_width(GraphLayer &layer, double* pdLeft, double* pdTop, double* pdRight, double* pdBottom)
bool set_layer_border_width(GraphLayer &layer, double* pdLeft, double* pdTop, double* pdRight, double* pdBottom, bool bUndo)//NULL, NULL, NULL, NULL, false
{
	if(!layer)
		return false;
	
	Tree tr;
	tr = layer.GetFormat(FPB_ALL, FOB_ALL, true, true);	
	
	if(pdLeft)
	{
		//layer.Background.Dimension.Left.dVal = *pdLeft; 
		tr.Root.Background.Dimension.Left.dVal = *pdLeft; 
	}
	
	if(pdTop)
	{
		//layer.Background.Dimension.Top.dVal = *pdTop; 
		tr.Root.Background.Dimension.Top.dVal = *pdTop; 
	}
	
	if(pdRight)
	{
		//layer.Background.Dimension.Right.dVal = *pdRight; 
		tr.Root.Background.Dimension.Right.dVal = *pdRight; 
	}
	
	if(pdBottom)
	{
		//layer.Background.Dimension.Bottom.dVal = *pdBottom; 
		tr.Root.Background.Dimension.Bottom.dVal = *pdBottom; 
	}
		
	int nErr = layer.UpdateThemeIDs(tr.Root);
	if(0 == nErr)
	{
		layer.ApplyFormat(tr, true, true, bUndo);	
		return true;
	}
	
	return false;
}
///End REWRTIE_WITH_APPLY_FORMAT_TO_UNDO

bool get_layer_border_width(GraphLayer &layer, double* pdLeft, double* pdTop, double* pdRight, double* pdBottom)
{
	if(!layer)
		return false;
	Tree tr;
	tr = layer.GetFormat(FPB_ALL, FOB_ALL, true, true);
	TreeNode trBackground = tr.Root.Background;
	if(pdLeft)
		*pdLeft = trBackground.Dimension.Left.dVal; 
	if(pdTop)
		*pdTop = trBackground.Dimension.Top.dVal; 
	if(pdRight)
	{
		*pdRight = 3;//default
		if(trBackground.Dimension.Right)
			*pdRight = trBackground.Dimension.Right.dVal; 
	}
	if(pdBottom)
		*pdBottom = trBackground.Dimension.Bottom.dVal; 
	return true;
}

bool fill_graph_background_color(GraphPage &gp, int nColor, int* pnGradColor, int* pnGradCntrl)// NULL, NULL
{
	if(!gp.IsValid())
		return false;;
	gp.Background.BaseColor.nVal = nColor;
	if(pnGradColor)
		gp.Background.GradientColor.nVal = *pnGradColor;
	if(pnGradCntrl)
		gp.Background.GradientControl.nVal = *pnGradCntrl;
	return true;
}
bool get_graph_background_color(GraphPage &gp, int& nColor, int* pnGradColor, int* pnGradCntrl)// NULL, NULL
{
	if(!gp.IsValid())
		return false;
	Tree tr;
	tr = gp.GetFormat();
	TreeNode trBackground = tr.Root.Page.Background;
	nColor = trBackground.BaseColor.nVal;
	if(pnGradColor)
	{
		*pnGradColor = -4;
		if(trBackground.GradientColor)
			*pnGradColor = trBackground.GradientColor.nVal;
	}
	if(pnGradCntrl)
	{
		*pnGradCntrl = 0;
		if(trBackground.GradientControl)
			*pnGradCntrl = trBackground.GradientControl.nVal;
	}
	return true;
}
///End MODIFICATION_SET_COLOR_FUNCTION
//static bool _legend_update_text(GraphObject& goLegend, LPCSTR lpcszText)
//{
	//bool			bRet;
	//
	//Tree			tr;
	//tr = goLegend.GetFormat();
	//TreeNode 		trText = tr.Root.Page.Layers.All.Legend.GetNode("Text");
	//if(trText)
	//{
		//trText.strVal = lpcszText;					
		//bRet = goLegend.ApplyFormat(tr);
	//}
	//else
		//return error_report("Not found Text node in format tree.");
	//
	//return bRet;
//}

/// Hong 08/08/07 QA80-10064 FIX_LEGEND_COMBINE_FAIL_KEEP_ORIGIN_ORDER
/*
bool legend_update_text_property(GraphPage& gp, bool bAllPlotsInOneLegend, int nLayerHasLegend)
{
	if( !gp )
		return false;

	//bool 	bRet = false;
	if(bAllPlotsInOneLegend)//only one legend in this graph
	{
		string			strText;
		GraphObject 	goLegend;
		foreach(GraphLayer glTemp in gp.Layers)
		{
			if(nLayerHasLegend == glTemp.GetIndex())//get/create legend from the first layer				
			{
				goLegend = glTemp.GraphObjects(GO_LEGEND_NAME);	
				if(!goLegend)
				{
					legend_update(glTemp, -1, true); //if not legend, then create a new one
					goLegend = glTemp.GraphObjects(GO_LEGEND_NAME);
				}
			}
			else//not the first layer
			{
				glTemp.RemoveGraphObject(GO_LEGEND_NAME);
			}	
			
			// update the text of legned to include all plots of all layers 					
			foreach(DataPlot dpTemp in glTemp.DataPlots)
			{
				///---Sim 07-30-2007 SKIP_ERR_BARS_WHEN_UPDATE_ADD_LEGEND
				if ( IS_TXT_ERR_BAR_PLOT_ID(dpTemp.GetPlotType()) )
					continue;
				///---END SKIP_ERR_BARS_WHEN_UPDATE_ADD_LEGEND

				string 	str;
				str.Format("(%d.%d)", glTemp.GetIndex() + 1, dpTemp.GetIndex() + 1);
				strText = strText + "\l" + str + " %" + str + "\r\n";
			}
						
		}
		
		strText.TrimRight('\n');
		strText.TrimRight('\r');					
		//bRet = _legend_update_text(goLegend, strText);										
		goLegend.Text = strText;
	}
	else //one legend for one layer
	{
		foreach(GraphLayer glTemp in gp.Layers)
		{
			GraphObject 	goLegend = glTemp.GraphObjects(GO_LEGEND_NAME);	
			if(!goLegend)
			{
				legend_update(glTemp, -1, true); //if not legend, then create a new one
				goLegend = glTemp.GraphObjects(GO_LEGEND_NAME);
			}
			
			string 	strText;
			foreach(DataPlot dpTemp in glTemp.DataPlots)
			{
				///---Sim 07-30-2007 SKIP_ERR_BARS_WHEN_UPDATE_ADD_LEGEND
				if ( IS_TXT_ERR_BAR_PLOT_ID(dpTemp.GetPlotType()) )
					continue;
				///---END SKIP_ERR_BARS_WHEN_UPDATE_ADD_LEGEND
				
				string 	str;
				str.Format("(%d)", dpTemp.GetIndex() + 1);
				strText = strText + "\l" + str + " %" + str + "\r\n";
			}
			strText.TrimRight('\n');
			strText.TrimRight('\r');		
			//bRet = _legend_update_text(goLegend, strText);	
			goLegend.Text = strText;
		}
		
	}
	
	gp.Refresh(true); //legend will not be auto updated if not do refresh...
	//return bRet;
	return true;
}
*/
/// end FIX_LEGEND_COMBINE_FAIL_KEEP_ORIGIN_ORDER

/// Iris 07/20/2007 IMPROVE_LEGEND_UPDATE, moved to page_utils
/*
static bool _is_one_legend_for_one_layer(const GraphObject& goLegend)
{
	string		strText = goLegend.Text;
	return strText.Find('.') < 0 ? true : false;		
}
*/
///end IMPROVE_LEGEND_UPDATE

///Arvin 06/07/07 CHANGE_LEGEND_TO_USE_LONG_NAME_AND_UNIT as max's suggestion
///Arvin 07/19/07 USE_STRING_TYPE_LEGEND_DIRECTLY as CP said
/*
static string _get_str_by_legend_type(int nLegendType = LEGEND_DEFAULT)
{
	string str;
	switch(nLegendType)
	{
	case LEGEND_LL:     //@LL		= Long Name
		str = _L("@LL");
		break;
	case LEGEND_LS: 	//@LS		= Short Name
		str = _L("@LS");
		break;
	case LEGEND_LA:		//@LA		= (Long Name if available else Short Name) 
		str = _L("@LA");
		break;
	case LEGEND_LN: 	//@LN		= @LM + [@LU]	
		str = _L("@LN");
		break;
	case LEGEND_LM: 	//@LM		= comment(1stline), or longname or short name, depending on presence in that order
		str = _L("@LM");
		break;
	case LEGEND_LG:		//@LG		= @LA + [@LU]
		str = _L("@LG"); 
		break;
		
	///Jake 07/18/07 ADD_MORE_LEGEND_STR_FORMAT
	case LEGEND_LD:
		str = _L("@LD");
		break;
	case LEGEND_LP:
		str = _L("@LP");
		break;
	case LEGEND_U:
		str = _L("@U");
		break;
	case LEGEND_R:
		str = _L("@R");
		break;
	///end ADD_MORE_LEGEND_STR_FORMAT
	
	default:
		str.Empty();
		break;
	}
	return str;
}
*/
///end USE_STRING_TYPE_LEGEND_DIRECTLY
///end CHANGE_LEGEND_TO_USE_LONG_NAME_AND_UNIT

/// Iris 07/20/2007 IMPROVE_LEGEND_UPDATE, moved to page_utils
/*
///Arvin 06/07/07 CHANGE_LEGEND_TO_USE_LONG_NAME_AND_UNIT as max's suggestion
//string construct_one_legend_LT_text(const GraphObject& goLegend, const GraphLayer& gl, int nPlot)
///Arvin 07/19/07 USE_STRING_TYPE_LEGEND_DIRECTLY as CP said
//string construct_one_legend_LT_text(const GraphObject& goLegend, const GraphLayer& gl, int nPlot, int nLegendType)
string construct_one_legend_LT_text(const GraphObject& goLegend, const GraphLayer& gl, int nPlot, LPCSTR lpcszExtraFormat)
///end USE_STRING_TYPE_LEGEND_DIRECTLY
///end CHANGE_LEGEND_TO_USE_LONG_NAME_AND_UNIT
{
	string	str;
	///Arvin 06/07/07 CHANGE_LEGEND_TO_USE_LONG_NAME_AND_UNIT as max's suggestion
	//if( _is_one_legend_for_one_layer(goLegend) )
	//	str.Format("(%d)", nPlot + 1);
	//else
	//	str.Format("(%d.%d)", gl.GetIndex() + 1, nPlot + 1);
	//string 	strLegendType = _get_str_by_legend_type(nLegendType); /// Iris 06/08/2007 v8.0637 CLEANUP_CODES_TO_AVOID_LOGIC_DUPLICATE
	if( _is_one_legend_for_one_layer(goLegend) )
	{
		/// Iris 06/08/2007 v8.0637 CLEANUP_CODES_TO_AVOID_LOGIC_DUPLICATE
		//
		//if(strLegendType.IsEmpty())
			//str = _L("(%d)");
		//else
			//str = _L("(%d,") + strLegendType + _L(")"); 
		//
		str = _L("(%d");		
		///end CLEANUP_CODES_TO_AVOID_LOGIC_DUPLICATE
		str.Format(str, nPlot + 1);
	}
	else
	{
		/// Iris 06/08/2007 v8.0637 CLEANUP_CODES_TO_AVOID_LOGIC_DUPLICATE
		//
		//if(strLegendType.IsEmpty())
			//str = _L("(%d.%d)");
		//else
			//str = _L("(%d.%d,") + strLegendType + _L(")"); 
		//
		str = _L("(%d.%d");
		///end CLEANUP_CODES_TO_AVOID_LOGIC_DUPLICATE
		str.Format(str, gl.GetIndex() + 1, nPlot + 1);
	}
	///Arvin 07/16/07 v8.0660 WRONG_LEGEND_FOR_GRAPH as cp said
	string strPre = str + _L(")");
	///end WRONG_LEGEND_FOR_GRAPH
	/// Iris 06/08/2007 v8.0637 CLEANUP_CODES_TO_AVOID_LOGIC_DUPLICATE
	///Arvin 07/19/07 USE_STRING_TYPE_LEGEND_DIRECTLY as CP said
	//string 	strLegendType = _get_str_by_legend_type(nLegendType);
	string 	strLegendType = lpcszExtraFormat;
	///end USE_STRING_TYPE_LEGEND_DIRECTLY
	if( !strLegendType.IsEmpty() )
		str += _L(",") + strLegendType;
	
	str += _L(")");
	///end CLEANUP_CODES_TO_AVOID_LOGIC_DUPLICATE
	
	///end CHANGE_LEGEND_TO_USE_LONG_NAME_AND_UNIT
	string	strText;
	///Arvin 07/16/07 v8.0660 WRONG_LEGEND_FOR_GRAPH as cp said
	//strText = "\l" + str + " %" + str;
	strText = "\l" + strPre + " %" + str;
	///end WRONG_LEGEND_FOR_GRAPH
	return strText;
}
*/
///end IMPROVE_LEGEND_UPDATE

//----Iris 07/31/2007 moved to page_utils to avoid always called by FindFunction since the two functions used in many place.
/*
///Arvin 06/07/07 CHANGE_LEGEND_TO_USE_LONG_NAME_AND_UNIT as max's suggestion
///Arvin 07/19/07 USE_STRING_TYPE_LEGEND_DIRECTLY as CP said
//bool legend_append_plot(GraphLayer& gl, int nPlot, int nLegendType)
bool legend_append_plot(GraphLayer& gl, int nPlot, LPCSTR lpcszExtraFormat, LPCSTR lpcszSep) //The seperator string may not be "\r\n" as Iris said
///end USE_STRING_TYPE_LEGEND_DIRECTLY
{
	if(!gl)
		return false;
	
	GraphObject	goLegend = gl.GraphObjects(GO_LEGEND_NAME);
	if(!goLegend)
	{
		return false;
	}
	
	string		strText = goLegend.Text;
	///Arvin 06/07/07 CHANGE_LEGEND_TO_USE_LONG_NAME_AND_UNIT as max's suggestion
	//strText = strText + "\r\n" + construct_one_legend_LT_text(goLegend, gl, nPlot);
	/// Iris 06/14/2007 v8.0640 AVOILD_DUP_PLOT_LEGEND_TEXT
	//strText = strText + "\r\n" + construct_one_legend_LT_text(goLegend, gl, nPlot, nLegendType);
	string		strNewLegend = construct_one_legend_LT_text(goLegend, gl, nPlot, lpcszExtraFormat);
	string strNew = strNewLegend.GetToken(0, '%');
	int nStart = strText.Find(strNew);
	//Append new legend
	string strSep(lpcszSep);
	if(strSep.IsEmpty())
		strSep = "\r\n";
	
	if(  nStart < 0)
		strText = strText + strSep + strNewLegend;
	
	////Replace old legend by new type legend
	//else
	//{
		//int nEnd = strText.Find("\r\n", nStart);
		//if(nEnd < 0) // is the last legned
			//nEnd = strText.GetLength()-1;
		//
		//strText.Delete(nStart, nEnd-nStart+1);
		//strText.Insert(nStart, strNewLegend);
	//}	
	///end AVOILD_DUP_PLOT_LEGEND_TEXT
	///end CHANGE_LEGEND_TO_USE_LONG_NAME_AND_UNIT
	
	///Jake 07/18/07 TRIM_LEGEND_TEXT_WHEN_GOLEGENDTEXT_IS_EMPTY_AT_FIRST
	strText.TrimLeft(strSep);
	strText.TrimRight(strSep);
	///end TRIM_LEGEND_TEXT_WHEN_GOLEGENDTEXT_IS_EMPTY_AT_FIRST
	
	goLegend.Text = strText;
	gl.GetPage().Refresh(true); //legend will not be auto updated if not do refresh...
	
	return true;	
}


bool legend_delete_plot(GraphLayer& gl, int nPlot)
{
	if(!gl)
		return false;
	
	GraphObject	goLegend = gl.GraphObjects(GO_LEGEND_NAME);
	if(!goLegend)
	{
		return false;
	}
	
	string		strText = goLegend.Text;
	string		strFind = construct_one_legend_LT_text(goLegend, gl, nPlot);
	
	vector<string>	vsText;
	///Arvin 07/20/07 SUPPORT_OTHER_SEPERATOR_STRINGS
	//The seperator string may not be "\r\n" as Iris said
	//string			strSep = "\r\n";
	//str_separate(strText, strSep, vsText);
	///END SUPPORT_OTHER_SEPERATOR_STRINGS
	///Jake 07/19/07 FIX_FAIDED_TO_FIND_FORMAT	
	//int		nFind = vsText.Find(strFind);
	//if(nFind < 0 || nFind >= vsText.GetSize())
		//return false;
	//
	//vsText.RemoveAt(nFind);	
	string		strFirstPart = strFind.GetToken(0, '%');
	///Arvin 07/20/07 SUPPORT_OTHER_SEPERATOR_STRINGS	
	//for(int ii = 0; ii < vsText.GetSize(); ii++)
	//{
		//int nRet = vsText[ii].Find(strFirstPart);
		//if(nRet > -1)
		//{
			//vsText.RemoveAt(ii);
			//break;
		//}
	//}
	int nStart = strText.Find(strFirstPart);
	if(nStart < 0)
		return false;
	int nEnd = strText.Find("\l(", nStart+1);//The start of next legend
	if(nEnd < 0) //is the last legend
		nEnd = strText.GetLength();
	
	strText.Delete(nStart, nEnd-nStart);
	strText.TrimLeft("\r\n");
	strText.TrimRight("\r\n");
	///end SUPPORT_OTHER_SEPERATOR_STRINGS
	///end FIX_FAIDED_TO_FIND_FORMAT
	
	//strText = str_combine(vsText, strSep); ///Arvin 07/20/07 SUPPORT_OTHER_SEPERATOR_STRINGS
	
	goLegend.Text = strText;
	return true;
}
*/
//----


bool legend_position(GraphObject& goLegend, int bHorizontal, int nPosition)
{
	if(!goLegend)
		return false;
	
	GraphLayer gl;
	goLegend.GetParent(gl);
	
	//get layer position with unit
	int		nUnit;
	///Kyle 01/24/2009 UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
	//double 	dLeft, dTop, dRight, dBottom;
	//layer_get_position(gl, &dLeft, &dTop, &dRight, &dBottom, &nUnit);
	
	// convert other units to Inch
	//GraphPage gp;
	//gl.GetParent(gp);
	//if(LAYER_UNIT_PERCENT_OF_PAGE == nUnit)
	//{
		//Tree	trPage;
		//trPage = gp.GetFormat(FPB_DIMENSION);
		//nUnit = trPage.Root.Page.Dimension.Units.nVal;
		//dLeft = dLeft * trPage.Root.Page.Dimension.Width.dVal / 100;
		//dTop = dTop * trPage.Root.Page.Dimension.Height.dVal / 100;
		//dRight = dRight * trPage.Root.Page.Dimension.Width.dVal / 100;
		//dBottom = dBottom * trPage.Root.Page.Dimension.Height.dVal / 100;
	//}
	//
	//double 	dFactor;
	//switch(nUnit)
	//{
	//case LAYER_UNIT_PERCENT_OF_PAGE:
	//case LAYER_UNIT_INCH:
		//dFactor = 1;
		//break;
	//case LAYER_UNIT_CM: 
		//dFactor = 0.3937;
		//break;
	//case LAYER_UNIT_MM: 
		//dFactor = 0.03937;
		//break;
	//case LAYER_UNIT_PIXEL: 
		//double 	dWidRes, dHeiRes;
		//gp.LT_execute("xx = page.resx");		LT_get_var("xx", &dWidRes);
		//gp.LT_execute("yy = page.resy");		LT_get_var("yy", &dHeiRes);
		//dFactor = 1 / dWidRes;
		//break;
	//case LAYER_UNIT_POINT: 
		//dFactor = 0.01389;
		//break;		
	//default:
		//break;		
	//}
	//dLeft = dLeft * dFactor;
	//dTop = dTop * dFactor;
	//dRight = dRight * dFactor;
	//dBottom = dBottom * dFactor;
	double dPos[TOTAL_POS];
	nUnit = gl.GetPosition(dPos);
	if(nUnit != LAYER_UNIT_INCH)
		gl.UnitsConvert(LAYER_UNIT_INCH, dPos, nUnit);
	///End UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
	
	// to computer legend posotion according to layer position
	double	dLegendLeft, dLegendTop;
	if(bHorizontal)
	{
		switch(nPosition)
		{
		case LEGEND_POS_LEFT:
			///Kyle 01/24/2009 UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			//dLegendLeft = dLeft;
			dLegendLeft = dPos[LEFT_POS];
			///End UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			break;		
		case LEGEND_POS_MID:
			///Kyle 01/24/2009 UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			//dLegendLeft = dLeft + ( (dRight - dLeft) / 2 );
			dLegendLeft = dPos[LEFT_POS] + dPos[WIDTH_POS] / 2;
			///End UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			break;		
		case LEGEND_POS_RIGHT:
			///Kyle 01/24/2009 UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			//dLegendLeft = dRight;
			dLegendLeft = dPos[LEFT_POS] + dPos[WIDTH_POS];
			///End UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			break;	
		}
	}
	else
	{
		switch(nPosition)
		{
		case LEGEND_POS_TOP:
			///Kyle 01/24/2009 UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			//dLegendTop = dTop;
			dLegendTop = dPos[TOP_POS];
			///End UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			break;		
		case LEGEND_POS_MID:
			///Kyle 01/24/2009 UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			//dLegendTop = dTop + ( (dBottom - dTop) / 2 );
			dLegendLeft = dPos[TOP_POS] + dPos[HEIGHT_POS] / 2;
			///End UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			break;		
		case LEGEND_POS_BOTTOM:
			///Kyle 01/24/2009 UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			//dLegendTop = dBottom;
			dLegendLeft = dPos[TOP_POS] + dPos[HEIGHT_POS];
			///End UPDATE_CODE_TO_CONVERT_UNITS_OF_LAYER_POSITION
			break;	
		}
		
	}
	
	Tree tr;
	tr = goLegend.GetFormat(FPB_DIMENSION);	
	
	tr.Root.Page.Layers.All.Legend.Dimension.Units.nVal = 0; //0: Inch
	if(bHorizontal)
		tr.Root.Page.Layers.All.Legend.Dimension.Left.dVal = dLegendLeft;
	else
		tr.Root.Page.Layers.All.Legend.Dimension.Top.dVal = dLegendTop;
	
	return goLegend.ApplyFormat(tr);	
}

///Arvin 07/19/07 USE_STRING_TYPE_LEGEND_DIRECTLY as CP said
/*
///Jake 07/17/07 GET_LEGEND_MODE_TYPE
#define STR_NO_LEGEND_FORMAT 	"no format"

static string _get_legend_type_str_mode(const GraphLayer& gl, int nPlot)
{
	
	GraphObject	goLegend = gl.GraphObjects(GO_LEGEND_NAME);
	if(!goLegend.IsValid())
		return "";
	
	string strLengendText = goLegend.Text;
	vector<string> vsText;
	str_separate(strLengendText,"\r\n",vsText);
	
	string		strLTText = construct_one_legend_LT_text(goLegend, gl, nPlot);
	string		strFirstPart = strLTText.GetToken(1);
	for(int ii = 0; ii < vsText.GetSize(); ii++)
	{
		if(vsText[ii].FindOneOf(strFirstPart) != -1)
			break;
	}
	
	if(ii == vsText.GetSize())
		return "";
	
	int nFind = vsText[ii].Find('@');
	if(-1 == nFind)
		return STR_NO_LEGEND_FORMAT;
	
	int nLen = vsText[ii].GetLength();
	string strFomat = vsText[ii].Mid(nFind,nLen - nFind - 1);
	return strFomat;
}

static int _get_legend_mode_template(const GraphLayer& gl)
{
	GraphPage gp = gl.GetPage();
	if(!gp.IsValid())
		return -1;
	
	Tree tr;
	tr = gp.GetFormat(FPB_OTHER, FOB_OTHER, true, true);
	if(!tr.Root.Legends.IsValid())
		return -1;
	
	return tr.Root.Legends.Type.nVal;
}

int legend_get_type(const GraphLayer& gl, int nPlot)// = -1
{
	if(!gl)
		return -1;

	int nLegendMode = -1;
	if(-1 == nPlot)
	{
		nLegendMode = _get_legend_mode_template(gl);
	}
	else
	{
		string strFormat = _get_legend_type_str_mode(gl, nPlot);
		
		if(!strFormat.Compare("@LL"))
			nLegendMode = LEGEND_LL;
		
		else if(!strFormat.Compare("@LS"))
			nLegendMode = LEGEND_LS;
		
		else if(!strFormat.Compare("@LA"))
			nLegendMode = LEGEND_LA;
		
		else if(!strFormat.Compare("@LN"))
			nLegendMode = LEGEND_LN;
		
		else if(!strFormat.Compare("@LM"))
			nLegendMode = LEGEND_LM;
		
		else if(!strFormat.Compare("@LG"))
			nLegendMode = LEGEND_LG;
		
		else if(!strFormat.Compare("@LD"))
			nLegendMode = LEGEND_LD;
		
		else if(!strFormat.Compare("@LP"))
			nLegendMode = LEGEND_LP;
		
		else if(!strFormat.Compare("@U"))
			nLegendMode = LEGEND_U;
		
		else if(!strFormat.Compare("@R"))
			nLegendMode = LEGEND_R;
		
		else if(!strFormat.Compare(STR_NO_LEGEND_FORMAT))
			nLegendMode = _get_legend_mode_template(gl);
		
		else
			nLegendMode = -1;
	}
	
	return nLegendMode;
}
///end GET_LEGEND_MODE_TYPE
*/
///end USE_STRING_TYPE_LEGEND_DIRECTLY

///Jake 07/19/07 GET_LEGEND_EXTRA_FORMAT
string legend_get_extra_format(const GraphLayer& gl, int nPlot)
{
	if(!gl.IsValid())
		return "";
	
	GraphObject	goLegend = gl.GraphObjects(GO_LEGEND_NAME);
	if(!goLegend.IsValid() || goLegend.Text.IsEmpty())
		return "";
	
	vector<string> vsText;
	str_separate(goLegend.Text,"\r\n",vsText);
	
	string		strLTText = construct_one_legend_LT_text(goLegend, gl, nPlot);
	string		strFirstPart = strLTText.GetToken(0, '%');
	
	for(int ii = 0; ii < vsText.GetSize(); ii++)
	{
		if(vsText[ii].Find(strFirstPart) != -1)
		{
			int nFind = vsText[ii].Find('@');
			if(-1 == nFind)
				return "";
			else
			{
				int nLen = vsText[ii].GetLength();
				string strFomat = vsText[ii].Mid(nFind, nLen-nFind-1);
				return strFomat;
			}
		}
	}
	
	return "";
}
///GET_LEGEND_EXTRA_FORMAT

//---- CPY 7/24/07 RANGE_BROWSER_NEED_BETTER_RANGE_STR
// should just use GetRangeString
/*
//---------Folger 07/24/07 GET_RANGE_CONTENT_FROM_DATAPLOT
string get_content(const DataPlot &dp)
{
	string strRange;
	if (dp)
	{
		XYRange drXY;
		dp.GetDataRange(drXY);
		int r1, c1, r2, c2;
		Worksheet wks;
		if(drXY)
			drXY.GetRange("Y", r1, c1, r2, c2, wks);
		string dsXName, dsYName;
		if(wks)
			dsYName = wks.Columns(c1).GetName();
		Curve crv(wks, c1);
		if(crv.HasX(dsXName))
		{
			int nIndex = dsXName.Find('_');
			if(0 <= nIndex)
				dsXName = dsXName.Mid(nIndex + 1);
		}
		string strWks;
		wks.GetRangeString(strWks);
		strRange.Format(STR_RANGE_FORMAT, strWks, dsXName, dsYName);
		//string strXYRange;
		//strXYRange.Format(STR_XYRANGE_FORMAT, dsXName, dsYName);
		//string strBook, strSheet, strObj;
		//okutil_parse_complete_range_string(strWks, &strBook, &strSheet, &strObj);
		//okutil_create_complete_range_string_col_row(&strRange, strBook, strSheet, strXYRange, r1, strXYRange, r2);
	}
	return strRange;
}*/
//---------End GET_RANGE_CONTENT_FROM_DATAPLOT



int get_gl_plot_data_base(GraphLayer& gl)// 0 for wks, 1 for matrix
{
	int nPlotBase = 0;
	int nTypeID;
	foreach(DataPlot dp in gl.DataPlots)
	{
		nTypeID = dp.GetPlotType();
		if(nTypeID == IDM_PLOT_3D_MESH || nTypeID == IDM_PLOT_CONTOUR || nTypeID == IDM_PLOT_MATRIX_IMAGE )
		{
			nPlotBase = PLOT_BASE_ON_MAT;
		}
		else
			nPlotBase = PLOT_BASE_ON_WKS;

	}		
	return nPlotBase;
}

double get_factor_by_dataplot_type(int nTypeID)
{
	double dFactor = 1.0;
	switch(nTypeID)
	{
	case IDM_PLOT_SCATTER:
		dFactor = 1/100.0;
		break;
	case IDM_PLOT_3D_LINE:
		dFactor = 1/50.0;
		break;
	case IDM_PLOT_MATRIX_IMAGE:
		dFactor = 1/100.0;
		break;
		
	case IDM_PLOT_3D_MESH:
		dFactor = 1/50.0;
		break;
		
	case IDM_PLOT_LINE:		
	case IDM_PLOT_CONTOUR:
	default:
		break;
		
	}
	return dFactor;
}

int get_lowest_speed_plot_type_in_gl(GraphLayer& gl, int nPlotBase)
{
	int nLowestPlotID;
	int nTypeID;
	if(PLOT_BASE_ON_WKS == nPlotBase)
	{
		foreach(DataPlot dp in gl.DataPlots)
		{
			nTypeID = dp.GetPlotType();
			if(nTypeID == IDM_PLOT_3D_LINE)
			{
				nLowestPlotID = IDM_PLOT_3D_LINE;
				break;
			}
			else if(nTypeID == IDM_PLOT_SCATTER)
				nLowestPlotID = IDM_PLOT_SCATTER;
		}
		
	}
	
	if(PLOT_BASE_ON_MAT == nPlotBase)
	{
		
		foreach(DataPlot dp in gl.DataPlots)
		{
			if(nTypeID == IDM_PLOT_MATRIX_IMAGE)
			{
				nLowestPlotID = IDM_PLOT_MATRIX_IMAGE;
				break;
			}
			else if(nTypeID == IDM_PLOT_3D_MESH)
				nLowestPlotID = IDM_PLOT_3D_MESH;
			else
				nLowestPlotID = IDM_PLOT_CONTOUR;
		}
	}
	
	return nLowestPlotID;

}

bool apply_speed_mode( GraphLayer& gl, int nSpeedMode, int nWks, int nMax, int nMatrix, int nX, int nY )
{
	if( !gl )
		return false;
	
	///Sandy 2007-1-4 add for detecting what type of dataplot in gl and decide the factor
	int nWksPlotID = get_lowest_speed_plot_type_in_gl(gl, PLOT_BASE_ON_WKS);
	int nMatPlotID = get_lowest_speed_plot_type_in_gl(gl, PLOT_BASE_ON_MAT);
	double dWksFactor = get_factor_by_dataplot_type(nWksPlotID);
	double dMatFactor = get_factor_by_dataplot_type(nMatPlotID);
	///end 
	
	Tree 	tr;
	tr.Root.Speed.Worksheet.nVal = 1;
	tr.Root.Speed.Matrix.nVal = 1;
	//trSpeed.Worksheet.nVal = 1;
	//trSpeed.Matrix.nVal = 1;
	switch(nSpeedMode)
	{
	case SPEED_MODE_OFF://0:
		tr.Root.Speed.Worksheet.nVal = 0;
		tr.Root.Speed.Matrix.nVal = 0;
		//trSpeed.Worksheet.nVal = 0;
		//trSpeed.Matrix.nVal = 0;
		break;
	case SPEED_MODE_LOW://1:
		tr.Root.Speed.Max.nVal = SPEED_MODE_LOW_WKS_MAX * dWksFactor;
		tr.Root.Speed.XMax.nVal = SPEED_MODE_LOW_MAT_XYMAX * dMatFactor;
		tr.Root.Speed.YMax.nVal = SPEED_MODE_LOW_MAT_XYMAX * dMatFactor;
		//trSpeed.Max.nVal = SPEED_MODE_LOW_WKS_MAX * dWksFactor;
		//trSpeed.XMax.nVal = SPEED_MODE_LOW_MAT_XYMAX * dMatFactor;
		//trSpeed.YMax.nVal = SPEED_MODE_LOW_MAT_XYMAX * dMatFactor;
		break;
	case SPEED_MODE_MEDIAM://2:
		tr.Root.Speed.Max.nVal = SPEED_MODE_MEDIAM_WKS_MAX * dWksFactor;
		tr.Root.Speed.XMax.nVal = SPEED_MODE_MEDIAM_MAT_XYMAX * dMatFactor;
		tr.Root.Speed.YMax.nVal = SPEED_MODE_MEDIAM_MAT_XYMAX * dMatFactor;
		//trSpeed.Max.nVal = SPEED_MODE_MEDIAM_WKS_MAX * dWksFactor;
		//trSpeed.XMax.nVal = SPEED_MODE_MEDIAM_MAT_XYMAX * dMatFactor;
		//trSpeed.YMax.nVal = SPEED_MODE_MEDIAM_MAT_XYMAX * dMatFactor;
		break;
	case SPEED_MODE_HIGH://3:
		//trSpeed.Max.nVal = SPEED_MODE_HIGH_WKS_MAX * dWksFactor;
		//trSpeed.XMax.nVal = SPEED_MODE_HIGH_MAT_XYMAX * dMatFactor;
		//trSpeed.YMax.nVal = SPEED_MODE_HIGH_MAT_XYMAX * dMatFactor;
		tr.Root.Speed.Max.nVal = SPEED_MODE_HIGH_WKS_MAX * dWksFactor;
		tr.Root.Speed.XMax.nVal = SPEED_MODE_HIGH_MAT_XYMAX * dMatFactor;
		tr.Root.Speed.YMax.nVal = SPEED_MODE_HIGH_MAT_XYMAX * dMatFactor;
		break;
	case SPEED_MODE_CUSTOM://4:
		tr.Root.Speed.Worksheet.nVal = nWks;
		//trSpeed.Worksheet.nVal = nWks;
		if( 1 == nWks )
		{
			tr.Root.Speed.Max.nVal = nMax; 
			//trSpeed.Max.nVal = nMax * dWksFactor; 
		}
		
		tr.Root.Speed.Matrix.nVal = nMatrix;
		//trSpeed.Matrix.nVal = nMatrix;
		if( 1 == nMatrix )
		{
			tr.Root.Speed.XMax.nVal = nX ;
			tr.Root.Speed.YMax.nVal = nY ; 
			//trSpeed.XMax.nVal = nX * dMatFactor;
			//trSpeed.YMax.nVal = nY * dMatFactor; 
		}
		break;
	}
	
	
	
	gl.UpdateThemeIDs(tr.Root);
	bool bRet = gl.ApplyFormat(tr, true, true);	
	
	return true;
}

///Sandy 2008-1-10 move from blpwiz.c
bool set_dataplot_format(DataPlot& dp, int nSymbolShape,int nSymbolSize, int nEdgeColor, int nFillColor) //default rectangle
{
	Tree tr;
	tr = dp.GetFormat(FPB_ALL, FOB_ALL,true, true);
	bool bRet = true;;
	
	TreeNode trSymbolShape = tree_get_node_by_nodeid(tr, OTID_CURVE_SYMBOL_SHAPE, 3);
	if(trSymbolShape && nSymbolShape > 0)
		trSymbolShape.nVal = nSymbolShape;
	else
		bRet = false;
	
	TreeNode trSymbolSize = tree_get_node_by_nodeid(tr, OTID_CURVE_SYMBOL_SIZE, 3);
	if(bRet && trSymbolSize && nSymbolSize > 0)
		trSymbolSize.nVal = nSymbolSize;
	else
		bRet = false;
	
	
	TreeNode trEdgeColor = tree_get_node_by_nodeid(tr, OTID_CURVE_SYMBOL_EDGE_COLOR, 3);
	if(bRet && trEdgeColor && FOB_INCREMENT != nEdgeColor)
		trEdgeColor.nVal = nEdgeColor + FOB_INCREMENT;

	TreeNode trFillColor = tree_get_node_by_nodeid(tr, OTID_CURVE_SYMBOL_FILL_COLOR, 3);
	if(bRet && trFillColor && FOB_INCREMENT != nFillColor)
		trFillColor.nVal = nFillColor + FOB_INCREMENT;
	
	dp.ApplyFormat(tr, true, true);
	return bRet;
}
//end

/// Iris 10/23/2008 v8.0959b SET_PA_FIT_LINE_NOT_SELECTABLE
bool set_dataplot_selectable(DataPlot &dp, bool bEnable)
{
	if( dp )
	{
		dp.Info.System.Parameters.Selectable = bEnable;
		return true;
	}
	return false;
}
///end SET_PA_FIT_LINE_NOT_SELECTABLE

///Sandy add 2008-3-31 for PA XFs
bool active_gl_to_add_points_and_sort(GraphLayer gl, vector& vx, vector& vy)
{

	if(!gl.IsValid())
		return false;
	
	gl.CheckShowActivate();
	
	vector vsx, vsy;
	graph_get_points(vsx, vsy, STR_GRAPH_ADD_PTS_MSG1, STR_GRAPH_ADD_PTS_MSG2 );
	vy.Append(vsy);
	vx.Append(vsx);
	
	vector<uint> vn;
	vx.Sort(SORT_ASCENDING, true, vn);
	vy.Reorder(vn);

	return true;	
}

///sandy move from blpwiz.h
bool hide_lengends_of_unshow_dataplots(GraphLayer& gl)
{
	if(!gl.IsValid())
		return false;
	
	gl.LT_execute("legend -s");
	foreach(DataPlot dp in gl.DataPlots)
	{
		if((dp.Show) == false)
			if ( !legend_delete_plot(gl, dp.GetIndex()) )
				error_report("error of legend delete");
	}
	return true;
}

///------ Folger 09/10/09 PEAK_LABEL_PLOT_IN_PA_SHOULD_HAVE_CERTAIN_OFFSET
//bool rotate_offset_resize_label_plot(DataPlot& dpLabel, int nRotate ,  int nOffset , int nSize)
bool rotate_offset_resize_label_plot(DataPlot& dpLabel, int nRotate ,  int nYOffset/* = 20*/ , int nXOffset/* = 30*/, int nSize/* = 20*/)
///------ End PEAK_LABEL_PLOT_IN_PA_SHOULD_HAVE_CERTAIN_OFFSET
{
	if(!dpLabel)
		return false;
	
	Tree tr;
	tr.Root.Angle.nVal = nRotate;
	tr.Root.YOffset.nVal = nYOffset;
	///------ Folger 09/10/09 PEAK_LABEL_PLOT_IN_PA_SHOULD_HAVE_CERTAIN_OFFSET
	tr.Root.XOffset.nVal = nXOffset;
	///------ End PEAK_LABEL_PLOT_IN_PA_SHOULD_HAVE_CERTAIN_OFFSET
	tr.Root.Size.nVal = nSize;
	
	int nErr = dpLabel.UpdateThemeIDs(tr.Root);
	if(nErr == 0)
	{
		dpLabel.ApplyFormat(tr, true, true);
		return true;
	}

	error_report("rotate_offset_label_plot err = "+nErr);
	return false;
}


bool copy_xy_axises_format(GraphLayer& glDest, GraphLayer& glSource)
{
	Axis xa, ya, xa1, ya1;
	xa = glSource.XAxis;
	ya = glSource.YAxis;
	xa1 = glDest.XAxis;
	ya1 = glDest.YAxis;

	Tree trX, trY;
	trX = xa.GetFormat(FPB_ALL, FOB_ALL,true, true);
	trY = ya.GetFormat(FPB_ALL, FOB_ALL,true, true);
	
	bool bRet = true;
	if(!xa1.ApplyFormat(trX, true, true) )
	{
		error_report("copy X-axis format fail!");
		bRet = false;
	}
	if(!ya1.ApplyFormat(trY, true, true) )
	{
		error_report("copy Y-axis format fail!");
		bRet = false;
	}
	
	return bRet;
}

/// Hong 03/19/09 QA80-13282 DATA_PLOT_SAFE_WAY_TO_GET_FORMAT_TREE
Tree	dataplot_get_format_safely(DataPlot& dp, DWORD dwPropertiesFilter/* = FPB_ALL*/, DWORD dwObjFilter/* = FOB_ALL*/, BOOL bGetTagNames/* = TRUE*/,
		BOOL bRelative/* = FALSE*/, DWORD dwPropertiesFilter2/* = FPBEX_ALL*/, DWORD dwObjFilter2/* = FOB2_ALL*/,
		vector<int>* piaErrors/* = NULL*/, vector<string>* psaErrors/* = NULL*/)
{
	if ( !dp )
	{
		Tree		trJunk;
		return trJunk;
	}
	GroupPlot gp = dp.GetGroupPlot();
	if ( gp )
		return gp.GetFormat(dwPropertiesFilter, dwObjFilter, bGetTagNames, bRelative, dwPropertiesFilter2, dwObjFilter2, piaErrors, psaErrors);
	else
		return dp.GetFormat(dwPropertiesFilter, dwObjFilter, bGetTagNames, bRelative, dwPropertiesFilter2, dwObjFilter2, piaErrors, psaErrors);
}
/// end DATA_PLOT_SAFE_WAY_TO_GET_FORMAT_TREE

///Kenny 05/31/2009 ADD_FUNC_GET_PLOT_COLOR_FOR_VERTICAL_CURSOR
int get_plot_color( const DataPlot& dp )
{
	int nColor = -1;
	if (dp)
	{
		Tree trColor;
		trColor = dataplot_get_format_safely(dp, FPB_STYLE_COLOR|FPB_STYLE_COLOR_LIST);
		if (trColor)
		{
			// Since there are so many types of DataPlot, and the color values may be stored as separate nodes,
			// to get the major color, we have to check the tree for the possible TreeNode by priority.
			TreeNode tnGlobalColor	= trColor.Root.Global.Color;	// Typically for line
			TreeNode tnEdgeColor	= tree_get_node_by_tagname(trColor, "EdgeColor", true);	// Typically for scatter/symbol
			TreeNode tnFillColor	= tree_get_node_by_tagname(trColor, "FillColor", true);	// Typically for scatter/symbol/box/column/bar
			TreeNode tnOtherColor	= tree_get_node_by_tagname(trColor, "Color", true);		// Typically treated as border color
			TreeNode tnGroupColor	= tree_get_node_by_tagname(trColor, "ColorList", true);	// Typically treated as color list

			if (tnGroupColor)
			{
				GroupPlot gp = dp.GetGroupPlot();
				if (gp)
				{
					vector<uint> vnColors;
					vnColors = tnGroupColor.nVals;

					const int nPlotUID = dp.GetUID(TRUE);
					const int nGroupPlotCount = gp.GetCount();
					for (int ii = 0; ii < nGroupPlotCount; ++ii)
					{
						if (gp.GetDataPlot(ii).GetUID(TRUE) == nPlotUID)
						{
							if (ii < vnColors.GetSize())
								nColor = vnColors[ii];
							break;
						}
					}
				}
				else
					ASSERT(FALSE);
			}
			else if (tnGlobalColor && tnGlobalColor.nVal >= 0)
				nColor = tnGlobalColor.nVal;
			else if (tnEdgeColor && tnEdgeColor.nVal >= 0)
				nColor = tnEdgeColor.nVal;
			else if (tnFillColor && tnFillColor.nVal >= 0)
				nColor = tnFillColor.nVal;
			else if (tnOtherColor && tnOtherColor.nVal >= 0)
				nColor = tnOtherColor.nVal;
		}
	}
	if (nColor > SYSCOLOR_DKGRAY)
		nColor = -1;
	return nColor;
}
///End ADD_FUNC_GET_PLOT_COLOR_FOR_VERTICAL_CURSOR

/// Kenny 07/24/2009 QA80-13992 NEW_XF_FOR_GENERAL_MULTI_AXES_PLOTTING
bool set_axis_color(GraphLayer& gl, const int nAxisType, int nColor, BOOL bRepaint /*= TRUE*/, BOOL bUndo /*= FALSE*/)
{
	AxisObject ao = get_axis_object(gl, nAxisType);
	if ( ao.IsValid() )
	{
		Tree trFormat;
		trFormat.Root.Global.Color.nVal = nColor;
		int nErr = ao.UpdateThemeIDs(trFormat.Root);
		if(0 == nErr)
			return ao.ApplyFormat(trFormat, bRepaint, FALSE, bUndo);
	}
	return false;
}

bool get_axis_color(GraphLayer& gl, const int nAxisType, int& nColor)
{
	AxisObject ao = get_axis_object(gl, nAxisType);
	if ( ao.IsValid() )
	{
		Tree trFormat;
		BOOL bRelative = FALSE;
		trFormat = ao.GetFormat(FPB_STYLE_COLOR, FOB_AXIS, TRUE, bRelative);
		if ( trFormat.Root.Global.Color )
		{
			nColor = trFormat.Root.Global.Color.nVal;
			return true;
		}
	}
	return false;
}
/// End QA80-13992 NEW_XF_FOR_GENERAL_MULTI_AXES_PLOTTING

/// Kenny 07/31/2009 QA80-13992 SUPPORT_GET_PAGE_SIZE_FOR_MULTI_Y_AXES_XF
int get_graph_size(const GraphPage& gp, double& dWidth, double& dHeight)
{
	int nUnits = -1;
	if ( gp )
	{
		Tree tr;
		tr = gp.GetFormat(FPB_DIMENSION, FOB_DIMENSION);
		TreeNode tnDimension = tr.Root.Page.Dimension;
		if ( tnDimension )
		{
			dWidth = tnDimension.Width.dVal;
			dHeight = tnDimension.Height.dVal;
			nUnits = tnDimension.Units.nVal;
		}
	}
	return nUnits;
}
/// End QA80-13992 SUPPORT_GET_PAGE_SIZE_FOR_MULTI_Y_AXES_XF


/// Kenny 08/12/2009 QA80-14100 USE_UNDEFINED_STATUS_FOR_UNCOMMON_PROPERTIES

#define CHECK_GET_SUB_NODE_VAL(node, subnode, ref)	\
	TreeNode node##subnode = node.GetNode(#subnode);\
	if ( node##subnode.IsValid() )\
		ref = node##subnode.nVal;

BOOL layer_get_axes_object_properties( GraphLayer& gl, vector<int>& vnAxes, vector<int>& vnLabels, vector<int>& vnTitles, vector<int>& vnMajorTicks, vector<int>& vnMinorTicks, TreeNode& trFmt /*= NULL*/ )
{
	if ( !gl )
		return FALSE;
	BOOL bRet = TRUE;
	bRet &= vnAxes.SetSize(AXIS_TOTAL);
	bRet &= vnLabels.SetSize(AXIS_TOTAL);
	bRet &= vnTitles.SetSize(AXIS_TOTAL);
	bRet &= vnMajorTicks.SetSize(AXIS_TOTAL);
	bRet &= vnMinorTicks.SetSize(AXIS_TOTAL);
	ASSERT(bRet);

	vnAxes = vnLabels = vnTitles = vnMajorTicks = vnMinorTicks = -1;

	Tree trFormat;
	if ( trFmt )
		trFormat = trFmt;
	else
		trFormat = gl.GetFormat(FPB_SHOW|FPB_OTHER, FOB_AXIS_TICKS|FOB_AXIS_LABELS|FOB_LABELS, TRUE, TRUE);

	TreeNode trAxes = trFormat.Root.Axes;
	if(!trAxes)
	{
		ASSERT(FALSE);
		return FALSE;
	}

	TreeNode trX = trAxes.X;
	TreeNode trY = trAxes.Y;

	ASSERT(trX.IsValid() && trY.IsValid());

	TreeNode trXTicks = trX.Ticks;
	TreeNode trXLabels = trX.Labels;

	TreeNode trYTicks = trY.Ticks;
	TreeNode trYLabels = trY.Labels;

	vector<string> vsNodePrefix = {"Bottom", "Left", "Top", "Right"};

	for(int nAxisType = 0; nAxisType < AXIS_TOTAL; ++nAxisType)
	{
		bool bIsXAxis = nAxisType == AXIS_BOTTOM || nAxisType == AXIS_TOP;
		TreeNode trTicksParent, trLabelsParent;
		if ( bIsXAxis )
		{
			trTicksParent = trXTicks;
			trLabelsParent = trXLabels;
		}
		else
		{
			trTicksParent = trYTicks;
			trLabelsParent = trYLabels;
		}

		string strTicksTagName = vsNodePrefix[nAxisType] + "Ticks";
		string strLabelsTagName = vsNodePrefix[nAxisType] + "Labels";

		if ( trTicksParent.IsValid() )
		{
			TreeNode trTicks = trTicksParent.GetNode(strTicksTagName);
			if ( trTicks.IsValid() )
			{
				CHECK_GET_SUB_NODE_VAL( trTicks, Show, vnAxes[nAxisType] );
				CHECK_GET_SUB_NODE_VAL( trTicks, Major, vnMajorTicks[nAxisType] )
				CHECK_GET_SUB_NODE_VAL( trTicks, Minor, vnMinorTicks[nAxisType] )
			}
		}
		if ( trLabelsParent.IsValid() )
		{
			TreeNode trLabels = trLabelsParent.GetNode(strLabelsTagName);
			if ( trLabels.IsValid() )
			{
				CHECK_GET_SUB_NODE_VAL( trLabels, Show, vnLabels[nAxisType] )
			}
		}
		AxisObject ao = get_axis_object(gl, nAxisType);
		GraphObject goTitle = ao.GetTitleObject();
		vnTitles[nAxisType] = goTitle.IsValid() && goTitle.Show;
	}
	return TRUE;
}
/// End QA80-14100 USE_UNDEFINED_STATUS_FOR_UNCOMMON_PROPERTIES

///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
#define		DEFAULT_PLOTS_IN_LAYER		1
#define		DEFAULT_PLOT_TYPE			IDM_PLOT_LINE

typedef BOOL (*PFN_DataPlotsArrangeHelper_FILTER_FUNC)(int nValue);

static	BOOL	DataPlotsArrangeHelper_FilterPlotArrage(int nNum)
{
	return nNum <= 0;
}

static	vector<int>*	_valid_plot_types()
{
	static	vector<int>		vnPlotTypes;
	return &vnPlotTypes;
}

static	BOOL	DataPlotsArrangeHelper_FilterPlotType(int nType)
{
	/// for now, all plotypes IDM_PLOT_* are valid
	//vector<uint>	vnIndices;
	//return _valid_plot_types()->Find(vnIndices, nType) <= 0;
	return nType <= 0;
}

DataPlotsArrangeHelper::DataPlotsArrangeHelper(GraphPage& gp, const string& strPlotArrange, const string& strPlotTypes/* = NULL*/, const vector<int>& vnPlotTypes/* = NULL*/, LPCSTR lpcszTemplate/* = "Origin"*/, UINT nMaxNumLayers/* = -1*/)
{
	m_gp = gp;
	m_strTemplate = lpcszTemplate;
	m_nMaxNumLayers = nMaxNumLayers;
	m_nPlotPos = m_nLayerPos = 0;
	m_nCurrentPlotType = -1;
	m_bIsNewLayer = FALSE;

	
	if ( vnPlotTypes )
		*_valid_plot_types() = vnPlotTypes;
	else
		_valid_plot_types()->RemoveAll();
	
	InitIntArray(m_vnPlotsInLayer, strPlotArrange, DEFAULT_PLOTS_IN_LAYER, "DataPlotsArrangeHelper_FilterPlotArrage");
	InitIntArray(m_vnPlotTypes, strPlotTypes, DEFAULT_PLOT_TYPE, "DataPlotsArrangeHelper_FilterPlotType");
}

void			DataPlotsArrangeHelper::InitIntArray(vector<int>& vn, const string& str, int nDefaultValue, LPCSTR lpcszFilterFunction)
{
	if ( NULL == str )
		return;

	PFN_DataPlotsArrangeHelper_FILTER_FUNC	pfnFilter = Project.FindCompiledFunction(lpcszFilterFunction);
	if ( !pfnFilter )
	{
		ASSERT(FALSE);
		return;
	}
	
	GetIntTokens(vn, str);
	if ( vn.GetSize() == 0 )
	{
		vn.Add(nDefaultValue);
	}
	else
	{
		for ( int ii=0; ii<vn.GetSize(); ++ii )
		{
			if ( pfnFilter(vn[ii]) )
				vn[ii] = nDefaultValue;
		}
	}
}

void			DataPlotsArrangeHelper::GetIntTokens(vector<int>& vn, const string& str)
{
	vector<string>		vs;
	str.GetTokens(vs);
	convert_string_vector_to_int_vector(vs, vn);
}

int				DataPlotsArrangeHelper::GetArrayItem(vector<int>& vn, int nIndex, int nDefaultValue)
{
	if ( vn.GetSize() == 0 )
		return nDefaultValue;

	return nIndex < vn.GetSize() ? vn[nIndex] : vn[vn.GetSize()-1];
}

GraphLayer		DataPlotsArrangeHelper::GetLayerAdvanced()
{
	if ( !m_gp )
		return NULL;

	m_bIsNewLayer = 0 == m_nPlotPos;

	///Jasmine 11/19/09 QA80-14619-P3 CHECK_USE_EXISTING_LAYERS_IN_PAGE
	//m_nLayerPos = CheckAddLayers(m_nLayerPos + 1);
	m_nLayerPos = CheckGetLayerPos(m_nLayerPos);
	///End CHECK_USE_EXISTING_LAYERS_IN_PAGE

	m_glCurrent = m_gp.Layers(m_nLayerPos);
	m_nCurrentPlotType = GetArrayItem(m_vnPlotTypes, m_nPlotPos, DEFAULT_PLOT_TYPE);
	
	int		nNumPlotsInLayer = GetArrayItem(m_vnPlotsInLayer, m_nLayerPos, DEFAULT_PLOTS_IN_LAYER);
	if ( ++m_nPlotPos >= nNumPlotsInLayer )
	{
		m_nPlotPos = 0;
		++m_nLayerPos;
	}
	
	return GetLayer();
}

GraphLayer		DataPlotsArrangeHelper::GetLayer()
{
	return m_glCurrent;
}

int				DataPlotsArrangeHelper::GetPlotType()
{
	return m_nCurrentPlotType;
}

BOOL			DataPlotsArrangeHelper::IsNewLayer()
{
	return m_bIsNewLayer;
}

BOOL			DataPlotsArrangeHelper::IsDataPlotsGroupAllowed()
{
	return m_vnPlotTypes.GetSize() == 1;
}

int				DataPlotsArrangeHelper::IsAddLayerAllowed()
{
	return m_gp.Layers.Count() < m_nMaxNumLayers;
}
///Jasmine 11/19/09 QA80-14619-P3 CHECK_USE_EXISTING_LAYERS_IN_PAGE
/*
int				DataPlotsArrangeHelper::CheckAddLayers(int nMax)
{
	int		nIndex = m_gp.Layers.Count() - 1;
	if ( IsAddLayerAllowed() )
	{
		while ( m_gp.Layers.Count() < nMax )
			nIndex = m_gp.AddLayer(NULL, 0, m_strTemplate);
	}

	return nIndex;
}
*/
int				DataPlotsArrangeHelper::CheckGetLayerPos(int nPos)
{
	int		nIndex = nPos;
	if( nPos >= m_gp.Layers.Count() )
	{
		int nDifference = nPos + 1 - m_gp.Layers.Count();//1 offset
		for(int ii = 0; ii < nDifference && IsAddLayerAllowed(); ii++)
		{
			nIndex = m_gp.AddLayer(NULL, 0, m_strTemplate);
		}
		
		//in case not allow AddLyer or AddLayer fail
		if(nPos >= m_gp.Layers.Count() || nIndex < 0)
			nIndex = m_gp.Layers.Count() - 1;
	}
	
	return nIndex;
}
///End CHECK_USE_EXISTING_LAYERS_IN_PAGE

static		void	_check_add_item(vector<int>* pnArrangement, int nn)
{
	if ( pnArrangement )
		pnArrangement->Add(nn);
}

int				DataPlotsArrangeHelper::GetTotalLayers(int nPlots, vector<int>* pnArrangement/* = NULL*/, UINT nMaxLayer/* = -1*/)
{
	if ( pnArrangement )
		pnArrangement->RemoveAll();

	for ( int ii=0; ii<m_vnPlotsInLayer.GetSize() && (nPlots -= m_vnPlotsInLayer[ii])>0; ++ii )
	{
		_check_add_item(pnArrangement, m_vnPlotsInLayer[ii]);
	}

	if ( ii > 0 && ii == m_vnPlotsInLayer.GetSize() )
	{
		int		nNumPlotsRepeat = m_vnPlotsInLayer[m_vnPlotsInLayer.GetSize()-1];
		while ( (nPlots -= nNumPlotsRepeat) > 0 )
		{
			_check_add_item(pnArrangement, nNumPlotsRepeat);
			++ii;
		}
		
		_check_add_item(pnArrangement, nPlots + nNumPlotsRepeat);
	}
	else
	{
		_check_add_item(pnArrangement, nPlots + m_vnPlotsInLayer[ii]);
	}

	if ( -1 != nMaxLayer )
	{
		if ( pnArrangement )
		{
			vector<int>&	vn = *pnArrangement;
			for ( int jj=nMaxLayer; jj<vn.GetSize(); ++jj )
			{
				vn[nMaxLayer-1] += vn[jj];
			}
			vn.SetSize(nMaxLayer);
		}
		ii = nMaxLayer - 1;
	}

	return ii + 1;
}

//Jasmine 11/19/09 QA80-14619-P3 CHECK_USE_EXISTING_LAYERS_IN_PAGE
//void			DataPlotsArrangeHelper::CheckAppendLayers()
//{
	//CheckAddLayers(m_nMaxNumLayers);
//}
void			DataPlotsArrangeHelper::CheckLayerCount()
{
	if(m_gp.Layers.Count() < m_nMaxNumLayers)
		CheckGetLayerPos(m_nMaxNumLayers - 1);
	else if(m_gp.Layers.Count() > m_nMaxNumLayers) 
	{
		int nDifference = m_gp.Layers.Count() - m_nMaxNumLayers;
		for(int ii = 0; ii < nDifference; ii++)
		{
			Layer lay = m_gp.Layers(m_nMaxNumLayers);
			if(lay) lay.Delete();
		}
		ASSERT(m_gp.Layers.Count() == m_nMaxNumLayers) ;
	}
}
	///End CHECK_USE_EXISTING_LAYERS_IN_PAGE

string			DataPlotsArrangeHelper::MakeActualNumPlotsInLayerString(int nPlots)
{
	vector<int>	vn;
	int			nLayers = GetTotalLayers(nPlots, &vn, m_nMaxNumLayers);
	
	const	int	nMaxNumLeft = 8;
	const	int	nMaxNumRight = 2;
	BOOL		bShowDot = vn.GetSize() > nMaxNumLeft + nMaxNumRight;
	
	string		str;
	for ( int ii=0; ii<vn.GetSize(); ++ii )
	{
		if ( bShowDot && ii >= nMaxNumLeft )
		{
			str += " ...";
			ii = vn.GetSize() - nMaxNumRight - 1;
			bShowDot = FALSE;
			continue;
		}

		string	strTemp;
		strTemp.Format(" %d", vn[ii]);
		str += strTemp;
	}
	
	return str;
}
///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS

///Kyle 03/11/2010 PICK_PEAK_ROI_TOOL_SHARES_UTILS_FUNCTION_WITH_PA

bool plot_xyr_to_graph(XYRange& xy,  GraphLayer gl, int nPlotType, int nColor, DataPlot& dpXY) // IDM_PLOT_LINE, = SYSCOLOR_BLACK, = NULL
{
	if(!xy.IsValid() || !gl.IsValid())
		return false;
	
	if(xy.IsValid())
	{
		DataPlot dp;
		//if(!get_plot_from_xyr_in_gl(gl, xy, dp))
		if(!get_plot_from_xyr_in_gl(gl, xy, dp, nPlotType))
		{
			 int nplot = gl.AddPlot(xy, nPlotType);//, GAP_USE_WKS_DESIGNATION);
			 dp = gl.DataPlots(nplot);
			 if(!dp)
			 {
				error_report("get plot from xyr fail in putResultToOriginalGraph()");
			 }
			 else
			 {
				dp.SetColor(nColor);
				int nplot = dp.GetIndex();
				legend_append_plot(gl, nplot);	
			 }
		}
		
		if(dpXY != NULL)
			dpXY = dp;
	}	
	
	return true;
}


static bool _plot_peak_center_to_graph(XYRange& yp, GraphLayer& gl )
{
	if( !yp.IsValid() || !gl.IsValid() )
		return false;
	
	DataPlot dpCenter;
	if(!plot_xyr_to_graph(yp,   gl, IDM_PLOT_SCATTER, SYSCOLOR_RED, dpCenter))
		return false;
	
	 if(!dpCenter)
	 {
		error_report("Invalid peak-center plot  when init peak-center plot!");
		return false;
	 }

	if(dpCenter.IsValid())
		set_dataplot_format(dpCenter, 16, 15);	//red color, hard code	
	
	return true;
}

bool get_plot_from_xyr_in_gl(GraphLayer gl, XYRange& xy, DataPlot& dp, int nPlotType)
{
	if(!xy||!gl)
	{
		//error_report("In PAWizCore.c, get_plot_from_xyr_in_gl(), Invalid xyrange or gl!");
		return false;
	}
	
	vector<int> vnPlotIndices;	
	
	///Sandy 2008-10-9 waitting for yuri
	//if(0>check_has_plotted_in_graph(xy, gl, vnPlotIndices) || vnPlotIndices.GetSize() <= 0)
	if(0>check_has_plotted_in_graph(xy, gl, vnPlotIndices, NTYPE_BOOKSHEET_XY_RANGE) || vnPlotIndices.GetSize() <= 0)
	{
		//error_report("In PAWizCore.c, get_plot_from_xyr_in_gl(), no dataplot!");
		return false;
	}
	
	if(nPlotType == -1)
	{
		int nplot = vnPlotIndices[0];
		dp = gl.DataPlots(nplot);
		return true;
	}
	else
	{
		for(int ii = 0; ii < vnPlotIndices.GetSize(); ii++)
		{
			DataPlot dpTemp;
			int nplot = vnPlotIndices[ii];
			dpTemp = gl.DataPlots(nplot);
			int nType = dpTemp.GetPlotType();
			if(nType == nPlotType)
			{
				dp = dpTemp;
				return true;
			}
		}
	}
	return false;
}


DataPlot add_label_plot(GraphLayer& gl, XYRange& xy, int nLabelXYType, int nYOffset,  int nXOffset,  bool bRotateLabel, int nLabelColor) //= LABEL_Y_VAL_GUI, = 0, = 0, = false, = -1
{
	if(!gl)
		return NULL;
	
	int nplot = gl.AddPlot(xy, IDM_PLOT_TEXT);//IDM_PLOT_SCATTER);
	DataPlot dp = gl.DataPlots(nplot);
	if(!dp)
		return NULL;
	
	//------ Folger 11/10/08 QA80-12509 v8.0968 ONLY_SET_PLOT_LABEL_TYPE_IN_PEAK_FIT_DIALOG_EXCLUDE_ROTATION
	//dp.LabTalk("tts", &nLabelXYType, true);
	dp.LabTalk(STR_PLOT_DATA_LABEL_TYPE, &nLabelXYType, true);
	//------
	///Kyle 03/11/2010 PICK_PEAK_ROI_TOOL_SHARES_UTILS_FUNCTION_WITH_PA
	if( nLabelColor >= 0 )
		dp.SetColor(nLabelColor);
	///End PICK_PEAK_ROI_TOOL_SHARES_UTILS_FUNCTION_WITH_PA

	///Sandy waitting for 12141 to use Y value

	int nRotate = (bRotateLabel? 90:0);
	///------ Folger 09/10/09 PEAK_LABEL_PLOT_IN_PA_SHOULD_HAVE_CERTAIN_OFFSET
	//if(!rotate_offset_resize_label_plot(dp, nRotate ,  nOffset))
	if(!rotate_offset_resize_label_plot(dp, nRotate, nYOffset, nXOffset))
	///------ End PEAK_LABEL_PLOT_IN_PA_SHOULD_HAVE_CERTAIN_OFFSET
		error_report("Fail to modify format of plot");

	return dp;

}


bool   update_peaks_labels(GraphLayer gl, XYRange xyPeaksCenter,  bool bShowCenter, bool bShowLabel, int nLabelXYType, bool bRotateLabel, int nLabelColor)
{
	if(!gl||!xyPeaksCenter)
		return false;
	
	DataPlot dp;
	if(get_plot_from_xyr_in_gl(gl, xyPeaksCenter, dp, IDM_PLOT_TEXT))
	{
		dp.Destroy();
	}
	if(bShowLabel)
	{
		///------ Folger 09/10/09 PEAK_LABEL_PLOT_IN_PA_SHOULD_HAVE_CERTAIN_OFFSET
		//dp = _add_label_plot(gl, xyPeaksCenter, nLabelXYType, 0,  bRotateLabel);
		///Kyle 03/11/2010 PICK_PEAK_ROI_TOOL_SHARES_UTILS_FUNCTION_WITH_PA
		//dp = add_label_plot(gl, xyPeaksCenter, nLabelXYType, 20,  30, bRotateLabel);
		dp = add_label_plot(gl, xyPeaksCenter, nLabelXYType, 20,  30, bRotateLabel, nLabelColor);
		///End PICK_PEAK_ROI_TOOL_SHARES_UTILS_FUNCTION_WITH_PA
		///------ End PEAK_LABEL_PLOT_IN_PA_SHOULD_HAVE_CERTAIN_OFFSET
		if(!dp)
			error_report("fail to add label plot!");
	}
	
	//create peak plot
	if(bShowCenter )
	{
		DataPlot dp;
		
		///Sandy 2008-9-12 after the change of #12141, peak centers should be plotted twice, but marker("|") plot be detected
		//if(!get_plot_from_xyr_in_gl(gl, xyPeaksCenter, dp))
		///Kyle 03/11/2010 PICK_PEAK_ROI_TOOL_SHARES_UTILS_FUNCTION_WITH_PA, peak center is a scatter plot
		//if(!get_plot_from_xyr_in_gl(gl, xyPeaksCenter, dp, IDM_PLOT_LINE))
		if(!get_plot_from_xyr_in_gl(gl, xyPeaksCenter, dp, IDM_PLOT_SCATTER))
		///End PICK_PEAK_ROI_TOOL_SHARES_UTILS_FUNCTION_WITH_PA
		{
			if(!_plot_peak_center_to_graph(xyPeaksCenter,gl))
				return false;
		}
	}
	///Kyle 03/11/2010 PICK_PEAK_ROI_TOOL_SHARES_UTILS_FUNCTION_WITH_PA
	else
	{
		DataPlot dp;
		if( get_plot_from_xyr_in_gl(gl, xyPeaksCenter, dp, IDM_PLOT_SCATTER) )
		{
			legend_delete_plot(gl, dp.GetIndex());
			dp.Destroy();
		}
	}
	///End PICK_PEAK_ROI_TOOL_SHARES_UTILS_FUNCTION_WITH_PA
	
	return true;
}

///End PICK_PEAK_ROI_TOOL_SHARES_UTILS_FUNCTION_WITH_PA
static bool _set_axis_thinkness(GraphLayer& gl, double dThinkness, AxisObject& ao)
{
	Tree trFormat;
	trFormat.Root.Width.dVal = dThinkness;
	
	ASSERT(ao);
	if( ao )
	{
		if( 0 == ao.UpdateThemeIDs(trFormat.Root) )
			return ao.ApplyFormat(trFormat, TRUE, TRUE);
	}	
	return false;
}

bool set_axis_thinkness(GraphLayer& gl, double dThinkness, int nAxis)
{
	if( !gl || dThinkness < 0 )
		return false;
	
	
	if( AXIS_TOTAL != nAxis )
	{
		AxisObject ao = get_axis_object(gl, nAxis);
		return _set_axis_thinkness(gl, dThinkness, ao);
	}
	else
	{
		for(int nn = 0; nn < AXIS_TOTAL; nn++)
		{
			AxisObject ao = get_axis_object(gl, nn);
			_set_axis_thinkness(gl, dThinkness, ao);
		}
		return true;			
	}
	return false;
}