📄 dispml.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*
* @doc INTERNAL
*
* @module dispml.cpp -- CDisplayML class |
*
* This is the Multi-line display engine. See disp.c for the base class
* methods and dispsl.c for the single-line display engine.
*
* Owner:<nl>
* Original RichEdit code: David R. Fulmer
* Christian Fortini (initial conversion to C++)
* Murray Sargent
* Rick Sailor (for most of RE 2.0)
*/
#include "_common.h"
#include "_dispml.h"
#include "_edit.h"
#include "_font.h"
#include "_measure.h"
#include "_render.h"
#include "_select.h"
#include "_dfreeze.h"
ASSERTDATA
//
// Invariant support
//
#define DEBUG_CLASSNAME CDisplayML
#include "_invar.h"
// Timer tick counts for background task
#define cmsecBgndInterval 300
#define cmsecBgndBusy 100
// Lines ahead
const LONG cExtraBeforeLazy = 60;
// if we need to calc at least this many characters, then put up a wait
// cursor. NB! 4096 is not a measured number; it just seemed like a good
// one.
#define NUMCHARFORWAITCURSOR 4096
// define only if we want to disable the horizontal scroll bar
#define DISABLE_HORZ
#ifndef DEBUG
#define CheckView()
#define CheckLineArray()
#endif
// V-GUYB: Add a local MulDiv to prevent wrapping during scroll calculations in long docs.
#define MulDivI64(nNumber, nNumerator, nDenominator) (int)((nDenominator) ? (((__int64)(nNumber) * (__int64)(nNumerator)) / (__int64)(nDenominator)) : -1)
// =========================== CDisplayML =====================================================
#ifdef DEBUG
/*
* CDisplayML::Invariant
*
* @mfunc Make sure the display is in a valid state
*
* @rdesc TRUE if the tests succeeded, FALSE otherwise
*/
BOOL CDisplayML::Invariant(void) const
{
CDisplay::Invariant();
if( _prgliNew )
{
Assert(_prgliNew->Invariant());
}
return TRUE;
}
#endif // DEBUG
/*
* CDisplayML::CalcScrollHeight()
*
* @mfunc
* Calculate the maximum Y scroll position.
*
* @rdesc
* Maximum possible scrolling position
*
* @devnote
* This routine exists because plain text controls do not have
* the auto-EOP and so the scroll height is different than
* the height of the control if the text ends in an EOP type
* character.
*
*/
LONG CDisplayML::CalcScrollHeight(LONG yHeight) const
{
// The max scroll height for plain text controls is calculated differently
// because they don't have an automatic EOP character.
if (!_ped->IsRich() && (Count() > 0))
{
// Get the last line in the array
CLine *lp = Elem(Count() - 1);
// Is the last character an EOP? - if so we need to bump the scroll
// height.
if (lp->_cchEOP)
yHeight += lp->GetHeight();
}
return yHeight;
}
/*
* CDisplayML::GetMaxYScroll()
*
* @mfunc
* Calculate the maximum Y scroll position.
*
* @rdesc
* Maximum possible scrolling position
*
* @devnote
* This routine exists because we may have to come back and modify this
* calculation for 1.0 compatibility. If we do, this routine only needs
* to be changed in one place rather than the three at which it is used.
*
*/
INLINE LONG CDisplayML::GetMaxYScroll() const
{
// The following code is turn off because we don't want to support
// 1.0 mode unless someone complained about it.
#if 0
if (_ped->Get10Mode())
{
// Ensure last line is always visible
// (use dy as temp to calculate max scroll)
yScroll = Elem(max(0, Count() - 1))->_yHeight;
if(yScroll > _yHeightView)
yScroll = _yHeightView;
yScroll = _yHeight - yScroll;
}
#endif //0
return CalcScrollHeight(_yHeight);
}
/*
* CDisplayML::ConvertYPosToMax()
*
* @mfunc
* Calculate the real scroll position from the scroll position
*
* @rdesc
* Y position from scroll
*
* @devnote
* This routine exists because the thumb position messages
* are limited to 16-bits so we extrapolate when the Y position
* gets greater than that.
*
*/
LONG CDisplayML::ConvertScrollToYPos(
LONG yPos) //@parm Scroll position
{
// Get the maximum scroll range
LONG yRange = GetMaxYScroll();
// Has the maximum scroll range exceeded 16-bits?
if (yRange >= _UI16_MAX)
{
// Yes - Extrapolate to the "real" yPos
#ifndef UNDER_CE
yPos = MulDiv(yPos, yRange, _UI16_MAX);
#else
// V-GUYB: 4 byte values can wrap in MulDiv, so use int64 here.
yPos = MulDivI64(yPos, yRange, _UI16_MAX);
#endif
}
return yPos;
}
/*
* CDisplayML::ConvertYPosToScrollPos()
*
* @mfunc
* Calculate the scroll position from the Y position in the document.
*
* @rdesc
* Scroll position from Y position
*
* @devnote
* This routine exists because the thumb position messages
* are limited to 16-bits so we extrapolate when the Y position
* gets greater than that.
*
*/
INLINE LONG CDisplayML::ConvertYPosToScrollPos(
LONG yPos) //@parm Y position in document
{
// Get the maximum scroll range
LONG yRange = GetMaxYScroll();
// Has the maximum scroll range exceeded 16-bits?
if (yRange >= _UI16_MAX)
{
// Yes - Extrapolate to the "real" yPos
#ifndef UNDER_CE
yPos = MulDiv(yPos, _UI16_MAX, yRange);
#else
// V-GUYB: 4 byte values can wrap in MulDiv, so use int64 here if necessary.
if(yPos < _UI16_MAX)
{
// yPos * _UI16_MAX won't wrap, so use 32 bit MulDiv.
yPos = MulDiv(yPos, _UI16_MAX, yRange);
}
else
{
// yPos * _UI16_MAX will wrap, so use 64 bit MulDiv.
yPos = MulDivI64(yPos, _UI16_MAX, yRange);
}
#endif
}
return yPos;
}
CDisplayML::CDisplayML (CTxtEdit* ped)
: CDisplay (ped), _pddTarget(NULL)
{
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::CDisplayML");
_xWidthMax = 0;
_yHeightMax = 0;
}
CDisplayML::~CDisplayML()
{
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::~CDisplayML");
delete _prgliNew;
delete _pddTarget;
}
/*
* CDisplayML::Init()
*
* @mfunc
* Init this display for the screen
*/
BOOL CDisplayML::Init()
{
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::Init");
// Initialize our base class
if (CDisplay::Init())
{
return FALSE;
}
DWORD dwScrollBars = _ped->TxGetScrollBars();
int fnBarHorz = SB_HORZ;
int fnBarVert = SB_VERT;
AssertSz(_ped, "CDisplayML::Init(): _ped not initialized in display");
Assert (_yCalcMax == 0); // Verify allocation zeroed memory out
Assert (_xWidth == 0);
Assert (_yHeight == 0);
Assert (_cpMin == 0);
Assert (_fBgndRecalc == FALSE);
Assert(!_fVScrollEnabled);
Assert(!_fHScrollEnabled);
// The printer view is not main, therefore we do this to make
// sure scroll bars are not created for print views.
if (IsMain())
{
if(dwScrollBars & WS_VSCROLL)
{
if(dwScrollBars & ES_DISABLENOSCROLL)
{
// This causes wlm to assert on the mac. something about the
// scrollbar being disabled
_ped->TxSetScrollRange(fnBarVert, 0, 1, TRUE);
_ped->TxEnableScrollBar(fnBarVert, ESB_DISABLE_BOTH);
}
}
// Set horizontal scroll range and pos
// ??? - CF need fixing for windowless case
if(dwScrollBars & WS_HSCROLL)
{
if(dwScrollBars & ES_DISABLENOSCROLL)
{
_ped->TxSetScrollRange (fnBarHorz, 0, 1, TRUE);
_ped->TxEnableScrollBar(fnBarHorz, ESB_DISABLE_BOTH);
}
}
}
SetWordWrap(_ped->TxGetWordWrap());
_cpFirstVisible = _cpMin;
Assert(_xScroll == 0);
Assert(_yScroll == 0);
Assert(_iliFirstVisible == 0);
Assert(_cpFirstVisible == 0);
Assert(_dyFirstVisible == 0);
_TEST_INVARIANT_
return TRUE;
}
//================================ Device drivers ===================================
/*
* CDisplayML::SetMainTargetDC(hdc, xWidthMax)
*
* @mfunc
* Sets a target device for this display and updates view
*
* @devnote
* Target device can't be a metafile (can get char width out of a
* metafile)
*
* @rdesc
* TRUE if success
*/
BOOL CDisplayML::SetMainTargetDC (
HDC hdc, //@parm Target DC, NULL for same as rendering device
LONG xWidthMax) //@parm Max line width (not used for screen)
{
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::SetMainTargetDC");
BOOL result = FALSE;
if ( SetTargetDC( hdc ) )
{
// This is here because this is what RE 1.0 did.
SetWordWrap(!(!hdc && (xWidthMax != 0)));
// if xWidthMax is greater than zero, then the caller is
// trying to set the maximum width of the window (for measuring,
// line breaking, etc.) However,in order to make our measuring
// algorithms more reasonable, we'll force the max size to
// be *at least* as wide as the width of a character.
// Note that xWidthMax = 0 means use the view rect width
_xWidthMax = (xWidthMax <= 0) ? 0 : max(DXtoLX(GetXWidthSys()),
xWidthMax);
// need to do a full recalc
// if it fails, it fails, the lines are left in a reasonable state
// no need to call WaitForRecalc() because UpdateView()
// start at position zero and we're always calced up to there
CDisplay::UpdateView();
// caret/selection has most likely moved
CTxtSelection *psel = _ped->GetSelNC();
if ( psel )
{
psel->UpdateCaret(FALSE);
}
result = TRUE;
}
return result;
}
// Useful for both main and printing devices. jonmat 6/08/1995
BOOL CDisplayML::SetTargetDC( HDC hdc )
{
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::SetTargetDC");
CDevDesc *pddTarget = NULL;
// don't allow metafiles to be set as the target device
if(hdc && GetDeviceCaps(hdc, TECHNOLOGY) == DT_METAFILE)
{
return FALSE;
}
if (hdc != NULL)
{
// Allocate the device first to see if we can. We
// don't want to change our state if this is going
// to fail.
pddTarget = new CDevDesc(_ped);
if (NULL == pddTarget)
{
// We couldn't so we are done.
return FALSE;
}
}
// remove any cached information for the old target device
if (_pddTarget != NULL)
{
delete _pddTarget;
_pddTarget = NULL;
}
if(hdc != NULL)
{
// Update the device because we have one.
_pddTarget = pddTarget;
_pddTarget->SetDC(hdc);
}
return TRUE;
}
//================================= Line recalc ==============================
/*
* CDisplayML::RecalcScrollBars(void)
*
* @mfunc
* Recalculate the scroll bars if the view has changed.
*
*
* @devnote There is a possibility of recursion here, so we
* need to protect ourselves.
*
* To visualize this, consider two types of characters, 'a' characters
* which are small in height and 'A' 's which are really tall, but the same
* width as an 'a'. So if I have
*
* a a A <nl>
* A <nl>
*
* I'll get a calced size that's basically 2 * heightof(A).
* With a scrollbar, this could wordwrap to
*
* a a <nl>
* A A <nl>
*
* which is of calced size heightof(A) + heightof(a); this is
* obviously less than the height in the first case.
*/
void CDisplayML::RecalcScrollBars()
{
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::RecalcScrollBars");
if(_fViewChanged)
{
_fViewChanged = FALSE;
UpdateScrollBar(SB_VERT, TRUE);
UpdateScrollBar(SB_HORZ, TRUE);
}
}
/*
* CDisplayML::RecalcLines(fWait)
*
* @mfunc
* Recalc all line breaks.
* This method does a lazy calc after the last visible line
* except for a bottomless control
*
* @rdesc
* TRUE if success
*/
BOOL CDisplayML::RecalcLines (
BOOL fWait) //@parm Recalc lines down to _cpWait/_yWait; then be lazy
{
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::RecalcLines");
LONG cliWait = cExtraBeforeLazy; // Extra lines before being lazy
BOOL fDone = TRUE;
BOOL fFirstInPara = TRUE;
CLine * pliNew = NULL;
LONG xWidth;
LONG yHeight = 0;
LONG cchText = _ped->GetTextLength();
BOOL fWaitingForFirstVisible = TRUE;
LONG yHeightView = _yHeightView;
LONG yHeightScrollOld = GetMaxYScroll();
LONG yHeightScrollNew;
Remove(0, -1, AF_KEEPMEM); // Remove all old lines from *this
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -