/*------------------------------------------------------------------------------*
 * File Name:GridControl.h														*
 * Creation: CPY 6/12/2003														*
 * Purpose: OriginC Header file for general vsFlexGrid control					*
 * Copyright (c) Originlab Corp. 2003, 2004, 2005, 2006, 						*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *------------------------------------------------------------------------------*/

#ifndef _GRID_CONTROL_H_
#define _GRID_CONTROL_H_

#include <vsFlexGrid.h>
#include "ResizeControl.h"

#define _DBINT(_STR, _INT)	//out_int(_STR, _INT);
#define _DBSTR(_STR)		//out_str(_STR);
#define _DBTREE(_STR)		//tree_dump(m_trEdit, _STR);

#define	SHIFT_DOWN	(GetKeyState(VK_SHIFT) < 0 )
#define CNTRL_DOWN	(GetKeyState(VK_CONTROL) < 0 ) /* restrict to vertical movement */
#define ALTER_DOWN	(GetKeyState(VK_MENU) < 0 )		/* restrict to horizontal movement */


class GridControl : public ResizeControl
{
public:
	GridControl() : ResizeControl()
	{
		_DBSTR("calling GridControl constructor");
		m_bMouseDownToggleCheck = true;//---- CPY 7/5/03 CHECK_BOX_CLICK_OUTSIDE_CHECK_TO_TOGGLE
	}
	~GridControl()
	{
		_DBSTR("calling GridControl Destructor");
	}
	void Init(int nID, Dialog& dlg)
	{
		InitControl(nID, dlg);
		Control* pCntrl = GetControl();
		if(pCntrl)
			m_flx = pCntrl->GetActiveXControl();
		
		SetFont();
		
//		m_flx.FormatString = lpcszColHeadings;
//		m_flx.Cols = nCols;
		m_flx.Rows = 1;
		m_flx.FixedRows = 1;
		
		m_flx.FixedCols = 0;//hide the Row heading
		m_flx.ExtendLastCol = true;
		
		m_nHaldGridLineWidth = m_flx.GridLineWidth/2;
		
		m_nLastRow = -1;
		m_nLastCol = -1;
	}
	bool HasRows()
	{
		int nRows = m_flx.Rows - m_flx.FixedRows;
		return nRows > 0? true:false;
	}
	// nx, ny in screen pixels
	// return false if not inside grid data area
	bool FindCell(int nx, int ny, int& nRow, int& nCol)
	{
		if(nx < 0 || ny < 0) // from keyboard
			return false;
		ScreenToClient(nx, ny);
		for(nCol = m_flx.LeftCol; nCol <= m_flx.RightCol; nCol++)
		{
			for(nRow = m_flx.TopRow; nRow <= m_flx.BottomRow; nRow++)
			{
				if(isInCell(nx, ny, nRow, nCol))
					return true;
			}
		}
		return false;
	}
	// return row number if found otherwise return -1
	int FindRow(LPCSTR lpcszRowText, int nCol = 0, bool bCaseSensitive = false, bool bFullmatch = true)
	{
		/*
		for(int nRow = m_flx.FixedRows; nRow < m_flx.Rows; nRow++)
		{
			string strTemp = m_flx.Cell(flexcpText, nRow, nCol);
			if(bCaseSensitive && strTemp.Compare(lpcszRowText) == 0)
				return nRow;
			if(!bCaseSensitive && strTemp.CompareNoCase(lpcszRowText) == 0)
				return nRow;
		}
		*/
		
		return m_flx.FindRow(lpcszRowText, m_flx.FixedRows, nCol, bCaseSensitive, bFullmatch);
	}
	int FindRow(DWORD dwData)
	{
		int nRowRet = m_flx.FindRow(dwData, m_flx.FixedRows, -1);
		if(nRowRet < 0)
		{
			for(int nRow = m_flx.FixedRows; nRow < m_flx.Rows; nRow++)
			{
				DWORD dw = m_flx.RowData(nRow);
				//printf("%d: %d\n", nRow, dw);
				if(dw == dwData)
					return nRow;
			}
		}
		return nRowRet;
	}		
	// return false if not selected
	// this function is used to obtained screen location of selected cell
	bool GetSelCell(int& nx, int& ny, int& nRow, int& nCol)
	{
		bool bRet = false;
		RECT rect;
		if(m_flx.Row >= 0 && m_flx.Col >= 0)
		{
			nRow = m_flx.Row;
			nCol = m_flx.Col;
			getCellRect(nRow, nCol, rect);
			bRet = true;
		}
		else
		{
			getCellRect(m_flx.TopRow, m_flx.LeftCol, rect);
			nRow = -1;
			nCol = -1;
		}
		nx = RECT_X(rect); nx = XTwipsToPixels(nx);
		ny = RECT_Y(rect); ny = YTwipsToPixels(ny);
		ClientToScreen(nx, ny);
		return bRet;
	}
	/////////////////////////////////////////////
	/////////////////////////////////////////////
	// return false if same location as last call
	bool GetMouseCell(int& nRow, int& nCol)
	{
		nRow = m_flx.MouseRow;
		nCol = m_flx.MouseCol;
		if(nRow == m_nLastRow && nCol == m_nLastCol)
			return false;

		m_nLastRow = nRow;
		m_nLastCol = nCol;
		return true;
	}
	bool GetMousePixel(float X, float Y, int& nRow, int& nCol, int& nx, int& ny)
	{
		nRow = m_flx.MouseRow;
		nCol = m_flx.MouseCol;
		nx = XTwipsToPixels(X);
		ny = YTwipsToPixels(Y);
		ClientToScreen(nx, ny);
		if(nRow < 0 || nCol < 0)
			return false;
		return true;
	}
	//for given pixel location, find best interpretation on grid, nx and ny both [in] and [out]
	// if ny/nx < 0, then we will need to look at grid Current position for location info
	bool ConvertPixelLocation(int& nRow, int& nCol, int& nx, int& ny)
	{
		// for now, only consider
		return false;
	}
	bool HideRow(int nRow = -1, bool bHide = true)
	{
		if(nRow < 0) // to all
		{
			for(int ii = 0; ii < m_flx.Rows; ii++)
				m_flx.RowHidden(ii) = bHide;
			
			return true;
		}
		if(nRow >= m_flx.Rows)
			return false;

		m_flx.RowHidden(nRow) = bHide;
		return true;
	}
	bool HideCol(int nCol = -1, bool bHide = true)
	{
		if(nCol < 0) // to all
		{
			for(int ii = 0; ii < m_flx.Cols; ii++)
				m_flx.ColHidden(ii) = bHide;
			
			return true;
		}
		if(nCol >= m_flx.Cols)
			return false;

		m_flx.ColHidden(nCol) = bHide;
		return true;
	}
	bool IsColHidden(int nCol)
	{
		if(nCol < 0 || nCol >= m_flx.Cols)
			return false;
		
		bool bHidden = m_flx.ColHidden(nCol);
		return bHidden;
	}
	bool IsColHeadingRow(int nRow)
	{
		return (nRow < m_flx.FixedRows)? true:false;
	}
	bool IsInGrid(int nRow, int nCol)
	{
		if(nRow < 0 || nRow >= m_flx.Rows)
			return false;
		if(nCol < 0 || nCol >= m_flx.Cols)
			return false;
		return true;
	}
	int GetRowRange(int& nRow1, int& nRow2)
	{
		nRow1 = m_flx.FixedRows;
		nRow2 = m_flx.Rows-1;
		return m_flx.Rows;
	}
	bool SetCheck(int nRow, int nCol, bool bCheck)
	{
		if(!IsInGrid(nRow, nCol))
			return false;
		m_flx.Cell(flexcpChecked, nRow, nCol) = bCheck? flexChecked: flexUnchecked;
		return true;
	}		
	bool GetCheck(int nRow, int nCol)
	{
		if(!IsInGrid(nRow, nCol))
			return false;
		
		int nVal = m_flx.Cell(flexcpChecked, nRow, nCol);
		bool bCheck = flexUnchecked == nVal? 0:1;
		return bCheck;
	}		
	DWORD GetCellData(int nRow, int nCol)
	{
		if(!IsInGrid(nRow, nCol))
			return 0;
		
		DWORD dw = m_flx.Cell(flexcpData, nRow, nCol);
		return dw;
	}
	bool SetCellData(int nRow, int nCol, DWORD dwData)
	{
		if(!IsInGrid(nRow, nCol))
			return false;
		m_flx.Cell(flexcpData, nRow, nCol) = dwData;
		return true;
	}
	string GetCell(int nRow, int nCol)
	{
		string strRet;
		if(!IsInGrid(nRow, nCol))
			return strRet;
		
		strRet = m_flx.Cell(flexcpText, nRow, nCol);
		return strRet;
	}
	bool SetCell(int nRow, int nCol, const string& str)
	{
		if(!IsInGrid(nRow, nCol))
			return false;
					
		m_flx.Cell(flexcpText, nRow, nCol) = str;
		return true;
	}
	bool SetCell(int nRow, int nCol, int nVal)
	{
		string strTemp = nVal;
		return SetCell(nRow, nCol, strTemp);
	}
	bool SetBold(int nRow, int nCol, bool bSet = true) {return SetCellState(nRow, nCol, bSet);}
	bool SetItalic(int nRow, int nCol, bool bSet = true) {return SetCellState(nRow, nCol, bSet, flexcpFontItalic);}
	bool IsItalic(int nRow, int nCol) {return IsCellState(nRow, nCol, flexcpFontItalic);}
	bool IsBold(int nRow, int nCol) {return IsCellState(nRow, nCol);}
	// nCol = -1 to set all cols, return last state of last col
	// nRow = -1 to set all rows, reutrn last state of last row
	// otherwise return last state of the specified cell
	bool SetCellState(int nRow, int nCol, bool bSet = true, int nStateType = flexcpFontBold)
	{
		if(nCol < 0)
		{
			bool bRet = false;
			for(int ii = 0; ii < m_flx.Cols; ii++)
				bRet = SetCellState(nRow, ii, bSet, nStateType);
			
			return bRet;
		}
		if(nRow < 0)
		{
			bool bRet = false;
			for(int ii = 0; ii < m_flx.Rows; ii++)
				bRet = SetCellState(ii, nCol, bSet, nStateType);
			
			return bRet;
		}
		if(!IsInGrid(nRow, nCol))
			return false;
		bool bWasBold = m_flx.Cell(nStateType, nRow, nCol);
		m_flx.Cell(nStateType, nRow, nCol) = bSet;
		return bWasBold;
	}
	bool IsCellState(int nRow, int nCol, int nStateType = flexcpFontBold)
	{
		if(!IsInGrid(nRow, nCol))
			return false;
		bool bIsBold = m_flx.Cell(nStateType, nRow, nCol);
		return bIsBold;
	}
	bool IsEnable() 
	{
		bool bEnable = m_flx.Enabled;
		return bEnable;
	}
	bool SetEnable(bool bSet = true)
	{
		bool bOldVal = IsEnable();
		m_flx.Enabled = bSet;
		return bOldVal;
	}
	void SetColHeading(int nCol, LPCSTR lpcszText)
	{
		if(nCol >= 0)
			//m_flx.Cell(flexcpText, 0, nCol) = lpcszText;
			m_flx.TextMatrix(0, nCol) = lpcszText;
	}
protected:
	 // assume format str in standard form, and we convert into new str with localization if needed
	int GetFormatStrAsArray(LPCSTR lpcszHeadings, vector<string>& vsHeadings, LPCSTR lpcszCategory = NULL, bool bCvtToLocal = true)
	{
		string strHeadings = lpcszHeadings;
		int nCount = strHeadings.GetTokens(vsHeadings, '|');
		if(!bCvtToLocal)
			return nCount;
		ASSERT(vsHeadings.GetSize() == nCount);
		for(int ii = 0; ii < nCount; ii++)
		{
			vsHeadings[ii] = _LC(vsHeadings[ii], lpcszCategory);
		}
		return nCount;
	}
	void SetFormatStrLocal(LPCSTR lpcszHeadings, LPCSTR lpcszCategory = NULL)
	{
		vector<string> vsHeadings;
		int nSize = GetFormatStrAsArray(lpcszHeadings, vsHeadings, lpcszCategory);
		ASSERT(vsHeadings.GetSize() == nSize);
		string strFormat;
		for(int ii = 0; ii < nSize; ii++)
		{
			if(ii > 0)
				strFormat += "|";
			strFormat += vsHeadings[ii];
		}
		m_flx.FormatString = strFormat;
	}
	LoadHideCols(LPCSTR lpcszDlgName, LPCSTR lpcszValName, vector<byte>* pvbColsDefault = NULL) // from registry
	{
		m_strHideColsRegValName = lpcszValName;
		m_strHideColsRegDlgName = lpcszDlgName;
		vector<byte> vbCols;
		if(load_default_checkboxes(m_strHideColsRegDlgName, vbCols, m_strHideColsRegValName))
			SetHiddenCols(vbCols);
		else if(pvbColsDefault)
			SetHiddenCols(*pvbColsDefault);
	}
	SaveHideCols() // to registry
	{
		if(m_strHideColsRegValName.IsEmpty() || m_strHideColsRegDlgName.IsEmpty())
			return;
		
		vector<byte> vbCols;
		GetHiddenCols(vbCols);
		save_default_checkboxes(m_strHideColsRegDlgName, vbCols, m_strHideColsRegValName);
	}
	//--- CPY 9/6/03 v7.5686 RESIZEING_CLEAN_UP move all m_cntrl and m_wndDlg related code to base class from GridControl
public:
	//virtual 
	int	GetPanelHeight(bool bGetMin = false)
	{
		return 50;
	}
	//virtual 
	int GetPanelWidth(bool bGetMin = false)
	{
		return 150;
	}
	//virtual 
	int GetMinHeight(bool bKeepFunctional = false) 
	{
		if(bKeepFunctional) // Min height that is still good to use the grid
			return YTwipsToPixels(m_nRowHeight * 4);
		
		return ResizeControl::GetMinHeight(bKeepFunctional);
	}
	//virtual 
	int GetMaxHeight(bool bKeepFunctional = false)
	{
		if(bKeepFunctional) // Min height that is still good to use the grid
			return YTwipsToPixels(m_nRowHeight * 6);
			
		return ResizeControl::GetMaxHeight(bKeepFunctional);
	}
	//---
private:
	string m_strHideColsRegValName;
	string m_strHideColsRegDlgName;
	/////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////
	// local helper functions
	/////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////
	
