Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

734 lines
19 KiB

  1. /*--------------------------------------------------------------------------*
  2. *
  3. * Microsoft Windows
  4. * Copyright (C) Microsoft Corporation, 1992 - 1999
  5. *
  6. * File: msgview.cpp
  7. *
  8. * Contents: Implementation file for CMessageView
  9. *
  10. * History: 28-Apr-99 jeffro Created
  11. *
  12. *--------------------------------------------------------------------------*/
  13. #include "stdafx.h"
  14. #include "msgview.h"
  15. #include "util.h"
  16. using std::_MAX;
  17. using std::_MIN;
  18. /*+-------------------------------------------------------------------------*
  19. * CMessageView::CMessageView
  20. *
  21. *
  22. *--------------------------------------------------------------------------*/
  23. CMessageView::CMessageView ()
  24. : m_hIcon (NULL),
  25. m_yScroll (0),
  26. m_yScrollMax (0),
  27. m_yScrollMin (0),
  28. m_cyPage (0),
  29. m_cyLine (0),
  30. m_sizeWindow (0, 0),
  31. m_sizeIcon (0, 0),
  32. m_sizeMargin (0, 0),
  33. m_nAccumulatedScrollDelta (0)
  34. {
  35. /*
  36. * can't be windowless
  37. */
  38. m_bWindowOnly = true;
  39. /*
  40. * get the system metrics we'll use
  41. */
  42. UpdateSystemMetrics();
  43. DEBUG_INCREMENT_INSTANCE_COUNTER(CMessageView);
  44. }
  45. /*+-------------------------------------------------------------------------*
  46. * CMessageView::~CMessageView
  47. *
  48. *
  49. *--------------------------------------------------------------------------*/
  50. CMessageView::~CMessageView ()
  51. {
  52. DEBUG_DECREMENT_INSTANCE_COUNTER(CMessageView);
  53. }
  54. /*+-------------------------------------------------------------------------*
  55. * CMessageView::OnCreate
  56. *
  57. * WM_CREATE handler for CMessageView.
  58. *--------------------------------------------------------------------------*/
  59. LRESULT CMessageView::OnCreate (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  60. {
  61. CreateFonts();
  62. RecalcLayout ();
  63. return (0);
  64. }
  65. /*+-------------------------------------------------------------------------*
  66. * CMessageView::OnDestroy
  67. *
  68. * WM_DESTROY handler for CMessageView.
  69. *--------------------------------------------------------------------------*/
  70. LRESULT CMessageView::OnDestroy (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  71. {
  72. DeleteFonts();
  73. return (0);
  74. }
  75. /*+-------------------------------------------------------------------------*
  76. * CMessageView::OnSize
  77. *
  78. * WM_SIZE handler for CMessageView.
  79. *--------------------------------------------------------------------------*/
  80. LRESULT CMessageView::OnSize (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  81. {
  82. /*
  83. * The transient appearance/disappearance of WS_VSCROLL makes the client
  84. * rect volatile and can mess up our calculations. We'll use the more
  85. * stable window rect instead.
  86. */
  87. WTL::CRect rectWindow;
  88. GetWindowRect (rectWindow);
  89. if (GetExStyle() & WS_EX_CLIENTEDGE)
  90. rectWindow.DeflateRect (GetSystemMetrics (SM_CXEDGE),
  91. GetSystemMetrics (SM_CYEDGE));
  92. WTL::CSize sizeWindow (rectWindow.Width(), rectWindow.Height());
  93. /*
  94. * if the overall size has changed, we have some work to do
  95. */
  96. if (m_sizeWindow != sizeWindow)
  97. {
  98. /*
  99. * load m_sizeWindow right away so scrollbar future calculations
  100. * will have the right values in the member variable
  101. */
  102. std::swap (m_sizeWindow, sizeWindow);
  103. /*
  104. * if the width has changed, we'll might need to recalculate
  105. * all of the text heights
  106. */
  107. if (m_sizeWindow.cx != sizeWindow.cx)
  108. RecalcLayout ();
  109. /*
  110. * if the height changed, there's scrollbar work
  111. */
  112. if (m_sizeWindow.cy != sizeWindow.cy)
  113. {
  114. int dy = m_sizeWindow.cy - sizeWindow.cy;
  115. /*
  116. * if the window's grown, we might need to scroll to keep the
  117. * bottom of our content glued to the bottom of the window
  118. */
  119. if ((dy > 0) && (m_yScroll > 0) &&
  120. ((m_yScroll + m_sizeWindow.cy) > GetOverallHeight()))
  121. ScrollToPosition (m_yScroll - dy);
  122. /*
  123. * otherwise, just update the scrollbar
  124. */
  125. else
  126. UpdateScrollSizes();
  127. }
  128. }
  129. return (0);
  130. }
  131. /*+-------------------------------------------------------------------------*
  132. * CMessageView::OnSettingChange
  133. *
  134. * WM_SETTINGCHANGE handler for CMessageView.
  135. *--------------------------------------------------------------------------*/
  136. LRESULT CMessageView::OnSettingChange (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  137. {
  138. if (wParam == SPI_SETNONCLIENTMETRICS)
  139. {
  140. DeleteFonts ();
  141. CreateFonts ();
  142. }
  143. UpdateSystemMetrics();
  144. RecalcLayout ();
  145. return (0);
  146. }
  147. /*+-------------------------------------------------------------------------*
  148. * CMessageView::UpdateSystemMetrics
  149. *
  150. * Updates the system metrics used by the message view control.
  151. *--------------------------------------------------------------------------*/
  152. void CMessageView::UpdateSystemMetrics ()
  153. {
  154. m_sizeMargin.cx = GetSystemMetrics (SM_CXVSCROLL);
  155. m_sizeMargin.cy = GetSystemMetrics (SM_CYVSCROLL);
  156. m_sizeIcon.cx = GetSystemMetrics (SM_CXICON);
  157. m_sizeIcon.cy = GetSystemMetrics (SM_CYICON);
  158. }
  159. /*+-------------------------------------------------------------------------*
  160. * CMessageView::OnKeyDown
  161. *
  162. * WM_KEYDOWN handler for CMessageView.
  163. *--------------------------------------------------------------------------*/
  164. LRESULT CMessageView::OnKeyDown (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  165. {
  166. return (0);
  167. }
  168. /*+-------------------------------------------------------------------------*
  169. * CMessageView::OnVScroll
  170. *
  171. * WM_VSCROLL handler for CMessageView.
  172. *--------------------------------------------------------------------------*/
  173. LRESULT CMessageView::OnVScroll (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  174. {
  175. VertScroll (LOWORD (wParam), HIWORD (wParam), 1);
  176. return (0);
  177. }
  178. /*+-------------------------------------------------------------------------*
  179. * CMessageView::OnMouseWheel
  180. *
  181. * WM_MOUSEWHEEL handler for CMessageView.
  182. *--------------------------------------------------------------------------*/
  183. LRESULT CMessageView::OnMouseWheel (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  184. {
  185. m_nAccumulatedScrollDelta += GET_WHEEL_DELTA_WPARAM (wParam);
  186. /*
  187. * scroll one line up or down for each WHEEL_DELTA unit in our
  188. * accumulated delta
  189. */
  190. const int nScrollCmd = (m_nAccumulatedScrollDelta < 0) ? SB_LINEDOWN : SB_LINEUP;
  191. const int nScrollRepeat = abs(m_nAccumulatedScrollDelta) / WHEEL_DELTA;
  192. VertScroll (nScrollCmd, 0, nScrollRepeat);
  193. m_nAccumulatedScrollDelta %= WHEEL_DELTA;
  194. return (0);
  195. }
  196. /*+-------------------------------------------------------------------------*
  197. * CMessageView::VertScroll
  198. *
  199. * Vertical scroll handler for CMessageView.
  200. *--------------------------------------------------------------------------*/
  201. void CMessageView::VertScroll (
  202. int nScrollCmd, /* I:how to scroll (e.g. SB_LINEUP) */
  203. int nScrollPos, /* I:absolute position (SB_THUMBTRACK only) */
  204. int nRepeat) /* I:repeat count */
  205. {
  206. int yScroll = m_yScroll;
  207. switch (nScrollCmd)
  208. {
  209. case SB_LINEUP:
  210. yScroll -= nRepeat * m_cyLine;
  211. break;
  212. case SB_LINEDOWN:
  213. yScroll += nRepeat * m_cyLine;
  214. break;
  215. case SB_PAGEUP:
  216. yScroll -= nRepeat * m_cyPage;
  217. break;
  218. case SB_PAGEDOWN:
  219. yScroll += nRepeat * m_cyPage;
  220. break;
  221. case SB_TOP:
  222. yScroll = m_yScrollMin;
  223. break;
  224. case SB_BOTTOM:
  225. yScroll = m_yScrollMax;
  226. break;
  227. case SB_THUMBTRACK:
  228. yScroll = nScrollPos;
  229. break;
  230. }
  231. ScrollToPosition (yScroll);
  232. }
  233. /*+-------------------------------------------------------------------------*
  234. * CMessageView::OnDraw
  235. *
  236. * Draw handler for CMessageView.
  237. *--------------------------------------------------------------------------*/
  238. HRESULT CMessageView::OnDraw(ATL_DRAWINFO& di)
  239. {
  240. /*
  241. * use CDCHandle instead of CDC so the dtor won't delete the DC
  242. * (we didn't create it, so we can't delete it)
  243. */
  244. WTL::CDCHandle dc = di.hdcDraw;
  245. /*
  246. * handle scrolling
  247. */
  248. dc.SetViewportOrg (0, -m_yScroll);
  249. /*
  250. * set up colors
  251. */
  252. COLORREF clrText = dc.SetTextColor (GetSysColor (COLOR_WINDOWTEXT));
  253. COLORREF clrBack = dc.SetBkColor (GetSysColor (COLOR_WINDOW));
  254. /*
  255. * get the clipping region for the DC
  256. */
  257. WTL::CRect rectT;
  258. WTL::CRect rectClip;
  259. dc.GetClipBox (rectClip);
  260. /*
  261. * if there is a title and it intersects the clipping region, draw it
  262. */
  263. if ((m_TextElement[Title].str.length() > 0) &&
  264. rectT.IntersectRect (rectClip, m_TextElement[Title].rect))
  265. DrawTextElement (dc, m_TextElement[Title]);
  266. /*
  267. * if there is a body and it intersects the clipping region, draw it
  268. */
  269. if ((m_TextElement[Body].str.length() > 0) &&
  270. rectT.IntersectRect (rectClip, m_TextElement[Body].rect))
  271. DrawTextElement (dc, m_TextElement[Body]);
  272. /*
  273. * if there is an icon and it intersects the clipping region, draw it
  274. */
  275. if ((m_hIcon != NULL) && rectT.IntersectRect (rectClip, m_rectIcon))
  276. dc.DrawIcon (m_rectIcon.TopLeft(), m_hIcon);
  277. /*
  278. * restore the DC
  279. */
  280. dc.SetTextColor (clrText);
  281. dc.SetBkColor (clrBack);
  282. #define SHOW_MARGINS 0
  283. #if (defined(DBG) && SHOW_MARGINS)
  284. {
  285. HBRUSH hbr = GetSysColorBrush (COLOR_GRAYTEXT);
  286. WTL::CRect rectAll;
  287. WTL::CRect rectTemp;
  288. rectTemp.UnionRect (m_TextElement[Body].rect, m_TextElement[Title].rect);
  289. rectAll.UnionRect (rectTemp, m_rectIcon);
  290. rectAll.InflateRect (m_sizeMargin);
  291. dc.FrameRect (m_TextElement[Title].rect, hbr);
  292. dc.FrameRect (m_TextElement[Body].rect, hbr);
  293. dc.FrameRect (m_rectIcon, hbr);
  294. dc.FrameRect (rectAll, hbr);
  295. }
  296. #endif
  297. return (S_OK);
  298. }
  299. /*+-------------------------------------------------------------------------*
  300. * CMessageView::SetTitleText
  301. *
  302. *
  303. *--------------------------------------------------------------------------*/
  304. STDMETHODIMP CMessageView::SetTitleText (LPCOLESTR pszTitleText)
  305. {
  306. return (SetTextElement (m_TextElement[Title], pszTitleText));
  307. }
  308. /*+-------------------------------------------------------------------------*
  309. * CMessageView::SetBodyText
  310. *
  311. *
  312. *--------------------------------------------------------------------------*/
  313. STDMETHODIMP CMessageView::SetBodyText (LPCOLESTR pszBodyText)
  314. {
  315. return (SetTextElement (m_TextElement[Body], pszBodyText));
  316. }
  317. /*+-------------------------------------------------------------------------*
  318. * CMessageView::SetTextElement
  319. *
  320. *
  321. *--------------------------------------------------------------------------*/
  322. HRESULT CMessageView::SetTextElement (TextElement& te, LPCOLESTR pszNewText)
  323. {
  324. USES_CONVERSION;
  325. tstring strNewText;
  326. if (pszNewText != NULL)
  327. strNewText = W2CT(pszNewText);
  328. if (te.str != strNewText)
  329. {
  330. te.str = strNewText;
  331. RecalcLayout();
  332. Invalidate();
  333. }
  334. return (S_OK);
  335. }
  336. /*+-------------------------------------------------------------------------*
  337. * CMessageView::SetIcon
  338. *
  339. *
  340. *--------------------------------------------------------------------------*/
  341. STDMETHODIMP CMessageView::SetIcon (IconIdentifier id)
  342. {
  343. bool fHadIconBefore = (m_hIcon != NULL);
  344. if (id == Icon_None)
  345. m_hIcon = NULL;
  346. else if ((id >= Icon_First) && (id <= Icon_Last))
  347. m_hIcon = LoadIcon (NULL, MAKEINTRESOURCE (id));
  348. else
  349. return (E_INVALIDARG);
  350. /*
  351. * if we had an icon before, but we don't have one now (or vice versa)
  352. * we need to recalculate the layout and redraw everything
  353. */
  354. if (fHadIconBefore != (m_hIcon != NULL))
  355. {
  356. RecalcLayout();
  357. Invalidate();
  358. }
  359. /*
  360. * otherwise, just redraw draw the icon
  361. */
  362. else
  363. InvalidateRect (m_rectIcon);
  364. return (S_OK);
  365. }
  366. /*+-------------------------------------------------------------------------*
  367. * CMessageView::Clear
  368. *
  369. *
  370. *--------------------------------------------------------------------------*/
  371. STDMETHODIMP CMessageView::Clear ()
  372. {
  373. m_TextElement[Title].str.erase();
  374. m_TextElement[Body].str.erase();
  375. m_hIcon = NULL;
  376. RecalcLayout();
  377. Invalidate();
  378. return (S_OK);
  379. }
  380. /*+-------------------------------------------------------------------------*
  381. * CMessageView::RecalcLayout
  382. *
  383. *
  384. *--------------------------------------------------------------------------*/
  385. void CMessageView::RecalcLayout()
  386. {
  387. RecalcIconLayout();
  388. RecalcTitleLayout();
  389. RecalcBodyLayout();
  390. UpdateScrollSizes();
  391. }
  392. /*+-------------------------------------------------------------------------*
  393. * CMessageView::RecalcIconLayout
  394. *
  395. *
  396. *--------------------------------------------------------------------------*/
  397. void CMessageView::RecalcIconLayout()
  398. {
  399. m_rectIcon = WTL::CRect (WTL::CPoint (m_sizeMargin.cx, m_sizeMargin.cy),
  400. (m_hIcon != NULL) ? m_sizeIcon : WTL::CSize(0,0));
  401. }
  402. /*+-------------------------------------------------------------------------*
  403. * CMessageView::RecalcTitleLayout
  404. *
  405. *
  406. *--------------------------------------------------------------------------*/
  407. void CMessageView::RecalcTitleLayout()
  408. {
  409. WTL::CRect& rect = m_TextElement[Title].rect;
  410. /*
  411. * prime the title rectangle for calculating the text height
  412. * (leave room for a vertical scrollbar on the right so its appearance
  413. * and disappearance don't affect the layout of the text)
  414. */
  415. rect.SetRect (
  416. m_rectIcon.right,
  417. m_rectIcon.top,
  418. _MAX (0, (int) (m_sizeWindow.cx - m_sizeMargin.cx - GetSystemMetrics(SM_CXVSCROLL))),
  419. 0);
  420. /*
  421. * if there is an icon, leave a gutter between the icon and title
  422. */
  423. if ((m_hIcon != NULL) && (rect.right > 0))
  424. {
  425. rect.left += m_cyLine;
  426. rect.right = _MAX (rect.left, rect.right);
  427. }
  428. /*
  429. * compute the height of the title
  430. */
  431. if (m_TextElement[Title].str.length() > 0)
  432. rect.bottom = rect.top + CalcTextElementHeight (m_TextElement[Title], rect.Width());
  433. /*
  434. * if the title is shorter than the icon, center it vertically
  435. */
  436. if (rect.Height() < m_rectIcon.Height())
  437. rect.OffsetRect (0, (m_rectIcon.Height() - rect.Height()) / 2);
  438. }
  439. /*+-------------------------------------------------------------------------*
  440. * CMessageView::RecalcBodyLayout
  441. *
  442. *
  443. *--------------------------------------------------------------------------*/
  444. void CMessageView::RecalcBodyLayout()
  445. {
  446. WTL::CRect& rect = m_TextElement[Body].rect;
  447. /*
  448. * prime the body rectangle for calculating the text height
  449. */
  450. rect.UnionRect (m_rectIcon, m_TextElement[Title].rect);
  451. rect.OffsetRect (0, rect.Height());
  452. /*
  453. * compute the height of the body; it starts empty, but adds the
  454. * height of the body text if we have any
  455. */
  456. rect.bottom = rect.top;
  457. if (m_TextElement[Body].str.length() > 0)
  458. rect.bottom += CalcTextElementHeight (m_TextElement[Body], rect.Width());
  459. /*
  460. * if there's an icon or title, we need to leave
  461. * a line's worth of space before the body
  462. */
  463. if (!rect.IsRectEmpty())
  464. rect.OffsetRect (0, m_cyLine);
  465. }
  466. /*+-------------------------------------------------------------------------*
  467. * CMessageView::CalcTextElementHeight
  468. *
  469. *
  470. *--------------------------------------------------------------------------*/
  471. int CMessageView::CalcTextElementHeight (const TextElement& te, int cx)
  472. {
  473. TextElement teWork = te;
  474. teWork.rect.SetRect (0, 0, cx, 0);
  475. DrawTextElement (WTL::CWindowDC(m_hWnd), teWork, DT_CALCRECT);
  476. teWork.font.Detach();
  477. return (teWork.rect.Height());
  478. }
  479. /*+-------------------------------------------------------------------------*
  480. * CMessageView::DrawTextElement
  481. *
  482. *
  483. *--------------------------------------------------------------------------*/
  484. void CMessageView::DrawTextElement (HDC hdc, TextElement& te, DWORD dwFlags)
  485. {
  486. /*
  487. * use CDCHandle instead of CDC so the dtor won't delete the DC
  488. * (we didn't create it, so we can't delete it)
  489. */
  490. WTL::CDCHandle dc = hdc;
  491. HFONT hFont = dc.SelectFont (te.font);
  492. dc.DrawText (te.str.data(), te.str.length(), te.rect,
  493. DT_LEFT | DT_TOP | DT_WORDBREAK | DT_NOPREFIX | dwFlags);
  494. dc.SelectFont (hFont);
  495. }
  496. /*+-------------------------------------------------------------------------*
  497. * CMessageView::CreateFonts
  498. *
  499. *
  500. *--------------------------------------------------------------------------*/
  501. void CMessageView::CreateFonts ()
  502. {
  503. /*
  504. * create a font that's a little larger than the
  505. * one used for icon titles to use for the body text
  506. */
  507. NONCLIENTMETRICS ncm;
  508. ncm.cbSize = sizeof (ncm);
  509. SystemParametersInfo (SPI_GETNONCLIENTMETRICS, 0, &ncm, false);
  510. m_TextElement[Body].font.CreateFontIndirect (&ncm.lfMessageFont);
  511. /*
  512. * create a bold version for the title
  513. */
  514. ncm.lfMessageFont.lfWeight = FW_BOLD;
  515. m_TextElement[Title].font.CreateFontIndirect (&ncm.lfMessageFont);
  516. /*
  517. * get the height of a line of text
  518. */
  519. SIZE siz;
  520. TCHAR ch = _T('0');
  521. WTL::CWindowDC dc(m_hWnd);
  522. HFONT hFontOld = dc.SelectFont (m_TextElement[Title].font);
  523. dc.GetTextExtent (&ch, 1, &siz);
  524. dc.SelectFont (hFontOld);
  525. m_cyLine = siz.cy;
  526. }
  527. /*+-------------------------------------------------------------------------*
  528. * CMessageView::DeleteFonts
  529. *
  530. *
  531. *--------------------------------------------------------------------------*/
  532. void CMessageView::DeleteFonts ()
  533. {
  534. m_TextElement[Title].font.DeleteObject();
  535. m_TextElement[Body].font.DeleteObject();
  536. }
  537. /*+-------------------------------------------------------------------------*
  538. * CMessageView::UpdateScrollSizes
  539. *
  540. *
  541. *--------------------------------------------------------------------------*/
  542. void CMessageView::UpdateScrollSizes ()
  543. {
  544. WTL::CRect rect;
  545. GetClientRect (rect);
  546. int cyTotal = GetOverallHeight();
  547. m_yScrollMax = _MAX (0, cyTotal - rect.Height());
  548. /*
  549. * The height of a page is a whole number of lines. If the window
  550. * can display N lines at a time, a page will be N-1 lines so there's
  551. * some continuity after a page up or down.
  552. */
  553. if (m_cyLine > 0)
  554. m_cyPage = rect.Height();// _MAX (0, ((rect.Height() / m_cyLine) - 1) * m_cyLine);
  555. else
  556. m_cyPage = 0;
  557. /*
  558. * update the scrollbar
  559. */
  560. SCROLLINFO si;
  561. si.cbSize = sizeof(si);
  562. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  563. si.nMax = cyTotal;
  564. si.nMin = m_yScrollMin;
  565. si.nPage = m_cyPage;
  566. si.nPos = m_yScroll;
  567. SetScrollInfo (SB_VERT, &si);
  568. }
  569. /*+-------------------------------------------------------------------------*
  570. * CMessageView::ScrollToPosition
  571. *
  572. *
  573. *--------------------------------------------------------------------------*/
  574. void CMessageView::ScrollToPosition (int yScroll)
  575. {
  576. yScroll = _MIN (m_yScrollMax, _MAX (m_yScrollMin, yScroll));
  577. if (m_yScroll != yScroll)
  578. {
  579. int dy = m_yScroll - yScroll;
  580. m_yScroll = yScroll;
  581. ScrollWindow (0, dy);
  582. UpdateScrollSizes();
  583. }
  584. }