GdipButton.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  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. void CGdipButton::Test(CString c)
  115. {
  116. m_pStdImage = new CGdiPlusBitmapResource;
  117. m_pStdImage->Loads(c);
  118. }
  119. //=============================================================================
  120. //
  121. // LoadAltImage()
  122. //
  123. // Purpose: The LoadAltImage() Loads the altername image for the button.
  124. // This function call is optional
  125. // Parameters:
  126. // [IN] id
  127. // resource id, one of the resources already imported with the
  128. // resource editor, usually begins with IDR_
  129. //
  130. // [IN] pType
  131. // pointer to string describing the resource type
  132. //
  133. // Returns: BOOL
  134. // Non zero if successful, otherwise zero
  135. //
  136. //=============================================================================
  137. BOOL CGdipButton::LoadAltImage(UINT id, LPCTSTR pType)
  138. {
  139. m_bHaveAltImage = TRUE;
  140. m_pAltImage = new CGdiPlusBitmapResource;
  141. return (m_pAltImage->Load(id, pType));
  142. }
  143. //=============================================================================
  144. //
  145. // The framework calls this member function when a child control is about to
  146. // be drawn. All the bitmaps are created here on the first call. Every thing
  147. // is done with a memory DC except the background, which get's it's information
  148. // from the parent. The background is needed for transparent portions of PNG
  149. // images. An always on top app (such as Task Manager) that is in the way can
  150. // cause it to get an incorrect background. To avoid this, the parent should
  151. // call the SetBkGnd function with a memory DC when it creates the background.
  152. //
  153. //=============================================================================
  154. HBRUSH CGdipButton::CtlColor(CDC* pScreenDC, UINT nCtlColor)
  155. {
  156. if(!m_bHaveBitmaps)
  157. {
  158. if(!m_pStdImage)
  159. {
  160. return NULL; // Load the standard image with LoadStdImage()
  161. }
  162. CBitmap bmp, *pOldBitmap;
  163. CRect rect;
  164. GetClientRect(rect);
  165. // do everything with mem dc
  166. CMemDCEx pDC(pScreenDC, rect);
  167. Gdiplus::Graphics graphics(pDC->m_hDC);
  168. // background
  169. if (m_dcBk.m_hDC == NULL)
  170. {
  171. CRect rect1;
  172. CClientDC clDC(GetParent());
  173. GetWindowRect(rect1);
  174. GetParent()->ScreenToClient(rect1);
  175. m_dcBk.CreateCompatibleDC(&clDC);
  176. bmp.CreateCompatibleBitmap(&clDC, rect.Width(), rect.Height());
  177. pOldBitmap = m_dcBk.SelectObject(&bmp);
  178. m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), &clDC, rect1.left, rect1.top, SRCCOPY);
  179. bmp.DeleteObject();
  180. }
  181. // standard image
  182. if (m_dcStd.m_hDC == NULL)
  183. {
  184. PaintBk(pDC);
  185. graphics.DrawImage(*m_pStdImage, 0, 0);
  186. m_dcStd.CreateCompatibleDC(pDC);
  187. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  188. pOldBitmap = m_dcStd.SelectObject(&bmp);
  189. m_dcStd.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  190. bmp.DeleteObject();
  191. // standard image pressed
  192. if (m_dcStdP.m_hDC == NULL)
  193. {
  194. PaintBk(pDC);
  195. graphics.DrawImage(*m_pStdImage, 1, 1);
  196. m_dcStdP.CreateCompatibleDC(pDC);
  197. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  198. pOldBitmap = m_dcStdP.SelectObject(&bmp);
  199. m_dcStdP.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  200. bmp.DeleteObject();
  201. }
  202. // standard image hot
  203. if(m_dcStdH.m_hDC == NULL)
  204. {
  205. PaintBk(pDC);
  206. ColorMatrix HotMat = { 1.05f, 0.00f, 0.00f, 0.00f, 0.00f,
  207. 0.00f, 1.05f, 0.00f, 0.00f, 0.00f,
  208. 0.00f, 0.00f, 1.05f, 0.00f, 0.00f,
  209. 0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
  210. 0.05f, 0.05f, 0.05f, 0.00f, 1.00f };
  211. ImageAttributes ia;
  212. ia.SetColorMatrix(&HotMat);
  213. float width = (float)m_pStdImage->m_pBitmap->GetWidth();
  214. float height = (float)m_pStdImage->m_pBitmap->GetHeight();
  215. RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height;
  216. graphics.DrawImage(*m_pStdImage, grect, 0, 0, width, height, UnitPixel, &ia);
  217. m_dcStdH.CreateCompatibleDC(pDC);
  218. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  219. pOldBitmap = m_dcStdH.SelectObject(&bmp);
  220. m_dcStdH.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  221. bmp.DeleteObject();
  222. }
  223. // grayscale image
  224. if(m_dcGS.m_hDC == NULL)
  225. {
  226. PaintBk(pDC);
  227. ColorMatrix GrayMat = { 0.30f, 0.30f, 0.30f, 0.00f, 0.00f,
  228. 0.59f, 0.59f, 0.59f, 0.00f, 0.00f,
  229. 0.11f, 0.11f, 0.11f, 0.00f, 0.00f,
  230. 0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
  231. 0.00f, 0.00f, 0.00f, 0.00f, 1.00f };
  232. ImageAttributes ia;
  233. ia.SetColorMatrix(&GrayMat);
  234. float width = (float)m_pStdImage->m_pBitmap->GetWidth();
  235. float height = (float)m_pStdImage->m_pBitmap->GetHeight();
  236. RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height;
  237. graphics.DrawImage(*m_pStdImage, grect, 0, 0, width, height, UnitPixel, &ia);
  238. m_dcGS.CreateCompatibleDC(pDC);
  239. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  240. pOldBitmap = m_dcGS.SelectObject(&bmp);
  241. m_dcGS.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  242. bmp.DeleteObject();
  243. }
  244. }
  245. // alternate image
  246. if( (m_dcAlt.m_hDC == NULL) && m_bHaveAltImage )
  247. {
  248. PaintBk(pDC);
  249. graphics.DrawImage(*m_pAltImage, 0, 0);
  250. m_dcAlt.CreateCompatibleDC(pDC);
  251. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  252. pOldBitmap = m_dcAlt.SelectObject(&bmp);
  253. m_dcAlt.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  254. bmp.DeleteObject();
  255. // alternate image pressed
  256. if( (m_dcAltP.m_hDC == NULL) && m_bHaveAltImage )
  257. {
  258. PaintBk(pDC);
  259. graphics.DrawImage(*m_pAltImage, 1, 1);
  260. m_dcAltP.CreateCompatibleDC(pDC);
  261. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  262. pOldBitmap = m_dcAltP.SelectObject(&bmp);
  263. m_dcAltP.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  264. bmp.DeleteObject();
  265. }
  266. // alternate image hot
  267. if(m_dcAltH.m_hDC == NULL)
  268. {
  269. PaintBk(pDC);
  270. ColorMatrix HotMat = { 1.05f, 0.00f, 0.00f, 0.00f, 0.00f,
  271. 0.00f, 1.05f, 0.00f, 0.00f, 0.00f,
  272. 0.00f, 0.00f, 1.05f, 0.00f, 0.00f,
  273. 0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
  274. 0.05f, 0.05f, 0.05f, 0.00f, 1.00f };
  275. ImageAttributes ia;
  276. ia.SetColorMatrix(&HotMat);
  277. float width = (float)m_pStdImage->m_pBitmap->GetWidth();
  278. float height = (float)m_pStdImage->m_pBitmap->GetHeight();
  279. RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height;
  280. graphics.DrawImage(*m_pAltImage, grect, 0, 0, width, height, UnitPixel, &ia);
  281. m_dcAltH.CreateCompatibleDC(pDC);
  282. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  283. pOldBitmap = m_dcAltH.SelectObject(&bmp);
  284. m_dcAltH.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
  285. bmp.DeleteObject();
  286. }
  287. }
  288. if(m_pCurBtn == NULL)
  289. {
  290. m_pCurBtn = &m_dcStd;
  291. }
  292. m_bHaveBitmaps = TRUE;
  293. }
  294. return NULL;
  295. }
  296. //=============================================================================
  297. // paint the background
  298. //=============================================================================
  299. void CGdipButton::PaintBk(CDC *pDC)
  300. {
  301. CRect rect;
  302. GetClientRect(rect);
  303. pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &m_dcBk, 0, 0, SRCCOPY);
  304. }
  305. //=============================================================================
  306. // paint the bitmap currently pointed to with m_pCurBtn
  307. //=============================================================================
  308. void CGdipButton::PaintBtn(CDC *pDC)
  309. {
  310. CRect rect;
  311. GetClientRect(rect);
  312. pDC->BitBlt(0, 0, rect.Width(), rect.Height(), m_pCurBtn, 0, 0, SRCCOPY);
  313. }
  314. //=============================================================================
  315. // enables the toggle mode
  316. // returns if it doesn't have the alternate image
  317. //=============================================================================
  318. void CGdipButton::EnableToggle(BOOL bEnable)
  319. {
  320. if(!m_bHaveAltImage) return;
  321. m_bIsToggle = bEnable;
  322. // this actually makes it start in the std state since toggle is called before paint
  323. if(bEnable) m_pCurBtn = &m_dcAlt;
  324. else m_pCurBtn = &m_dcStd;
  325. }
  326. //=============================================================================
  327. // sets the image type and disabled state then repaints
  328. //=============================================================================
  329. void CGdipButton::SetImage(int type)
  330. {
  331. m_nCurType = type;
  332. (type == DIS_TYPE) ? m_bIsDisabled = TRUE : m_bIsDisabled = FALSE;
  333. Invalidate();
  334. }
  335. //=============================================================================
  336. // set the control to owner draw
  337. //=============================================================================
  338. void CGdipButton::PreSubclassWindow()
  339. {
  340. // Set control to owner draw
  341. ModifyStyle(0, BS_OWNERDRAW, SWP_FRAMECHANGED);
  342. CButton::PreSubclassWindow();
  343. }
  344. //=============================================================================
  345. // disable double click
  346. //=============================================================================
  347. BOOL CGdipButton::PreTranslateMessage(MSG* pMsg)
  348. {
  349. if (pMsg->message == WM_LBUTTONDBLCLK)
  350. pMsg->message = WM_LBUTTONDOWN;
  351. if (m_pToolTip != NULL)
  352. {
  353. if (::IsWindow(m_pToolTip->m_hWnd))
  354. {
  355. m_pToolTip->RelayEvent(pMsg);
  356. }
  357. }
  358. return CButton::PreTranslateMessage(pMsg);
  359. }
  360. //=============================================================================
  361. // overide the erase function
  362. //=============================================================================
  363. BOOL CGdipButton::OnEraseBkgnd(CDC* pDC)
  364. {
  365. return TRUE;
  366. }
  367. //=============================================================================
  368. // Paint the button depending on the state of the mouse
  369. //=============================================================================
  370. void CGdipButton::DrawItem(LPDRAWITEMSTRUCT lpDIS)
  371. {
  372. CDC* pDC = CDC::FromHandle(lpDIS->hDC);
  373. // handle disabled state
  374. if(m_bIsDisabled)
  375. {
  376. m_pCurBtn = &m_dcGS;
  377. PaintBtn(pDC);
  378. return;
  379. }
  380. BOOL bIsPressed = (lpDIS->itemState & ODS_SELECTED);
  381. // handle toggle button
  382. if(m_bIsToggle && bIsPressed)
  383. {
  384. (m_nCurType == STD_TYPE) ? m_nCurType = ALT_TYPE : m_nCurType = STD_TYPE;
  385. }
  386. if(bIsPressed)
  387. {
  388. if(m_nCurType == STD_TYPE)
  389. m_pCurBtn = &m_dcStdP;
  390. else
  391. m_pCurBtn = &m_dcAltP;
  392. }
  393. else if(m_bIsHovering)
  394. {
  395. if(m_nCurType == STD_TYPE)
  396. m_pCurBtn = &m_dcStdH;
  397. else
  398. m_pCurBtn = &m_dcAltH;
  399. }
  400. else
  401. {
  402. if(m_nCurType == STD_TYPE)
  403. m_pCurBtn = &m_dcStd;
  404. else
  405. m_pCurBtn = &m_dcAlt;
  406. }
  407. // paint the button
  408. PaintBtn(pDC);
  409. }
  410. //=============================================================================
  411. LRESULT CGdipButton::OnMouseHover(WPARAM wparam, LPARAM lparam)
  412. //=============================================================================
  413. {
  414. m_bIsHovering = TRUE;
  415. Invalidate();
  416. DeleteToolTip();
  417. // Create a new Tooltip with new Button Size and Location
  418. SetToolTipText(m_tooltext);
  419. if (m_pToolTip != NULL)
  420. {
  421. if (::IsWindow(m_pToolTip->m_hWnd))
  422. {
  423. //Display ToolTip
  424. m_pToolTip->Update();
  425. }
  426. }
  427. return 0;
  428. }
  429. //=============================================================================
  430. LRESULT CGdipButton::OnMouseLeave(WPARAM wparam, LPARAM lparam)
  431. //=============================================================================
  432. {
  433. m_bIsTracking = FALSE;
  434. m_bIsHovering = FALSE;
  435. Invalidate();
  436. return 0;
  437. }
  438. //=============================================================================
  439. void CGdipButton::OnMouseMove(UINT nFlags, CPoint point)
  440. //=============================================================================
  441. {
  442. if (!m_bIsTracking)
  443. {
  444. TRACKMOUSEEVENT tme;
  445. tme.cbSize = sizeof(tme);
  446. tme.hwndTrack = m_hWnd;
  447. tme.dwFlags = TME_LEAVE|TME_HOVER;
  448. tme.dwHoverTime = 1;
  449. m_bIsTracking = _TrackMouseEvent(&tme);
  450. }
  451. CButton::OnMouseMove(nFlags, point);
  452. }
  453. //=============================================================================
  454. //
  455. // Call this member function with a memory DC from the code that paints
  456. // the parents background. Passing the screen DC defeats the purpose of
  457. // using this function.
  458. //
  459. //=============================================================================
  460. void CGdipButton::SetBkGnd(CDC* pDC)
  461. {
  462. CRect rect, rectS;
  463. CBitmap bmp, *pOldBitmap;
  464. GetClientRect(rect);
  465. GetWindowRect(rectS);
  466. GetParent()->ScreenToClient(rectS);
  467. m_dcBk.DeleteDC();
  468. m_dcBk.CreateCompatibleDC(pDC);
  469. bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  470. pOldBitmap = m_dcBk.SelectObject(&bmp);
  471. m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, rectS.left, rectS.top, SRCCOPY);
  472. bmp.DeleteObject();
  473. }
  474. //=============================================================================
  475. // Set the tooltip with a string resource
  476. //=============================================================================
  477. void CGdipButton::SetToolTipText(UINT nId, BOOL bActivate)
  478. {
  479. // load string resource
  480. m_tooltext.LoadString(nId);
  481. // If string resource is not empty
  482. if (m_tooltext.IsEmpty() == FALSE)
  483. {
  484. SetToolTipText(m_tooltext, bActivate);
  485. }
  486. }
  487. //=============================================================================
  488. // Set the tooltip with a CString
  489. //=============================================================================
  490. void CGdipButton::SetToolTipText(CString spText, BOOL bActivate)
  491. {
  492. // We cannot accept NULL pointer
  493. if (spText.IsEmpty()) return;
  494. // Initialize ToolTip
  495. InitToolTip();
  496. m_tooltext = spText;
  497. // If there is no tooltip defined then add it
  498. if (m_pToolTip->GetToolCount() == 0)
  499. {
  500. CRect rectBtn;
  501. GetClientRect(rectBtn);
  502. m_pToolTip->AddTool(this, m_tooltext, rectBtn, 1);
  503. }
  504. // Set text for tooltip
  505. m_pToolTip->UpdateTipText(m_tooltext, this, 1);
  506. m_pToolTip->SetDelayTime(2000);
  507. m_pToolTip->Activate(bActivate);
  508. }
  509. //=============================================================================
  510. void CGdipButton::InitToolTip()
  511. //=============================================================================
  512. {
  513. if (m_pToolTip == NULL)
  514. {
  515. m_pToolTip = new CToolTipCtrl;
  516. // Create ToolTip control
  517. m_pToolTip->Create(this);
  518. m_pToolTip->Activate(TRUE);
  519. }
  520. }
  521. //=============================================================================
  522. void CGdipButton::DeleteToolTip()
  523. //=============================================================================
  524. {
  525. // Destroy Tooltip incase the size of the button has changed.
  526. if (m_pToolTip != NULL)
  527. {
  528. delete m_pToolTip;
  529. m_pToolTip = NULL;
  530. }
  531. }