	// nx ny in Twips in client coordinates
	bool isInCell(int nx, int ny, int nRow, int nCol)
	{
		RECT rect;
		if(!getCellRect(nRow, nCol, rect))
			return false;
		if(nx >= rect.left && nx <= rect.right && ny >= rect.top && ny <= rect.bottom)
			return true;
		
		return false;
	}
	bool	getCellRect(int nRow, int nCol, RECT& rect)
	{
		if(nRow < 0 || nCol < 0)
			return false;
	
		rect.left = m_flx.Cell(flexcpLeft, nRow, nCol);
		rect.top = 	m_flx.Cell(flexcpTop, nRow, nCol);
		rect.right = rect.left + m_flx.Cell(flexcpWidth, nRow, nCol);
		rect.bottom = rect.top + m_flx.Cell(flexcpHeight, nRow, nCol);
		return true;
	}
	
	/////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////
		
private:
	int	m_nLastRow;
	int m_nLastCol;
	bool m_bMouseDownToggleCheck; //CPY 5/20/03 CHECK_BOX_CLICK_OUTSIDE_CHECK_TO_TOGGLE
	
	int	toggleCheck(int nVal)
	{
		switch(nVal)
		{
		case flexChecked:
			return flexUnchecked;
		case flexUnchecked:
			return flexChecked;
		case flexTSChecked:
			return flexTSUnchecked;
		case flexTSUnchecked:
			return flexTSChecked;
		}
		ASSERT(FALSE);
		return flexTSGrayed;
	}
	void resizeAllCols(int nExtraPixcels = 8)
	{
		bool bAllColSameWidth = false;
		int nExtra = PixelsToTwips(nExtraPixcels);
		m_flx.AutoSize(0, m_flx.Cols - 1, bAllColSameWidth, nExtra);
	}
	bool is_WindowJapanese()
	{
		int nCharSet;
		string str = get_system_font_name(GSFI_TYPE_DEFAULT, &nCharSet);
		if(SHIFTJIS_CHARSET == nCharSet)
			return true;
		
		return false;
	}
public:	
	virtual string GetRuntimeClass()
	{
		return "GridControl";
	}
	void SetFont()
	{
		//---- CPY v7.5699 QA70-5000 9/19/03 GET_WINDOW_MENU_FONT 
		/*
		//---- we try to set the grid to use same font as dialog, this is needed to support
		// Japanese etc.
		if(is_win2k(false) && is_WindowJapanese())
			m_flx.FontName = get_system_font_name(SYSTEM_FONT); //"‚l‚r –¾’©"; // somehow setting to dlg font still won't work
		else
			m_flx.FontName = FontName();
		
		m_flx.FontSize = FontSize();
		//----
		*/
		// temp code, need clean up later, some codes need to goto base class
		if(is_WindowJapanese())
		{
			int nFontSize;
			m_flx.FontName = GetFontName(ORIGIN_MENU_FONT,&nFontSize);
			if(nFontSize < 0) // convert to pt
			{
				double vv = - PixelsToTwips(nFontSize);
				vv = vv * 72.0 / 1440.0;
				nFontSize = 0.5 + vv;
			}
			
			m_flx.FontSize = nFontSize;
		}
		else
		{
			m_flx.FontName = FontName();
			m_flx.FontSize = FontSize();
		}
		//---- end CPY QA70-5000 9/19/03 
		m_nFontSize = m_flx.CellFontSize;
		
		m_nRowHeight= m_flx.RowHeight(0);
	}	
	virtual void ClearAll() 
	{
		m_flx.Rows = m_flx.FixedRows;
	}
	//OnAfterEdit call base method first 
	virtual void OnAfterEdit(int nRow, int nCol) 
	{
		_DBINT("OnAfterEdit bToggleCheck =", m_bMouseDownToggleCheck);
		
		m_bMouseDownToggleCheck = false;//---- CPY 5/20/03 CHECK_BOX_CLICK_OUTSIDE_CHECK_TO_TOGGLE
	}
	
