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.
 
 
 
 
 
 

1602 lines
40 KiB

//
// range.cpp
//
#include "private.h"
#include "range.h"
#include "ic.h"
#include "immxutil.h"
#include "rprop.h"
#include "tim.h"
#include "anchoref.h"
#include "compose.h"
/* b68832f0-34b9-11d3-a745-0050040ab407 */
const IID IID_PRIV_CRANGE = { 0xb68832f0,0x34b9, 0x11d3, {0xa7, 0x45, 0x00, 0x50, 0x04, 0x0a, 0xb4, 0x07} };
DBG_ID_INSTANCE(CRange);
MEMCACHE *CRange::_s_pMemCache = NULL;
//+---------------------------------------------------------------------------
//
// _InitClass
//
//----------------------------------------------------------------------------
/* static */
void CRange::_InitClass()
{
_s_pMemCache = MemCache_New(32);
}
//+---------------------------------------------------------------------------
//
// _UninitClass
//
//----------------------------------------------------------------------------
/* static */
void CRange::_UninitClass()
{
if (_s_pMemCache == NULL)
return;
MemCache_Delete(_s_pMemCache);
_s_pMemCache = NULL;
}
//+---------------------------------------------------------------------------
//
// _Init
//
// NB: If fSetDefaultGravity == TRUE, make certain paStart <= paEnd, or you
// will break something!
//----------------------------------------------------------------------------
BOOL CRange::_Init(CInputContext *pic, AnchorOwnership ao, IAnchor *paStart, IAnchor *paEnd, RInit rinit)
{
TsGravity gStart;
TsGravity gEnd;
// can't check the anchors because we may be cloned from a range with crossed anchors
// can't do anything about crossed anchors until we know we have a doc lock
//Assert(CompareAnchors(paStart, paEnd) <= 0);
Assert(_paStart == NULL);
Assert(_paEnd == NULL);
Assert(_fDirty == FALSE);
Assert(_nextOnChangeRangeInIcsub == NULL);
if (ao == OWN_ANCHORS)
{
_paStart = paStart;
_paEnd = paEnd;
}
else
{
Assert(ao == COPY_ANCHORS);
if (paStart->Clone(&_paStart) != S_OK || _paStart == NULL)
goto ErrorExit;
if (paEnd->Clone(&_paEnd) != S_OK || _paEnd == NULL)
goto ErrorExit;
}
_pic = pic;
switch (rinit)
{
case RINIT_DEF_GRAVITY:
Assert(CompareAnchors(paStart, paEnd) <= 0); // Issue: this is only a safe assert for acp implementations
if (_SetGravity(TF_GRAVITY_BACKWARD, TF_GRAVITY_FORWARD, FALSE) != S_OK)
goto ErrorExit;
break;
case RINIT_GRAVITY:
if (_paStart->GetGravity(&gStart) != S_OK)
goto ErrorExit;
if (_paEnd->GetGravity(&gEnd) != S_OK)
goto ErrorExit;
_InitLastLockReleaseId(gStart, gEnd);
break;
default:
// caller must init _dwLastLockReleaseID!
break;
}
_pic->AddRef();
return TRUE;
ErrorExit:
Assert(0);
if (ao == COPY_ANCHORS)
{
SafeReleaseClear(_paStart);
SafeReleaseClear(_paEnd);
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// dtor
//
//----------------------------------------------------------------------------
CRange::~CRange()
{
_paStart->Release();
_paEnd->Release();
_pic->Release();
Assert(_prgChangeSinks == NULL || _prgChangeSinks->Count() == 0); // all ITfRangeChangeSink's should have been unadvised
delete _prgChangeSinks;
}
//+---------------------------------------------------------------------------
//
// IUnknown
//
//----------------------------------------------------------------------------
STDAPI CRange::QueryInterface(REFIID riid, void **ppvObj)
{
CAnchorRef *par;
if (&riid == &IID_PRIV_CRANGE ||
IsEqualIID(riid, IID_PRIV_CRANGE))
{
*ppvObj = SAFECAST(this, CRange *);
return S_OK; // No AddRef for IID_PRIV_CRANGE! this is a private IID....
}
*ppvObj = NULL;
if (IsEqualIID(riid, IID_ITfRange) ||
IsEqualIID(riid, IID_IUnknown))
{
*ppvObj = SAFECAST(this, ITfRangeAnchor *);
}
else if (IsEqualIID(riid, IID_ITfRangeACP))
{
if ((par = GetCAnchorRef_NA(_paStart)) != NULL) // just a test to see if we're wrapping
{
*ppvObj = SAFECAST(this, ITfRangeACP *);
}
}
else if (IsEqualIID(riid, IID_ITfRangeAnchor))
{
if ((par = GetCAnchorRef_NA(_paStart)) == NULL) // just a test to see if we're wrapping
{
*ppvObj = SAFECAST(this, ITfRangeAnchor *);
}
}
else if (IsEqualIID(riid, IID_ITfSource))
{
*ppvObj = SAFECAST(this, ITfSource *);
}
if (*ppvObj)
{
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDAPI_(ULONG) CRange::AddRef()
{
return ++_cRef;
}
STDAPI_(ULONG) CRange::Release()
{
long cr;
cr = --_cRef;
Assert(cr >= 0);
if (cr == 0)
{
delete this;
}
return cr;
}
//+---------------------------------------------------------------------------
//
// _IsValidEditCookie
//
//----------------------------------------------------------------------------
BOOL CRange::_IsValidEditCookie(TfEditCookie ec, DWORD dwFlags)
{
// any time someone is about to access the doc, we also need
// to verify the last app edit didn't cross this range's anchors
_QuickCheckCrossedAnchors();
return _pic->_IsValidEditCookie(ec, dwFlags);
}
//+---------------------------------------------------------------------------
//
// _CheckCrossedAnchors
//
//----------------------------------------------------------------------------
void CRange::_CheckCrossedAnchors()
{
DWORD dw;
Assert(_dwLastLockReleaseID != IGNORE_LAST_LOCKRELEASED); // use _QuickCheckCrossedAnchors first!
#ifdef DEBUG
// we shold only make is this far if this range has TF_GRAVITY_FORWARD,
// TF_GRAVITY_BACKWARD otherwise we should never be able to get crossed
// anchors.
TsGravity gStart;
TsGravity gEnd;
_paStart->GetGravity(&gStart);
_paEnd->GetGravity(&gEnd);
Assert(gStart == TS_GR_FORWARD && gEnd == TS_GR_BACKWARD);
#endif // DEBUG
dw = _pic->_GetLastLockReleaseID();
Assert(dw != IGNORE_LAST_LOCKRELEASED);
if (_dwLastLockReleaseID == dw)
return;
_dwLastLockReleaseID = dw;
if (CompareAnchors(_paStart, _paEnd) > 0)
{
// for crossed anchors, we always move the start anchor to the end pos -- ie, don't move
_paStart->ShiftTo(_paEnd);
}
}
//+---------------------------------------------------------------------------
//
// GetText
//
//----------------------------------------------------------------------------
STDAPI CRange::GetText(TfEditCookie ec, DWORD dwFlags, WCHAR *pch, ULONG cchMax, ULONG *pcch)
{
HRESULT hr;
BOOL fMove;
Perf_IncCounter(PERF_RGETTEXT_COUNT);
if (pcch == NULL)
return E_INVALIDARG;
*pcch = 0;
if (dwFlags & ~(TF_TF_MOVESTART | TF_TF_IGNOREEND))
return E_INVALIDARG;
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
fMove = (dwFlags & TF_TF_MOVESTART);
hr = _pic->_ptsi->GetText(0, _paStart, (dwFlags & TF_TF_IGNOREEND) ? NULL : _paEnd, pch, cchMax, pcch, fMove);
if (hr != S_OK)
{
hr = E_FAIL;
}
// don't let the start advance past the end
if (fMove && CompareAnchors(_paStart, _paEnd) > 0)
{
_paEnd->ShiftTo(_paStart);
}
return hr;
}
//+---------------------------------------------------------------------------
//
// SetText
//
//----------------------------------------------------------------------------
STDAPI CRange::SetText(TfEditCookie ec, DWORD dwFlags, const WCHAR *pchText, LONG cch)
{
CComposition *pComposition;
HRESULT hr;
BOOL fNewComposition;
Perf_IncCounter(PERF_RSETTEXT_COUNT);
if (pchText == NULL && cch != 0)
return E_INVALIDARG;
if ((dwFlags & ~TF_ST_CORRECTION) != 0)
return E_INVALIDARG;
if (!_IsValidEditCookie(ec, TF_ES_READWRITE))
{
Assert(0);
return TF_E_NOLOCK;
}
hr = _PreEditCompositionCheck(ec, &pComposition, &fNewComposition);
if (hr != S_OK)
return hr;
if (cch < 0)
{
cch = wcslen(pchText);
}
#ifdef DEBUG
for (LONG i=0; i<cch; i++)
{
Assert(pchText[i] != TF_CHAR_EMBEDDED); // illegal to insert TF_CHAR_EMBEDDED!
Assert(pchText[i] != TS_CHAR_REGION); // illegal to insert TS_CHAR_REGION!
}
#endif
//
// set the text
//
hr = _pic->_ptsi->SetText(dwFlags, _paStart, _paEnd, pchText ? pchText : L"", cch);
if (hr == S_OK)
{
_pic->_DoPostTextEditNotifications(pComposition, ec, dwFlags, cch, NULL, NULL, this);
}
// terminate the default composition, if there is one
if (fNewComposition)
{
Assert(pComposition != NULL);
pComposition->EndComposition(ec);
pComposition->Release(); // don't need Release if !fNewComposition
}
return hr;
}
//+---------------------------------------------------------------------------
//
// GetEmbedded
//
//----------------------------------------------------------------------------
STDAPI CRange::GetEmbedded(TfEditCookie ec, REFGUID rguidService, REFIID riid, IUnknown **ppunk)
{
if (ppunk == NULL)
return E_INVALIDARG;
*ppunk = NULL;
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
return _pic->_ptsi->GetEmbedded(0, _paStart, rguidService, riid, ppunk);
}
//+---------------------------------------------------------------------------
//
// Clone
//
//----------------------------------------------------------------------------
STDAPI CRange::InsertEmbedded(TfEditCookie ec, DWORD dwFlags, IDataObject *pDataObject)
{
CComposition *pComposition;
BOOL fNewComposition;
HRESULT hr;
if ((dwFlags & ~TF_IE_CORRECTION) != 0)
return E_INVALIDARG;
if (pDataObject == NULL)
return E_INVALIDARG;
if (!_IsValidEditCookie(ec, TF_ES_READWRITE))
{
Assert(0);
return TF_E_NOLOCK;
}
hr = _PreEditCompositionCheck(ec, &pComposition, &fNewComposition);
if (hr != S_OK)
return hr;
hr = _pic->_ptsi->InsertEmbedded(dwFlags, _paStart, _paEnd, pDataObject);
if (hr == S_OK)
{
_pic->_DoPostTextEditNotifications(pComposition, ec, dwFlags, 1, NULL, NULL, this);
}
// terminate the default composition, if there is one
if (fNewComposition)
{
Assert(pComposition != NULL);
pComposition->EndComposition(ec);
pComposition->Release(); // don't need Release if !fNewComposition
}
return hr;
}
//+---------------------------------------------------------------------------
//
// GetFormattedText
//
//----------------------------------------------------------------------------
STDAPI CRange::GetFormattedText(TfEditCookie ec, IDataObject **ppDataObject)
{
if (ppDataObject == NULL)
return E_INVALIDARG;
*ppDataObject = NULL;
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
return _pic->_ptsi->GetFormattedText(_paStart, _paEnd, ppDataObject);
}
//+---------------------------------------------------------------------------
//
// Clone
//
//----------------------------------------------------------------------------
STDAPI CRange::Clone(ITfRange **ppClone)
{
if (ppClone == NULL)
return E_INVALIDARG;
return (*ppClone = (ITfRangeAnchor *)_Clone()) ? S_OK : E_OUTOFMEMORY;
}
//+---------------------------------------------------------------------------
//
// GetContext
//
//----------------------------------------------------------------------------
STDAPI CRange::GetContext(ITfContext **ppContext)
{
if (ppContext == NULL)
return E_INVALIDARG;
*ppContext = _pic;
if (*ppContext)
{
(*ppContext)->AddRef();
return S_OK;
}
return E_FAIL;
}
//+---------------------------------------------------------------------------
//
// ShiftStart
//
//----------------------------------------------------------------------------
STDAPI CRange::ShiftStart(TfEditCookie ec, LONG cchReq, LONG *pcch, const TF_HALTCOND *pHalt)
{
CRange *pRangeP;
IAnchor *paLimit;
IAnchor *paShift;
HRESULT hr;
Perf_IncCounter(PERF_SHIFTSTART_COUNT);
if (pcch == NULL)
return E_INVALIDARG;
*pcch = 0;
if (pHalt != NULL && (pHalt->dwFlags & ~TF_HF_OBJECT))
return E_INVALIDARG;
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
paLimit = NULL;
if (pHalt != NULL && pHalt->pHaltRange != NULL)
{
if ((pRangeP = GetCRange_NA(pHalt->pHaltRange)) == NULL)
return E_FAIL;
paLimit = (pHalt->aHaltPos == TF_ANCHOR_START) ? pRangeP->_GetStart() : pRangeP->_GetEnd();
}
if (pHalt == NULL || pHalt->dwFlags == 0)
{
// caller doesn't care about special chars, so we can do it the easy way
hr = _paStart->Shift(0, cchReq, pcch, paLimit);
}
else
{
// caller wants us to halt for special chars, need to read text
if (_paStart->Clone(&paShift) != S_OK)
return E_FAIL;
hr = _ShiftConditional(paShift, paLimit, cchReq, pcch, pHalt);
if (hr == S_OK)
{
hr = _paStart->ShiftTo(paShift);
}
paShift->Release();
}
if (hr != S_OK)
return E_FAIL;
// don't let the start advance past the end
if (cchReq > 0 && CompareAnchors(_paStart, _paEnd) > 0)
{
_paEnd->ShiftTo(_paStart);
}
return S_OK;
}
//+---------------------------------------------------------------------------
//
// ShiftEnd
//
//----------------------------------------------------------------------------
STDAPI CRange::ShiftEnd(TfEditCookie ec, LONG cchReq, LONG *pcch, const TF_HALTCOND *pHalt)
{
CRange *pRangeP;
IAnchor *paLimit;
IAnchor *paShift;
HRESULT hr;
Perf_IncCounter(PERF_SHIFTEND_COUNT);
if (pcch == NULL)
return E_INVALIDARG;
*pcch = 0;
if (pHalt != NULL && (pHalt->dwFlags & ~TF_HF_OBJECT))
return E_INVALIDARG;
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
paLimit = NULL;
if (pHalt != NULL && pHalt->pHaltRange != NULL)
{
if ((pRangeP = GetCRange_NA(pHalt->pHaltRange)) == NULL)
return E_FAIL;
paLimit = (pHalt->aHaltPos == TF_ANCHOR_START) ? pRangeP->_GetStart() : pRangeP->_GetEnd();
}
if (pHalt == NULL || pHalt->dwFlags == 0)
{
// caller doesn't care about special chars, so we can do it the easy way
hr = _paEnd->Shift(0, cchReq, pcch, paLimit);
}
else
{
// caller wants us to halt for special chars, need to read text
if (_paEnd->Clone(&paShift) != S_OK)
return E_FAIL;
hr = _ShiftConditional(paShift, paLimit, cchReq, pcch, pHalt);
if (hr == S_OK)
{
hr = _paEnd->ShiftTo(paShift);
}
paShift->Release();
}
if (hr != S_OK)
return E_FAIL;
// don't let the start advance past the end
if (cchReq < 0 && CompareAnchors(_paStart, _paEnd) > 0)
{
_paStart->ShiftTo(_paEnd);
}
return S_OK;
}
//+---------------------------------------------------------------------------
//
// _ShiftConditional
//
//----------------------------------------------------------------------------
HRESULT CRange::_ShiftConditional(IAnchor *paStart, IAnchor *paLimit, LONG cchReq, LONG *pcch, const TF_HALTCOND *pHalt)
{
HRESULT hr;
ITextStoreAnchor *ptsi;
LONG cchRead;
LONG cch;
LONG i;
LONG iStop;
LONG delta;
BOOL fHaltObj;
WCHAR ach[64];
Assert(*pcch == 0);
Assert(pHalt && pHalt->dwFlags);
hr = S_OK;
ptsi = _pic->_ptsi;
fHaltObj = pHalt->dwFlags & TF_HF_OBJECT;
delta = (cchReq > 0) ? +1 : -1;
while (cchReq != 0)
{
if (cchReq > 0)
{
cch = (LONG)min(cchReq, ARRAYSIZE(ach));
}
else
{
// going backwards is tricky!
cch = max(cchReq, -(LONG)ARRAYSIZE(ach));
hr = paStart->Shift(0, cch, &cchRead, paLimit);
if (hr != S_OK)
break;
if (cchRead == 0)
break; // at top of doc or hit paLimit
cch = -cchRead; // must read text forward
}
Perf_IncCounter(PERF_SHIFTCOND_GETTEXT);
hr = ptsi->GetText(0, paStart, paLimit, ach, cch, (ULONG *)&cchRead, (cchReq > 0));
if (hr != S_OK)
break;
if (cchRead == 0)
break; // end of doc
if (fHaltObj)
{
// scan for special chars
if (cchReq > 0)
{
// scan left-to-right
i = 0;
iStop = cchRead;
}
else
{
// scan right-to-left
i = cchRead - 1;
iStop = -1;
}
for (; i != iStop; i += delta)
{
if (ach[i] == TS_CHAR_EMBEDDED)
{
if (cchReq > 0)
{
hr = paStart->Shift(0, i - cchRead, &cch, NULL);
cchReq = cchRead = i;
}
else
{
hr = paStart->Shift(0, i + 1, &cch, NULL);
cchRead -= i + 1;
cchReq = -cchRead;
}
goto ExitLoop;
}
}
}
ExitLoop:
if (cchReq < 0)
{
cchRead = -cchRead;
}
cchReq -= cchRead;
*pcch += cchRead;
}
if (hr != S_OK)
{
*pcch = 0;
}
return hr;
}
//+---------------------------------------------------------------------------
//
// ShiftStartToRange
//
//----------------------------------------------------------------------------
STDAPI CRange::ShiftStartToRange(TfEditCookie ec, ITfRange *pRange, TfAnchor aPos)
{
CRange *pRangeP;
HRESULT hr;
if (pRange == NULL)
return E_INVALIDARG;
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
if ((pRangeP = GetCRange_NA(pRange)) == NULL)
return E_INVALIDARG;
if (!VerifySameContext(this, pRangeP))
return E_INVALIDARG;
pRangeP->_QuickCheckCrossedAnchors();
hr = _paStart->ShiftTo((aPos == TF_ANCHOR_START) ? pRangeP->_GetStart() : pRangeP->_GetEnd());
// don't let the start advance past the end
if (CompareAnchors(_paStart, _paEnd) > 0)
{
_paEnd->ShiftTo(_paStart);
}
return hr;
}
//+---------------------------------------------------------------------------
//
// ShiftEndToRange
//
//----------------------------------------------------------------------------
STDAPI CRange::ShiftEndToRange(TfEditCookie ec, ITfRange *pRange, TfAnchor aPos)
{
CRange *pRangeP;
HRESULT hr;
if (pRange == NULL)
return E_INVALIDARG;
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
if ((pRangeP = GetCRange_NA(pRange)) == NULL)
return E_FAIL;
if (!VerifySameContext(this, pRangeP))
return E_INVALIDARG;
pRangeP->_QuickCheckCrossedAnchors();
hr = _paEnd->ShiftTo((aPos == TF_ANCHOR_START) ? pRangeP->_GetStart() : pRangeP->_GetEnd());
// don't let the end advance past the start
if (CompareAnchors(_paStart, _paEnd) > 0)
{
_paStart->ShiftTo(_paEnd);
}
return hr;
}
//+---------------------------------------------------------------------------
//
// ShiftStartRegion
//
//----------------------------------------------------------------------------
STDAPI CRange::ShiftStartRegion(TfEditCookie ec, TfShiftDir dir, BOOL *pfNoRegion)
{
HRESULT hr;
if (pfNoRegion == NULL)
return E_INVALIDARG;
*pfNoRegion = TRUE;
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
hr = _paStart->ShiftRegion(0, (TsShiftDir)dir, pfNoRegion);
if (hr == S_OK && dir == TF_SD_FORWARD && !*pfNoRegion)
{
// don't let the start advance past the end
if (CompareAnchors(_paStart, _paEnd) > 0)
{
_paEnd->ShiftTo(_paStart);
}
}
else if (hr == E_NOTIMPL)
{
// app doesn't support regions, so we can still succeed
// it's just that there's no region to shift over
*pfNoRegion = TRUE; // be paranoid, the app could be wacky
hr = S_OK;
}
return hr;
}
//+---------------------------------------------------------------------------
//
// ShiftEndRegion
//
//----------------------------------------------------------------------------
STDAPI CRange::ShiftEndRegion(TfEditCookie ec, TfShiftDir dir, BOOL *pfNoRegion)
{
HRESULT hr;
if (pfNoRegion == NULL)
return E_INVALIDARG;
*pfNoRegion = TRUE;
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
hr = _paEnd->ShiftRegion(0, (TsShiftDir)dir, pfNoRegion);
if (hr == S_OK && dir == TF_SD_BACKWARD && !*pfNoRegion)
{
// don't let the end advance past the start
if (CompareAnchors(_paStart, _paEnd) > 0)
{
_paStart->ShiftTo(_paEnd);
}
}
else if (hr == E_NOTIMPL)
{
// app doesn't support regions, so we can still succeed
// it's just that there's no region to shift over
*pfNoRegion = TRUE; // be paranoid, the app could be wacky
hr = S_OK;
}
return hr;
}
//+---------------------------------------------------------------------------
//
// _SnapToRegion
//
//----------------------------------------------------------------------------
#if 0
HRESULT CRange::_SnapToRegion(DWORD dwFlags)
{
ITfRange *range;
TF_HALTCOND hc;
LONG cch;
HRESULT hr;
if (Clone(&range) != S_OK)
return E_OUTOFMEMORY;
hc.pHaltRange = (ITfRangeAnchor *)this;
hc.dwFlags = 0;
if (dwFlags & TF_GS_SNAPREGION_START)
{
if ((hr = range->Collapse(BACKDOOR_EDIT_COOKIE, TF_ANCHOR_START)) != S_OK)
goto Exit;
hc.aHaltPos = TF_ANCHOR_END;
do
{
if ((hr = range->ShiftEnd(BACKDOOR_EDIT_COOKIE, LONG_MAX, &cch, &hc)) != S_OK)
goto Exit;
}
while (cch >= LONG_MAX); // just in case this is a _really_ huge doc
hr = ShiftEndToRange(BACKDOOR_EDIT_COOKIE, range, TF_ANCHOR_END);
}
else
{
Assert(dwFlags & TF_GS_SNAPREGION_END);
if ((hr = range->Collapse(BACKDOOR_EDIT_COOKIE, TF_ANCHOR_END)) != S_OK)
goto Exit;
hc.aHaltPos = TF_ANCHOR_START;
do
{
if ((hr = range->ShiftStart(BACKDOOR_EDIT_COOKIE, LONG_MIN, &cch, &hc)) != S_OK)
goto Exit;
}
while (cch <= LONG_MIN); // just in case this is a _really_ huge doc
hr = ShiftStartToRange(BACKDOOR_EDIT_COOKIE, range, TF_ANCHOR_START);
}
Exit:
if (hr != S_OK)
{
hr = E_FAIL;
}
range->Release();
return hr;
}
#endif // 0
//+---------------------------------------------------------------------------
//
// IsEmpty
//
//----------------------------------------------------------------------------
STDAPI CRange::IsEmpty(TfEditCookie ec, BOOL *pfEmpty)
{
return IsEqualStart(ec, (ITfRangeAnchor *)this, TF_ANCHOR_END, pfEmpty);
}
//+---------------------------------------------------------------------------
//
// Collapse
//
//----------------------------------------------------------------------------
STDAPI CRange::Collapse(TfEditCookie ec, TfAnchor aPos)
{
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
return (aPos == TF_ANCHOR_START) ? _paEnd->ShiftTo(_paStart) : _paStart->ShiftTo(_paEnd);
}
//+---------------------------------------------------------------------------
//
// IsEqualStart
//
//----------------------------------------------------------------------------
STDAPI CRange::IsEqualStart(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, BOOL *pfEqual)
{
return _IsEqualX(ec, TF_ANCHOR_START, pWith, aPos, pfEqual);
}
//+---------------------------------------------------------------------------
//
// IsEqualEnd
//
//----------------------------------------------------------------------------
STDAPI CRange::IsEqualEnd(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, BOOL *pfEqual)
{
return _IsEqualX(ec, TF_ANCHOR_END, pWith, aPos, pfEqual);
}
//+---------------------------------------------------------------------------
//
// _IsEqualX
//
//----------------------------------------------------------------------------
HRESULT CRange::_IsEqualX(TfEditCookie ec, TfAnchor aPosThisRange, ITfRange *pWith, TfAnchor aPos, BOOL *pfEqual)
{
LONG lComp;
HRESULT hr;
if (pfEqual == NULL)
return E_INVALIDARG;
*pfEqual = FALSE;
// perf: we could check TS_SS_NOHIDDENTEXT for better perf
hr = _CompareX(ec, aPosThisRange, pWith, aPos, &lComp);
if (hr != S_OK)
return hr;
*pfEqual = (lComp == 0);
return S_OK;
}
//+---------------------------------------------------------------------------
//
// CompareStart
//
//----------------------------------------------------------------------------
STDAPI CRange::CompareStart(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, LONG *plResult)
{
return _CompareX(ec, TF_ANCHOR_START, pWith, aPos, plResult);
}
//+---------------------------------------------------------------------------
//
// CompareEnd
//
//----------------------------------------------------------------------------
STDAPI CRange::CompareEnd(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, LONG *plResult)
{
return _CompareX(ec, TF_ANCHOR_END, pWith, aPos, plResult);
}
//+---------------------------------------------------------------------------
//
// _CompareX
//
//----------------------------------------------------------------------------
HRESULT CRange::_CompareX(TfEditCookie ec, TfAnchor aPosThisRange, ITfRange *pWith, TfAnchor aPos, LONG *plResult)
{
CRange *pRangeP;
IAnchor *paThis;
IAnchor *paWith;
IAnchor *paTest;
LONG lComp;
LONG cch;
BOOL fEqual;
HRESULT hr;
if (plResult == NULL)
return E_INVALIDARG;
*plResult = 0;
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
if (pWith == NULL)
return E_INVALIDARG;
if ((pRangeP = GetCRange_NA(pWith)) == NULL)
return E_INVALIDARG;
if (!VerifySameContext(this, pRangeP))
return E_INVALIDARG;
pRangeP->_QuickCheckCrossedAnchors();
paWith = (aPos == TF_ANCHOR_START) ? pRangeP->_GetStart() : pRangeP->_GetEnd();
paThis = (aPosThisRange == TF_ANCHOR_START) ? _paStart : _paEnd;
if (paThis->Compare(paWith, &lComp) != S_OK)
return E_FAIL;
if (lComp == 0) // exact match
{
Assert(*plResult == 0);
return S_OK;
}
// we need to account for hidden text, so we actually have to do a shift
// perf: we could check TS_SS_NOHIDDENTEXT for better perf
if (paThis->Shift(TS_SHIFT_COUNT_ONLY, (lComp < 0) ? 1 : -1, &cch, paWith) != S_OK)
return E_FAIL;
if (cch == 0)
{
// nothing but hidden text between the two anchors?
// one special case: we might have hit a region boundary
if (paThis->Clone(&paTest) != S_OK || paTest == NULL)
return E_FAIL;
hr = E_FAIL;
// if we're not at paWith after the shift, we must have hit a region
if (paTest->Shift(0, (lComp < 0) ? 1 : -1, &cch, paWith) != S_OK)
goto ReleaseTest;
Assert(cch == 0);
if (paTest->IsEqual(paWith, &fEqual) != S_OK)
goto ReleaseTest;
hr = S_OK;
ReleaseTest:
paTest->Release();
if (hr != S_OK)
return E_FAIL;
if (fEqual)
{
Assert(*plResult == 0);
return S_OK;
}
}
*plResult = lComp;
return S_OK;
}
//+---------------------------------------------------------------------------
//
// GetGravity
//
//----------------------------------------------------------------------------
STDAPI CRange::AdjustForInsert(TfEditCookie ec, ULONG cchInsert, BOOL *pfInsertOk)
{
TfGravity gStart;
TfGravity gEnd;
IAnchor *paStartResult;
IAnchor *paEndResult;
HRESULT hr;
if (pfInsertOk == NULL)
return E_INVALIDARG;
*pfInsertOk = FALSE;
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
hr = _pic->_ptsi->QueryInsert(_paStart, _paEnd, cchInsert, &paStartResult, &paEndResult);
if (hr == E_NOTIMPL)
{
// ok, just allow the request
goto Exit;
}
else if (hr != S_OK)
{
Assert(*pfInsertOk == FALSE);
return E_FAIL;
}
else if (paStartResult == NULL || paEndResult == NULL)
{
Assert(paEndResult == NULL);
// NULL out params means no insert possible
Assert(*pfInsertOk == FALSE);
return S_OK;
}
// all set, just swap anchors and make sure gravity doesn't change
GetGravity(&gStart, &gEnd);
_paStart->Release();
_paEnd->Release();
_paStart = paStartResult;
_paEnd = paEndResult;
_SetGravity(gStart, gEnd, TRUE);
Exit:
*pfInsertOk = TRUE;
return S_OK;
}
//+---------------------------------------------------------------------------
//
// GetGravity
//
//----------------------------------------------------------------------------
STDAPI CRange::GetGravity(TfGravity *pgStart, TfGravity *pgEnd)
{
TsGravity gStart;
TsGravity gEnd;
if (pgStart == NULL || pgEnd == NULL)
return E_INVALIDARG;
_paStart->GetGravity(&gStart);
_paEnd->GetGravity(&gEnd);
*pgStart = (gStart == TS_GR_BACKWARD) ? TF_GRAVITY_BACKWARD : TF_GRAVITY_FORWARD;
*pgEnd = (gEnd == TS_GR_BACKWARD) ? TF_GRAVITY_BACKWARD : TF_GRAVITY_FORWARD;
return S_OK;
}
//+---------------------------------------------------------------------------
//
// SetGravity
//
//----------------------------------------------------------------------------
STDAPI CRange::SetGravity(TfEditCookie ec, TfGravity gStart, TfGravity gEnd)
{
if (!_IsValidEditCookie(ec, TF_ES_READ))
{
Assert(0);
return TF_E_NOLOCK;
}
return _SetGravity(gStart, gEnd, TRUE);
}
//+---------------------------------------------------------------------------
//
// _SetGravity
//
//----------------------------------------------------------------------------
HRESULT CRange::_SetGravity(TfGravity gStart, TfGravity gEnd, BOOL fCheckCrossedAnchors)
{
if (fCheckCrossedAnchors)
{
// make sure we're not crossed in case we're switching away from inward gravity
_QuickCheckCrossedAnchors();
}
if (_paStart->SetGravity((TsGravity)gStart) != S_OK)
return E_FAIL;
if (_paEnd->SetGravity((TsGravity)gEnd) != S_OK)
return E_FAIL;
_InitLastLockReleaseId((TsGravity)gStart, (TsGravity)gEnd);
return S_OK;
}
//+---------------------------------------------------------------------------
//
// AdviseSink
//
//----------------------------------------------------------------------------
STDAPI CRange::AdviseSink(REFIID riid, IUnknown *punk, DWORD *pdwCookie)
{
const IID *rgiid = &IID_ITfRangeChangeSink;
HRESULT hr;
if (_prgChangeSinks == NULL)
{
// we delay allocate our sink container
if ((_prgChangeSinks = new CStructArray<GENERICSINK>) == NULL)
return E_OUTOFMEMORY;
}
hr = GenericAdviseSink(riid, punk, &rgiid, _prgChangeSinks, 1, pdwCookie);
if (hr == S_OK && _prgChangeSinks->Count() == 1)
{
// add this range to the list of ranges with sinks in the icsub
_nextOnChangeRangeInIcsub = _pic->_pOnChangeRanges;
_pic->_pOnChangeRanges = this;
// start tracking anchor collapses
//_paStart->TrackCollapse(TRUE);
//_paEnd->TrackCollapse(TRUE);
}
return hr;
}
//+---------------------------------------------------------------------------
//
// UnadviseSink
//
//----------------------------------------------------------------------------
STDAPI CRange::UnadviseSink(DWORD dwCookie)
{
CRange *pRange;
CRange **ppRange;
HRESULT hr;
if (_prgChangeSinks == NULL)
return CONNECT_E_NOCONNECTION;
hr = GenericUnadviseSink(_prgChangeSinks, 1, dwCookie);
if (hr == S_OK && _prgChangeSinks->Count() == 0)
{
// remove this range from the list of ranges in its icsub
ppRange = &_pic->_pOnChangeRanges;
while (pRange = *ppRange)
{
if (pRange == this)
{
*ppRange = pRange->_nextOnChangeRangeInIcsub;
break;
}
ppRange = &pRange->_nextOnChangeRangeInIcsub;
}
// stop tracking anchor collapses
//_paStart->TrackCollapse(FALSE);
//_paEnd->TrackCollapse(FALSE);
}
return hr;
}
//+---------------------------------------------------------------------------
//
// GetExtent
//
//----------------------------------------------------------------------------
STDAPI CRange::GetExtent(LONG *pacpAnchor, LONG *pcch)
{
CAnchorRef *par;
HRESULT hr = E_FAIL;
if (pacpAnchor == NULL || pcch == NULL)
return E_INVALIDARG;
*pacpAnchor = 0;
*pcch = 0;
// make the validation call anyways because we do other stuff in there
_IsValidEditCookie(BACKDOOR_EDIT_COOKIE, TF_ES_READ);
if ((par = GetCAnchorRef_NA(_paStart)) != NULL)
{
// we have a wrapped ACP impl, this is easy
*pacpAnchor = par->_GetACP();
if ((par = GetCAnchorRef_NA(_paEnd)) == NULL)
goto ErrorExit;
*pcch = par->_GetACP() - *pacpAnchor;
hr = S_OK;
}
else
{
Assert(0); // who's doing this?
// we fail if someone tries to do GetExtentACP on a
// non-acp text store. Users of this method should
// be aware of whether or not they are using an acp
// store.
}
return hr;
ErrorExit:
*pacpAnchor = 0;
*pcch = 0;
return E_FAIL;
}
//+---------------------------------------------------------------------------
//
// GetExtent
//
//----------------------------------------------------------------------------
STDAPI CRange::GetExtent(IAnchor **ppaStart, IAnchor **ppaEnd)
{
if (ppaStart == NULL || ppaEnd == NULL)
return E_INVALIDARG;
*ppaStart = NULL;
*ppaEnd = NULL;
// make the validation call anyways because we do other stuff in there
_IsValidEditCookie(BACKDOOR_EDIT_COOKIE, TF_ES_READ);
if (_paStart->Clone(ppaStart) != S_OK)
return E_FAIL;
if (_paEnd->Clone(ppaEnd) != S_OK)
{
SafeReleaseClear(*ppaStart);
return E_FAIL;
}
return S_OK;
}
//+---------------------------------------------------------------------------
//
// SetExtent
//
//----------------------------------------------------------------------------
STDAPI CRange::SetExtent(LONG acpAnchor, LONG cch)
{
CAnchorRef *par;
IAnchor *paStart;
IAnchor *paEnd;
// make the validation call anyways because we do other stuff in there
_IsValidEditCookie(BACKDOOR_EDIT_COOKIE, TF_ES_READ);
if (acpAnchor < 0 || cch < 0)
return E_INVALIDARG;
paStart = paEnd = NULL;
if ((par = GetCAnchorRef_NA(_paStart)) != NULL)
{
// we have a wrapped ACP impl, this is easy
// need to work with Clones to handle failure gracefully
if (FAILED(_paStart->Clone(&paStart)))
goto ErrorExit;
if ((par = GetCAnchorRef_NA(paStart)) == NULL)
goto ErrorExit;
if (!par->_SetACP(acpAnchor))
goto ErrorExit;
if (FAILED(_paEnd->Clone(&paEnd)))
goto ErrorExit;
if ((par = GetCAnchorRef_NA(paEnd)) == NULL)
goto ErrorExit;
if (!par->_SetACP(acpAnchor + cch))
goto ErrorExit;
}
else
{
Assert(0); // who's doing this?
// we fail if someone tries to do SetExtentACP on a
// non-acp text store. Users of this method should
// be aware of whether or not they are using an acp
// store.
goto ErrorExit;
}
SafeRelease(_paStart);
SafeRelease(_paEnd);
_paStart = paStart;
_paEnd = paEnd;
return S_OK;
ErrorExit:
SafeRelease(paStart);
SafeRelease(paEnd);
return E_FAIL;
}
//+---------------------------------------------------------------------------
//
// SetExtent
//
//----------------------------------------------------------------------------
STDAPI CRange::SetExtent(IAnchor *paStart, IAnchor *paEnd)
{
IAnchor *paStartClone;
IAnchor *paEndClone;
// make the validation call anyways because we do other stuff in there
_IsValidEditCookie(BACKDOOR_EDIT_COOKIE, TF_ES_READ);
if (paStart == NULL || paEnd == NULL)
return E_INVALIDARG;
if (CompareAnchors(paStart, paEnd) > 0)
return E_INVALIDARG;
if (paStart->Clone(&paStartClone) != S_OK)
return E_FAIL;
if (paEnd->Clone(&paEndClone) != S_OK)
{
paStartClone->Release();
return E_FAIL;
}
SafeRelease(_paStart);
SafeRelease(_paEnd);
_paStart = paStartClone;
_paEnd = paEndClone;
return S_OK;
}
//+---------------------------------------------------------------------------
//
// _PreEditCompositionCheck
//
//----------------------------------------------------------------------------
HRESULT CRange::_PreEditCompositionCheck(TfEditCookie ec, CComposition **ppComposition, BOOL *pfNewComposition)
{
IRC irc;
// any active compositions?
*pfNewComposition = FALSE;
irc = CComposition::_IsRangeCovered(_pic, _pic->_GetClientInEditSession(ec), _paStart, _paEnd, ppComposition);
if (irc == IRC_COVERED)
{
// this range is within an owned composition
Assert(*ppComposition != NULL);
return S_OK;
}
else if (irc == IRC_OUTSIDE)
{
// the caller owns compositions, but this range isn't wholly within them
return TF_E_RANGE_NOT_COVERED;
}
else
{
Assert(irc == IRC_NO_OWNEDCOMPOSITIONS);
}
// not covered, need to create a default composition
if (_pic->_StartComposition(ec, _paStart, _paEnd, NULL, ppComposition) != S_OK)
return E_FAIL;
if (*ppComposition != NULL)
{
*pfNewComposition = TRUE;
return S_OK;
}
return TF_E_COMPOSITION_REJECTED;
}