#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
enum{
	plottype_line,
	plottype_scatter,
	plottype_linesymb,
	plottype_custom,
};

/*------------------------------------------------------------------------------*
 * Modification Log:															*
 *	Kenny 08/25/2009 QA81-14203 RESET_PAGE_NAME_IF_CREATED_FROM_OC				*
 *	Kenny 08/31/2009 QA81-13992	CORRECT_SETTING_FIRST_LAYER_UNITS				*
 *	Kenny 09/09/2009 QA81-14268 SUPPORT_LINK_AXIS_TITLE_COLOR_TO_PLOT			*
 *	Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
 *	Jasmine 08/13/2010 ORG-172-S2 CENTRALIZE_NOCLICK_BIT_FOR_PREVIEW_IN_OC_TOOL	*
 *------------------------------------------------------------------------------*/

//put additional include files here

#include <event_utils.h>
#include <..\Originlab\graph_utils.h>
#include <..\Originlab\grobj_utils.h>	/// Kenny 09/09/2009 QA81-14268 SUPPORT_LINK_AXIS_TITLE_COLOR_TO_PLOT

//put your own global variables here
static string	s_strErrMsg		= "";

//put your own support static functions here

///Jasmine 08/13/2010 ORG-172-S2 CENTRALIZE_NOCLICK_BIT_FOR_PREVIEW_IN_OC_TOOL
//#define	PREVIEW_GRAPH_NOCLICKS		(NOCLICK_WINDOW | NOCLICK_LABEL | NOCLICK_DATA_PLOT | NOCLICK_TICKLABEL | NOCLICK_LAYER | NOCLICK_LAYERICON | NOCLICK_AXES | NOCLICK_BUTTONS)
#define	PREVIEW_GRAPH_NOCLICKS		(OC_PREVIEW_NOCLICK_NOZOOMPAN | NOCLICK_WINDOW | NOCLICK_LABEL | NOCLICK_BUTTONS)
///End CENTRALIZE_NOCLICK_BIT_FOR_PREVIEW_IN_OC_TOOL

/*----------------------------------------------------------------------------*/
/* class PlotMultiAxesHelper
/*----------------------------------------------------------------------------*/
enum
{
	YAP_LEFT_MOST,
	YAP_RIGHT_MOST,
	YAP_MIDDLE,
};

#define SYSCOLOR_TOTAL					(SYSCOLOR_DKGRAY+1)

#define MYAXES_TEMPLATE					"myaxes"

#define DEFAULT_AXIS_GAP				10
#define DEFAULT_AXIS_GAP_WITH_TITLE		14

class PlotMultiAxesHelper
{
public:
	XYRange		m_XYRange;
	GraphPage	m_gp;
	int			m_nLeftYAxes;
	int			m_nRightYAxes;
	int			m_nMiddleYAxes;
	bool		m_bLinkAxisColor;
	bool		m_bShowYAxisTitle;
	bool		m_bShowTopXAxis;
	
	bool		m_bIsPreviewing;
	
