Caption.cpp
上传用户:zhoushen
上传日期:2022-06-15
资源大小:84k
文件大小:15k
源码类别:

对话框与窗口

开发平台:

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