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