// TerrCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "LeoCAD.h"
#include "TerrCtrl.h"
#include "IPEdit.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define IDC_INPLACE_EDIT  8  // ID of inplace edit control
#define GRIDSIZE 28

#define ControlPoint(row, col) m_pControl[row-1][(col-1)*3+2]

#define GetFixedRowHeight() GRIDSIZE
#define GetFixedColumnWidth() GRIDSIZE
#define m_nFixedRows 1
#define m_nFixedCols 1
#define GetRowHeight(a) GRIDSIZE
#define GetColumnWidth(a) GRIDSIZE
#define GetVirtualWidth() m_nCols*GRIDSIZE
#define GetVirtualHeight() m_nRows*GRIDSIZE
#define m_nMargin 1
#define GetFixedRowCount() 1
#define GetFixedColumnCount() 1
#define GetColumnCount() m_nCols
#define GetRowCount() m_nRows

/////////////////////////////////////////////////////////////////////////////
// CTerrainCtrl

CTerrainCtrl::CTerrainCtrl()
{
	RegisterWindowClass();

	m_nRows = 0;
	m_nCols = 0;
	m_nVScrollMax = 0; // Scroll position
	m_nHScrollMax = 0;
	m_MouseMode = MOUSE_NOTHING;
	m_pControl = NULL;
/*
	m_nMargin			 = 0;		  // cell padding
	m_nRowsPerWheelNotch = GetMouseScrollLines(); // Get the number of lines
												  // per mouse wheel notch to scroll

	m_bHandleTabKey 	 = TRUE;
	m_bTitleTips		 = TRUE;	  // show cell title tips

	m_nTimerID			 = 0;		  // For drag-selection
	m_nTimerInterval	 = 25;		  // (in milliseconds)

	m_crShadow			 = ::GetSysColor(COLOR_3DSHADOW);
	m_crGridColour		 = RGB(0,0,0);
	SetTextColor(m_crWindowText);
	SetBkColor(m_crShadow);

	SetTextBkColor(RGB(0xFF, 0xFF, 0xE0));
*/
	// Set the colours
	m_crFixedBkColour = GetSysColor(COLOR_3DFACE);
	m_crFixedTextColour = GetSysColor(COLOR_WINDOWTEXT);
	m_crTextBkColour = GetSysColor(COLOR_WINDOW);

	// Initially use the system message font for the GridCtrl font
	NONCLIENTMETRICS ncm;
	LOGFONT lf;
	ncm.cbSize = sizeof(NONCLIENTMETRICS);
	VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0));
	memcpy(&lf, &(ncm.lfMessageFont), sizeof(LOGFONT));
//	lf.lfWeight = SELECTED_CELL_FONT_WEIGHT;
	m_Font.CreateFontIndirect(&lf);

	// Set up the initial grid size
	SetRowCount(5);
	SetColumnCount(5);

/*
	// set initial selection range (ie. none)
	m_SelectedCellMap.RemoveAll();
	m_PrevSelectedCellMap.RemoveAll();
*/
}

CTerrainCtrl::~CTerrainCtrl()
{
/*
	DeleteAllItems();
*/
	DestroyWindow();
	m_Font.DeleteObject();
}

// Register the window class if it has not already been registered.
BOOL CTerrainCtrl::RegisterWindowClass()
{
	WNDCLASS wndcls;
	HINSTANCE hInst = AfxGetResourceHandle();

	if (!(::GetClassInfo(hInst, TERRAINCTRL_CLASSNAME, &wndcls)))
	{
		// otherwise we need to register a new class
		wndcls.style			= CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
		wndcls.lpfnWndProc		= ::DefWindowProc;
		wndcls.cbClsExtra		= wndcls.cbWndExtra = 0;
		wndcls.hInstance		= hInst;
		wndcls.hIcon			= NULL;
		wndcls.hCursor			= LoadCursor(NULL,IDC_ARROW);
		wndcls.hbrBackground	= (HBRUSH) (COLOR_3DFACE + 1);
		wndcls.lpszMenuName 	= NULL;
		wndcls.lpszClassName	= TERRAINCTRL_CLASSNAME;

		if (!AfxRegisterClass(&wndcls)) {
			AfxThrowResourceException();
			return FALSE;
		}
	}

	return TRUE;
}

BEGIN_MESSAGE_MAP(CTerrainCtrl, CWnd)
	//{{AFX_MSG_MAP(CTerrainCtrl)
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	ON_WM_SIZE()
	ON_WM_GETDLGCODE()
	ON_WM_HSCROLL()
	ON_WM_VSCROLL()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_KEYDOWN()
	ON_WM_CHAR()
	//}}AFX_MSG_MAP
	ON_MESSAGE(WM_GETFONT, OnGetFont)
	ON_MESSAGE(WM_LC_EDIT_CLOSED, OnEditClosed)
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CTerrainCtrl message handlers

BOOL CTerrainCtrl::Create(const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwStyle)
{
	ASSERT(pParentWnd->GetSafeHwnd());

	if (!CWnd::Create(TERRAINCTRL_CLASSNAME, NULL, dwStyle, rect, pParentWnd, nID)) 
		return FALSE;

	// Create titletips
#ifdef GRIDCONTROL_USE_TITLETIPS
	if (m_bTitleTips)		 
		m_TitleTip.Create(this);
#endif

	ResetScrollBars();
	return TRUE;
}

void CTerrainCtrl::PreSubclassWindow() 
{
	CWnd::PreSubclassWindow();
/*
	HFONT hFont = ::CreateFontIndirect(&m_Logfont);
	OnSetFont((LPARAM)hFont, 0);
	DeleteObject(hFont);
*/
	ResetScrollBars();	 
}

BOOL CTerrainCtrl::SubclassWindow(HWND hWnd)
{
	if (!CWnd::SubclassWindow(hWnd))
		return FALSE;

#ifdef GRIDCONTROL_USE_TITLETIPS
	if (m_bTitleTips && !IsWindow(m_TitleTip.m_hWnd))
		m_TitleTip.Create(this);
#endif

	return TRUE;
}

LRESULT CTerrainCtrl::OnGetFont(WPARAM /*wParam*/, LPARAM /*lParam*/)
{
	return (LRESULT) (HFONT) m_Font;
}

void CTerrainCtrl::OnPaint() 
{
	CPaintDC dc(this);		// device context for painting

	CDC MemDC;
	CRect rect;
    CBitmap bitmap, *pOldBitmap;
	dc.GetClipBox(&rect);
	MemDC.CreateCompatibleDC(&dc);
	bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
	pOldBitmap = MemDC.SelectObject(&bitmap);
	MemDC.SetWindowOrg(rect.left, rect.top);
	OnDraw(&MemDC);
	dc.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &MemDC, rect.left, rect.top, SRCCOPY);
	MemDC.SelectObject(pOldBitmap);
}

BOOL CTerrainCtrl::OnEraseBkgnd(CDC* pDC) 
{
	return TRUE;	// Don't erase the background.
}

