windows-nt/Source/XPSP1/NT/admin/activec/nodemgr/msgview.cpp
2020-09-26 16:20:57 +08:00

735 lines
19 KiB
C++

/*--------------------------------------------------------------------------*
*
* 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
*/
rect.UnionRect (m_rectIcon, m_TextElement[Title].rect);
rect.OffsetRect (0, rect.Height());
/*
* 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, 0, &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();
}
}