	///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	string		m_strPlotArrange;
	string		m_strPlotTypes;
	///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
protected:
	int			m_nPlottype;
	double		m_dAxesGap;
public:
	PlotMultiAxesHelper(
		bool bIsPreviewing = false,
		const XYRange& iy = NULL, 
		int nGUIPlotType = 0, 
		int nLeftYAxes = 0, 
		int nRightYAxes = 0, 
		int nMiddleYAxes = 0, 
		bool bLinkAxisColor = true, 
		bool bShowYAxisTitle = false, 
		bool bShowTopXAxis = false,
		double dAxesGap = -1,
		PageBase& pgTemp = NULL
		)
	{
		m_bIsPreviewing = bIsPreviewing;
		m_gp = pgTemp;
		m_XYRange = iy;
		SetPlotType(nGUIPlotType);
		m_nLeftYAxes = nLeftYAxes;
		m_nRightYAxes = nRightYAxes;
		m_nMiddleYAxes = nMiddleYAxes;
		m_bLinkAxisColor = bLinkAxisColor;
		m_bShowYAxisTitle = bShowYAxisTitle;
		m_bShowTopXAxis = bShowTopXAxis;
		SetAxesGap(dAxesGap);
	}
public:
	BOOL Perform()
	{
		if ( !Init() )
		{
			ASSERT(FALSE);
			return FALSE;
		}

		int nRangeNum = 0;
		if ( !m_XYRange || (nRangeNum = m_XYRange.GetNumData(DRR_GET_DEPENDENT)) <= 0 )
		{
			XF_THROW(CER_INVALID_RANGE);
			return FALSE;
		}
		// Do the plotting.
		///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
		//int nCount = 0;
		vector<int>		vnPlotTypes = {IDM_PLOT_LINE
			, IDM_PLOT_SCATTER
			, IDM_PLOT_LINESYMB
		};
		DataPlotsArrangeHelper		clHelper(m_gp, m_strPlotArrange, m_strPlotTypes, vnPlotTypes, NULL, m_nLeftYAxes + m_nRightYAxes + m_nMiddleYAxes);
		///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
		for(int ii = 0; ii < nRangeNum; ii++)
		{
			DataRange dr;
			m_XYRange.GetSubRange(dr, DRR_GET_DEPENDENT | DRR_NO_FACTORS, ii);
			///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
			//BOOL bRet = DoNextPlotMultiAxes(dr, nCount);
			//if (bRet)
				//{
				//++nCount;
			//}
			BOOL	bRet = DoNextPlotMultiAxes(dr, clHelper);
			///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
		}
		
		///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
		clHelper.CheckLayerCount();//clHelper.CheckAppendLayers();	///Jasmine 11/19/09 QA80-14619-P3 CHECK_USE_EXISTING_LAYERS_IN_PAGE
		for ( ii=clHelper.GetTotalLayers(nRangeNum); ii<m_gp.Layers.Count(); ++ii )
		{
			GraphLayer	gl = m_gp.Layers(ii);
			if ( !gl )
				break;
			
			initGraphLayer(gl);
		}
		///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS

		legend_combine(m_gp, 0);

		GraphLayer glFirst = m_gp.Layers(0);
		glFirst.Rescale();

		return TRUE;
	}
	void SetPlotType(int nGUIPlotType)
	{
		m_nPlottype = convert_gui_plot_type(nGUIPlotType);
	}
	void SetAxesGap(double dAxesGap = -1)
	{
		m_dAxesGap = dAxesGap >= 0 ? dAxesGap : (m_bShowYAxisTitle ? DEFAULT_AXIS_GAP_WITH_TITLE : DEFAULT_AXIS_GAP);
	}
protected:
	BOOL Init()
	{
		if ( !m_gp )
		{
			/// Kenny 08/25/2009 QA81-14203 RESET_PAGE_NAME_IF_CREATED_FROM_OC
			//m_gp.Create(MYAXES_TEMPLATE);
			if (m_bIsPreviewing)
				m_gp.Create(MYAXES_TEMPLATE);
			else
				m_gp.Create(MYAXES_TEMPLATE, CREATE_DEFAULT_OPTIONS | CREATE_ENUM_EXIST_PAGE);
			/// End QA81-14203 RESET_PAGE_NAME_IF_CREATED_FROM_OC
		}
		if ( !m_gp.IsValid() )
		{
			XF_THROW(CER_UNKNOWN_ERROR);
			return FALSE;
		}
		if (m_bIsPreviewing)
		{
			page_load(m_gp, MYAXES_TEMPLATE, PREVIEW_GRAPH_NOCLICKS);
		}
		initFirstLayerSizePos();
		return TRUE;
	}
	///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	//BOOL DoNextPlotMultiAxes(DataRange& dr, const int nLayerIndex)
	BOOL DoNextPlotMultiAxes(DataRange& dr, DataPlotsArrangeHelper& clHelper)
	///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	{
		if( !dr || !dr.GetNumData() )
			return FALSE;
		///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
		//GraphLayer gl;
		//int nLayerCount = m_gp.Layers.Count();
		//if( nLayerIndex < nLayerCount )
		//{
			//gl = m_gp.Layers(nLayerIndex);
		//}
		//else
		//{
			//int nIndex = m_gp.AddLayer();
			//if (nIndex >= 0)
				//gl = m_gp.Layers(nIndex);
		//}
		GraphLayer	gl = clHelper.GetLayerAdvanced();
		int			nLayerIndex = gl.GetIndex();
		///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
		if(gl.IsValid())
		{
			///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
			if ( clHelper.IsNewLayer() )
			///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
				initGraphLayer(gl);

			///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
			//int nPlotIndex = gl.AddPlot(dr, m_nPlottype);
			int nPlotIndex = gl.AddPlot(dr, m_nPlottype > IDM_PLOT_LINESYMB ? clHelper.GetPlotType() : m_nPlottype);
			///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
			if ( nPlotIndex >= 0 )
			{
				DataPlot dp = gl.DataPlots(nPlotIndex);

				dp.SetColor( getEnumPlotColor(nLayerIndex) );
				
				if ( IDM_PLOT_SCATTER == m_nPlottype || IDM_PLOT_LINESYMB == m_nPlottype )
				{
					dp.SetSymbol( getEnumPlotSymbol(nLayerIndex) );
				}

				gl.Rescale();
				return TRUE;
			}
			return TRUE;
		}
		return FALSE;
	}
