UniButton.cpp
上传用户:linpin
上传日期:2007-01-01
资源大小:46k
文件大小:13k
源码类别:

按钮控件

开发平台:

Visual C++

  1. // UniButton.cpp : implementation file
  2. //
  3. #include "stdafx.h"
  4. #include "MySphereButton.h"
  5. #include "UniButton.h"
  6. #ifdef _DEBUG
  7. #define new DEBUG_NEW
  8. #undef THIS_FILE
  9. static char THIS_FILE[] = __FILE__;
  10. #endif
  11. /////////////////////////////////////////////////////////////////////////////
  12. // CUniButton
  13. CUniButton::CUniButton()
  14. {
  15. m_nColor = GetSysColor(COLOR_BTNFACE);
  16. m_sColor = m_nColor;
  17. m_hColor = m_nColor;
  18. m_dColor = m_nColor;
  19. m_nBorder = 1;
  20. m_lfEscapement = 0;
  21. m_pNormal = NULL;
  22. m_pSelected = NULL;
  23. m_pHover = NULL;
  24. m_pDisabled = NULL;
  25. m_hRgn = 0;
  26. m_bHover = false;
  27. m_bCapture = false;
  28. m_bMouseDown = false;
  29. m_bNeedBitmaps = true;
  30. }
  31. CUniButton::~CUniButton()
  32. {
  33. delete m_pNormal;
  34. delete m_pSelected;
  35. delete m_pHover;
  36. delete m_pDisabled;
  37. DeleteObject(m_hRgn);
  38. }
  39. BEGIN_MESSAGE_MAP(CUniButton, CButton)
  40. //{{AFX_MSG_MAP(CUniButton)
  41. ON_WM_ERASEBKGND()
  42. ON_WM_MOUSEMOVE()
  43. ON_WM_CREATE()
  44. ON_WM_LBUTTONDOWN()
  45. ON_WM_LBUTTONUP()
  46. //}}AFX_MSG_MAP
  47. END_MESSAGE_MAP()
  48. /////////////////////////////////////////////////////////////////////////////
  49. // CUniButton message handlers
  50. BOOL CUniButton::Create(LPCTSTR lpszCaption, DWORD dwStyle, const CPoint point, const HRGN hRgn, CWnd* pParentWnd, UINT nID)
  51. {
  52. // store region in member variable
  53. DeleteObject(m_hRgn);
  54. m_hRgn = CreateRectRgn(0, 0, 31, 31);
  55. CRect box(0, 0, 0, 0);
  56. if (m_hRgn != 0) 
  57. CombineRgn(m_hRgn, hRgn, 0, RGN_COPY);
  58. // make sure that region bounding rect is located in (0, 0)
  59. GetRgnBox(m_hRgn, &box);
  60. OffsetRgn(m_hRgn, -box.left, -box.top);
  61. GetRgnBox(m_hRgn, &box);
  62. // update position of region center for caption output
  63. m_CenterPoint = CPoint(box.left + box.Width() /2 , box.top + box.Height() /2);
  64. box.OffsetRect(point);
  65. return CButton::Create(lpszCaption, dwStyle, box, pParentWnd, nID);
  66. }
  67. BOOL CUniButton::Create(LPCTSTR lpszCaption, DWORD dwStyle, const CPoint point, const HRGN hRgn, CWnd* pParentWnd, UINT nID, COLORREF color)
  68. {
  69. m_sColor = color;
  70. m_hColor = color;
  71. // call another constructor
  72. return Create(lpszCaption, dwStyle, point, hRgn, pParentWnd, nID);
  73. }
  74. BOOL CUniButton::Create(LPCTSTR lpszCaption, DWORD dwStyle, const CPoint point, const HRGN hRgn, CWnd* pParentWnd, UINT nID, UINT nBorder, LONG lfEscapement, COLORREF nColor, COLORREF sColor, COLORREF hColor, COLORREF dColor)
  75. {
  76. m_lfEscapement = lfEscapement;
  77. return Create(lpszCaption, dwStyle, point, hRgn, pParentWnd, nID, nBorder, nColor, sColor, hColor, dColor);
  78. }
  79. BOOL CUniButton::Create(LPCTSTR lpszCaption, DWORD dwStyle, const CPoint point, const HRGN hRgn, CWnd* pParentWnd, UINT nID, UINT nBorder, COLORREF nColor, COLORREF sColor, COLORREF hColor, COLORREF dColor)
  80. {
  81. // change default colors
  82. m_nBorder = nBorder;
  83. m_nColor = nColor;
  84. m_sColor = sColor;
  85. m_hColor = hColor;
  86. m_dColor = dColor;
  87. // call another constructor
  88. return Create(lpszCaption, dwStyle, point, hRgn, pParentWnd, nID);
  89. }
  90. void CUniButton::PreSubclassWindow() 
  91. {
  92. // change window style to allow owner draw
  93. ModifyStyle(0, BS_OWNERDRAW | BS_PUSHBUTTON);
  94. CButton::PreSubclassWindow();
  95. }
  96. int CUniButton::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  97. {
  98. if (CButton::OnCreate(lpCreateStruct) == -1)
  99. return -1;
  100. // assign new region to a window
  101. m_bNeedBitmaps = true;
  102. SetWindowRgn(m_hRgn, true);
  103. return 0;
  104. }
  105. void CUniButton::OnLButtonDown(UINT nFlags, CPoint point) 
  106. {
  107. // record that mouse is down
  108. m_bMouseDown = true;
  109. if (!m_bCapture) {
  110. SetCapture();
  111. m_bCapture = true;
  112. }
  113. CButton::OnLButtonDown(nFlags, point);
  114. }
  115. void CUniButton::OnLButtonUp(UINT nFlags, CPoint point) 
  116. {
  117. // record that mouse is released
  118. CButton::OnLButtonUp(nFlags, point);
  119. m_bMouseDown = false;
  120. if (m_bCapture) {
  121. ReleaseCapture();
  122. m_bCapture = false;
  123. }
  124. CheckHover(point);
  125. }
  126. void CUniButton::OnMouseMove(UINT nFlags, CPoint point) 
  127. {
  128. // Test if mouse is above the button.
  129. if (!m_bMouseDown)
  130. CheckHover(point);
  131. CButton::OnMouseMove(nFlags, point);
  132. }
  133. void CUniButton::CheckHover(CPoint point)
  134. {
  135. if (HitTest(point)) {
  136. if (!m_bCapture) {
  137. SetCapture();
  138. m_bCapture = true;
  139. }
  140. if (!m_bHover) {
  141. m_bHover = true;
  142. RedrawWindow();
  143. }
  144. }
  145. else {
  146. if (m_bCapture) {
  147. ReleaseCapture();
  148. m_bCapture = false;
  149. }
  150. m_bHover = false;
  151. RedrawWindow();
  152. }
  153. }
  154. LRESULT CUniButton::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
  155. {
  156. // I have noticed that default windows buttons can be clicked very quickly.
  157. // Double or single click both result in a button being pushed down.
  158. // For owner drawn buttons this is not the case. Double click does
  159. // not push button down. Here is a solution for the problem:
  160. // double click message is substituted for single click.
  161. if (message == WM_LBUTTONDBLCLK)
  162. message = WM_LBUTTONDOWN;
  163. return CButton::DefWindowProc(message, wParam, lParam);
  164. }
  165. BOOL CUniButton::HitTest(CPoint point)
  166. {
  167. BOOL result = false;
  168. // Obtain handle to window region.
  169. HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
  170. GetWindowRgn(hRgn);
  171. CRect rgnRect;
  172. GetRgnBox(hRgn, &rgnRect);
  173. // First check if point is in region bounding rect.
  174. // Then check if point is in the region in adition to being in the bouding rect.
  175. result = PtInRect(&rgnRect, point) && PtInRegion(hRgn, point.x, point.y);
  176. // Clean up and exit.
  177. DeleteObject(hRgn);
  178. return result;
  179. }
  180. BOOL CUniButton::OnEraseBkgnd(CDC* pDC) 
  181. {
  182. // do not erase background
  183. return 1;
  184. }
  185. //////////////////////// DRAWING ROUTINES ////////////////////////////
  186. void CUniButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
  187. {
  188. // prepare DC
  189. CDC* pDC = CDC::FromHandle(lpDrawItemStruct -> hDC);
  190. CRect rect;
  191. GetClientRect(rect);
  192. // prepare bitmaps they need to be prepared
  193. if (m_bNeedBitmaps)
  194. PrepareStateBitmaps(pDC, &rect);
  195. // draw button to the screen
  196. DrawButton(pDC, &rect, lpDrawItemStruct -> itemState);
  197. }
  198. void CUniButton::PrepareStateBitmaps(CDC * pDC, CRect * pRect)
  199. {
  200. // prepare memory DC
  201. CDC * pMemDC;
  202. pMemDC = new CDC;
  203. pMemDC -> CreateCompatibleDC(pDC);
  204. // prepare bitmaps for all button states and for the mask
  205. PrepareNormalState(pDC, pMemDC, pRect);
  206. PrepareSelectedState(pDC, pMemDC, pRect);
  207. PrepareHoverState(pDC, pMemDC, pRect);
  208. PrepareDisabledState(pDC, pMemDC, pRect);
  209. // clean up
  210. delete pMemDC; 
  211. m_bNeedBitmaps = false;
  212. }
  213. void CUniButton::PrepareNormalState(CDC * pDC, CDC * pMemDC, CRect * pRect)
  214. {
  215. // prepare MYBS_NORMAL state bitmap
  216. delete m_pNormal;
  217. m_pNormal = new CBitmap;
  218. PaintRgn(pDC, pMemDC, m_pNormal, m_nColor, pRect, true, false);
  219. }
  220. void CUniButton::PrepareSelectedState(CDC * pDC, CDC * pMemDC, CRect * pRect)
  221. {
  222. // prepare MYBS_SELECTED state bitmap
  223. delete m_pSelected;
  224. m_pSelected = new CBitmap;
  225. PaintRgn(pDC, pMemDC, m_pSelected, m_sColor, pRect, true, true);
  226. }
  227. void CUniButton::PrepareHoverState(CDC * pDC, CDC * pMemDC, CRect * pRect)
  228. {
  229. // prepare MYBS_HOVER state bitmap
  230. delete m_pHover;
  231. m_pHover = new CBitmap;
  232. PaintRgn(pDC, pMemDC, m_pHover, m_hColor, pRect, true, false);
  233. }
  234. void CUniButton::PrepareDisabledState(CDC * pDC, CDC * pMemDC, CRect * pRect)
  235. {
  236. // prepare MYBS_DISABLED state bitmap
  237. delete m_pDisabled;
  238. m_pDisabled = new CBitmap;
  239. PaintRgn(pDC, pMemDC, m_pDisabled, m_dColor, pRect, false, false);
  240. }
  241. void CUniButton::PaintRgn(CDC * pDC, CDC * pMemDC, CBitmap * pBitmap, COLORREF color, CRect * pRect, BOOL bEnabled, BOOL bSunken)
  242. {
  243. // create bitmap
  244. pBitmap -> CreateCompatibleBitmap(pDC, pRect -> Width(), pRect -> Height());
  245. CBitmap * pOldBitmap = pMemDC -> SelectObject(pBitmap); 
  246. // prepare region
  247. HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
  248. GetWindowRgn(hRgn);
  249. // fill rect a with transparent color and fill rgn
  250. HBRUSH hBrush = CreateSolidBrush(color);
  251. pMemDC -> FillSolidRect(pRect, RGB(0, 0, 0));
  252. FillRgn(pMemDC -> GetSafeHdc(), hRgn, hBrush);
  253. DeleteObject(hBrush);
  254. // draw 3D border and text
  255. DrawButtonCaption(pMemDC -> GetSafeHdc(), pRect, bEnabled, bSunken);
  256. FrameRgn3D(pMemDC -> GetSafeHdc(), hRgn, bSunken);
  257. // clean up
  258. DeleteObject(hRgn);
  259. pMemDC -> SelectObject(pOldBitmap); 
  260. }
  261. void CUniButton::DrawButtonCaption(HDC hDC, CRect * pRect, BOOL bEnabled, BOOL bSunken) 
  262. {
  263. // select parent font
  264. int nOldMode = SetBkMode(hDC, TRANSPARENT);
  265. CString text;
  266. GetWindowText(text);
  267. LOGFONT lf;
  268. GetParent() -> GetFont() -> GetLogFont(&lf);
  269. HFONT hFont = CreateFontIndirect(&lf);
  270. HFONT hOldFont = (HFONT) SelectObject(hDC, hFont);
  271. // determine point where to output text
  272. TEXTMETRIC tm;
  273. GetTextMetrics(hDC, &tm);
  274. CPoint p = CPoint(m_CenterPoint.x, m_CenterPoint.y + tm.tmHeight/ 2); 
  275. if (bSunken) 
  276. p.Offset(m_nBorder, m_nBorder); 
  277. // draw button caption depending upon button state
  278. if (bEnabled) {
  279. SetTextColor(hDC, GetSysColor(COLOR_BTNTEXT));
  280. SetTextAlign(hDC, TA_CENTER | TA_BOTTOM);
  281. TextOut(hDC, p.x, p.y, text, text.GetLength());
  282. }
  283. else {
  284. SetTextColor(hDC, GetSysColor(COLOR_3DHILIGHT));
  285. TextOut(hDC, p.x + 1, p.y + 1, text, text.GetLength());
  286. SetTextColor(hDC, GetSysColor(COLOR_3DSHADOW));
  287. TextOut(hDC, p.x, p.y, text, text.GetLength());
  288. }
  289. SelectObject(hDC, hOldFont);
  290. DeleteObject(hFont);
  291. SetBkMode(hDC, nOldMode);
  292. }
  293. void CUniButton::FrameRgn3D(HDC hDC, const HRGN hRgn, BOOL bSunken)
  294. {
  295. // we need two differenr regions to keep base region and border region
  296. HBRUSH hBrush;
  297. HRGN hBaseRgn = CreateRectRgn(0, 0, 0, 0);
  298. COLORREF ltOuter, ltInner, rbOuter, rbInner; // colors of inner and outer shadow for top-left and right-bottom corners
  299. // decide on color scheme
  300. if (!bSunken) {
  301. ltOuter = GetSysColor(COLOR_3DLIGHT);
  302. ltInner = GetSysColor(COLOR_3DHILIGHT);
  303. rbOuter = GetSysColor(COLOR_3DDKSHADOW);
  304. rbInner = GetSysColor(COLOR_3DSHADOW);
  305. }
  306. else {
  307. rbInner = GetSysColor(COLOR_3DLIGHT);
  308. rbOuter = GetSysColor(COLOR_3DHILIGHT);
  309. ltInner = GetSysColor(COLOR_3DDKSHADOW);
  310. ltOuter = GetSysColor(COLOR_3DSHADOW);
  311. }
  312. // offset highlight and shadow regions
  313. // substract them from the base region 
  314. switch (m_nBorder)
  315. {
  316. case 2:
  317. CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
  318. OffsetRgn(hBaseRgn, 2, 2);
  319. CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
  320. hBrush = CreateSolidBrush(ltInner);
  321. FillRgn(hDC, hBaseRgn, hBrush);
  322. DeleteObject(hBrush);
  323. CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
  324. OffsetRgn(hBaseRgn, -2, -2);
  325. CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
  326. hBrush = CreateSolidBrush(rbInner);
  327. FillRgn(hDC, hBaseRgn, hBrush);
  328. DeleteObject(hBrush);
  329. CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
  330. OffsetRgn(hBaseRgn, 1, 1);
  331. CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
  332. hBrush = CreateSolidBrush(ltOuter);
  333. FillRgn(hDC, hBaseRgn, hBrush);
  334. DeleteObject(hBrush);
  335. CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
  336. OffsetRgn(hBaseRgn, -1, -1);
  337. CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
  338. hBrush = CreateSolidBrush(rbOuter);
  339. FillRgn(hDC, hBaseRgn, hBrush);
  340. DeleteObject(hBrush);
  341. break;
  342. default:
  343. CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
  344. OffsetRgn(hBaseRgn, 1, 1);
  345. CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
  346. hBrush = CreateSolidBrush(ltInner);
  347. FillRgn(hDC, hBaseRgn, hBrush);
  348. DeleteObject(hBrush);
  349. CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
  350. OffsetRgn(hBaseRgn, -1, -1);
  351. CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
  352. hBrush = CreateSolidBrush(rbOuter);
  353. FillRgn(hDC, hBaseRgn, hBrush);
  354. DeleteObject(hBrush);
  355. break;
  356. }
  357. // clean up regions
  358. DeleteObject(hBaseRgn);
  359. }
  360. void CUniButton::DrawButton(CDC * pDC, CRect * pRect, UINT state)
  361. {
  362. // create memory DC
  363. CDC * pMemDC = new CDC;
  364. pMemDC -> CreateCompatibleDC(pDC);
  365. CBitmap * pOldBitmap;
  366. // get region
  367. HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
  368. GetWindowRgn(hRgn);
  369. // select bitmap to paint depending upon button state
  370. if (state & ODS_DISABLED)
  371. pOldBitmap = pMemDC -> SelectObject(m_pDisabled);
  372. else {
  373. if (state & ODS_SELECTED)
  374. pOldBitmap = pMemDC -> SelectObject(m_pSelected);
  375. else {
  376. if (m_bHover)
  377. pOldBitmap = pMemDC -> SelectObject(m_pHover);
  378. else 
  379. pOldBitmap = pMemDC -> SelectObject(m_pNormal);
  380. }
  381. }
  382. // paint using region for clipping
  383. ::SelectClipRgn(pDC -> GetSafeHdc(), hRgn);
  384. pDC -> BitBlt(0, 0, pRect -> Width(), pRect -> Height(), pMemDC, 0, 0, SRCCOPY);
  385. ::SelectClipRgn(pDC -> GetSafeHdc(), NULL);
  386. // clean up
  387. DeleteObject(hRgn);
  388. pMemDC -> SelectObject(pOldBitmap);
  389. delete pMemDC;
  390. }
  391. void CUniButton::RgnPixelWork(CDC * pDC, CRgn * pRgn)
  392. {
  393. // get size of data composing region
  394. int size = pRgn -> GetRegionData(NULL, 0);
  395. HANDLE hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size);
  396. RGNDATA * pData = (RGNDATA *)GlobalLock(hData);
  397. // retrieve region data
  398. int res = pRgn -> GetRegionData(pData, size);
  399. RECT * pRect = (RECT *) pData -> Buffer;
  400. // now we know how region is represented and we are able to manipulate it as we like
  401. for (DWORD i = 0; i < pData -> rdh.nCount; i++) {
  402. RECT rect = *(pRect + i);
  403. for (int x = rect.left; x < rect.right; x++)
  404. for (int y = rect.top; y < rect.bottom; y++) {
  405. // use SetPixel(x, y, color) to do pixel work
  406. }
  407. }
  408. // free region data
  409. GlobalUnlock(hData);
  410. GlobalFree(hData);
  411. }