#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
//put additional include files here
#include	<..\Originlab\graph_utils.h>
#include	<..\Originlab\grobj_utils.h>
#include	<..\Originlab\GraphObjTools.h>
/*------------------------------------------------------------------------------*
 * Modification Log:															*
 *	Sophy 4/13/2010 SUPPORT_CHANGE_DERIV_ORDER_FROM_CONTEXT_MENU				*
 *	Sophy 5/10/2010 QA82-15278 REPAIR_PREVIEW_GRAPHS_WHEN_DELETED_BY_USER		*
 *	Sophy 5/10/2010 PREVIEW_GRAPH_X_FROM_TO_FOLLOW_MAIN_RECTANGLE				*
 *	Sophy 5/10/2010 QA82-15278 BETTER_ALIGNMENT_OF_Y_AXIS_TITLE_FOR_MULTI_LAYER	*
 *	Sophy 5/11/2010 QA82-15278 SHOW_ORIGINAL_GRAPH_AS_NORMAL_WINDOW_STATE		*
 *	Sophy 5/11/2010 QA82-15278 BETTER_MAJOR_MINOR_TICKS_FOR_X_AXIS				*
 *	Sophy 5/11/2010 QA82-15278 CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
 *	Sophy 6/24/2010 ORG-392-P1 RESET_SOURCE_GRAPH_ON_DATA_CHANGE				*
 *------------------------------------------------------------------------------*/
//put your own support static functions here
#include <o8dlg.h>

#define	XF_NAME		"addtool_curve_deriv"
#define	TOOLNAME	"Gadget Differentiate"
#define	TOOL_PREFERENCES_TITLE	_L("Differentiate")

enum {
	ITD_OBJ_FIRST = GOT_OBJ_TOTAL,
	ITD_OBJ_DIFFERENTIATE,
};

enum {
	DISP_RATIO	= 0,
	DISP_VALUE	= 1,
};

//custom cmd IDs
enum {
	GOT_CLOSE_ROI	= GOT_CMD_CUSTOM | 0x0010,
	GOT_KEEP_GRAPH	= GOT_CMD_CUSTOM | 0x0020,
};
#define	MIN_POINTS_SMOOTH	3
#define	MIN_POINTS_WINDOW	2
///Sophy 4/28/2010 QA82-15278 DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH
#define	PREVIEW_SRC_INDEX	0
#define	PREVIEW_DERIV_INDEX	1
#define	PREVIEW_1ST_DERIV	"PREVIEW_1ST_DERIV"
#define	PREVIEW_2ND_DERIV	"PREVIEW_2ND_DERIV"
#define	PREVIEWSETTINGS		"PreviewSettings"
enum PREVIEWDATAINDEX
{
	PREVIEW_X_INDEX = 0,
	PREVIEW_NTH_INDEX,
	PREVIEW_1ST_INDEX,
	PREVIEW_2ND_INDEX,
	PREVIEW_TOTAL_COLS //new items should insert before this. 
};
#define	PREVIEW_INIT_COLS	(PREVIEW_NTH_INDEX + 1)	///Sophy 6/10/2010 ORG-48-P13 DEFAULT_SHOW_LONGNAME_IN_RESULT_SHEET
///end DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH

static	bool	_check_reportwks_event(TreeNode& tr, int nRow, int nType, Dialog& Dlg)
{
	TreeNode trGUI = tr.trGUI;
	if ( !trGUI )
		trGUI = tr;
	trGUI.output.wksName.Show = trGUI.output.report.nVal == 1;
	return true;
}
static	bool	_check_smooth_event(TreeNode& tr, int nRow, int nType, Dialog& Dlg)
{
	TreeNode trGUI = tr.trGUI;
	if ( !trGUI )
		trGUI = tr;
	int nSmooth = trGUI.smoothOptions.smooth.nVal;
	trGUI.smoothOptions.polyOrder.Enable = trGUI.smoothOptions.npts.Enable = (nSmooth == 1) ? true : false;
	return true;
}

static	bool	_deriv_order_event(TreeNode& tr, int nRow, int nType, Dialog& Dlg)
{
	TreeNode trGUI = tr.trGUI;
	if ( !trGUI )
		trGUI = tr;
	int nOrder = trGUI.DerivOrder.nVal;
	if ( nOrder <= 0 )
		trGUI.DerivOrder.nVal = 1; //at least 1
	return true;
}
///Sophy 4/30/2010 QA82-15278 DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH
static	bool	_copy_column_settings(Column& colSrc, Column& colDst)
{
	if ( !colSrc || !colDst )
		return false;
	int		nType = colSrc.GetType();			colDst.SetType(nType);
	///Sophy 6/8/2010 ORG-48-P12 COPY_X_LONGNAME_FORM_SRC_TO_DERIV
	string	strLongName = colSrc.GetLongName();	colDst.SetLongName(strLongName);
	///end COPY_X_LONGNAME_FORM_SRC_TO_DERIV
	string	strUnits = colSrc.GetUnits();		colDst.SetUnits(strUnits);
	int		nFormat = colSrc.GetFormat();		colDst.SetFormat(nFormat);
	int		nSubFormat = colSrc.GetSubFormat();	colDst.SetSubFormat(nSubFormat);
	int		nDataType = colSrc.GetInternalDataType();	colDst.SetInternalDataType(nDataType);
	return true;
}
///end DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH

///Sophy 6/10/2010 ORG-48-P14 BETTER_EVENT_HANDLE_FOR_PREFERENCE_IN_ROI_TOOLS
int deriv_error_check(TreeNode& tr)
{
	TreeNode trGUI = tr.trGUI;
	if ( !trGUI )
		trGUI = tr;
	int nErr = CER_NO_ERROR;
	int nOrder = trGUI.DerivOrder.nVal;
	int nSmooth = trGUI.smoothOptions.smooth.nVal;
	int nPolyOrder = trGUI.smoothOptions.polyOrder.nVal + 1;
	int nPts = trGUI.smoothOptions.npts.nVal;
	if ( nSmooth == 1 )
	{
		if ( nOrder > nPolyOrder )
			return CER_POLYDEG_GREATER_THAN_DERVORDER;
		if ( nPts < MIN_POINTS_WINDOW )
			return XFERR_INVALID_WINDOW_POINTS;
	}
	return nErr;
}
int	getn_handle_deriv(TreeNode& tr, int nRow, int nEvent, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& getNContainer, string& strAux, string& strErrMsg)
{
	DECLARE_BUTTON_ENABLES
	int nErr = deriv_error_check(tr);
	if ( nErr != CER_NO_ERROR )
	{
		bOKEnable = false;
		strErrMsg = nErr; 
	}
	return 0;
}
///end BETTER_EVENT_HANDLE_FOR_PREFERENCE_IN_ROI_TOOLS

static	int*	_get_xf_treenode_evt_handle_state()
{
	static int nUpdated = 0;
	return &nUpdated;
}

#define	STR_DERIV_GRAPHPAGE	"gDerivative"
#define	STR_DERIV_WORKBOOK	"wDerivative"

#define	CHECK_NEW_GRAPHPAGE	0x0001
#define	CHECK_NEW_WORKSHEET	0x0002

string	g_strLastResultName;	///Sophy 5/19/2010 QA82-15278 NEED_TO_DESTROY_PREVIEW_PAGES_WHEN_SRC_GRAPH_DESTROY

