/*------------------------------------------------------------------------------*
 * File Name: ParamBox.c														*
 * Creation: CPY 1/27/03														*
 * Purpose: Origin C support for a general parameters edit box					*
 * Copyright (c) OriginLab Corp.2003											*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *	CPY 4/23/03 v7.0568 OPERATION_CLASS_NEED_TRY_AND_AUTO_UPDATE				*
 *	CPY 5/20/03 CHECK_BOX_CLICK_OUTSIDE_CHECK_TO_TOGGLE							*
 *	CPY 5/22/03 v7.0589 SHIFT_SHOW_FOR_CLASSES									*
 *	CPY 5/30/03 QA70-4577 EVENT_HANDLER_NEED_DIALOG_AND_VALUE_UPDATE			*
 *	CPY 6/11/03 QA70-4450 v7.0601 TTILE_KEY_TO_BECOME_MODLESS					*
 *	CPY 7/8/03 QA70-4796 v7.0620 GET_N_BOX_EVENT_FUNC_MOVE_WIN					*
 *	CPY 8/24/03 v7.5674 QA70-5061 ADD_DISPLAY_OPTIONS							*
 *	CPY 10/11/03 QA70-5332 HELP_CUSTOMIZED										*
 *------------------------------------------------------------------------------*/
 
////////////////////////////////////////////////////////////////////////////////////
#include <Origin.h>
//#include <Project.h>
//#include <sys_utils.h> // basic routines implemeted through Origin C, see sys_utils.c
//#include <utilities.h>
#include <Settings.h>
#include <Dialog.h>
#include <tree_utils.h>

#include <vsFlexGrid.h>
#include <GetNbox.h>
#include "Odlg.h"
#include "HelpID.h"
//#include "local.h"

#include "theme_utils.h" // some theme related utility functions, will move into separate utils file later
#include <RangeControl.h>
#include "ResizeDialog.h"
#include "TreeEditControl.h"
#include <Profiler.h>

#define THE_TREE_NODE	m_paramTree.FirstNode
enum {
	GRID_CHANGE_SHOW = 0x0001,
	GRID_CHANGE_ENABLE = 0x0002,
	GRID_CHANGE_VALUE = 0x0004,
	GRID_CHANGE_SIZE = 0x0008
};


bool GetNBox(TreeNode& tr, LPCSTR lpcszTitle, LPCSTR lpcszDescription, PAPPLY_FUNC pfn, PEVENT_FUNC pEvtFn, HWND hWndParent) //=NULL,NULL,NULL, NULL, NULL
{
	int nID = IDD_GET_N_BOX_TREE;//IDD_GET_N_BOX;
	// we have two GetNBox dialog, one support ActiveCurve with range control and the other does not
	// and the decision is coming from the tree attribute
	string strCurveNode;
	if(tr.GetAttribute(STR_ACTIVE_CURVE, strCurveNode))
		nID = IDD_GET_N_BOX_RANGE;
	else if(tr.tagName == "GetNBox")
		nID = IDD_GET_N_BOX;
		
	
	GetNBoxDlg myDlg(nID, tr, lpcszTitle, lpcszDescription, pfn, pEvtFn, strCurveNode);
	int nRet = myDlg.DoModalEx(hWndParent);
	if(IDOK == nRet)
	{
		myDlg.UpdateEditTreeNode(tr);
		return true;
	}
	return false;
}


//------------------------- utilities ----------------------


class GetNBoxDlg : public ResizeDialog
{
private:
	Tree		m_paramTree;
	TreeNode	m_input_tr; //CPY 4/23/03 v7.0568 OPERATION_CLASS_NEED_TRY_AND_AUTO_UPDATE
	PAPPLY_FUNC m_pApplyFunc;
	PEVENT_FUNC m_pEventFunc;
	bool		m_bUseTryForApply;//CPY 4/23/03 v7.0568 OPERATION_CLASS_NEED_TRY_AND_AUTO_UPDATE

	bool		m_bApply1stTime;
	string		m_strTitle;
	string		m_strDescription;
	//
	string		m_strCuveNode;
	string		m_strClassName;
	
