#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>

#include <ocm.h>			///Jasmine 01/14/09 QA80-12852 REDUCE_POINT_NEED_SPECIFY_METHOD
#include <xfGraph_utils.h>
#include <XFMenu.h>			//---Jasmine 12/12/08 v8.0986 QA80-12715 ADD_BUTTON_FOR_POP_UP_MENU
#include <..\OriginLab\graph_utils.h>	///Kyle 01/12/2009 v8.0995c MOVE_DATAPLOT_APPARENTLY

//////////////////////////////////////
// Modification Log
//
// CPY 7/12/2007 CURVE_TRANSLATE_UNDO_AND_REMOVAL_AFTER_MOVE
// CPY 11/29/07 QA70-10509 FAILED_MOVING_DATA_AT_END_OF_MOVE, added error_report in many places
// Jasmine 11/25/08 v8.0979 QA80-12367 ALLOW_X_IS_SAMPLING_INTERVAL
// Jasmine 11/27/08 v8.0980d QA80-12367 SNAP_TO_MIDDLE_POINT_IN_DATAPLOT_MAY_OUT_OF_VIEW
// Jasmine 12/04/08 v8.0982b GET_PLOT_COLUMNS
// Jasmine 12/12/08 v8.0986 QA80-12715 ADD_BUTTON_FOR_POP_UP_MENU
// Folger 12/19/08 QA80-12815 v8.0988c SUPPORT_GET_DATA_POINTS_BY_RECT_MADE_OF_TWO_DOUBLE_POINTS
// Kyle 12/24/2008 CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
// Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
// Jasmine 12/29/08 v8.0991b QA80-12367 KEEP_MISSING_VALUE_IN_PLOT
// Jasmine 01/06/09 v8.0993e QA80-12715 GIVE_MORE_VALUE_INFO_TO_USER
// Jasmine 01/08/09 v8.0994d QA80-12715 ADD_TARGET_LINE
// Kyle 01/12/2009 v8.0995c MOVE_DATAPLOT_APPARENTLY
// Jasmine 01/09/09 v8.0995 QA80-12715 SHOW_VALUE_IN_DATA_DISPLAY_BAR
// Jasmine 01/12/09 v8.0995c QA80-12715 PUT_ANCHOR_TO_EDGE_IF_OUT_OF_DATA
// Jasmine 01/12/09 v8.0995c QA80-12852 REDUCE_POINT_TO_SPEED_UP
// Jasmine 01/14/09 QA80-12852 REDUCE_POINT_NEED_SPECIFY_METHOD
// Jasmine 01/15/09 GET_FROM_TO_INDEX_BY_VISIBLE_RANGE
// Jasmine 01/08/09 GET_NEAREST_BUT_NOT_SO_CLOSE_POINT
// Jasmine 01/19/09 QA80-12944 SHOW_ANCHOR_AND_TARGET_LINE_AND_THEIR_DISTANCE
// CPY 1/19/09 QA70-12982-P2 DISABLE_LAYER_AND_DATAPLOT_SEL_WHILE_TOOL_IS_USED
// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
// Jasmine 01/21/09 QA80-12715 OPTION_OF_USE_SAMPLING_INTERVAL
// Jasmine 01/21/09 QA80-12715 PUT_TEXT_IN_DIFFERENT_ROW_SO_FONT_CAN_BIGGER
// RVD 1/21/2009 qa70-12995-p1 RIGHT_CLICK_WHEN_DRAGING_OBJ_GHOST_CURVE_WRONG_POS
// Jasmine 02/04/09 OUTPUT_RESULT_TO_COLUMN_COMMENTS
// Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION	
// Jasmine 02/05/09 BY_DEFAULT_USE_STEP_SHIFT_IF_SAMPLING_INTERVAL  
// Jasmine 02/05/09 COMBINE_STEP_SIZE_IN_STEP_SHIFT_MENU
// YuI 02/03/09 QA70-13053 IMPROVEMENTS_TO_BUTTONIZE_OBJECT_DRAWING
/// RVD 2/6/2009 SNAP_USELESS_IF_NO_OCD_MODIFY_BIT
// Jasmine 02/10/09 REMEMBER_KEEPTOOL_IN_REGISTER
// Jasmine 02/18/09 SNAP_TO_PARENT_NOT_WORK_FOR_LINEINFO_IN_981
// RVD 7/2/2009 qa70-13499 GARBAGE_GC_EVERY_2ND_MOVING
// Jasmine 07/30/09 QA80-14031 CAN_NOT_CHANGE_FUNCTION_PLOT_DATA
// Jasmine 07/30/09 QA80-14031 USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
// Jasmine 09/23/09 GRAPH_LEGEND_IS_COL_COMMENT_AND_BETTER_NOT_USE_IN_COMMENT_AGAIN
// Jasmine 10/10/09 DATAPLOT_GETDATAPOINTS_WILL_GET_DATA_OUTSIDE_RANGE
// Folger 02/10/10 QA81-14959-P3 XF_SUPPORT_CREATE_HIDDEN_ANCHOR
///////////////////////////////////////////////////////////////////////

//#define _DEBUG_CURVE_TRANSLATE

//----- CPY 11/29/07 QA70-10509 FAILED_MOVING_DATA_AT_END_OF_MOVE
// I removed from XF builder enum, so add here by hand
enum {
	ntype_vertical,
	ntype_horizontal,
};
// there is something wrong with the internal code to get the correct positions sometimes on end of move, so I decide to just use static to save moving position for later to use
static double s_dx, s_dy;
static bool s_bReady;
//-----

///Jasmine 12/12/08 v8.0986 QA80-12715 ADD_BUTTON_FOR_POP_UP_MENU
static double 	s_dStep;

///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
static bool		s_bStepShift;	
static int		s_n;
static bool		s_bUseSamplingInterval;
///End COUNT_STEP_BY_SAMPLING_INTERVAL
static bool		s_HasTargetLine;
///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION	
static bool   s_bKeepTool;		
///End ALLOW_KEEP_TOOL_AFTER_TRANSLATION

enum
{
	CURVE_TRANSLATE_MOVE_ANCHOR = 1,	// must start with >0 value, since TrackPopupMenu get cmd > 0
	///Jasmine 01/08/09 v8.0994d QA80-12715 ADD_TARGET_LINE
	CURVE_TRANSLATE_TARGET_ANCHOR,		
	CURVE_TRANSLATE_SHIFT_TO_TARGET,
	///End ADD_TARGET_LINE
	CURVE_TRANSLATE_SHIFT_VALUE,
		CURVE_TRANSLATE_SEPARATOR1,
	///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
	CURVE_TRANSLATE_SHIFT_STEP,
	//CURVE_TRANSLATE_STEP_SIZE,	///Jasmine 02/05/09 COMBINE_STEP_SIZE_IN_STEP_SHIFT_MENU
		CURVE_TRANSLATE_SEPARATOR2,
	///End COUNT_STEP_BY_SAMPLING_INTERVAL
	///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION	
	CURVE_TRANSLATE_KEEP_TOOL,
	CURVE_TRANSLATE_DELETE_TOOL,
		CURVE_TRANSLATE_SEPARATOR3,
	///End ALLOW_KEEP_TOOL_AFTER_TRANSLATION
	CURVE_TRANSLATE_HELP,
	
	CURVE_TRANSLATE_TOTAL
};

///Jasmine 01/09/09 v8.0995 QA80-12715 SHOW_VALUE_IN_DATA_DISPLAY_BAR
#define NEXT_LINE			"\r\n"
#define STR_TARGET			_L("Target Line")		
#define STR_ANCHOR_VALUE	_L("Anchor")
///End SHOW_VALUE_IN_DATA_DISPLAY_BAR

///Jasmine 01/06/09 v8.0993e QA80-12715 GIVE_MORE_VALUE_INFO_TO_USER
#define STR_NO_DATA			_L("No Data");

///Jasmine 07/30/09 QA80-14031 USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
//if error return -1. 
//return 1 if dp has x else return 0
static int _has_x(const DataPlot& dp, string* pstrX = NULL, string* pstrY = NULL)
{
	string 	strY, strX;
	XYRange	dr;
	if( !dp || !dp.GetDataRange(dr) )
		return -1;
		
	dr.GetDatasetNames(strY, strX);
	ASSERT( !strY.IsEmpty() );
	
	if(pstrX)
		*pstrX = strX;
	if(pstrY)
		*pstrY = strY;
	
	return strX.IsEmpty()? CER_NO_X_COL : CER_NO_ERROR;
}
///End USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
 
/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
static double	get_marker_pos(MarkerInfo& mi, bool bVertTrans)
{
	double x = 0, y = 0;
	int nRet = mi.GetWorldXY(0, &x, &y);
	return bVertTrans ? y : x;
}
/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP

//------- CPY 1/19/09 QA70-12982-P2 DISABLE_LAYER_AND_DATAPLOT_SEL_WHILE_TOOL_IS_USED
static void _set_page_no_click(bool bSet, DWORD dwNoClick = NOCLICK_DATA_PLOT | NOCLICK_LAYER)
{
	static DWORD l_save_page_no_click; // temp solution, later will need to use page level storage
	static string l_strPageName;
	if(bSet)
	{
		l_strPageName.Empty();
		GraphPage gp = Project.Pages();
		if(gp)
		{
			l_save_page_no_click = get_gp_noclick(gp);
			set_gp_noclick(gp, dwNoClick);
			l_strPageName = gp.GetName();
		}
	}
	else // reset
	{
		if(!l_strPageName.IsEmpty())
		{
			GraphPage gp(l_strPageName);
			if(gp)
			{
				set_gp_noclick(gp, l_save_page_no_click);
			}
			l_strPageName.Empty();
		}
	}
}
//------- end DISABLE_LAYER_AND_DATAPLOT_SEL_WHILE_TOOL_IS_USED

class CurveTranslateMenu : public XFMenu
{
private:
	int 	m_ntype;
	bool 	m_bStepShift;		///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
	bool	m_bTargetLine;		///Jasmine 01/08/09 v8.0994d QA80-12715 ADD_TARGET_LINE
	bool	m_bKeepTool;		///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION
			
public:
	///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
	//CurveTranslateMenu(int ntype, ButtonInfo& biMenu) : XFMenu(biMenu)
	CurveTranslateMenu(int ntype, bool bStepShift, bool bTargetAnchor, bool bKeepTool, ButtonInfo& biMenu) : XFMenu(biMenu)
	{
		m_ntype 		= ntype;
		m_bStepShift	= bStepShift;
		m_bTargetLine = bTargetAnchor;	///Jasmine 01/08/09 v8.0994d QA80-12715 ADD_TARGET_LINE
		///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION
		m_bKeepTool		= bKeepTool;
		///End ALLOW_KEEP_TOOL_AFTER_TRANSLATION
	}
	///End COUNT_STEP_BY_SAMPLING_INTERVAL
	
