www.pudn.com > OSDemo.rar > CheckerCtrl.cpp


//Author:			Mehdi Mousavi 
//Data of release:	8th of September, 2000 
//Email:			Webmaster@modemmania.com 
 
// CheckerCtrl.cpp : implementation file 
// 
 
#include "stdafx.h" 
#include "CheckerCtrl.h" 
#include "Resource.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
///////////////////////////////////////////////////////////////////////////// 
// CCheckerCtrl 
 
#define BLANKED_BLOCKS_COLOR	RGB(252, 252, 252) 
 
CCheckerCtrl::CCheckerCtrl() 
{ 
	//Sets the background brush of the client area 
	m_backgroundBrush.CreateSolidBrush(GetSysColor(COLOR_WINDOW)); 
	 
	//Resets m_nyPos for scrolling purposes 
	m_nyPos = 0; 
 
	//Resets m_nBlockStartPos so that when the WM_PAINT message 
	//is triggered, the control starts to show blocks 
	//from m_nBlockStartPos position 
	m_nBlockStartPos = 0; 
 
	//Sets the starting index of blocks 
	m_nStartIndex = 0; 
 
	//Total number of blocks 
	m_nNumberofBlocks = 0; 
 
	//Offset for scrolling purposes 
	m_nOffset = 0; 
 
	m_nTotalVisibleBlocks = 0; 
 
	m_nBlocksPerRow = 0; 
} 
 
CCheckerCtrl::~CCheckerCtrl() 
{ 
} 
 
BOOL CCheckerCtrl::Create(DWORD dwStyle, const RECT &rect, CWnd *pParentWnd, UINT nID) 
{ 
	//Postcondition: 
	//	Creates a window after being registered, as well as 
	//	setting all the required variables used hereinafter. 
	static CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW); 
	BOOL bRet = CWnd::CreateEx(WS_EX_CLIENTEDGE,  
								className, 
								NULL, 
								dwStyle, 
								rect.left, 
								rect.top, 
								rect.right - rect.left, 
								rect.bottom - rect.top, 
								pParentWnd->GetSafeHwnd(), 
								(HMENU) nID); 
 
	m_nID = nID; 
	m_pParentWnd = pParentWnd; 
	 
	SetCursor(LoadCursor(NULL, IDC_ARROW)); 
	 
	return bRet; 
} 
 
 
BEGIN_MESSAGE_MAP(CCheckerCtrl, CWnd) 
	//{{AFX_MSG_MAP(CCheckerCtrl) 
	ON_WM_PAINT() 
	ON_WM_VSCROLL() 
	ON_WM_LBUTTONDOWN() 
	ON_WM_LBUTTONUP() 
	ON_WM_MOUSEMOVE() 
	ON_WM_RBUTTONDOWN() 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
 
///////////////////////////////////////////////////////////////////////////// 
// CCheckerCtrl message handlers 
 
void CCheckerCtrl::OnPaint()  
{ 
	CPaintDC dc(this); // device context for painting 
	 
	// TODO: Add your message handler code here 
 
	//Fill the background color of the client area 
	dc.FillRect(m_rcClient, &m_backgroundBrush); 
 
	UINT nColumn = 0, nRow = 0; 
	 
	//Calculate the index of the last visible block 
	//within the client area 
	UINT nBlockEndPos = m_nBlockStartPos + m_nTotalVisibleBlocks + m_nBlocksPerRow; 
	if(nBlockEndPos > m_nNumberofBlocks) 
		nBlockEndPos = m_nNumberofBlocks; 
 
	for(UINT i = m_nBlockStartPos; i < nBlockEndPos; i++) 
	{ 
		CBrush brush(m_crColor.GetAt(i)); 
		SetBlock(nRow, nColumn, brush, dc); 
		if((i + 1 - m_nBlockStartPos) % m_nBlocksPerRow == 0) 
		{ 
			nRow++; 
			nColumn = 0; 
		} 
		else 
			nColumn++; 
	} 
} 
 