	// OnBeforeMouseDown call base class method last	
	void OnBeforeMouseDown(short nButton, short nShift, float X, float Y, BOOL* pCancel)
	{
		int iRow = m_flx.MouseRow;
		int iCol = m_flx.MouseCol;
		if(iRow < 0 || iCol < 0)
		{
			*pCancel = TRUE;
			return;
		}
		//---- CPY 5/20/03 CHECK_BOX_CLICK_OUTSIDE_CHECK_TO_TOGGLE
		int nVal = m_flx.Cell(flexcpChecked, iRow, iCol);// cannot use COM in == test directly
		_DBINT("OnBeforeMouseDown bToggleCheck =", m_bMouseDownToggleCheck);
		if(m_bMouseDownToggleCheck && nVal != flexNoCheckbox)
		{
			m_flx.Cell(flexcpChecked, iRow, iCol) = toggleCheck(nVal);//bWasCheck? flexUnchecked : flexChecked;

			OnAfterEdit(iRow, iCol);
		}
		m_bMouseDownToggleCheck = true;
		//---- end CPY 5/20/03 CHECK_BOX_CLICK_OUTSIDE_CHECK_TO_TOGGLE
	}
	
	//nMaxColSizeFactor = 0 will not consider max col width
	// nMaxColSizeFactor = 2 will set each col no bigger then 1/2 the width of the control width
	void ResizeCols(int nMaxColSizeFactor = 0, int nExtraPixcels = 8)
	{
		int nMaxColWidth = 0;
		
		if(nMaxColSizeFactor)
		{
			RECT rect;
			GetWindowRect(rect);
			nMaxColWidth = PixelsToTwips(RECT_WIDTH(rect)/(double)nMaxColSizeFactor);
		}
		m_flx.ColWidthMax = nMaxColWidth;
		if(nMaxColWidth)
			m_flx.ColWidthMin = PixelsToTwips(5); // ,min width

		resizeAllCols(nExtraPixcels);
	}
	// min col by row height as a factor, max col by screen width as a divider
	void ResizeCols(double dbMinColRowFactor, double dbMaxColDivScreen, int nExtraPixcels = 8)
	{		
		m_flx.ColWidthMin = 0.5 + m_nRowHeight * dbMinColRowFactor;
		int nScreenW = PixelsToTwips(GetSystemMetrics(SM_CXSCREEN));
		m_flx.ColWidthMax = 0.5 + nScreenW /dbMaxColDivScreen;

		resizeAllCols(nExtraPixcels);
	}