// Custom background erasure. This gets called from within the OnDraw function,
// since we will (most likely) be using a memory DC to stop flicker. If we just
// erase the background normally through OnEraseBkgnd, and didn't fill the memDC's
// selected bitmap with colour, then all sorts of vis problems would occur
void CTerrainCtrl::EraseBkgnd(CDC* pDC)
{

	CRect  VisRect, ClipRect, rect;
	CBrush FixedBack(m_crFixedBkColour), TextBack(m_crTextBkColour);

	if (pDC->GetClipBox(ClipRect) == ERROR)
		return;

	int nFixedColumnWidth = GetFixedColumnWidth();
	int nFixedRowHeight = GetFixedRowHeight();
	GetClientRect(VisRect);
	VisRect.top = nFixedRowHeight;
	VisRect.left = nFixedColumnWidth;

	// Draw Fixed columns background
	if (ClipRect.left < nFixedColumnWidth && ClipRect.top < VisRect.bottom)
		pDC->FillRect(CRect(ClipRect.left, ClipRect.top, nFixedColumnWidth, VisRect.bottom),
					  &FixedBack);
		
	// Draw Fixed rows background
	if (ClipRect.top < nFixedRowHeight && 
		ClipRect.right > nFixedColumnWidth && ClipRect.left < VisRect.right)
		pDC->FillRect(CRect(nFixedColumnWidth-1, ClipRect.top, VisRect.right, nFixedRowHeight),
					  &FixedBack);

	// Draw non-fixed cell background
	if (rect.IntersectRect(VisRect, ClipRect)) 
	{
		CRect CellRect(max(nFixedColumnWidth, rect.left), max(nFixedRowHeight, rect.top),
					   rect.right, rect.bottom);
		pDC->FillRect(CellRect, &TextBack);
	}
}

void CTerrainCtrl::OnSize(UINT nType, int cx, int cy) 
{
  if (::IsWindow(GetSafeHwnd()) && GetFocus()->GetSafeHwnd() != GetSafeHwnd()) 
		SetFocus(); // Auto-destroy any InPlaceEdit's

	CWnd::OnSize(nType, cx, cy);
	ResetScrollBars();	  
}

UINT CTerrainCtrl::OnGetDlgCode() 
{
	UINT nCode = DLGC_WANTARROWS | DLGC_WANTCHARS;
/*
	if (m_bHandleTabKey && !IsCTRLpressed()) 
		nCode |= DLGC_WANTTAB;
*/
	return nCode;
}

// wParam = key pressed, lParam = modified
LRESULT CTerrainCtrl::OnEditClosed(WPARAM wParam, LPARAM lParam)
{
	// In case OnEndInPlaceEdit called as window is being destroyed
	if (!IsWindow(GetSafeHwnd()))
		return TRUE;

	// Only set as modified if it actually was, and ESC was not hit.
	if ((wParam != VK_ESCAPE) && (lParam == TRUE))
	{
	CWnd* pParent = GetOwner();
	if (pParent)
		pParent->SendMessage(WM_LC_EDIT_CLOSED);
//		SetModified(TRUE);
	}

	switch (wParam)
	{
	case VK_DOWN:
	case VK_UP:
	case VK_RIGHT:
	case VK_LEFT:
	case VK_NEXT:
	case VK_PRIOR:
	case VK_HOME:
	case VK_END:
		OnKeyDown(wParam, 0, 0);
		EditCell(m_idCurrentCell.row, m_idCurrentCell.col, wParam);
    }

	return TRUE;
}

// Handle horz scrollbar notifications
void CTerrainCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	if (GetFocus()->GetSafeHwnd() != GetSafeHwnd()) 
		SetFocus();  // Auto-destroy any InPlaceEdit's

#ifdef GRIDCONTROL_USE_TITLETIPS
	m_TitleTip.Hide();	// hide any titletips
#endif

	int scrollPos = GetScrollPos32(SB_HORZ);

	CELLID idTopLeft = GetTopleftNonFixedCell();

	CRect rect;
	GetClientRect(rect);

	switch (nSBCode)
	{
		case SB_LINERIGHT:
		if (scrollPos < m_nHScrollMax)
		{
			int xScroll = GetColumnWidth(nTopLeftCol);
			SetScrollPos32(SB_HORZ, scrollPos + xScroll);
			if (GetScrollPos32(SB_HORZ) == scrollPos)
				break; // didn't work

			rect.left = GetFixedColumnWidth() + xScroll;
			ScrollWindow(-xScroll, 0, rect);
			rect.left = rect.right - xScroll;
			InvalidateRect(rect);
		} break;

		case SB_LINELEFT:
		if (scrollPos > 0 && idTopLeft.col > GetFixedColumnCount())
		{
			int xScroll = GetColumnWidth(nTopLeftCol-1);
			SetScrollPos32(SB_HORZ, max(0,scrollPos - xScroll));
			rect.left = GetFixedColumnWidth();
			ScrollWindow(xScroll, 0, rect);
			rect.right = rect.left + xScroll;
			InvalidateRect(rect);
		} break;

		case SB_PAGERIGHT:
		if (scrollPos < m_nHScrollMax)
		{
			rect.left = GetFixedColumnWidth();
			int offset = rect.Width();
			int pos = min(m_nHScrollMax, scrollPos + offset);
			SetScrollPos32(SB_HORZ, pos);
			rect.left = GetFixedColumnWidth();
			InvalidateRect(rect);
		} break;

		case SB_PAGELEFT:
		if (scrollPos > 0)
		{
			rect.left = GetFixedColumnWidth();
			int offset = -rect.Width();
			int pos = max(0, scrollPos + offset);
			SetScrollPos32(SB_HORZ, pos);
			rect.left = GetFixedColumnWidth();
			InvalidateRect(rect);
		} break;

		case SB_THUMBPOSITION:
		case SB_THUMBTRACK:
		{
			SetScrollPos32(SB_HORZ, GetScrollPos32(SB_HORZ, TRUE));
			rect.left = GetFixedColumnWidth();
			InvalidateRect(rect);
		} break;

		case SB_LEFT:
		if (scrollPos > 0)
		{
			SetScrollPos32(SB_HORZ, 0);
			Invalidate();
		} break;

		case SB_RIGHT:
		if (scrollPos < m_nHScrollMax)
		{
			SetScrollPos32(SB_HORZ, m_nHScrollMax);
			Invalidate();
		} break;
	}
}

// Handle vert scrollbar notifications
void CTerrainCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	if (GetFocus()->GetSafeHwnd() != GetSafeHwnd()) 
		SetFocus(); 	   // Auto-destroy any InPlaceEdit's

#ifdef GRIDCONTROL_USE_TITLETIPS
	m_TitleTip.Hide();	// hide any titletips