class DifferentiateTool : public GraphObjCurveTool
{
public:
	DifferentiateTool(LPCSTR lpcszMainObjName = NULL) : GraphObjCurveTool(lpcszMainObjName)
	{
		///Sophy 4/28/2010 QA82-15278 DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH
		m_strPreviewDataWksName = STR_DERIV_WORKBOOK;
		m_strPreviewDataWksLongName = "Differentiate Tool Data";
		m_strGraphTemplate = "DerivPreview";
		m_strPreviewGraphName = STR_DERIV_GRAPHPAGE;
		m_strPreviewGraphLongName = "Differentiate Tool Preview";
		///end DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH
		m_vnObjIDs.Add(ITD_OBJ_DIFFERENTIATE);
		m_vsObjNames.Add("Differentiate");
	}
	//virtual
	string	GetSignature()
	{
		return "xf_" XF_NAME;
	}

protected:
	
	//virtual
	BOOL	OnMove()
	{
		if ( !OnPreMove() )
			return false;
		///Sophy 4/28/2010 QA82-15278 DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH
		bool bShowDiffLine = updateDiffData();
		///Sophy 5/10/2010 QA82-15278 REPAIR_PREVIEW_GRAPHS_WHEN_DELETED_BY_USER
		//rearrangePreviewGraphs(); //preview may be shared by multiple tools.
		checkPreparePreviewGraphs();
		///end REPAIR_PREVIEW_GRAPHS_WHEN_DELETED_BY_USER
		updatePreviewGraphs();
		///end DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH
	
		return GraphObjCurveTool::OnMove();
	}

	
	//virtual
	int		CreateAttachments()
	{
		int nRet = GraphObjCurveTool::CreateAttachments();
		if ( 0 != nRet )
			return nRet;
		
		nRet = CreateAttachment(m_goDiffCurve, ITD_OBJ_DIFFERENTIATE, CTP_FREE_1, GROBJ_TN_POLYLINE);
		if ( 0 != nRet )
			return nRet;
		set_go_color(m_goDiffCurve, SYSCOLOR_BLUE);
		set_go_selectable(m_goDiffCurve, false);
		bool bShow1st = false, bShow2nd = false;
		previewSettingsAccess(bShow1st, bShow2nd, false); //init preview graph as invisible.
		updateDiffColor();
		
		return nRet;
	}
	
	//virtual
	int		GetAttachments()
	{
		int nRet = GraphObjCurveTool::GetAttachments();
		if ( 0 != nRet )
			return nRet;
		nRet = GetAttachment(m_goDiffCurve, ITD_OBJ_DIFFERENTIATE);
		if ( 0 != nRet )
			return nRet;
		
		return nRet;
	}

	///Sophy 4/13/2010 SUPPORT_CHANGE_DERIV_ORDER_FROM_CONTEXT_MENU
	//virtual
	bool	UpdateContextMenu(Menu& pm, uint& nPosition)
	{
		Tree trGUI;
		if ( !GetGUITree(trGUI) )
			return false;
		
		///Sophy 5/11/2010 QA82-15278 CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
#define	MF_STRING		0x00000000L		//since XF should not include control relative headers, I have to define it here.
#define MF_GRAYED		0x00000001L
#define	MF_CHECKED		0x00000008L
#define MF_SEPARATOR	0x00000800L
		int nInsertStartPos = 2;
		///Sophy 9/21/2010 ORG-392 HIDE_DELETE_ROI_AND_LEAVE_DERIV_GRAPH_CONTEXT_MENU
		//string strCloseROI = _L("Delete ROI and Leave Derivative Graph"); //maybe need a better label.
		//InsertMenuItem(pm, nInsertStartPos++, strCloseROI, GOT_CLOSE_ROI, MF_STRING) ; nPosition++;  //after New Output and Go to Report item
		///end HIDE_DELETE_ROI_AND_LEAVE_DERIV_GRAPH_CONTEXT_MENU
		DWORD dwFlag0 = MF_STRING;
		bool bLeaveGraph = false;
		destroySettingsAccess(bLeaveGraph, true);
		if ( bLeaveGraph )
			dwFlag0 |= MF_CHECKED;
		InsertMenuItem(pm, nInsertStartPos++, _L("Keep Derivative Graph when Close ROI"), GOT_KEEP_GRAPH, dwFlag0); nPosition++;
		
		RemoveMenu(pm, nPosition, 0); //remove New Output
		RemoveMenu(pm, nPosition, 0); //remove Go to Report
		///end CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
		InsertMenuItem(pm, nPosition , NULL, 0, MF_SEPARATOR); nPosition++; //insert a separator
		DWORD dwFlag1 = MF_STRING,  dwFlag2 = MF_STRING;
		
		bool bShow1st, bShow2nd;
		previewSettingsAccess(bShow1st, bShow2nd, true); //get
		if ( bShow1st )
			dwFlag1 |= MF_CHECKED;
		if ( bShow2nd )
			dwFlag2 |= MF_CHECKED;
		
		if ( 1 == trGUI.DerivOrder.nVal )
			dwFlag1 |= MF_GRAYED;
		else if ( 2 == trGUI.DerivOrder.nVal )
			dwFlag2 |= MF_GRAYED;
		
		string strLabel;
		strLabel = _L("Show 1st Derivative");
		InsertMenuItem(pm, nPosition, strLabel, GOT_CMD_CUSTOM | 1, dwFlag1); nPosition++;
		
		strLabel = _L("Show 2nd Derivative");
		InsertMenuItem(pm, nPosition, strLabel, GOT_CMD_CUSTOM | 2, dwFlag2); nPosition++;
		return true;
	}
	
	//virtual
	bool	OnCmdCustom(int nCmdID)
	{
		switch ( nCmdID )
		{
		case GOT_CLOSE_ROI:
			clonePreviewAndCloseROI();
			break;
			
		case GOT_KEEP_GRAPH:
			bool bLeaveGraph;
			destroySettingsAccess(bLeaveGraph, true);
			bLeaveGraph = !bLeaveGraph;
			destroySettingsAccess(bLeaveGraph, false);
			break;
			
		default:
			int nOrder = nCmdID & GOT_BTN_FLEX_MASK;
			if ( nOrder > 0 )
			{
				bool bShow1st, bShow2nd;
				previewSettingsAccess(bShow1st, bShow2nd, true); //get
				if ( 1 == nOrder )
					bShow1st = !bShow1st;
				else if ( 2 == nOrder )
					bShow2nd = !bShow2nd;
				previewSettingsAccess(bShow1st, bShow2nd, false); //set
				///Sophy 5/10/2010 QA82-15278 REPAIR_PREVIEW_GRAPHS_WHEN_DELETED_BY_USER
				//rearrangePreviewGraphs();
				checkPreparePreviewGraphs();
				///end REPAIR_PREVIEW_GRAPHS_WHEN_DELETED_BY_USER
			}
		}
		return true;
	}
	///end SUPPORT_CHANGE_DERIV_ORDER_FROM_CONTEXT_MENU
	
	BOOL	OnDestroy()
	{
		///Sophy 5/12/2010 QA82-15278 CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
		if ( GraphObjTool::OnDestroy() ) //skip GraphObjCurveTool::OnDestroy, since destroy preview pages will/need to be done here.
		{
			if ( QUERY_ROI_TOOL_OPTION(ROI_OPTION_IGNORE_DESTROY) ) //check whether need to keep preview/result pages.
			{
				roi_tool_option_access(ROI_OPTION_IGNORE_DESTROY, ROI_OP_REMOVE); //ignore once.
				return TRUE;
			}
			
			checkShowResult(); //attach pages.
			bool bLeaveGraph = false;
			destroySettingsAccess(bLeaveGraph, true);
			if ( !bLeaveGraph && m_gpPreview.IsValid() )
				m_gpPreview.Destroy();
			if ( !bLeaveGraph && m_wksPreview.IsValid() )
				m_wksPreview.GetPage().Destroy();
		}
		///end CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
		return TRUE;
	}
protected:
	//virtual
	string	GetXFName(){ return XF_NAME; }
	//virtual
	string	GetToolName() { return TOOLNAME; }
	//virtual
	string	GetPreferenceTitle(){ return TOOL_PREFERENCES_TITLE; }
	//virtual
	int*	GetXFEventHandlersState() { return _get_xf_treenode_evt_handle_state(); }