	//virtual
	bool ProcessEvents(const EventInfo& ei, const ButtonInfo& biMenu, DataRange& dr, int& nMenuCmd, int nMinPts = 1, LineInfo& ln = NULL, TextInfo& tx = NULL, LPCSTR lpcszInitText = NULL)
	{
		nMenuCmd = 0;
		switch(ei.GetEvent())
		{
		case OE_INIT_ANCHORS:
			biMenu.Init(OTCIC_RIGHT);
			_set_page_no_click(true);//------- CPY 1/19/09 QA70-12982-P2 DISABLE_LAYER_AND_DATAPLOT_SEL_WHILE_TOOL_IS_USED
			break;
		//------- CPY 1/19/09 QA70-12982-P2 DISABLE_LAYER_AND_DATAPLOT_SEL_WHILE_TOOL_IS_USED
		case OE_DELETE:
			_set_page_no_click(false);
			break;
		//------- end DISABLE_LAYER_AND_DATAPLOT_SEL_WHILE_TOOL_IS_USED	
		case OE_BUTTONCLICK:
			if(biMenu.GetID() == ei.GetSenderId())
			{
				nMenuCmd = TrackPopupMenu(GetWindow());
				if(nMenuCmd <= 0)
				{
					nMenuCmd = IDM_XF_CANCEL;
					return false;
				}
			}
			break;
			
		case OE_XF_MSG:
			return XFMenu::ProcessEvents(ei, biMenu, dr, nMenuCmd, nMinPts, ln, tx, lpcszInitText);
			
		}
		
		return false;
	}
	//virtual 
	int TrackPopupMenu(HWND hWndParent, int nx = -1, int ny = -1)
	{
		Construct();// no need to create until we do the TrackPopupMenu
		if(nx < 0)
		{
			POINT pt;
			GetCursorPos(&pt);
			nx = pt.x;
			ny = pt.y;
		}
		int nCmd;
		Menu::TrackPopupMenu(0, nx, ny, hWndParent, &nCmd);
		if(nCmd > 0)			
			return nCmd; // not yet processed, or still need further processing
		
		return -1;				
	}
	
protected:
	//virtual
	void Construct()
	{
		//int nFlag = m_ntype == ntype_vertical? MF_DISABLED|MF_GRAYED : MF_ENABLED;
		
		Add(_L("Move Anchor..."), 		CURVE_TRANSLATE_MOVE_ANCHOR, 	MF_ENABLED);
		///Jasmine 01/08/09 v8.0994d QA80-12715 ADD_TARGET_LINE
		Add(_L("Show Target Line"), 	CURVE_TRANSLATE_TARGET_ANCHOR, 	m_bTargetLine? MF_CHECKED : MF_UNCHECKED);	
		Add(_L("Shift Curve to Target Line"), CURVE_TRANSLATE_SHIFT_TO_TARGET, 	m_bTargetLine? MF_ENABLED : MF_DISABLED|MF_GRAYED);
		///End ADD_TARGET_LINE
		Add(_L("Shift Curve..."), 		CURVE_TRANSLATE_SHIFT_VALUE, 	MF_ENABLED);
			Add(NULL, CURVE_TRANSLATE_SEPARATOR1, MF_SEPARATOR);	
		///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
		Add(_L("Step Shift"), 			CURVE_TRANSLATE_SHIFT_STEP, 	m_bStepShift? MF_CHECKED : MF_UNCHECKED);
		//Add(_L("Step Size..."), 		CURVE_TRANSLATE_STEP_SIZE, 		m_bStepShift? MF_ENABLED : MF_DISABLED|MF_GRAYED);			///Jasmine 02/05/09 COMBINE_STEP_SIZE_IN_STEP_SHIFT_MENU
			Add(NULL, CURVE_TRANSLATE_SEPARATOR2, MF_SEPARATOR);			
		///End COUNT_STEP_BY_SAMPLING_INTERVAL
		///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION
		Add(_L("Keep Tool after Translation"), CURVE_TRANSLATE_KEEP_TOOL, m_bKeepTool? MF_CHECKED : MF_UNCHECKED);
		Add(_L("Remove"), 			CURVE_TRANSLATE_DELETE_TOOL, 	MF_ENABLED);
			Add(NULL, CURVE_TRANSLATE_SEPARATOR3, MF_SEPARATOR);			
		///End ALLOW_KEEP_TOOL_AFTER_TRANSLATION
		Add(_L("Help"), 				CURVE_TRANSLATE_HELP, 			MF_ENABLED);
	}
};

#define ct_type_var		"ct_type_var"
#define ct_uid_var		"ct_uid_var"

class TemporaryLTVar
{
public:
	TemporaryLTVar(int nType, int nPlotUID)
	{
		string strLT;
		strLT.Format("%s=%d;%s=%d;", ct_type_var, nType, ct_uid_var, nPlotUID);
		LT_execute(strLT);
	}
	~TemporaryLTVar()
	{
		string strLT;
		strLT.Format("del -v %s;del -v %s;", ct_type_var, ct_uid_var);
		LT_execute(strLT);
	}	
};

static bool get_TemporaryStorage(int& nType, int& nPlotUID)
{
	double vv;
	
	if( !LT_get_var(ct_type_var, &vv) )
		return false;
	nType = vv;
	
	if( !LT_get_var(ct_uid_var, &vv) )
		return false;
	nPlotUID = vv;
	
	return true;
}
///End GIVE_MORE_VALUE_INFO_TO_USER
static bool _move_anchor_radio_event(TreeNode& myTree, int nRow, int nCol, TreeNode& trNode, DWORD dwCntrl, int nType, WndContainer& theDlg)
{	
	if(myTree.ToValue && myTree.Index && myTree.Value)
	{
		bool bValue = myTree.ToValue.nVal;
		///Jasmine 01/06/09 v8.0993e QA80-12715 GIVE_MORE_VALUE_INFO_TO_USER
		//myTree.Index.Show = !bValue;
		//myTree.Value.Show = bValue;
		myTree.Index.Enable = bValue? ENABLE_READ_ONLY : ENABLE;
		myTree.Value.Enable = bValue? ENABLE : ENABLE_READ_ONLY;
		///End GIVE_MORE_VALUE_INFO_TO_USER
		return true;
	}
	
	return false;
}
///Jasmine 01/06/09 v8.0993e QA80-12715 GIVE_MORE_VALUE_INFO_TO_USER
static bool _move_anchor_index_event(TreeNode& myTree, int nRow, int nCol, TreeNode& trNode, DWORD dwCntrl, int nType, WndContainer& theDlg)
{
	if(myTree.Index && myTree.Index.Enable != ENABLE_READ_ONLY)
	{
		int ntype, nPlotUID;
		if( !get_TemporaryStorage(ntype, nPlotUID) )
			return false;
		
		DataPlot dp;
		dp = Project.GetObject(nPlotUID);
		if(!dp)
			return false;
		
		double x, y;
		if( dp.GetDataPoint(myTree.Index.nVal - 1, &x, &y) )
		{
			myTree.Value.dVal = ntype_vertical == ntype? y : x;
			return true;
		}
	}
	
	return false;
}
static bool _move_anchor_value_event(TreeNode& myTree, int nRow, int nCol, TreeNode& trNode, DWORD dwCntrl, int nType, WndContainer& theDlg)
{
	if(myTree.Value && myTree.Value.Enable != ENABLE_READ_ONLY)
	{
		int ntype, nPlotUID;
		if( !get_TemporaryStorage(ntype, nPlotUID) )
			return false;
		
		DataPlot dp;
		dp = Project.GetObject(nPlotUID);
		if(!dp)
			return false;
		
		int nIndex;
		if(dp.XIndex(myTree.Value.dVal, nIndex) == XIR_WITHIN_RANGE)	
		{
			myTree.Index.nVal = nIndex + 1;
			return true;
		}
		
	}
	
	return false;
}
///End GIVE_MORE_VALUE_INFO_TO_USER
/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
/*
static int get_plot_point(MarkerInfo& mi)
{
	DataPlot dp;
	mi.GetParent(dp);
	if(!dp)
	  return -1;
	
	/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
	//double dX = mi.GetX();
	//double dY = mi.GetY(); 
	double dX = 0, dY = 0;
	int nRet = mi.GetWorldXY(0, &dX, &dY);
	/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP

	fpoint fptTL(dX, dY);
	
	vector<double>	vX, vY;
	vector<int>     vIndex; 
	if( 1 > dp.GetRectPoints( fptTL, fptTL, vX, vY, &vIndex ) )	///Folger 12/19/08 QA80-12815 v8.0988c SUPPORT_GET_DATA_POINTS_BY_RECT_MADE_OF_TWO_DOUBLE_POINTS
	  return -1;
	
	double x, y;
	ASSERT( dp.GetDataPoint(vIndex[0], &x, &y) && is_equal(dX, x) && is_equal(dY, y) );
	return vIndex[0];
}
*/
/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP

///Jasmine 01/06/09 v8.0993e QA80-12715 GIVE_MORE_VALUE_INFO_TO_USER
///Jasmine 10/10/09 DATAPLOT_GETDATAPOINTS_WILL_GET_DATA_OUTSIDE_RANGE
//dMin/dMax: the minimum and maximum value 
//nFrom/nTo: the first and last row index of data
static int _get_plot_range(const DataPlot& dp, bool bVertical, double& dMin, double& dMax, int& nFrom, int& nTo)
///End DATAPLOT_GETDATAPOINTS_WILL_GET_DATA_OUTSIDE_RANGE
{
	if(!dp)
		return -1;
	
	///Jasmine 10/10/09 DATAPLOT_GETDATAPOINTS_WILL_GET_DATA_OUTSIDE_RANGE
	nFrom = 0, nTo = -1;
	//parse plot range string to get correct row range
	{
		string strRange;
		dp.GetRangeString(strRange, NTYPE_ADD_ROW_RANGE);
		
		string strBook, strSheet, strObj;
		okutil_parse_complete_range_string(strRange, &strBook, &strSheet, &strObj);
		
		string strCol1, strR1, strCol2, strR2;
		okutil_parse_range_string(strObj, &strCol1, &strR1, &strCol2, &strR2);
		
		string strRowRange;
		strRowRange.Format("[%s:%s]", strR1, strR2);
		
	    BOOL  	bAuto1 = false;
	    BOOL	bAuto2 = false;
	    int 	i1 = -1;
	    int 	i2 = -1;
	    if( okutil_parse_row_range(strRowRange, &i1, &bAuto1, &i2 , &bAuto2) )
	    {
	    	nFrom = i1;
	    	nTo = i2;
	    }
	}    
	
	vector vx, vy;
	//int nPoints = dp.GetDataPoints(0, -1, vx, vy);
	int nPoints = dp.GetDataPoints(nFrom, nTo, vx, vy);//get all points in range
	///End DATAPLOT_GETDATAPOINTS_WILL_GET_DATA_OUTSIDE_RANGE
	if(nPoints < 1)
		return nPoints;
	
	vector vData;
	vData = bVertical? vy : vx;
	if(vData.GetMinMax(dMin, dMax) <= 0)
	{
		ASSERT(0);
		return -1;
	}	
	
	return nPoints;
}
///End GIVE_MORE_VALUE_INFO_TO_USER

