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

对话框与窗口

开发平台:

Visual C++

  1. ////////////////////////////////////////////////////////////////
  2. //
  3. // class CMultiLineCaption
  4. //
  5. // Multi-line auto-wrap caption painter. 
  6. //
  7. // To use:
  8. // - call Install from your frame's OnCreate function. 
  9. // - Set a custom CaptionBackground if desired
  10. // - Set custom TextAttributes if required
  11. //
  12. //   If you are drawing custom caption buttons, you must handle WM_NCLBUTTONDOWN & co.
  13. //   yourself. CMultiLineCaption does not handle the mouse for custom caption buttons. 
  14. //
  15. // Author: Dave Lorde (dlorde@cix.compulink.co.uk)
  16. //
  17. //          Copyright 1999
  18. //
  19. ////////////////////////////////////////////////////////////////
  20. //
  21. #include "StdAfx.h"
  22. #include "MultiLineCaption.h"
  23. #include "CaptionBackground.h"
  24. #include "CaptionTextAttributes.h"
  25. #include "SuppressStyle.h"
  26. #include "AutoSelector.h"
  27. #include <algorithm>
  28. #ifdef _DEBUG
  29. #define new DEBUG_NEW
  30. #undef THIS_FILE
  31. static char THIS_FILE[] = __FILE__;
  32. #endif
  33. using std::_cpp_max;
  34. using std::_cpp_min;
  35. IMPLEMENT_DYNAMIC(CMultiLineCaption, CCaption);    
  36. namespace
  37. {
  38. const int TextFormat = DT_CENTER|DT_WORDBREAK;
  39. const int SingleLineTextFormat = DT_LEFT|DT_END_ELLIPSIS;
  40. const int DrawTextFudgeFactor = 2;
  41. const int TextLowerBorder = 2;
  42. const UINT RestoreFromMinimized = 0x8124;   // WindowPosChanged flags
  43. const CString ellipsis("...");
  44. }
  45. ///////////////
  46. // Constructor
  47. //
  48. CMultiLineCaption::CMultiLineCaption(int maxLines)
  49. :m_MaxLines(maxLines > 0 ? maxLines : 1), 
  50. m_InitialShow(true), 
  51. m_ActiveSysColor(GetSysColor(COLOR_ACTIVECAPTION)),
  52. m_InactiveSysColor(GetSysColor(COLOR_INACTIVECAPTION))
  53. {
  54. }
  55. //////////
  56. // Adjust maximum lines for title wrapping, and refresh
  57. //
  58. void CMultiLineCaption::SetMaxLines(int maxLines)
  59. {
  60. m_MaxLines = maxLines > 0 ? maxLines : 1;
  61. AdjustHeight();
  62. Repaint(); // Redraw caption
  63. }
  64. ////////////////
  65. // Return maximum permitted lines for title
  66. //
  67. int CMultiLineCaption::GetMaxLines() const
  68. {
  69. return m_MaxLines;
  70. }
  71. //////////////////
  72. // Message handler handles caption size-related messages
  73. //
  74. LRESULT CMultiLineCaption::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
  75. {
  76. switch (msg) 
  77. {
  78. case WM_NCACTIVATE:
  79. return OnNcActivate(wp);
  80. ///////////
  81. //  Adjust height of client area according to text wrapping
  82. //
  83. case WM_SETTEXT:
  84. OnSetText((LPCTSTR)lp);
  85. return 0;
  86. ///////////
  87. // Change colours if using system colours and SysColorChange
  88. case WM_NCPAINT:
  89. OnNcPaint(HRGN(wp));
  90. return 0;
  91. ///////////
  92. // Adjust height of client area according to required caption height
  93. //
  94. case WM_NCCALCSIZE:
  95. return OnNcCalcSize((BOOL)wp, (LPNCCALCSIZE_PARAMS)lp);
  96. ////////////
  97. // Ensure caption repaints smoothly when sized
  98. //
  99. case WM_SIZE:
  100. OnSize( (UINT)wp, LOWORD(lp), HIWORD(lp) );
  101. return 0;
  102. ///////////
  103. // Prevent multi-line caption being overdrawn by client area
  104. //
  105. case WM_GETMINMAXINFO:
  106. OnGetMinMaxInfo((LPMINMAXINFO)lp);
  107. return 0;
  108. ///////////
  109. // Refresh when activated or inactivated
  110. //
  111. case WM_MDIACTIVATE:
  112. OnMDIActivate();
  113. return 0;
  114. ///////////
  115. // Refresh when sizing stopped
  116. // 
  117. case WM_EXITSIZEMOVE:
  118. OnExitSizeMove();
  119. return 0;
  120. ///////////
  121. // Refresh caption when window is tiled 
  122. //
  123. case WM_WINDOWPOSCHANGED:
  124. OnWindowPosChanged( (WINDOWPOS*) lp);
  125. return 0;
  126. }
  127. ////////////
  128. // We don't handle it: pass to base class
  129. //
  130. return CCaption::WindowProc(msg, wp, lp);
  131. }
  132. //////////////////
  133. // Handle WM_NCACTIVATE for main window
  134. //
  135. BOOL CMultiLineCaption::OnNcActivate(BOOL bActive)
  136. {
  137. BOOL result = CCaption::OnNcActivate(bActive);
  138. Repaint(); // Redraw caption
  139. return result;
  140. }
  141. void CMultiLineCaption::OnSetText(LPCTSTR lpStr)
  142. {
  143. CCaption::OnSetText(lpStr);
  144. AdjustHeight();
  145. Repaint(); // Redraw caption
  146. }
  147. void CMultiLineCaption::OnNcPaint(HRGN hRgn)
  148. {
  149. if (GetBackground()->IsUsingSystemColors())
  150. {
  151. if (m_ActiveSysColor != GetSysColor(COLOR_ACTIVECAPTION) ||
  152. m_InactiveSysColor != GetSysColor(COLOR_INACTIVECAPTION))
  153. {
  154. m_ActiveSysColor = GetSysColor(COLOR_ACTIVECAPTION);
  155. m_InactiveSysColor = GetSysColor(COLOR_INACTIVECAPTION);
  156. Repaint();
  157. }
  158. }
  159. CCaption::OnNcPaint(hRgn);
  160. }
  161. void CMultiLineCaption::OnSize( UINT nType, int cx, int cy )
  162. {
  163. Default();
  164. Refresh();
  165. }
  166. void CMultiLineCaption::OnExitSizeMove()
  167. {
  168. Default();
  169. Refresh();
  170. }
  171. /////////////
  172. // Paint wrapped header text in rectangle
  173. //
  174. void CMultiLineCaption::PaintText(CDC* pDC)
  175. {
  176. ASSERT(m_pWndHooked);
  177. ASSERT(m_pWndHooked);
  178. CWnd& wnd = *m_pWndHooked;
  179. // For minimised windows, forget all the multi-line stuff
  180. if (wnd.IsIconic())
  181. {
  182. CCaption::PaintText(pDC);
  183. return;
  184. }
  185. pDC->SetBkMode(TRANSPARENT); // draw on top of our background
  186. // Get window text to paint
  187. //
  188. CString text;
  189. wnd.GetWindowText(text);
  190. // Set text color into device context
  191. //
  192. COLORREF textColor = GetTextColor(m_bActive);
  193. pDC->SetTextColor(textColor);
  194. // Get the rect to paint the text in
  195. //
  196. CRect paintRect(CalcTextRect(m_MaxLines > 1 ? TextFormat : SingleLineTextFormat));
  197. // Get caption font and select into DC  
  198. //
  199. AutoSelector a(pDC, GetFont(m_bActive));
  200. // Paint text into rectangle
  201. //
  202. if (m_MaxLines > 1)
  203. {
  204. int height = pDC->DrawText(text, paintRect, TextFormat|DT_TOP);
  205. // Unfortunately, DT_END_ELLIPSIS doesn't work with DT_WORDBREAK, so we must handle
  206. // ellipsis manually...  (unless you know better)
  207. //
  208. if (height > paintRect.Height()) // Text won't fit, so output truncation ellipsis
  209. {
  210. int lenEllipsis = pDC->GetTextExtent(ellipsis).cx;
  211. paintRect.left = paintRect.right;
  212. paintRect.right += lenEllipsis;
  213. paintRect.top = paintRect.bottom - (GetLineHeight(pDC) + 2);
  214. pDC->DrawText(ellipsis, paintRect, DT_SINGLELINE|DT_LEFT); 
  215. }
  216. }
  217. else // m_MaxLines == 1
  218. {
  219. pDC->DrawText(text, paintRect, SingleLineTextFormat);
  220. }
  221. }
  222. //////////////
  223. // Refresh caption when window is tiled
  224. //
  225. void CMultiLineCaption::OnWindowPosChanged( WINDOWPOS* lpwndpos )
  226. {
  227. Default(); // Let Windows handle it
  228. // These flags together seem unique to windows being tiled
  229. //
  230. UINT tileFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS;
  231. if ((lpwndpos->flags & tileFlags) == tileFlags)
  232. {
  233. Refresh();
  234. }
  235. }
  236. void CMultiLineCaption::OnGetMinMaxInfo( MINMAXINFO FAR* lpMMI )
  237. {
  238. Default(); // Let Windows handle it
  239. // Ensure min height is correct - we mustn't allow a height less 
  240. // than the caption height
  241. //
  242. lpMMI->ptMinTrackSize.y = 
  243. max(lpMMI->ptMinTrackSize.y, GetCaptionHeight() + GetFrameSize().cy);
  244. }
  245. ////////////////
  246. // Refresh on activate/deactivate for windows with different font
  247. // sizes for active and inactive caption
  248. //
  249. void CMultiLineCaption::OnMDIActivate()
  250. {
  251. AdjustHeight();
  252. Repaint(); // Redraw caption
  253. Default(); // Let Windows handle it
  254. }
  255. // Call Windows to do WM_NCCALCSIZE then set the top of the new client area to allow for 
  256. // multi-line caption
  257. //
  258. LRESULT CMultiLineCaption::OnNcCalcSize( BOOL bCalcValidRects, LPNCCALCSIZE_PARAMS lpncsp )
  259. {
  260. LRESULT res = Default(); // Let Windows handle it
  261. CString text;
  262. ASSERT(m_pWndHooked);
  263. CWnd& wnd = *m_pWndHooked;
  264. wnd.GetWindowText(text);
  265. // When restoring from minimised (old client area left and right are both zero), CalcTextRect()
  266. // won't calculate the new client area correctly because the current client area is not valid.
  267. // In this case, skip it and let OnSize() trigger this function when the client area is valid.
  268. //
  269. BOOL wasIconic = lpncsp->rgrc[2].left == lpncsp->rgrc[2].right;
  270. BOOL isZoomed = wnd.IsZoomed();
  271. if (text.GetLength() > 0 && !isZoomed && !wasIconic)
  272. {
  273. // LPNCCALCSIZE_PARAMS are relative to parent client area (or screen origin if no parent),
  274. // so add vertical offset of child frame into parent client area 
  275. //
  276. lpncsp->rgrc[0].top =  GetChildOffset() + GetCaptionHeight();
  277. }
  278. return res;
  279. ///////////////
  280. // Return the rectangle occupied by caption text with the specified DrawText() format
  281. //
  282. CRect CMultiLineCaption::CalcTextRect(int textFormat)
  283. {
  284. CString text;
  285. ASSERT(m_pWndHooked);
  286. m_pWndHooked->GetWindowText(text);
  287. // Get caption font and select into window DC
  288. //
  289. CWindowDC dc(m_pWndHooked);
  290. AutoSelector a(&dc, GetFont(m_bActive));
  291. // Size caption to exclude buttons & icon
  292. //
  293. CRect textRect = GetCaptionRect();
  294. textRect.left  += GetIconWidth(); // start text after icon
  295. textRect.right -= GetButtonsWidth() + 4; // end text before buttons
  296. // Use DrawText to calculate the height necessary to contain the text
  297. //
  298. int width = textRect.Width();
  299. int height = dc.DrawText(text, textRect, textFormat | DT_CALCRECT | DT_EXTERNALLEADING);
  300. // Restrict to specified maximun height
  301. //
  302. int maxHeight = GetLineHeight(&dc) * m_MaxLines;
  303. if (height > maxHeight)
  304. textRect.bottom = textRect.top + maxHeight;
  305. // Restrict to minimum height
  306. //
  307. int minTextHeight = GetSystemMetrics(SM_CYCAPTION) - 6; 
  308. if (textRect.Height() < minTextHeight)
  309. textRect.bottom = textRect.top + minTextHeight;
  310. // I'll work out why I need this when I get more time... :-)
  311.   textRect.top -= DrawTextFudgeFactor;
  312. // DrawText word-wrap fails to clip for very narrow rects, so ignore if necessary
  313. //
  314. if (textRect.Width() > width)
  315. textRect.right = textRect.left + width;
  316. return textRect;
  317. }
  318. ///////////
  319. // Returns the height of a line of text in the specified device context
  320. //
  321. LONG CMultiLineCaption::GetLineHeight(CDC* pDC)
  322. {
  323.     TEXTMETRIC textMetric;
  324.     pDC->GetOutputTextMetrics(&textMetric);
  325.     return textMetric.tmHeight;
  326. }
  327. ///////////
  328. // Returns the total caption height needed to contain the text
  329. //
  330. int CMultiLineCaption::GetCaptionHeight()
  331. {
  332. return GetFrameSize().cy + 
  333. CalcTextRect(m_MaxLines > 1 ? TextFormat : SingleLineTextFormat).bottom + 
  334. TextLowerBorder;
  335. }
  336. ///////////
  337. // Return vertical offset of child frame into parent client area (for OnNcCalcSize).
  338. //
  339. int CMultiLineCaption::GetChildOffset()
  340. {
  341. CRect parentRect(0,0,0,0);
  342. ASSERT(m_pWndHooked);
  343. CWnd& wnd = *m_pWndHooked;
  344. CWnd* pParent = wnd.GetParent();
  345. if (pParent)
  346. {
  347. pParent->GetClientRect(parentRect );
  348. pParent->ClientToScreen(parentRect);
  349. }
  350. CRect hookedRect;
  351. wnd.GetWindowRect( &hookedRect );
  352. return hookedRect.top - parentRect.top;
  353. }
  354. /////////////
  355. // Override of base class function:
  356. // Return caption drawing rectangle, the lower edge abutting the
  357. // client area (as adjusted in OnNcCalcSize())
  358. //
  359. CRect CMultiLineCaption::GetCaptionRect()
  360. {
  361. ASSERT(m_pWndHooked);
  362. CWnd& wnd = *m_pWndHooked;
  363. // When minimised, draw normal size caption
  364. if (wnd.IsIconic())
  365. return CCaption::GetCaptionRect();
  366. // Get client rect in screen co-ordinates
  367. //
  368. CRect captionRect;
  369. wnd.GetClientRect(captionRect);
  370. wnd.ClientToScreen(captionRect);
  371. // Convert client rect to window co-ordinates
  372. //
  373. CRect windowRect;
  374. wnd.GetWindowRect(windowRect);
  375. captionRect -= windowRect.TopLeft();
  376. // Set top and botton for caption
  377. //
  378. captionRect.bottom = captionRect.top; // bottom of caption is top of client window
  379. captionRect.top = GetFrameSize().cy; // top of caption is window rect top + frame thickness
  380. return captionRect;
  381. }
  382. ///////////
  383. // Ensure caption size is recalculated by triggering an OnNcCalcSize()
  384. //
  385. void CMultiLineCaption::Repaint()
  386. {
  387. ASSERT(m_pWndHooked);
  388. CWnd& wnd = *m_pWndHooked;
  389. // Trigger an OnNcCalcSize() to resize caption
  390. //
  391. Invalidate();
  392. wnd.SetWindowPos(&CWnd::wndTopMost, 0,0,0,0, 
  393. SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE);
  394. }
  395. ///////////
  396. // Redraw caption and client area, sizing window as required
  397. void CMultiLineCaption::Refresh()
  398. {
  399. AdjustHeight();  
  400. Repaint(); // Redraw caption
  401. ASSERT(m_pWndHooked);
  402. CWnd& wnd = *m_pWndHooked;
  403. wnd.Invalidate(); // paint client area
  404. UpdateWindow(wnd.GetSafeHwnd());
  405. }
  406. void CMultiLineCaption::AdjustHeight()
  407. {
  408. // Size window to caption if caption is taller
  409. //
  410. ASSERT(m_pWndHooked);
  411. CWnd& wnd = *m_pWndHooked;
  412. CRect wndRect;
  413. wnd.GetWindowRect(wndRect);
  414. if (wndRect.Height() < GetCaptionHeight() + GetFrameSize().cy)
  415. {
  416. wnd.SetWindowPos(&CWnd::wndTopMost, 0,0,wndRect.Width(),
  417.                  GetCaptionHeight() + GetFrameSize().cy, 
  418.  SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE);
  419. }
  420. }