#endif

	// Get the scroll position ourselves to ensure we get a 32 bit value
	int scrollPos = GetScrollPos32(SB_VERT);

	CELLID idTopLeft = GetTopleftNonFixedCell();

	CRect rect;
	GetClientRect(rect);

	switch (nSBCode)
	{
		case SB_LINEDOWN:
		if (scrollPos < m_nVScrollMax)
		{
			int yScroll = GetRowHeight(nTopLeftRow);
			SetScrollPos32(SB_VERT, scrollPos + yScroll);
			if (GetScrollPos32(SB_VERT) == scrollPos)
				break; // didn't work

			rect.top = GetFixedRowHeight() + yScroll;
			ScrollWindow( 0, -yScroll, rect);
			rect.top = rect.bottom - yScroll;
			InvalidateRect(rect);
		} break;

		case SB_LINEUP:
		if (scrollPos > 0 && idTopLeft.row > GetFixedRowCount())
		{
			int yScroll = GetRowHeight(nTopLeftRow-1);
			SetScrollPos32(SB_VERT, max(0, scrollPos - yScroll));
			rect.top = GetFixedRowHeight();
			ScrollWindow(0, yScroll, rect);
			rect.bottom = rect.top + yScroll;
			InvalidateRect(rect);
		} break;

		case SB_PAGEDOWN:
		if (scrollPos < m_nVScrollMax)
		{
			rect.top = GetFixedRowHeight();
			scrollPos = min(m_nVScrollMax, scrollPos + rect.Height());
			SetScrollPos32(SB_VERT, scrollPos);
			rect.top = GetFixedRowHeight();
			InvalidateRect(rect);
		} break;

		case SB_PAGEUP:
		if (scrollPos > 0)
		{
			rect.top = GetFixedRowHeight();
			int offset = -rect.Height();
			int pos = max(0, scrollPos + offset);
			SetScrollPos32(SB_VERT, pos);
			rect.top = GetFixedRowHeight();
			InvalidateRect(rect);
		} break;

		case SB_THUMBPOSITION:
		case SB_THUMBTRACK:
		{
			SetScrollPos32(SB_VERT, GetScrollPos32(SB_VERT, TRUE));
			rect.top = GetFixedRowHeight();
			InvalidateRect(rect);
		} break;

		case SB_TOP:
		if (scrollPos > 0)
		{
			SetScrollPos32(SB_VERT, 0);
			Invalidate();
		} break;

		case SB_BOTTOM:
		if (scrollPos < m_nVScrollMax)
		{
			SetScrollPos32(SB_VERT, m_nVScrollMax);
			Invalidate();
		}
	}
}

void CTerrainCtrl::OnDraw(CDC* pDC)
{
	int row,col;
	CRect clientRect;
	GetClientRect(clientRect);

	CRect clipRect;
	if (pDC->GetClipBox(&clipRect) == ERROR)
		return;

	// OnEraseBkgnd does nothing, so erase bkgnd here.
	// This necessary since we may be using a Memory DC.
	EraseBkgnd(pDC);

	int nFixedRowHeight = GetFixedRowHeight();
	int nFixedColWidth	= GetFixedColumnWidth();

	CELLID idTopLeft = GetTopleftNonFixedCell();
/*
	CRect VisRect;
	CCellRange VisCellRange = GetVisibleNonFixedCellRange(VisRect);
	int maxVisibleRow = VisCellRange.GetMaxRow(),
		maxVisibleCol = VisCellRange.GetMaxCol();
*/
	// calc bottom
	int bottom = GetFixedRowHeight();
	for (int i = idTopLeft.row; i < m_nRows; i++)
	{
		bottom += GetRowHeight(i);
		if (bottom >= clientRect.bottom)
		{
			bottom = clientRect.bottom;
			break;
		}
	}								 
	int maxVisibleRow = min(i, m_nRows - 1);
	
	// calc right
	int right = GetFixedColumnWidth();
	for (i = idTopLeft.col; i < m_nCols; i++)
	{
		right += GetColumnWidth(i);
		if (right >= clientRect.right)
		{
			right = clientRect.right;
			break;
		}
	}
	int maxVisibleCol = min(i, m_nCols - 1);

	// draw top-left cell
	CRect rect;
	rect.top = 0;
	rect.bottom = GetRowHeight(0)-1;
	rect.left = 0;
	rect.right = GetColumnWidth(0)-1;
	DrawFixedCell(pDC, 0, 0, rect);
	 
	// draw fixed column cells
	rect.bottom = nFixedRowHeight-1;
	for (row = idTopLeft.row; row <= maxVisibleRow; row++)
	{
		rect.top = rect.bottom+1;
		rect.bottom = rect.top + GetRowHeight(row)-1;

		// rect.bottom = bottom pixel of previous row
		if (rect.top > clipRect.bottom) break;	  // Gone past cliprect
		if (rect.bottom < clipRect.top) continue; // Reached cliprect yet?

		rect.left = 0;
		rect.right = GetColumnWidth(0)-1;

		if (rect.left > clipRect.right) break;	  // gone past cliprect
		if (rect.right < clipRect.left) continue; // Reached cliprect yet?

		DrawFixedCell(pDC, row, 0, rect);
	}

	// draw end of column
	if (rect.bottom < clientRect.bottom)
	{
		rect.top = rect.bottom+1;
		rect.bottom = clientRect.bottom-1;

		if (rect.top < clipRect.bottom)
		if (rect.bottom > clipRect.top)
		if (rect.left < clipRect.right)
		if (rect.right > clipRect.left)
			DrawFixedCell(pDC, 0, 0, rect);
	}
	
	// draw fixed row cells  0..m_nFixedRows, m_nFixedCols..n
	rect.top = 0;
	rect.bottom = GetRowHeight(0)-1;

	// rect.bottom = bottom pixel of previous row
	if (rect.top < clipRect.bottom) // Gone past cliprect
	if (rect.bottom > clipRect.top) // Reached cliprect yet?
	{
		rect.right = nFixedColWidth-1;
		for (col = idTopLeft.col; col <= maxVisibleCol; col++)
		{
			rect.left = rect.right+1;
			rect.right = rect.left + GetColumnWidth(col)-1;
			
			if (rect.left > clipRect.right) break;	  // gone past cliprect
			if (rect.right < clipRect.left) continue; // Reached cliprect yet?
			
			DrawFixedCell(pDC, 0, col, rect);
		}
	}

	// draw end of row
	if (rect.right < clientRect.right)
	{
		rect.left = rect.right+1;
		rect.right = clientRect.right - 1;

		if (rect.top < clipRect.bottom)
		if (rect.bottom > clipRect.top)
		if (rect.left < clipRect.right)
		if (rect.right > clipRect.left)
			DrawFixedCell(pDC, 0, 0, rect);

	}

	// draw rest of non-fixed cells
	rect.bottom = nFixedRowHeight-1;
	for (row = idTopLeft.row; row <= maxVisibleRow; row++)
	{
		rect.top = rect.bottom+1;
		rect.bottom = rect.top + GetRowHeight(row)-1;

		// rect.bottom = bottom pixel of previous row
		if (rect.top > clipRect.bottom) break;				  // Gone past cliprect
		if (rect.bottom < clipRect.top) continue;			  // Reached cliprect yet?

		rect.right = nFixedColWidth-1;
		for (col = idTopLeft.col; col <= maxVisibleCol; col++)
		{
			rect.left = rect.right+1;
			rect.right = rect.left + GetColumnWidth(col)-1;

			if (rect.left > clipRect.right) break;		  // gone past cliprect
			if (rect.right < clipRect.left) continue;	  // Reached cliprect yet?

			DrawCell(pDC, row, col, rect);
		}
	}
}

// Get/Set scroll position using 32 bit functions
int CTerrainCtrl::GetScrollPos32(int nBar, BOOL bGetTrackPos /* = FALSE */)
{
	SCROLLINFO si;
	si.cbSize = sizeof(SCROLLINFO);

	if (bGetTrackPos)
	{
		if (GetScrollInfo(nBar, &si, SIF_TRACKPOS))
			return si.nTrackPos;
	}
	else 
	{
		if (GetScrollInfo(nBar, &si, SIF_POS))
			return si.nPos;
	}

	return 0;
}

BOOL CTerrainCtrl::SetScrollPos32(int nBar, int nPos, BOOL bRedraw /* = TRUE */)
{
	SCROLLINFO si;
	si.cbSize = sizeof(SCROLLINFO);
	si.fMask  = SIF_POS;
	si.nPos   = nPos;
	return SetScrollInfo(nBar, &si, bRedraw);
}