	RangeControl m_dataRange;
	
	Button		m_chkShiftShow;
	int			m_nShiftShowCheck;
	Button		m_chkAutoUpdate;
	int			m_nDlgID;
	
	TreeEditControl		m_treeEditCntrl;
		
public:
	GetNBoxDlg(int nID, TreeNode& tr, LPCSTR lpcszTitle, LPCSTR lpcszDescription, PAPPLY_FUNC pfn, PEVENT_FUNC pEvtFn, LPCSTR lpcszCurveNode) : ResizeDialog(nID, "ODlg")
	{
		m_input_tr = tr;//CPY 4/23/03 v7.0568 OPERATION_CLASS_NEED_TRY_AND_AUTO_UPDATE
		
		m_paramTree.AddNode(tr.Clone());
	
		m_pApplyFunc = pfn;
		m_pEventFunc = pEvtFn;
		m_bApply1stTime = true;
		
		m_strTitle = lpcszTitle;
		m_strDescription = lpcszDescription;
		
		m_strCuveNode = lpcszCurveNode;
		
		m_bUseTryForApply = false;
		m_bUseTryForApply = false;
		//---  CPY 5/22/03 v7.0589 SHIFT_SHOW_FOR_CLASSES
		tr.GetAttribute(STR_CLASS_NAME, m_strClassName);
		//---
		m_nDlgID = nID; 
		//m_bIsFlatDialogDisplay = IDD_GET_N_BOX == nID? true:false;
	}
	void UpdateEditTreeNode(TreeNode& tr) { tr = m_input_tr;}
	
	
	int DoModalEx(HWND hWndParent = NULL)
	{
		InitMsgMap();// will be called from internal later
		
		DWORD dwDlgOptions = 0;
		//--- CPY 7/8/03 QA70-4796 v7.0620 GET_N_BOX_EVENT_FUNC_MOVE_WIN
		if(hWndParent)
			dwDlgOptions |= DLG_NO_DEFAULT_REPOSITION;
		//---
			
		
		if(isUseCurveRange())
		{
			TreeNode trCurve = THE_TREE_NODE.GetNode(m_strCuveNode);
			if(trCurve)
			{
				TreeNode trCurveOnMain = m_paramTree.AddNode(m_strCuveNode);
				trCurveOnMain.Replace(trCurve.Clone());
				THE_TREE_NODE.RemoveChild(m_strCuveNode); // to be consistent, so we will always add it back later
				string strYdata = trCurveOnMain.Range1.Ydata.strVal;
				string strXdata = trCurveOnMain.Range1.Xdata.strVal;
				Curve	cuv(strXdata, strYdata);
				m_dataRange.InitRange(cuv, trCurveOnMain, false);
			}
			else
			{
				trCurve = m_paramTree.AddNode(m_strCuveNode);
				m_dataRange.InitRange(Project.ActiveCurveBase(), trCurve);
			}
			//dwDlgOptions |= DLG_MODAL_WITH_KEY;//---- CPY 6/11/03 QA70-4450 v7.0601 TTILE_KEY_TO_BECOME_MODLESS
		}
		
		//CPY, we can only use the info from Construct
		//load_default_getnbox_gui(m_input_tr, m_strClassName);
		
		int nRet = DoModal(hWndParent, dwDlgOptions);
		if(nRet != IDCANCEL)
		{
			m_input_tr.Replace(THE_TREE_NODE);
				
			if(isUseCurveRange())
			{
				TreeNode trCurve = m_dataRange.GetCurveRange();
				m_input_tr.AddNode(trCurve.Clone());
			}
			updateShiftShowStatus();
			saveDefaultGUI();
		}
		// the following are no longer needed since we are no longer static
		//s_paramTree.Reset();
		//s_pApplyFunc = NULL;
		//s_pEventFunc = NULL;
		return nRet;
	}
		

//#define THE_GRID MyDlg.GetItem(IDC_PARAMS_GRID).GetActiveXControl()
protected:
	
EVENTS_BEGIN
	ON_INIT(OnInitDialog) 
	ON_DESTROY(OnDestroy)
	ON_BN_CLICKED(IDC_APPLY, OnApplyButton)
	ON_EVENT(EN_SETFOCUS, IDC_PARAMS_GRID, OnSetFocus)
	ON_USER_MSG(WM_USER_ON_CHILD_KILL_FOCUS, OnEditorKillFocus)
	ON_USER_MSG(WM_USER_RECONSTRUCT, OnReconstructGrid)
	//ON_SIZE(OnResize)
	ON_GRID_BEFORE_EDIT(IDC_PARAMS_GRID, OnBeforeEdit)
	ON_GRID_VALIDATE_EDIT(IDC_PARAMS_GRID, OnValidateEdit)
	ON_GRID_AFTER_EDIT(IDC_PARAMS_GRID, OnAfterEdit)
	ON_GRID_ROW_COL_CHANGE(IDC_PARAMS_GRID, OnRowColChange)
	ON_GRID_COMBO_CLOSEUP(IDC_PARAMS_GRID, OnComboCloseUp)
	ON_GRID_BEFORE_MOUSE_DOWN(IDC_PARAMS_GRID, OnBeforeMouseDown)
	ON_GRID_DRAW_CELL(IDC_PARAMS_GRID, OnDrawCell)
	ON_GRID_BUTTON_CLICK(IDC_PARAMS_GRID, OnButtonClick)
	ON_GRID_AFTER_COLLAPSE(IDC_PARAMS_GRID, OnAfterCollapse)
	