	//virtual
	TreeNode	GetFillColorNode(const TreeNode& trGUI)
	{
		TreeNode trColor;
		if ( trGUI )
			trColor = trGUI.roi.rectcolor;
		return trColor;
	}
	//virtual
	TreeNode	GetToolNameNode(const TreeNode& trGUI)
	{
		TreeNode trToolName;
		if ( trGUI )
			trToolName = trGUI.roi.toolname;
		return trToolName;
	}
	
	//virtual
	BOOL		GetReportWorksheet(Worksheet& wksReport)
	{
		return wksReport.IsValid();
	}
	//virtual
	bool	UpdateROIGUI()
	{
		updateDiffColor();
		///Sophy 5/10/2010 QA82-15278 REPAIR_PREVIEW_GRAPHS_WHEN_DELETED_BY_USER
		//rearrangePreviewGraphs();
		checkPreparePreviewGraphs();
		///end REPAIR_PREVIEW_GRAPHS_WHEN_DELETED_BY_USER
		return GraphObjCurveTool::UpdateROIGUI();
	}
	
	///Sophy 6/24/2010 ORG-392-P1 RESET_SOURCE_GRAPH_ON_DATA_CHANGE
	//virtual
	BOOL	OnPlotChanged()
	{
		attachPreviewGraphs();
		initSrcPreview();
		return TRUE;
	}
	///end RESET_SOURCE_GRAPH_ON_DATA_CHANGE

private:
	
	///Sophy 6/24/2010 ORG-392-P1 RESET_SOURCE_GRAPH_ON_DATA_CHANGE
	void	initSrcPreview()
	{
		///Sophy 9/2/2010 ORG-392-P3 PROPER_ADD_SRC_PLOT_ON_PREVIEW_GRAPH
		//GetData(); //attach m_dp
		if ( !GetData() && m_gl )
		{
			m_dp = m_gl.DataPlots(-1); //get active plot as input
		}
		///end PROPER_ADD_SRC_PLOT_ON_PREVIEW_GRAPH
		//remove plots;
		int nPlots = m_glSrcPreview.DataPlots.Count();
		while(nPlots--)
			m_glSrcPreview.RemovePlot(nPlots);
		int nSrcPlot = m_glSrcPreview.AddPlot(m_dp);
		DataPlot dpSrc = m_glSrcPreview.DataPlots(nSrcPlot);
		set_dataplot_selectable(dpSrc, false);
	}
	///end RESET_SOURCE_GRAPH_ON_DATA_CHANGE
	void	updateDiffColor()
	{
		Tree trGUI;
		if ( GetGUITree(trGUI) )
		{
			int nColor = trGUI.roi.derivLineColor.nVal;
			///Sophy 4/28/2010 QA82-15278 DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH
			if ( m_glDerivPreview )
			{
				ASSERT(m_glDerivPreview.DataPlots.Count() <= 1);	//assume there are only one plot
				if ( m_glDerivPreview.DataPlots.Count() < 1 )
					initnthDerivPreview();
				DataPlot dpDeriv = m_glDerivPreview.DataPlots(0);
				dpDeriv.SetColor(nColor);
			}
			if ( m_gl1stDerivPreview )
			{
				ASSERT(m_gl1stDerivPreview.DataPlots.Count() <= 1);	//assume there are only one plot
				if ( m_gl1stDerivPreview.DataPlots.Count() < 1 ) //when preview worksheet deleted by user, this might happen
					init1stDerivPreview();
				DataPlot dpDeriv = m_gl1stDerivPreview.DataPlots(0);
				dpDeriv.SetColor(nColor + 1);
			}
			if ( m_gl2ndDerivPreview )
			{
				ASSERT(m_gl2ndDerivPreview.DataPlots.Count() <= 1);	//assume there are only one plot
				if ( m_gl2ndDerivPreview.DataPlots.Count() < 1 ) //when preview worksheet deleted by user, this might happen
					init2ndDerivPreview();
				DataPlot dpDeriv = m_gl2ndDerivPreview.DataPlots(0);
				dpDeriv.SetColor(nColor + 2);
			}
		}
	}
	
	bool	updateDiffData()
	{
		Tree trGUI;
		if ( getData() && GetGUITree(trGUI) )
		{
			int nOrder = trGUI.DerivOrder.nVal;
			int nSmooth = trGUI.smoothOptions.smooth.nVal;
			int nPolyOrder = trGUI.smoothOptions.polyOrder.nVal + 1;
			int nPts = trGUI.smoothOptions.npts.nVal;
			
			m_vDerivY = m_vY;
			m_v1stDerivY = m_v2ndDerivY = m_vY;
			if ( nSmooth )
			{
				if ( m_vY.GetSize() < MIN_POINTS_SMOOTH )
				{
					error_report("Too few points for smooting.", true);
					return false;
				}
				if ( nOrder > nPolyOrder )
				{
					error_report("Derivative order should not be greater than polynomial order.", true);
					return false;
				}
				if ( nPts < MIN_POINTS_WINDOW || nPts >= m_vY.GetSize() )
				{
					error_report("Invalid number of points in window.", true);
					return false;
				}
				ocmath_savitsky_golay_derivative(m_vX, m_vY, m_vDerivY, m_vX.GetSize(), nPts/2, -1, nPolyOrder, nOrder);
				ocmath_savitsky_golay_derivative(m_vX, m_vY, m_v1stDerivY, m_vX.GetSize(), nPts/2, -1, nPolyOrder, 1);
				ocmath_savitsky_golay_derivative(m_vX, m_vY, m_v2ndDerivY, m_vX.GetSize(), nPts/2, -1, nPolyOrder, 2);
			}
			else
			{
				for ( int iOrder = 1; iOrder <= nOrder; iOrder++ )
				{
					if ( OE_NOERROR != ocmath_derivative(m_vX, m_vDerivY, m_vX.GetSize()) )
					{
						return error_report("Differentiate fail.");
					}
					if ( 1 == iOrder )
						m_v1stDerivY = m_vDerivY;
					else if ( 2 == iOrder )
						m_v2ndDerivY = m_vDerivY;
				}
				if ( nOrder < 2 )
				{
					m_v2ndDerivY = m_v1stDerivY;
					if ( OE_NOERROR != ocmath_derivative(m_vX, m_v2ndDerivY, m_vX.GetSize()) )
					{
						return error_report("Differentiate fail.");
					}
				}
				
			}
			UpdateTopLabel("No Message", false); //hide top label
			return true;
		}
		return false;
	}
	
	bool	updateDiffLine(bool bShow = true)
	{
		if ( m_goDiffCurve )
		{
			int nSize = ocmath_1d_remove_missingvalue(m_vX, m_vDerivY, m_vX.GetSize(), NANUM);
			m_vDerivY.SetSize(nSize);
			m_goDiffCurve.SetPoints(m_vX, m_vDerivY);
			m_goDiffCurve.Show(bShow);
			m_gp.Refresh();
		}
		return true;
	}
	