void CTerrainCtrl::ResetScrollBars()
{
	if (!::IsWindow(GetSafeHwnd())) 
		return;

	CRect rect;
	GetClientRect(rect);
	rect.left  += GetFixedColumnWidth();
	rect.top   += GetFixedRowHeight();
	if (rect.left >= rect.right || rect.top >= rect.bottom)
		return;

	CRect VisibleRect(GetFixedColumnWidth(), GetFixedRowHeight(), rect.right, rect.bottom);
	CRect VirtualRect(GetFixedColumnWidth(), GetFixedRowHeight(), GetVirtualWidth(), GetVirtualHeight());
/*
	CCellRange visibleCells = GetUnobstructedNonFixedCellRange();
	if (!IsValid(visibleCells)) return;
*/
	SCROLLINFO si;
	si.cbSize = sizeof(SCROLLINFO);
	si.fMask  = SIF_PAGE;
	si.nPage  = VisibleRect.Width();
	SetScrollInfo(SB_HORZ, &si, FALSE); 
	si.nPage  = VisibleRect.Height();
	SetScrollInfo(SB_VERT, &si, FALSE); 

	if (VisibleRect.Height() < VirtualRect.Height())
		m_nVScrollMax = VirtualRect.Height()-1;//-VisibleRect.Height();//+GetRowHeight(0);
	else
		m_nVScrollMax = 0;

	if (VisibleRect.Width() < VirtualRect.Width())
		m_nHScrollMax = VirtualRect.Width()-1;//-VisibleRect.Width();//+GetColumnWidth(0);
	else
		m_nHScrollMax = 0;

	ASSERT(m_nVScrollMax < INT_MAX && m_nHScrollMax < INT_MAX); // This should be fine :)
	SetScrollRange(SB_VERT, 0, m_nVScrollMax, TRUE);
	SetScrollRange(SB_HORZ, 0, m_nHScrollMax, TRUE);
}