private:
	void initFirstLayerSizePos()
	{
		// We don't need to resize the layer if no axes gap exist.
		if ( m_nLeftYAxes > 1 || m_nRightYAxes > 1 )
		{
			// Calculate the width of layer and the axe
			GraphLayer gl = m_gp.Layers(0);

			int nLeftAxesGapCount = m_nLeftYAxes;
			int nRightAxesGapCount = m_nRightYAxes;
			if ( nLeftAxesGapCount > 0 )
				--nLeftAxesGapCount;
			if ( nRightAxesGapCount > 0 )
				--nRightAxesGapCount;

			int nAxesGapCount = nLeftAxesGapCount + nRightAxesGapCount;
			double dAxesGapLayerPercent = nAxesGapCount * m_dAxesGap;

			// I think pixel may be a precise enough unit.
			double dLayerPos[TOTAL_POS];
			int nLayerUnits = gl.GetPosition(dLayerPos);
			if ( M_PIXEL != nLayerUnits )
			{
				BOOL bRet = gl.UnitsConvert(M_PIXEL, dLayerPos, nLayerUnits);
				ASSERT(bRet);
			}
			double dGapWidth = m_dAxesGap * dLayerPos[WIDTH_POS] / 100;
			double dTotalWidth = (dGapWidth * nAxesGapCount) + dLayerPos[WIDTH_POS];

			// Get the page's size
			double dPageWidth, dPageHeight;
			int nPageUnits = get_graph_size(m_gp, dPageWidth, dPageHeight);
			if ( UNITS_PIXEL != nPageUnits )
			{
				switch (nPageUnits)
				{
				case UNITS_INCH:
					nPageUnits = M_INCH;
					break;
				case UNITS_CM:
					nPageUnits = M_CM;
					break;
				case UNITS_MM:
					nPageUnits = M_MM;
					break;
				case UNITS_POINT:
					nPageUnits = M_PT;
					break;
				}
				double dPagePos[TOTAL_POS];
				dPagePos[WIDTH_POS] = dPageWidth;
				dPagePos[HEIGHT_POS] = dPageHeight;
				BOOL bRet = gl.UnitsConvert(M_PIXEL, dPagePos, nPageUnits);
				dPageWidth = dPagePos[WIDTH_POS];
				dPageHeight = dPagePos[HEIGHT_POS];
				ASSERT(bRet);
			}

			// Resize the layer
			bool bIsLayerAxesWiderThanPage = false;	// True when the boundary of the layers and the axes is larger than the page
			double dMaxWidth = dPageWidth - dGapWidth;

			if ( dTotalWidth > dMaxWidth )
			{
				double dLayerNewWidth = dMaxWidth / (100+dAxesGapLayerPercent) * 100;
				double dHalfLayerWidth = dLayerPos[WIDTH_POS] / 2;
				if ( dLayerNewWidth < dHalfLayerWidth )
				{
					dLayerNewWidth = dHalfLayerWidth;
					bIsLayerAxesWiderThanPage = true;
				}
				double dLayerNewHeight = dLayerPos[HEIGHT_POS] * dLayerNewWidth / dLayerPos[WIDTH_POS];
				
				dGapWidth = m_dAxesGap * dLayerNewWidth / 100;

				dLayerPos[WIDTH_POS] = dLayerNewWidth;
				dLayerPos[HEIGHT_POS] = dLayerNewHeight;
				dLayerPos[TOP_POS] = (dPageHeight-dLayerPos[HEIGHT_POS]) / 2;

				dTotalWidth = dMaxWidth;
			}

			// Move the layer to the proper place
			if (bIsLayerAxesWiderThanPage)
			{
				double dMinAxesOffset = ( 0.5 + min(nLeftAxesGapCount, nRightAxesGapCount) )* dGapWidth;
				double dMinMiddleOfLayer = dMinAxesOffset + dLayerPos[WIDTH_POS] / 2;
				double dCenterOfPage = dPageWidth / 2;
				if (dMinMiddleOfLayer > dCenterOfPage)
				{
					// Place the layer at the center.
					dLayerPos[LEFT_POS] = dCenterOfPage - dLayerPos[WIDTH_POS] / 2;
				}
				else
				{
					// Place the layer at the leftmost/rightmost but make the less axes side shown.
					if (nLeftAxesGapCount <= nRightAxesGapCount)
					{
						dLayerPos[LEFT_POS] = dMinAxesOffset;
					}
					else
					{
						dLayerPos[LEFT_POS] = dPageWidth - dMinAxesOffset - dLayerPos[WIDTH_POS];
					}
				}
			}
			else
			{
				// Center the layers and the axes as a whole
				double dLeftBoundary = (dPageWidth - dTotalWidth) / 2;
				dLayerPos[LEFT_POS] = dLeftBoundary + nLeftAxesGapCount * dGapWidth;
			}
			/// Kenny 08/31/2009 QA81-13992	CORRECT_SETTING_FIRST_LAYER_UNITS
			//BOOL bRet = gl.SetPosition(dLayerPos, M_PIXEL);
			BOOL bRet = gl.UnitsConvert(M_PERCENT, dLayerPos, M_PIXEL);
			bRet &= gl.SetPosition(dLayerPos, M_PERCENT);
			/// End CORRECT_SETTING_FIRST_LAYER_UNITS
			ASSERT(bRet);
		}
	}
	void initGraphLayer(GraphLayer& gl)
	{
		linkLayerSetPos(gl);

		const int nLayerIndex = gl.GetIndex();
		bool bIsFirstLayer = 0 == nLayerIndex;
		int nYAxisPosType = getYAxisPosType(nLayerIndex);
		bool bShowLeftYAxis = YAP_RIGHT_MOST != nYAxisPosType;

		vector<int> vnAxesShow(AXIS_TOTAL), vnTitleShow(AXIS_TOTAL), vnTicksShow(AXIS_TOTAL);
		vnAxesShow[AXIS_BOTTOM]		= bIsFirstLayer;
		vnAxesShow[AXIS_LEFT]		= bShowLeftYAxis;
		vnAxesShow[AXIS_TOP]		= bIsFirstLayer && m_bShowTopXAxis;
		vnAxesShow[AXIS_RIGHT]		= !bShowLeftYAxis;

		vnTitleShow[AXIS_BOTTOM]	= bIsFirstLayer;
		vnTitleShow[AXIS_LEFT]		= m_bShowYAxisTitle && bShowLeftYAxis;
		vnTitleShow[AXIS_TOP]		= 0;
		vnTitleShow[AXIS_RIGHT]		= m_bShowYAxisTitle && !bShowLeftYAxis;

		int nTickInOutType = YAP_MIDDLE == nYAxisPosType ? TICK_IN_OUT : TICK_OUT;
		vnTicksShow[AXIS_BOTTOM] 	= bIsFirstLayer ? TICK_OUT : 0;
		vnTicksShow[AXIS_LEFT] 		= bShowLeftYAxis ? nTickInOutType : 0;
		vnTicksShow[AXIS_TOP] 		= TICK_IN;
		vnTicksShow[AXIS_RIGHT] 	= !bShowLeftYAxis ? nTickInOutType  : 0;

		gl_smart_show_object(gl, vnAxesShow, vnAxesShow, vnTitleShow, vnTicksShow, vnTicksShow);

		int nYAxisType = bShowLeftYAxis ? AXIS_LEFT : AXIS_RIGHT;
		if ( m_bLinkAxisColor || m_bIsPreviewing )
		{
			/// Kenny 09/09/2009 QA81-14268 SUPPORT_LINK_AXIS_TITLE_COLOR_TO_PLOT
			//set_axis_color(gl, nYAxisType, m_bLinkAxisColor ? INDEX_COLOR_AUTOMATIC : SYSCOLOR_BLACK );
			int nColorIndex = m_bLinkAxisColor ? INDEX_COLOR_AUTOMATIC : SYSCOLOR_BLACK;
			set_axis_color(gl, nYAxisType, nColorIndex );
			if( m_bShowYAxisTitle )
			{
				setTitleObjectColor(gl, nYAxisType, nColorIndex);
			}
			/// End QA81-14268 SUPPORT_LINK_AXIS_TITLE_COLOR_TO_PLOT
		}
		
		if (bIsFirstLayer)
		{
			GraphPage gp = gl.GetPage();
			gp.Refresh(1);
		}

		AxisObject ao = get_axis_object(gl, nYAxisType);
		setYAxisPos(ao, nLayerIndex, nYAxisPosType);
	}
	int getYAxisPosType(const int nLayerIndex)
	{
		int nYAxisPosType = YAP_LEFT_MOST;
		if ( nLayerIndex < m_nLeftYAxes )
		{
			nYAxisPosType = YAP_LEFT_MOST;
		}
		else if ( nLayerIndex < m_nLeftYAxes + m_nMiddleYAxes )
		{
			nYAxisPosType = YAP_MIDDLE;
		}
		else /*if ( nLayerIndex < m_nLeftYAxes + m_nMiddleYAxes + m_nRightYAxes )*/
		{
			nYAxisPosType = YAP_RIGHT_MOST;
		}
		return nYAxisPosType;
	}
	BOOL linkLayerSetPos(GraphLayer& gl)
	{
		BOOL bRet = TRUE;
		const int nLayerIndex = gl.GetIndex();
		if (nLayerIndex > 0)
		{
			layer_set_link(gl, 0, LINK_STRAIGHT, LINK_NONE);

			double dLayer1Pos[TOTAL_POS] = {100, 100, 0, 0};
			bRet = gl.SetPosition(dLayer1Pos, M_LINK);
		}
		return bRet;
	}
	void setYAxisPos(AxisObject& ao, const int nLayerIndex, const int nYAxisPosType)
	{
		/// Kenny 09/09/2009 QA81-14268 SUPPORT_LINK_AXIS_TITLE_COLOR_TO_PLOT
		//if ( nLayerIndex == 0 || nLayerIndex == (m_nLeftYAxes+m_nMiddleYAxes) )
		if ( (nLayerIndex == 0 && m_nLeftYAxes > 0) || nLayerIndex == (m_nLeftYAxes+m_nMiddleYAxes) )
		/// End QA81-14268 SUPPORT_LINK_AXIS_TITLE_COLOR_TO_PLOT
		{
			// This is the most inner Y axis, they should have been placed at the default position
			// so no need to move it.
			return;
		}

		double dAxisPos = 0;

		switch (nYAxisPosType)
		{
		case YAP_LEFT_MOST:
			dAxisPos = m_dAxesGap * nLayerIndex;
			break;
		case YAP_MIDDLE:
			dAxisPos = -50;
			break;
		case YAP_RIGHT_MOST:
			dAxisPos = m_dAxesGap * (nLayerIndex - m_nLeftYAxes - m_nMiddleYAxes);
			break;
		}
		ao.SetPosition(AXIS_POS_PERCENT, dAxisPos);
	}
	/// Kenny 09/09/2009 QA81-14268 SUPPORT_LINK_AXIS_TITLE_COLOR_TO_PLOT
	bool setTitleObjectColor(GraphLayer& gl, int nAxisType, int nColor)
	{
		bool bRet = false;
		AxisObject ao = get_axis_object(gl, nAxisType);
		GraphObject goTitle = ao.GetTitleObject();
		if ( goTitle )
		{
			bRet = set_go_color(goTitle, nColor);
			ASSERT(bRet);
		}
		return bRet;
	}
	/// End QA81-14268 SUPPORT_LINK_AXIS_TITLE_COLOR_TO_PLOT
	int getEnumPlotColor(const int nLayerIndex)
	{
		int nColor = mod(nLayerIndex, SYSCOLOR_TOTAL);
		return nColor;
	}
	int getEnumPlotSymbol(const int nLayerIndex)
	{
		int nSymbolIndex = mod(nLayerIndex, 11) * 11;
		return nSymbolIndex;
	}
};