//exclude the datapoint whose X/Y is NANUM
static bool _get_visible_range(const GraphLayer& gl, const DataPlot& dp, double& dMin, double& dMax, int& nFirst, int& nLast, bool bVertical)
{
	double left 	= gl.X.From;
	double right 	= gl.X.To;
	double top  	= gl.Y.From;
	double bottom	= gl.Y.To;
	if(left > right)
	{
		double temp = right;
		right = left;
		left = temp;
	}
    
	if(bottom > top)
	{
		double temp = bottom;
		bottom = top;
		top = temp;
	}
	
	fpoint fptLeftTop(left, top);
	fpoint fptRightBottom(right, bottom);	
	vector vx, vy;
	vector<int> vnIndices;
	int nCount = dp.GetRectPoints(fptLeftTop, fptRightBottom, vx, vy, &vnIndices);
	if(nCount <= 0)
	{
		ASSERT(0);
		return false;
	}
	
	nFirst =	vnIndices[0];
	nLast = 	vnIndices[nCount-1];
	
	vector vData;
	vData = bVertical? vy : vx;
	if(vData.GetMinMax(dMin, dMax) <= 0)
	{
		ASSERT(0);
		return false;
	}	
	
	return true;	
}

static int move_anchor(const int& ntype, const LineInfo& li, MarkerInfo& mi, const EventInfo& ei, LineInfo& gc, const ButtonInfo& biMenu)
{	
	DataPlot dp;
	mi.GetParent(dp);
	if(!dp)
		return error_report("Fail! DataPlot is gone!");
	
	GraphLayer gl;
	dp.GetParent(gl);
	
	///Jasmine 01/06/09 v8.0993e QA80-12715 GIVE_MORE_VALUE_INFO_TO_USER
	TemporaryLTVar myVars( ntype, dp.GetUID(TRUE) );
	
	string strRange, strIndexRange;	
	double 	dMin, dMax;
	int nFirstPoint, nLastPoint;
	if( _get_visible_range(gl, dp, dMin, dMax, nFirstPoint, nLastPoint, ntype_vertical == ntype) )
	{
		strIndexRange.Format("[%d, %d]", nFirstPoint+1, nLastPoint+1);//1 = offset
		strRange.Format("[%g, %g]", dMin, dMax);
	}
	else
	{
		strIndexRange = strRange = STR_NO_DATA;
	}
	string strIndexVisibleRangeHint, strCurveVisibleRangeHint;
	strIndexVisibleRangeHint.Format("%s = %s", _L("Visible Range"), strIndexRange);
	strCurveVisibleRangeHint.Format("%s = %s", ntype_vertical == ntype? _L("Visible Y Range"):_L("Visible X Range"), strRange);
	
	if( _get_plot_range(dp, ntype_vertical == ntype, dMin, dMax, nFirstPoint, nLastPoint) > 0 )
	{
		ASSERT(nFirstPoint >= 0 && nLastPoint >= nFirstPoint);
		strIndexRange.Format("[%d, %d]", nFirstPoint+1, nLastPoint+1);//1 = offset
		strRange.Format("[%g, %g]", dMin, dMax);
	}
	else
	{
		strIndexRange = strRange = STR_NO_DATA;
	}
	string 	strIndexRangeHint, strCurveRangeHint;
	strIndexRangeHint.Format("%s = %s", _L("Index Range"), strIndexRange);
	strCurveRangeHint.Format("%s = %s", ntype_vertical == ntype? _L("Y Range"):_L("X Range"), strRange);
	///End GIVE_MORE_VALUE_INFO_TO_USER
	
	
	GETN_TREE(tr)	
	GETN_RADIO_INDEX(ToValue, 0, _L("Index|Value"))
		GETN_OPTION_DISPLAY_FORMAT(DISPLAY_EDITOR_LEFT|DISPLAY_EDITOR_HORZ)
		GETN_OPTION_EVENT_EX(_move_anchor_radio_event)
		if(ntype_vertical == ntype) GETN_READ_ONLY_COLOR		
	///Jasmine 01/06/09 v8.0993e QA80-12715 GIVE_MORE_VALUE_INFO_TO_USER
	GETN_NUM(Index, _L("Index"), 0)		GETN_EDIT_DISPLAY_WIDTH_RANGE( "30-30" )
		GETN_OPTION_EVENT_EX(_move_anchor_index_event)
	GETN_STR(hint1, "", strIndexRangeHint)			GETN_INFO	
	GETN_STR(hint12, "", strIndexVisibleRangeHint)	GETN_INFO
	GETN_STR(hint5, "", _L("0/-1 = Last point"))	GETN_INFO
	
	GETN_NUM(Value, _L("Value"), 0)		GETN_EDIT_DISPLAY_WIDTH_RANGE( "30-30" )
		GETN_OPTION_EVENT_EX(_move_anchor_value_event)
	GETN_STR(hint3, "", strCurveRangeHint)			GETN_INFO
	GETN_STR(hint4, "", strCurveVisibleRangeHint)	GETN_INFO
	///End GIVE_MORE_VALUE_INFO_TO_USER
	
	
	/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
	//int nIndex = get_plot_point(mi);		
	int nIndex = mi.GetX();
	/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
	tr.Index.nVal = nIndex + 1;//offset
	
	double x, y;
	if( dp.GetDataPoint(nIndex, &x, &y) )
		tr.Value.dVal = ntype_vertical == ntype? y : x;
	
	string strDescription = _L("Move the anchor point to the specified position by data index or value.");
	if( !GetNBox( tr, _L("Move Anchor to"), strDescription, NULL, NULL, GetWindow() ) )
		return 0;

	if(0 == tr.Index.nVal || -1 == tr.Index.nVal)		//0/-1 means last point
		tr.Index.nVal = nLastPoint+1;//offset
	
	nIndex = tr.Index.nVal - 1;//1 offset
	if(tr.ToValue.nVal)	
	{
		if(ntype_vertical == ntype)
		{
			//now no function to find nearest Y, so this not support vertical
			return 0;
		}
		else
		{
			int nRet = dp.XIndex(tr.Value.dVal, nIndex);
			///Jasmine 01/12/09 v8.0995c QA80-12715 PUT_ANCHOR_TO_EDGE_IF_OUT_OF_DATA
			if(XIR_WITHIN_RANGE != nRet)
			{
				//return 0;				
				if(XIR_LEFT_OF_RANGE == nRet)
					nIndex = nFirstPoint;
				if(XIR_RIGHT_OF_RANGE == nRet)
					nIndex = nLastPoint;
			}
			///End PUT_ANCHOR_TO_EDGE_IF_OUT_OF_DATA
			double x, y;
			dp.GetDataPoint(nIndex, &x, &y);
			bool bSame = ntype_vertical == ntype? is_equal(tr.Value.dVal, y) : is_equal(tr.Value.dVal, x);
			if(!bSame)
			{
				string strMessage;
				strMessage.Format(_L("%g is not found in curve. Anchor point is moved to %g."), tr.Value.dVal, ntype_vertical == ntype? y : x);
				MessageBox(GetWindow(), strMessage, _L("Attention!"), MB_OK);
			}
		}
	}
	
	///Jasmine 01/12/09 v8.0995c QA80-12715 PUT_ANCHOR_TO_EDGE_IF_OUT_OF_DATA
	if(nIndex < nFirstPoint)
		nIndex = nFirstPoint;
	if(nIndex > nLastPoint)
		nIndex = nLastPoint;
	///End PUT_ANCHOR_TO_EDGE_IF_OUT_OF_DATA
	
	if( !dp.GetDataPoint(nIndex, &x, &y) || is_missing_value(x) || is_missing_value(y) )
	{
		MessageBox(GetWindow(), _L("Cannot move to missing value data point."), _L("Attention!"), MB_OK);
		return 0;
	}
	
	/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
	//if( mi.SnapToData(dp, nIndex) )
	if( mi.GetX() != nIndex )
	{
		vector vx(1);
		vx[0] = nIndex;
		mi.SetXY(vx, vx);
	/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
	
		_rescale_to_show_anchor(mi);
		
		string strDisplay;
		
		/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
		//strDisplay.Format("%s = %s", STR_ANCHOR_VALUE, ftoa( ntype == ntype_vertical?mi.GetY():mi.GetX() ));
		double dMarker = get_marker_pos(mi, ntype == ntype_vertical);
		strDisplay.Format("%s = %s", STR_ANCHOR_VALUE, ftoa(dMarker));
		/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
		
		SetDataDisplayText(strDisplay);
		return OCD_MODIFY|OCD_REPAINT;
	}
	
	return 0;
}

static bool _rescale_to_show_anchor(LineInfo& mi)
{
	DataPlot dp;
	mi.GetParent(dp);
	if(!dp)
		return error_report("Fail! DataPlot is gone!");
	
	GraphLayer gl;
	dp.GetParent(gl);
	
	double left 	= gl.X.From;
	double right	= gl.X.To;
	double top 		= gl.Y.To;
	double bottom 	= gl.Y.From; 
	if(left > right)
	{
		double temp = right;
		right = left;
		left = temp;
	}
    
	if(bottom > top)
	{
		double temp = bottom;
		bottom = top;
		top = temp;
	}
	
	bool bXOut = false;
	bool bYOut = false;
	
	/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
	//double dX = mi.GetX();
	//double dY = mi.GetY(); 
	double dX = 0, dY = 0;
	int nRet = mi.GetWorldXY(0, &dX, &dY);
	/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
	
	if(dX < left || dX > right)
		bXOut = true;
	if(dY < bottom || dY > top)
		bYOut = true;
	
	if(bXOut && bYOut)
		gl.Rescale();
	else if(bXOut)
		gl.Rescale(OKAXISTYPE_Y);
	else if(bYOut)
		gl.Rescale(OKAXISTYPE_X);
	
	return bXOut || bYOut;
}

///Jasmine 01/08/09 v8.0994d QA80-12715 ADD_TARGET_LINE
static int _shift_to_tager(const int& ntype, const LineInfo& li, MarkerInfo& mi, const EventInfo& ei, LineInfo& gc, const ButtonInfo& biMenu, const LineInfo& tli)
{	
	if( !tli || !tli.GetShow() )
		return 0;
	
	DataPlot dp;
	mi.GetParent(dp);
	if(!dp)
		return error_report("Fail! DataPlot is gone!");
	
	/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
	double dX = 0, dY = 0;
	int nRet = mi.GetWorldXY(0, &dX, &dY);
	/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
	
	if(ntype_vertical == ntype)
	{
		s_dx = 0;
		/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
		//s_dy = tli.GetY() - mi.GetY();
		s_dy = tli.GetY() - dY;
		/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
	}
	else
	{			
		/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
		//s_dx = tli.GetX() - mi.GetX();
		s_dx = tli.GetX() - dX;
		/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
		s_dy = 0;
	}	

	s_bReady = true;
	s_bStepShift = false;	///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
	
	///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION	
	/*if( !_move_curve_on_end_move(li, mi, dp, ntype == ntype_vertical? true:false) )
		return error_report("_move_curve_on_end_move failed");
		
	return OCD_MODIFY_DEL;*/
	return _move_curve(li, mi, dp, ntype == ntype_vertical? true:false, s_bKeepTool);
	///End ALLOW_KEEP_TOOL_AFTER_TRANSLATION
}
///End ADD_TARGET_LINE
enum{
	SHIFT_CURVE_BY_VALUE, 
	SHIFT_CURVE_TO_VALUE, 
};