BOOL CTerrainCtrl::DrawFixedCell(CDC* pDC, int nRow, int nCol, CRect rect, BOOL bEraseBk)
{
	if (bEraseBk)
	{
		CBrush brush(m_crFixedBkColour);
		pDC->FillRect(rect, &brush);
	}
	pDC->SetTextColor(m_crFixedTextColour);
	
	int nSavedDC = pDC->SaveDC();
/*	
	// Create the appropriate font and select into DC
	LOGFONT lf, *pLF = GetItemFont(nRow, nCol);
	if (pLF)
		memcpy(&lf, pLF, sizeof(LOGFONT));
	else
		memcpy(&lf, &m_Logfont, sizeof(LOGFONT));
		
	CCellID FocusCell = GetFocusCell();
	if (FocusCell.row == nRow || FocusCell.col == nCol)
		lf.lfWeight = SELECTED_CELL_FONT_WEIGHT;
	
	CFont Font;
	Font.CreateFontIndirect(&lf);
	pDC->SelectObject(&Font);

	if (IsValid(FocusCell) &&  (FocusCell.row == nRow || FocusCell.col == nCol))
	{
		rect.right++; rect.bottom++;
		pDC->DrawEdge(rect, EDGE_RAISED, BF_RECT);
		rect.DeflateRect(1,1);
	}
	else
*/	{
		CPen lightpen(PS_SOLID, 1, ::GetSysColor(COLOR_3DHIGHLIGHT)),
			  darkpen(PS_SOLID, 1, ::GetSysColor(COLOR_3DDKSHADOW)),
			 *pOldPen = pDC->GetCurrentPen();
	
		pDC->SelectObject(&lightpen);
		pDC->MoveTo(rect.right, rect.top);
		pDC->LineTo(rect.left, rect.top);
		pDC->LineTo(rect.left, rect.bottom);

		pDC->SelectObject(&darkpen);
		pDC->MoveTo(rect.right, rect.top);
		pDC->LineTo(rect.right, rect.bottom);
		pDC->LineTo(rect.left, rect.bottom);

		pDC->SelectObject(pOldPen);
		rect.DeflateRect(1,1);
	}

	pDC->SetBkMode(TRANSPARENT);
	rect.DeflateRect(m_nMargin, 0);

	if ((nRow != 0 || nCol != 0) && nRow < m_nRows && nCol < m_nCols)
	{
		char szText[10];
		sprintf(szText, "%d", nRow == 0 ? nCol : nRow);
		DrawText(pDC->m_hDC, szText, -1, rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
	}

	pDC->RestoreDC(nSavedDC);
	return TRUE;
}

BOOL CTerrainCtrl::DrawCell(CDC* pDC, int nRow, int nCol, CRect rect, BOOL bEraseBk)
{
	COLORREF TextClr = m_crFixedTextColour;

	int nSavedDC = pDC->SaveDC();

	pDC->SetBkMode(TRANSPARENT);

	if (m_RowData[nRow][nCol].state & GS_FOCUSED) 
	{
		rect.right++; rect.bottom++;	// FillRect doesn't draw RHS or bottom
		if (bEraseBk) 
		{
			CBrush brush(m_crTextBkColour);
			pDC->FillRect(rect, &brush);
		}
		rect.right--; rect.bottom--;	
		pDC->SelectStockObject(BLACK_PEN);
		pDC->SelectStockObject(NULL_BRUSH);
		pDC->Rectangle(rect);
		pDC->SetTextColor(TextClr);

		rect.DeflateRect(1,1);

	}
	else if (m_RowData[nRow][nCol].state & GS_SELECTED) 
	{
		rect.right++; rect.bottom++;	// FillRect doesn't draw RHS or bottom
		pDC->FillSolidRect(rect, ::GetSysColor(COLOR_HIGHLIGHT));
		rect.right--; rect.bottom--;
		pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
	}
	else
	{
		rect.right++; rect.bottom++;	// FillRect doesn't draw RHS or bottom
		if (bEraseBk) 
		{
			CBrush brush(m_crTextBkColour);
			pDC->FillRect(rect, &brush);
		}
		rect.right--; rect.bottom--;
		pDC->SetTextColor(TextClr);
	}
/*
	if (Item.state & GVIS_DROPHILITED)
	{
		pDC->SelectStockObject(BLACK_PEN);
		pDC->SelectStockObject(NULL_BRUSH);
		pDC->Rectangle(rect);
	}
*/

	CPen lightpen(PS_SOLID, 1, RGB(0xE0, 0xE0, 0xE0)),
		 darkpen(PS_SOLID, 1, RGB(0,0,0)),
		 *pOldPen = pDC->GetCurrentPen();

	int left = nCol == 1 ? GRIDSIZE/2-1 : 0;
	int top = nRow == 1 ? GRIDSIZE/2-1 : 0;
	int right = nCol == m_nCols-1 ? GRIDSIZE/2-1 : -1;
	int bottom = nRow == m_nRows-1 ? GRIDSIZE/2-1 : -1;

	if ((nRow-1) % 3)
	{
		pDC->SelectObject(&lightpen);
		pDC->MoveTo(rect.left + left, (rect.top + rect.bottom)/2);
		pDC->LineTo(rect.right - right, (rect.top + rect.bottom)/2);
	}

	if ((nCol-1) % 3)
	{
		pDC->SelectObject(&lightpen);
		pDC->MoveTo((rect.left + rect.right)/2, rect.top + top);
		pDC->LineTo((rect.left + rect.right)/2, rect.bottom - bottom);
	}

	if ((nRow-1) % 3 == 0)
	{
		pDC->SelectObject(&darkpen);
		pDC->MoveTo(rect.left + left, (rect.top + rect.bottom)/2);
		pDC->LineTo(rect.right - right, (rect.top + rect.bottom)/2);
	}

	if ((nCol-1) % 3 == 0)
	{
		pDC->SelectObject(&darkpen);
		pDC->MoveTo((rect.left + rect.right)/2, rect.top + top);
		pDC->LineTo((rect.left + rect.right)/2, rect.bottom - bottom);
	}

	pDC->SelectObject(pOldPen);

/*	// Create the appropriate font and select into DC
	CFont Font;
	LOGFONT *pLF = GetItemFont(nRow, nCol);
	if (pLF)
		Font.CreateFontIndirect(pLF);
	else
		Font.CreateFontIndirect(&m_Logfont);
*/
	CFont *pOldFont = pDC->SelectObject(&m_Font);
	rect.DeflateRect(m_nMargin, 0);
	rect.OffsetRect(GRIDSIZE/2, GRIDSIZE/4+2);

	char szText[10];
	sprintf(szText, "%.0f", ControlPoint(nRow, nCol));

	DrawText(pDC->m_hDC, szText, -1, rect, DT_LEFT|DT_VCENTER|DT_SINGLELINE);

	pDC->SelectObject(pOldFont);

	pDC->RestoreDC(nSavedDC);
	return TRUE;
}

BOOL CTerrainCtrl::SetRowCount(int nRows)
{
	ASSERT(nRows > 0);
	if (nRows == m_nRows)
		return TRUE;

	if (m_idCurrentCell.row >= nRows)
		SetFocusCell(-1,-1);

	int addedRows = nRows - m_nRows;

	// Change the number of rows.
	m_nRows = nRows;
	m_RowData.SetSize(m_nRows);

	// If we have just added rows, we need to construct new elements for each cell
	// and set the default row height
	if (addedRows > 0) 
	{
		// initialize row heights and data
		int startRow = nRows - addedRows;
		for (int row = startRow; row < m_nRows; row++)
		{
			m_RowData[row].SetSize(m_nCols);
			for (int col = 0; col < m_nCols; col++)
				m_RowData[row][col].state = 0;
		}
	}

	if (GetSafeHwnd())
	{
		ResetScrollBars();
		Invalidate();
	}

	return TRUE;
}

BOOL CTerrainCtrl::SetColumnCount(int nCols)
{
	ASSERT(nCols > 0);

	if (nCols == m_nCols)
		return TRUE;

	if (m_idCurrentCell.col >= nCols)
		SetFocusCell(-1,-1);

	int addedCols = nCols - m_nCols;

	// Change the number of columns.
	m_nCols = nCols;

	// Change the number of columns in each row.
	for (int i = 0; i < m_nRows; i++)
		m_RowData[i].SetSize(nCols);

	// If we have just added columns, we need to construct new elements for each cell
	// and set the default column width
	if (addedCols > 0)
	{
		int startCol = nCols - addedCols;

		for (int row = 0; row < m_nRows; row++)
			for (int col = startCol; col < m_nCols; col++)
				m_RowData[row][col].state = 0;
	}

	if (GetSafeHwnd())
	{
		ResetScrollBars();
		Invalidate();
	}
	return TRUE;
}

CELLID CTerrainCtrl::GetTopleftNonFixedCell()
{
	CELLID cell;

	int nVertScroll = GetScrollPos(SB_VERT), nHorzScroll = GetScrollPos(SB_HORZ);

	int nColumn = m_nFixedCols, nRight = 0;
	while (nRight < nHorzScroll && nColumn < (m_nCols-1))
	{
		nColumn++;
		nRight += GetColumnWidth(nColumn);
	}

	int nRow = m_nFixedRows, nTop = 0;
	while (nTop < nVertScroll && nRow < (m_nRows-1))
	{
		nRow++;
		nTop += GetRowHeight(nRow);
	}

	cell.row = nRow;
	cell.col = nColumn;

	return cell;
}

void CTerrainCtrl::SetControlPoints(int uCount, int vCount, float** pControl)
{
	SetRowCount(uCount+1);
	SetColumnCount(vCount+1);
	m_pControl = pControl;
	InvalidateRect(NULL, FALSE);
}

void CTerrainCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
/*
	HWND hOldFocusWnd = ::GetFocus();
*/
	m_LeftClickDownPoint = point;
	m_LeftClickDownCell = GetCellFromPt(point);
	if (!IsValid(m_LeftClickDownCell))
		return;
/*
	m_SelectionStartCell = (nFlags & MK_SHIFT)? m_idCurrentCell : m_LeftClickDownCell;
*/
	SetFocus(); // Auto-destroy any InPlaceEdit's

	// If the user clicks on the current cell, then prepare to edit it.
	// (If the user moves the mouse, then dragging occurs)
	if (m_LeftClickDownCell == m_idCurrentCell)
	{
		m_MouseMode = MOUSE_PREPARE_EDIT;
		return;
	}
	else
	{
		SetFocusCell(-1,-1);
		SetFocusCell(max(m_LeftClickDownCell.row, 1),
					 max(m_LeftClickDownCell.col, 1));
	}
/*
	// If the user clicks on a selected cell, then prepare to drag it.
	// (If the user moves the mouse, then dragging occurs)
	if (m_bAllowDragAndDrop && hOldFocusWnd == GetSafeHwnd() && 
		GetItemState(m_LeftClickDownCell.row, m_LeftClickDownCell.col) & GVNI_SELECTED)
	{
		m_MouseMode = MOUSE_PREPARE_DRAG;
		return;
	}
*/
	SetCapture();

	// If Ctrl pressed, save the current cell selection. This will get added
/*	// to the new cell selection at the end of the cell selection process
	m_PrevSelectedCellMap.RemoveAll();
	if (nFlags & MK_CONTROL)
	{
		for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
		{
			DWORD key;
			CCellID cell;
			m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
			m_PrevSelectedCellMap.SetAt(key, cell);
		}
	}
		
	if (m_LeftClickDownCell.row < GetFixedRowCount())
		OnFixedRowClick(m_LeftClickDownCell);
	else if (m_LeftClickDownCell.col < GetFixedColumnCount())
		OnFixedColumnClick(m_LeftClickDownCell);
	else
	{
		m_MouseMode = m_bListMode? MOUSE_SELECT_ROW : MOUSE_SELECT_CELLS;
		OnSelecting(m_LeftClickDownCell);
	}

	m_nTimerID = SetTimer(WM_LBUTTONDOWN, m_nTimerInterval, 0);

	m_LastMousePoint = point;
*/
}

void CTerrainCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
{
	CWnd::OnLButtonUp(nFlags, point);
	ClipCursor(NULL);

	if (GetCapture()->GetSafeHwnd() == GetSafeHwnd())
	{
		ReleaseCapture();
/*		KillTimer(m_nTimerID);
		m_nTimerID = 0;
*/	}

	// m_MouseMode == MOUSE_PREPARE_EDIT only if user clicked down on current cell
	// and then didn't move mouse before clicking up (releasing button)
	if (m_MouseMode == MOUSE_PREPARE_EDIT)
	{
		EditCell(m_idCurrentCell.row, m_idCurrentCell.col, VK_LBUTTON);
	}
	// m_MouseMode == MOUSE_PREPARE_DRAG only if user clicked down on a selected cell
/*	// and then didn't move mouse before clicking up (releasing button)
	else if (m_MouseMode == MOUSE_PREPARE_DRAG) 
	{
		ResetSelectedRange();
	}
*/
	m_MouseMode = MOUSE_NOTHING;
	SetCursor(::LoadCursor(NULL, IDC_ARROW));

	if (!IsValid(m_LeftClickDownCell))
		return;
/*
	CWnd *pOwner = GetOwner();
	if (pOwner && IsWindow(pOwner->m_hWnd))
		pOwner->PostMessage(WM_COMMAND, MAKELONG(GetDlgCtrlID(), BN_CLICKED), 
							(LPARAM) GetSafeHwnd());
*/
}

