📄 runptr.cpp
字号:
/*
* CRunPtrBase::AdvanceCp(cch)
*
* @mfunc
* Advance this RunPtr by (signed) cch chars. If it lands on the
* end of a run, it automatically goes to the start of the next run
* (if one exists).
*
* @rdesc
* Count of characters actually moved
*/
LONG CRunPtrBase::AdvanceCp(
LONG cch) //@parm signed count of chars to move this RunPtr by
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::AdvanceCp");
if (!cch)
return cch;
DWORD cchSave = cch;
AssertSz(_iRun == 0 || (_iRun > 0 && _prgRun), "Invalid CRunPtr");
// No runs, so just update _ich as if there were a run
if(!IsValid())
{
_ich += cch;
// We have to assume that caller ensures that cch isn't too large,
// since a runless run ptr doesn't know the cch of the document.
// But we can check for too-negative values of cch:
if( (LONG)_ich < 0 )
{
cch = -cch + _ich; // Calculate actual cch moved
_ich = 0;
}
return cch;
}
if(cch < 0)
while(cch < 0)
{
// this cast to LONG is OK, since -cch will be positive
// (and we aren't likely to have 3 billion characters in
// a given run :-)
if( -cch <= (LONG)_ich )
{
_ich += cch; // Target is in this run
cch = 0;
break;
}
// otherwise, we need to go to the previous run
cch += _ich; // we moved by the number of
// characters left in the
// current run.
if(_iRun <= 0) // Already in first run
{
_iRun = 0;
_ich = 0; // Move to run beginning
break;
}
// move to previous run.
Assert(_prgRun->Elem(_iRun - 1));
_ich = _prgRun->Elem(--_iRun)->_cch;
}
else
while(cch > 0) // Move forward
{
const DWORD cchRun = _prgRun->Elem(_iRun)->_cch;
_ich += cch;
if(_ich < cchRun) // Target is in this run
{
cch = 0; // Signal countdown completed
break; // (if _ich = cchRun, go to
} // next run)
cch = _ich - cchRun; // Advance to next run
if(++_iRun >= Count()) // Ran past end, back up to
{ // end of story
--_iRun;
Assert(_iRun == Count() - 1);
Assert(_prgRun->Elem(_iRun)->_cch == cchRun);
_ich = cchRun;
break;
}
_ich = 0; // Start at new BOL
}
// NB! we check the invariant at the end to handle the case where
// we are updating the cp for a floating range (i.e., we know that
// the cp is invalid, so we fix it up). So we have to check for
// validity *after* the fixup.
_TEST_INVARIANT_
return cchSave - cch; // Return TRUE if countdown
} // completed
/*
* CRunPtrBase::CountRuns(&cRun, cchMax, &tp)
*
* @mfunc
* Count characters up to <p cRun> runs away or <p cchMax> chars,
* whichever comes first. If the target run and <p cchMax> are both
* beyond the corresponding end of the document, count up thru the
* closest run (0 or Count() - 1). The direction of counting is
* determined by the sign of <p cRun>. To count without being limited
* by <p cchMax>, set it equal to tomForward. An initial partial
* run counts as a run, i.e., if cRun > 0 and _ich < cch in current
* run or if cRun < 0 and _ich > 0, that counts as a run.
*
* @rdesc
* Return the signed cch counted and set <p cRun> equal to count of runs
* actually counted. If no runs are allocated, the text is treated as
* a single run. If <p cRun> = 0, -_ich is returned. If <p cRun> <gt> 0
* and this run ptr points to the end of the last run, no change is made
* and 0 is returned.
*
* @devnote
* The maximum count capability is included to be able to count units in
* a range. The <p tp> argument is needed for getting the text length
* when no runs exist and <p cRun> selects forward counting.
*/
LONG CRunPtrBase::CountRuns (
LONG & cRun, //@parm Count of runs to get cch for
LONG cchMax, //@parm Maximum char count
LONG cchText) const //@parm Text length of associated story
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::CountRuns");
_TEST_INVARIANT_
LONG cch;
if(!cRun) // Nothing to do
return 0;
// Study the following simple special single-run case to get a feel for
// how run and character counts work and how the initial run contributes
// to run counting
if(!_prgRun || !Count()) // No runs instantiated: act as a
{ // single run
if(cRun > 0) // Count forward
{
cch = cchText - _ich; // Partial count to end of run
cRun = 1; // No more than one run
}
else // Count backward
{
cch = -(LONG)_ich; // Partial count to start of run
cRun = -1; // No less than minus one run
}
if(!cch) // No partial run, so no runs
cRun = 0; // counted
return cch;
}
// General case for which runs are instantiated
LONG cb = _prgRun->Size(); // Size of text run element
LONG iDir;
DWORD iRun = _iRun; // Cache run index for current run
LONG j, k; // Handy integers
CTxtRun * pRun = GetRun(0); // Not NULL since runs exist
if(cRun < 0) // Try to count backward cRun runs
{
iDir = -1;
cb = -cb; // Byte count to previous element
cch = _ich; // Remaining cch in current run
if(cch) // If cch != 0, initial run counts
cRun++; // as a run: 1 less for for loop
cRun = max(cRun, -(LONG)iRun); // Don't let for loop overshoot
}
else
{ // Try to count forward cRun runs
Assert(cRun > 0);
iDir = 1;
cch = pRun->_cch - _ich; // Remaining cch in current run
if(cch) // If cch != 0, initial run counts
cRun--; // as a run: 1 less for for loop
k = Count() - iRun - 1; // k = # runs following current run
cRun = min(cRun, k); // Don't let for loop overshoot
}
k = cch; // Remember if initial cch != 0
for(j = cRun; j && cch < cchMax; j -= iDir)
{
pRun = (CTxtRun *)((BYTE *)pRun + cb); // Point at following run
cch += pRun->_cch; // Add in its count
}
if(k) // Initial partial run counts as
cRun += iDir; // a run
cRun -= j; // Discount any runs not counted
// if |cch| >= cchMax
return iDir*cch; // Return total cch bypassed
}
/*
* CRunPtrBase::FindRun (pcpMin, pcpMost, cpMin, cch)
*
* @mfunc
* Set *<p pcpMin> = closest run cpMin <lt>= range cpMin, and
* set *<p pcpMost> = closest run cpMost <gt>= range cpMost
*
* @devnote
* This routine plays a role analogous to CTxtRange::FindParagraph
* (pcpMin, pcpMost), but needs extra arguments since this run ptr does
* not know the range cp's. This run ptr is located at the range active
* end, which is determined by the range's signed length <p cch> in
* conjunction with <p cpMin>.
*/
void CRunPtrBase::FindRun (
LONG *pcpMin, //@parm Out parm for bounding-run cpMin
LONG *pcpMost, //@parm Out parm for bounding-run cpMost
LONG cpMin, //@parm Range cpMin
LONG cch, //@parm Range signed length
LONG cchText) const //@parm Story length
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::FindRun");
BOOL bAdvanceCp; // Controls AdvanceCp for pcpMost
CRunPtrBase rp((CRunPtrBase&)(*this)); // Clone this runptr to keep it const
rp.AdjustForward(); // Select forward run
if(pcpMin)
{ // If cch != 0, rp is sure to end up
bAdvanceCp = cch; // at cpMin, so pcpMost needs advance
if(cch > 0) // rp is at cpMost, so move it to
rp.AdvanceCp(-cch); // cpMin
*pcpMin = cpMin - rp._ich; // Subtract off offset in this run
}
else
bAdvanceCp = cch < 0; // Need to advance to get pcpMost
if(pcpMost)
{
*pcpMost = cchText; // Default plain-text value (or
if(rp.IsValid()) // single-line value)
{
cch = abs(cch);
if(bAdvanceCp) // Advance to cpMost = cpMin + cch,
rp.AdvanceCp(cch); // i.e., range's cpMost
if(cch)
rp.AdjustBackward(); // Since nondegenerate range
*pcpMost = cpMin + cch // Add remaining cch in run to range's
+ rp.GetCchLeft(); // cpMost
}
}
}
/*
* CRunPtrBase::AdjustBackward()
*
* @mfunc
* If the cp for this run ptr is at the "boundary" or edge between two
* runs, then make sure this run ptr points to the end of the first run.
*
* @comm
* This function does nothing unless this run ptr points to the beginning
* or the end of a run. This function may be needed in those cases
* because a cp at the beginning of a run is identical to the cp for the
* end of the previous run (if it exists), i.e., such an "edge" cp is
* ambiguous, and you may need to be sure that this run ptr points to the
* end of the first run.
*
* For example, consider a run that describes characters at cp's 0 to 10
* followed by a run that describes characters at cp's 11 through 12. For
* a cp of 11, it is possible for the run ptr to be either at the *end*
* of the first run or at the *beginning* of the second run.
*
* @rdesc nothing
*/
void CRunPtrBase::AdjustBackward()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::AdjustBackward");
_TEST_INVARIANT_
if( !_ich && PrevRun() ) // If at start of run that isn't
_ich = _prgRun->Elem(_iRun)->_cch; // the first, go to end of
} // previous run
/*
* CRunPtrBase::AdjustForward()
*
* @mfunc
* If the cp for this run ptr is at the "boundary" or edge between two
* runs, then make sure this run ptr points to the start of the second
* run.
*
* @rdesc
* nothing
*
* @xref
* <mf CRunPtrBase::AdjustBackward>
*/
void CRunPtrBase::AdjustForward()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::AdjustForward");
_TEST_INVARIANT_
if(!IsValid())
return;
CTxtRun *pRun = _prgRun->Elem(_iRun);
if(pRun->_cch == _ich) // If at end of run, go to start
NextRun(); // of next run (if it exists)
}
/*
* CRunPtrBase::IsValid()
*
* @mfunc
* indicates whether the current run pointer is in the empty
* or NULL states (i.e. "invalid" states).
*
* @rdesc
* TRUE is in the empty or NULL state, FALSE otherwise.
*/
BOOL CRunPtrBase::IsValid() const
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::IsValid");
return _prgRun && _prgRun->Count();
}
/*
* CRunPtrBase::SetToNull()
*
* @mfunc
* Sets all run pointer information to be NULL. This
* is designed to handle the response to clearing document runs
* such as when we convert from rich text to plain text.
*
* @rdesc
* VOID
*/
void CRunPtrBase::SetToNull()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::SetToNull");
_prgRun = NULL;
_iRun = 0;
_ich = 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -