CheckerCtrl.cpp
上传用户:tianjwyx
上传日期:2007-01-13
资源大小:813k
文件大小:11k
源码类别:

操作系统开发

开发平台:

Visual C++

  1. //Author: Mehdi Mousavi
  2. //Data of release: 8th of September, 2000
  3. //Email: Webmaster@modemmania.com
  4. // CheckerCtrl.cpp : implementation file
  5. //
  6. #include "stdafx.h"
  7. #include "CheckerCtrl.h"
  8. #include "Resource.h"
  9. #ifdef _DEBUG
  10. #define new DEBUG_NEW
  11. #undef THIS_FILE
  12. static char THIS_FILE[] = __FILE__;
  13. #endif
  14. /////////////////////////////////////////////////////////////////////////////
  15. // CCheckerCtrl
  16. #define BLANKED_BLOCKS_COLOR RGB(252, 252, 252)
  17. CCheckerCtrl::CCheckerCtrl()
  18. {
  19. //Sets the background brush of the client area
  20. m_backgroundBrush.CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  21. //Resets m_nyPos for scrolling purposes
  22. m_nyPos = 0;
  23. //Resets m_nBlockStartPos so that when the WM_PAINT message
  24. //is triggered, the control starts to show blocks
  25. //from m_nBlockStartPos position
  26. m_nBlockStartPos = 0;
  27. //Sets the starting index of blocks
  28. m_nStartIndex = 0;
  29. //Total number of blocks
  30. m_nNumberofBlocks = 0;
  31. //Offset for scrolling purposes
  32. m_nOffset = 0;
  33. m_nTotalVisibleBlocks = 0;
  34. m_nBlocksPerRow = 0;
  35. }
  36. CCheckerCtrl::~CCheckerCtrl()
  37. {
  38. }
  39. BOOL CCheckerCtrl::Create(DWORD dwStyle, const RECT &rect, CWnd *pParentWnd, UINT nID)
  40. {
  41. //Postcondition:
  42. // Creates a window after being registered, as well as
  43. // setting all the required variables used hereinafter.
  44. static CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);
  45. BOOL bRet = CWnd::CreateEx(WS_EX_CLIENTEDGE, 
  46. className,
  47. NULL,
  48. dwStyle,
  49. rect.left,
  50. rect.top,
  51. rect.right - rect.left,
  52. rect.bottom - rect.top,
  53. pParentWnd->GetSafeHwnd(),
  54. (HMENU) nID);
  55. m_nID = nID;
  56. m_pParentWnd = pParentWnd;
  57. SetCursor(LoadCursor(NULL, IDC_ARROW));
  58. return bRet;
  59. }
  60. BEGIN_MESSAGE_MAP(CCheckerCtrl, CWnd)
  61. //{{AFX_MSG_MAP(CCheckerCtrl)
  62. ON_WM_PAINT()
  63. ON_WM_VSCROLL()
  64. ON_WM_LBUTTONDOWN()
  65. ON_WM_LBUTTONUP()
  66. ON_WM_MOUSEMOVE()
  67. ON_WM_RBUTTONDOWN()
  68. //}}AFX_MSG_MAP
  69. END_MESSAGE_MAP()
  70. /////////////////////////////////////////////////////////////////////////////
  71. // CCheckerCtrl message handlers
  72. void CCheckerCtrl::OnPaint() 
  73. {
  74. CPaintDC dc(this); // device context for painting
  75. // TODO: Add your message handler code here
  76. //Fill the background color of the client area
  77. dc.FillRect(m_rcClient, &m_backgroundBrush);
  78. UINT nColumn = 0, nRow = 0;
  79. //Calculate the index of the last visible block
  80. //within the client area
  81. UINT nBlockEndPos = m_nBlockStartPos + m_nTotalVisibleBlocks + m_nBlocksPerRow;
  82. if(nBlockEndPos > m_nNumberofBlocks)
  83. nBlockEndPos = m_nNumberofBlocks;
  84. for(UINT i = m_nBlockStartPos; i < nBlockEndPos; i++)
  85. {
  86. CBrush brush(m_crColor.GetAt(i));
  87. SetBlock(nRow, nColumn, brush, dc);
  88. if((i + 1 - m_nBlockStartPos) % m_nBlocksPerRow == 0)
  89. {
  90. nRow++;
  91. nColumn = 0;
  92. }
  93. else
  94. nColumn++;
  95. }
  96. }
  97. void CCheckerCtrl::SetTotalBlocks(const UINT nNumberofBlocks, const UINT nStartIndex)
  98. {
  99. //Postcondition:
  100. // Sets the member variable m_nNumberofBlocks to the specified
  101. // number of blocks. Then creates an array of COLORREF, sized
  102. // nNumberofBlocks, and initialize it with white color.
  103. // Thereafter, it computes m_nOffset for scrolling purposes.
  104. m_nNumberofBlocks = nNumberofBlocks;
  105. m_crColor.SetSize(m_nNumberofBlocks);
  106. for(UINT i = 0; i < m_nNumberofBlocks; i++)
  107. m_crColor.SetAt(i, BLANKED_BLOCKS_COLOR);
  108. GetClientRect(m_rcClient);
  109. m_nBlocksPerRow = m_rcClient.Width() / 9;
  110. m_nBlocksPerColumn = m_rcClient.Height() / 11;
  111. m_nTotalVisibleBlocks = m_nBlocksPerRow * m_nBlocksPerColumn;
  112. //Calculate the vertical scroll bar's range
  113. int nOffset = (m_nNumberofBlocks / m_nBlocksPerRow);
  114. if(m_nNumberofBlocks % m_nBlocksPerRow != 0)
  115. nOffset++;
  116. m_nOffset = nOffset - m_nBlocksPerColumn;
  117. if(m_nOffset > 0)
  118. SetScrollRange(SB_VERT, 0, m_nOffset);
  119. // Sets the starting index of blocks
  120. m_nStartIndex = nStartIndex;
  121. }
  122. void CCheckerCtrl::SetBlock(int nRow, int nColumn, CBrush &brush, CDC &dc)
  123. {
  124. //Postcondition:
  125. // Places a block on nRow, nColumn ordered pair, 
  126. // and paints it with brush color
  127. CRect rect = GetRect(nRow, nColumn);
  128. dc.Rectangle(&rect);
  129. rect.left++;
  130. rect.top++;
  131. rect.bottom--;
  132. rect.right--;
  133. dc.FillRect(rect, &brush);
  134. }
  135. void CCheckerCtrl::SetBlock(const UINT nBlockNumber, const COLORREF crColor)
  136. {
  137. //Postcondition:
  138. // Sets the color of a specified block number to crColor
  139. ASSERT(nBlockNumber - m_nStartIndex < m_nNumberofBlocks);
  140. m_crColor.SetAt(nBlockNumber - m_nStartIndex, crColor);
  141. }
  142. void CCheckerCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
  143. {
  144. // TODO: Add your message handler code here and/or call default
  145. int nyOffset;
  146. switch(nSBCode)
  147. {
  148. case SB_PAGEDOWN:
  149. if(m_nyPos < m_nOffset)
  150. {
  151. if(m_nyPos + 2 < m_nOffset)
  152. nyOffset = 2;
  153. else
  154. nyOffset = m_nOffset - m_nyPos;
  155. m_nyPos += nyOffset;
  156. m_nBlockStartPos += m_nBlocksPerRow * nyOffset;
  157. SetScrollPos(SB_VERT, m_nyPos);
  158. ScrollWindow(0, -nyOffset * 11);
  159. }
  160. break;
  161. case SB_PAGEUP:
  162. if(m_nyPos > 0)
  163. {
  164. if(m_nyPos - 2 > 0)
  165. nyOffset = 2;
  166. else
  167. nyOffset = m_nyPos;
  168. m_nBlockStartPos -= m_nBlocksPerRow * nyOffset;
  169. m_nyPos -= nyOffset;
  170. SetScrollPos(SB_VERT, m_nyPos);
  171. ScrollWindow(0, nyOffset * 11);
  172. }
  173. break;
  174. case SB_LINEUP:
  175. if(m_nyPos > 0)
  176. {
  177. m_nBlockStartPos -= m_nBlocksPerRow;
  178. m_nyPos--;
  179. SetScrollPos(SB_VERT, m_nyPos);
  180. ScrollWindow(0, 11);
  181. }
  182. break;
  183. case SB_LINEDOWN:
  184. if(m_nyPos < m_nOffset)
  185. {
  186. m_nBlockStartPos += m_nBlocksPerRow;
  187. m_nyPos++;
  188. SetScrollPos(SB_VERT, m_nyPos);
  189. ScrollWindow(0, -11);
  190. }
  191. break;
  192. }
  193. CWnd::OnVScroll(nSBCode, nPos, pScrollBar);
  194. }
  195. void CCheckerCtrl::Refresh()
  196. {
  197. //Postcondition:
  198. // Refreshes the client area of the control
  199. Invalidate();
  200. }
  201. COLORREF CCheckerCtrl::GetBlock(const UINT nBlockNumber) const
  202. {
  203. //Precondition:
  204. // nBlockNumber must be in the range of the defined blocks
  205. //Postcondition:
  206. // Takes the color of the specified index
  207. ASSERT(nBlockNumber >= m_nStartIndex && nBlockNumber - m_nStartIndex <= m_nNumberofBlocks);
  208. return m_crColor.GetAt(nBlockNumber - m_nStartIndex);
  209. }
  210. void CCheckerCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
  211. {
  212. // TODO: Add your message handler code here and/or call default
  213. SetFocus();
  214. DWORD dwPos = GetMessagePos();
  215. CPoint clickedPoint((int)(short)LOWORD(dwPos), (int)(short)HIWORD(dwPos));
  216. ScreenToClient(&clickedPoint);
  217. if(clickedPoint.x % 9 == 0 || clickedPoint.y % 11 == 0)
  218. m_bShouldUpdated = FALSE;
  219. else
  220. {
  221. UINT nY = clickedPoint.y / 11;
  222. UINT nX = clickedPoint.x / 9;
  223. UINT nIndex = nY * m_nBlocksPerRow + nX + m_nyPos * m_nBlocksPerRow;
  224. if(nIndex < m_nNumberofBlocks && nX < m_nBlocksPerRow)
  225. {
  226. SetCapture();
  227. CString strNumber;
  228. strNumber.Format("%d", nIndex + m_nStartIndex);
  229. CreateSafeTooltipRect(clickedPoint.x, clickedPoint.y, strNumber);
  230. CClientDC dc(this);
  231. CBrush brush;
  232. brush.CreateSolidBrush(GetSysColor(COLOR_INFOBK));
  233. dc.Rectangle(tooltipRect);
  234. tooltipRect.left++;
  235. tooltipRect.top++;
  236. tooltipRect.bottom--;
  237. tooltipRect.right--;
  238. dc.FillRect(tooltipRect, &brush);
  239. CFont font;
  240. LOGFONT logFont;
  241. strcpy(logFont.lfFaceName, "Verdana");
  242. logFont.lfHeight = -MulDiv(12, GetDeviceCaps(dc.m_hDC, LOGPIXELSY), 72);
  243. logFont.lfWidth = 0;
  244. logFont.lfEscapement = 0;
  245. logFont.lfItalic = FALSE;
  246. logFont.lfStrikeOut = FALSE;
  247. logFont.lfUnderline = FALSE;
  248. logFont.lfWeight = FW_BOLD;
  249. font.CreateFontIndirect(&logFont);
  250. dc.SelectObject(font);
  251. dc.SetTextColor(GetSysColor(COLOR_INFOTEXT));
  252. dc.SetBkColor(GetSysColor(COLOR_INFOBK));
  253. dc.DrawText(strNumber, tooltipRect, DT_VCENTER | DT_CENTER | DT_SINGLELINE);
  254. tooltipRect.left--;
  255. tooltipRect.top--;
  256. tooltipRect.bottom++;
  257. tooltipRect.right++;
  258. m_bShouldUpdated = TRUE;
  259. }
  260. }
  261. CWnd::OnLButtonDown(nFlags, point);
  262. }
  263. void CCheckerCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
  264. {
  265. // TODO: Add your message handler code here and/or call default
  266. if(m_bShouldUpdated)
  267. {
  268. CClientDC dc(this);
  269. InvalidateRect(&tooltipRect, FALSE);
  270. ReleaseCapture();
  271. m_bShouldUpdated = FALSE;
  272. }
  273. CWnd::OnLButtonUp(nFlags, point);
  274. }
  275. void CCheckerCtrl::CreateSafeTooltipRect(int x, int y, LPCTSTR lpszText)
  276. {
  277. int nTextLength = strlen(lpszText);
  278. int nTextWidth = nTextLength;
  279. if(nTextWidth < 5)
  280. nTextWidth = 5;
  281. if(x + 12 * nTextWidth <= m_rcClient.right)
  282. tooltipRect.right = x + 12 * nTextWidth;
  283. else
  284. tooltipRect.right = x - 12 * nTextWidth;
  285. if(y + 25 <= m_rcClient.bottom)
  286. tooltipRect.bottom = y + 25;
  287. else
  288. tooltipRect.bottom = y - 25;
  289. tooltipRect.left = x;
  290. tooltipRect.top = y;
  291. if(tooltipRect.left > tooltipRect.right)
  292. {
  293. int nTemp = tooltipRect.left;
  294. tooltipRect.left = tooltipRect.right;
  295. tooltipRect.right = nTemp;
  296. }
  297. if(tooltipRect.top > tooltipRect.bottom)
  298. {
  299. int nTemp = tooltipRect.bottom;
  300. tooltipRect.bottom = tooltipRect.top;
  301. tooltipRect.top = nTemp;
  302. }
  303. }
  304. void CCheckerCtrl::OnMouseMove(UINT nFlags, CPoint point) 
  305. {
  306. // TODO: Add your message handler code here and/or call default
  307. SetCursor(LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_CURSOR_HAND)));
  308. CWnd::OnMouseMove(nFlags, point);
  309. }
  310. void CCheckerCtrl::Reset()
  311. {
  312. for(UINT i = 0; i < m_nNumberofBlocks; i++)
  313. m_crColor.SetAt(i, BLANKED_BLOCKS_COLOR);
  314. }
  315. void CCheckerCtrl::Update(const UINT nBlockNumber)
  316. {
  317. //Precondition:
  318. // nBlockNumber must be in the range of the defined blocks
  319. //Postcondition:
  320. // Updates the color of a specified index on CRT, if and only
  321. // if it's already visible
  322. ASSERT(nBlockNumber >= m_nStartIndex && nBlockNumber - m_nStartIndex <= m_nNumberofBlocks);
  323. if(IsVisible(nBlockNumber))
  324. {
  325. UINT nRow = (nBlockNumber - m_nStartIndex) / m_nBlocksPerRow - m_nyPos;
  326. UINT nColumn = (nBlockNumber - m_nStartIndex) % m_nBlocksPerRow;
  327. CClientDC dc(this);
  328. CRect rect = GetRect(nRow, nColumn);
  329. rect.left++;
  330. rect.top++;
  331. rect.bottom--;
  332. rect.right--;
  333. CBrush brush;
  334. brush.CreateSolidBrush(m_crColor.GetAt(nBlockNumber - m_nStartIndex));
  335. dc.FillRect(rect, &brush);
  336. }
  337. }
  338. CRect CCheckerCtrl::GetRect(const UINT nRow, const UINT nColumn)
  339. {
  340. //Postcondition:
  341. // Returns the rectangular area of a specified block
  342. // placed in nRow(th) row and nColumn(th) column.
  343. CRect rect;
  344. rect.left = nColumn * 9 + 1;
  345. rect.top = nRow * 11 + 1;
  346. rect.right = rect.left + 7;
  347. rect.bottom = rect.top + 9;
  348. return rect;
  349. }
  350. BOOL CCheckerCtrl::IsVisible(const UINT nBlockNumber)
  351. {
  352. //Calculate the index of the last visible block
  353. //within the client area
  354. UINT nBlockEndPos = m_nBlockStartPos + m_nTotalVisibleBlocks + m_nBlocksPerRow;
  355. if(nBlockEndPos > m_nNumberofBlocks)
  356. nBlockEndPos = m_nNumberofBlocks;
  357. if(nBlockNumber >= m_nBlockStartPos && nBlockNumber <= nBlockEndPos)
  358. return TRUE;
  359. else
  360. return FALSE;
  361. }
  362. void CCheckerCtrl::OnRButtonDown(UINT nFlags, CPoint point) 
  363. {
  364. // TODO: Add your message handler code here and/or call default
  365. SetFocus();
  366. if(!(point.x % 9 == 0 || point.y % 11 == 0))
  367. {
  368. UINT nY = point.y / 11;
  369. UINT nX = point.x / 9;
  370. UINT nIndex = nY * m_nBlocksPerRow + nX + m_nyPos * m_nBlocksPerRow;
  371. if(nIndex < m_nNumberofBlocks && nX < m_nBlocksPerRow)
  372. m_pParentWnd->PostMessage(WM_CHECKERCTRL_RBUTTONDOWN, m_nID, nIndex + m_nStartIndex);
  373. }
  374. CWnd::OnRButtonDown(nFlags, point);
  375. }