void CTerrainCtrl::OnMouseMove(UINT nFlags, CPoint point) 
{
	CRect rect;
	GetClientRect(rect);
/*
	// If outside client area, return (unless we are drag n dropping)
	if (m_MouseMode != MOUSE_DRAGGING && !rect.PtInRect(point))
		return;

	// If the left mouse button is up, then test to see if row/column sizing is imminent
	if (!(nFlags & MK_LBUTTON))
	{
		if (point.y < GetFixedRowHeight() && m_bAllowColumnResize)
		{
			CCellID idCurrentCell = GetCellFromPt(point);
			CPoint start;
			if (!GetCellOrigin(idCurrentCell, &start)) return;

			int endx = start.x + GetColumnWidth(idCurrentCell.col);

			if ((point.x - start.x <= m_nResizeCaptureRange && idCurrentCell.col != 0) || 
				endx - point.x <= m_nResizeCaptureRange)
			{
				if (m_MouseMode != MOUSE_OVER_COL_DIVIDE)
					SetCursor(::LoadCursor(NULL, IDC_SIZEWE));
				m_MouseMode = MOUSE_OVER_COL_DIVIDE;
			}
			else 
			{
				if (m_MouseMode != MOUSE_NOTHING)
					SetCursor(::LoadCursor(NULL, IDC_ARROW));
				m_MouseMode = MOUSE_NOTHING;
			}
		}
		else if (point.x < GetFixedColumnWidth() && m_bAllowRowResize)
		{
			CCellID idCurrentCell = GetCellFromPt(point);
			CPoint start;
			if (!GetCellOrigin(idCurrentCell, &start)) return;

			int endy = start.y + GetRowHeight(idCurrentCell.row);

			if ((point.y - start.y <= m_nResizeCaptureRange && idCurrentCell.row != 0) || 
				endy - point.y <= m_nResizeCaptureRange)
			{
				if (m_MouseMode != MOUSE_OVER_ROW_DIVIDE)
					SetCursor(::LoadCursor(NULL, IDC_SIZENS));
				m_MouseMode = MOUSE_OVER_ROW_DIVIDE;
			}
			else
			{
				if (m_MouseMode != MOUSE_NOTHING)
					SetCursor(::LoadCursor(NULL, IDC_ARROW));
				m_MouseMode = MOUSE_NOTHING;
			}
		}
		else
		{
			if (m_MouseMode != MOUSE_NOTHING)
				SetCursor(::LoadCursor(NULL, IDC_ARROW));
			m_MouseMode = MOUSE_NOTHING;
		}

#ifdef GRIDCONTROL_USE_TITLETIPS
		if (m_MouseMode == MOUSE_NOTHING && m_bTitleTips)
		{
			CCellID idCurrentCell = GetCellFromPt(point);
			CRect rect;
			if (GetCellRect(idCurrentCell.row, idCurrentCell.col, rect))
				m_TitleTip.Show( rect, GetItemText(idCurrentCell.row, idCurrentCell.col), 0);
		}
#endif

		m_LastMousePoint = point;
		return;
	}

	if (!IsValid(m_LeftClickDownCell))
	{
		m_LastMousePoint = point;
		return;
	}

	// If the left mouse button is down, the process appropriately
	if (nFlags & MK_LBUTTON) 
	{
		switch(m_MouseMode)
		{
			case MOUSE_SELECT_ALL:
				break;

			case MOUSE_SELECT_COL:
			case MOUSE_SELECT_ROW:	  
			case MOUSE_SELECT_CELLS:
			{
				CCellID idCurrentCell = GetCellFromPt(point);
				if (!IsValid(idCurrentCell))
					return;
				OnSelecting(idCurrentCell);
//				SetFocusCell(max(idCurrentCell.row, m_nFixedRows),
//				max(idCurrentCell.col, m_nFixedCols));
				if (idCurrentCell.row >= m_nFixedRows &&
					idCurrentCell.col >= m_nFixedCols)
					SetFocusCell(idCurrentCell);
				break;
			}

			case MOUSE_PREPARE_DRAG:
				OnBeginDrag();
				break;
		}	 
	}

	m_LastMousePoint = point;
*/
}

// Sets the currently selected cell
void CTerrainCtrl::SetFocusCell(CELLID cell)
{
	SetFocusCell(cell.row, cell.col);
}

void CTerrainCtrl::SetFocusCell(int nRow, int nCol)
{
	CELLID cell(nRow, nCol);

	if (cell == m_idCurrentCell) 
		return;

	CELLID idPrev = m_idCurrentCell;
	m_idCurrentCell = cell;

	if (IsValid(idPrev)) 
	{
/*		SendMessageToParent(idPrev.row, idPrev.col, GVN_SELCHANGING);
*/
		m_RowData[idPrev.row][idPrev.col].state &= ~GS_FOCUSED;

		RedrawCell(idPrev);
/*
		if (idPrev.col != m_idCurrentCell.col)
			for (int row = 0; row < m_nFixedRows; row++)
				RedrawCell(row, idPrev.col);
		if (idPrev.row != m_idCurrentCell.row)
			for (int col = 0; col < m_nFixedCols; col++) 
				RedrawCell(idPrev.row, col);
*/	}

	if (IsValid(m_idCurrentCell))
	{
		m_RowData[nRow][nCol].state |= GS_FOCUSED;

		RedrawCell(m_idCurrentCell);
/*
		if (idPrev.col != m_idCurrentCell.col)
			for (int row = 0; row < m_nFixedRows; row++) 
				RedrawCell(row, m_idCurrentCell.col);
		if (idPrev.row != m_idCurrentCell.row)
			for (int col = 0; col < m_nFixedCols; col++) 
				RedrawCell(m_idCurrentCell.row, col);

		SendMessageToParent(m_idCurrentCell.row, m_idCurrentCell.col, GVN_SELCHANGED); 
*/	}
}

BOOL CTerrainCtrl::IsValid(int nRow, int nCol)
{
	return (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols);
}

BOOL CTerrainCtrl::IsValid(CELLID cell)
{
	return IsValid(cell.row, cell.col);
}

// Get cell from point
CELLID CTerrainCtrl::GetCellFromPt(CPoint point, BOOL bAllowFixedCellCheck)
{
	CELLID idTopLeft = GetTopleftNonFixedCell();
	CELLID cellID; // return value

	// calculate column index
	int fixedColWidth = GetFixedColumnWidth();

	if (point.x < 0 || (!bAllowFixedCellCheck && point.x < fixedColWidth)) // not in window
		cellID.col = -1;
	else if (point.x < fixedColWidth) // in fixed col
	{
		int xpos = 0;
		for (int col = 0; col < m_nFixedCols; col++)
		{
			xpos += GetColumnWidth(col);
			if (xpos > point.x) break;
		}
		cellID.col = col;
	}
	else	// in non-fixed col
	{
		int xpos = fixedColWidth;
		for (int col = idTopLeft.col; col < m_nCols; col++)
		{
			xpos += GetColumnWidth(col);
			if (xpos > point.x) break;
		}

		if (col >= GetColumnCount())
			cellID.col = -1;
		else
			cellID.col = col;
	}
	
	// calculate row index
	int fixedRowHeight = GetFixedRowHeight();
	if (point.y < 0 || (!bAllowFixedCellCheck && point.y < fixedRowHeight)) // not in window
		cellID.row = -1;
	else if (point.y < fixedRowHeight) // in fixed col
	{
		int ypos = 0;
		for (int row = 0; row < m_nFixedRows; row++)
		{
			ypos += GetRowHeight(row);
			if (ypos > point.y) break;
		}
		cellID.row = row;
	}
	else
	{
		int ypos = fixedRowHeight;
		for (int row = idTopLeft.row; row < GetRowCount(); row++)
		{
			ypos += GetRowHeight(row);
			if (ypos > point.y) break;
		}

		if (row >= GetRowCount())
			cellID.row = -1;
		else
			cellID.row = row;
	}

	return cellID;
}