static void _run_plotsmaxes_on_current_data(TreeNode& trGetN, PageBase& pgTemp)
{
	XYRange iy;
	xy_range_from_GetN_data_node(trGetN.iy, iy);

	PlotMultiAxesHelper plothelper;

	plothelper.m_XYRange			= iy;
	plothelper.m_gp					= pgTemp;
	plothelper.m_nLeftYAxes			= trGetN.ly.nVal;
	plothelper.m_nRightYAxes		= trGetN.ry.nVal;
	plothelper.m_nMiddleYAxes		= trGetN.my.nVal;
	plothelper.m_bLinkAxisColor		= trGetN.axiscolor.nVal;
	plothelper.m_bShowYAxisTitle	= trGetN.ytitle.nVal;
	plothelper.m_bShowTopXAxis		= trGetN.topx.nVal;

	plothelper.m_bIsPreviewing		= true;

	plothelper.SetPlotType(trGetN.plottype.nVal);
	plothelper.SetAxesGap(trGetN.gap.dVal);
	///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	plothelper.m_strPlotArrange = trGetN.number.strVal;
	if ( plottype_custom == trGetN.plottype.nVal )
		plothelper.m_strPlotTypes = trGetN.custom.strVal;
	///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	try
	{
		plothelper.Perform();
	}
	catch(int nErr)
	{
	}
}