	ON_CLICK_KEY(OnKey) //CPY 6/11/03 QA70-4450 v7.0601 TTILE_KEY_TO_BECOME_MODLESS

	ON_HELPINFO(OnHelp) //CPY 10/11/03 QA70-5332 HELP_CUSTOMIZED
	
	///////////// Range Control related ////////////
	ON_SLIDE_BEFORE(IDC_PARAMS_RANGE_SLIDER, OnRangeSlide)
	ON_EVENT(EN_SETFOCUS, IDC_PARAMS_X_FROM, OnSetFocusFrom)
	ON_EVENT(EN_SETFOCUS, IDC_PARAMS_X_TO, OnSetFocusTo)
	ON_EVENT(EN_KILLFOCUS, IDC_PARAMS_X_FROM, OnKillFocusFrom)
	ON_EVENT(EN_KILLFOCUS, IDC_PARAMS_X_TO, OnKillFocusTo)
	ON_BN_CLICKED(IDC_PARAMS_RANGE_FULL, OnFullRange)
	///////////// end Range Control related ////////////
EVENTS_END

//------------ Event Handlers ------------------------
//private:
	BOOL OnInitDialog()
	{
		waitCursor junk;
		//Profiler jj;
		
		ResizeDialog::OnInitDialog();
		
		if(isUseCurveRange())
			m_dataRange.InitControls(*this, 
			IDC_PARAMS_RANGE_FROM, IDC_PARAMS_RANGE_TO, IDC_PARAMS_RANGE_SLIDER, 
			IDC_PARAMS_RANGE_FULL,IDC_PARAMS_X_FROM, IDC_PARAMS_X_TO, IDC_PARAMS_DATASET );

		//---  CPY 5/22/03 v7.0589 SHIFT_SHOW_FOR_CLASSES
		m_chkShiftShow = GetItem(IDC_PARAMS_SHIFTSHOW);
//		m_chkAutoUpdate = GetItem(
		int nIsShiftShow = getShiftShowStatus();
		if(nIsShiftShow >= 0)
		{
			m_chkShiftShow.Visible = true;
			m_chkShiftShow.Check = nIsShiftShow;
		}
		//--- end SHIFT_SHOW_FOR_CLASSES
		
		
		//check if we need Outline tree
		int nSubBranchs = 0;
		tree_count_items(THE_TREE_NODE, &nSubBranchs);
		
		PEVENT_FUNC pfn = getEventHandler();
		m_treeEditCntrl.Init(IDC_PARAMS_GRID, IDD_GET_N_BOX == m_nDlgID? true:false, nSubBranchs>0? true:false, pfn, *this);
		
		// if there is event handler, we will need to call that too
		if(pfn)
		{
			int nCntrlTypes[] = {
				ONODETYPE_CHECKBOX,
				ONODETYPE_DROPDOWN_NUMERIC_FLOAT,
				ONODETYPE_DROPLIST_STRINGS,
				ONODETYPE_DROPLIST_COLORS,
				0
			};
			int ii = 0;			
			while(nCntrlTypes[ii] > 0)
			{
				//m_pEventFunc(THE_TREE_NODE, -1, nCntrlTypes[ii]);
				pfn(THE_TREE_NODE, -1, nCntrlTypes[ii], *this); //CPY 5/30/03 QA70-4577 EVENT_HANDLER_NEED_DIALOG_AND_VALUE_UPDATE, added *this
				ii++;
			}
		}

		int nCaptionID = 0;
		Control cCaption = GetItem(IDC_PARAMS_DESCRIPTION);
		if(!m_strDescription.IsEmpty())
		{
			nCaptionID = IDC_PARAMS_DESCRIPTION;
			cCaption.Text = m_strDescription;
			cCaption.Visible = true;
		}
		else
			cCaption.Visible = false;		
			
		Button cApplyBtn = GetItem(IDC_APPLY);
		m_bUseTryForApply = false;
		if(m_pApplyFunc)
		{
			//CPY 4/23/03 v7.0568 OPERATION_CLASS_NEED_TRY_AND_AUTO_UPDATE
			string strApplyButtonText;
			if(THE_TREE_NODE.GetAttribute(STR_OPERATION_TRY, strApplyButtonText))
			{
				m_bUseTryForApply = true;
				cApplyBtn.Text = strApplyButtonText;
			}
		}
		else
		{
			cApplyBtn.Visible = false;
		}
		
		m_treeEditCntrl.Update(THE_TREE_NODE, true, true);
		
		enableButton(IDC_APPLY, m_pApplyFunc? true:false);
		if(!m_strTitle.IsEmpty())
			m_wndDlg.Text = m_strTitle;
		
		resizeDlgToFit();
		return true;
	}
	