void CCheckerCtrl::SetTotalBlocks(const UINT nNumberofBlocks, const UINT nStartIndex) 
{ 
	//Postcondition: 
	//	Sets the member variable m_nNumberofBlocks to the specified 
	//	number of blocks. Then creates an array of COLORREF, sized 
	//	nNumberofBlocks, and initialize it with white color. 
	//	Thereafter, it computes m_nOffset for scrolling purposes. 
	m_nNumberofBlocks = nNumberofBlocks; 
 
	m_crColor.SetSize(m_nNumberofBlocks); 
	for(UINT i = 0; i < m_nNumberofBlocks; i++) 
		m_crColor.SetAt(i, BLANKED_BLOCKS_COLOR); 
 
	GetClientRect(m_rcClient); 
	m_nBlocksPerRow = m_rcClient.Width() / 9; 
	m_nBlocksPerColumn = m_rcClient.Height() / 11; 
	 
	m_nTotalVisibleBlocks = m_nBlocksPerRow * m_nBlocksPerColumn; 
	 
	//Calculate the vertical scroll bar's range 
	int nOffset = (m_nNumberofBlocks / m_nBlocksPerRow); 
	if(m_nNumberofBlocks % m_nBlocksPerRow != 0) 
		nOffset++; 
	 
	m_nOffset = nOffset - m_nBlocksPerColumn; 
	if(m_nOffset > 0) 
		SetScrollRange(SB_VERT, 0, m_nOffset); 
 
	//	Sets the starting index of blocks 
	m_nStartIndex = nStartIndex; 
} 
 
void CCheckerCtrl::SetBlock(int nRow, int nColumn, CBrush &brush, CDC &dc) 
{ 
	//Postcondition: 
	//	Places a block on nRow, nColumn ordered pair,  
	//	and paints it with brush color 
	CRect rect = GetRect(nRow, nColumn); 
 
	dc.Rectangle(&rect); 
	 
	rect.left++; 
	rect.top++; 
	rect.bottom--; 
	rect.right--; 
	 
	dc.FillRect(rect, &brush); 
} 
 
void CCheckerCtrl::SetBlock(const UINT nBlockNumber, const COLORREF crColor) 
{ 
	//Postcondition: 
	//	Sets the color of a specified block number to crColor 
	ASSERT(nBlockNumber - m_nStartIndex < m_nNumberofBlocks); 
	m_crColor.SetAt(nBlockNumber - m_nStartIndex, crColor); 
} 
 
void CCheckerCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)  
{ 
	// TODO: Add your message handler code here and/or call default 
 
	int nyOffset; 
	switch(nSBCode) 
	{ 
	case SB_PAGEDOWN: 
		if(m_nyPos < m_nOffset) 
		{ 
			if(m_nyPos + 2 < m_nOffset) 
				nyOffset = 2; 
			else 
				nyOffset = m_nOffset - m_nyPos; 
 
			m_nyPos += nyOffset; 
			m_nBlockStartPos += m_nBlocksPerRow * nyOffset; 
 
			SetScrollPos(SB_VERT, m_nyPos); 
			ScrollWindow(0, -nyOffset * 11); 
		} 
		break; 
 
	case SB_PAGEUP: 
		if(m_nyPos > 0) 
		{ 
			if(m_nyPos - 2 > 0) 
				nyOffset = 2; 
			else 
				nyOffset = m_nyPos; 
 
			m_nBlockStartPos -= m_nBlocksPerRow * nyOffset; 
 
			m_nyPos -= nyOffset; 
			SetScrollPos(SB_VERT, m_nyPos); 
			ScrollWindow(0, nyOffset * 11); 
		} 
		break; 
 
	case SB_LINEUP: 
		if(m_nyPos > 0) 
		{ 
			m_nBlockStartPos -= m_nBlocksPerRow; 
 
			m_nyPos--; 
			SetScrollPos(SB_VERT, m_nyPos); 
			ScrollWindow(0, 11); 
		} 
		break; 
 
	case SB_LINEDOWN: 
		if(m_nyPos < m_nOffset) 
		{ 
			m_nBlockStartPos += m_nBlocksPerRow; 
 
			m_nyPos++; 
			SetScrollPos(SB_VERT, m_nyPos); 
			ScrollWindow(0, -11); 
		} 
		break; 
	} 
 
	CWnd::OnVScroll(nSBCode, nPos, pScrollBar); 
} 
 
void CCheckerCtrl::Refresh() 
{ 
	//Postcondition: 
	//	Refreshes the client area of the control 
	Invalidate(); 
} 
 
