// // attr.cpp // #include "private.h" #include "attr.h" #include "ic.h" #include "saa.h" #include "erfa.h" #include "epval.h" #include "immxutil.h" #include "range.h" //+--------------------------------------------------------------------------- // // CalcAppPropertyTrackerAnchors // // NB: an empty range will result in a single anchor at the range pos. //---------------------------------------------------------------------------- CSharedAnchorArray *CalcAppPropertyTrackerAnchors(ITextStoreAnchor *ptsi, ITfRange *rangeSuper, ULONG cGUIDs, const GUID *prgGUIDs) { CSharedAnchorArray *prgAnchors; CRange *rangeScan; IAnchor *paPrevTrans; IAnchor **ppa; BOOL fRet; BOOL fFound; LONG lFoundOffset; HRESULT hr; if ((rangeScan = GetCRange_NA(rangeSuper)) == NULL) return NULL; if ((prgAnchors = new CSharedAnchorArray) == NULL) return NULL; fRet = FALSE; if (rangeScan->_GetStart()->Clone(&paPrevTrans) != S_OK) { paPrevTrans = NULL; goto Exit; } // now scan down the length of the range, building up a list while (TRUE) { // we've just found the end point of this run if (!prgAnchors->Append(1)) goto Exit; ppa = prgAnchors->GetPtr(prgAnchors->Count()-1); if (paPrevTrans->Clone(ppa) != S_OK) goto Exit; if (cGUIDs == 0) // no transition for zero GUIDs, just do the end anchor Clone outside the loop break; hr = ptsi->FindNextAttrTransition(paPrevTrans, rangeScan->_GetEnd(), cGUIDs, prgGUIDs, TS_ATTR_FIND_UPDATESTART, &fFound, &lFoundOffset); if (hr != S_OK) goto Exit; // no more property spans? if (!fFound) break; // stop when we hit the end of the range if (IsEqualAnchor(paPrevTrans, rangeScan->_GetEnd())) break; } if (!IsEqualAnchor(rangeScan->_GetStart(), rangeScan->_GetEnd())) { // add a final anchor at the end of the range if (!prgAnchors->Append(1)) goto Exit; ppa = prgAnchors->GetPtr(prgAnchors->Count()-1); if (rangeScan->_GetEnd()->Clone(ppa) != S_OK) goto Exit; } // shrink the array down to size, it won't be modified again prgAnchors->CompactSize(); fRet = TRUE; Exit: if (!fRet) { prgAnchors->_Release(); prgAnchors = NULL; } else { Assert(prgAnchors != NULL); } SafeRelease(paPrevTrans); return prgAnchors; } //+--------------------------------------------------------------------------- // // GetDefaultValue // //---------------------------------------------------------------------------- HRESULT GetDefaultValue(ITextStoreAnchor *ptsi, REFGUID guidType, VARIANT *pvarValue) { TS_ATTRVAL av; ULONG cFetched; HRESULT hr; Assert(pvarValue != NULL); // VT_EMPTY for unsupported attrib/error QuickVariantInit(pvarValue); hr = ptsi->RequestSupportedAttrs(TS_ATTR_FIND_WANT_VALUE, 1, &guidType); if (hr != S_OK) { return (hr == E_NOTIMPL) ? E_NOTIMPL : E_FAIL; } if (ptsi->RetrieveRequestedAttrs(1, &av, &cFetched) == S_OK && cFetched == 1) { Assert(IsEqualGUID(av.idAttr, guidType)); *pvarValue = av.varValue; // caller owns it now } else { // the aimm layer will sometimes stop supporting an attribute // it has two sink callback points, the one for reconversion doesn't // handle attributes. // we'll just return VT_EMPTY Assert(pvarValue->vt == VT_EMPTY); } return S_OK; } //+--------------------------------------------------------------------------- // // FillAppValueArray // //---------------------------------------------------------------------------- HRESULT FillAppValueArray(ITextStoreAnchor *ptsi, CRange *range, TF_PROPERTYVAL *rgPropVal, ULONG cGUIDs, const GUID *prgGUIDs) { ULONG i; ULONG j; ULONG iNext; ULONG cMissing; TS_ATTRVAL *prgVals; ULONG cFetched; HRESULT hr; if (cGUIDs == 0) return S_OK; if ((prgVals = (TS_ATTRVAL *)cicMemAlloc(cGUIDs*sizeof(TS_ATTRVAL))) == NULL) return E_OUTOFMEMORY; hr = ptsi->RequestAttrsAtPosition(range->_GetStart(), cGUIDs, prgGUIDs, 0); if (hr != S_OK) goto Exit; hr = ptsi->RetrieveRequestedAttrs(cGUIDs, prgVals, &cFetched); if (hr != S_OK) goto Exit; // copy over the values in prgVals for (i=0; i_GetTSI(), rangeSuper, 1, &rguid); if (_prgAnchors == NULL) return FALSE; _pic = pic; _pic->AddRef(); return TRUE; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // // CAppProperty // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// class CAppProperty : public ITfReadOnlyProperty, public CComObjectRootImmx { public: CAppProperty(CInputContext *pic, REFGUID guid); ~CAppProperty(); BEGIN_COM_MAP_IMMX(CAppProperty) COM_INTERFACE_ENTRY(ITfReadOnlyProperty) END_COM_MAP_IMMX() IMMX_OBJECT_IUNKNOWN_FOR_ATL() // 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; GUID _guid; DBG_ID_DECLARE; }; DBG_ID_INSTANCE(CAppProperty); //+--------------------------------------------------------------------------- // // ctor // //---------------------------------------------------------------------------- CAppProperty::CAppProperty(CInputContext *pic, REFGUID guid) { Dbg_MemSetThisNameIDCounter(TEXT("CAppProperty"), PERF_APPPROP_COUNTER); _pic = pic; _pic->AddRef(); _guid = guid; } //+--------------------------------------------------------------------------- // // dtor // //---------------------------------------------------------------------------- CAppProperty::~CAppProperty() { _pic->Release(); } //+--------------------------------------------------------------------------- // // GetType // //---------------------------------------------------------------------------- STDAPI CAppProperty::GetType(GUID *pguid) { if (pguid == NULL) return E_INVALIDARG; *pguid = _guid; return S_OK; } //+--------------------------------------------------------------------------- // // EnumRanges // //---------------------------------------------------------------------------- STDAPI CAppProperty::EnumRanges(TfEditCookie ec, IEnumTfRanges **ppEnum, ITfRange *pTargetRange) { CEnumAppPropRanges *pEnum; if (ppEnum == NULL) return E_INVALIDARG; *ppEnum = NULL; if (!_IsValidEditCookie(ec, TF_ES_READ)) { Assert(0); return TF_E_NOLOCK; } // nb: unlike ITfProperty, ITfReadOnlyProperty does not accept pTargetRange == NULL! if (pTargetRange == NULL) return E_INVALIDARG; // make sure ic, range are in the same context if (!VerifySameContext(_pic, pTargetRange)) return E_INVALIDARG; pEnum = new CEnumAppPropRanges; if (pEnum == NULL) return E_OUTOFMEMORY; if (!pEnum->_Init(_pic, pTargetRange, _guid)) { pEnum->Release(); return E_FAIL; } *ppEnum = pEnum; return S_OK; } //+--------------------------------------------------------------------------- // // GetValue // //---------------------------------------------------------------------------- STDAPI CAppProperty::GetValue(TfEditCookie ec, ITfRange *pRange, VARIANT *pvarValue) { TS_ATTRVAL av; HRESULT hr; CRange *range; ULONG cFetched; ITextStoreAnchor *ptsi; if (pvarValue == NULL) return E_INVALIDARG; QuickVariantInit(pvarValue); if (!_IsValidEditCookie(ec, TF_ES_READ)) { Assert(0); return TF_E_NOLOCK; } if (pRange == NULL) return E_INVALIDARG; // supporting "whole doc" behavior too expensive! if ((range = GetCRange_NA(pRange)) == NULL) return E_INVALIDARG; if (!VerifySameContext(_pic, range)) return E_INVALIDARG; ptsi = _pic->_GetTSI(); // we always return the value at the start anchor hr = ptsi->RequestAttrsAtPosition(range->_GetStart(), 1, &_guid, 0); if (hr != S_OK) return E_FAIL; QuickVariantInit(&av.varValue); // just return the single VARIANT value directly if (ptsi->RetrieveRequestedAttrs(1, &av, &cFetched) != S_OK) return E_FAIL; if (cFetched == 0) { // default value return GetDefaultValue(_pic->_GetTSI(), _guid, pvarValue); } *pvarValue = av.varValue; // caller takes ownership return S_OK; } //+--------------------------------------------------------------------------- // // GetContext // //---------------------------------------------------------------------------- STDAPI CAppProperty::GetContext(ITfContext **ppContext) { if (ppContext == NULL) return E_INVALIDARG; *ppContext = _pic; (*ppContext)->AddRef(); return S_OK; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // // CInputContext // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //+--------------------------------------------------------------------------- // // GetAppProperty // //---------------------------------------------------------------------------- STDAPI CInputContext::GetAppProperty(REFGUID guidProp, ITfReadOnlyProperty **ppProp) { CAppProperty *prop; TS_ATTRVAL av; ULONG cFetched; BOOL fUnsupported; HRESULT hr; if (ppProp == NULL) return E_INVALIDARG; *ppProp = NULL; if (!_IsConnected()) return TF_E_DISCONNECTED; // // if we have a mapping property, it will be returned. // APPPROPMAP *pMap = FindMapAppProperty(guidProp); if (pMap) { CProperty *pProp; if (SUCCEEDED(_GetProperty(pMap->guidProp, &pProp))) { *ppProp = (ITfReadOnlyProperty *)pProp; return S_OK; } } // does the app support this property? fUnsupported = TRUE; if ((hr = _ptsi->RequestSupportedAttrs(0, 1, &guidProp)) != S_OK) { return (hr == E_NOTIMPL) ? E_NOTIMPL : E_FAIL; } QuickVariantInit(&av.varValue); if (_ptsi->RetrieveRequestedAttrs(1, &av, &cFetched) == S_OK && cFetched == 1) { if (IsEqualGUID(av.idAttr, guidProp)) // paranoia { fUnsupported = FALSE; } else { Assert(0); // bad out param! } } if (fUnsupported) { return S_FALSE; } if ((prop = new CAppProperty(this, guidProp)) == NULL) return E_OUTOFMEMORY; *ppProp = prop; return S_OK; } //+--------------------------------------------------------------------------- // // MapAppProperty // //---------------------------------------------------------------------------- STDAPI CInputContext::MapAppProperty(REFGUID guidAppProp, REFGUID guidProp) { APPPROPMAP *pMap; // // overwrite the mapping guidProp. // if (pMap = FindMapAppProperty(guidAppProp)) { pMap->guidProp = guidProp; return S_OK; } pMap = _rgAppPropMap.Append(1); if (!pMap) return E_OUTOFMEMORY; pMap->guidAppProp = guidAppProp; pMap->guidProp = guidProp; return S_OK; } //+--------------------------------------------------------------------------- // // FindMapProp // //---------------------------------------------------------------------------- CInputContext::APPPROPMAP *CInputContext::FindMapAppProperty(REFGUID guidAppProp) { int i; for (i = 0; i < _rgAppPropMap.Count(); i++) { APPPROPMAP *pMap = _rgAppPropMap.GetPtr(i); if (IsEqualGUID(pMap->guidAppProp, guidAppProp)) return pMap; } return NULL; } //+--------------------------------------------------------------------------- // // GetMappedAppProperty // //---------------------------------------------------------------------------- HRESULT CInputContext::GetMappedAppProperty(REFGUID guidProp, CProperty **ppProp) { if (!_IsConnected()) return TF_E_DISCONNECTED; // // if we have a mapping property, it will be returned. // APPPROPMAP *pMap = FindMapAppProperty(guidProp); if (pMap) { if (SUCCEEDED(_GetProperty(pMap->guidProp, ppProp))) { return S_OK; } } return E_FAIL; }