	//----- CPY 10/11/03 QA70-5332 HELP_CUSTOMIZED
	BOOL OnHelp(int &nHelpID, int nIdCtrlFocus)
	{
		/*
		PEVENT_FUNC pfn = getEventHandler();
		if(pfn)
		{
			pfn(THE_TREE_NODE, -2, -1, *this); //CPY 5/30/03 QA70-4577 EVENT_HANDLER_NEED_DIALOG_AND_VALUE_UPDATE, added *this
		}
		*/
		return FALSE; // do no call default help
	}
	//----- end QA70-5332
	
	BOOL OnDestroy(void)
	{
		m_treeEditCntrl.OnDestroy();
		m_nShiftShowCheck = m_chkShiftShow.Visible? m_chkShiftShow.Check : -1;
		return true;
	}

	BOOL OnApplyButton(Control cntrl)
	{
		PAPPLY_FUNC pfn = getApplyFunction();

		if(pfn)
		{
			//---- CPY 4/23/03 v7.0568 OPERATION_CLASS_NEED_TRY_AND_AUTO_UPDATE
			//bool bResult = s_pApplyFunc(THE_TREE_NODE);
			if(m_bUseTryForApply) // for Operation class Try case, use can still Cancel and can still OK
			{
				m_input_tr.Replace(THE_TREE_NODE);		
				pfn(m_input_tr); // must pass original treenode for operation to get_parent to get real tree
				return TRUE;
			}
			else
				pfn(THE_TREE_NODE);	
			//---- end OPERATION_CLASS_NEED_TRY_AND_AUTO_UPDATE
			
			enableButton(IDOK, false); // already apply, no need to do OK
			if(m_bApply1stTime)
			{
				Button cBtn = GetItem(IDCANCEL);
				cBtn.Text = "Close";
				m_bApply1stTime = false;
			}
		}
		return TRUE;
	}