	///Sophy 4/28/2010 QA82-15278 DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH
	bool	getData()
	{
		if ( !GetData() )
			return false;
		return true;
	}
	int		attachPreviewGraphs()
	{
		///Sophy 5/12/2010 QA82-15278 CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
		int nRet = checkShowResult();
		///end CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
		if ( nRet >= 0 )
		{
			///Sophy 5/12/2010 QA82-15278 CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
			GraphPage gpPreview = m_gpPreview;
			///end CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
			if ( !gpPreview.IsValid() )
				return -1;
			m_glSrcPreview = gpPreview.Layers(PREVIEW_SRC_INDEX);
			m_glDerivPreview = gpPreview.Layers(PREVIEW_DERIV_INDEX);
			m_gl1stDerivPreview = gpPreview.Layers(PREVIEW_1ST_DERIV);
			m_gl2ndDerivPreview = gpPreview.Layers(PREVIEW_2ND_DERIV);
			if (  !(m_glSrcPreview.IsValid() && m_glDerivPreview.IsValid()) )
				return -1;
			
			gpPreview.GetWindow().ShowWindow(SW_RESTORE); ///Sophy 5/11/2010 QA82-15278 SHOW_ORIGINAL_GRAPH_AS_NORMAL_WINDOW_STATE
		}
		return nRet;
	}
	
	///Sophy 5/12/2010 QA82-15278 CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
	string	getResultName()
	{
		string strLongName;
		///Sophy 6/8/2010 ORG-48-P7 RUNTIME_WHEN_NEW_PROJECT_WITH_SRC_PAGE_INACTIVE
		//if ( m_gp.IsValid() )
		if ( m_gp.IsValid() && m_gl.IsValid() && m_go.IsValid() )
		///end RUNTIME_WHEN_NEW_PROJECT_WITH_SRC_PAGE_INACTIVE			
		{
			strLongName.Format("%s_%s_%s", m_gp.GetName(), m_gl.GetName(), m_go.GetName());
			g_strLastResultName = strLongName;
		}
		else
		{
			strLongName = g_strLastResultName;
		}
		return strLongName;
	}
	
	int		checkShowResult()
	{
		string strLongName = getResultName();
		Folder fld = Project.ActiveFolder();
		foreach(PageBase pb in fld.Pages)
		{
			Page pg(pb);
			if ( pg.IsValid() ) ///Sophy 6/4/2010 ORG-48-P4 RUNTIME_ERROR_WHEN_ACCESS_NOTES_PAGE
			{
				string strLN = pg.GetLongName();
				if ( strLN.CompareNoCase(strLongName) == 0 )
				{
					if ( pg.GetType() == EXIST_WKS )
						m_wksPreview = pg.Layers(0);
					else if ( pg.GetType() == EXIST_PLOT )
						m_gpPreview = pg;
				}
			}
		}
		int nRet = 0;
		if ( !m_wksPreview.IsValid() )
		{
			WorksheetPage wp;
			wp.Create(NULL, CREATE_HIDDEN);
			wp.SetName(m_strPreviewDataWksName, OCD_ENUM_NEXT);
			wp.SetLongName(strLongName);
			m_wksPreview = wp.Layers(0);
			wks_set_show_labels(m_wksPreview, RCLT_LONG_NAME);	///Sophy 6/10/2010 ORG-48-P13 DEFAULT_SHOW_LONGNAME_IN_RESULT_SHEET

			nRet |= CHECK_NEW_WORKSHEET;
		}
		if ( !m_gpPreview.IsValid() )
		{
			m_gpPreview.Create(m_strGraphTemplate);
	   		m_gpPreview.SetName(m_strPreviewGraphName, OCD_ENUM_NEXT);
	   		m_gpPreview.SetLongName(strLongName);
	   		
	   		nRet |= CHECK_NEW_GRAPHPAGE;
		}
		return nRet; //nRet for new create
	}
	///end CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
	///Sophy 5/10/2010 QA82-15278 REPAIR_PREVIEW_GRAPHS_WHEN_DELETED_BY_USER
	bool	checkPreparePreviewGraphs()
	{
		int nRet;
		if ( (nRet = attachPreviewGraphs()) > 0 )
		{
			///Sophy 5/12/2010 QA82-15278 CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
			//m_wks.SetSize(-1, PREVIEW_TOTAL_COLS); 
			//updateColumnSettings(); //update preview worksheet properties
			m_wks = m_wksPreview;

			if ( nRet & CHECK_NEW_WORKSHEET )
			{
				///Sophy 6/10/2010 ORG-48-P13 DEFAULT_SHOW_LONGNAME_IN_RESULT_SHEET
				//m_wks.SetSize(-1, PREVIEW_TOTAL_COLS);
				m_wks.SetSize(-1, PREVIEW_INIT_COLS);
				///end DEFAULT_SHOW_LONGNAME_IN_RESULT_SHEET
				updateColumnSettings(); //update preview worksheet properties
			}
			///end CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
			if ( nRet & CHECK_NEW_GRAPHPAGE )
			{
				updateGraphSettings(true, false, false);

				///Sophy 6/24/2010 ORG-392-P1 RESET_SOURCE_GRAPH_ON_DATA_CHANGE
				//GetData(); //attach m_dp
				//int nSrcPlot = m_glSrcPreview.AddPlot(m_dp);
				//DataPlot dpSrc = m_glSrcPreview.DataPlots(nSrcPlot);
				//set_dataplot_selectable(dpSrc, false);
				initSrcPreview();
				///end RESET_SOURCE_GRAPH_ON_DATA_CHANGE
				
				//differentiate preview layer
				initnthDerivPreview();
				updatePlotSettings();
			}
			m_gl.CheckShowActivate();	///Sophy 5/11/2010 QA82-15278 SHOW_ORIGINAL_GRAPH_AS_NORMAL_WINDOW_STATE

		}
		updateDerivTitle();
		rearrangePreviewGraphs(); //preview may be shared by multiple tools.
		return true;
	}
	///end REPAIR_PREVIEW_GRAPHS_WHEN_DELETED_BY_USER
	