static bool _shift_value_radio_event(TreeNode& myTree, int nRow, int nCol, TreeNode& trNode, DWORD dwCntrl, int nType, WndContainer& theDlg)
{
	if(myTree.ToValue && myTree.Distance && myTree.Value)
	{
		myTree.Distance.Show = myTree.ToValue.nVal == SHIFT_CURVE_BY_VALUE;
		myTree.Value.Show = myTree.ToValue.nVal == SHIFT_CURVE_TO_VALUE;  
		return true;
	}
	
	return false;
}
///End ADD_TARGET_LINE
static int shift_value(const int& ntype, const LineInfo& li, MarkerInfo& mi, const EventInfo& ei, LineInfo& gc, const ButtonInfo& biMenu, const LineInfo& tli)
{
	///Jasmine 01/06/09 v8.0993e QA80-12715 GIVE_MORE_VALUE_INFO_TO_USER
	DataPlot dp;
	mi.GetParent(dp);
	if(!dp)
		return error_report("Fail! DataPlot is gone!");
	
	string strRange, strCurveRangeHint;
	double dMin, dMax;
	int nFrom, nTo;
	if( _get_plot_range(dp, ntype_vertical == ntype, dMin, dMax, nFrom, nTo) > 0 )
	{		
		strRange.Format("[%g, %g]", dMin, dMax);
	}
	else
	{
		strRange = STR_NO_DATA;
	}
	strCurveRangeHint.Format("%s = %s", ntype_vertical == ntype? _L("Y Range"):_L("X Range"), strRange);
	
	GETN_TREE(tr)	
	GETN_RADIO_INDEX(ToValue, 0, _L("Shift Curve by|Shift Curve to"))
		GETN_OPTION_DISPLAY_FORMAT(DISPLAY_EDITOR_LEFT|DISPLAY_EDITOR_HORZ)
		GETN_OPTION_EVENT_EX(_shift_value_radio_event)			
	GETN_NUM(Distance, _L("Value"), 0)		GETN_EDIT_DISPLAY_WIDTH_RANGE( "30-30" )
	GETN_NUM(Value, _L("Value"), 0)			GETN_EDIT_DISPLAY_WIDTH_RANGE( "30-30" )
	GETN_STR(hint, "", strCurveRangeHint)	GETN_INFO
	
	/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
	//tr.Value.dVal = ntype_vertical == ntype? mi.GetY() : mi.GetX();	
	double dMarkerX = 0, dMarkerY = 0;
	mi.GetWorldXY(0, &dMarkerX, &dMarkerY);
	tr.Value.dVal = ntype_vertical == ntype ? dMarkerY : dMarkerX;	
	/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP

	string strAnchor;
	strAnchor.Format("%s = %s", STR_ANCHOR_VALUE, ftoa(tr.Value.dVal));
	GETN_STR(hint2, "", strAnchor)			GETN_INFO
	
				
	string strDescription = _L("Shfit the curve such that Anchor line will be moved to specified position");	
	//if( !GetNBox( tr, _L("Shift Curve"), NULL, NULL, NULL, GetWindow() ) )
	if( !GetNBox( tr, _L("Shift Curve"), strDescription, NULL, NULL, GetWindow() ) )
		return 0;
	///End GIVE_MORE_VALUE_INFO_TO_USER
	
	switch(tr.ToValue.nVal)
	{
	case SHIFT_CURVE_BY_VALUE:
		
		if(ntype_vertical == ntype)
		{
			s_dx = 0;
			s_dy = tr.Distance.dVal;
		}
		else
		{
			s_dx = tr.Distance.dVal;			
			s_dy = 0;
		}
		break;
		
	case SHIFT_CURVE_TO_VALUE:
	
		if(ntype_vertical == ntype)
		{
			s_dx = 0;
			/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
			//double yy = mi.GetY();
			//s_dy = tr.Value.dVal - yy;
			s_dy = tr.Value.dVal - dMarkerY;
			/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
		}
		else
		{			
			/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
			//double xx = mi.GetX();
			//s_dx = tr.Value.dVal - xx;
			s_dx = tr.Value.dVal - dMarkerX;
			/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
			s_dy = 0;
		}		
		break;
		
	default:
		return 0;
	}
	
	s_bReady = true;
	s_bStepShift = false;	///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
	///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION	
	/*if( !_move_curve_on_end_move(li, mi, dp, ntype == ntype_vertical? true:false) )
		return error_report("_move_curve_on_end_move failed");
		
	return OCD_MODIFY_DEL;*/
	return _move_curve(li, mi, dp, ntype == ntype_vertical? true:false, s_bKeepTool);
	///End ALLOW_KEEP_TOOL_AFTER_TRANSLATION
}
///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
static int show_shift_step_box(const int& ntype, const LineInfo& li, MarkerInfo& mi, const EventInfo& ei, LineInfo& gc, const ButtonInfo& biMenu)
{
	bool 	bSamplingInterval = false;
	double 	dInc;
	
	if( ntype != ntype_vertical )
		//&& is_missing_value(s_dStep) )  
	{
		DataPlot dp;
		mi.GetParent(dp);
		if(!dp)
			return error_report("Fail! DataPlot is gone!");

		///Jasmine 07/30/09 QA80-14031 USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
		//Column colX, colY;
		//if( CER_NO_X_COL == get_plot_columns(dp, colX, colY) && colY.IsValid() )
		if(_has_x(dp) == CER_NO_X_COL)
		{
			XYRange dr;
			dp.GetDataRange(dr);
			
			bSamplingInterval = true;
			
			double dX0;
			//colY.IsEvenSampling(&dX0, &dInc);
			dr.IsEvenSampling(0, &dX0, &dInc);
		}
		///End USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
	}
	
	if(!bSamplingInterval)
		s_bUseSamplingInterval = false;
///Jasmine 01/21/09 QA80-12715 OPTION_OF_USE_SAMPLING_INTERVAL
	GETN_TREE(tr)	
	
	///Jasmine 02/05/09 COMBINE_STEP_SIZE_IN_STEP_SHIFT_MENU
	GETN_CHECK(StepShift, _L("Step Shift"), s_bStepShift)
		GETN_OPTION_DISPLAY_FORMAT(DISPLAY_EDITOR_CHECK_ON_LEFT)
		GETN_OPTION_EVENT_EX(_shift_step_check_event)	
	///End COMBINE_STEP_SIZE_IN_STEP_SHIFT_MENU
	
	GETN_CHECK(UseSampling, _L("Use Sampling Interval"), s_bUseSamplingInterval)
		GETN_OPTION_DISPLAY_FORMAT(DISPLAY_EDITOR_CHECK_ON_LEFT)
		GETN_CURRENT_SUBNODE.Show = bSamplingInterval;
		GETN_OPTION_EVENT_EX(_use_sampling_interval_event)	
			
	GETN_NUM(Step, _L("Step"), is_missing_value(s_dStep)? 0 : s_dStep)
		GETN_EDIT_DISPLAY_WIDTH_RANGE( "20-20" )
		GETN_CURRENT_SUBNODE.Show = tr.UseSampling.nVal == false;
	
	GETN_STR(hint1, _L("Step"), _L("= N * Sampling Interval")) GETN_INFO
		GETN_CURRENT_SUBNODE.Show = tr.UseSampling.nVal == true;

	GETN_COMBO(N, _L("N"), s_n, "|1|2|3|4|5|6")
		GETN_EDIT_DISPLAY_WIDTH_RANGE( "20-20" )
		GETN_CURRENT_SUBNODE.Show = bSamplingInterval;
		GETN_CURRENT_SUBNODE.Enable = tr.UseSampling.nVal == true;
			
	string strHint;
	strHint.Format(_L("Sampling Interval = %g"), dInc);
	GETN_STR(hint2, "", strHint) GETN_INFO
		GETN_CURRENT_SUBNODE.Show = bSamplingInterval;
	
	
	string strDescription = _L("Set the step size on each translation.");
	if( !GetNBox( tr, _L("Step Shift"), strDescription, NULL, NULL, GetWindow() ) )
		return 0;
	
	///Jasmine 02/05/09 COMBINE_STEP_SIZE_IN_STEP_SHIFT_MENU
	s_bStepShift = tr.StepShift.nVal;
	///End COMBINE_STEP_SIZE_IN_STEP_SHIFT_MENU
	
	if(tr.UseSampling.nVal == true)
	{
		s_n = tr.N.nVal;
		s_dStep = s_n * dInc;//N * Sampling Interval
	}
	else
		s_dStep = tr.Step.dVal;
	
	s_bUseSamplingInterval = tr.UseSampling.nVal;
	
	return 0;
}

static bool _shift_step_check_event(TreeNode& myTree, int nRow, int nCol, TreeNode& trNode, DWORD dwCntrl, int nType, WndContainer& theDlg)
{
	if(myTree.StepShift)
	{
		foreach(TreeNode trNode in myTree.Children)
		{
			myTree.UseSampling.Enable 	= 
			myTree.Step.Enable 			= 
			myTree.N.Enable 			= myTree.StepShift.nVal;
		}
		
		if(!(dwCntrl & GETNEVENT_ON_INIT) && myTree.StepShift.nVal)
			_use_sampling_interval_event(myTree);
		
		return true;
	}
	
	return false;
}
static bool _use_sampling_interval_event(TreeNode& myTree, int nRow = -1, int nCol = -1, TreeNode& trNode = NULL, DWORD dwCntrl = 0, int nType = 0, WndContainer& theDlg = NULL)
{	
	if(myTree.UseSampling && myTree.UseSampling.Show != false && myTree.Step && myTree.hint1 && myTree.N)
	{
		bool bSampling = myTree.UseSampling.nVal;
		
		myTree.Step.Show = !bSampling;
		myTree.hint1.Show = bSampling;
		myTree.N.Enable = bSampling;
		
		return true;
	}
	
	return false;
}
///End OPTION_OF_USE_SAMPLING_INTERVAL
///End COUNT_STEP_BY_SAMPLING_INTERVAL
///End ADD_BUTTON_FOR_POP_UP_MENU

///Jasmine 01/08/09 v8.0994d QA80-12715 ADD_TARGET_LINE
///Jasmine 01/08/09 GET_NEAREST_BUT_NOT_SO_CLOSE_POINT
static int _get_nearest_value_point(MarkerInfo& mi, bool bShiftY)
{
	DataPlot dp;
	mi.GetParent(dp);
	if(!dp)
		return -1;
	
	GraphLayer gl;
	dp.GetParent(gl);
	double left 	= gl.X.From;
	double right	= gl.X.To;
	double top 		= gl.Y.To;
	double bottom 	= gl.Y.From; 
	
	if(left > right)
	{
		double temp = right;
		right = left;
		left = temp;
	}
    
	if(bottom > top)
	{
		double temp = bottom;
		bottom = top;
		top = temp;
	}
	
	fpoint fptTL(left,top);
	fpoint fptBR(right,bottom);
	vector<double>	vx, vy;
	vector<int>     vIndex; 
	int nSize = dp.GetRectPoints( fptTL, fptBR, vx, vy, &vIndex );
	if(1 > nSize)
		return -1;
	if(nSize == 1)
		return 0;
	
	vector vData;
	if(bShiftY)
		vData = vy;
	else 
		vData = vx;
	
	//sort : so that we can get a nearest data by index
	vector<uint> 	vnIndeces;
	vData.Sort(SORT_ASCENDING, TRUE, vnIndeces); 
	
	vector<uint> 	vnFinds;

	/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
	//double dd = bShiftY? mi.GetY() : mi.GetX();
	double dd = get_marker_pos(mi, bShiftY);
	/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
	
	if(vData.Find(vnFinds, dd) < 1)
		return -1;
	
	int nFind = vnFinds[0];
	int nOffset = nSize/10;
	if(0 == nOffset)
		nOffset = 1;
	int nNearest = nFind - nOffset;
	if(nNearest < 0)
		nNearest = nFind + nOffset;
	
	int nRet = vnIndeces[nNearest];
	if(nRet < 0 || nRet >= nSize)
		return -1;
	return vIndex[nRet];
}
///End GET_NEAREST_BUT_NOT_SO_CLOSE_POINT
///End ADD_TARGET_LINE

