📄 dispml.cpp
字号:
_yCalcMax = 0; // Set both maxes to start of text
_cpCalcMax = 0;
// Don't stop at the bottom of the view if we're bottomless and we're active.
if (!_ped->TxGetAutoSize() && IsActive())
{
// be lazy - don't bother going past visible portion
_cpWait = -1;
_yWait = -1;
fWait = TRUE;
}
// Init measurer at cp = 0
CMeasurer me(this);
// The following loop generates new lines
while( (LONG)me.GetCp() < cchText )
{
// Add one new line
pliNew = Add(1, NULL);
if (!pliNew)
{
_ped->GetCallMgr()->SetOutOfMemory();
TRACEWARNSZ("Out of memory Recalc'ing lines");
goto err;
}
// stuff text into new line
UINT uiFlags = MEASURE_BREAKATWORD | (fFirstInPara ? MEASURE_FIRSTINPARA : 0);
Tracef(TRCSEVINFO, "Measuring new line from cp = %d", me.GetCp());
if(!pliNew->Measure(me, -1, uiFlags))
{
Assert(FALSE);
goto err;
}
fFirstInPara = pliNew->_cchEOP;
yHeight += pliNew->GetHeight();
_cpCalcMax = me.GetCp();
if (fWait)
{
// Do we want to do a background recalc? - the answer is yes if
// three things are true: (1) We have recalc'd beyond the old first
// visible character, (2) We have recalc'd beyond the visible
// portion of the screen and (3) we have gone beyond the next
// cExtraBeforeLazy lines to make page down go faster.
if (fWaitingForFirstVisible)
{
if ((LONG) me.GetCp() > _cpFirstVisible)
{
_yWait = yHeight + yHeightView;
fWaitingForFirstVisible = FALSE;
}
}
else if ((yHeight > _yWait) && cliWait-- <= 0)
{
fDone = FALSE;
break;
}
}
}
_yCalcMax = yHeight;
_fRecalcDone = fDone;
_fNeedRecalc = FALSE;
yHeightScrollNew = CalcScrollHeight(yHeight);
if((fDone
&& ((yHeight != _yHeight) || (yHeightScrollNew != yHeightScrollOld)))
|| (yHeightScrollNew > yHeightScrollOld))
{
_yHeight = yHeight;
_fViewChanged = TRUE;
}
xWidth = CalcDisplayWidth();
if((fDone && (xWidth != _xWidth)) || (xWidth > _xWidth))
{
_xWidth = xWidth;
_fViewChanged = TRUE;
}
Tracef(TRCSEVINFO, "CDisplayML::RecalcLine() - Done. Recalced down to line #%d", Count());
if(!fDone) // if not done, do rest in background
{
fDone = StartBackgroundRecalc();
}
if (fDone)
{
_yWait = -1;
_cpWait = -1;
CheckLineArray();
_fLineRecalcErr = FALSE;
}
#ifdef DEBUG
if( 1 )
{
_TEST_INVARIANT_
}
#endif // DEBUG
return TRUE;
err:
TRACEERRORSZ("CDisplayML::RecalcLines() failed");
if(!_fLineRecalcErr)
{
_cpCalcMax = me.GetCp();
_yCalcMax = yHeight;
_fLineRecalcErr = TRUE;
_ped->GetCallMgr()->SetOutOfMemory();
_fLineRecalcErr = FALSE; // fix up CArray & bail
}
return FALSE;
}
/*
* CDisplayML::RecalcLines(tpFirst, cchOld, cchNew, fBackground, fWait, pled)
*
* @mfunc
* Recompute line breaks after text modification
*
* @rdesc
* TRUE if success
*/
BOOL CDisplayML::RecalcLines (
const CRchTxtPtr &tpFirst, //@parm Where change happened
LONG cchOld, //@parm Count of chars deleted
LONG cchNew, //@parm Count of chars added
BOOL fBackground, //@parm This method called as background process
BOOL fWait, //@parm Recalc lines down to _cpWait/_yWait; then be lazy
CLed *pled) //@parm Records & returns impact of edit on lines (can be NULL)
{
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CDisplayML::RecalcLines");
LONG cchEdit;
LONG cchSkip;
LONG cli = 0;
LONG cliBackedUp = 0;
LONG cliWait = cExtraBeforeLazy;
BOOL fDone = TRUE;
BOOL fFirstInPara = TRUE;
LONG ili;
CLed led;
LONG lT; // long Temporary
CLine * pliNew;
CLine * pli = NULL;
CLinePtr rpOld(this);
LONG xWidth;
LONG yHeight;
LONG yHeightPrev = 0;
LONG cchText = _ped->GetTextLength();
UINT uiFlags;
BOOL fReplaceResult;
BOOL fTryForMatch = TRUE;
LONG yHeightScrollOld = GetMaxYScroll();
LONG yHeightScrollNew;
WORD wNumber = 0;
if (!pled)
pled = &led;
#ifdef DEBUG
LONG cp;
if(tpFirst.GetCp() > _cpCalcMax)
Tracef(TRCSEVERR, "tpFirst %ld, _cpCalcMax %ld", tpFirst.GetCp(), _cpCalcMax);
AssertSz(tpFirst.GetCp() <= _cpCalcMax,
"CDisplayML::RecalcLines Caller didn't setup RecalcLines()");
AssertSz(!(fWait && fBackground),
"CDisplayML::RecalcLines wait and background both true");
AssertSz(!(fWait && (-1 == _cpWait) && (-1 == _yWait)),
"CDisplayML::RecalcLines background recalc parms invalid");
#endif
// We will not use background recalc if this is already a background recalc,
// or if the control is not active or if this is an auto sized control.
if(!IsActive() || _ped->TxGetAutoSize())
{
fWait = FALSE;
}
// Init line pointer on old CLineArray and backup to start of line
rpOld.RpSetCp(tpFirst.GetCp(), FALSE);
cchSkip = rpOld.RpGetIch();
rpOld.RpAdvanceCp(-cchSkip); // Point rp at 1st char in line
ili = rpOld; // Save line # at change for
if(ili && (IsInOutlineView() || // numbering. Back up if not
tpFirst.GetPF()->IsListNumbered())) // first number in list or if
{ // in OutlineView (Outline
ili--; // symbol may change)
}
// Back up at least one line in case we can now fit more on it
// If on a line border, e.g., just inserted an EOP, backup 2; else 1
lT = !cchSkip + 1;
while(lT-- > 0 && rpOld > 0 && (!rpOld[-1]._cchEOP || ili < rpOld))
{
cliBackedUp++;
rpOld--;
cchSkip += rpOld->_cch;
}
// Init measurer at tpFirst
CMeasurer me(this, tpFirst);
me.Advance(-cchSkip); // Point at start of text to measure
cchEdit = cchNew + cchSkip; // Number of chars affected by edit
me.SetNumber(rpOld.GetNumber()); // Initialize list number
// Determine whether we're on first line of paragraph
if(rpOld > 0)
{
fFirstInPara = rpOld[-1]._cchEOP;
AssertSz(me.GetCp() >= _cpCalcMax ||
!(rpOld->_bFlags & fliFirstInPara) == !fFirstInPara,
"fHasEOL & fFirstInPara disagree");
}
yHeight = YposFromLine(rpOld);
// Update first-affected and pre-edit-match lines in pled
pled->_iliFirst = rpOld;
pled->_cpFirst = pled->_cpMatchOld = me.GetCp();
pled->_yFirst = pled->_yMatchOld = yHeight;
AssertSz(pled->_yFirst >= 0, "CDisplayML::RecalcLines _yFirst < 0");
Tracef(TRCSEVINFO, "Start recalcing from line #%d, cp=%d", pled->_iliFirst, pled->_cpFirst);
// If we are past the requested area to recalc and background recalc is
// allowed, then just go directly to background recalc. If there is no
// height, we just go a head and calculate some lines anyway. This
// prevents any weird background recalcs from occuring when it is
// unnecessary to go into background recalc.
if(fWait && _yWait > 0 && yHeight > _yWait && (LONG)me.GetCp() > _cpWait)
{
// Reset the recalc max to where we would have started the recalc
_cpCalcMax = (LONG) me.GetCp();
// Remove all old lines from here on
rpOld.Remove(-1, AF_KEEPMEM);
// Start up the background recalc
StartBackgroundRecalc();
pled->SetMax(this);
return TRUE;
}
// In case of error, set both maxes to where we are now
_yCalcMax = yHeight;
_cpCalcMax = me.GetCp();
// Create or reuse new CArray of lines
if (!_prgliNew && !(_prgliNew = new CLineArray()))
{
TRACEWARNSZ("CDisplayML::RecalcLines unable to alloc CLineArray");
goto errspace;
}
_prgliNew->Clear(AF_KEEPMEM);
pliNew = NULL;
// The following loop generates new lines for each line we backed
// up over and for lines directly affected by edit
while(cchEdit > 0)
{
pliNew = _prgliNew->Add(1, NULL); // Add one new line
if (!pliNew)
{
TRACEWARNSZ("CDisplayML::RecalcLines unable to alloc additional CLine in CLineArray");
goto errspace;
}
// Can't reuse old results if we've got a target device
// For SPEED: it'd be nice to cache a few values when we do have a
// target device - a good caching heuristic could halve the measuring
const LONG cchNonWhite = rpOld.IsValid() ?
(rpOld->_cch - rpOld->_cchWhite ) : 0;
if(cchSkip > 0 && cchSkip >= cchNonWhite && !IsInOutlineView() &&
(NULL == _pddTarget || !_pddTarget->IsValid()))
{
me.NewLine(*rpOld); // Don't remeasure anything we
me.Advance(cchNonWhite); // already have valid info on
me._cch = cchNonWhite;
me._xWidth = rpOld->_xWidth;
// clear out any of the old flags _except_ for tabs and OLE or
// of screen. Note that this algorithm is somewhat bogus; there
// is no guarantee that the line still matches the flag state.
// However,those flags are simply 'hints'--i.e. the line _may_
// be in that state. Keeping those flags set will result
// in a minor slowdown for rendering the line.
me._bFlags &= (fliHasTabs | fliHasOle | fliUseOffScreenDC);
if (rpOld->_bFlags & fliOffScreenOnce)
me._bFlags &= ~fliUseOffScreenDC;
me._cchEOP = 0;
}
else
me.NewLine(fFirstInPara); // Remeasure from start of line
// Stuff text into new line
uiFlags = MEASURE_BREAKATWORD | (fFirstInPara ? MEASURE_FIRSTINPARA : 0);
Tracef(TRCSEVINFO, "Measuring new line from cp = %d", me.GetCp());
if (!me.MeasureLine(-1, -1, uiFlags))
{
Assert(FALSE);
goto err;
}
*pliNew = me;
if( pliNew->_cch == 0 )
{
TRACEWARNSZ(
"CDisplayML::RecalcLines measure returned a zero length line");
goto errspace;
}
// Calculate on what line the edit started. We do this because
// we want to render the first edited line off screen so if
// the line is being edited via the key board we don't clip
// any characters.
if(cchSkip > 0)
{
// Check whether we backed up and the line we are examining
// changed at all. Even if it didn't change in outline view
// have to redraw in case outline symbol changes
if(cliBackedUp != 0 && cchSkip >= (LONG)pliNew->_cch
&& pliNew->IsEqual(*rpOld) && !IsInOutlineView())
{
// perfect match, this line was not the first edited.
Tracef(TRCSEVINFO, "New line matched old line #%d", (LONG)rpOld);
cchSkip -= rpOld->_cch;
// Update first affected line and match in pled
pled->_iliFirst++;
pled->_cpFirst += rpOld->_cch;
pled->_cpMatchOld += rpOld->_cch;
pled->_yFirst += rpOld->GetHeight();
AssertSz(pled->_yFirst >= 0, "CDisplayML::RecalcLines _yFirst < 0");
pled->_yMatchOld += rpOld->GetHeight();
cliBackedUp--;
_prgliNew->Clear(AF_KEEPMEM); // Discard new line
if (!(rpOld++)) // Next line
cchSkip = 0;
}
else // No match in the line, so
cchSkip = 0; // this line is the first to
} // be edited
Assert( !!(pliNew->_bFlags & fliFirstInPara) == !!fFirstInPara);
fFirstInPara = pliNew->_cchEOP;
yHeightPrev = yHeight;
yHeight += pliNew->GetHeight();
cchEdit -= pliNew->_cch;
if(fBackground && (GetTickCount() >= _dwBgndTickMax))
{
fDone = FALSE; // took too long, stop for now
goto no_match;
}
if(fWait && yHeight > _yWait && (LONG) me.GetCp() > _cpWait
&& cliWait-- <= 0)
{
// Not really done, just past region we're waiting for
// so let background recalc take it from here
fDone = FALSE;
goto no_match;
}
} // while(cchEdit > 0) { }
Tracef(TRCSEVINFO, "Done recalcing edited text. Created %d new lines", _prgliNew->Count());
// Edit lines have been exhausted. Continue breaking lines,
// but try to match new & old breaks
wNumber = me._wNumber;
while( (LONG) me.GetCp() < cchText )
{
// Assume there are no matches to try for
BOOL frpOldValid = FALSE;
// If we run out of runs, then no match is possible. Therefore,
// we only try for a match as long as we have runs.
if (fTryForMatch)
{
// We are trying for a match so assume that there
// is a match after all
frpOldValid = TRUE;
// Look for match in old line break CArray
lT = me.GetCp() - cchNew + cchOld;
while (rpOld.IsValid() && pled->_cpMatchOld < lT)
{
pled->_yMatchOld += rpOld->GetHeight();
pled->_cpMatchOld += rpOld->_cch;
if( !rpOld.NextRun() )
{
// No more line array entries so we can give up on
// trying to match for good.
fTryForMatch = FALSE;
frpOldValid = FALSE;
break;
}
}
}
// If perfect match, stop
if (frpOldValid && rpOld.IsValid() && pled->_cpMatchOld == lT &&
rpOld->_cch && (!wNumber || me._wNumber < wNumber))
{
Tracef(TRCSEVINFO, "Found match with old line #%d", rpOld.GetLineIndex());
// Update fliFirstInPara flag in 1st old line that matches. Note
// that if the new array doesn't have any lines, we have to look
// into the line array preceding the current change.
rpOld->_bFlags |= fliFirstInPara;
if( _prgliNew->Count() > 0 )
{
if(!(_prgliNew->Elem(_prgliNew->Count() - 1)->_cchEOP))
rpOld->_bFlags &= ~fliFirstInPara;
}
else if( rpOld > pled->_iliFirst && pled->_iliFirst )
{
if(!rpOld[pled->_iliFirst - rpOld -1]._cchEOP )
rpOld->_bFlags &= ~fliFirstInPara;
}
pled->_iliMatchOld = rpOld;
// Replace old lines by new ones
lT = rpOld - pled->_iliFirst;
rpOld = pled->_iliFirst;
if(!rpOld.Replace (lT, _prgliNew))
{
TRACEWARNSZ("CDisplayML::RecalcLines unable to alloc additional CLines in rpOld");
goto errspace;
}
frpOldValid = rpOld.ChgRun(_prgliNew->Count());
_prgliNew->Clear(AF_KEEPMEM); // Clear aux array
// Remember information about match after editing
Assert((cp = rpOld.GetCp()) == (LONG) me.GetCp());
pled->_yMatchNew = yHeight;
pled->_yMatchNewTop = yHeightPrev;
pled->_iliMatchNew = rpOld;
pled->_cpMatchNew = me.GetCp();
// Compute height and cp after all matches
_cpCalcMax = me.GetCp();
if( frpOldValid && rpOld.IsValid() )
{
do
{
yHeight += rpOld->GetHeight();
_cpCalcMax += rpOld->_cch;
}
while( rpOld.NextRun() );
}
// Make sure _cpCalcMax is sane after the above update
AssertSz(_cpCalcMax <= cchText,
"CDisplayML::RecalcLines match extends beyond EOF");
// We stop calculating here.Note that if _cpCalcMax < size
// of text, this means a background recalc is in progress.
// We will let that background recalc get the arrays
// fully in sync.
AssertSz(((_cpCalcMax == cchText) || _fBgndRecalc),
"CDisplayML::Match less but no background recalc");
if (_cpCalcMax != cchText)
{
// This is going to be finished by the background recalc
// so set the done flag appropriately.
fDone = FALSE;
}
goto match;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -