Leaked source code of windows server 2003
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

//
// 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;
}
}