/// YuI 02/14/08 QA70-10913 CURVE_TRANSLATE_TO_RESPECT_DATA_MARKERS
static BOOL	_get_data_plot_i1_i2(DataPlot& dp, int& i1, int& i2)
{
	i1 = 0;
	i2 = -1;
	vector<int> vBeginMarkers;
	vector<int> vEndMarkers;
	if( dp.GetDataMarkers(vBeginMarkers, vEndMarkers) > 0 && vBeginMarkers.GetSize() > 0 && vEndMarkers.GetSize() > 0 )
	{
		i1 = vBeginMarkers[0];
		i2 = vEndMarkers[0];
		
		return TRUE;
	}
	
	return FALSE;
}
/// end CURVE_TRANSLATE_TO_RESPECT_DATA_MARKERS

///Jasmine 12/02/08 v8.0981d QA80-12367 TRANSLATE_ALL_MARKERS
static int _get_data_plot_segment(DataPlot& dp, vector<int>& vBegin, vector<int>& vEnd)
{
	if( dp.GetDataMarkers(vBegin, vEnd) > 0 )
	{
		ASSERT(vBegin.GetSize() == vEnd.GetSize());
		return vBegin.GetSize();
	}
	else
	{
		vBegin.SetSize(1);
		vBegin[0] = 0;
		vEnd.SetSize(1);
		vEnd[0] = -1;
		return 1;
	}
}
///End TRANSLATE_ALL_MARKERS

/// YuI 02/03/09 QA70-13053 IMPROVEMENTS_TO_BUTTONIZE_OBJECT_DRAWING
//	static void _ghost_curve_end_move(LineInfo& gc, DataPlot dp)
//	{
//		gc.SetShow(false);
//	}

static BOOL _ghost_curve_end_move(LineInfo& gc, DataPlot dp)
{
	if( !gc.GetShow() )
		return FALSE;
	
	gc.SetShow(false);
	return TRUE;
}
/// end IMPROVEMENTS_TO_BUTTONIZE_OBJECT_DRAWING

///Kyle 01/12/2009 v8.0995c MOVE_DATAPLOT_APPARENTLY
static void _move_vector_apparently(vector& vec, double dBase, double dInc, int ScaleType)
{
	if(ScaleType != LINEAR_SPACE)
	{
		double dxReal = real_space(dBase + dInc, ScaleType) - real_space(dBase, ScaleType);
		vec = real_space(vec, ScaleType);
		vec += dxReal;
		vec = real_inv_space(vec, ScaleType);
	}
	else
		vec += dInc;
}
///End MOVE_DATAPLOT_APPARENTLY

static void _ghost_curve_moving(LineInfo& gc, DataPlot& dp, LineInfo& li, MarkerInfo& mi, bool bHorizontal)
{
	vector vx, vy;
	if(!gc.GetShow()) // if not shown, then first time
	{
		TreeNode trUserData = gc.GetUserData(true);
		if(trUserData)
		{
			trUserData.Created.nVal = 0;
			
			///Jasmine 01/15/09 GET_FROM_TO_INDEX_BY_VISIBLE_RANGE
			GraphLayer gl;
			dp.GetParent(gl);
			
			double left 	= gl.X.From;
			double right	= gl.X.To;
			if(left > right)
			{
				double temp = right;
				right = left;
				left = temp;
			}
			double dWidth = right - left;
			
			int nLeft, nRight;
			dp.XIndex(left - dWidth, nLeft);
			dp.XIndex(right + dWidth, nRight);
			//must keep nLeft > nRight
			if(nLeft > nRight)
			{
				int nn = nRight;
				nRight = nLeft;
				nLeft = nn;
			}
			///End GET_FROM_TO_INDEX_BY_VISIBLE_RANGE
			
			/// YuI 02/14/08 QA70-10913 CURVE_TRANSLATE_TO_RESPECT_DATA_MARKERS
			//	if(get_plot_data_xy(dp, vx, vy))
			///Jasmine 12/02/08 v8.0981d QA80-12367 TRANSLATE_ALL_MARKERS
			//int i1;
			//int i2;
			//_get_data_plot_i1_i2(dp, i1, i2);
			//if( get_plot_data_xy(dp, vx, vy, i1, i2) )
			vector<int> vBegin, vEnd;
			int nCount = _get_data_plot_segment(dp, vBegin, vEnd);
			for(int ii = 0; ii < nCount; ii++)
			{
				///Jasmine 01/15/09 GET_FROM_TO_INDEX_BY_VISIBLE_RANGE
				if(vBegin[ii] < nLeft)
					vBegin[ii] = nLeft;
				if(vEnd[ii] == -1 || vEnd[ii] > nRight)
					vEnd[ii] = nRight;
				if( vBegin[ii] > vEnd[ii] )
					continue;
				///End GET_FROM_TO_INDEX_BY_VISIBLE_RANGE
				
				vector vSubX, vSubY;
				///Jasmine 01/12/09 v8.0995c QA80-12852 REDUCE_POINT_TO_SPEED_UP
				//if( get_plot_data_xy(dp, vSubX, vSubY, vBegin[ii], vEnd[ii]) )
				int nPoints = dp.GetDataPoints(vBegin[ii], vEnd[ii], vSubX, vSubY, NULL, NULL, RD_LOCAL_EXTREME | GETDATAPTS_RD_LARGEST_TRIANGLE);	///Jasmine 01/14/09 QA80-12852 REDUCE_POINT_NEED_SPECIFY_METHOD
				
				if( 0 < nPoints )
				///End REDUCE_POINT_TO_SPEED_UP
				{
					vx.Append(vSubX);
					vy.Append(vSubY);
				}
			}

			// RVD 7/2/2009 qa70-13499 GARBAGE_GC_EVERY_2ND_MOVING
			//if(vx.GetSize() > 0)
			if( vx.GetSize() > 1 && vx.GetSize() == vy.GetSize() )
			// end GARBAGE_GC_EVERY_2ND_MOVING
			///End TRANSLATE_ALL_MARKERS			
			/// end CURVE_TRANSLATE_TO_RESPECT_DATA_MARKERS
			{
				trUserData.Data.X.dVals = vx;
				trUserData.Data.Y.dVals = vy;
				trUserData.Created.nVal = 1;
			}
		}
	}
	TreeNode trUserData = gc.GetUserData(false);
	if(trUserData.Created)
	{
		if(trUserData.Created.nVal == 0)
			return;// no data to draw
		double dx, dy;
		GraphLayer gly;
		dp.GetParent(gly);
		if(get_distance(li, mi, 0, &dx, &dy, gly))
		{
			gc.SetShow(true);
			TreeNode tr = gc;
			vx = trUserData.Data.X.dVals;
			vy = trUserData.Data.Y.dVals;
			//----- CPY 11/29/07 QA70-10509 FAILED_MOVING_DATA_AT_END_OF_MOVE
			s_dx = dx, s_dy = dy;
			s_bReady = true;
			//-----
			
			//out_double("dy = ", dy);
			string strText;
			/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
			double dMarkerX = 0, dMarkerY = 0;
			mi.GetWorldXY(0, &dMarkerX, &dMarkerY);
			/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
			if(bHorizontal)
			{
				///Kyle 01/12/2009 v8.0995c MOVE_DATAPLOT_APPARENTLY
				//vx += dx;
				int nXScaleType;
				axis_get_scale_type(gly, &nXScaleType);
				/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
				//_move_vector_apparently(vx, mi.GetX(), dx, nXScaleType);
				_move_vector_apparently(vx, dMarkerX, dx, nXScaleType);
				/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
				///End MOVE_DATAPLOT_APPARENTLY
				strText = (string)"dx = " + ftoa(dx);
			}
			else
			{
				///Kyle 01/12/2009 v8.0995c MOVE_DATAPLOT_APPARENTLY
				//vy += dy;
				int nYScaleType;
				axis_get_scale_type(gly, NULL, &nYScaleType);
				/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
				//_move_vector_apparently(vy, mi.GetY(), dy, nYScaleType);
				_move_vector_apparently(vy, dMarkerY, dy, nYScaleType);
				/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
				///End MOVE_DATAPLOT_APPARENTLY
				strText = (string)"dy = " + ftoa(dy);
			}
			
			///Jasmine 01/09/09 v8.0995 QA80-12715 SHOW_VALUE_IN_DATA_DISPLAY_BAR
			///Jasmine 01/21/09 QA80-12715 PUT_TEXT_IN_DIFFERENT_ROW_SO_FONT_CAN_BIGGER
			double distance = bHorizontal? dx : dy;	
			
			if(s_bStepShift && s_dStep && !is_missing_value(s_dStep))
				distance = nint( distance/s_dStep ) * s_dStep;
			
			/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
			//double dCurrent = bHorizontal? mi.GetX() : mi.GetY();
			double dCurrent = bHorizontal? dMarkerX : dMarkerY;
			/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
			double dRetData = dCurrent + distance;
			string strAnchor, strTarget;
			strAnchor.Format( "%s = %s", STR_ANCHOR_VALUE, 								ftoa(dCurrent) );
			strTarget.Format( "%s = %s", bHorizontal? _L("Target X") : _L("Target Y"), 	ftoa(dRetData) );
			strText += NEXT_LINE + strAnchor + NEXT_LINE + strTarget;
			///End PUT_TEXT_IN_DIFFERENT_ROW_SO_FONT_CAN_BIGGER
			///End SHOW_VALUE_IN_DATA_DISPLAY_BAR			
			SetDataDisplayText(strText);
			
			tr.Data.X.dVals = vx;
			tr.Data.Y.dVals = vy;
		}
	}
}

/*
static void  _drag_anchor_dx(MarkerInfo& obj, double dx)
{
	TreeNode tr = obj;
	vector vx;
	
	vx = tr.Data.X.dVals;
	tr.Data.X.dVals = vx + dx;

}
*/

