|
|
/*--------------------------------------------------------------------------*
* * Microsoft Windows * Copyright (C) Microsoft Corporation, 1992 - 1999 * * File: msgview.cpp * * Contents: Implementation file for CMessageView * * History: 28-Apr-99 jeffro Created * *--------------------------------------------------------------------------*/
#include "stdafx.h"
#include "msgview.h"
#include "util.h"
using std::_MAX; using std::_MIN;
/*+-------------------------------------------------------------------------*
* CMessageView::CMessageView * * *--------------------------------------------------------------------------*/
CMessageView::CMessageView () : m_hIcon (NULL), m_yScroll (0), m_yScrollMax (0), m_yScrollMin (0), m_cyPage (0), m_cyLine (0), m_sizeWindow (0, 0), m_sizeIcon (0, 0), m_sizeMargin (0, 0), m_nAccumulatedScrollDelta (0) { /*
* can't be windowless */ m_bWindowOnly = true;
/*
* get the system metrics we'll use */ UpdateSystemMetrics();
DEBUG_INCREMENT_INSTANCE_COUNTER(CMessageView); }
/*+-------------------------------------------------------------------------*
* CMessageView::~CMessageView * * *--------------------------------------------------------------------------*/
CMessageView::~CMessageView () { DEBUG_DECREMENT_INSTANCE_COUNTER(CMessageView); }
/*+-------------------------------------------------------------------------*
* CMessageView::OnCreate * * WM_CREATE handler for CMessageView. *--------------------------------------------------------------------------*/
LRESULT CMessageView::OnCreate (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { CreateFonts(); RecalcLayout ();
return (0); }
/*+-------------------------------------------------------------------------*
* CMessageView::OnDestroy * * WM_DESTROY handler for CMessageView. *--------------------------------------------------------------------------*/
LRESULT CMessageView::OnDestroy (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { DeleteFonts(); return (0); }
/*+-------------------------------------------------------------------------*
* CMessageView::OnSize * * WM_SIZE handler for CMessageView. *--------------------------------------------------------------------------*/
LRESULT CMessageView::OnSize (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { /*
* The transient appearance/disappearance of WS_VSCROLL makes the client * rect volatile and can mess up our calculations. We'll use the more * stable window rect instead. */ WTL::CRect rectWindow; GetWindowRect (rectWindow);
if (GetExStyle() & WS_EX_CLIENTEDGE) rectWindow.DeflateRect (GetSystemMetrics (SM_CXEDGE), GetSystemMetrics (SM_CYEDGE));
WTL::CSize sizeWindow (rectWindow.Width(), rectWindow.Height());
/*
* if the overall size has changed, we have some work to do */ if (m_sizeWindow != sizeWindow) { /*
* load m_sizeWindow right away so scrollbar future calculations * will have the right values in the member variable */ std::swap (m_sizeWindow, sizeWindow);
/*
* if the width has changed, we'll might need to recalculate * all of the text heights */ if (m_sizeWindow.cx != sizeWindow.cx) RecalcLayout ();
/*
* if the height changed, there's scrollbar work */ if (m_sizeWindow.cy != sizeWindow.cy) { int dy = m_sizeWindow.cy - sizeWindow.cy;
/*
* if the window's grown, we might need to scroll to keep the * bottom of our content glued to the bottom of the window */ if ((dy > 0) && (m_yScroll > 0) && ((m_yScroll + m_sizeWindow.cy) > GetOverallHeight())) ScrollToPosition (m_yScroll - dy);
/*
* otherwise, just update the scrollbar */ else UpdateScrollSizes(); } }
return (0); }
/*+-------------------------------------------------------------------------*
* CMessageView::OnSettingChange * * WM_SETTINGCHANGE handler for CMessageView. *--------------------------------------------------------------------------*/
LRESULT CMessageView::OnSettingChange (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if (wParam == SPI_SETNONCLIENTMETRICS) { DeleteFonts (); CreateFonts (); }
UpdateSystemMetrics(); RecalcLayout ();
return (0); }
/*+-------------------------------------------------------------------------*
* CMessageView::UpdateSystemMetrics * * Updates the system metrics used by the message view control. *--------------------------------------------------------------------------*/
void CMessageView::UpdateSystemMetrics () { m_sizeMargin.cx = GetSystemMetrics (SM_CXVSCROLL); m_sizeMargin.cy = GetSystemMetrics (SM_CYVSCROLL); m_sizeIcon.cx = GetSystemMetrics (SM_CXICON); m_sizeIcon.cy = GetSystemMetrics (SM_CYICON); }
/*+-------------------------------------------------------------------------*
* CMessageView::OnKeyDown * * WM_KEYDOWN handler for CMessageView. *--------------------------------------------------------------------------*/
LRESULT CMessageView::OnKeyDown (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return (0); }
/*+-------------------------------------------------------------------------*
* CMessageView::OnVScroll * * WM_VSCROLL handler for CMessageView. *--------------------------------------------------------------------------*/
LRESULT CMessageView::OnVScroll (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { VertScroll (LOWORD (wParam), HIWORD (wParam), 1); return (0); }
/*+-------------------------------------------------------------------------*
* CMessageView::OnMouseWheel * * WM_MOUSEWHEEL handler for CMessageView. *--------------------------------------------------------------------------*/
LRESULT CMessageView::OnMouseWheel (UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { m_nAccumulatedScrollDelta += GET_WHEEL_DELTA_WPARAM (wParam);
/*
* scroll one line up or down for each WHEEL_DELTA unit in our * accumulated delta */ const int nScrollCmd = (m_nAccumulatedScrollDelta < 0) ? SB_LINEDOWN : SB_LINEUP; const int nScrollRepeat = abs(m_nAccumulatedScrollDelta) / WHEEL_DELTA; VertScroll (nScrollCmd, 0, nScrollRepeat);
m_nAccumulatedScrollDelta %= WHEEL_DELTA;
return (0); }
/*+-------------------------------------------------------------------------*
* CMessageView::VertScroll * * Vertical scroll handler for CMessageView. *--------------------------------------------------------------------------*/
void CMessageView::VertScroll ( int nScrollCmd, /* I:how to scroll (e.g. SB_LINEUP) */ int nScrollPos, /* I:absolute position (SB_THUMBTRACK only) */ int nRepeat) /* I:repeat count */ { int yScroll = m_yScroll;
switch (nScrollCmd) { case SB_LINEUP: yScroll -= nRepeat * m_cyLine; break;
case SB_LINEDOWN: yScroll += nRepeat * m_cyLine; break;
case SB_PAGEUP: yScroll -= nRepeat * m_cyPage; break;
case SB_PAGEDOWN: yScroll += nRepeat * m_cyPage; break;
case SB_TOP: yScroll = m_yScrollMin; break;
case SB_BOTTOM: yScroll = m_yScrollMax; break;
case SB_THUMBTRACK: yScroll = nScrollPos; break; }
ScrollToPosition (yScroll); }
/*+-------------------------------------------------------------------------*
* CMessageView::OnDraw * * Draw handler for CMessageView. *--------------------------------------------------------------------------*/
HRESULT CMessageView::OnDraw(ATL_DRAWINFO& di) { /*
* use CDCHandle instead of CDC so the dtor won't delete the DC * (we didn't create it, so we can't delete it) */ WTL::CDCHandle dc = di.hdcDraw;
/*
* handle scrolling */ dc.SetViewportOrg (0, -m_yScroll);
/*
* set up colors */ COLORREF clrText = dc.SetTextColor (GetSysColor (COLOR_WINDOWTEXT)); COLORREF clrBack = dc.SetBkColor (GetSysColor (COLOR_WINDOW));
/*
* get the clipping region for the DC */ WTL::CRect rectT; WTL::CRect rectClip; dc.GetClipBox (rectClip);
/*
* if there is a title and it intersects the clipping region, draw it */ if ((m_TextElement[Title].str.length() > 0) && rectT.IntersectRect (rectClip, m_TextElement[Title].rect)) DrawTextElement (dc, m_TextElement[Title]);
/*
* if there is a body and it intersects the clipping region, draw it */ if ((m_TextElement[Body].str.length() > 0) && rectT.IntersectRect (rectClip, m_TextElement[Body].rect)) DrawTextElement (dc, m_TextElement[Body]);
/*
* if there is an icon and it intersects the clipping region, draw it */ if ((m_hIcon != NULL) && rectT.IntersectRect (rectClip, m_rectIcon)) dc.DrawIcon (m_rectIcon.TopLeft(), m_hIcon);
/*
* restore the DC */ dc.SetTextColor (clrText); dc.SetBkColor (clrBack);
#define SHOW_MARGINS 0
#if (defined(DBG) && SHOW_MARGINS)
{ HBRUSH hbr = GetSysColorBrush (COLOR_GRAYTEXT); WTL::CRect rectAll; WTL::CRect rectTemp;
rectTemp.UnionRect (m_TextElement[Body].rect, m_TextElement[Title].rect); rectAll.UnionRect (rectTemp, m_rectIcon); rectAll.InflateRect (m_sizeMargin);
dc.FrameRect (m_TextElement[Title].rect, hbr); dc.FrameRect (m_TextElement[Body].rect, hbr); dc.FrameRect (m_rectIcon, hbr); dc.FrameRect (rectAll, hbr); } #endif
return (S_OK); }
/*+-------------------------------------------------------------------------*
* CMessageView::SetTitleText * * *--------------------------------------------------------------------------*/
STDMETHODIMP CMessageView::SetTitleText (LPCOLESTR pszTitleText) { return (SetTextElement (m_TextElement[Title], pszTitleText)); }
/*+-------------------------------------------------------------------------*
* CMessageView::SetBodyText * * *--------------------------------------------------------------------------*/
STDMETHODIMP CMessageView::SetBodyText (LPCOLESTR pszBodyText) { return (SetTextElement (m_TextElement[Body], pszBodyText)); }
/*+-------------------------------------------------------------------------*
* CMessageView::SetTextElement * * *--------------------------------------------------------------------------*/
HRESULT CMessageView::SetTextElement (TextElement& te, LPCOLESTR pszNewText) { USES_CONVERSION; tstring strNewText;
if (pszNewText != NULL) strNewText = W2CT(pszNewText);
if (te.str != strNewText) { te.str = strNewText; RecalcLayout(); Invalidate(); }
return (S_OK); }
/*+-------------------------------------------------------------------------*
* CMessageView::SetIcon * * *--------------------------------------------------------------------------*/
STDMETHODIMP CMessageView::SetIcon (IconIdentifier id) { bool fHadIconBefore = (m_hIcon != NULL);
if (id == Icon_None) m_hIcon = NULL;
else if ((id >= Icon_First) && (id <= Icon_Last)) m_hIcon = LoadIcon (NULL, MAKEINTRESOURCE (id));
else return (E_INVALIDARG);
/*
* if we had an icon before, but we don't have one now (or vice versa) * we need to recalculate the layout and redraw everything */ if (fHadIconBefore != (m_hIcon != NULL)) { RecalcLayout(); Invalidate(); }
/*
* otherwise, just redraw draw the icon */ else InvalidateRect (m_rectIcon);
return (S_OK); }
/*+-------------------------------------------------------------------------*
* CMessageView::Clear * * *--------------------------------------------------------------------------*/
STDMETHODIMP CMessageView::Clear () { m_TextElement[Title].str.erase(); m_TextElement[Body].str.erase(); m_hIcon = NULL;
RecalcLayout(); Invalidate();
return (S_OK); }
/*+-------------------------------------------------------------------------*
* CMessageView::RecalcLayout * * *--------------------------------------------------------------------------*/
void CMessageView::RecalcLayout() { RecalcIconLayout(); RecalcTitleLayout(); RecalcBodyLayout(); UpdateScrollSizes(); }
/*+-------------------------------------------------------------------------*
* CMessageView::RecalcIconLayout * * *--------------------------------------------------------------------------*/
void CMessageView::RecalcIconLayout() { m_rectIcon = WTL::CRect (WTL::CPoint (m_sizeMargin.cx, m_sizeMargin.cy), (m_hIcon != NULL) ? m_sizeIcon : WTL::CSize(0,0)); }
/*+-------------------------------------------------------------------------*
* CMessageView::RecalcTitleLayout * * *--------------------------------------------------------------------------*/
void CMessageView::RecalcTitleLayout() { WTL::CRect& rect = m_TextElement[Title].rect;
/*
* prime the title rectangle for calculating the text height * (leave room for a vertical scrollbar on the right so its appearance * and disappearance don't affect the layout of the text) */ rect.SetRect ( m_rectIcon.right, m_rectIcon.top, _MAX (0, (int) (m_sizeWindow.cx - m_sizeMargin.cx - GetSystemMetrics(SM_CXVSCROLL))), 0);
/*
* if there is an icon, leave a gutter between the icon and title */ if ((m_hIcon != NULL) && (rect.right > 0)) { rect.left += m_cyLine; rect.right = _MAX (rect.left, rect.right); }
/*
* compute the height of the title */ if (m_TextElement[Title].str.length() > 0) rect.bottom = rect.top + CalcTextElementHeight (m_TextElement[Title], rect.Width());
/*
* if the title is shorter than the icon, center it vertically */ if (rect.Height() < m_rectIcon.Height()) rect.OffsetRect (0, (m_rectIcon.Height() - rect.Height()) / 2); }
/*+-------------------------------------------------------------------------*
* CMessageView::RecalcBodyLayout * * *--------------------------------------------------------------------------*/
void CMessageView::RecalcBodyLayout() { WTL::CRect& rect = m_TextElement[Body].rect;
/*
* prime the body rectangle for calculating the text height */
/* Compute the envelope of the Icon and Title rectangles, and then
* shift it down until it no longer overlaps the former two. * Bug# 666434: Cannot use UnionRect for computing the envelope because * it ignores empty rectangles. The Icon and Title recatangles can be * empty if SetTitleText or SetIcon haven't been called, or called with * empty strings/images */ rect.left = _MIN (m_rectIcon.left, m_TextElement[Title].rect.left); rect.right = _MAX (m_rectIcon.right, m_TextElement[Title].rect.right); rect.top = _MAX (m_rectIcon.bottom, m_TextElement[Title].rect.bottom);
/*
* compute the height of the body; it starts empty, but adds the * height of the body text if we have any */ rect.bottom = rect.top; if (m_TextElement[Body].str.length() > 0) rect.bottom += CalcTextElementHeight (m_TextElement[Body], rect.Width());
/*
* if there's an icon or title, we need to leave * a line's worth of space before the body */ if (!rect.IsRectEmpty()) rect.OffsetRect (0, m_cyLine); }
/*+-------------------------------------------------------------------------*
* CMessageView::CalcTextElementHeight * * *--------------------------------------------------------------------------*/
int CMessageView::CalcTextElementHeight (const TextElement& te, int cx) { TextElement teWork = te; teWork.rect.SetRect (0, 0, cx, 0);
DrawTextElement (WTL::CWindowDC(m_hWnd), teWork, DT_CALCRECT); teWork.font.Detach();
return (teWork.rect.Height()); }
/*+-------------------------------------------------------------------------*
* CMessageView::DrawTextElement * * *--------------------------------------------------------------------------*/
void CMessageView::DrawTextElement (HDC hdc, TextElement& te, DWORD dwFlags) { /*
* use CDCHandle instead of CDC so the dtor won't delete the DC * (we didn't create it, so we can't delete it) */ WTL::CDCHandle dc = hdc;
HFONT hFont = dc.SelectFont (te.font);
dc.DrawText (te.str.data(), te.str.length(), te.rect, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_NOPREFIX | dwFlags); dc.SelectFont (hFont); }
/*+-------------------------------------------------------------------------*
* CMessageView::CreateFonts * * *--------------------------------------------------------------------------*/
void CMessageView::CreateFonts () { /*
* create a font that's a little larger than the * one used for icon titles to use for the body text */ NONCLIENTMETRICS ncm; ncm.cbSize = sizeof (ncm); SystemParametersInfo (SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, false);
m_TextElement[Body].font.CreateFontIndirect (&ncm.lfMessageFont);
/*
* create a bold version for the title */ ncm.lfMessageFont.lfWeight = FW_BOLD; m_TextElement[Title].font.CreateFontIndirect (&ncm.lfMessageFont);
/*
* get the height of a line of text */ SIZE siz; TCHAR ch = _T('0'); WTL::CWindowDC dc(m_hWnd); HFONT hFontOld = dc.SelectFont (m_TextElement[Title].font); dc.GetTextExtent (&ch, 1, &siz); dc.SelectFont (hFontOld); m_cyLine = siz.cy; }
/*+-------------------------------------------------------------------------*
* CMessageView::DeleteFonts * * *--------------------------------------------------------------------------*/
void CMessageView::DeleteFonts () { m_TextElement[Title].font.DeleteObject(); m_TextElement[Body].font.DeleteObject(); }
/*+-------------------------------------------------------------------------*
* CMessageView::UpdateScrollSizes * * *--------------------------------------------------------------------------*/
void CMessageView::UpdateScrollSizes () { WTL::CRect rect; GetClientRect (rect);
int cyTotal = GetOverallHeight(); m_yScrollMax = _MAX (0, cyTotal - rect.Height());
/*
* The height of a page is a whole number of lines. If the window * can display N lines at a time, a page will be N-1 lines so there's * some continuity after a page up or down. */ if (m_cyLine > 0) m_cyPage = rect.Height();// _MAX (0, ((rect.Height() / m_cyLine) - 1) * m_cyLine);
else m_cyPage = 0;
/*
* update the scrollbar */ SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; si.nMax = cyTotal; si.nMin = m_yScrollMin; si.nPage = m_cyPage; si.nPos = m_yScroll;
SetScrollInfo (SB_VERT, &si); }
/*+-------------------------------------------------------------------------*
* CMessageView::ScrollToPosition * * *--------------------------------------------------------------------------*/
void CMessageView::ScrollToPosition (int yScroll) { yScroll = _MIN (m_yScrollMax, _MAX (m_yScrollMin, yScroll));
if (m_yScroll != yScroll) { int dy = m_yScroll - yScroll; m_yScroll = yScroll;
ScrollWindow (0, dy); UpdateScrollSizes(); } }
|