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.
619 lines
16 KiB
619 lines
16 KiB
//
|
|
// 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<cFetched; i++)
|
|
{
|
|
rgPropVal[i].guidId = prgVals[i].idAttr;
|
|
rgPropVal[i].varValue = prgVals[i].varValue; // we take ownership, no VariantCopy
|
|
}
|
|
|
|
// figure out what was missing
|
|
cMissing = cGUIDs - cFetched;
|
|
|
|
if (cMissing == 0)
|
|
goto Exit;
|
|
|
|
iNext = cFetched; // index of first missing guid
|
|
|
|
// perf: this is O(n^2), we could do a sort or something...
|
|
for (i=0; i<cGUIDs; i++)
|
|
{
|
|
for (j=0; j<cFetched; j++)
|
|
{
|
|
if (IsEqualGUID(prgVals[j].idAttr, prgGUIDs[i]))
|
|
break;
|
|
}
|
|
|
|
if (j < cFetched)
|
|
continue;
|
|
|
|
// found a missing GUID, need to get the default value
|
|
hr = GetDefaultValue(ptsi, prgGUIDs[i], &rgPropVal[iNext].varValue);
|
|
if (hr != S_OK)
|
|
{
|
|
Assert(0); // why did we fail?
|
|
QuickVariantInit(&rgPropVal[iNext].varValue);
|
|
}
|
|
|
|
rgPropVal[iNext].guidId = prgGUIDs[i];
|
|
|
|
if (--cMissing == 0) // anything left to look for?
|
|
break;
|
|
iNext++;
|
|
}
|
|
|
|
Exit:
|
|
cicMemFree(prgVals);
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CEnumAppPropRanges
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
class CEnumAppPropRanges : public CEnumRangesFromAnchorsBase
|
|
{
|
|
public:
|
|
CEnumAppPropRanges()
|
|
{
|
|
Dbg_MemSetThisNameIDCounter(TEXT("CEnumAppPropRanges"), PERF_ENUMAPPPROP_COUNTER);
|
|
}
|
|
|
|
BOOL _Init(CInputContext *pic, ITfRange *rangeSuper, REFGUID rguid);
|
|
|
|
private:
|
|
DBG_ID_DECLARE;
|
|
};
|
|
|
|
DBG_ID_INSTANCE(CEnumAppPropRanges);
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Init
|
|
//
|
|
// Scan the superset range and build up a list of covered ranges.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CEnumAppPropRanges::_Init(CInputContext *pic, ITfRange *rangeSuper, REFGUID rguid)
|
|
{
|
|
Assert(_iCur == 0);
|
|
Assert(_pic == NULL);
|
|
Assert(_prgAnchors == NULL);
|
|
|
|
_prgAnchors = CalcAppPropertyTrackerAnchors(pic->_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;
|
|
}
|