	//------- CPY 6/11/03 QA70-4450 v7.0601 TTILE_KEY_TO_BECOME_MODLESS
	BOOL OnKey(BOOL bRollingUp)
	{
		if(!bRollingUp && isUseCurveRange())
		{
			TreeNode trInput = m_paramTree.GetNode(m_strCuveNode);
			if(trInput)
			{
				GraphPage gpg = Project.Pages();
				if(gpg)
				{
					if(trInput.Page.strVal.CompareNoCase(gpg.GetName()) == 0 )
						return TRUE;
				}
			}
			return FALSE;
		}
		return TRUE;
	}
	//------- end TTILE_KEY_TO_BECOME_MODLESS
	
	
	//------------ vcFlex Grid Event Handlers ------------
	BOOL OnSetFocus(Control cntrl)
	{
		return m_treeEditCntrl.OnSetFocus();
	}
	void OnBeforeEdit(Control cntrl, long nRow, long nCol, BOOL* pCancel)
	{		
		m_treeEditCntrl.OnBeforeEdit(nRow, nCol, pCancel);
	}
	void OnValidateEdit(Control cntrl, int nRow, int nCol, BOOL* pCancel)
	{
		m_treeEditCntrl.OnValidateEdit(nRow, nCol, pCancel);
	}
	void OnAfterEdit(Control flxControl, int nRow, int nCol)
	{
		m_treeEditCntrl.OnAfterEdit(nRow, nCol);
	}
	void OnButtonClick(Control cntrl, int nRow, int nCol)
	{
		m_treeEditCntrl.OnButtonClick(nRow, nCol);
	}
	void OnAfterCollapse(Control cntrl, int nRow, short nState)
	{
		if(m_treeEditCntrl.IsResizeOnCollapse(nRow, nState))	//----CPY 8/24/03 v7.5674 QA70-5061 ADD_DISPLAY_OPTIONS
			resizeDlgToFit();
	}
	void OnRowColChange(Control cntrl)
	{
		m_treeEditCntrl.OnRowColChange();
	}	
	void OnDrawCell(Control cntrl, UINT hDC, int nRow, int nCol, int nLeft, int nTop, int nRight, int nBottom, BOOL* pDone)
	{
		m_treeEditCntrl.OnDrawCell(hDC, nRow, nCol, nLeft, nTop, nRight, nBottom, pDone);
	}
	void OnComboCloseUp(Control cntrl, int nRow, int nCol, BOOL* pFinishEdit)
	{
		m_treeEditCntrl.OnComboCloseUp(nRow, nCol, pFinishEdit);
	}
	void OnBeforeMouseDown(Control cntrl, short nButton, short nShift, float X, float Y, BOOL* pCancel)
	{
		m_treeEditCntrl.OnBeforeMouseDown(nButton, nShift, X, Y, pCancel);
	}
	//------------ WM_USER messages ------------
	BOOL OnEditorKillFocus(uint wParam, uint lParam)
	{
		return m_treeEditCntrl.OnEditorKillFocus(wParam, lParam);
	}
	BOOL OnReconstructGrid(uint wParam, uint lParam)
	{
		if(m_treeEditCntrl.OnReconstructGrid(wParam, lParam))
			resizeDlgToFit();

		return TRUE;
	}
	
	
	
	///////////// Range Control related ////////////
	void OnRangeSlide(Control ctrl, UINT nCode, UINT nPos)
	{
		m_dataRange.OnSlide();
	}
	void	OnSetFocusFrom(Control ctrl)
	{
		m_dataRange.OnSetFocus(true);
	}
	void	OnSetFocusTo(Control ctrl)
	{
		m_dataRange.OnSetFocus(false);
	}
	void	OnKillFocusFrom(Control ctrl)
	{
		m_dataRange.OnKillFocus(true);
	}
	void	OnKillFocusTo(Control ctrl)
	{
		m_dataRange.OnKillFocus(false);
	}
	void	OnFullRange(Control ctrl)
	{
		m_dataRange.OnSetFullRange();
	}
	///////////// end Range Control related ////////////

	

	//////////// local utilities/helper functions
private:

