You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
792 lines
20 KiB
792 lines
20 KiB
//
|
|
// anchor.cpp
|
|
//
|
|
// CACPWrap
|
|
//
|
|
|
|
#include "private.h"
|
|
#include "anchor.h"
|
|
#include "globals.h"
|
|
#include "normal.h"
|
|
#include "anchoref.h"
|
|
#include "txtcache.h"
|
|
|
|
DBG_ID_INSTANCE(CAnchor);
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _NormalizeAnchor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CACPWrap::_NormalizeAnchor(CAnchor *pa)
|
|
{
|
|
int iNew;
|
|
int iCurrent;
|
|
LONG acpNew;
|
|
CAnchor *paInto;
|
|
|
|
AssertPrivate(!pa->_fNormalized); // perf: we shouldn't normalize more than once
|
|
|
|
Perf_IncCounter(PERF_LAZY_NORM);
|
|
|
|
acpNew = Normalize(_ptsi, pa->GetIch());
|
|
|
|
pa->_fNormalized = TRUE;
|
|
|
|
if (acpNew == pa->GetIch())
|
|
return;
|
|
|
|
_Find(pa->GetIch(), &iCurrent);
|
|
paInto = _Find(acpNew, &iNew);
|
|
_Update(pa, acpNew, iCurrent, paInto, iNew);
|
|
|
|
if (paInto == NULL)
|
|
{
|
|
// the target anchor didn't get merged, pretend it did
|
|
paInto = pa;
|
|
}
|
|
else
|
|
{
|
|
// the target anchor got merged
|
|
// need to set the norm bit for the anchor it got merged into
|
|
paInto->_fNormalized = TRUE;
|
|
}
|
|
|
|
// we may have just skipped over anchors to the right of this one
|
|
// handle those guys now
|
|
while ((pa = _rgAnchors.Get(iCurrent)) != paInto)
|
|
{
|
|
Assert(pa->GetIch() < acpNew);
|
|
|
|
_Merge(paInto, pa);
|
|
}
|
|
|
|
_Dbg_AssertAnchors();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _DragAnchors
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CACPWrap::_DragAnchors(LONG acpFrom, LONG acpTo)
|
|
{
|
|
CAnchor *paFrom;
|
|
CAnchor *paTo;
|
|
int iFrom;
|
|
int iTo;
|
|
int i;
|
|
|
|
Assert(acpFrom > acpTo); // this method only handles dragging to the left
|
|
|
|
_Find(acpFrom, &iFrom);
|
|
if (iFrom < 0)
|
|
return; // nothing to drag
|
|
|
|
if (!_Find(acpTo, &iTo))
|
|
{
|
|
// if acpTo isn't occupied, drag to the next highest anchor
|
|
iTo++;
|
|
}
|
|
|
|
if (iTo > iFrom)
|
|
return; // nothing to drag, iTo and iFrom ref anchors to the left of acpTo
|
|
|
|
// merge all the anchors into the left most
|
|
paTo = _rgAnchors.Get(iTo);
|
|
|
|
for (i=iFrom; i>iTo; i--)
|
|
{
|
|
paFrom = _rgAnchors.Get(i);
|
|
Assert(paFrom->GetIch() > paTo->GetIch());
|
|
|
|
_Merge(paTo, paFrom);
|
|
}
|
|
|
|
// if the left most anchor isn't already positioned, do that now
|
|
paTo->SetACP(acpTo);
|
|
|
|
_Dbg_AssertAnchors();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Insert
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CACPWrap::_Insert(CAnchorRef *par, LONG ich)
|
|
{
|
|
int i;
|
|
CAnchor *pa;
|
|
|
|
if ((pa = _Find(ich, &i)) == NULL)
|
|
{
|
|
// this ich isn't in the array, allocate a new anchor
|
|
if ((pa = new CAnchor) == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// and insert it into the array
|
|
if (!_rgAnchors.Insert(i+1, 1))
|
|
{
|
|
delete pa;
|
|
return E_FAIL;
|
|
}
|
|
|
|
// update _lPendingDeltaIndex
|
|
if (i+1 <= _lPendingDeltaIndex)
|
|
{
|
|
_lPendingDeltaIndex++;
|
|
}
|
|
else
|
|
{
|
|
// new anchor is covered by a pending delta, so account for it
|
|
ich -= _lPendingDelta;
|
|
}
|
|
|
|
pa->_paw = this;
|
|
pa->_ich = ich;
|
|
|
|
_rgAnchors.Set(i+1, pa);
|
|
}
|
|
|
|
return _Insert(par, pa);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Insert
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CACPWrap::_Insert(CAnchorRef *par, CAnchor *pa)
|
|
{
|
|
par->_pa = pa;
|
|
par->_prev = NULL;
|
|
par->_next = pa->_parFirst;
|
|
if (pa->_parFirst)
|
|
{
|
|
Assert(pa->_parFirst->_prev == NULL);
|
|
pa->_parFirst->_prev = par;
|
|
}
|
|
pa->_parFirst = par;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Remove
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CACPWrap::_Remove(CAnchorRef *parIn)
|
|
{
|
|
CAnchor *pa = parIn->_pa;
|
|
|
|
if (parIn->_prev != NULL)
|
|
{
|
|
// make the previous guy point past parIn
|
|
parIn->_prev->_next = parIn->_next;
|
|
}
|
|
else
|
|
{
|
|
// this par is at the head of the list
|
|
pa->_parFirst = parIn->_next;
|
|
}
|
|
if (parIn->_next != NULL)
|
|
{
|
|
parIn->_next->_prev = parIn->_prev;
|
|
}
|
|
|
|
if (pa->_parFirst == NULL)
|
|
{
|
|
// no more refs, delete the anchor
|
|
_Delete(pa);
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Remove
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CACPWrap::_Delete(CAnchor *pa)
|
|
{
|
|
int i;
|
|
|
|
if (_Find(pa->GetIch(), &i) == pa)
|
|
{
|
|
_rgAnchors.Remove(i, 1);
|
|
|
|
// update _lPendingDeltaIndex
|
|
if (i < _lPendingDeltaIndex)
|
|
{
|
|
_lPendingDeltaIndex--;
|
|
}
|
|
}
|
|
|
|
delete pa;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Update
|
|
//
|
|
// Normalization is handled by the caller who needs to use Renormalize after
|
|
// calling this method.
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CACPWrap::_Update(const TS_TEXTCHANGE *pdctc)
|
|
{
|
|
int iStart;
|
|
int ichEndOrg;
|
|
int iEndOrg;
|
|
int iEndNew;
|
|
CAnchorRef *par;
|
|
CAnchorRef **ppar;
|
|
CAnchor *paStart;
|
|
CAnchor *paEnd;
|
|
CAnchor *paInto;
|
|
BOOL fExactEndMatch;
|
|
int dSize;
|
|
int iDeltaStart;
|
|
int ichStart = pdctc->acpStart;
|
|
int ichEndOld = pdctc->acpOldEnd;
|
|
int ichEndNew = pdctc->acpNewEnd;
|
|
|
|
// always invalidate our text cache
|
|
CProcessTextCache::Invalidate(_ptsi);
|
|
|
|
Assert(ichStart >= 0);
|
|
Assert(ichEndOld >= ichStart);
|
|
Assert(ichEndNew >= ichStart);
|
|
|
|
if (_rgAnchors.Count() == 0) // any anchors?
|
|
return;
|
|
|
|
dSize = ichEndNew - ichEndOld;
|
|
|
|
if (dSize == 0)
|
|
return;
|
|
|
|
paStart = _Find(ichStart, &iStart);
|
|
if (paStart == NULL)
|
|
{
|
|
iStart++; // return value was anchor closest to but less than ichStart
|
|
|
|
if (iStart >= _rgAnchors.Count())
|
|
{
|
|
// there aren't any anchors >= iStart, so bail, nothing to do
|
|
return;
|
|
}
|
|
|
|
paStart = _rgAnchors.Get(iStart);
|
|
|
|
if (iStart == 0 && paStart->GetIch() > ichEndOld)
|
|
{
|
|
// the delta is to the left of all the ranges
|
|
_AdjustIchs(0, dSize);
|
|
_Dbg_AssertAnchors();
|
|
return;
|
|
}
|
|
}
|
|
paEnd = _Find(ichEndOld, &iEndOrg);
|
|
if (paEnd == NULL)
|
|
{
|
|
Assert(iEndOrg >= 0); // there should be at least one anchor <= ichEndOld
|
|
// if we made it this far
|
|
fExactEndMatch = FALSE;
|
|
paEnd = _rgAnchors.Get(iEndOrg);
|
|
}
|
|
else
|
|
{
|
|
fExactEndMatch = TRUE;
|
|
}
|
|
|
|
if (dSize < 0)
|
|
{
|
|
// if dSize < 0, then gotta merge all the anchors between old, new pos
|
|
if (paEnd->GetIch() > ichEndNew) // are there any anchors between old, new pos?
|
|
{
|
|
// track change history
|
|
|
|
// drag the paEnd to its new position
|
|
ichEndOrg = paEnd->GetIch();
|
|
paInto = _Find(ichEndNew, &iEndNew);
|
|
|
|
_TrackDelHistory(iEndOrg, fExactEndMatch, iEndNew, paInto != NULL);
|
|
|
|
iEndNew = _Update(paEnd, ichEndNew, iEndOrg, paInto, iEndNew);
|
|
_Find(ichEndOrg, &iEndOrg); // might be a prev anchor if paEnd got deleted in Update!
|
|
|
|
while (iEndOrg > iEndNew)
|
|
{
|
|
// don't try to cache pointers, _rgAnchors might get realloc'd
|
|
// during the _Merge!
|
|
_Merge(_rgAnchors.Get(iEndNew), _rgAnchors.Get(iEndOrg));
|
|
iEndOrg--;
|
|
}
|
|
|
|
iEndOrg = iEndNew; // for below
|
|
}
|
|
// iEndOrg was updated so let's start from next.
|
|
iDeltaStart = iEndOrg + 1;
|
|
}
|
|
else // dSize > 0
|
|
{
|
|
// iEndOrg will be < iStart when the delta fits entirely between two existing anchors
|
|
Assert(iEndOrg >= iStart || (iEndOrg == iStart - 1));
|
|
iDeltaStart = (iEndOrg <= iStart) ? iEndOrg + 1 : iEndOrg;
|
|
}
|
|
|
|
// update all the following anchors
|
|
_AdjustIchs(iDeltaStart, dSize);
|
|
|
|
if (dSize > 0 && paStart == paEnd)
|
|
{
|
|
// Need to account for gravity in the case of an insert to a single anchor pos.
|
|
// In practice, this means that we have to handle the anchor refs at the insertion
|
|
// anchor individually -- some will want to shift left, others right.
|
|
|
|
ppar = &paStart->_parFirst;
|
|
while (par = *ppar)
|
|
{
|
|
if (par->_fForwardGravity)
|
|
{
|
|
// remove the ref from this anchor
|
|
*ppar = par->_next;
|
|
if (par->_next != NULL)
|
|
{
|
|
par->_next->_prev = par->_prev;
|
|
}
|
|
|
|
// shove the ref over, it needs to be moved. Insert adds to the head of the list
|
|
// so this call is fast and in constant time.
|
|
_Insert(par, paStart->GetIch() + dSize);
|
|
}
|
|
else
|
|
{
|
|
ppar = &par->_next;
|
|
}
|
|
}
|
|
|
|
if (paStart->_parFirst == NULL)
|
|
{
|
|
// we cleaned this guy out!
|
|
Assert(_rgAnchors.Get(iEndOrg) == paStart);
|
|
_rgAnchors.Remove(iEndOrg, 1);
|
|
delete paStart;
|
|
// update _lPendingDeltaIndex
|
|
if (iEndOrg < _lPendingDeltaIndex)
|
|
{
|
|
_lPendingDeltaIndex--;
|
|
}
|
|
}
|
|
}
|
|
|
|
_Dbg_AssertAnchors();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _TrackDelHistory
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CACPWrap::_TrackDelHistory(int iEndOrg, BOOL fExactEndOrgMatch, int iEndNew, BOOL fExactEndNewMatch)
|
|
{
|
|
CAnchorRef *par;
|
|
int i;
|
|
|
|
Assert(iEndOrg >= iEndNew);
|
|
|
|
if (fExactEndOrgMatch)
|
|
{
|
|
// all the anchoref's at iEndOrg get a preceding del
|
|
for (par = _rgAnchors.Get(iEndOrg)->_parFirst; par != NULL; par = par->_next)
|
|
{
|
|
par->_dwHistory |= TS_CH_PRECEDING_DEL;
|
|
}
|
|
|
|
// if iEndOrg == iEndNew on entry, that's everything
|
|
if (iEndOrg == iEndNew)
|
|
return;
|
|
|
|
iEndOrg--; // exclude this anchor from loop below
|
|
}
|
|
|
|
if (fExactEndNewMatch)
|
|
{
|
|
// all the anchoref's at iEndNew get a following del
|
|
for (par = _rgAnchors.Get(iEndNew)->_parFirst; par != NULL; par = par->_next)
|
|
{
|
|
par->_dwHistory |= TS_CH_FOLLOWING_DEL;
|
|
}
|
|
}
|
|
// exclude leftmost anchor from loop below
|
|
// either we just handled it in the loop above or !fExactEndNewMatch
|
|
// in which case it lies to the left of the affected anchors
|
|
iEndNew++;
|
|
|
|
// the the anchoref's in between get both dels
|
|
for (i=iEndNew; i<=iEndOrg; i++)
|
|
{
|
|
for (par = _rgAnchors.Get(i)->_parFirst; par != NULL; par = par->_next)
|
|
{
|
|
par->_dwHistory = (TS_CH_PRECEDING_DEL | TS_CH_FOLLOWING_DEL);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Update
|
|
//
|
|
// piInto gets loaded with the index of the anchor after the update. If the
|
|
// index changed, pa is now bogus and a new pointer should be grabbed using
|
|
// the index.
|
|
//----------------------------------------------------------------------------
|
|
|
|
int CACPWrap::_Update(CAnchor *pa, int ichNew, int iOrg, CAnchor *paInto, int iInto)
|
|
{
|
|
int i;
|
|
|
|
Assert(pa->GetIch() != ichNew); // we are so hosed if this happens
|
|
|
|
i = iInto;
|
|
|
|
if (paInto != NULL)
|
|
{
|
|
// gotta do a merge
|
|
_Merge(paInto, pa);
|
|
}
|
|
else
|
|
{
|
|
if (iInto != iOrg)
|
|
{
|
|
// move the entry in the array to the new position
|
|
i = _rgAnchors.Move(iInto+1, iOrg);
|
|
}
|
|
|
|
// did we cross _lPendingDeltaIndex?
|
|
if (i > _lPendingDeltaIndex)
|
|
{
|
|
// new position is in the pending range, adjust ich
|
|
ichNew -= _lPendingDelta;
|
|
}
|
|
else if (iOrg >= _lPendingDeltaIndex && i <= _lPendingDeltaIndex) // shifting an anchor out of the pending range
|
|
{
|
|
// one less anchor in the pending range
|
|
_lPendingDeltaIndex++;
|
|
}
|
|
|
|
// change the ich
|
|
_rgAnchors.Get(i)->_ich = ichNew;
|
|
}
|
|
|
|
_Dbg_AssertAnchors();
|
|
|
|
return i;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Renormalize
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CACPWrap::_Renormalize(int ichStart, int ichEnd)
|
|
{
|
|
CAnchor *pa;
|
|
int iCurrent;
|
|
int iEnd;
|
|
BOOL fExactEndMatch;
|
|
|
|
Perf_IncCounter(PERF_RENORMALIZE_COUNTER);
|
|
|
|
if (_rgAnchors.Count() == 0)
|
|
return;
|
|
|
|
fExactEndMatch = (_Find(ichEnd, &iEnd) != NULL);
|
|
if (iEnd < 0)
|
|
return;
|
|
|
|
if (ichStart == ichEnd)
|
|
{
|
|
if (!fExactEndMatch)
|
|
return;
|
|
// this can happen on deletions
|
|
iCurrent = iEnd;
|
|
}
|
|
else if (_Find(ichStart, &iCurrent) == NULL)
|
|
{
|
|
iCurrent++; // we don't care about anchors < ichStart
|
|
}
|
|
Assert(iCurrent >= 0);
|
|
|
|
for (;iCurrent <= iEnd; iCurrent++)
|
|
{
|
|
pa = _rgAnchors.Get(iCurrent);
|
|
|
|
Assert(pa->GetIch() >= 0);
|
|
Assert(pa->GetIch() >= ichStart);
|
|
Assert(pa->GetIch() <= ichEnd);
|
|
|
|
// we'll do the real work only when we have too...
|
|
pa->_fNormalized = FALSE;
|
|
}
|
|
|
|
_Dbg_AssertAnchors();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Find
|
|
//
|
|
// If piOut != NULL then it is set to the index where ich was found, or the
|
|
// index of the next lower ich if ich isn't in the array.
|
|
// If there is no element in the array with a lower ich, returns offset -1.
|
|
//----------------------------------------------------------------------------
|
|
|
|
CAnchor *CACPWrap::_Find(int ich, int *piOut)
|
|
{
|
|
CAnchor *paMatch;
|
|
int iMin;
|
|
int iMax;
|
|
int iMid;
|
|
LONG lPendingDeltaIch;
|
|
|
|
iMin = 0;
|
|
iMax = _rgAnchors.Count();
|
|
|
|
// adjust search for pending delta range
|
|
// values aren't consistent across the range boundary
|
|
if (_lPendingDelta != 0 && _lPendingDeltaIndex < _rgAnchors.Count())
|
|
{
|
|
lPendingDeltaIch = _rgAnchors.Get(_lPendingDeltaIndex)->_ich;
|
|
|
|
if (ich < lPendingDeltaIch + _lPendingDelta)
|
|
{
|
|
iMax = _lPendingDeltaIndex;
|
|
}
|
|
else if (ich > lPendingDeltaIch + _lPendingDelta)
|
|
{
|
|
iMin = _lPendingDeltaIndex+1;
|
|
ich -= _lPendingDelta; // make the search below work
|
|
}
|
|
else
|
|
{
|
|
iMid = _lPendingDeltaIndex;
|
|
paMatch = _rgAnchors.Get(_lPendingDeltaIndex);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
paMatch = _FindInnerLoop(ich, iMin, iMax, &iMid);
|
|
|
|
Exit:
|
|
if (piOut != NULL)
|
|
{
|
|
if (paMatch == NULL && iMid >= 0)
|
|
{
|
|
// couldn't find a match, return the next lowest ich
|
|
Assert(iMid == 0 || iMid == _lPendingDeltaIndex || _rgAnchors.Get(iMid-1)->_ich < ich);
|
|
if (_rgAnchors.Get(iMid)->_ich > ich)
|
|
{
|
|
iMid--;
|
|
}
|
|
}
|
|
*piOut = iMid;
|
|
}
|
|
|
|
return paMatch;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _FindInnerLoop
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CAnchor *CACPWrap::_FindInnerLoop(LONG acp, int iMin, int iMax, int *piIndex)
|
|
{
|
|
CAnchor *pa;
|
|
CAnchor *paMatch;
|
|
int iMid;
|
|
|
|
paMatch = NULL;
|
|
iMid = iMin - 1;
|
|
|
|
while (iMin < iMax)
|
|
{
|
|
iMid = (iMin + iMax) / 2;
|
|
pa = _rgAnchors.Get(iMid);
|
|
Assert(pa != NULL);
|
|
|
|
if (acp < pa->_ich)
|
|
{
|
|
iMax = iMid;
|
|
}
|
|
else if (acp > pa->_ich)
|
|
{
|
|
iMin = iMid + 1;
|
|
}
|
|
else // acp == pa->_ich
|
|
{
|
|
paMatch = pa;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*piIndex = iMid;
|
|
|
|
return paMatch;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Merge
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CACPWrap::_Merge(CAnchor *paInto, CAnchor *paFrom)
|
|
{
|
|
CAnchorRef *par;
|
|
|
|
Assert(paInto != paFrom); // very bad!
|
|
Assert(paInto->_parFirst != NULL); // should never have an anchor w/o any refs
|
|
|
|
if (par = paFrom->_parFirst)
|
|
{
|
|
// update the anchor for the paFrom refs
|
|
|
|
while (TRUE)
|
|
{
|
|
par->_pa = paInto;
|
|
|
|
if (par->_next == NULL)
|
|
break;
|
|
|
|
par = par->_next;
|
|
}
|
|
|
|
// now par is the last ref in paFrom
|
|
// shove all the refs in paFrom into paInto
|
|
|
|
if (par != NULL)
|
|
{
|
|
Assert(par->_next == NULL);
|
|
par->_next = paInto->_parFirst;
|
|
Assert(paInto->_parFirst->_prev == NULL);
|
|
paInto->_parFirst->_prev = par;
|
|
paInto->_parFirst = paFrom->_parFirst;
|
|
}
|
|
}
|
|
|
|
// and free the parFrom
|
|
_Delete(paFrom);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Dbg_AssertAnchors
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef DEBUG
|
|
|
|
void CACPWrap::_Dbg_AssertAnchors()
|
|
{
|
|
int i;
|
|
int ichLast;
|
|
CAnchor *pa;
|
|
|
|
ichLast = -1;
|
|
|
|
// assert that the anchor array has ascending ich's
|
|
for (i=0; i<_rgAnchors.Count(); i++)
|
|
{
|
|
pa = _rgAnchors.Get(i);
|
|
Assert(ichLast < pa->GetIch());
|
|
ichLast = pa->GetIch();
|
|
}
|
|
}
|
|
|
|
#endif // DEBUG
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _AdjustIchs
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CACPWrap::_AdjustIchs(int iFirst, int dSize)
|
|
{
|
|
CAnchor **ppaFirst;
|
|
CAnchor **ppaLast;
|
|
LONG dSizeAdjust;
|
|
int iLastAnchor;
|
|
|
|
Assert(dSize != 0);
|
|
|
|
iLastAnchor = _rgAnchors.Count()-1;
|
|
|
|
if (iFirst > iLastAnchor)
|
|
return;
|
|
|
|
if (_lPendingDelta == 0 || // no delta pending
|
|
_lPendingDeltaIndex > iLastAnchor) // old pending anchors got deleted before update
|
|
{
|
|
// no pending delta, start a new one
|
|
_lPendingDeltaIndex = iFirst;
|
|
_lPendingDelta = dSize;
|
|
return;
|
|
}
|
|
|
|
if (max(iFirst, _lPendingDeltaIndex) - min(iFirst, _lPendingDeltaIndex)
|
|
> iLastAnchor / 2)
|
|
{
|
|
// adjust points are far apart, update the tail of the range
|
|
if (iFirst > _lPendingDeltaIndex)
|
|
{
|
|
ppaFirst = _rgAnchors.GetPtr(iFirst);
|
|
dSizeAdjust = dSize;
|
|
}
|
|
else
|
|
{
|
|
ppaFirst = _rgAnchors.GetPtr(_lPendingDeltaIndex);
|
|
_lPendingDeltaIndex = iFirst;
|
|
dSizeAdjust = _lPendingDelta;
|
|
_lPendingDelta = dSize;
|
|
}
|
|
ppaLast = _rgAnchors.GetPtr(iLastAnchor);
|
|
}
|
|
else
|
|
{
|
|
// adjust points are close, update the head of the range
|
|
if (iFirst > _lPendingDeltaIndex)
|
|
{
|
|
ppaFirst = _rgAnchors.GetPtr(_lPendingDeltaIndex);
|
|
_lPendingDeltaIndex = iFirst;
|
|
ppaLast = _rgAnchors.GetPtr(iFirst - 1);
|
|
dSizeAdjust = _lPendingDelta;
|
|
}
|
|
else
|
|
{
|
|
ppaFirst = _rgAnchors.GetPtr(iFirst);
|
|
ppaLast = _rgAnchors.GetPtr(_lPendingDeltaIndex-1);
|
|
dSizeAdjust = dSize;
|
|
}
|
|
_lPendingDelta += dSize;
|
|
}
|
|
|
|
// do the real work
|
|
while (ppaFirst <= ppaLast)
|
|
{
|
|
(*ppaFirst++)->_ich += dSizeAdjust;
|
|
}
|
|
}
|