|
|
//
// spans.cpp
//
// CSpanSet
//
#include "private.h"
#include "spans.h"
#include "immxutil.h"
DBG_ID_INSTANCE(CSpanSet);
//+---------------------------------------------------------------------------
//
// _InsertNewSpan
//
//----------------------------------------------------------------------------
SPAN *CSpanSet::_InsertNewSpan(int iIndex) { if (!_rgSpans.Insert(iIndex, 1)) return NULL;
return _rgSpans.GetPtr(iIndex); }
//+---------------------------------------------------------------------------
//
// Add
//
//----------------------------------------------------------------------------
void CSpanSet::Add(DWORD dwFlags, IAnchor *paStart, IAnchor *paEnd, AnchorOwnership ao) { int iStart; int iEnd; SPAN *psStart; SPAN *psEnd; IAnchor *paLowerBound; IAnchor *paUpperBound; IAnchor *paClone; BOOL fReleaseStart; BOOL fReleaseEnd;
fReleaseStart = fReleaseEnd = (ao == OWN_ANCHORS);
if (_AllSpansCovered()) { // if we already cover the entire doc, nothing to do
goto ExitRelease; }
if (paStart == NULL) { Assert(paEnd == NULL);
// NULL, NULL means "the whole doc"
// so this new span automatically eats all pre-existing ones
dwFlags = 0; // don't accept corrections for the entire doc
iStart = 0; iEnd = _rgSpans.Count();
if (iEnd == 0) { if ((psStart = _InsertNewSpan(0)) == NULL) return; // out-of-memory!
memset(psStart, 0, sizeof(*psStart)); } else { // need to free the anchors in the first span
psStart = _rgSpans.GetPtr(0); SafeReleaseClear(psStart->paStart); SafeReleaseClear(psStart->paEnd); psStart->dwFlags = 0; }
goto Exit; }
Assert(CompareAnchors(paStart, paEnd) <= 0);
psStart = _Find(paStart, &iStart); psEnd = _Find(paEnd, &iEnd);
if (iStart == iEnd) { if (psStart == NULL) { // this span doesn't overlap with anything else
iStart++;
if ((psStart = _InsertNewSpan(iStart)) == NULL) goto ExitRelease; // out-of-memory!
if (ao == OWN_ANCHORS) { psStart->paStart = paStart; fReleaseStart = FALSE; psStart->paEnd = paEnd; fReleaseEnd = FALSE; } else { if (paStart->Clone(&psStart->paStart) != S_OK) { _rgSpans.Remove(iStart, 1); goto ExitRelease; } if (paEnd->Clone(&psStart->paEnd) != S_OK) { psStart->paStart->Release(); _rgSpans.Remove(iStart, 1); goto ExitRelease; } } psStart->dwFlags = dwFlags; } else if (psEnd != NULL) { Assert(psStart == psEnd); // the new span is a subset of an existing span
psStart->dwFlags &= dwFlags; } else { // this spans overlaps with an existing one, but extends further to the right
// just swap out the end anchor, since we know (iStart == iEnd) that we only
// cover just this one span
if (ao == OWN_ANCHORS) { psStart->paEnd->Release(); psStart->paEnd = paEnd; fReleaseEnd = FALSE; } else { if (paEnd->Clone(&paClone) != S_OK || paClone == NULL) goto ExitRelease; psStart->paEnd->Release(); psStart->paEnd = paClone; } }
goto ExitRelease; }
// delete all but one of the covered spans
if (psStart == NULL) { iStart++; psStart = _rgSpans.GetPtr(iStart); Assert(psStart != NULL);
if (ao == OWN_ANCHORS) { paLowerBound = paStart; fReleaseStart = FALSE; } else { if (FAILED(paStart->Clone(&paLowerBound))) goto ExitRelease; } } else { paLowerBound = psStart->paStart; paLowerBound->AddRef(); } if (psEnd == NULL) { if (ao == OWN_ANCHORS) { paUpperBound = paEnd; fReleaseEnd = FALSE; } else { if (FAILED(paEnd->Clone(&paUpperBound))) goto ExitRelease; } } else { paUpperBound = psEnd->paEnd; paUpperBound->AddRef(); }
// psStart grows to cover the entire span
psStart->paStart->Release(); psStart->paEnd->Release(); psStart->paStart = paLowerBound; psStart->paEnd = paUpperBound;
Exit: // then delete the covered spans
for (int i=iStart + 1; i <= iEnd; i++) { SPAN *ps = _rgSpans.GetPtr(i); dwFlags &= ps->dwFlags; ps->paStart->Release(); ps->paEnd->Release(); }
psStart->dwFlags &= dwFlags; // only set correction bit if all spans were corrections
//Remove all spans we just cleared out
if (iEnd - iStart > 0) { _rgSpans.Remove(iStart+1, iEnd - iStart); }
ExitRelease: if (fReleaseStart) { SafeRelease(paStart); } if (fReleaseEnd) { SafeRelease(paEnd); } }
//+---------------------------------------------------------------------------
//
// _Find
//
//----------------------------------------------------------------------------
SPAN *CSpanSet::_Find(IAnchor *pa, int *piOut) { SPAN *ps; SPAN *psMatch; int iMin; int iMax; int iMid;
psMatch = NULL; iMid = -1; iMin = 0; iMax = _rgSpans.Count();
while (iMin < iMax) { iMid = (iMin + iMax) / 2; ps = _rgSpans.GetPtr(iMid); Assert(ps != NULL);
if (CompareAnchors(pa, ps->paStart) < 0) { iMax = iMid; } else if (CompareAnchors(pa, ps->paEnd) > 0) { iMin = iMid + 1; } else // anchor is in the span
{ psMatch = ps; break; } }
if (piOut != NULL) { if (psMatch == NULL && iMid >= 0) { // couldn't find a match, return the next lowest span
Assert(iMid == 0 || CompareAnchors(_rgSpans.GetPtr(iMid-1)->paEnd, pa) < 0); if (CompareAnchors(_rgSpans.GetPtr(iMid)->paStart, pa) > 0) { iMid--; } } *piOut = iMid; }
return psMatch; }
//+---------------------------------------------------------------------------
//
// AnchorsAway
//
// Note we don't zero-out the IAnchors pointers! Be careful.
//----------------------------------------------------------------------------
void CSpanSet::_AnchorsAway() { SPAN *span; int i;
for (i=0; i<_rgSpans.Count(); i++) { span = _rgSpans.GetPtr(i); SafeRelease(span->paStart); SafeRelease(span->paEnd); } }
//+---------------------------------------------------------------------------
//
// Normalize
//
// Replaces (NULL, NULL) spans with actual anchor values for start, end of doc
//----------------------------------------------------------------------------
BOOL CSpanSet::Normalize(ITextStoreAnchor *ptsi) { SPAN *span;
if (!_AllSpansCovered()) return TRUE;
// if we get here, we have a single span with NULL/NULL anchors
span = _rgSpans.GetPtr(0);
if (ptsi->GetStart(&span->paStart) != S_OK || span->paStart == NULL) return FALSE;
// Issue: need a GetEnd wrapper that handle unimplemented case! DON'T USE GetEnd!
if (ptsi->GetEnd(&span->paEnd) != S_OK || span->paEnd == NULL) { SafeReleaseClear(span->paStart); return FALSE; }
return TRUE; }
|