///Jasmine 11/27/08 v8.0980d QA80-12367 SNAP_TO_MIDDLE_POINT_IN_DATAPLOT_MAY_OUT_OF_VIEW
///Jasmine 01/06/09 v8.0993e QA80-12715 GIVE_MORE_VALUE_INFO_TO_USER
static int _get_dataplot_points_in_view(const DataPlot& dp, vector<double>& vX, vector<double>& vY, vector<int>& vIndex)
{
	if(!dp || NULL == vX || NULL == vY || NULL == vIndex)
		return -1;
	
	GraphLayer gl;
	dp.GetParent(gl);
	
	double left, right, top, bottom;
	left 	= gl.X.From;
	right	= gl.X.To;
	top 	= gl.Y.To;
	bottom 	= gl.Y.From; 
	
	if(left > right)
	{
		double temp = right;
		right = left;
		left = temp;
	}
    
	if(bottom > top)
	{
		double temp = bottom;
		bottom = top;
		top = temp;
	}
	
#ifdef _DEBUG_CURVE_TRANSLATE
	printf("left=%f; top=%f; right=%f; bottom=%f\n", left, top, right, bottom);
#endif//_DEBUG_CURVE_TRANSLATE	

	//if( crv.HasX() )//#12683, GetRectPoints crash if curve has no X  
	//{
		fpoint fptTL(left,top);
		fpoint fptBR(right,bottom);
		///Kyle 12/24/2008 CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
		//num = crv.GetRectPoints( fptTL, fptBR, vX, vY, vIndex );
		int num = dp.GetRectPoints( fptTL, fptBR, vX, vY, &vIndex );
		///End CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
#ifdef _DEBUG_CURVE_TRANSLATE
		for(int ii = 0; ii < num; ii++)
		{
			printf("point%d\t=(%f,\t%f)\n", vIndex[ii], vX[ii], vY[ii]);
		}
#endif//_DEBUG_CURVE_TRANSLATE
	//}
	return num;
}

static int _get_mid_value(const DataPlot& dp, int nFrom, int nTo, bool bVertical)
{
	if(nFrom > nTo)
	{
		ASSERT(0);
		return -1;
	}
	
	vector vx, vy;
	if(dp.GetDataPoints(nFrom, nTo, vx, vy) <= 0)
		return -1;
	
	double dLower = bVertical? vy[0] : vx[0];
	double dUpper = bVertical? vy[vy.GetSize()-1] : vx[vx.GetSize()-1];
	if( is_missing_value(dLower) || is_missing_value(dUpper) )
	{
		ASSERT(0);
		return -1;
	}
	if(dLower > dUpper)
	{
		double dTemp = dUpper;
		dUpper = dLower;
		dLower = dTemp;
	}
	
	//exclude missing value no matter it's in vx or xy
	vx += vy*0;
	vy += vx*0;
	
	vector vData;
	vData = bVertical? vy : vx;
	
	vector<uint> vnSort;
	if( !vData.Sort(SORT_ASCENDING, TRUE, vnSort) )
		return -1;
	
	vector<uint> vnFind;
	int nFind = vData.Find(vnFind, dLower, dUpper);
	if(nFind <= 0)
		return -1;
	
	int nMin = vnFind[0];
	int nMax = vnFind[nFind-1];
	int nMedianAfterSort = nMin + (nMax - nMin)/2;	
	int nMedianInPlot = nFrom + vnSort[nMedianAfterSort];
	
	return nMedianInPlot;
}

static int _get_dataplot_mid_point_in_view(const DataPlot& dp, bool bVertical)
{
	///Kyle 12/24/2008 CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
	//Curve crv(dp);
	//if(!crv)
		//return -1;
	///End CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
			
	vector<double> 	vX, vY;
	vector<int> 	vIndex; 
	int num = _get_dataplot_points_in_view(dp, vX, vY, vIndex);
///End GIVE_MORE_VALUE_INFO_TO_USER
	vector<int> vBeginMarkers, vEndMarkers;
	int	nMarker = dp.GetDataMarkers(vBeginMarkers, vEndMarkers);		
#ifdef _DEBUG_CURVE_TRANSLATE
	for(int ii = 0; ii < nMarker; ii++)
		printf("Markers%d:%d~%d\n", ii, vBeginMarkers[ii], vEndMarkers[ii]);
#endif//_DEBUG_CURVE_TRANSLATE

	
	int 	nFrom, nTo;
	if(nMarker > 0)
	{
		nFrom 	= vBeginMarkers[0];
		nTo 	= vEndMarkers[0];

		vector<uint> vnFind;
		int nFind = vIndex.Find(vnFind, nFrom, nTo); 
		if(nFind > 0)
		{
			int nFirst 	= vnFind[0];
			int nLast 	= vnFind[nFind-1];
			nFrom 	= vIndex[nFirst];
			nTo 	= vIndex[nLast];
		}
	}
	else if(num > 0)
	{
		nFrom 	= vIndex[0];
		nTo 	= vIndex[num-1];		
	}
	else//no marker, and all data is out of scale range
		return -1;
	
	return _get_mid_value(dp, nFrom, nTo, bVertical);
	
}
///End SNAP_TO_MIDDLE_POINT_IN_DATAPLOT_MAY_OUT_OF_VIEW

///Jasmine 11/25/08 v8.0979 QA80-12367 ALLOW_X_IS_SAMPLING_INTERVAL
static bool _set_even_sampling(DataPlot& dp, double dy)//int dy	//---Jasmine 12/19/08 v8.0988c no reason for dy being an integer
{
	///Jasmine 12/04/08 v8.0982b GET_PLOT_COLUMNS
	Column colX, colY;
	///Jasmine 07/30/09 QA80-14031 USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
	//if(CER_NO_X_COL != get_plot_columns(dp, colX, colY) || !colY)
	get_plot_columns(dp, colX, colY);
	
	if(_has_x(dp) != CER_NO_X_COL)
		return false;
	///End GET_PLOT_COLUMNS
	
	double dX0, dInc;
	//colY.IsEvenSampling(&dX0, &dInc);
	XYRange dr;
	dp.GetDataRange(dr);
	dr.IsEvenSampling(0, &dX0, &dInc);
	dX0 += dy;
	//if( colY.SetEvenSampling(dX0, dInc, NULL, NULL, NULL, TRUE) )
	if( dr.SetEvenSampling(dX0, dInc, 0, NULL, NULL, NULL, TRUE) )
	{
		if(colY)
		///End USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
		{
			Worksheet wks;
			colY.GetParent(wks);
			if( !wks.IsLabelTypeShown(RCLT_SAMPLE_RATE) )
			{
				wks.CheckAddLabelByType(RCLT_SAMPLE_RATE);
				wks.AutoSizeRow(RCLT_SAMPLE_RATE); 
			}
		}
		
		GraphLayer gly;
		dp.GetParent(gly);
		gly.GetPage().Refresh(true); //update axis title
		
		return true;
	}
	
	return false;
}
///End ALLOW_X_IS_SAMPLING_INTERVAL