// Forces a redraw of a cell immediately using a direct
// DC construction,  or the supplied DC
BOOL CTerrainCtrl::RedrawCell(CELLID cell, CDC* pDC)
{
	return RedrawCell(cell.row, cell.col, pDC);
}

BOOL CTerrainCtrl::RedrawCell(int nRow, int nCol, CDC* pDC)
{
	BOOL bResult = TRUE;
	BOOL bMustReleaseDC = FALSE;

	if (!IsCellVisible(nRow, nCol))
		return FALSE;

	CRect rect;
	if (!GetCellRect(nRow, nCol, rect))
		return FALSE;

	if (!pDC)
	{
		pDC = GetDC();
		if (pDC)
			bMustReleaseDC = TRUE;
	}

	if (pDC)
	{
		// Redraw cells directly
		if (nRow < m_nFixedRows || nCol < m_nFixedCols)
			bResult = DrawFixedCell(pDC, nRow, nCol, rect, TRUE);
		else
			bResult = DrawCell(pDC, nRow, nCol, rect, TRUE);
/*
		// Since we have erased the background, we will need to redraw the gridlines
		CPen pen;
		try {
			pen.CreatePen(PS_SOLID, 0, m_crGridColour);
		} catch (...) {}

		CPen* pOldPen = (CPen*) pDC->SelectObject(&pen);
		if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ) 
		{
			pDC->MoveTo(rect.left,	  rect.bottom);
			pDC->LineTo(rect.right+1, rect.bottom);
		}
		if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT) 
		{
			pDC->MoveTo(rect.right, rect.top);
			pDC->LineTo(rect.right, rect.bottom+1);    
		}
		pDC->SelectObject(pOldPen);
*/
	} else
		InvalidateRect(rect, TRUE); 	// Could not get a DC - invalidate it anyway
										// and hope that OnPaint manages to get one

	if (bMustReleaseDC) 
		ReleaseDC(pDC);

	return bResult;
}

// returns the top left point of the cell. Returns FALSE if cell not visible.
BOOL CTerrainCtrl::GetCellOrigin(CELLID cell, LPPOINT p)
{
	return GetCellOrigin(cell.row, cell.col, p);
}

BOOL CTerrainCtrl::GetCellOrigin(int nRow, int nCol, LPPOINT p)
{
	int i;

	if (!IsValid(nRow, nCol))
		return FALSE;

	CELLID idTopLeft;
	if (nCol >= m_nFixedCols || nRow >= m_nFixedRows)
		idTopLeft = GetTopleftNonFixedCell();

	if ((nRow >= m_nFixedRows && nRow < idTopLeft.row) ||
		(nCol>= m_nFixedCols && nCol < idTopLeft.col))
		return FALSE;

	p->x = 0;
	if (nCol < m_nFixedCols) // is a fixed column
		for (i = 0; i < nCol; i++)
			p->x += GetColumnWidth(i);
	else // is a scrollable data column
	{
		for (i = 0; i < m_nFixedCols; i++)
			p->x += GetColumnWidth(i);
		for (i = idTopLeft.col; i < nCol; i++)
			p->x += GetColumnWidth(i);
	}

	p->y = 0;
	if (nRow < m_nFixedRows) // is a fixed row
		for (i = 0; i < nRow; i++)
			p->y += GetRowHeight(i);
	else // is a scrollable data row
	{
		for (i = 0; i < m_nFixedRows; i++)
			p->y += GetRowHeight(i);
		for (i = idTopLeft.row; i < nRow; i++)
			p->y += GetRowHeight(i);
	}

	return TRUE;
}

// Returns the bounding box of the cell
BOOL CTerrainCtrl::GetCellRect(CELLID cell, LPRECT pRect)
{
	return GetCellRect(cell.row, cell.col, pRect);
}

BOOL CTerrainCtrl::GetCellRect(int nRow, int nCol, LPRECT pRect)
{
	CPoint CellOrigin;
	if (!GetCellOrigin(nRow, nCol, &CellOrigin))
		return FALSE;

	pRect->left   = CellOrigin.x;
	pRect->top	  = CellOrigin.y;
	pRect->right  = CellOrigin.x + GetColumnWidth(nCol)-1;
	pRect->bottom = CellOrigin.y + GetRowHeight(nRow)-1;

	return TRUE;
}

BOOL CTerrainCtrl::IsCellVisible(CELLID cell)
{
	return IsCellVisible(cell.row, cell.col);
}

BOOL CTerrainCtrl::IsCellVisible(int nRow, int nCol)
{
	if (!IsWindow(m_hWnd))
		return FALSE;

	int x,y;

	CELLID TopLeft;
	if (nCol >= GetFixedColumnCount() || nRow >= GetFixedRowCount())
	{
		TopLeft = GetTopleftNonFixedCell();
		if (nCol >= GetFixedColumnCount() && nCol < TopLeft.col) return FALSE;
		if (nRow >= GetFixedRowCount() && nRow < TopLeft.row) return FALSE;
	}

	CRect rect;
	GetClientRect(rect);
	if (nCol < GetFixedColumnCount())
	{
		x = 0;
		for (int i = 0; i <= nCol; i++) 
		{
			if (x >= rect.right) return FALSE;
			x += GetColumnWidth(i);    
		}
	} 
	else 
	{
		x = GetFixedColumnWidth();
		for (int i = TopLeft.col; i <= nCol; i++) 
		{
			if (x >= rect.right) return FALSE;
			x += GetColumnWidth(i);    
		}
	}

	if (nRow < GetFixedRowCount())
	{
		y = 0;
		for (int i = 0; i <= nRow; i++) 
		{
			if (y >= rect.bottom) return FALSE;
			y += GetRowHeight(i);	 
		}
	} 
	else 
	{
		if (nRow < TopLeft.row) return FALSE;
		y = GetFixedRowHeight();
		for (int i = TopLeft.row; i <= nRow; i++) 
		{
			if (y >= rect.bottom) return FALSE;
			y += GetRowHeight(i);	 
		}
	}

	return TRUE;
}

// move about with keyboard
void CTerrainCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	if (!IsValid(m_idCurrentCell)) 
	{
		CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
		return;
	}

	CELLID next = m_idCurrentCell;
	BOOL bChangeLine = FALSE;
