GdipButton.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. //
  2. // GdipButton.cpp : Version 1.0 - see article at CodeProject.com
  3. //
  4. // Author: Darren Sessions
  5. //
  6. //
  7. // Description:
  8. // GdipButton is a CButton derived control that uses GDI+
  9. // to support alternate image formats
  10. //
  11. // History
  12. // Version 1.0 - 2008 June 10
  13. // - Initial public release
  14. //
  15. // License:
  16. // This software is released under the Code Project Open License (CPOL),
  17. // which may be found here: http://www.codeproject.com/info/eula.aspx
  18. // You are free to use this software in any way you like, except that you
  19. // may not sell this source code.
  20. //
  21. // This software is provided "as is" with no expressed or implied warranty.
  22. // I accept no liability for any damage or loss of business that this
  23. // software may cause.
  24. //
  25. ///////////////////////////////////////////////////////////////////////////////
  26. #include "stdafx.h"
  27. #include "GdipButton.h"
  28. #include "CGdiPlusBitmap.h"
  29. #include "MemDC.h"
  30. #include "CP_Main.h"
  31. #ifdef _DEBUG
  32. #define new DEBUG_NEW
  33. #undef THIS_FILE
  34. static char THIS_FILE[] = __FILE__;
  35. #endif
  36. /////////////////////////////////////////////////////////////////////////////
  37. // CGdipButton
  38. CGdipButton::CGdipButton()
  39. {
  40. m_pStdImage = NULL;
  41. m_pAltImage = NULL;
  42. m_bHaveBitmaps = FALSE;
  43. m_bHaveAltImage = FALSE;
  44. m_pCurBtn = NULL;
  45. m_bIsDisabled = FALSE;
  46. m_bIsToggle = FALSE;
  47. m_bIsHovering = FALSE;
  48. m_bIsTracking = FALSE;
  49. m_nCurType = STD_TYPE;
  50. m_pToolTip = NULL;
  51. }
  52. CGdipButton::~CGdipButton()
  53. {
  54. if(m_pStdImage) delete m_pStdImage;
  55. if(m_pAltImage) delete m_pAltImage;
  56. if(m_pToolTip) delete m_pToolTip;
  57. }
  58. BEGIN_MESSAGE_MAP(CGdipButton, CButton)
  59. //{{AFX_MSG_MAP(CGdipButton)
  60. ON_WM_DRAWITEM()
  61. ON_WM_ERASEBKGND()
  62. ON_WM_CTLCOLOR_REFLECT()
  63. ON_WM_MOUSEMOVE()
  64. ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
  65. ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
  66. //}}AFX_MSG_MAP
  67. END_MESSAGE_MAP()
  68. BOOL CGdipButton::LoadStdImageDPI(UINT id96, UINT id120, UINT id144, UINT id192, LPCTSTR pType)
  69. {
  70. BOOL ret = FALSE;
  71. if (theApp.m_metrics.GetDPIX() >= 192)
  72. {
  73. ret = LoadStdImage(id192, pType);
  74. }
  75. else if (theApp.m_metrics.GetDPIX() >= 144)
  76. {
  77. ret = LoadStdImage(id144, pType);
  78. }
  79. else if (theApp.m_metrics.GetDPIX() >= 120)
  80. {
  81. ret = LoadStdImage(id120, pType);
  82. }
  83. else
  84. {
  85. ret = LoadStdImage(id96, pType);
  86. }
  87. return ret;
  88. }
  89. //=============================================================================
  90. //
  91. // LoadStdImage()
  92. //
  93. // Purpose: The LoadStdImage() Loads the image for the button. This
  94. // function must be called at a minimum or the button wont do
  95. // anything.
  96. //
  97. // Parameters:
  98. // [IN] id
  99. // resource id, one of the resources already imported with the
  100. // resource editor, usually begins with IDR_
  101. //
  102. // [IN] pType
  103. // pointer to string describing the resource type
  104. //
  105. // Returns: BOOL
  106. // Non zero if successful, otherwise zero
  107. //
  108. //=============================================================================
  109. BOOL CGdipButton::LoadStdImage(UINT id, LPCTSTR pType)
  110. {
  111. m_pStdImage = new CGdiPlusBitmapResource;
  112. return m_pStdImage->Load(id, pType);
  113. }
  114. //=============================================================================
  115. //
  116. // LoadAltImage()
  117. //
  118. // Purpose: The LoadAltImage() Loads the altername image for the button.
  119. // This function call is optional
  120. // Parameters:
  121. // [IN] id
  122. // resource id, one of the resources already imported with the
  123. // resource editor, usually begins with IDR_
  124. //
  125. // [IN] pType
  126. // pointer to string describing the resource type
  127. //
  128. // Returns: BOOL
  129. // Non zero if successful, otherwise zero
  130. //
  131. //=============================================================================
  132. BOOL CGdipButton::LoadAltImage(UINT id, LPCTSTR pType)
  133. {
  134. m_bHaveAltImage = TRUE;
  135. m_pAltImage = new CGdiPlusBitmapResource;
  136. return (m_pAltImage->Load(id, pType));
  137. }
  138. //=============================================================================
  139. //
  140. // The framework calls this member function when a child control is about to
  141. // be drawn. All the bitmaps are created here on the first call. Every thing
  142. // is done with a memory DC except the background, which get's it's information
  143. // from the parent. The background is needed for transparent portions of PNG
  144. // images. An always on top app (such as Task Manager) that is in the way can
  145. // cause it to get an incorrect background. To avoid this, the parent should
  146. // call the SetBkGnd function with a memory DC when it creates the background.
  147. //
  148. //=============================================================================
  149. HBRUSH CGdipButton::CtlColor(CDC* pScreenDC, UINT nCtlColor)
  150. {
  151. if(!m_bHaveBitmaps)
  152. {
  153. if(!m_pStdImage)
  154. {
  155. return NULL; // Load the standard image with LoadStdImage()
  156. }
  157. CBitmap bmp, *pOldBitmap;
  158. CRect rect;
  159. GetClientRect(rect);
  160. // do everything with mem dc
  161. CMemDCEx pDC(pScreenDC, rect);
  162. Gdiplus::Graphics graphics(pDC->m_hDC);
  163. // background
  164. if (m_dcBk.m_hDC == NULL)
  165. {
  166. CRect rect1;
  167. CClientDC clDC(GetParent());
  168. GetWindowRect(rect1);
  169. GetParent()->ScreenToClient(rect1);
  170. m_dcBk.CreateCompatibleDC(&clDC);
  171. bmp.CreateCompatibleBitmap(&clDC, rect.Width(), rect.Height());
  172. pOldBitmap = m_dcBk.SelectObject(&bmp);
  173. m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), &clDC, rect1.left, rect1.top, SRCCOPY);
  174. bmp.DeleteObject();
  175. }
  176. // standard image
  177. if (m_dcStd.m_hDC == NULL)
  178. {
  179. PaintBk(pDC);
  180. graphics.DrawImage(*m_pStdImage, 0, 0);
  181. m_dcStd.CreateCompatibleDC(pDC);
  182. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  183. pOldBitmap = m_dcStd.SelectObject(&bmp);
  184. m_dcStd.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  185. bmp.DeleteObject();
  186. // standard image pressed
  187. if (m_dcStdP.m_hDC == NULL)
  188. {
  189. PaintBk(pDC);
  190. graphics.DrawImage(*m_pStdImage, 1, 1);
  191. m_dcStdP.CreateCompatibleDC(pDC);
  192. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  193. pOldBitmap = m_dcStdP.SelectObject(&bmp);
  194. m_dcStdP.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  195. bmp.DeleteObject();
  196. }
  197. // standard image hot
  198. if(m_dcStdH.m_hDC == NULL)
  199. {
  200. PaintBk(pDC);
  201. ColorMatrix HotMat = { 1.05f, 0.00f, 0.00f, 0.00f, 0.00f,
  202. 0.00f, 1.05f, 0.00f, 0.00f, 0.00f,
  203. 0.00f, 0.00f, 1.05f, 0.00f, 0.00f,
  204. 0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
  205. 0.05f, 0.05f, 0.05f, 0.00f, 1.00f };
  206. ImageAttributes ia;
  207. ia.SetColorMatrix(&HotMat);
  208. float width = (float)m_pStdImage->m_pBitmap->GetWidth();
  209. float height = (float)m_pStdImage->m_pBitmap->GetHeight();
  210. RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height;
  211. graphics.DrawImage(*m_pStdImage, grect, 0, 0, width, height, UnitPixel, &ia);
  212. m_dcStdH.CreateCompatibleDC(pDC);
  213. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  214. pOldBitmap = m_dcStdH.SelectObject(&bmp);
  215. m_dcStdH.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  216. bmp.DeleteObject();
  217. }
  218. // grayscale image
  219. if(m_dcGS.m_hDC == NULL)
  220. {
  221. PaintBk(pDC);
  222. ColorMatrix GrayMat = { 0.30f, 0.30f, 0.30f, 0.00f, 0.00f,
  223. 0.59f, 0.59f, 0.59f, 0.00f, 0.00f,
  224. 0.11f, 0.11f, 0.11f, 0.00f, 0.00f,
  225. 0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
  226. 0.00f, 0.00f, 0.00f, 0.00f, 1.00f };
  227. ImageAttributes ia;
  228. ia.SetColorMatrix(&GrayMat);
  229. float width = (float)m_pStdImage->m_pBitmap->GetWidth();
  230. float height = (float)m_pStdImage->m_pBitmap->GetHeight();
  231. RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height;
  232. graphics.DrawImage(*m_pStdImage, grect, 0, 0, width, height, UnitPixel, &ia);
  233. m_dcGS.CreateCompatibleDC(pDC);
  234. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  235. pOldBitmap = m_dcGS.SelectObject(&bmp);
  236. m_dcGS.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  237. bmp.DeleteObject();
  238. }
  239. }
  240. // alternate image
  241. if( (m_dcAlt.m_hDC == NULL) && m_bHaveAltImage )
  242. {
  243. PaintBk(pDC);
  244. graphics.DrawImage(*m_pAltImage, 0, 0);
  245. m_dcAlt.CreateCompatibleDC(pDC);
  246. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  247. pOldBitmap = m_dcAlt.SelectObject(&bmp);
  248. m_dcAlt.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  249. bmp.DeleteObject();
  250. // alternate image pressed
  251. if( (m_dcAltP.m_hDC == NULL) && m_bHaveAltImage )
  252. {
  253. PaintBk(pDC);
  254. graphics.DrawImage(*m_pAltImage, 1, 1);
  255. m_dcAltP.CreateCompatibleDC(pDC);
  256. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  257. pOldBitmap = m_dcAltP.SelectObject(&bmp);
  258. m_dcAltP.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  259. bmp.DeleteObject();
  260. }
  261. // alternate image hot
  262. if(m_dcAltH.m_hDC == NULL)
  263. {
  264. PaintBk(pDC);
  265. ColorMatrix HotMat = { 1.05f, 0.00f, 0.00f, 0.00f, 0.00f,
  266. 0.00f, 1.05f, 0.00f, 0.00f, 0.00f,
  267. 0.00f, 0.00f, 1.05f, 0.00f, 0.00f,
  268. 0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
  269. 0.05f, 0.05f, 0.05f, 0.00f, 1.00f };
  270. ImageAttributes ia;
  271. ia.SetColorMatrix(&HotMat);
  272. float width = (float)m_pStdImage->m_pBitmap->GetWidth();
  273. float height = (float)m_pStdImage->m_pBitmap->GetHeight();
  274. RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height;
  275. graphics.DrawImage(*m_pAltImage, grect, 0, 0, width, height, UnitPixel, &ia);
  276. m_dcAltH.CreateCompatibleDC(pDC);
  277. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  278. pOldBitmap = m_dcAltH.SelectObject(&bmp);
  279. m_dcAltH.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  280. bmp.DeleteObject();
  281. }
  282. }
  283. if(m_pCurBtn == NULL)
  284. {
  285. m_pCurBtn = &m_dcStd;
  286. }
  287. m_bHaveBitmaps = TRUE;
  288. }
  289. return NULL;
  290. }
  291. //=============================================================================
  292. // paint the background
  293. //=============================================================================
  294. void CGdipButton::PaintBk(CDC *pDC)
  295. {
  296. CRect rect;
  297. GetClientRect(rect);
  298. pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &m_dcBk, 0, 0, SRCCOPY);
  299. }
  300. //=============================================================================
  301. // paint the bitmap currently pointed to with m_pCurBtn
  302. //=============================================================================
  303. void CGdipButton::PaintBtn(CDC *pDC)
  304. {
  305. CRect rect;
  306. GetClientRect(rect);
  307. pDC->BitBlt(0, 0, rect.Width(), rect.Height(), m_pCurBtn, 0, 0, SRCCOPY);
  308. }
  309. //=============================================================================
  310. // enables the toggle mode
  311. // returns if it doesn't have the alternate image
  312. //=============================================================================
  313. void CGdipButton::EnableToggle(BOOL bEnable)
  314. {
  315. if(!m_bHaveAltImage) return;
  316. m_bIsToggle = bEnable;
  317. // this actually makes it start in the std state since toggle is called before paint
  318. if(bEnable) m_pCurBtn = &m_dcAlt;
  319. else m_pCurBtn = &m_dcStd;
  320. }
  321. //=============================================================================
  322. // sets the image type and disabled state then repaints
  323. //=============================================================================
  324. void CGdipButton::SetImage(int type)
  325. {
  326. m_nCurType = type;
  327. (type == DIS_TYPE) ? m_bIsDisabled = TRUE : m_bIsDisabled = FALSE;
  328. Invalidate();
  329. }
  330. //=============================================================================
  331. // set the control to owner draw
  332. //=============================================================================
  333. void CGdipButton::PreSubclassWindow()
  334. {
  335. // Set control to owner draw
  336. ModifyStyle(0, BS_OWNERDRAW, SWP_FRAMECHANGED);
  337. CButton::PreSubclassWindow();
  338. }
  339. //=============================================================================
  340. // disable double click
  341. //=============================================================================
  342. BOOL CGdipButton::PreTranslateMessage(MSG* pMsg)
  343. {
  344. if (pMsg->message == WM_LBUTTONDBLCLK)
  345. pMsg->message = WM_LBUTTONDOWN;
  346. if (m_pToolTip != NULL)
  347. {
  348. if (::IsWindow(m_pToolTip->m_hWnd))
  349. {
  350. m_pToolTip->RelayEvent(pMsg);
  351. }
  352. }
  353. return CButton::PreTranslateMessage(pMsg);
  354. }
  355. //=============================================================================
  356. // overide the erase function
  357. //=============================================================================
  358. BOOL CGdipButton::OnEraseBkgnd(CDC* pDC)
  359. {
  360. return TRUE;
  361. }
  362. //=============================================================================
  363. // Paint the button depending on the state of the mouse
  364. //=============================================================================
  365. void CGdipButton::DrawItem(LPDRAWITEMSTRUCT lpDIS)
  366. {
  367. CDC* pDC = CDC::FromHandle(lpDIS->hDC);
  368. // handle disabled state
  369. if(m_bIsDisabled)
  370. {
  371. m_pCurBtn = &m_dcGS;
  372. PaintBtn(pDC);
  373. return;
  374. }
  375. BOOL bIsPressed = (lpDIS->itemState & ODS_SELECTED);
  376. // handle toggle button
  377. if(m_bIsToggle && bIsPressed)
  378. {
  379. (m_nCurType == STD_TYPE) ? m_nCurType = ALT_TYPE : m_nCurType = STD_TYPE;
  380. }
  381. if(bIsPressed)
  382. {
  383. if(m_nCurType == STD_TYPE)
  384. m_pCurBtn = &m_dcStdP;
  385. else
  386. m_pCurBtn = &m_dcAltP;
  387. }
  388. else if(m_bIsHovering)
  389. {
  390. if(m_nCurType == STD_TYPE)
  391. m_pCurBtn = &m_dcStdH;
  392. else
  393. m_pCurBtn = &m_dcAltH;
  394. }
  395. else
  396. {
  397. if(m_nCurType == STD_TYPE)
  398. m_pCurBtn = &m_dcStd;
  399. else
  400. m_pCurBtn = &m_dcAlt;
  401. }
  402. // paint the button
  403. PaintBtn(pDC);
  404. }
  405. //=============================================================================
  406. LRESULT CGdipButton::OnMouseHover(WPARAM wparam, LPARAM lparam)
  407. //=============================================================================
  408. {
  409. m_bIsHovering = TRUE;
  410. Invalidate();
  411. DeleteToolTip();
  412. // Create a new Tooltip with new Button Size and Location
  413. SetToolTipText(m_tooltext);
  414. if (m_pToolTip != NULL)
  415. {
  416. if (::IsWindow(m_pToolTip->m_hWnd))
  417. {
  418. //Display ToolTip
  419. m_pToolTip->Update();
  420. }
  421. }
  422. return 0;
  423. }
  424. //=============================================================================
  425. LRESULT CGdipButton::OnMouseLeave(WPARAM wparam, LPARAM lparam)
  426. //=============================================================================
  427. {
  428. m_bIsTracking = FALSE;
  429. m_bIsHovering = FALSE;
  430. Invalidate();
  431. return 0;
  432. }
  433. //=============================================================================
  434. void CGdipButton::OnMouseMove(UINT nFlags, CPoint point)
  435. //=============================================================================
  436. {
  437. if (!m_bIsTracking)
  438. {
  439. TRACKMOUSEEVENT tme;
  440. tme.cbSize = sizeof(tme);
  441. tme.hwndTrack = m_hWnd;
  442. tme.dwFlags = TME_LEAVE|TME_HOVER;
  443. tme.dwHoverTime = 1;
  444. m_bIsTracking = _TrackMouseEvent(&tme);
  445. }
  446. CButton::OnMouseMove(nFlags, point);
  447. }
  448. //=============================================================================
  449. //
  450. // Call this member function with a memory DC from the code that paints
  451. // the parents background. Passing the screen DC defeats the purpose of
  452. // using this function.
  453. //
  454. //=============================================================================
  455. void CGdipButton::SetBkGnd(CDC* pDC)
  456. {
  457. CRect rect, rectS;
  458. CBitmap bmp, *pOldBitmap;
  459. GetClientRect(rect);
  460. GetWindowRect(rectS);
  461. GetParent()->ScreenToClient(rectS);
  462. m_dcBk.DeleteDC();
  463. m_dcBk.CreateCompatibleDC(pDC);
  464. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  465. pOldBitmap = m_dcBk.SelectObject(&bmp);
  466. m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, rectS.left, rectS.top, SRCCOPY);
  467. bmp.DeleteObject();
  468. }
  469. //=============================================================================
  470. // Set the tooltip with a string resource
  471. //=============================================================================
  472. void CGdipButton::SetToolTipText(UINT nId, BOOL bActivate)
  473. {
  474. // load string resource
  475. m_tooltext.LoadString(nId);
  476. // If string resource is not empty
  477. if (m_tooltext.IsEmpty() == FALSE)
  478. {
  479. SetToolTipText(m_tooltext, bActivate);
  480. }
  481. }
  482. //=============================================================================
  483. // Set the tooltip with a CString
  484. //=============================================================================
  485. void CGdipButton::SetToolTipText(CString spText, BOOL bActivate)
  486. {
  487. // We cannot accept NULL pointer
  488. if (spText.IsEmpty()) return;
  489. // Initialize ToolTip
  490. InitToolTip();
  491. m_tooltext = spText;
  492. // If there is no tooltip defined then add it
  493. if (m_pToolTip->GetToolCount() == 0)
  494. {
  495. CRect rectBtn;
  496. GetClientRect(rectBtn);
  497. m_pToolTip->AddTool(this, m_tooltext, rectBtn, 1);
  498. }
  499. // Set text for tooltip
  500. m_pToolTip->UpdateTipText(m_tooltext, this, 1);
  501. m_pToolTip->SetDelayTime(2000);
  502. m_pToolTip->Activate(bActivate);
  503. }
  504. //=============================================================================
  505. void CGdipButton::InitToolTip()
  506. //=============================================================================
  507. {
  508. if (m_pToolTip == NULL)
  509. {
  510. m_pToolTip = new CToolTipCtrl;
  511. // Create ToolTip control
  512. m_pToolTip->Create(this);
  513. m_pToolTip->Activate(TRUE);
  514. }
  515. }
  516. //=============================================================================
  517. void CGdipButton::DeleteToolTip()
  518. //=============================================================================
  519. {
  520. // Destroy Tooltip incase the size of the button has changed.
  521. if (m_pToolTip != NULL)
  522. {
  523. delete m_pToolTip;
  524. m_pToolTip = NULL;
  525. }
  526. }