	bool	updateGraphSettings(bool bSrc = true, bool b1stPrev = true, bool b2ndPrev  = true)
	{
		#define	OBJ_FORMAT	(FPB_TEXT | FPB_SCALE | FPB_STYLE | FPB_OTHER | FPB_DATA)
		if (bSrc && m_gl && m_glSrcPreview )
		{
			Tree trFmt;
			trFmt = m_gl.GetFormat(OBJ_FORMAT, FOB_ALL, true, true);
			///Sophy 7/1/2010 ORG-392-P2 TREENODE_NOT_EXIST_WHEN_SRC_GRAPH_HAS_NO_X_TITLE
			TreeNode trTitles = trFmt.Root.Axes.X.Titles;
			if ( trTitles && trTitles.BottomTitle )
				trTitles.BottomTitle.GetNode("Text").strVal = ""; //no X title
			///end TREENODE_NOT_EXIST_WHEN_SRC_GRAPH_HAS_NO_X_TITLE
			m_glSrcPreview.UpdateThemeIDs(trFmt.Root);
			m_glSrcPreview.ApplyFormat(trFmt, true, true);
		}
		

				
		if (b1stPrev && m_glDerivPreview && m_gl1stDerivPreview )
		{
			Tree trFmt;
			trFmt = m_glDerivPreview.GetFormat(FPB_ALL & ~FPB_DIMENSION, FOB_ALL & ~FOB_INCREMENT, true, true);
			trFmt.Root.Axes.X.Labels.BottomLabels.GetNode("Show").nVal = (m_gl2ndDerivPreview.IsValid() ? 0 : 1);
			trFmt.Root.Axes.X.Titles.BottomTitle.GetNode("Show").nVal = (m_gl2ndDerivPreview.IsValid() ? 0 : 1); //NOT show if not the last layer
			m_gl1stDerivPreview.UpdateThemeIDs(trFmt.Root);
			m_gl1stDerivPreview.ApplyFormat(trFmt, true, true);
		}
		
		if (b2ndPrev && m_glDerivPreview && m_gl2ndDerivPreview )
		{
			Tree trFmt;
			trFmt = m_glDerivPreview.GetFormat(FPB_ALL & ~FPB_DIMENSION, FOB_ALL & ~FOB_INCREMENT, true, true);
			trFmt.Root.Axes.X.Labels.BottomLabels.GetNode("Show").nVal = 1;
			trFmt.Root.Axes.X.Titles.BottomTitle.GetNode("Show").nVal = 1; //show title
			m_gl2ndDerivPreview.UpdateThemeIDs(trFmt.Root);
			m_gl2ndDerivPreview.ApplyFormat(trFmt, true, true);
	
		}
		
		if ( m_glDerivPreview )
		{
			Tree trFmt;
			trFmt = m_glDerivPreview.GetFormat(FPB_ALL & ~FPB_DIMENSION, FOB_ALL & ~FOB_INCREMENT, true, true);
			trFmt.Root.Axes.X.Labels.BottomLabels.GetNode("Show").nVal = 0;
			trFmt.Root.Axes.X.Labels.BottomLabels.GetNode("Show").nVal = (m_gl1stDerivPreview || m_gl2ndDerivPreview) ? 0 : 1;
			trFmt.Root.Axes.X.Titles.BottomTitle.GetNode("Show").nVal = (m_gl1stDerivPreview || m_gl2ndDerivPreview) ? 0 : 1; //NOT show if not the last layer
			m_glDerivPreview.ApplyFormat(trFmt, true, true);
		}
		return true;
	}
	
	bool	updateColumnSettings()
	{
		XYRange xySrc;
		if ( m_dp && m_dp.GetDataRange(xySrc) )
		{
			Column colSrcX, colSrcY;
			///Sophy 6/8/2010 ORG-48-P8 PROPER_COLUMN_SETTINGS_WHEN_NO_SRC_X_COLUMN
			//if ( xySrc.GetXColumn(colSrcX) && xySrc.GetYColumn(colSrcY) )
			if ( xySrc.GetXColumn(colSrcX) || xySrc.GetYColumn(colSrcY) )
			///end PROPER_COLUMN_SETTINGS_WHEN_NO_SRC_X_COLUMN
			{
				Column colSrcPreviewX(m_wks, PREVIEW_X_INDEX);
				_copy_column_settings(colSrcX, colSrcPreviewX);
	
				Column colDerivY(m_wks, PREVIEW_NTH_INDEX);
				_copy_column_settings(colSrcY, colDerivY);
				
				Column col1stDerivY(m_wks, PREVIEW_1ST_INDEX);
				_copy_column_settings(colSrcY, col1stDerivY);
				col1stDerivY.SetLongName("1st Deriv");

				Column col2ndDerivY(m_wks, PREVIEW_2ND_INDEX);
				_copy_column_settings(colSrcY, col2ndDerivY);
				col2ndDerivY.SetLongName("2nd Deriv");
			}
		}
		return true;
	}
	
	bool	updateDerivTitle()
	{
		Tree trGUI;
		GetGUITree(trGUI);
		int nOrder = trGUI.DerivOrder.nVal;
		string strTitle;
		if ( 1 == nOrder )
			strTitle = "1st Deriv";
		else if ( 2 == nOrder )
			strTitle = "2nd Deriv";
		else if ( 3 == nOrder )
			strTitle = "3rd Deriv";
		else
			strTitle.Format("%dth Deriv", nOrder);
		///Sophy 5/12/2010 QA82-15278 CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
		m_wks = m_wksPreview;
		Column colDeriv(m_wks, PREVIEW_NTH_INDEX);
		ASSERT(colDeriv.IsValid());
		if ( colDeriv.IsValid() )
			colDeriv.SetLongName(strTitle);
		return true;
	}
	
	bool	updatePlotSettings()
	{
		if ( m_dp )
		{
	#define	OBJ_FORMAT	(FPB_TEXT | FPB_SCALE | FPB_STYLE | FPB_OTHER | FPB_DATA)
			Tree trFmt;
			trFmt = m_dp.GetFormat(OBJ_FORMAT, FOB_ALL, true, true);
			
			DataPlot dpSrc = m_glSrcPreview.DataPlots(0);
			ASSERT(dpSrc);
			dpSrc.ApplyFormat(trFmt, true, true);
		}
		return true;
	}
	