	// return -1, 0 1, -1 if not applicable
	int	getShiftShowStatus()
	{
		if(m_strClassName.IsEmpty())
			return -1;
		
		return shiftShow();
	}
	void updateShiftShowStatus()
	{
		if(!m_strClassName.IsEmpty() && m_nShiftShowCheck >= 0)
			shiftShow(false, m_nShiftShowCheck);
	}
	bool shiftShow(bool bGet = true, bool bVal = false)
	{
		Registry reg(HKEY_CURRENT_USER);
		string strKey = GetRegKey() + "\\Classes\\" + m_strClassName;
		string strVal = "GUI"; // we use just one dword
		DWORD	dwShiftShowBit = 0x00000001;
		
		DWORD dwVal = 0;
		if(bGet)
		{
			reg.GetValue(strKey, strVal, dwVal);
			return (dwVal & dwShiftShowBit)? true:false;
		}
		else
		{
			reg.GetValue(strKey, strVal, dwVal);
			if(bVal)
				dwVal |= dwShiftShowBit;
			else
				dwVal &=~dwShiftShowBit;
			
			reg.SetValue(strKey, strVal, dwVal);
		}
		return true;
	}
	void	saveDefaultGUI()
	{
		if(!m_strClassName.IsEmpty())
			save_default_settings(m_input_tr, m_strClassName, SETTINGS_GUI);
	}
	
	bool isUseCurveRange() { return m_strCuveNode.IsEmpty()? false:true;}
	
	PEVENT_FUNC getEventHandler()
	{
		PEVENT_FUNC temp_pEventFunc = m_pEventFunc;// wait until we can call member function pointer directly
		TreeNode trNode = THE_TREE_NODE;

		if(trNode)
			return temp_pEventFunc;
		
		return NULL;
	}
	PAPPLY_FUNC getApplyFunction()
	{
		PAPPLY_FUNC temp_pApplyFunc = m_pApplyFunc;// wait until we can call member function pointer directly
		TreeNode trNode = THE_TREE_NODE;

		if(trNode)
			return temp_pApplyFunc;
		
		return NULL;
	}
	