static bool _get_xy_range_col_number(XYRange& xy, int& nNumXCol, int& nNumYCol)
{
	nNumXCol = 0;
	nNumYCol= 0;
	if ( xy.IsValid() )
	{
		int nNumData = xy.GetNumData(DRR_GET_DEPENDENT);
		for( int ii = 0; ii < nNumData; ii++ )
		{
			Column colX, colY;
			if( xy.GetXColumn(colX, ii) )
			{
				++nNumXCol;
			}
			if( xy.GetYColumn(colY, ii) )
			{
				++nNumYCol;
			}
		}
		return true;
	}
	return false;
}

static void _get_gui_number_axes_auto_status(TreeNode& trGetN, int& nAutoLY, int& nAutoRY)
{
	TreeNode& tnLeft = trGetN.ly;
	TreeNode& tnRight = trGetN.ry;
	nAutoLY = octree_get_auto_support( &tnLeft );
	nAutoRY = octree_get_auto_support( &tnRight );
}

static	int	_get_one_auto_axes_number(int nNumAxes, int nMidY, int nNonAutoY)
{
	int		nY = nNumAxes - nMidY - nNonAutoY;
	return nY < 0 ? 0 : nY;
}

static void _update_gui_number_axes_val(TreeNode& trGetN, int nNumXCol, int nNumYCol)
{
	int nLeftY = trGetN.ly.nVal;
	int nRightY = trGetN.ry.nVal;
	int nMidY = trGetN.my.nVal;

	int nAutoLY, nAutoRY;
	_get_gui_number_axes_auto_status(trGetN, nAutoLY, nAutoRY);
	
	int nAutoNum = (1 == nAutoLY) + (1 == nAutoRY);
	
	int nMaxYForAuto = nNumYCol - nMidY;
	if (0 == nNumYCol)
	{
		nLeftY = nRightY = nMidY = 0;
	}
	else if (2 == nAutoNum)
	{
		if (1 == nMaxYForAuto)
		{
			nLeftY = 1;
			nRightY = 0;
		}
		else
		{
			nLeftY = nMaxYForAuto / 2;
			nRightY = nMaxYForAuto - nLeftY;
		}
	}
	else if (1 == nAutoNum)
	{
		///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
		//if ( (nLeftY + nRightY) != nMaxYForAuto )
		//{
			//if (1 == nAutoLY)
			//{
				//if( nRightY > nMaxYForAuto )
					//nRightY = nMaxYForAuto;
				//else if (nRightY < 0)
					//nRightY = 0;
				//nLeftY = nMaxYForAuto - nRightY;
			//}
			//else
			//{
				//if( nLeftY > nMaxYForAuto )
					//nLeftY = nMaxYForAuto;
				//else if (nLeftY < 0)
					//nLeftY = 0;
				//nRightY = nMaxYForAuto - nLeftY;
			//}
		//}
		if ( 1 == nAutoRY )
			nRightY = _get_one_auto_axes_number(nNumYCol, nMidY, nLeftY);
		else
			nLeftY = _get_one_auto_axes_number(nNumYCol, nMidY, nRightY);
		///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	}

	trGetN.ly.nVal = nLeftY;
	trGetN.ry.nVal = nRightY;
	trGetN.my.nVal = nMidY;
}

static void _update_gui_number_axes_auto_status(TreeNode& trGetN, TreeNode& trNode)
{
	if( trGetN && trNode )
	{
		if ( trNode.tagName == "ly" || trNode.tagName == "ry")
		{
			bool bIsUpdatingLeftYNum = trNode.tagName == "ly";
			TreeNode& tnLeft = trGetN.ly;
			TreeNode& tnRight = trGetN.ry;
			int nAutoLY, nAutoRY;
			_get_gui_number_axes_auto_status(trGetN, nAutoLY, nAutoRY);
			int nAutoNum = (1 == nAutoLY) + (1 == nAutoRY);

			///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
			// We don't allow zero auto, at least one option should be checked to be auto.
			//if (0 == nAutoNum)
			//{
				//octree_set_auto_support( bIsUpdatingLeftYNum ? &tnRight : &tnLeft, 1);
			//}
			///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
		}
	}
}

///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
static	void	_update_custom_plottypes_show(TreeNode& trGetN)
{
	trGetN.custom.Show = trGetN.hint_custom.Show = plottype_custom == trGetN.plottype.nVal;
}

static	string&	_plots_in_layer_hint()
{
	static	string	l_str;
	return l_str;
}

static	void	_update_hint_plots_in_layer(TreeNode& trGetN)
{
	XYRange	xy;
	if ( xy_range_from_GetN_data_node(trGetN.iy, xy) )
	{
		DataPlotsArrangeHelper		clHelper(NULL, trGetN.number.strVal, NULL, NULL, NULL, trGetN.ly.nVal + trGetN.ry.nVal + trGetN.my.nVal);
		trGetN.hint_plots.SetAttribute(STR_LABEL_ATTRIB, _plots_in_layer_hint() + clHelper.MakeActualNumPlotsInLayerString(xy.GetNumData(DRR_GET_DEPENDENT)));
	}
}
///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS

static void _update_gui_axes_gap(TreeNode& trGetN, TreeNode& trNode)
{
	if( trGetN && trNode && trNode.tagName == "gap" )
	{
		int nAuto = octree_get_auto_support( &trNode );
		double dAxesGap;
		if ( 1 == nAuto )
		{
			bool bShowYAxisTitle = trGetN.ytitle.nVal;
			trNode.dVal = bShowYAxisTitle ? DEFAULT_AXIS_GAP_WITH_TITLE : DEFAULT_AXIS_GAP;
		}
		else
		{
			if (trNode.nVal < 0)
				trNode.nVal = 0;
		}
	}
}

///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
//static void _update_gui_axes_number_hint(TreeNode& trGetN, int nNumXCol, int nNumYCol)
static void _update_gui_axes_number_hint(TreeNode& trGetN, int nNumXCol, int nNumYCol, int nNumAxes)
///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
{
	string strHint;
	///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	//strHint.Format(_L("%d Y axes in total"), nNumYCol);
	strHint.Format(_L("%d Y axes and %d plots in total"), nNumAxes, nNumYCol);
	///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	trGetN.hint_axes.SetAttribute(STR_LABEL_ATTRIB, strHint);
	///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	/// use P:2 is enough
	//trGetN.hint_axes.Enable = ENABLE_READ_ONLY;
	//trGetN.hint_axes.SetAttribute(STR_ATTRIB_DISPFMT, DISPLAY_EDITOR_USE_LABEL);
	///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
}

///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
static	BOOL	_check_custom_plottype(TreeNode& trGetN, string* pstrErrMsg = NULL)
{
	if ( plottype_custom == trGetN.plottype.nVal )
	{
		if ( trGetN.custom.strVal.IsEmpty() )
		{
			if ( pstrErrMsg )
				pstrErrMsg->Format("%d:%s", XFERR_SPEICIFIED_STRING_EMPTY, _L("plot types"));
			return FALSE;
		}
	}
	
	return TRUE;
}
static	BOOL	_check_num_plots_in_layer(TreeNode& trGetN, string* pstrErrMsg = NULL)
{
	if ( trGetN.number.IsEmpty() )
	{
		if ( pstrErrMsg )
			pstrErrMsg->Format("%d:%s", XFERR_SPEICIFIED_STRING_EMPTY, _L("number of plots"));
		return FALSE;
	}
	
	return TRUE;
}

static	BOOL	_check_total_number_of_layers(TreeNode& trGetN, string* pstrErrMsg = NULL)
{
	if ( trGetN.ly.nVal + trGetN.ry.nVal + trGetN.my.nVal == 0 )
	{
		if ( pstrErrMsg )
			*pstrErrMsg = XFERR_NUMBER_OF_LAYERS_IS_ZERO;
		return FALSE;
	}
	
	return TRUE;
}

static	BOOL	_error_checkings(TreeNode& trGetN, string* pstrErrMsg = NULL)
{
	return ( _check_custom_plottype(trGetN, pstrErrMsg)
			&& _check_num_plots_in_layer(trGetN, pstrErrMsg)
			&& _check_total_number_of_layers(trGetN, pstrErrMsg)
			);
}
///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS

static bool _check_input(TreeNode& trGetN, string& strErrMsg)
{
	strErrMsg = "";

	// Check the input range
	int nSize;
	XYRange xy;
	get_error_for_input_xyrange(trGetN.iy, strErrMsg, nSize, xy, true);
	
	bool bRet = strErrMsg.IsEmpty();

	// Check the axes number options
	int nNumXCol = 0;
	int nNumYCol = 0;
	bRet &= _get_xy_range_col_number(xy, nNumXCol, nNumYCol);
	///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	//_update_gui_axes_number_hint(trGetN, nNumXCol, nNumYCol);
	DataPlotsArrangeHelper		clHelper(NULL, trGetN.number.strVal);
	int		nNumAxes = clHelper.GetTotalLayers(nNumYCol);
	///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	if ( bRet )
	{
		_update_gui_number_axes_val(trGetN, nNumXCol, nNumAxes);
		_update_gui_axes_gap(trGetN, trGetN.gap);
	}
	
	///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	_update_gui_axes_number_hint(trGetN, nNumXCol, nNumYCol, trGetN.ly.nVal + trGetN.ry.nVal + trGetN.my.nVal);
	_update_hint_plots_in_layer(trGetN);
	if ( bRet )
	{
		bRet = _error_checkings(trGetN, &strErrMsg);
	}
	///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	
	return bRet;
}


void plotmyaxes(const XYRange& iy, const int& plottype, const string& custom, const string& hint_custom, const int& ly, const int& ry, const int& my, const string& hint_axes, const string& number, const string& hint_number, const string& hint_plots, const int& axiscolor, const int& ytitle, const int& topx, const double& gap)
{
	PlotMultiAxesHelper plothelper;
	
	plothelper.m_XYRange			= iy;
	plothelper.m_nLeftYAxes			= ly;
	plothelper.m_nRightYAxes		= ry;
	plothelper.m_nMiddleYAxes		= my;
	plothelper.m_bLinkAxisColor		= axiscolor;
	plothelper.m_bShowYAxisTitle	= ytitle;
	plothelper.m_bShowTopXAxis		= topx;
	
	plothelper.SetPlotType(plottype);
	plothelper.SetAxesGap(gap);
	
	///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	plothelper.m_strPlotArrange = number;
	if ( plottype_custom == plottype )
		plothelper.m_strPlotTypes = custom;
	///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	
	plothelper.Perform();
}

//nEventID < 0 on event, like GETNE_ON_INIT, GETNE_ON_OK etc, >0 on change from control
//return 0 to do nothing, return PEVENT_GETN_RET_TO_CLOSE to close dialog
//dwEnables = bOKEnable, bBTN1Enable, bBTN2Enable, bBTN3Enable, , can set to false to disable OK/Apply buttons
//lpcszNodeName = the trGetN TreeNode tag name that generates the event, or is  if GETNE_ON_OK etc
//strAux = optional string for XF Wizard Bar, like DLG_NEED_CHANGE_GRID etc, it will be invalid if from internal node value change event
//strErrMsg = allowing messages to be shown
int plotmyaxes_event1(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	if( xf_is_extended_event(nEventID) )
		return plotmyaxes_event1_ex(trGetN, nRow, nEventID, dwEnables, lpcszNodeName, DynaCntrlContainer, strAux, strErrMsg);
	DECLARE_BUTTON_ENABLES
	return false;

}

int plotmyaxes_make_tree(TreeNode& tr, LPCSTR lpcszVarName)
{
	return false;
}
//nGetNDialog = 0,1,2, corresponding to the XF GetN dropdown, 0 = dialog is not used
//you can change nRet to be one of XFEVT_PROCEED (defualt), XFEVT_ABORT, XFEVT_PROCEED_NO_DLG or XFEVT_PROCEED_WITH_DLG(when nGetNDialog = 0)
//nGetNDialog = -1 when Ok on GetN dialog to allow handler to preocess <auto> etc variables
//dwCntrl is combination of bits
//LTXF_FROM_GUI_MENU, LTXF_FROM_GUI_PROMPT, LTXF_FROM_AUTO_UPDATE, LTXF_FROM_FUNCTION, LTXF_THEME_USED, LTXF_CHANGE_PARAM
void plotmyaxes_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{
	if ( is_xf_startup(nGetNDialog, dwCntrl) )
	{
		trGetN.hint_plots.GetAttribute(STR_LABEL_ATTRIB, _plots_in_layer_hint());
		if ( 0 == nGetNDialog )
		{
			string	strErrMsg;
			if ( !_check_input(trGetN, strErrMsg) )
			{
				nRet = XFEVT_PROCEED_WITH_DLG;
				return;
			}
		}
	}
}
//nEventID < 0 on event, like GETNE_ON_INIT, GETNE_ON_OK etc, >0 on change from control
//return 0 to do nothing, return PEVENT_GETN_RET_TO_CLOSE to close dialog
//dwEnables = bOKEnable, bBTN1Enable, bBTN2Enable, bBTN3Enable, , can set to false to disable OK/Apply buttons
//lpcszNodeName = the trGetN TreeNode tag name that generates the event, or is  if GETNE_ON_OK etc
//strAux = optional string for XF Wizard Bar, like DLG_NEED_CHANGE_GRID etc, it will be invalid if from internal node value change event
//strErrMsg = allowing messages to be shown
int plotmyaxes_event1_ex(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	return false;
}


// put your grid initialization here
// you may also initialize wksTemp here
static void GetNGraphPreview_OnInitGrid(TreeNode& trGetN, PageBase& pgTemp, Datasheet& wksTemp,
PictureControl& pict, // optional tab for showing an image or EMF, as requested in OnDialogSetup 
DWORD& dwUpdateGraph,  // default to 0, UPDATE_GRAPH_CHANGED to cause graphs to be updated, UPDATE_GRAPH_INVALID to disable preview button
DWORD& dwUpdateGrid, // default to 0, set to UPDATE_GRID_SIMPLE to update the DynaControl grid, UPDATE_GRID_RESET to reset DynaControl grid
DWORD& dwEnables,// bOKEnable, bBTN1Enable, bBTN2Enable and bBTN3Enable :default to true, can set to false to disable buttons
string& strErrMsg) // optional err msg to be shown on dialog
{
	DECLARE_BUTTON_ENABLES
	DECLARE_UPDATE_GRAPH
	DECLARE_UPDATE_GRID
	
	///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	_update_custom_plottypes_show(trGetN);
	///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	
	bOKEnable = _check_input(trGetN, strErrMsg);

	s_strErrMsg = strErrMsg;
	bUpdateGraph = bOKEnable;
	bUpdateGrid = true;

}

///////////////////////////////////////////////////////

// pgTemp is the graph for preview or control
// wksTemp is the the temp wks created to hold data to plot into pgTemp
static void GetNGraphPreview_OnInitGraph(TreeNode& trGetN, PageBase& pgTemp, Datasheet& wksTemp,
PictureControl& pict, // optional tab for showing an image or EMF, as requested in OnDialogSetup  
DWORD& dwUpdateGrid, // default to 0, set to UPDATE_GRID_SIMPLE to update the DynaControl grid, UPDATE_GRID_RESET to reset DynaControl grid
DWORD& dwEnables,// bOKEnable, bBTN1Enable, bBTN2Enable and bBTN3Enable :default to true, can set to false to disable buttons
string& strErrMsg) // optional err msg to be shown on dialog
{
	DECLARE_BUTTON_ENABLES
	DECLARE_UPDATE_GRID

	strErrMsg = s_strErrMsg;
	///Jasmine 08/13/2010 ORG-172-S2 CENTRALIZE_NOCLICK_BIT_FOR_PREVIEW_IN_OC_TOOL
	//GraphPage gp = pgTemp;
	//page_load(gp, "NonePreview", PREVIEW_GRAPH_NOCLICKS);
	//load_text_for_nonepreview_graphpage(gp, TEMPLATE_NONE_PREVIEW_CONTENT, TEMPLATE_NONE_PREVIEW_HINTS);
	xf_load_nopreview(pgTemp);
	///End CENTRALIZE_NOCLICK_BIT_FOR_PREVIEW_IN_OC_TOOL

}

///////////////////////////////////////////////////////

static void GetNGraphPreview_OnUpdateGraph(TreeNode& trGetN, PageBase& pgTemp, Datasheet& wksTemp, PictureControl& pict)
{

	_run_plotsmaxes_on_current_data(trGetN, pgTemp);

}

///////////////////////////////////////////////////////

static void GetNGraphPreview_OnChange(TreeNode& trGetN, TreeNode& trNode, PageBase& pgTemp, Datasheet& wksTemp,
PictureControl& pict, // optional tab for showing an image or EMF that might be present, as requested in OnDialogSetup  
bool bChangedByGraph, // called when graph has been modified by user, like dragging a vertical line
DWORD& dwUpdateGraph,  // default to 0, UPDATE_GRAPH_CHANGED to cause graphs to be updated, UPDATE_GRAPH_INVALID to disable preview button
DWORD& dwUpdateGrid,  // default to 0, set to UPDATE_GRID_SIMPLE to update the DynaControl grid, UPDATE_GRID_RESET to reset DynaControl grid
DWORD& dwEnables,	// bOKEnable, bBTN1Enable, bBTN2Enable and bBTN3Enable :default to true, can set to false to disable buttons
string& strErrMsg) // optional err msg to be shown on dialog
{
	DECLARE_BUTTON_ENABLES
	DECLARE_UPDATE_GRAPH
	DECLARE_UPDATE_GRID

	///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	if ( !trNode )
	{
		_update_gui_number_axes_auto_status(trGetN, trNode);
		_update_custom_plottypes_show(trGetN);
	}
	else
	{
		_update_gui_number_axes_auto_status(trGetN, trNode);
		if ( trNode.tagName.Compare("plottype") == 0 )
		{
			_update_custom_plottypes_show(trGetN);
		}
	}
	///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	
	bOKEnable = _check_input(trGetN, strErrMsg);
	bUpdateGrid = true;
	
	///------ Folger 11/12/09 QA81-14636 PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS
	//if (trNode)
	//{
		//_update_gui_number_axes_auto_status(trGetN, trNode);
	//}
	///------ End PLOT_STACK_AND_MYAXES_REARRANGE_DATAPLOTS_IN_MULTIPLE_LAYERS

	if(!bOKEnable)
		dwUpdateGraph = UPDATE_GRAPH_INVALID;
	else
		bUpdateGraph = bOKEnable;

}

// called from OnInitDialog to allow options on setting up of dialog
// dwOptions = 0 (default) will create a line graph with data in a temp worksheet
// dwOptions = XFGSETUP_IMAGEGRAPH_CNTRL to create an image graph with data in a temp matrix book
// dwOptions = XFGSETUP_MATRIX_CNTRL to have a MatrixControl with data in a temp matrix book
// dwOptions = XFGSETUP_2_TABS | XFGSETUP_EMF_1ST to have a PictureControl on 1st tab and a GraphControl on 2nd tab
// vsTabTitles is empty on entering the event, but you can put tab title strings in this array.
static void GetNGraphPreview_OnDialogSetup(TreeNode& trGetN, WndContainer& theDlg, DWORD& dwOptions, vector<string>& vsTabTitles)
{

}

// called when dialog is being destroyed, either as OK button or as Cancel button being pressed
static void GetNGraphPreview_OnDestroy(TreeNode& trGetN, PageBase& pgTemp, Datasheet& wksTemp, PictureControl& pict)
{

}
//------------
//
static void GetNGraphPreview_OnCustomButton(TreeNode& trGetN, int nButton, PageBase& pgTemp, Datasheet& wksTemp,
PictureControl& pict, // optional tab for showing an image or EMF that might be present, as requested in OnDialogSetup  
bool bChangedByGraph, // called when graph has been modified by user, like dragging a vertical line
DWORD& dwUpdateGraph,  // default to 0, UPDATE_GRAPH_CHANGED to cause graphs to be updated, UPDATE_GRAPH_INVALID to disable preview button
DWORD& dwUpdateGrid,  // default to 0, set to UPDATE_GRID_SIMPLE to update the DynaControl grid, UPDATE_GRID_RESET to reset DynaControl grid
DWORD& dwEnables,	// bOKEnable, bBTN1Enable, bBTN2Enable and bBTN3Enable :default to true, can set to false to disable buttons
string& strErrMsg) // optional err msg to be shown on dialog
{
	DECLARE_BUTTON_ENABLES
	DECLARE_UPDATE_GRAPH
	DECLARE_UPDATE_GRID

}
//


///////////////////////////////////////////////////////
	
int GetNGraphBox_plotmyaxes(TreeNode& trGetN, int nRow, int nCol, TreeNode& trNode, DWORD dwCntrl, int nType, WndContainer& theDlg, PageBase& pgTemp, Datasheet& wksTemp, PictureControl& pict)
{
	return xf_GetNPreviewDlg_event_handler(trGetN, nRow, nCol, trNode, dwCntrl, nType, theDlg, pgTemp, wksTemp, pict,
	GetNGraphPreview_OnInitGrid, GetNGraphPreview_OnInitGraph,
	GetNGraphPreview_OnChange, GetNGraphPreview_OnUpdateGraph, GetNGraphPreview_OnDialogSetup, GetNGraphPreview_OnDestroy,
	GetNGraphPreview_OnCustomButton);	
}


//This file is the template for GetNEvent4

///////////////////////////////////////////////////////