	bool	updatePreviewGraphs()
	{
		updateDiffColor(); //we allow more derivative tools on share same preview graph, they may have different color settings, so should update according to current one.

		///Sophy 6/4/2010 ORG-48-P5 CONSTRUCT_DATASET_FROM_COLUMN_NOT_DOUBLE_FORMAT_RUNTIME
		//Dataset dsSrcX(m_wks, PREVIEW_X_INDEX);
		Column colSrcX(m_wks, PREVIEW_X_INDEX);
		vectorbase& dsSrcX = colSrcX.GetDataObject();
		///end CONSTRUCT_DATASET_FROM_COLUMN_NOT_DOUBLE_FORMAT_RUNTIME
		dsSrcX = m_vX;
		
		///Sophy 5/10/2010 PREVIEW_GRAPH_X_FROM_TO_FOLLOW_MAIN_RECTANGLE
		double dXFrom, dXTo;
		///Sophy 5/11/2010 QA82-15278 BETTER_MAJOR_MINOR_TICKS_FOR_X_AXIS
		//m_vX.GetMinMax(dXFrom, dXTo);
		GetRectCoordinate(dXFrom, dXTo);
		m_glSrcPreview.X.Inc = round(round(fabs(dXTo - dXFrom), 0) / 8, 1); //8 major ticks on X-axis
		///end BETTER_MAJOR_MINOR_TICKS_FOR_X_AXIS
		m_glSrcPreview.X.From = dXFrom;
		m_glSrcPreview.X.To = dXTo;
		///end PREVIEW_GRAPH_X_FROM_TO_FOLLOW_MAIN_RECTANGLE
		
		m_glSrcPreview.Rescale(ANL_NO_EXPAND);
	
		///Sophy 6/4/2010 ORG-48-P5 CONSTRUCT_DATASET_FROM_COLUMN_NOT_DOUBLE_FORMAT_RUNTIME
		//Dataset dsDerivY(m_wks, PREVIEW_NTH_INDEX);
		Column colDerivY(m_wks, PREVIEW_NTH_INDEX);
		vectorbase& dsDerivY = colDerivY.GetDataObject();
		///end CONSTRUCT_DATASET_FROM_COLUMN_NOT_DOUBLE_FORMAT_RUNTIME
		dsDerivY = m_vDerivY;
		///Sophy 5/11/2010 QA82-15278 BETTER_MAJOR_MINOR_TICKS_FOR_X_AXIS
		m_vDerivY.GetMinMax(dXFrom, dXTo);
		m_glDerivPreview.Y.Inc = round(round(fabs(dXTo - dXFrom), 0) / 4, 1); //8 major ticks on X-axis
		///end BETTER_MAJOR_MINOR_TICKS_FOR_X_AXIS
		m_glDerivPreview.Rescale(ANL_NO_XINC_CHANGE | ANL_NO_INC_CHANGE);
		
		if ( m_gl1stDerivPreview.IsValid() )
		{
			///Sophy 6/4/2010 ORG-48-P5 CONSTRUCT_DATASET_FROM_COLUMN_NOT_DOUBLE_FORMAT_RUNTIME
			//Dataset ds1stDerivY(m_wks, PREVIEW_1ST_INDEX);
			Column col1stDerivY(m_wks, PREVIEW_1ST_INDEX);;
			vectorbase& ds1stDerivY = col1stDerivY.GetDataObject();
			///end CONSTRUCT_DATASET_FROM_COLUMN_NOT_DOUBLE_FORMAT_RUNTIME
			ds1stDerivY = m_v1stDerivY;
			
			///Sophy 5/11/2010 QA82-15278 BETTER_MAJOR_MINOR_TICKS_FOR_X_AXIS
			m_v1stDerivY.GetMinMax(dXFrom, dXTo);
			m_gl1stDerivPreview.Y.Inc = round(round(fabs(dXTo - dXFrom), 0) / 4, 1); //8 major ticks on X-axis
			///end BETTER_MAJOR_MINOR_TICKS_FOR_X_AXIS
			m_gl1stDerivPreview.Rescale(ANL_NO_XINC_CHANGE | ANL_NO_INC_CHANGE);
		}
		
		if ( m_gl2ndDerivPreview.IsValid() )
		{
			///Sophy 5/11/2010 QA82-15278 BETTER_MAJOR_MINOR_TICKS_FOR_X_AXIS
			///Sophy 6/4/2010 ORG-48-P5 CONSTRUCT_DATASET_FROM_COLUMN_NOT_DOUBLE_FORMAT_RUNTIME
			//Dataset ds2ndDerivY(m_wks, PREVIEW_2ND_INDEX);
			Column col2ndDerivY(m_wks, m_gl1stDerivPreview.IsValid() ? PREVIEW_2ND_INDEX : PREVIEW_1ST_INDEX);
			vectorbase& ds2ndDerivY = col2ndDerivY.GetDataObject();
			///end CONSTRUCT_DATASET_FROM_COLUMN_NOT_DOUBLE_FORMAT_RUNTIME
			ds2ndDerivY = m_v2ndDerivY;
			
			m_v2ndDerivY.GetMinMax(dXFrom, dXTo);
			m_gl2ndDerivPreview.Y.Inc = round(round(fabs(dXTo - dXFrom), 0) / 4, 1); //4 major ticks on X-axis
			///end BETTER_MAJOR_MINOR_TICKS_FOR_X_AXIS
			m_gl2ndDerivPreview.Rescale(ANL_NO_XINC_CHANGE | ANL_NO_INC_CHANGE);
		}
		
		return true;
	}
	
	void	destroySettingsAccess(bool& bLeaveGraph, bool bGet = true)
	{
		if ( !m_go.IsValid() )
			return;
		
		Tree trSettings;
		tree_get_binary_storage(trSettings, m_go, PREVIEWSETTINGS); //assume the storage always exist, created on creating ROI.
		if ( bGet && trSettings.LeaveGraph )
		{
			bLeaveGraph = trSettings.LeaveGraph.nVal == 1;
		}
		else
		{
			trSettings.LeaveGraph.nVal = bLeaveGraph ? 1 : 0;
		}
		tree_put_binary_storage(trSettings, m_go, PREVIEWSETTINGS); //save settings	
	}
	
	void	previewSettingsAccess(bool& bShow1st, bool& bShow2nd, bool bGet = true)
	{
		if ( !m_go.IsValid() )
			return;
		Tree trSettings;
		tree_get_binary_storage(trSettings, m_go, PREVIEWSETTINGS); //assume the storage always exist, created on creating ROI.
		if ( bGet && trSettings.ShowFirstDeriv )
		{
			bShow1st = trSettings.ShowFirstDeriv.nVal == 1;
			bShow2nd = trSettings.ShowSecondDeriv.nVal == 1;
		}
		else
		{
			trSettings.ShowFirstDeriv.nVal = bShow1st ? 1 : 0;
			trSettings.ShowSecondDeriv.nVal = bShow2nd ? 1 : 0;
		}
		tree_put_binary_storage(trSettings, m_go, PREVIEWSETTINGS); //save settings
	}
	///Sophy 6/10/2010 ORG-48-P13 DEFAULT_SHOW_LONGNAME_IN_RESULT_SHEET
	void	initnthDerivPreview()
	{
		if ( m_glDerivPreview.IsValid() )
		{
			DataRange drDeriv;
			drDeriv.Add(m_wks, PREVIEW_X_INDEX, "X");
			drDeriv.Add(m_wks, PREVIEW_NTH_INDEX, "Y");
			int nDerivPlot = m_glDerivPreview.AddPlot(drDeriv, IDM_PLOT_LINE);
			DataPlot dpDeriv = m_glDerivPreview.DataPlots(nDerivPlot);
			set_dataplot_selectable(dpDeriv, false);
		}
	}
	///end DEFAULT_SHOW_LONGNAME_IN_RESULT_SHEET
	
	void	init1stDerivPreview()
	{
		if ( m_gl1stDerivPreview.IsValid() )
		{
			///Sophy 6/10/2010 ORG-48-P13 DEFAULT_SHOW_LONGNAME_IN_RESULT_SHEET
			string strDerivLongName = "1st Deriv";
			Column col1stDeriv(m_wks, PREVIEW_1ST_INDEX);
			if ( !col1stDeriv.IsValid() || strDerivLongName.CompareNoCase(col1stDeriv.GetLongName()) != 0 )
			{
				string strName;
				m_wks.InsertCol(PREVIEW_1ST_INDEX, "1stDerivative", strName, TRUE);
				col1stDeriv.Attach(m_wks, PREVIEW_1ST_INDEX);
				Column colDeriv(m_wks, PREVIEW_NTH_INDEX);
				_copy_column_settings(colDeriv, col1stDeriv);
				col1stDeriv.SetLongName(strDerivLongName);
			}
			///end DEFAULT_SHOW_LONGNAME_IN_RESULT_SHEET
			DataRange dr1stDeriv;
			dr1stDeriv.Add(m_wks, PREVIEW_X_INDEX, "X");
			dr1stDeriv.Add(m_wks, PREVIEW_1ST_INDEX, "Y");
			int n1stDerivPlot = m_gl1stDerivPreview.AddPlot(dr1stDeriv, IDM_PLOT_LINE);
			DataPlot dp1stDeriv = m_gl1stDerivPreview.DataPlots(n1stDerivPlot);
			set_dataplot_selectable(dp1stDeriv, false);
			
			GraphObject goLegend = m_gl1stDerivPreview.GraphObjects(GO_LEGEND_NAME);
			if ( goLegend )
				goLegend.Show = false;
		}
	}
	