COLORREF CCheckerCtrl::GetBlock(const UINT nBlockNumber) const 
{ 
	//Precondition: 
	//	nBlockNumber must be in the range of the defined blocks 
	//Postcondition: 
	//	Takes the color of the specified index 
	ASSERT(nBlockNumber >= m_nStartIndex && nBlockNumber - m_nStartIndex <= m_nNumberofBlocks); 
	return m_crColor.GetAt(nBlockNumber - m_nStartIndex); 
} 
 
void CCheckerCtrl::OnLButtonDown(UINT nFlags, CPoint point)  
{ 
	// TODO: Add your message handler code here and/or call default 
	SetFocus(); 
 
	DWORD dwPos = GetMessagePos(); 
	CPoint clickedPoint((int)(short)LOWORD(dwPos), (int)(short)HIWORD(dwPos)); 
	ScreenToClient(&clickedPoint); 
	 
	if(clickedPoint.x % 9 == 0 || clickedPoint.y % 11 == 0) 
		m_bShouldUpdated = FALSE; 
	else 
	{ 
		UINT nY = clickedPoint.y / 11; 
		UINT nX = clickedPoint.x / 9; 
 
		UINT nIndex = nY * m_nBlocksPerRow + nX + m_nyPos * m_nBlocksPerRow; 
		 
		if(nIndex < m_nNumberofBlocks && nX < m_nBlocksPerRow) 
		{ 
			SetCapture(); 
 
			CString strNumber; 
			strNumber.Format("%d", nIndex + m_nStartIndex); 
			 
			CreateSafeTooltipRect(clickedPoint.x, clickedPoint.y, strNumber); 
			 
			CClientDC dc(this); 
			CBrush brush; 
			brush.CreateSolidBrush(GetSysColor(COLOR_INFOBK)); 
			dc.Rectangle(tooltipRect); 
			 
			tooltipRect.left++; 
			tooltipRect.top++; 
			tooltipRect.bottom--; 
			tooltipRect.right--; 
			 
			dc.FillRect(tooltipRect, &brush); 
			 
			CFont font; 
			LOGFONT logFont; 
			strcpy(logFont.lfFaceName, "Verdana"); 
			logFont.lfHeight = -MulDiv(12, GetDeviceCaps(dc.m_hDC, LOGPIXELSY), 72); 
			logFont.lfWidth = 0; 
			logFont.lfEscapement = 0; 
			logFont.lfItalic = FALSE; 
			logFont.lfStrikeOut = FALSE; 
			logFont.lfUnderline = FALSE; 
			logFont.lfWeight = FW_BOLD; 
			 
			font.CreateFontIndirect(&logFont); 
			 
			dc.SelectObject(font); 
			 
			dc.SetTextColor(GetSysColor(COLOR_INFOTEXT)); 
			dc.SetBkColor(GetSysColor(COLOR_INFOBK)); 
			dc.DrawText(strNumber, tooltipRect, DT_VCENTER | DT_CENTER | DT_SINGLELINE); 
			 
			tooltipRect.left--; 
			tooltipRect.top--; 
			tooltipRect.bottom++; 
			tooltipRect.right++; 
 
			m_bShouldUpdated = TRUE; 
		} 
	} 
 
	CWnd::OnLButtonDown(nFlags, point); 
} 
 
void CCheckerCtrl::OnLButtonUp(UINT nFlags, CPoint point)  
{ 
	// TODO: Add your message handler code here and/or call default 
	if(m_bShouldUpdated) 
	{ 
		CClientDC dc(this); 
		InvalidateRect(&tooltipRect, FALSE); 
		ReleaseCapture(); 
		 
		m_bShouldUpdated = FALSE; 
	} 
 
	CWnd::OnLButtonUp(nFlags, point); 
} 
 