	void enableButton(uint nBtnID, bool bEnable)
	{
		Control cBtn = GetItem(nBtnID);
		cBtn.Enable = bEnable;
	}
	void resizeDlgToFit()
	{
		uint nBtnIds[] = {
			IDC_APPLY,
			IDCANCEL,
			IDOK,
			0};
	
		uint nBtnNoApply[] = {
			IDCANCEL,
			IDOK,
			0};
		
		if(m_pApplyFunc)	
			resizeDialog(nBtnIds);
		else
			resizeDialog(nBtnNoApply);
	}
	// assume grid as top control underneath a caption and other controls follows in a row
	// we assume the caption top position is unchanged
	void resizeDialog(uint* pIntIDs, int nCaptionID = IDC_PARAMS_DESCRIPTION)
	{
		int nEdge = GetControlGap();
		int nGap = 8;
		int nyCaptionAndEdge = nEdge;
				
		int nxGrid, nyGrid;
		
		m_treeEditCntrl.GetGridSize(nxGrid, nyGrid);
			
		RECT r1, r2;
		Button btn = GetItem(pIntIDs[0]);
		btn.GetWindowRect(&r1);
		int nBtnWidth = r1.right - r1.left;
		int nBtnHeight= r1.bottom - r1.top;
		
		// next we caculate the size of dialog
		int cx = nEdge + nxGrid + nEdge;
		
		m_wndDlg.GetClientRect(&r1);
		int nOldDlgW = r1.right - r1.left;
		int nOldDlgH = (r1.bottom - r1.top);
		if(cx < nOldDlgW)
			cx = nOldDlgW;
	
		Control cCaption = GetItem(nCaptionID);
		if(cCaption)
		{
			cCaption.GetWindowRect(&r2);
			m_wndDlg.ScreenToClient(&r2);
			int nyCaption = r2.top;
			if(cCaption.Visible)
				nyCaption = r2.bottom;
				
			r2.left = nEdge;
			r2.right = cx - nEdge;
			cCaption.MoveWindow(&r2);
			nyCaptionAndEdge = nyCaption + nEdge;
		}
		//---- CPY 5/22/03 v7.0589 SHIFT_SHOW_FOR_CLASSES
		int nCheckBoxes = 0;
		int nCheckBoxHeight = 0;
		if(m_chkShiftShow.Visible)
		{
			RECT rCheckBox;
			m_chkShiftShow.GetWindowRect(&rCheckBox);
			nCheckBoxHeight = rCheckBox.bottom - rCheckBox.top;
			nCheckBoxes = nEdge + nCheckBoxHeight + nEdge; 
		}
		//----
		int cy = nyCaptionAndEdge + nyGrid + nEdge + nCheckBoxes + nBtnHeight + nEdge;
		// we need to resize the dialog
		int nxDiff = cx - nOldDlgW;
		int nyDiff = cy - nOldDlgH;
		m_wndDlg.GetWindowRect(&r1);
		r1.right += nxDiff;
		r1.bottom += nyDiff;
		m_wndDlg.MoveWindow(&r1);
		
		
		r2.top = nyCaptionAndEdge;
		r2.left = nEdge;
		r2.right = cx - nEdge;
		r2.bottom = cy - 2*nEdge - nBtnHeight - nCheckBoxes;
		
		m_treeEditCntrl.MoveWindow(r2);	

		//---- CPY 5/22/03 v7.0589 SHIFT_SHOW_FOR_CLASSES
		if(nCheckBoxHeight)
		{
			r2.top = nyCaptionAndEdge + nyGrid + nEdge;
			r2.left = nEdge;
			r2.right = cx - nEdge;
			r2.bottom = r2.top + nCheckBoxHeight;
			m_chkShiftShow.MoveWindow(&r2);
		}
		//---- end 

		// now move the buttons to all along the bottom
		ArrangeControlsRightLeft(pIntIDs, cx, r2.bottom + nEdge);
	}
};


// has to write own function as GetTokens
static string get_combo_str_by_index(int nIndex, LPCSTR lpcsz)
{
	int nPos = 0;
	if(lpcsz[0] == '|')
		lpcsz++; // skip, usually should not be allowed
	
	string strSrc = lpcsz;
	return strSrc.GetToken(nIndex, '|');
}

static string get_node_val_as_str(TreeNode& trNode)
{
	string strTemp;
	int	nIndex;
	trNode.GetAttribute(STR_COMBO_ATTRIB, strTemp);
	nIndex = atoi(trNode.Text);
	return get_combo_str_by_index(nIndex, strTemp);
}

static string get_node_combo_str(TreeNode& trNode)
{
	string strTemp;
	// only some node type can support combo
	int nNodeID = trNode.ID;
	if(TRGP_ENUM_COMBO == nNodeID || TRGP_STR_LIST == nNodeID || TRGP_RANGE == nNodeID || TRGP_STR_BUTTON == nNodeID)
	{
		trNode.GetAttribute(STR_COMBO_ATTRIB, strTemp);
	}

	// the following code is used for customized list with numeric value on each entry
	// we don't need this for now
	/*
	switch(trNode.ID)
	{
	case TRGP_STR_LIST:
		break;
	default:
		return strTemp;
	}
	//TRGP_STR_LIST
	StringArray sa;
	int nItems = strTemp.GetTokens(sa, '|');
	if(nItems > 1)
	{
		int nIndex = 0;
		strTemp = "";
		for(int ii = 0; ii < nItems; ii++)
		{
			strTemp += "#" + nIndex++ + ";";
			strTemp += sa[ii];
			if(ii < nItems - 1)
				strTemp += "|";
		}
	}
	//out_str(strTemp);
	*/
	return strTemp;
}

static bool is_node_need_numeric_validation(TreeNode& trNode)
{
	switch(trNode.ID)
	{
	case TRGP_ENUM_COMBO:
		{
			string str = get_node_combo_str(trNode);
			if(str[0] != '|')
				return false;
		}
		// fall through
	case TRGP_DOUBLE:
		return true;
	}
	return false;
}