	void RemoveSelection()
	{
		m_flx.Col = 0;
		m_flx.Row = -1;
	}
	
	int XTwipsToPixels(int nx)	{return TwipsToPixels(nx);}
	int YTwipsToPixels(int ny)	{return TwipsToPixels(ny, false);}

	void	GridToDlgOffsets(int& dx, int &dy)
	{
		RECT rectGrid;
		GetRect(rectGrid);
	
		dx = rectGrid.left;
		dy = rectGrid.top;
	}
	bool	GetCellRect(int iRow, int iCol, RECT& rect, bool bCvtToFrame = true, bool bCvtToScree = false)
	{
		if(!getCellRect(iRow, iCol, rect))
			return false;
	
		rect.left = XTwipsToPixels(rect.left);
		rect.top = 	YTwipsToPixels(rect.top);
		rect.right = XTwipsToPixels(rect.right);
		rect.bottom = YTwipsToPixels(rect.bottom);
		InflateRect(&rect, -m_nHaldGridLineWidth, -m_nHaldGridLineWidth);
		
		if(bCvtToFrame) // convert
		{
			int dx, dy;
			GridToDlgOffsets(dx, dy);
			OffsetRect(&rect, dx, dy);
		}
		if(bCvtToScree)
		{
			ClientToScreen(rect);
		}
		return true;
	}
	void GetGridSize(int& nx, int& ny)
	{
		int nCellH;
		ny = 0;
		nx = 0;
		for(int nRow = 0; nRow < m_flx.Rows; nRow++)
		{
			nCellH = m_flx.Cell(flexcpHeight, nRow);
			ny += nCellH;
		}
		ny = TwipsToPixels(ny);
		
		int nn = 0;
		for(int nCol = 0; nCol < m_flx.Cols; nCol++)
		{
			nn += m_flx.ColWidth(nCol); //flx.Cell(flexcpWidth, 0, nCol);
		}
			
		nx = TwipsToPixels(nn);
		// gain 1 more pixels
		nx++;
		ny++;
	}
	void ClientToScreen(int &nx, int &ny, bool bCvtToFrame = true)
	{
		RECT rect;
		rect.left = rect.right = nx;
		rect.top = rect.bottom = ny;
		if(bCvtToFrame)
		{
			int dx, dy;
			GridToDlgOffsets(dx, dy);
			OffsetRect(&rect, dx, dy);
		}
		ClientToScreen(rect);
		nx = rect.left;
		ny = rect.top;
	}
	void ScreenToClient(int &nx, int &ny, bool bCvtToTwips = true, bool bCvtToFrame = true)
	{
		RECT rect;
		rect.left = rect.right = nx;
		rect.top = rect.bottom = ny;
		ScreenToClient(rect);
		if(bCvtToFrame)
		{
			int dx, dy;
			GridToDlgOffsets(dx, dy);
			OffsetRect(&rect, -dx, -dy);
		}		
		nx = rect.left;
		ny = rect.top;
		if(bCvtToTwips)
		{
			nx = PixelsToTwips(nx);
			ny = PixelsToTwips(ny, false);
		}
	}
	// nx ny in Twips in clint coordinates
	virtual void GetHiddenCols(vector<byte>& vbCols)
	{
		vbCols.SetSize(0);
		for(int nCol = 0; nCol < m_flx.Cols; nCol++)
		{
			vbCols.Add(IsColHidden(nCol));
		}
	}
	virtual void SetHiddenCols(const vector<byte>& vbCols, bool bDefault = true)
	{
		for(int nCol = 0; nCol < m_flx.Cols; nCol++)
		{
			bool bHide = bDefault;
			if(nCol < vbCols.GetSize())
				bHide = vbCols[nCol];
			HideCol(nCol, bHide);
		}
	}
protected:
	Object		m_flx;
	int			m_nRowHeight;
	int 		m_nFontSize;
	int			m_nHaldGridLineWidth;
};


