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

钩子与API截获

开发平台:

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. }
  112. ////////////
  113. // We don't handle it: pass to base class
  114. //
  115. return CCaption::WindowProc(msg, wp, lp);
  116. }
  117. /////////////
  118. // Paint wrapped header text in rectangle
  119. //
  120. void CMultiLineCaption::PaintText(CDC* pDC)
  121. {
  122. ASSERT(m_pWndHooked);
  123. // For minimised windows, forget all the multi-line stuff
  124. if (m_pWndHooked->IsIconic())
  125. {
  126. CCaption::PaintText(pDC);
  127. return;
  128. }
  129. pDC->SetBkMode(TRANSPARENT); // draw on top of our background
  130. // Get window text to paint
  131. //
  132. CString text;
  133. m_pWndHooked->GetWindowText(text);
  134. // Set text color into device context
  135. //
  136. COLORREF textColor = GetTextColor(m_bActive);
  137. pDC->SetTextColor(textColor);
  138. // Get the rect to paint the text in
  139. //
  140. CRect paintRect(CalcTextRect(m_MaxLines > 1 ? TextFormat : SingleLineTextFormat));
  141. paintRect.top -= DrawTextFudgeFactor; // ? offset by fudge factor...
  142. // Get caption font and select into DC  
  143. //
  144. CFont* pOldFont = pDC->SelectObject(GetFont(m_bActive));
  145. // Paint text into rectangle
  146. //
  147. if (m_MaxLines > 1)
  148. {
  149. int height = pDC->DrawText(text, paintRect, TextFormat|DT_TOP);
  150. // Unfortunately, DT_END_ELLIPSIS doesn't work with DT_WORDBREAK, so we must handle
  151. // ellipsis manually...  (unless you know better)
  152. //
  153. if (height > paintRect.Height()) // Text won't fit, so output truncation ellipsis
  154. {
  155. int lenEllipsis = pDC->GetTextExtent(ellipsis).cx;
  156. paintRect.left = paintRect.right;
  157. paintRect.right += lenEllipsis;
  158. paintRect.top = paintRect.bottom - (GetLineHeight(pDC) + DrawTextFudgeFactor);
  159. pDC->DrawText(ellipsis, paintRect, DT_SINGLELINE|DT_LEFT); 
  160. }
  161. }
  162. else // m_MaxLines == 1
  163. {
  164. pDC->DrawText(text, paintRect, SingleLineTextFormat);
  165. }
  166. // Restore DC
  167. //
  168. pDC->SelectObject(pOldFont);
  169. }
  170. //////////////
  171. // Refresh caption when window is tiled
  172. //
  173. void CMultiLineCaption::OnWindowPosChanged( WINDOWPOS* lpwndpos )
  174. {
  175. Default(); // Let Windows handle it
  176. // These flags together seem unique to windows being tiled
  177. //
  178. UINT tileFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS;
  179. if ( ((lpwndpos->flags & tileFlags) == tileFlags) ||
  180.    lpwndpos->flags == RestoreFromMinimized) // Window restored from minimized
  181. {
  182. Refresh();
  183. }
  184. }
  185. void CMultiLineCaption::OnGetMinMaxInfo( MINMAXINFO FAR* lpMMI )
  186. {
  187. Default(); // Let Windows handle it
  188. // Ensure min height is correct - we mustn't allow a height less 
  189. // than the caption height
  190. //
  191. lpMMI->ptMinTrackSize.y = GetCaptionHeight() + GetFrameSize().cy;
  192. }
  193. void CMultiLineCaption::OnShowWindow( BOOL bShow, UINT nStatus )
  194. {
  195. // Refresh before showing window the first time
  196. //
  197. if (m_InitialShow)
  198. {
  199. m_InitialShow = false;
  200. Refresh();
  201. }
  202. Default(); // Let Windows handle it
  203. }
  204. void CMultiLineCaption::OnSetText(LPCTSTR lpText)
  205. {
  206. ASSERT_VALID(m_pWndHooked);
  207. CWnd& wnd = *m_pWndHooked;
  208. CString text;
  209. wnd.GetWindowText(text);
  210. // Turn WS_VISIBLE style off before calling Windows to
  211. // set the text, then turn it back on again after.
  212. //
  213. {
  214. SuppressStyle ss(wnd.GetSafeHwnd(), WS_VISIBLE);
  215. Default(); // Let Windows handle it
  216. }
  217. ////////////
  218. // Refresh caption size if new text
  219. //
  220. if (text != CString(lpText))
  221. Refresh();
  222. }
  223. ////////////////
  224. // Refresh on activate/deactivate for windows with different font
  225. // sizes for active and inactive caption
  226. //
  227. void CMultiLineCaption::OnMDIActivate()
  228. {
  229. Refresh();
  230. Default(); // Let Windows handle it
  231. }
  232. // Call Windows to do WM_NCCALCSIZE then set the top of the new client area to allow for 
  233. // multi-line caption
  234. //
  235. void CMultiLineCaption::OnNcCalcSize( BOOL bCalcValidRects, LPNCCALCSIZE_PARAMS lpncsp )
  236. {
  237. Default(); // Let Windows handle it
  238. CString text;
  239. ASSERT(m_pWndHooked);
  240. m_pWndHooked->GetWindowText(text);
  241. // When restoring from minimised (old client area left and right are both zero), CalcTextRect()
  242. // won't calculate the new client area correctly because the current client area is not valid.
  243. // In this case, skip it and let OnSize() trigger this function when the client area is valid.
  244. //
  245. BOOL wasIconic = lpncsp->rgrc[2].left == lpncsp->rgrc[2].right;
  246. BOOL isZoomed = m_pWndHooked->IsZoomed();
  247. if (text.GetLength() > 0 && !isZoomed && !wasIconic)
  248. {
  249. // LPNCCALCSIZE_PARAMS are relative to parent client area (or screen origin if no parent),
  250. // so add vertical offset of child frame into parent client area 
  251. //
  252. lpncsp->rgrc[0].top =  GetChildOffset() + GetCaptionHeight();
  253. }
  254. ///////////////
  255. // Return the rectangle occupied by caption text with the specified DrawText() format
  256. //
  257. CRect CMultiLineCaption::CalcTextRect(int textFormat)
  258. {
  259. CString text;
  260. ASSERT(m_pWndHooked);
  261. m_pWndHooked->GetWindowText(text);
  262. // Get caption font and select into window DC
  263. //
  264. CWindowDC dc(m_pWndHooked);
  265. CFont* pOldFont = dc.SelectObject(GetFont(m_bActive));
  266. // Size caption to exclude buttons & icon
  267. //
  268. CRect textRect = GetCaptionRect();
  269. textRect.left  += GetIconWidth(); // start after icon
  270. textRect.right -= GetButtonsWidth() + 4; // don't draw past buttons
  271. // Use DrawText to calculate the height necessary to contain the text
  272. //
  273. int width = textRect.Width();
  274. int height = dc.DrawText(text, textRect, textFormat | DT_CALCRECT | DT_EXTERNALLEADING);
  275. // Restrict to specified maximun height
  276. //
  277. int maxHeight = GetLineHeight(&dc) * m_MaxLines;
  278. if (height > maxHeight)
  279. textRect.bottom = textRect.top + maxHeight;
  280. // DrawText word-wrap fails to clip for very narrow rects, so ignore if necessary
  281. //
  282. if (textRect.Width() > width)
  283. textRect.right = textRect.left + width;
  284. // Restore DC
  285. //
  286. dc.SelectObject(pOldFont);
  287. return textRect;
  288. }
  289. ///////////
  290. // Returns the height of a line of text in the specified device context
  291. //
  292. LONG CMultiLineCaption::GetLineHeight(CDC* pDC)
  293. {
  294.     TEXTMETRIC textMetric;
  295.     pDC->GetOutputTextMetrics(&textMetric);
  296.     return textMetric.tmHeight;
  297. }
  298. ///////////
  299. // Returns the total caption height needed to contain the text
  300. //
  301. int CMultiLineCaption::GetCaptionHeight()
  302. {
  303. return GetFrameSize().cy + 
  304. CalcTextRect(m_MaxLines > 1 ? TextFormat : SingleLineTextFormat).bottom + 
  305. TextLowerBorder;
  306. }
  307. ///////////
  308. // Return vertical offset of child frame into parent client area (for OnNcCalcSize).
  309. //
  310. int CMultiLineCaption::GetChildOffset()
  311. {
  312. CRect parentRect(0,0,0,0);
  313. ASSERT(m_pWndHooked);
  314. CWnd* pParent = m_pWndHooked->GetParent();
  315. if (pParent)
  316. {
  317. pParent->GetClientRect(parentRect );
  318. pParent->ClientToScreen(parentRect);
  319. }
  320. CRect hookedRect;
  321. m_pWndHooked->GetWindowRect( &hookedRect );
  322. return hookedRect.top - parentRect.top;
  323. }
  324. /////////////
  325. // Override of base class function:
  326. // Return caption drawing rectangle, the lower edge abutting the
  327. // client area (as adjusted in OnNcCalcSize())
  328. //
  329. CRect CMultiLineCaption::GetCaptionRect()
  330. {
  331. ASSERT(m_pWndHooked);
  332. CWnd& wnd = *m_pWndHooked;
  333. // When minimised, draw normal size caption
  334. if (wnd.IsIconic())
  335. return CCaption::GetCaptionRect();
  336. // Get client rect in screen co-ordinates
  337. //
  338. CRect captionRect;
  339. wnd.GetClientRect(captionRect);
  340. wnd.ClientToScreen(captionRect);
  341. // Convert client rect to window co-ordinates
  342. //
  343. CRect windowRect;
  344. wnd.GetWindowRect(windowRect);
  345. captionRect -= windowRect.TopLeft();
  346. // Set top and botton for caption
  347. //
  348. captionRect.bottom = captionRect.top; // bottom of caption is top of client window
  349. captionRect.top = GetFrameSize().cy; // top of caption is window rect top + frame thickness
  350. return captionRect;
  351. }
  352. ///////////
  353. // Ensure caption size is recalculated by triggering an OnNcCalcSize()
  354. //
  355. void CMultiLineCaption::Refresh()
  356. {
  357. // Trigger an OnNcCalcSize() to resize caption
  358. //
  359. Invalidate();
  360. if (m_pWndHooked)
  361. m_pWndHooked->SetWindowPos(&CWnd::wndTopMost, 0,0,0,0, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE);
  362. }