static void SetRect(RECT& rect, int nLeft, int nTop, int nRight, int nBottom)
{
	rect.left = nLeft;
	rect.top = nTop;
	rect.right = nRight;
	rect.bottom = nBottom;
}



// global functin for displaying the tree
bool out_params(TreeNode& tr)
{
	TreeNode cNode = tr.FirstNode;
	
	if(cNode == NULL)
		return false;
	
	string strLabel;
	string strVal;
	
	while(cNode)
	{
		//--- 
		if(!cNode.GetAttribute(STR_LABEL_ATTRIB, strLabel))
			strLabel = cNode.tagName;
		
		strVal = cNode.Text;
		
		printf("%s->%s\n", strLabel, strVal);
		
		//----
		cNode = cNode.NextNode;
	}
	return true;
}

//nSetShow = 0,1 will force all child nodes to set Show to same
static void tree_get_shows(TreeNode& tr, vector<int>& vv, int nSetShow = -1)
{
	int nVal;
	foreach(TreeNode cNode in tr.Children)
	{
		if(nSetShow >= 0)
			cNode.Show = nSetShow;
		
		nVal = cNode.Show;
		vv.Add(nVal);
		if(cNode.GetNodeCount() > 0) // branch node
			tree_get_shows(cNode, vv, nVal);
	}
}

//nSetEnable = 0,1 will force all child nodes to set Enable to same
static void tree_get_enables(TreeNode& tr, vector<int>& vv, int nSetEnable = -1)
{
	int nVal;
	foreach(TreeNode cNode in tr.Children)
	{
		if(nSetEnable >= 0)
			cNode.Enable = nSetEnable;
		
		nVal = cNode.Enable;
		vv.Add(nVal);
		if(cNode.GetNodeCount() > 0) // branch node
			tree_get_enables(cNode, vv, nVal);
	}
}

static void out_v(LPCSTR lpcsz, vector<int>& vv)
{
	//printf("%s:", lpcsz);
	for(int ii = 0; ii < vv.GetSize(); ii++)
		printf(" %d,", vv[ii]);
	
	out_str("");
}
		
static bool is_same(const vector<int>& v1, const vector<int>& v2)
{
	//----
	//out_v("New", v1);
	//out_v("Old", v2);
	//----
	
	uint nSize = v1.GetSize();
	if(nSize != v2.GetSize())
		return false;
	
	for(int ii = 0; ii < nSize; ii++)
	{
		if(v1[ii] != v2[ii])
			return false;
	}
	return true;
}

	

//--------------------- vsFlex routines that might be used in other dialogs --------


int TwipsToPixels(Window wndDlg, int twips, bool bX=true)
{
	DeviceContext dc = wndDlg.GetDC();
	
	double vv = 0.5 + dc.GetDeviceCaps(bX? LOGPIXELSX : LOGPIXELSY) * (double)twips / 1440.0;
	return vv;
}





/////////////////////////////////////////
// utilities for GetNBox handling
// convert i1 0-offset and i2 to notation "12 - 34" that is one offset and contains at least 1 point
// pass in values of -1 to indicate Auto
string range_to_str(int i1, int i2)
{
	string strAuto = "Auto";
	string str1 = (i1+1);
	string str2 = i2;

	if(i1<0)
		str1 = strAuto;
	if(i2<0)
		str2 = strAuto;
	
	return str1 + " - " + str2;
}

static int atoi_check_auto(const string& str)
{
	if(str.IsEmpty())
		return -1;
	str.TrimLeft();
	if(str[0] >= '0' && str[0] <= '9')
		return atoi(str);
	
	return -1;
}

bool str_to_range(LPCSTR lpcszRange, int& i1, int& i2)
{
	string strTemp = lpcszRange;
	int nPos = strTemp.Find('-');
	i1 = i2 = -1;
	if(nPos < 0)
		return false;
	
	i1 = atoi_check_auto(strTemp.Left(nPos));
	i2 = atoi_check_auto(strTemp.Mid(nPos+1));
	return true;
}