//------- CPY 11/29/07 QA70-10509 FAILED_MOVING_DATA_AT_END_OF_MOVE
static bool _move_curve_on_end_move(LineInfo& li, MarkerInfo& mi, DataPlot& dp, bool bVertical)
{
	if(!dp)
		return error_report("_move_curve_on_end_move found no dp");
		
	GraphLayer gly;
	dp.GetParent(gly);
	GraphPage gp = gly.GetPage();
	
	if(s_bReady) // get lastest as regetting is not working reliablely, sometimes work and some times not
	{
		s_bReady = false;//----- CPY 11/29/07 QA70-10509 FAILED_MOVING_DATA_AT_END_OF_MOVE
		///Jasmine 12/02/08 v8.0981d QA80-12367 TRANSLATE_ALL_MARKERS
		int 	nRet 	= 0;
		DWORD 	dwCntrl = DRS_FORCE_UNDO | DRS_KEEP_LARGER_DEST_SIZE;
		double 	dy		= bVertical? s_dy : s_dx;
		///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
		bool	bSamplingInterval = false;		
		///Jasmine 11/25/08 v8.0979 QA80-12367 ALLOW_X_IS_SAMPLING_INTERVAL
		///Kyle 12/24/2008 CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
		//Curve cv(dp);
		//if(cv && !cv.HasX())
		///Jasmine 07/30/09 QA80-14031 USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
		//Column colX, colY;
		//int nErrCode = get_plot_columns(dp, colX, colY);
		int nErrCode = _has_x(dp);
		///End USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
		ASSERT(nErrCode != -1);
		if(!bVertical && nErrCode == CER_NO_X_COL)
			bSamplingInterval = true;		
		
		if( s_bStepShift )
		{
			if( s_dStep && !is_missing_value(s_dStep) )
			{
				dy = nint( dy/s_dStep ) * s_dStep;
				
				string strDisplay;
				strDisplay.Format("%s = %s", bVertical?"dy":"dx", ftoa(dy));
				SetDataDisplayText(strDisplay);
			}
		}
		///End COUNT_STEP_BY_SAMPLING_INTERVAL
		
		/// YuI 02/14/08 QA70-10913 CURVE_TRANSLATE_TO_RESPECT_DATA_MARKERS
		//	if(!dp.GetDataRange(dr))
		int i1;
		int i2;
		//_get_data_plot_i1_i2(dp, i1, i2);
		vector<int> vBegin, vEnd;
		int nCount = _get_data_plot_segment(dp, vBegin, vEnd);		
		for(int ii = 0; ii < nCount; ii++)
		{
			XYRange dr;
			
			i1 = vBegin[ii];
			i2 = vEnd[ii];
			if( !dp.GetDataRange(dr, i1, i2) )
			/// end CURVE_TRANSLATE_TO_RESPECT_DATA_MARKERS
				//return error_report("case dp failed GetDataRange");	
			{
				error_report("case dp failed GetDataRange " + i1 + ":" + i2);	
				continue;
			}
			
			vector vx, vy;
			///Jasmine 12/29/08 v8.0991b QA80-12367 KEEP_MISSING_VALUE_IN_PLOT
			//dr.GetData(vy, vx);DRR_GET_MISSING
			dr.GetData(vy, vx, NULL, 0, DRR_BAD_WEIGHT_TREATMENT | DRR_GET_MISSING);
			///End KEEP_MISSING_VALUE_IN_PLOT
			/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
			double dMarkerX = 0, dMarkerY = 0;
			int nRes = mi.GetWorldXY(0, &dMarkerX, &dMarkerY);
			/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
			bool bRet;
			if(bVertical)
			{
				///Kyle 01/12/2009 v8.0995c MOVE_DATAPLOT_APPARENTLY
				//vy += dy;
				int nYScaleType;
				axis_get_scale_type(gly, NULL, &nYScaleType);
				/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
				//_move_vector_apparently(vy, mi.GetY(), dy, nYScaleType);
				_move_vector_apparently(vy, dMarkerY, dy, nYScaleType);
				/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
				///End MOVE_DATAPLOT_APPARENTLY
				bRet = dr.SetData(&vy, NULL, 0, NULL, dwCntrl);
			}
			else
			{
				if(bSamplingInterval)	///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
				///End CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
				{	
					if( !_set_even_sampling(dp, dy) )
						error_report("SetEvenSampling failed");
					else
						nRet = nCount;
					
					break;
				}
				///End ALLOW_X_IS_SAMPLING_INTERVAL
				else
				{
					///Kyle 01/12/2009 v8.0995c MOVE_DATAPLOT_APPARENTLY
					//vx += dy;
					int nXScaleType;
					axis_get_scale_type(gly, &nXScaleType);
					/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
					//_move_vector_apparently(vx, mi.GetX(), dy, nXScaleType);
					_move_vector_apparently(vx, dMarkerX, dy, nXScaleType);
					/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
					///End MOVE_DATAPLOT_APPARENTLY
					bRet = dr.SetData(NULL, &vx, 0, NULL, dwCntrl);
				}
			}
			
			if(!bRet)
				error_report("dr.SetData " + i1 + ":" + i2 + " failed");
			else
				nRet++;
		}
		///End TRANSLATE_ALL_MARKERS
		if(nRet == nCount)
		{		
			string strDir = bVertical? "vertically" : "horizontally";
			///Jasmine 09/23/09 GRAPH_LEGEND_IS_COL_COMMENT_AND_BETTER_NOT_USE_IN_COMMENT_AGAIN
			//string strLegend;dp.GetLegend(strLegend, NULL, false);
			//string str;
			//str.Format("curve translate:%s%s is moved %s by %g", NEXT_LINE, strLegend, strDir, dy);
			//Project.OutStringToResultsLog(str, true, gp.GetName());
			string strColName, strComment;
			///Jasmine 02/04/09 OUTPUT_RESULT_TO_COLUMN_COMMENTS
			Column colX, colY, colTemp;
			int nErrCode = get_plot_columns(dp, colX, colY);
			if(colY && (CER_NO_ERROR == nErrCode || CER_NO_X_COL == nErrCode))
			{
				if(bVertical || CER_NO_X_COL == nErrCode)
				{
					//colY.SetComments(str, TRUE); /// YuI 02/27/09 QA70-13194 SET_OUTPUT_COMMENT_MUST_BE_UNDOABLE (added TRUE)
					colTemp = colY;
				}
				else
				{
					//colX.SetComments(str, TRUE); /// YuI 02/27/09 QA70-13194 SET_OUTPUT_COMMENT_MUST_BE_UNDOABLE (added TRUE)
					colTemp = colX;
				}
				
				strColName = get_column_name(colTemp);
				strComment.Format("curve translate:%s%s is moved %s by %g", NEXT_LINE, strColName, strDir, dy);
				colTemp.SetComments(strComment, TRUE); 
				
				Worksheet wks;
				colY.GetParent(wks);
				if( !wks.IsLabelTypeShown(RCLT_COMMENT) )
					wks.CheckAddLabelByType(RCLT_COMMENT);
				wks.AutoSizeRow(RCLT_COMMENT); 
				
			}
			///End OUT_PUT_RESULT_TO_COLUMN_COMMENTS
			
			Project.OutStringToResultsLog(strComment, true, gp.GetName());
			///End GRAPH_LEGEND_IS_COL_COMMENT_AND_BETTER_NOT_USE_IN_COMMENT_AGAIN
			return true;
		}
		
	}
	return false;
}
///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION	
static int _move_curve(LineInfo& li, MarkerInfo& mi, DataPlot& dp, bool bVertical, bool bKeepTool)
{
	if(!_move_curve_on_end_move(li, mi, dp, bVertical))
		error_report("_move_curve_on_end_move failed");
			
	if(bKeepTool)
		return OCD_MODIFY|OCD_REPAINT;
	
	return OCD_MODIFY_DEL; // to indicate Anchor should be removed, together with all associated objects
}
///End ALLOW_KEEP_TOOL_AFTER_TRANSLATION
/// RVD 7/25/2008 v8.0909 qa70-11782 CURVE_TRANSLATE_LOCK_DATA
static bool	IsDestroyOnInit(DataPlot& dp, bool bVertical)
{
	if( !dp )
	{
		error_report("Invalid data plot");
		return true;
	}
	///Kyle 12/24/2008 CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
	//Curve cv(dp);
//
	//if( !cv )
	//{
		//error_report("Curve can not be obtained from data plot");
		//return true;
	//}
	///End CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
	
	///Jasmine 07/30/09 QA80-14031 CAN_NOT_CHANGE_FUNCTION_PLOT_DATA
	string strDataset = dp.GetDatasetName();
	DWORD dwInfo = Project.GetDatasetInfo(strDataset);
	if(dwInfo & PGDN_FUNCTION)
	{
		warning_msg_box(XFERR_FUNC_PLOT_NOT_TRANSLATE, true, 'E');
		return true;
	}
	///End CAN_NOT_CHANGE_FUNCTION_PLOT_DATA
	
	int nLock = 0;
	string strName;	

	if( bVertical )
	{
		///Kyle 12/24/2008 CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
		//cv.GetName(strName);
		strName = dp.GetDatasetName();
		///End CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
		
		DatasetObject ds(strName);
		nLock = ds.GetLock(OAM_DATA);
	}
	else
	{
		///Kyle 12/24/2008 CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
		//if( !cv.HasX(strName) )
		//{
			/////Jasmine 11/25/08 v8.0979 QA80-12367 ALLOW_X_IS_SAMPLING_INTERVAL
			//Column colX, colY;
			//if(CER_NO_X_COL != get_plot_columns(dp, colX, colY) || !colY)
			//{
				//error_report("No appropriate X data to translate", TRUE);
				//return true;
			//}
			/////End ALLOW_X_IS_SAMPLING_INTERVAL
		//}
		///Jasmine 07/30/09 QA80-14031 USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
		bool bOK = false;
		if(dwInfo & PGDN_LOOSE)
			bOK = true;
		else
		{
			Column colX, colY;
			int nErrCode = get_plot_columns(dp, colX, colY);
			if(CER_NO_ERROR == nErrCode || CER_NO_X_COL == nErrCode)
				bOK = colY.IsValid();
		}

		//if(CER_NO_ERROR != nErrCode)
		//{
			//if(CER_NO_X_COL != nErrCode || !colY)
			//{
		if(!bOK)
		{
				//error_report("No appropriate X data to translate", TRUE);
				warning_msg_box(XFERR_NO_DATA_TRANSLATE, true, 'E');
				return true;
		}	
		///End CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
		else
		{
			///Kyle 12/24/2008 CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
			//DatasetObject ds(strName);
			//DatasetObject ds(colX);
			///End CURVE_CONSTRUCTOR_FROM_DATAPLOT_WITH_DATATYPE_OF_Y_IS_FLOAT_WILL_FAIL
			string 	strY, strX;
			if(_has_x(dp, &strX) == CER_NO_ERROR)
			{
				DatasetObject ds(strX);
				nLock = ds.GetLock(OAM_DATA);
			}
		}
		///End USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET	
	}
	
	if( nLock )
	{
		//error_report("Locked data can not be translated", TRUE);
		warning_msg_box(XFERR_LOCK_DATA_NOT_TRANSLATE, true, 'E');
		return true;
	}
		
	return false;
}
/// end CURVE_TRANSLATE_LOCK_DATA