void CCheckerCtrl::CreateSafeTooltipRect(int x, int y, LPCTSTR lpszText) 
{ 
	int nTextLength = strlen(lpszText); 
	int nTextWidth = nTextLength; 
	if(nTextWidth < 5) 
		nTextWidth = 5; 
	 
	if(x + 12 * nTextWidth <= m_rcClient.right) 
		tooltipRect.right = x + 12 * nTextWidth; 
	else 
		tooltipRect.right = x - 12 * nTextWidth; 
 
	if(y + 25 <= m_rcClient.bottom) 
		tooltipRect.bottom = y + 25; 
	else 
		tooltipRect.bottom = y - 25; 
 
	tooltipRect.left = x; 
	tooltipRect.top = y; 
 
	if(tooltipRect.left > tooltipRect.right) 
	{ 
		int nTemp = tooltipRect.left; 
		tooltipRect.left = tooltipRect.right; 
		tooltipRect.right = nTemp; 
	} 
 
	if(tooltipRect.top > tooltipRect.bottom) 
	{ 
		int nTemp = tooltipRect.bottom; 
		tooltipRect.bottom = tooltipRect.top; 
		tooltipRect.top = nTemp; 
	} 
} 
 
void CCheckerCtrl::OnMouseMove(UINT nFlags, CPoint point)  
{ 
	// TODO: Add your message handler code here and/or call default 
	SetCursor(LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_CURSOR_HAND))); 
	CWnd::OnMouseMove(nFlags, point); 
} 
 
void CCheckerCtrl::Reset() 
{ 
	for(UINT i = 0; i < m_nNumberofBlocks; i++) 
		m_crColor.SetAt(i, BLANKED_BLOCKS_COLOR); 
} 
 
void CCheckerCtrl::Update(const UINT nBlockNumber) 
{ 
	//Precondition: 
	//	nBlockNumber must be in the range of the defined blocks 
	//Postcondition: 
	//	Updates the color of a specified index on CRT, if and only 
	//	if it's already visible 
	ASSERT(nBlockNumber >= m_nStartIndex && nBlockNumber - m_nStartIndex <= m_nNumberofBlocks); 
 
	if(IsVisible(nBlockNumber)) 
	{ 
		UINT nRow = (nBlockNumber - m_nStartIndex) / m_nBlocksPerRow - m_nyPos; 
		UINT nColumn = (nBlockNumber - m_nStartIndex) % m_nBlocksPerRow; 
		 
		CClientDC dc(this); 
		CRect rect = GetRect(nRow, nColumn); 
		 
		rect.left++; 
		rect.top++; 
		rect.bottom--; 
		rect.right--; 
		 
		CBrush brush; 
		brush.CreateSolidBrush(m_crColor.GetAt(nBlockNumber - m_nStartIndex)); 
		dc.FillRect(rect, &brush); 
	} 
} 
 
CRect CCheckerCtrl::GetRect(const UINT nRow, const UINT nColumn) 
{ 
	//Postcondition: 
	//	Returns the rectangular area of a specified block 
	//	placed in nRow(th) row and nColumn(th) column. 
	CRect rect; 
	rect.left = nColumn * 9 + 1; 
	rect.top = nRow * 11 + 1; 
	rect.right = rect.left + 7; 
	rect.bottom = rect.top + 9; 
	 
	return rect; 
} 
 
BOOL CCheckerCtrl::IsVisible(const UINT nBlockNumber) 
{ 
	//Calculate the index of the last visible block 
	//within the client area 
	UINT nBlockEndPos = m_nBlockStartPos + m_nTotalVisibleBlocks + m_nBlocksPerRow; 
	if(nBlockEndPos > m_nNumberofBlocks) 
		nBlockEndPos = m_nNumberofBlocks; 
 
	if(nBlockNumber >= m_nBlockStartPos && nBlockNumber <= nBlockEndPos) 
		return TRUE; 
	else 
		return FALSE; 
} 
 
void CCheckerCtrl::OnRButtonDown(UINT nFlags, CPoint point)  
{ 
	// TODO: Add your message handler code here and/or call default 
	SetFocus(); 
	if(!(point.x % 9 == 0 || point.y % 11 == 0)) 
	{ 
		UINT nY = point.y / 11; 
		UINT nX = point.x / 9; 
 
		UINT nIndex = nY * m_nBlocksPerRow + nX + m_nyPos * m_nBlocksPerRow; 
		if(nIndex < m_nNumberofBlocks && nX < m_nBlocksPerRow) 
			m_pParentWnd->PostMessage(WM_CHECKERCTRL_RBUTTONDOWN, m_nID, nIndex + m_nStartIndex); 
	} 
 
	CWnd::OnRButtonDown(nFlags, point); 
}