/*
	if (IsCTRLpressed())
	{
		switch (nChar)
		{
		   case 'A': OnEditSelectAll();  break;
		}
	}
*/
	switch (nChar)
	{
/*
		case VK_DELETE: 
			if (IsCellEditable(m_idCurrentCell.row, m_idCurrentCell.col))
			{
				SetItemText(m_idCurrentCell.row, m_idCurrentCell.col, _T(""));
				RedrawCell(m_idCurrentCell);
				SetModified(TRUE);
			}
			break;

		case VK_TAB:	
			if (IsSHIFTpressed())
			{
				if (next.col > m_nFixedCols) 
					next.col--;
				else if (next.col == m_nFixedCols && next.row > m_nFixedRows) 
				{
					next.row--; 
					next.col = GetColumnCount() - 1; 
					bChangeLine = TRUE;
				}
				else
					CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
			}
			else
			{
				if (next.col < (GetColumnCount() - 1)) 
					next.col++;
				else if (next.col == (GetColumnCount() - 1) && 
						 next.row < (GetRowCount() - 1) )
				{
					next.row++; 
					next.col = m_nFixedCols; 
					bChangeLine = TRUE;
				}
				else
					CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
			} 
			break;
*/
		case VK_DOWN:
			if (next.row < (GetRowCount() - 1))
				next.row++;
			break;

		case VK_UP:
			if (next.row > 1)
				next.row--; 
			break;

		case VK_RIGHT:
			if (next.col < (GetColumnCount() - 1))
				next.col++;
			break;

		case VK_LEFT:
			if (next.col > 1)
				next.col--;
			break;

		case VK_NEXT:	
		{
			CELLID idOldTopLeft = GetTopleftNonFixedCell();
			SendMessage(WM_VSCROLL, SB_PAGEDOWN, 0);
			CELLID idNewTopLeft = GetTopleftNonFixedCell();

			int increment = idNewTopLeft.row - idOldTopLeft.row;
			if (increment)
			{
				next.row += increment;
				if (next.row > (GetRowCount() - 1)) 
					next.row = GetRowCount() - 1;
			}
			else
				next.row = GetRowCount() - 1;
			break;
		}

		case VK_PRIOR:	
		{
			CELLID idOldTopLeft = GetTopleftNonFixedCell();
			SendMessage(WM_VSCROLL, SB_PAGEUP, 0);
			CELLID idNewTopLeft = GetTopleftNonFixedCell();

			int increment = idNewTopLeft.row - idOldTopLeft.row;
			if (increment) 
			{
				next.row += increment;
				if (next.row < m_nFixedRows) 
					next.row = m_nFixedRows;
			}
			else
				next.row = m_nFixedRows;
			break;
		}

		case VK_HOME:	
			SendMessage(WM_VSCROLL, SB_TOP, 0);
			next.row = m_nFixedRows;
			break;

		case VK_END:	
			SendMessage(WM_VSCROLL, SB_BOTTOM, 0);
			next.row = GetRowCount() - 1;
			break;

		default:
			CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
	}
  
	if (next != m_idCurrentCell) 
	{
		// While moving with the Cursorkeys the current ROW/CELL will get selected
		// OR Selection will get expanded when SHIFT is pressed
		// Cut n paste from OnLButtonDown - Franco Bez 
/*		// Added check for NULL mouse mode - Chris Maunder.
		if (m_MouseMode == MOUSE_NOTHING)
		{
			m_PrevSelectedCellMap.RemoveAll();
			m_MouseMode = m_bListMode? MOUSE_SELECT_ROW : MOUSE_SELECT_CELLS;
			if (!IsSHIFTpressed() || nChar == VK_TAB)
				m_SelectionStartCell = next;
			OnSelecting(next);
			m_MouseMode = MOUSE_NOTHING;
		}
*/
		SetFocusCell(next);

		if (!IsCellVisible(next))
		{	
			EnsureVisible(next); // Make sure cell is visible

			switch (nChar)
			{
				case VK_RIGHT:	
					SendMessage(WM_HSCROLL, SB_LINERIGHT, 0); 
					break;

				case VK_LEFT:	
					SendMessage(WM_HSCROLL, SB_LINELEFT, 0);  
					break;

				case VK_DOWN:	
					SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);  
					break;
				
				case VK_UP: 	
					SendMessage(WM_VSCROLL, SB_LINEUP, 0);	  
					break;				  
/*				
				case VK_TAB:	
					if (IsSHIFTpressed())
					{
						if (bChangeLine) 
						{
							SendMessage(WM_VSCROLL, SB_LINEUP, 0);
							SetScrollPos32(SB_HORZ, m_nHScrollMax);
							break;
						}
						else 
							SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
					}
					else
					{
						if (bChangeLine) 
						{
							SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
							SetScrollPos32(SB_HORZ, 0);
							break;
						}
						else 
							SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
					}
					break;
*/
			}
			Invalidate();
		}
	}
}

void CTerrainCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	if (
	/*	!IsCTRLpressed() && 
	*/
	m_MouseMode == MOUSE_NOTHING)
	{
/*		if (!m_bHandleTabKey || (m_bHandleTabKey && nChar != VK_TAB))
*/			EditCell(m_idCurrentCell.row, m_idCurrentCell.col, nChar);
	}

	CWnd::OnChar(nChar, nRepCnt, nFlags);
}

// Instant editing of cells when keys are pressed
void CTerrainCtrl::EditCell(int nRow, int nCol, UINT nChar)
{
	EnsureVisible(nRow, nCol);

	if (!IsValid(nRow, nCol) || !IsCellVisible(nRow, nCol)) 
		return;

	CRect rect;
	if (!GetCellRect(nRow, nCol, rect))
		return;
/*
	SendMessageToParent(nRow, nCol, GVN_BEGINLABELEDIT);
*/
	char szText[20];
	sprintf(szText, "%.2f", ControlPoint(nRow, nCol));
	new CInPlaceEdit(this, rect, ES_LEFT, IDC_INPLACE_EDIT, &ControlPoint(nRow, nCol), szText, nChar);
}

void CTerrainCtrl::EnsureVisible(CELLID cell)
{
	EnsureVisible(cell.row, cell.col);
}

void CTerrainCtrl::EnsureVisible(int nRow, int nCol)
{
//	CCellRange VisibleCells = GetVisibleNonFixedCellRange();
	CRect rect;
	GetClientRect(rect);
	CELLID idTopLeft = GetTopleftNonFixedCell();

	// calc bottom
	int bottom = GetFixedRowHeight();
	for (int i = idTopLeft.row; i < m_nRows; i++)
	{
		bottom += GetRowHeight(i);
		if (bottom >= rect.bottom)
		{
			bottom = rect.bottom;
			break;
		}
	}								 
	int maxVisibleRow = min(i, m_nRows - 1);
	
	// calc right
	int right = GetFixedColumnWidth();
	for (i = idTopLeft.col; i < m_nCols; i++)
	{
		right += GetColumnWidth(i);
		if (right >= rect.right)
		{
			right = rect.right;
			break;
		}
	}
	int maxVisibleCol = min(i, m_nCols - 1);

	right = nCol - maxVisibleCol;
	int left  = idTopLeft.col - nCol;
	int down  = nRow - maxVisibleRow;
	int up	  = idTopLeft.row - nRow;
	
	while (right > 0)
	{
		SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
		right--;
	}
	while (left > 0)
	{
		SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
		left--;
	}
	while (down > 0)
	{
		SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
		down--;
	}
	while (up > 0)
	{
		SendMessage(WM_VSCROLL, SB_LINEUP, 0);
		up--;
	}
	
	// Move one more if we only see a small bit of the cell
	CRect rectCell, rectWindow;
	GetCellRect(nRow, nCol, rectCell);
	GetClientRect(rectWindow);
	if (rectCell.right > rectWindow.right)
		SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
	if (rectCell.bottom > rectWindow.bottom)
		SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
}
