|
|
//
// ptrack.cpp
//
#include "private.h"
#include "ic.h"
#include "saa.h"
#include "attr.h"
#include "immxutil.h"
#include "erfa.h"
#include "epval.h"
#include "range.h"
//+---------------------------------------------------------------------------
//
// CalcCicPropertyTrackerAnchors
//
//----------------------------------------------------------------------------
CSharedAnchorArray *CalcCicPropertyTrackerAnchors(CInputContext *pic, IAnchor *paStart, IAnchor *paEnd, ULONG cGUIDATOMs, const TfGuidAtom *prgGUIDATOMs) { CProperty *pProperty; CSharedAnchorArray *prgAnchors; CSharedAnchorArray **prgAnchorArrays; ULONG i; LONG iStartEdge; LONG iEndEdge; LONG iSpan; PROPERTYLIST *pPropList; IAnchor **ppa; ULONG cArrays; BOOL fExactEndMatch; ULONG cMaxElems;
if ((prgAnchorArrays = (CSharedAnchorArray **)cicMemAlloc(sizeof(CSharedAnchorArray *)*(cGUIDATOMs+1))) == NULL) return NULL;
cArrays = 0;
//
// shove in the range start and end pts
//
if ((prgAnchors = new CSharedAnchorArray) == NULL) goto ErrorExit;
if (!prgAnchors->Insert(0, 2)) goto ErrorExit;
if (paStart->Clone(prgAnchors->GetPtr(0)) != S_OK) goto ErrorExit;
if (IsEqualAnchor(paStart, paEnd)) { // empty range, we just want a single anchor at the range pos
prgAnchors->SetCount(1); goto Exit; }
if (paEnd->Clone(prgAnchors->GetPtr(1)) != S_OK) goto ErrorExit;
prgAnchorArrays[0] = prgAnchors;
//
// assemble a list of all the points between start, end
//
cArrays = 1; // 1 for the start, end anchors array
for (i=0; i<cGUIDATOMs; i++) { if ((pProperty = pic->_FindProperty(prgGUIDATOMs[i])) == NULL) continue; // no instances of this property
// find the start, end points
pProperty->Find(paStart, &iStartEdge, FALSE); fExactEndMatch = (pProperty->Find(paEnd, &iEndEdge, TRUE) != NULL);
if (iEndEdge < iStartEdge) continue; // start, end are in the same property span, so value is constant over range
// alloc memory for all the new anchors
if ((prgAnchors = new CSharedAnchorArray) == NULL) goto ErrorExit;
// alloc for max anchors
cMaxElems = (iEndEdge - iStartEdge + 1)*2;
if ((ppa = prgAnchors->Append(cMaxElems)) == NULL) goto ErrorExit; // prep for failure
memset(ppa, 0, sizeof(IAnchor *)*cMaxElems);
// add all the covered anchors for this prop to the list
if (iStartEdge < 0) { iSpan = 0; } else { iSpan = iStartEdge;
// if paStart is to the right of the span, skip it
pPropList = pProperty->GetPropList(iStartEdge); if (CompareAnchors(paStart, pPropList->_paEnd) >= 0) { // we don't cover this span at all, or we just touch the right edge
// so skip it
iSpan++; } }
while (iSpan <= iEndEdge) { // shove in this span's anchors
pPropList = pProperty->GetPropList(iSpan);
if (iSpan != iStartEdge) { // filter out dups
// perf: we could elim the dup check for static compact props
if (ppa == prgAnchors->GetPtr(0) || !IsEqualAnchor(*(ppa-1), pPropList->_paStart)) { if (pPropList->_paStart->Clone(ppa++) != S_OK) goto ErrorExit; } }
Assert(!IsEqualAnchor(pPropList->_paStart, pPropList->_paEnd)); // no zero-len properties!
if (iSpan != iEndEdge || (!fExactEndMatch && (iStartEdge < iEndEdge || CompareAnchors(paStart, pPropList->_paEnd) < 0))) { if (pPropList->_paEnd->Clone(ppa++) != S_OK) goto ErrorExit; }
iSpan++; } // may also want the start anchor of the next span
if (!fExactEndMatch && pProperty->GetPropNum() > iEndEdge+1) { pPropList = pProperty->GetPropList(iEndEdge+1);
if (CompareAnchors(paEnd, pPropList->_paStart) > 0) { // start of this span may be same as end of prev for non-compact property, check for dup
if (ppa == prgAnchors->GetPtr(0) || !IsEqualAnchor(*(ppa-1), pPropList->_paStart)) { // don't need a dup check w/ paEnd because we would have set fExactEndMatch in that case
if (pPropList->_paStart->Clone(ppa++) != S_OK) goto ErrorExit; } } }
// need to resize the array since we may have over-alloc'd
Assert((int)cMaxElems >= ppa - prgAnchors->GetPtr(0)); prgAnchors->SetCount((int)(ppa - prgAnchors->GetPtr(0))); prgAnchorArrays[cArrays++] = prgAnchors; }
//
// sort the list
//
if (cArrays > 1) { // mergesort will free all the arrays in prgAnchorArrays
prgAnchors = CSharedAnchorArray::_MergeSort(prgAnchorArrays, cArrays); } else { Assert(prgAnchors == prgAnchorArrays[0]); }
// shrink the array down to size, it won't be modified again
if (prgAnchors) prgAnchors->CompactSize();
Exit: cicMemFree(prgAnchorArrays); return prgAnchors;
ErrorExit: for (i=0; i<cArrays; i++) { prgAnchorArrays[i]->_Release(); } prgAnchors = NULL; goto Exit; }
//+---------------------------------------------------------------------------
//
// FillCicValueArray
//
//----------------------------------------------------------------------------
void FillCicValueArray(CInputContext *pic, CRange *range, TF_PROPERTYVAL *rgPropVal, ULONG cGUIDATOMs, const TfGuidAtom *prgGUIDATOMs) { ULONG i; CProperty *pProperty;
for (i=0; i<cGUIDATOMs; i++) { Assert(rgPropVal[i].varValue.vt == VT_EMPTY);
if (MyGetGUID(prgGUIDATOMs[i], &rgPropVal[i].guidId) != S_OK) { Assert(0); // this shouldn't happen, we registered the GUID when caller created the property
rgPropVal[i].guidId = GUID_NULL; continue; }
if ((pProperty = pic->_FindProperty(prgGUIDATOMs[i])) != NULL) { pProperty->_GetDataInternal(range->_GetStart(), range->_GetEnd(), &rgPropVal[i].varValue); } } }
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// CEnumUberRanges
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
class CEnumUberRanges : public CEnumRangesFromAnchorsBase { public: CEnumUberRanges() { Dbg_MemSetThisNameIDCounter(TEXT("CEnumUberRanges"), PERF_ENUMUBERPROP_COUNTER); } BOOL _Init(CInputContext *pic, ITfRange *rangeSuper, ULONG cCicGUIDs, const TfGuidAtom *prgCicGUIDATOMs, ULONG cAppGUIDs, const GUID *prgAppGUIDs);
private: DBG_ID_DECLARE; };
DBG_ID_INSTANCE(CEnumUberRanges);
//+---------------------------------------------------------------------------
//
// _Init
//
//----------------------------------------------------------------------------
BOOL CEnumUberRanges::_Init(CInputContext *pic, ITfRange *rangeSuper, ULONG cCicGUIDATOMs, const TfGuidAtom *prgCicGUIDATOMs, ULONG cAppGUIDs, const GUID *prgAppGUIDs) { CRange *range; CSharedAnchorArray *prgSrcAnchorArrays[2];
Assert(_iCur == 0); Assert(_pic == NULL); Assert(_prgAnchors == NULL);
// find the app property transitions
prgSrcAnchorArrays[0] = CalcAppPropertyTrackerAnchors(pic->_GetTSI(), rangeSuper, cAppGUIDs, prgAppGUIDs);
if (prgSrcAnchorArrays[0] == NULL) return FALSE;
// find the cicero property transitions
if ((range = GetCRange_NA(rangeSuper)) == NULL) goto ErrorExit;
prgSrcAnchorArrays[1] = CalcCicPropertyTrackerAnchors(pic, range->_GetStart(), range->_GetEnd(), cCicGUIDATOMs, prgCicGUIDATOMs);
if (prgSrcAnchorArrays[1] == NULL) goto ErrorExit;
// now combine the two lists
_prgAnchors = CSharedAnchorArray::_MergeSort(prgSrcAnchorArrays, 2);
if (_prgAnchors == NULL) return FALSE;
_pic = pic; _pic->AddRef();
return TRUE;
ErrorExit: prgSrcAnchorArrays[0]->_Release(); return FALSE; }
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// CUberProperty
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
class CUberProperty : public ITfReadOnlyProperty, // perf: share base clas with CAppProperty
public CComObjectRootImmx { public: CUberProperty(CInputContext *pic); ~CUberProperty();
BEGIN_COM_MAP_IMMX(CUberProperty) COM_INTERFACE_ENTRY(ITfReadOnlyProperty) END_COM_MAP_IMMX()
IMMX_OBJECT_IUNKNOWN_FOR_ATL()
BOOL _Init(ULONG cCicGUIDs, const GUID **prgCicGUIDs, ULONG cAppGUIDs, const GUID **prgAppGUIDs);
// ITfReadOnlyProperty
STDMETHODIMP GetType(GUID *pguid); STDMETHODIMP EnumRanges(TfEditCookie ec, IEnumTfRanges **ppEnum, ITfRange *pTargetRange); STDMETHODIMP GetValue(TfEditCookie ec, ITfRange *pRange, VARIANT *pvarValue); STDMETHODIMP GetContext(ITfContext **ppContext);
private: BOOL _IsValidEditCookie(TfEditCookie ec, DWORD dwFlags) { return _pic->_IsValidEditCookie(ec, dwFlags); }
CInputContext *_pic;
ULONG _cCicGUIDATOMs; TfGuidAtom *_prgCicGUIDATOMs;
ULONG _cAppGUIDs; GUID *_prgAppGUIDs;
DBG_ID_DECLARE; };
DBG_ID_INSTANCE(CUberProperty);
//+---------------------------------------------------------------------------
//
// ctor
//
//----------------------------------------------------------------------------
CUberProperty::CUberProperty(CInputContext *pic) { Dbg_MemSetThisNameIDCounter(TEXT("CUberProperty"), PERF_UBERPROP_COUNTER);
_pic = pic; _pic->AddRef(); }
//+---------------------------------------------------------------------------
//
// dtor
//
//----------------------------------------------------------------------------
CUberProperty::~CUberProperty() { _pic->Release(); cicMemFree(_prgCicGUIDATOMs); cicMemFree(_prgAppGUIDs); }
//+---------------------------------------------------------------------------
//
// _Init
//
//----------------------------------------------------------------------------
BOOL CUberProperty::_Init(ULONG cCicGUIDs, const GUID **prgCicGUIDs, ULONG cAppGUIDs, const GUID **prgAppGUIDs) { ULONG i;
if ((_prgCicGUIDATOMs = (TfGuidAtom *)cicMemAlloc(cCicGUIDs*sizeof(TfGuidAtom))) == NULL) return FALSE;
for (i=0; i<cCicGUIDs; i++) { if (MyRegisterGUID(*prgCicGUIDs[i], &_prgCicGUIDATOMs[i]) != S_OK) goto ExitError; }
if ((_prgAppGUIDs = (GUID *)cicMemAlloc(cAppGUIDs*sizeof(GUID))) == NULL) goto ExitError;
_cCicGUIDATOMs = cCicGUIDs;
_cAppGUIDs = cAppGUIDs; for (i=0; i<cAppGUIDs; i++) { _prgAppGUIDs[i] = *prgAppGUIDs[i]; }
return TRUE;
ExitError: cicMemFree(_prgCicGUIDATOMs); _prgCicGUIDATOMs = NULL; // no funny business in the dtor please
return FALSE; }
//+---------------------------------------------------------------------------
//
// EnumRanges
//
//----------------------------------------------------------------------------
STDAPI CUberProperty::EnumRanges(TfEditCookie ec, IEnumTfRanges **ppEnum, ITfRange *pTargetRange) { CEnumUberRanges *pEnum;
if (ppEnum == NULL) return E_INVALIDARG;
*ppEnum = NULL;
if (pTargetRange == NULL) return E_INVALIDARG;
if (!VerifySameContext(_pic, pTargetRange)) return E_INVALIDARG; if (!_IsValidEditCookie(ec, TF_ES_READ)) { Assert(0); return TF_E_NOLOCK; }
pEnum = new CEnumUberRanges;
if (pEnum == NULL) return E_OUTOFMEMORY;
if (!pEnum->_Init(_pic, pTargetRange, _cCicGUIDATOMs, _prgCicGUIDATOMs, _cAppGUIDs, _prgAppGUIDs)) { pEnum->Release(); return E_FAIL; }
*ppEnum = pEnum; return S_OK; }
//+---------------------------------------------------------------------------
//
// GetType
//
//----------------------------------------------------------------------------
STDAPI CUberProperty::GetType(GUID *pguid) { if (pguid != NULL) { // tracker's don't support GetType
*pguid = GUID_NULL; }
return E_NOTIMPL; // by design
}
//+---------------------------------------------------------------------------
//
// GetValue
//
//----------------------------------------------------------------------------
STDAPI CUberProperty::GetValue(TfEditCookie ec, ITfRange *pRange, VARIANT *pvarValue) { CEnumPropertyValue *pEnumVal; CRange *range; SHARED_TFPROPERTYVAL_ARRAY *pPropVal; HRESULT hr;
if (pvarValue == NULL) return E_INVALIDARG;
QuickVariantInit(pvarValue);
if (pRange == NULL) return E_INVALIDARG;
if ((range = GetCRange_NA(pRange)) == NULL) return E_INVALIDARG;
if (!VerifySameContext(_pic, range)) return E_INVALIDARG;
if (!_IsValidEditCookie(ec, TF_ES_READ)) { Assert(0); return TF_E_NOLOCK; }
hr = E_FAIL;
if ((pPropVal = SAA_New(_cCicGUIDATOMs + _cAppGUIDs)) == NULL) goto Exit;
// get an array of app values
if (FillAppValueArray(_pic->_GetTSI(), range, pPropVal->rgAttrVals, _cAppGUIDs, _prgAppGUIDs) != S_OK) goto Exit;
// get an array of cic values
FillCicValueArray(_pic, range, pPropVal->rgAttrVals + _cAppGUIDs, _cCicGUIDATOMs, _prgCicGUIDATOMs);
// stick them in an enum
if ((pEnumVal = new CEnumPropertyValue(pPropVal)) == NULL) goto Exit;
pvarValue->vt = VT_UNKNOWN; pvarValue->punkVal = pEnumVal;
hr = S_OK;
Exit: if (pPropVal != NULL) { SAA_Release(pPropVal); } return hr; }
//+---------------------------------------------------------------------------
//
// GetContext
//
// perf: identical to CAppProperty::GetContext....move to base class?
//----------------------------------------------------------------------------
STDAPI CUberProperty::GetContext(ITfContext **ppContext) { if (ppContext == NULL) return E_INVALIDARG;
*ppContext = _pic; (*ppContext)->AddRef();
return S_OK; }
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// CInputContext
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//+---------------------------------------------------------------------------
//
// TrackProperties
//
//----------------------------------------------------------------------------
STDAPI CInputContext::TrackProperties(const GUID **pguidProp, ULONG cProp, const GUID **pguidAppProp, ULONG cAppProp, ITfReadOnlyProperty **ppPropX) { CUberProperty *pup;
if (ppPropX == NULL) return E_INVALIDARG;
*ppPropX = NULL;
if (pguidProp == NULL && cProp > 0) return E_INVALIDARG;
if (pguidAppProp == NULL && cAppProp > 0) return E_INVALIDARG;
if (!_IsConnected()) return TF_E_DISCONNECTED;
if ((pup = new CUberProperty(this)) == NULL) return E_OUTOFMEMORY;
if (!pup->_Init(cProp, pguidProp, cAppProp, pguidAppProp)) { pup->Release(); return E_OUTOFMEMORY; }
*ppPropX = pup;
return S_OK; }
|