	void	init2ndDerivPreview()
	{
			
		if ( m_gl2ndDerivPreview.IsValid() )
		{
			///Sophy 6/10/2010 ORG-48-P13 DEFAULT_SHOW_LONGNAME_IN_RESULT_SHEET
			string strDerivLongName = "2nd Deriv";
			int nDerivYIndex = PREVIEW_2ND_INDEX;
			Column col2ndDeriv(m_wks, nDerivYIndex);
			if ( !col2ndDeriv.IsValid() )
			{
				col2ndDeriv.Attach(m_wks, PREVIEW_1ST_INDEX);
				if ( !col2ndDeriv.IsValid() || strDerivLongName.CompareNoCase(col2ndDeriv.GetLongName())!= 0 )
				{
					nDerivYIndex = m_wks.AddCol("2ndDerivative");
					col2ndDeriv.Attach(m_wks, nDerivYIndex);
					Column colDeriv(m_wks, PREVIEW_NTH_INDEX);
					_copy_column_settings(colDeriv, col2ndDeriv);
					col2ndDeriv.SetLongName(strDerivLongName);
				}
			}
			///end DEFAULT_SHOW_LONGNAME_IN_RESULT_SHEET
			DataRange dr2ndDeriv;
			dr2ndDeriv.Add(m_wks, PREVIEW_X_INDEX, "X");
			dr2ndDeriv.Add(m_wks, nDerivYIndex, "Y");
			int n2ndDerivPlot = m_gl2ndDerivPreview.AddPlot(dr2ndDeriv, IDM_PLOT_LINE);
			DataPlot dp2ndDeriv = m_gl2ndDerivPreview.DataPlots(n2ndDerivPlot);
			set_dataplot_selectable(dp2ndDeriv, false);
			
			GraphObject goLegend = m_gl2ndDerivPreview.GraphObjects(GO_LEGEND_NAME);
			if ( goLegend )
				goLegend.Show = false;
		}
	}
	
	bool	rearrangePreviewGraphs()
	{
		bool bNeedRearrange = false;
		bool bShow1st, bShow2nd;
		previewSettingsAccess(bShow1st, bShow2nd, true); //get settings;
		Tree trGUI;
		GetGUITree(trGUI);
		if ( trGUI.DerivOrder.nVal == 1 )
			bShow1st = false;
		else if ( trGUI.DerivOrder.nVal == 2 )
			bShow2nd = false;
		
		if ( (m_gl1stDerivPreview.IsValid() != bShow1st) || (m_gl2ndDerivPreview.IsValid() != bShow2nd) )
			bNeedRearrange = true;
		
		if ( bNeedRearrange )
		{
			m_wks.SetSize(-1, PREVIEW_INIT_COLS);	///Sophy 6/10/2010 ORG-48-P13 DEFAULT_SHOW_LONGNAME_IN_RESULT_SHEET
			///Sophy 5/12/2010 QA82-15278 CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
			GraphPage gpPreview = m_gpPreview;
			///end CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
			vector<int> vnLayerIndex;
			vnLayerIndex.Add(m_glSrcPreview.GetIndex());
			vnLayerIndex.Add(m_glDerivPreview.GetIndex());
			if ( m_gl1stDerivPreview.IsValid() )
				vnLayerIndex.Add(m_gl1stDerivPreview.GetIndex());
			if ( m_gl2ndDerivPreview.IsValid() )
				vnLayerIndex.Add(m_gl2ndDerivPreview.GetIndex());
			
			double xx[TOTAL_POS];
			m_glSrcPreview.GetPosition(xx);
			m_glSrcPreview.UnitsConvert(M_PERCENT, xx);
			int nLeftMargin = xx[LEFT_POS];
			int nRightMargin = 100 - xx[LEFT_POS] - xx[WIDTH_POS];
			int nTopMargin = xx[TOP_POS];
			///Sophy 6/7/2010 ORG-48-P6 ENLARGE_BOTTOM_GAP_TO_VIEW_X_LABEL
			//int nBottomMargin = 5;
			int nBottomMargin = 10;
			///end ENLARGE_BOTTOM_GAP_TO_VIEW_X_LABEL
			

			stLayersGridFormat stFormat;
			stFormat.nXGap = 5;
			stFormat.nYGap = 0;
			stFormat.nLeftMg = nLeftMargin;
			stFormat.nRightMg = nRightMargin;
			stFormat.nTopMg = nTopMargin;
			stFormat.nBottomMg = nBottomMargin;
			
			layer_set_link(m_glDerivPreview, 0, LINK_STRAIGHT, LINK_NONE);
			if ( bShow1st && !m_gl1stDerivPreview.IsValid() )
			{
				m_gl1stDerivPreview = gpPreview.Layers(gpPreview.AddLayer(PREVIEW_1ST_DERIV));
				init1stDerivPreview();
				layer_set_link(m_gl1stDerivPreview, 0, LINK_STRAIGHT, LINK_NONE);
			}
			else if ( !bShow1st && m_gl1stDerivPreview.IsValid() )
				m_gl1stDerivPreview.Destroy();
			
			if ( bShow2nd && !m_gl2ndDerivPreview.IsValid() )
			{
				m_gl2ndDerivPreview = gpPreview.Layers(gpPreview.AddLayer(PREVIEW_2ND_DERIV));
				init2ndDerivPreview();
				layer_set_link(m_gl2ndDerivPreview, 0, LINK_STRAIGHT, LINK_NONE);
			}
			else if ( !bShow2nd && m_gl2ndDerivPreview.IsValid() )
				m_gl2ndDerivPreview.Destroy();
			
			//need to switch index of 1st & 2nd preview layer for better view, if both exist.
			if ( m_gl1stDerivPreview.IsValid() && m_gl2ndDerivPreview.IsValid() )
			{
				int nIndex1st = m_gl1stDerivPreview.GetIndex();
				int nIndex2nd = m_gl2ndDerivPreview.GetIndex();
				if ( nIndex1st > nIndex2nd )
				{
					gpPreview.Reorder(nIndex1st, nIndex2nd); //swap the indices.
				}
			}
			
			vnLayerIndex.RemoveAll();
			vnLayerIndex.Add(m_glSrcPreview.GetIndex());
			vnLayerIndex.Add(m_glDerivPreview.GetIndex());
			if ( m_gl1stDerivPreview.IsValid() )
				vnLayerIndex.Add(m_gl1stDerivPreview.GetIndex());
			if ( m_gl2ndDerivPreview.IsValid() )
				vnLayerIndex.Add(m_gl2ndDerivPreview.GetIndex());
			
			page_arrange_layers(gpPreview, vnLayerIndex.GetSize(), 1, &stFormat, false, false, vnLayerIndex, false, true);
			updateGraphSettings(false, true, true);
			alignYTitles();	///Sophy 5/10/2010 QA82-15278 BETTER_ALIGNMENT_OF_Y_AXIS_TITLE_FOR_MULTI_LAYER
		}
		return true;
	}

	///Sophy 5/10/2010 QA82-15278 BETTER_ALIGNMENT_OF_Y_AXIS_TITLE_FOR_MULTI_LAYER
	bool	alignYTitles()
	{
#define	AXIS_TITLE_NAME	"YL"
		GraphObject goSrc, goDst;
		goSrc = m_glDerivPreview.GraphObjects(AXIS_TITLE_NAME);
		if ( !goSrc.IsValid() )
			return false;
		
		LT_execute("sec -p 0.01"); //wait until object painted.
		if ( m_gl1stDerivPreview.IsValid() )
		{
			goDst = m_gl1stDerivPreview.GraphObjects(AXIS_TITLE_NAME);
			if ( goDst )
			{
				Tree trFmt;
				trFmt = goSrc.GetFormat(FPB_DIMENSION, FOB_ALL, true, true);
				goDst.ApplyFormat(trFmt, true, true);
			}
		}

		if ( m_gl2ndDerivPreview.IsValid() )
		{
			goDst = m_gl2ndDerivPreview.GraphObjects(AXIS_TITLE_NAME);
			if ( goDst )
			{
				Tree trFmt;
				trFmt = goSrc.GetFormat(FPB_DIMENSION, FOB_ALL, true, true);
				goDst.ApplyFormat(trFmt, true, true);
			}
		}
		return true;
	}
	///end BETTER_ALIGNMENT_OF_Y_AXIS_TITLE_FOR_MULTI_LAYER
	