// a vsFlexGrid that is used like a list control
class GridListControl : public GridControl
{
public:
	void Init(int nID, Dialog& dlg)
	{
		GridControl::Init(nID, dlg);
		m_flx.SelectionMode = flexSelectionByRow;
		m_flx.AllowSelection = false;
		//m_flx.ExplorerBar = flexExSortShow;
		m_flx.AllowUserResizing = flexResizeColumns;
	}
	// return false if no change, otherwise check if sel consistent, otherwise set to specified row/col
	bool CheckSelRow(int nRow, bool bOnlyInsideGrid = true)
	{
		if(bOnlyInsideGrid && nRow < 0)
			return false;
		
		if(m_flx.SelectionMode == flexSelectionListBox)
		{
			//--- CPY 8/19/03
			if(SHIFT_DOWN || CNTRL_DOWN)
				return false;
			//---
			int nNumSelected = m_flx.SelectedRows;

			for(int ii = 0; ii < nNumSelected; ii++)
			{
				if( nRow == m_flx.SelectedRow(ii) )
					return false;
			}
		}
		else
		{
			if(m_flx.Row == nRow)
				return false;
		}
		
		m_flx.Row = nRow;
		return true;
	}
	
	bool GetSelRows(vector<uint>& vnRows)
	{
		vnRows.SetSize(0);// empty first
		if(m_flx.SelectionMode == flexSelectionListBox)
		{
			int nNumSelected = m_flx.SelectedRows;
			if(nNumSelected > 0)
			{
				int nRow;
				for(int ii = 0; ii < nNumSelected; ii++)
				{
					nRow = m_flx.SelectedRow(ii);
					vnRows.Add(nRow);
				}
				return true;
			}
		}
		else
		{
			if(m_flx.Col >= 0)
			{
				int nSel = m_flx.Row;
				if(nSel >= m_flx.FixedRows)
				{
					vnRows.Add(nSel);
					return true;
				}
			}
		}
		return false;
	}
	// vn = [out] array of 1 = collapsed and 0 = open of all outline nodes
	// return -1 if not applicable, otherwise return number of branches
	int GetCollapsed(vector<byte>& vn)
	{
		if(m_flx.OutlineBar == flexOutlineBarNone)
			return -1;
		vn.SetSize(0);
		int nCounts = 0;
		for(int nRow = m_flx.FixedRows; nRow < m_flx.Rows; nRow++)
		{
			if(m_flx.IsSubtotal(nRow) )//&& m_flx.RowOutlineLevel(nRow) < 3)
			{
				nCounts++;
				int nCollapsed = m_flx.IsCollapsed(nRow);
				if(flexOutlineCollapsed == nCollapsed)
				{
					vn.Add(1);
				}
				else
					vn.Add(0);
			}
		}
		return nCounts;
	}
	bool SetCollapsed(const vector<byte>& vn)
	{
		if(m_flx.OutlineBar == flexOutlineBarNone)
			return false;
		int nn = 0;
		for(int nRow = m_flx.FixedRows; nRow < m_flx.Rows; nRow++)
		{
			if(m_flx.IsSubtotal(nRow))// && m_flx.RowOutlineLevel(nRow) < 3)
			{
				if(nn >= vn.GetSize())
					return false;
				
				int nCollapsed = vn[nn++]?flexOutlineCollapsed : flexOutlineSubtotals;//flexOutlineExpanded;
				m_flx.IsCollapsed(nRow) = nCollapsed;
			}
		}
		return true;
	}
protected:
	void SetAlternateRowColors()
	{
		m_flx.BackColorAlternate = RGB(0xF0,0xF0,0xF0);
	}
	bool GetSelItems(int nCol, vector<string>& vsItems)
	{
		vector<uint> vnRows;
		vsItems.SetSize(0);
		string strTemp;
		if(GetSelRows(vnRows))
		{
			for(int ii = 0; ii < vnRows.GetSize(); ii++)
			{
				strTemp = m_flx.Cell(flexcpText, vnRows[ii], nCol);
				vsItems.Add(strTemp);
			}
			return true;
		}
		return false;
	}
public:
	// return -1 if none selected, reutrn 1st sel Row if multiple
	int  GetSelectedRow()
	{
		vector<uint> vnRows;
		if(GetSelRows(vnRows))
			return vnRows[0];
		
		return -1;
	}
	void SelRow(int nRow, bool bSelLast = false) // -1 to sel last row
	{
		if(bSelLast)
			nRow = m_flx.Rows-1;
		
		m_flx.Row = nRow;
		
		if(nRow >= 0)
		{
			m_flx.ShowCell(nRow, 0);
		}
	}
	bool SelRows(const vector<int> nRows)
	{
		if(nRows.GetSize() == 1)
		{
			SelRow(nRows[0]);
			return true;
		}
		if(m_flx.SelectionMode != flexSelectionListBox)
			return false;
		m_flx.Row = -1;
		for(int ii = 0; ii < nRows.GetSize(); ii++)
			m_flx.IsSelected(nRows[ii]) = true;
		
		return true;
	}
	//virtual 
	string GetRuntimeClass()
	{
		return "GridListControl";
	}
	
private:

};


#endif //_GRID_CONTROL_H_