int curvetranslate(const int& ntype, const LineInfo& li, MarkerInfo& mi, const EventInfo& ei, LineInfo& gc, const ButtonInfo& biMenu, const LineInfo& tli)
{
	//---Jasmine 12/12/08 v8.0986 QA80-12715 ADD_BUTTON_FOR_POP_UP_MENU 
	
	///Jasmine 01/08/09 v8.0994d QA80-12715 ADD_TARGET_LINE
	///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
	//CurveTranslateMenu 	menu(ntype, biMenu);
	//CurveTranslateMenu 	menu(ntype, s_bStepShift, biMenu);
	///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION
	//CurveTranslateMenu 	menu(ntype, s_bStepShift, tli.GetShow(), biMenu);
	CurveTranslateMenu 	menu(ntype, s_bStepShift, tli.GetShow(), s_bKeepTool, biMenu);
	///End ALLOW_KEEP_TOOL_AFTER_TRANSLATION
	///End COUNT_STEP_BY_SAMPLING_INTERVAL
	///End ADD_TARGET_LINE
	
	DataRange 	dr;
	int 		nMenuCmd = 0;
	bool bFoundData = menu.ProcessEvents(ei, biMenu, dr, nMenuCmd, 1, li);
	if(nMenuCmd < 0)
		return 0;
	//---
	
	// ntype = 0,1 for vertical/horizontal translate
	bool bVertTrans = (ntype == ntype_vertical);
	
	int iRet = 0; 
	int nEventID = ei.GetEvent();
	switch( nEventID )
	{
	case OE_INIT_ANCHORS:
		{
			/// RVD 7/25/2008 v8.0909 qa70-11782 CURVE_TRANSLATE_LOCK_DATA
			DataPlot dp;
			mi.GetParent(dp);
			
			if( IsDestroyOnInit(dp, bVertTrans) )
				return OCD_MODIFY_DEL;
			/// end CURVE_TRANSLATE_LOCK_DATA
			
			s_bReady 	= false;//----- CPY 11/29/07 QA70-10509 FAILED_MOVING_DATA_AT_END_OF_MOVE
			s_dStep 	= NANUM;	//---Jasmine 12/12/08 v8.0986 QA80-12715 ADD_BUTTON_FOR_POP_UP_MENU
			///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
			
			s_n 		= 1;
			s_bUseSamplingInterval = true;
			
			///Jasmine 02/05/09 BY_DEFAULT_USE_STEP_SHIFT_IF_SAMPLING_INTERVAL
			s_bStepShift= false;	
			///Jasmine 07/30/09 QA80-14031 USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
			//Column colX, colY;
			//if( !bVertTrans && CER_NO_X_COL == get_plot_columns(dp, colX, colY) )
			if(!bVertTrans && _has_x(dp) == CER_NO_X_COL)
			{
				s_bStepShift = true;
				if(s_bUseSamplingInterval)
				{
					double dX0, dInc;
					//colY.IsEvenSampling(&dX0, &dInc);
					XYRange dr;
					dp.GetDataRange(dr);
					dr.IsEvenSampling(0, &dX0, &dInc);
					///End USE_DATA_RANGE_CODE_TO_SUPPORT_LOOSE_DATASET
					s_dStep = s_n * dInc;
				}
			}
			///End BY_DEFAULT_USE_STEP_SHIFT_IF_SAMPLING_INTERVAL
			///End COUNT_STEP_BY_SAMPLING_INTERVAL
			
			///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION
			///Jasmine 02/10/09 REMEMBER_KEEPTOOL_IN_REGISTER
			//s_bKeepTool = false;
			DWORD dw;
			dlg_load_registry("curve translate", "KeepTool", dw, false);
			s_bKeepTool = dw;
			///End REMEMBER_KEEPTOOL_IN_REGISTER
			//End ALLOW_KEEP_TOOL_AFTER_TRANSLATION
			
			mi.SetWidth(0);
			li.Init(bVertTrans ? LN_HORIZONTAL : LN_VERTICAL, LNPOS_CENTER);
			///Jasmine 11/27/08 v8.0980d QA80-12367 SNAP_TO_MIDDLE_POINT_IN_DATAPLOT_MAY_OUT_OF_VIEW
			int nMidPoint = _get_dataplot_mid_point_in_view(dp, bVertTrans);
			/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
			//if(nMidPoint >= 0)
			//	mi.SnapToData(dp, nMidPoint);
			//else
			//	mi.SnapToParent(-1);
			// SnapToParent and SnapToData are not needed in marker is following parent - snapping is automatic in this case
			mi.SetFollowsParent(TRUE);
			if(nMidPoint >= 0)
			{
			vector vx(1);		// only 1 marker for curve translate tool
			vx[0] = nMidPoint;	// data plot index is used as coordinate if marker follows parent (dataplot)
			mi.SetXY(vx, vx);
			/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
			}
			else
				mi.SnapToParent(-1);
			_rescale_to_show_anchor(mi);
			
			string strDisplay;
			
			/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
			//strDisplay.Format("%s = %s", STR_ANCHOR_VALUE, ftoa(bVertTrans ? mi.GetY() : mi.GetX()));
			double dValue = get_marker_pos(mi, bVertTrans);
			strDisplay.Format("%s = %s", STR_ANCHOR_VALUE, ftoa(dValue));
			/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
			
			SetDataDisplayText(strDisplay);
			///End SNAP_TO_MIDDLE_POINT_IN_DATAPLOT_MAY_OUT_OF_VIEW
			iRet = OCD_MODIFY|OCD_REPAINT;
			gc.Init(LN_FREE,LNPOS_CENTER,0.1,0);
			gc.SetShow(false);
			TreeNode trgc = gc;
			trgc.Notify.nVal = 1;// this indicate that we need to follow another anchor's move event
			
			///Jasmine 01/08/09 v8.0994d QA80-12715 ADD_TARGET_LINE
			s_HasTargetLine = false;
			tli.Init(bVertTrans ? LN_HORIZONTAL : LN_VERTICAL, LNPOS_CENTER, 0.2, SYSCOLOR_BLUE);
			tli.SetShow(false);
			///End ADD_TARGET_LINE
			
			///------ Folger 02/10/10 QA81-14959-P3 XF_SUPPORT_CREATE_HIDDEN_ANCHOR
			biMenu.SetShow(true);
			mi.SetShow(true);
			///------ End XF_SUPPORT_CREATE_HIDDEN_ANCHOR
		}
		break;
		
	case OE_GRAPHOBJ_EDIT:
		if( ei.GetSenderId() == mi.GetID() )
		{
			/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
			//mi.SnapToParent(ei.GetSenderParam());
			/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
			_rescale_to_show_anchor(mi);
			iRet = OCD_MODIFY|OCD_REPAINT;
		}
		break;		
	case OE_GRAPHOBJ_EDITING:
		if( ei.GetSenderId() == mi.GetID() )
		{
			/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
			//mi.SnapToParent(ei.GetSenderParam());
			/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
	
			///Jasmine 01/09/09 v8.0995 QA80-12715 SHOW_VALUE_IN_DATA_DISPLAY_BAR
			/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
			//double dValue = ntype == ntype_vertical? mi.GetY() : mi.GetX();
			double dValue = get_marker_pos(mi, bVertTrans);
			/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
			
			string strText;  
			strText.Format("%s = %s", STR_ANCHOR_VALUE, ftoa(dValue));
			SetDataDisplayText(strText);				
			///End SHOW_VALUE_IN_DATA_DISPLAY_BAR
			
			/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
			//return OCD_MODIFY;// immediately return, faster, not to update line
			/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
		}
		return 0; // otherwise not modified, faster
	case OE_MOVING:
		if( ei.GetSenderId() == li.GetID() )
		{
			DataPlot dp;
			mi.GetParent(dp);
			if(dp)
			{
				_ghost_curve_moving(gc, dp, li, mi, ntype);
				return OCD_MODIFY;
			}
		}
		///Jasmine 01/09/09 v8.0995 QA80-12715 SHOW_VALUE_IN_DATA_DISPLAY_BAR
		if( ei.GetSenderId() == tli.GetID() )
		{
			//double dValue	= ntype == ntype_vertical? tli.GetY() : tli.GetX();
			DataPlot dp;
			mi.GetParent(dp);
			if(dp)
			{
				GraphLayer gly;
				dp.GetParent(gly);
				
				double dx, dy;
				
				///Jasmine 01/19/09 QA80-12944 SHOW_ANCHOR_AND_TARGET_LINE_AND_THEIR_DISTANCE
				if(get_distance(tli, mi, 0, &dx, &dy, gly))
				{
					double distance = bVertTrans? dy : dx;
					/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
					//double dAnchor	= bVertTrans? mi.GetY() : mi.GetX();
					double dAnchor	= get_marker_pos(mi, bVertTrans);
					/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
					///Jasmine 01/21/09 QA80-12715 PUT_TEXT_IN_DIFFERENT_ROW_SO_FONT_CAN_BIGGER
					string strText, strAnchor, strTarget, strDistance;  
					strAnchor.Format( "%s = %s", 	STR_ANCHOR_VALUE, 		ftoa(dAnchor) );
					strTarget.Format( "%s = %s", 	STR_TARGET, 			ftoa(dAnchor+distance) );
					strDistance.Format( "%s = %s", 	bVertTrans?"dy":"dx", 	ftoa(distance));
					strText = strAnchor + NEXT_LINE + strTarget + NEXT_LINE + strDistance;
					///End PUT_TEXT_IN_DIFFERENT_ROW_SO_FONT_CAN_BIGGER
					SetDataDisplayText(strText);				
				}
				///End SHOW_ANCHOR_AND_TARGET_LINE_AND_THEIR_DISTANCE
			}
		}
		///End SHOW_VALUE_IN_DATA_DISPLAY_BAR
		return 0; // otherwise not modified, faster
	case OE_MOVE:
		if( ei.GetSenderId() == li.GetID() )
		{
			DataPlot dp;
			mi.GetParent(dp);
			_ghost_curve_end_move(gc, dp);
			///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION	
			/*if(!_move_curve_on_end_move(li, mi, dp, bVertTrans))
				error_report("_move_curve_on_end_move failed");
			
			return OCD_MODIFY_DEL; // to indicate Anchor should be removed, together with all associated objects
			*/
			iRet = _move_curve(li, mi, dp, bVertTrans, s_bKeepTool);
			///End ALLOW_KEEP_TOOL_AFTER_TRANSLATION
		}
		else
			error_report("case OE_MOVE ei.GetSenderId() != li.GetID()");
		break;
	
	/// RVD 1/21/2009 qa70-12995-p1 RIGHT_CLICK_WHEN_DRAGING_OBJ_GHOST_CURVE_WRONG_POS
	case OE_UNSELECT:
		{
			DataPlot dp;
			mi.GetParent(dp);
			/// YuI 02/03/09 QA70-13053 IMPROVEMENTS_TO_BUTTONIZE_OBJECT_DRAWING
			//	_ghost_curve_end_move(gc, dp);
			//	iRet = OCD_MODIFY|OCD_REPAINT;
			if( _ghost_curve_end_move(gc, dp) )
				iRet = OCD_MODIFY|OCD_REPAINT;
			/// end IMPROVEMENTS_TO_BUTTONIZE_OBJECT_DRAWING
		}
		break;
	/// end RIGHT_CLICK_WHEN_DRAGING_OBJ_GHOST_CURVE_WRONG_POS
		
	//---Jasmine 12/12/08 v8.0986 QA80-12715 ADD_BUTTON_FOR_POP_UP_MENU		
	case OE_BUTTONCLICK:
		//Do Command
		switch(nMenuCmd)
		{
		case CURVE_TRANSLATE_MOVE_ANCHOR:
			iRet = move_anchor(ntype, li, mi, ei, gc, biMenu);
			break;
		///Jasmine 01/08/09 v8.0994d QA80-12715 ADD_TARGET_LINE
		case CURVE_TRANSLATE_TARGET_ANCHOR: 	
			bool bTargetLine = !tli.GetShow();
			tli.SetShow(bTargetLine);
			if(bTargetLine)
			{
				if(!s_HasTargetLine)
				{
					DataPlot dp;
					mi.GetParent(dp);
					
					int nNearest = _get_nearest_value_point(mi, bVertTrans);
					if(nNearest >= 0)
						tli.SnapToData(dp, nNearest);
					else
					{
						///Jasmine 02/18/09 SNAP_TO_PARENT_NOT_WORK_FOR_LINEINFO_IN_981
						//tli.SnapToParent(-1);
						tli.SnapToData(dp, 0);
						///End SNAP_TO_PARENT_NOT_WORK_FOR_LINEINFO_IN_981
					}
					
					s_HasTargetLine = true;
				}
			
				_rescale_to_show_anchor(tli);
				///Jasmine 01/09/09 v8.0995 QA80-12715 SHOW_VALUE_IN_DATA_DISPLAY_BAR
				double dValue	= bVertTrans? tli.GetY() : tli.GetX();
				/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
				//double dAnchor	= bVertTrans? mi.GetY() : mi.GetX();
				double dAnchor	= get_marker_pos(mi, bVertTrans);
				/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP
				string strText, strAnchor, strTarget, strDistance;  
				strAnchor.Format( "%s = %s", 	STR_ANCHOR_VALUE, 		ftoa(dAnchor) );
				strTarget.Format( "%s = %s", 	STR_TARGET, 			ftoa(dValue) ); 
				strDistance.Format( "%s = %s", 	bVertTrans?"dy":"dx", 	ftoa(dValue - dAnchor) );
				strText = strAnchor + NEXT_LINE + strTarget + NEXT_LINE + strDistance;  
				SetDataDisplayText(strText);				
				///End SHOW_VALUE_IN_DATA_DISPLAY_BAR
			}
				
			iRet = OCD_MODIFY|OCD_REPAINT;
			break;
		case CURVE_TRANSLATE_SHIFT_TO_TARGET:
			iRet = _shift_to_tager(ntype, li, mi, ei, gc, biMenu, tli);
			break;
		///End ADD_TARGET_LINE
		case CURVE_TRANSLATE_SHIFT_VALUE:
			///Jasmine 01/08/09 v8.0994d QA80-12715 ADD_TARGET_LINE
			//iRet = shift_value(ntype, li, mi, ei, gc, biMenu);
			iRet = shift_value(ntype, li, mi, ei, gc, biMenu, tli);
			///End ADD_TARGET_LINE
			break;
		///Jasmine 12/26/08 v8.0991 QA80-12715 COUNT_STEP_BY_SAMPLING_INTERVAL
		case CURVE_TRANSLATE_SHIFT_STEP:
		///Jasmine 02/05/09 COMBINE_STEP_SIZE_IN_STEP_SHIFT_MENU
			//s_bStepShift = !s_bStepShift;
			//if(s_bStepShift)
				iRet = show_shift_step_box(ntype, li, mi, ei, gc, biMenu);
			break;
		/*case CURVE_TRANSLATE_STEP_SIZE:
			iRet = show_shift_step_box(ntype, li, mi, ei, gc, biMenu);
			break;*/
		///End COMBINE_STEP_SIZE_IN_STEP_SHIFT_MENU
		///End COUNT_STEP_BY_SAMPLING_INTERVAL
		///Jasmine 02/05/09 ALLOW_KEEP_TOOL_AFTER_TRANSLATION	
		case CURVE_TRANSLATE_KEEP_TOOL:
			s_bKeepTool = !s_bKeepTool;
			///Jasmine 02/10/09 REMEMBER_KEEPTOOL_IN_REGISTER
			dlg_save_to_registry("curve translate", "KeepTool", s_bKeepTool);
			///End REMEMBER_KEEPTOOL_IN_REGISTER
			break;
		case CURVE_TRANSLATE_DELETE_TOOL:
			iRet = OCD_MODIFY_DEL;
			break;
		///End ALLOW_KEEP_TOOL_AFTER_TRANSLATION
		case CURVE_TRANSLATE_HELP:
			LT_execute("help curve translate");
			break;
		}
		break;
	//---
	}
	
	if( iRet & OCD_MODIFY )	/// RVD 2/6/2009 SNAP_USELESS_IF_NO_OCD_MODIFY_BIT
	{
		li.SnapTo(mi, 0);
		biMenu.SnapTo(li, 1);//0=from, 1=to
	}
	
	return iRet;
	
}

//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 curvetranslate_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{

}