	///Sophy 5/11/2010 QA82-15278 CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
	bool	makeOnePlot(Worksheet& wksSrc, int nX, int nY, GraphPage& gp, int nLayerIndex)
	{
		GraphLayer gl;
		if ( gp.IsValid() )
			gl = gp.Layers(nLayerIndex);
		if ( !wksSrc.IsValid() || !gl.IsValid() )
			return false;
		
		DataRange drSrc;
		drSrc.Add(wksSrc, nX, "X");
		drSrc.Add(wksSrc, nY, "Y");
		
		//assume there is one exsting plot
		ASSERT(gl.DataPlots.Count() == 1);
		DataPlot dpOld = gl.DataPlots(0);
		int nPlot = gl.AddPlot(drSrc, IDM_PLOT_LINE);
		DataPlot dpNew = gl.DataPlots(nPlot);
		if ( dpNew && dpOld )
		{
			Tree trFmt;
			trFmt = dpOld.GetFormat(FPB_ALL, FOB_ALL, true, true);
			dpNew.ApplyFormat(trFmt, true, true);
			gl.RemovePlot(dpOld); //remove junk plot from layer
		}
		return dpNew.IsValid();
	}
	
	bool	clonePreviewAndCloseROI()
	{
		checkPreparePreviewGraphs();
		Column colDeriv(m_wksPreview, PREVIEW_NTH_INDEX);
		m_wksPreview.GetPage().SetLongName(colDeriv.GetLongName()); //rename longname, or else reopen ROI on same layer, preview may attach to it, should be forbidden currently.
		m_gpPreview.SetLongName(colDeriv.GetLongName());
		roi_tool_option_access(ROI_OPTION_IGNORE_DESTROY, ROI_OP_SET); //ignore destroy in derived class one time to avoid deleting result, still GraphObjTool::OnDestroy will be called.
		///end CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
		
		//destroy ROI.
		string strCmd;
		strCmd.Format("run.section(graph_controls, DelayDestroy, %s);", m_go.GetName());
		LT_execute(strCmd);
		return true;
	}
	///end CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
	///end DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH

private:
	PolylineGraphObject	m_goDiffCurve;
	vector				m_vDerivY;
	///Sophy 4/30/2010 QA82-15278 DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH
	vector				m_v1stDerivY;
	vector				m_v2ndDerivY;
	///Sophy 5/12/2010 QA82-15278 CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
	Worksheet			m_wksPreview;
	GraphPage			m_gpPreview;
	///end CONTEXT_MENU_SUPPORT_LEAVING_PREVIEW_AS_RESULT_ON_DESTROY
	GraphLayer			m_glSrcPreview;
	GraphLayer			m_glDerivPreview;
	GraphLayer			m_gl1stDerivPreview;
	GraphLayer			m_gl2ndDerivPreview;
///end DERIV_GADGET_USE_SEPARATE_PREVIEW_GRAPH
};

void	addtool_curve_deriv_events(string strGrName, int nEvent, int nMsg = 0)
{
	DifferentiateTool dtool;
	graphobjtool_events(dtool, strGrName, nEvent, nMsg);
}

void addtool_curve_deriv(const TreeNode& trGUI)
{
	DifferentiateTool dtool;
	int nErr = dtool.Create(XF_NAME, 0, trGUI);
	if ( nErr < 0 )
	{
		XF_THROW(XFERR_INVALID_GRAPH_LAYER);
		return;
	}
	if ( nErr )
		error_report("addtool_curve_deriv failed Init");
}

//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 addtool_curve_deriv_event1(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	if( xf_is_extended_event(nEventID) )
		return addtool_curve_deriv_event1_ex(trGetN, nRow, nEventID, dwEnables, lpcszNodeName, DynaCntrlContainer, strAux, strErrMsg);
	DECLARE_BUTTON_ENABLES
	///Sophy 6/10/2010 ORG-48-P14 BETTER_EVENT_HANDLE_FOR_PREFERENCE_IN_ROI_TOOLS
	getn_handle_deriv(trGetN, nRow, nEventID, dwEnables, lpcszNodeName, DynaCntrlContainer, strAux, strErrMsg);
	///end BETTER_EVENT_HANDLE_FOR_PREFERENCE_IN_ROI_TOOLS
	return false;

}

int addtool_curve_deriv_make_tree(TreeNode& tr, LPCSTR lpcszVarName)
{
	if ( tr.IsValid() )
	{
		int nBranchID = 1;
		int nUserID = GET_USER_DATAID(0);
		if ( strcmp(lpcszVarName, "trGUI") == 0 )
		{
			GETN_USE(tr)
			GETN_OPTION_BRANCH(GETNBRANCH_OPEN)
			GETN_BEGIN_BRANCH(roi, STR_ROI_LABEL)	GETN_ID(nBranchID++)
				GETN_STR(toolname, STR_TOOLNAME, TOOLNAME)	GETN_ID(nUserID++)	GETN_CONTROL_OPTION_BOX(0)
				GETN_COLOR(rectcolor, STR_FILL_COLOR, SYSCOLOR_LTYELLOW)	GETN_COLOR_CHOICE_OPTIONS(COLORLIST_CUSTOM | COLORLIST_SINGLE)	GETN_ID(nUserID++)
				GETN_COLOR(derivLineColor, _L("Derivative Line Color"), SYSCOLOR_BLUE)	GETN_COLOR_CHOICE_OPTIONS(COLORLIST_CUSTOM | COLORLIST_SINGLE)	GETN_ID(nUserID++)
			GETN_END_BRANCH(roi)
			GETN_COMBO(DerivOrder, _L("Derivative Order"), 1, "|1|2|3|4|5|6|7|8|9") GETN_OPTION_NUM_FORMAT("%d")	GETN_ID(nUserID++)	GETN_OPTION_EVENT(_deriv_order_event)
			GETN_BEGIN_BRANCH(smoothOptions, _L("Smooth"))	GETN_ID(nBranchID++)
				GETN_CHECK(smooth, _L("Savitzky-Golay Smooth"), 0)	GETN_ID(nUserID++)	GETN_OPTION_EVENT(_check_smooth_event)
				GETN_LIST(polyOrder, _L("Polynomial Order"), 1, "1|2|3|4|5|6|7|8|9")		GETN_ID(nUserID++)	GETN_READ_ONLY
				GETN_NUM(npts, _L("Points of Window"), 10)	GETN_OPTION_NUM_FORMAT("%d")	GETN_ID(nUserID++)	GETN_READ_ONLY
			GETN_END_BRANCH(smoothOptions)
			GETN_STR(GETNEVT, "EventHandle", "") GETN_GLOBAL_EVT(getn_handle_deriv)	GETN_ID(nUserID++) GETN_CURRENT_SUBNODE.Show = false;
		}
	}
	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 addtool_curve_deriv_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{
	if ( is_xf_startup(nGetNDialog, dwCntrl) )
	{
		if(is_use_origin_chm())
			trGetN.SetAttribute(STR_HELPID_ATTRIB, IDD_XF_addtool_curve_deriv);
	}
}
//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 addtool_curve_deriv_event1_ex(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	return false;
}
