Caption.cpp
上传用户:kssdz899
上传日期:2007-01-08
资源大小:79k
文件大小:15k
源码类别:

钩子与API截获

开发平台:

Visual C++

  1. ////////////////////////////////////////////////////////////////
  2. //
  3. // class CCaption
  4. //
  5. // Generic caption painter. Handles WM_NCPAINT, WM_NCACTIVATE, etc. to
  6. // handle drawing custom captions. To use it:
  7. //
  8. // - call Install from your frame's OnCreate function. 
  9. // - Set a custom CaptionBackground if desired
  10. // - Set custom TextAttributes if required
  11. //
  12. //  Derive from this class for custom caption layouts.
  13. // 
  14. //   If you are drawing custom caption buttons, you must handle WM_NCLBUTTONDOWN & co.
  15. //   yourself. CCaption does not handle the mouse for custom caption buttons. 
  16. //
  17. // Author: Dave Lorde (dlorde@cix.compulink.co.uk)
  18. //
  19. //          Copyright 1999
  20. //
  21. // - loosely based on a 1997 Microsoft Systems Journal
  22. //            C++ Q&A article by Paul DiLascia. 
  23. //
  24. ////////////////////////////////////////////////////////////////
  25. //
  26. #include "StdAfx.h"
  27. #include "Caption.h"
  28. #include "CaptionBackground.h"
  29. #include "CaptionTextAttributes.h"
  30. #include "SuppressStyle.h"
  31. #include <algorithm>
  32. using std::_cpp_max;
  33. using std::_cpp_min;
  34. #ifdef _DEBUG
  35. #define new DEBUG_NEW
  36. #undef THIS_FILE
  37. static char THIS_FILE[] = __FILE__;
  38. #endif
  39. const int MinLuminosity = 90;    // good from trial & error
  40. const COLORREF ColorWhite = RGB(255,255,255);
  41. IMPLEMENT_DYNAMIC(CCaption, CSubclassWnd);
  42. ////////
  43. // Default Constructor 
  44. //
  45. CCaption::CCaption()
  46. :m_pBackground(0), m_pTextAttributes(0)
  47. {
  48. Invalidate();
  49. }
  50. CCaption::~CCaption()
  51. {
  52. delete m_pBackground, m_pBackground = 0;
  53. delete m_pTextAttributes, m_pTextAttributes = 0;
  54. }
  55. //////////////////
  56. // Install caption handler. 
  57. //
  58. BOOL CCaption::Install(CFrameWnd* pFrameWnd)
  59. {
  60. ASSERT_KINDOF(CFrameWnd, pFrameWnd);
  61. return HookWindow(pFrameWnd);
  62. }
  63. //////////////////
  64. // Replace default background painter. 
  65. //
  66. void CCaption::SetBackground(CCaptionBackground* pBackground)
  67. {
  68. if (m_pBackground)
  69. delete m_pBackground;
  70. m_pBackground = pBackground;
  71. Refresh();
  72. }
  73. //////////////////
  74. // Replace default text attributes
  75. //
  76. void CCaption::SetTextAttributes(CCaptionTextAttributes* pTextAttributes)
  77. {  
  78. if (m_pTextAttributes)
  79. delete m_pTextAttributes;
  80. m_pTextAttributes = pTextAttributes;
  81. Refresh();
  82. }
  83. /////////////////
  84. // Regenerate and display caption
  85. //
  86. void CCaption::Refresh()
  87. {
  88. Invalidate();
  89. PaintCaption();
  90. }
  91. //////////////////
  92. // Ensure caption bitmaps are repainted next time through OnNcPaint()
  93. //
  94. void CCaption::Invalidate() 
  95. m_szCaption = CSize(0,0); 
  96. }
  97. //////////////////
  98. // Message handler handles caption-related messages
  99. //
  100. LRESULT CCaption::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
  101. {
  102. switch (msg) 
  103. {
  104. case WM_NCPAINT:
  105. OnNcPaint(HRGN(wp));
  106. return 0;
  107. case WM_NCACTIVATE:
  108. return OnNcActivate(wp);
  109. case WM_SETTEXT:
  110. OnSetText((LPCTSTR)lp);
  111. return 0;
  112. case WM_SYSCOLORCHANGE:
  113. case WM_SETTINGCHANGE:
  114. OnColorChange();
  115. return 0;
  116. // case WM_SIZE:
  117. // OnSize( (UINT)wp, HIWORD(lp), LOWORD(lp) );
  118. // return 0;
  119. }
  120. // We don't handle it: pass along
  121. return CSubclassWnd::WindowProc(msg, wp, lp);
  122. }
  123. /////////////////
  124. // Handle WM_NCPAINT for main window
  125. //
  126. void CCaption::OnNcPaint(HRGN hRgn)
  127. {
  128. ASSERT_VALID(m_pWndHooked);
  129. CWnd& wnd = *m_pWndHooked;
  130. CRect rc = GetCaptionRect(); // caption rectangle in window coords
  131. CRect rcWin;
  132. wnd.GetWindowRect(&rcWin); // .. get window rect
  133. rc += rcWin.TopLeft(); // convert caption rect to screen coords
  134. // Don't bother painting if the caption doesn't lie within the region.
  135. //
  136. if ((WORD)hRgn > 1 && !::RectInRegion(hRgn, &rc)) 
  137. {
  138. Default(); // just do default thing
  139. return; // and quit
  140. }
  141. // Exclude caption from update region
  142. //
  143. HRGN hRgnCaption = ::CreateRectRgnIndirect(&rc);
  144. HRGN hRgnNew     = ::CreateRectRgnIndirect(&rc);
  145. if ((WORD)hRgn > 1) 
  146. {
  147. // wParam is a valid region: subtract caption from it
  148. //
  149. ::CombineRgn(hRgnNew, hRgn, hRgnCaption, RGN_DIFF);
  150. else 
  151. {
  152. // wParam is not a valid region: create one that's the whole
  153. // window minus the caption bar
  154. //
  155. HRGN hRgnAll = ::CreateRectRgnIndirect(&rcWin);
  156. CombineRgn(hRgnNew, hRgnAll, hRgnCaption, RGN_DIFF);
  157. DeleteObject(hRgnAll);
  158. }
  159. // Call Windows to do WM_NCPAINT with altered update region
  160. //
  161. MSG& msg = AfxGetThreadState()->m_lastSentMsg;
  162. WPARAM savewp = msg.wParam; // save original wParam
  163. msg.wParam = (WPARAM)hRgnNew; // set new region for DefWindowProc
  164. Default(); // Normal message handling
  165. DeleteObject(hRgnCaption); // clean up
  166. DeleteObject(hRgnNew); // ...
  167. msg.wParam = savewp; // restore original wParam
  168. PaintCaption(); // Now paint our special caption
  169. }
  170. //////////////////
  171. // Handle WM_NCACTIVATE for main window
  172. //
  173. BOOL CCaption::OnNcActivate(BOOL bActive)
  174. {
  175. ASSERT_VALID(m_pWndHooked);
  176. CFrameWnd& frame = *((CFrameWnd*)m_pWndHooked);
  177. ASSERT_KINDOF(CFrameWnd, &frame);
  178. // Mimic MFC kludge to stay active if WF_STAYACTIVE bit is on
  179. //
  180. if (frame.m_nFlags & WF_STAYACTIVE)
  181. bActive = TRUE;
  182. if (!frame.IsWindowEnabled()) // but not if disabled
  183. bActive = FALSE;
  184. if (bActive == m_bActive)
  185. return TRUE; // nothing to do
  186. // In case this is a MDI app, manually activate/paint active MDI child
  187. // window, because Windows won't do it if parent frame is invisible.
  188. // Must do this BEFORE calling Default, or it will not work.
  189. //
  190. CFrameWnd* pActiveFrame = frame.GetActiveFrame();
  191. if (pActiveFrame != &frame) 
  192. {
  193. pActiveFrame->SendMessage(WM_NCACTIVATE, bActive);
  194. pActiveFrame->SendMessage(WM_NCPAINT);
  195. }
  196. // Turn WS_VISIBLE off before calling DefWindowProc,
  197. // so DefWindowProc won't paint and thereby cause flicker.
  198. //
  199. {
  200. SuppressStyle ss(frame.GetSafeHwnd(), WS_VISIBLE);
  201. MSG& msg = AfxGetThreadState()->m_lastSentMsg;
  202. msg.wParam = bActive;
  203. Default(); // Normal message handling
  204. }
  205. // At this point, nothing has happened (since WS_VISIBLE was off).
  206. // Now it's time to paint.
  207. //
  208. m_bActive = bActive; // update state
  209. frame.SendMessage(WM_NCPAINT); // paint non-client area (frame too)
  210. return TRUE; // done OK
  211. }
  212. //////////////////
  213. // Handle WM_SETTEXT for main window
  214. //
  215. void CCaption::OnSetText(LPCTSTR)
  216. {
  217. ASSERT_VALID(m_pWndHooked);
  218. CWnd& wnd = *m_pWndHooked;
  219. // Turn WS_VISIBLE style off before calling Windows to
  220. // set the text. Reset to visible afterwards
  221. {
  222. SuppressStyle ss(wnd.GetSafeHwnd(), WS_VISIBLE);
  223. Default(); // Normal message handling
  224. }
  225. // wnd.SendMessage(WM_NCPAINT); // paint non-client (frame)
  226. Refresh();
  227. }
  228. //////////
  229. // Ensure caption is repainted when system colors change
  230. //
  231. void CCaption::OnColorChange()
  232. {  
  233. Default();  // Normal message handling
  234. Refresh();
  235. }
  236. //////////////////
  237. // Paint custom caption. m_bActive flag tells whether frame is active or not. 
  238. // Just blast the bitmap to the title bar.
  239. //
  240. void CCaption::PaintCaption()
  241. {
  242. ASSERT(m_pWndHooked);
  243. CWnd& wnd = *m_pWndHooked;
  244. // Get caption DC and rectangle
  245. //
  246. CWindowDC dcWin(&wnd); // window DC
  247. CDC dc; // memory DC
  248. dc.CreateCompatibleDC(&dcWin); // ...create it
  249. CRect rc = GetCaptionRect(); // get caption rectangle
  250. if (rc.Size() != m_szCaption)  // if size changed:
  251. {
  252. m_bmCaption[0].DeleteObject(); // invalidate bitmaps
  253. m_bmCaption[1].DeleteObject(); // ...
  254. m_szCaption = rc.Size(); // update new size
  255. }
  256. // Get active/inactive bitmap & determine if needs to be regenerated
  257. //
  258. CBitmap& bm = m_bmCaption[m_bActive != 0]; // get bitmap
  259. BOOL bPaintIt = FALSE; // paint new bitmap?
  260. if (!HBITMAP(bm)) 
  261. { // no bitmap, so create one   :
  262. bm.CreateCompatibleBitmap(&dcWin, rc.Width(), rc.Height()); 
  263. bPaintIt = TRUE; // and paint it
  264. }
  265. CBitmap* pOldBitmap = dc.SelectObject(&bm); // select bitmap into memory DC
  266. // If bitmap needs painting, do it
  267. //
  268. if (bPaintIt) 
  269. PaintBitmap(&dc);
  270. // blast bits to screen
  271. //
  272. dcWin.BitBlt(rc.left, rc.top, rc.Width(), rc.Height(), &dc, 0, 0, SRCCOPY);
  273. dc.SelectObject(pOldBitmap); // restore DC
  274. }
  275. /////////
  276. // Paint the caption bitmap. Override for custom caption painters.
  277. // Note: SDK DrawCaption() method not used, so as to provide useful 
  278. // virtual functions for derived caption painters
  279. //
  280. void CCaption::PaintBitmap(CDC* pDC)
  281. {
  282. PaintBackground(pDC);
  283. PaintIcon(pDC);
  284. PaintButtons(pDC);
  285. PaintText(pDC);
  286. PaintLowerBorder(pDC);
  287. }
  288. ///////////
  289. // Delegate background painting to CCaptionBackground class
  290. //
  291. void CCaption::PaintBackground(CDC* pDC)
  292. {
  293. GetBackground()->Paint(pDC, m_szCaption, m_bActive);
  294. }
  295. ////////
  296. // Paint the border between the bottom of the caption rectangle and the 
  297. // shadow on the top of the client rectangle 
  298. //
  299. void CCaption::PaintLowerBorder(CDC* pDC)
  300. {
  301. int x = 0;
  302. int y = m_szCaption.cy - GetSystemMetrics(SM_CYBORDER);
  303. int h = m_szCaption.cy;
  304. int w = m_szCaption.cx;
  305. CCaptionBackground::PaintRect(pDC, x, y, w, h, GetSysColor(COLOR_3DFACE));
  306. }
  307. ////////
  308. // Calculate the caption text clipping rect
  309. //
  310. CRect CCaption::GetTextRect()
  311. {
  312. CRect textRect = GetCaptionRect();
  313. textRect.left += GetIconWidth();
  314. textRect.right -= GetButtonsWidth() + 4;
  315. textRect.top -= 2;
  316. return textRect;
  317. }
  318. ///////////
  319. // Draw the caption text onto the bitmap
  320. //
  321. void CCaption::PaintText(CDC* pDC)
  322. {
  323. pDC->SetBkMode(TRANSPARENT); // draw on top of our background
  324. CString text;
  325. ASSERT(m_pWndHooked);
  326. CWnd& wnd = *m_pWndHooked;
  327. wnd.GetWindowText(text);
  328. // Set text color
  329. COLORREF textColor = GetTextColor(m_bActive);
  330. pDC->SetTextColor(textColor);
  331. // Get caption font and select into DC
  332. CFont* pOldFont = pDC->SelectObject(GetFont(m_bActive));
  333. CRect textRect = GetTextRect();
  334. pDC->DrawText(text, textRect, DT_LEFT|DT_END_ELLIPSIS);
  335. // Restore DC
  336. pDC->SelectObject(pOldFont);
  337. }
  338. ////////////////
  339. // Draw caption icon if valid DC is provided. Returns effective width of icon.
  340. //
  341. int CCaption::PaintIcon(CDC* pDC)
  342. {
  343. ASSERT(m_pWndHooked);
  344. CWnd& wnd = *m_pWndHooked;
  345. ////////////
  346. // If there's no icon or system menu, don't draw one
  347. //
  348. if (!(wnd.GetStyle() & WS_SYSMENU))
  349. return 0;
  350. //////////////
  351. // Within the basic button rectangle, Windows 95 uses a 1 or 2 pixel border
  352. // Icon has 2 pixel border on left, 1 pixel on top/bottom, 0 right
  353. //
  354. int cxIcon = GetSystemMetrics(SM_CXSIZE);
  355. CRect rc(0, 0, cxIcon, GetSystemMetrics(SM_CYSIZE));
  356. rc.DeflateRect(0,1);
  357. rc.left += 2;
  358. if (pDC != 0)
  359. {
  360. DrawIconEx(pDC->m_hDC, rc.left, rc.top, 
  361. (HICON)GetClassLong(wnd.m_hWnd, GCL_HICONSM),
  362. rc.Width(), rc.Height(), 0, NULL, DI_NORMAL);
  363. }
  364. return cxIcon;
  365. }
  366. //////////////
  367. // Helper
  368. //
  369. int CCaption::GetIconWidth()
  370. {
  371. return PaintIcon();
  372. }
  373. ////////////////
  374. // Draw min, max/restore, close buttons.
  375. // Returns total width of buttons drawn.
  376. //
  377. int CCaption::PaintButtons(CDC* pDC)
  378. {
  379. ASSERT(m_pWndHooked);
  380. CWnd& wnd = *m_pWndHooked;
  381. DWORD dwStyle = wnd.GetStyle();
  382. if (!(dwStyle & WS_CAPTION))
  383. return 0;
  384. int cxIcon = GetSystemMetrics(SM_CXSIZE);
  385. int cyIcon = GetSystemMetrics(SM_CYSIZE);
  386. // Draw caption buttons. These are all drawn inside a rectangle
  387. // of dimensions SM_CXSIZE by SM_CYSIZE
  388. CRect captRect = GetCaptionRect();
  389. CRect rc(0, 0, cxIcon, cyIcon);
  390. rc += CPoint(captRect.Width() - cxIcon, 0); // move right
  391. //////////////
  392. // Close box has a 2 pixel border on all sides but left, which is zero
  393. //
  394. rc.DeflateRect(0,2);
  395. rc.right -= 2;
  396. if (pDC)
  397. pDC->DrawFrameControl(&rc, DFC_CAPTION, DFCS_CAPTIONCLOSE);
  398. //////////////
  399. // Max/restore button is like close box; just shift rectangle left
  400. // Also does help button, if any.
  401. //
  402. BOOL bMaxBox = dwStyle & WS_MAXIMIZEBOX;
  403. if (bMaxBox || (wnd.GetExStyle() & WS_EX_CONTEXTHELP)) 
  404. {
  405. rc -= CPoint(cxIcon, 0);
  406. if (pDC)
  407. pDC->DrawFrameControl(&rc, DFC_CAPTION,
  408. bMaxBox ? (wnd.IsZoomed() ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX) :
  409. DFCS_CAPTIONHELP);
  410. }
  411. ///////////////
  412. // Minimize button has 2 pixel border on all sides but right.
  413. //
  414. if (dwStyle & WS_MINIMIZEBOX) 
  415. {
  416. rc -= CPoint(cxIcon - 2,0);
  417. if (pDC)
  418. pDC->DrawFrameControl(&rc, DFC_CAPTION, 
  419. (wnd.IsIconic() ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMIN));
  420. }
  421. return captRect.Width() - (rc.left - 2);
  422. }
  423. int CCaption::GetButtonsWidth()
  424. {
  425. return PaintButtons();
  426. }
  427. ///////////////
  428. // Calculate the caption rectangle relative to the window frame
  429. //
  430. CRect CCaption::GetCaptionRect()
  431. {
  432. ASSERT(m_pWndHooked);
  433. CWnd& wnd = *m_pWndHooked;
  434. CRect captionRect;
  435. wnd.GetWindowRect(captionRect);
  436. captionRect -= CPoint(captionRect.left, captionRect.top); // shift origin to (0,0)
  437. CSize szFrame = GetFrameSize();
  438. captionRect.InflateRect(-szFrame.cx, -szFrame.cy);
  439. captionRect.bottom = captionRect.top + GetSystemMetrics(SM_CYCAPTION); // height of caption
  440. return captionRect;
  441. }
  442. ///////
  443. // Return width and height of window frame elements
  444. //
  445. CSize CCaption::GetFrameSize() const
  446. {
  447. ASSERT(m_pWndHooked);
  448. CWnd& wnd = *m_pWndHooked;
  449. ////////////
  450. // Get size of frame around window
  451. //
  452. DWORD dwStyle = wnd.GetStyle();
  453. CSize szFrame = (dwStyle & WS_THICKFRAME) ?
  454. CSize(GetSystemMetrics(SM_CXSIZEFRAME),
  455.    GetSystemMetrics(SM_CYSIZEFRAME)) :
  456. CSize(GetSystemMetrics(SM_CXFIXEDFRAME),
  457. GetSystemMetrics(SM_CYFIXEDFRAME));
  458. return szFrame;
  459. }
  460. COLORREF CCaption::GetTextColor(BOOL bActive)
  461. {
  462. COLORREF textColor = bActive ? GetTextAttributes()->GetActiveColor() :
  463.    GetTextAttributes()->GetInactiveColor();
  464. //////////////////
  465. // Uncomment these lines to automatically set the text colour to white
  466. // when the background is too dark
  467. //    
  468. // if (GetLuminosity(textColor) < MinLuminosity)
  469. // textColor = ColorWhite;
  470. return textColor;
  471. }
  472. ///////////////
  473. // Delegate to CCaptionTextAttributes for appropriate font
  474. //
  475. CFont* CCaption::GetFont(BOOL m_bActive)
  476. {
  477. ASSERT(m_pWndHooked);
  478. CWnd& wnd = *m_pWndHooked;
  479. if (!wnd.IsIconic())
  480. return m_bActive ? GetTextAttributes()->GetActiveFont() : GetTextAttributes()->GetInactiveFont();
  481. else
  482. return CCaptionTextAttributes::GetSystemFont();
  483. }
  484. ////////////
  485. // Lazy construction of text attributes object
  486. //
  487. CCaptionTextAttributes* CCaption::GetTextAttributes()
  488. {
  489. if (!m_pTextAttributes)
  490. m_pTextAttributes = new CCaptionTextAttributes();
  491. return m_pTextAttributes;
  492. }
  493. ////////////
  494. // Lazy construction of background object
  495. //
  496. CCaptionBackground* CCaption::GetBackground()
  497. {
  498. if (!m_pBackground)
  499. m_pBackground = new CCaptionBackground();
  500. return m_pBackground;
  501. }
  502. //////////////////
  503. // Helper function to compute the luminosity for an RGB color.
  504. // Measures how bright the color is. I use this so I can draw the caption
  505. // text using the user's chosen color, unless it's too dark. See MSDN for
  506. // definition of luminosity and how to compute it.
  507. //
  508. int CCaption::GetLuminosity(COLORREF color) const
  509. {
  510. const int HlsMax = 240; // This is what Display Properties uses
  511. const int RgbMax = 255; // max r/g/b value is 255
  512. int r = GetRValue(color);
  513. int g = GetGValue(color);
  514. int b = GetBValue(color);
  515. int rgbMax = _MAX( _MAX(r, g), b);
  516. int rgbMin = _MIN( _MIN(r, g), b);
  517. return (((rgbMax + rgbMin) * HlsMax) + RgbMax ) / (2 * RgbMax);
  518. }