735 lines
19 KiB
C++
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();
|
|
}
|
|
}
|