// // anchoref.cpp // // CAnchorRef // #include "private.h" #include "anchoref.h" #include "anchor.h" #include "acp2anch.h" #include "globals.h" #include "normal.h" #include "memcache.h" #include "ic.h" #include "txtcache.h" /* 9135f8f0-38e6-11d3-a745-0050040ab407 */ const IID IID_PRIV_CANCHORREF = { 0x9135f8f0, 0x38e6, 0x11d3, {0xa7, 0x45, 0x00, 0x50, 0x04, 0x0a, 0xb4, 0x07} }; DBG_ID_INSTANCE(CAnchorRef); MEMCACHE *CAnchorRef::_s_pMemCache = NULL; //+--------------------------------------------------------------------------- // // _InitClass // //---------------------------------------------------------------------------- /* static */ void CAnchorRef::_InitClass() { _s_pMemCache = MemCache_New(128); } //+--------------------------------------------------------------------------- // // _UninitClass // //---------------------------------------------------------------------------- /* static */ void CAnchorRef::_UninitClass() { if (_s_pMemCache == NULL) return; MemCache_Delete(_s_pMemCache); _s_pMemCache = NULL; } //+--------------------------------------------------------------------------- // // IUnknown // //---------------------------------------------------------------------------- STDAPI CAnchorRef::QueryInterface(REFIID riid, void **ppvObj) { if (&riid == &IID_PRIV_CANCHORREF || IsEqualIID(riid, IID_PRIV_CANCHORREF)) { *ppvObj = SAFECAST(this, CAnchorRef *); return S_OK; // No AddRef for IID_PRIV_CANCHORREF! this is a private IID.... } *ppvObj = NULL; if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IAnchor)) { *ppvObj = SAFECAST(this, IAnchor *); } if (*ppvObj) { AddRef(); return S_OK; } return E_NOINTERFACE; } STDAPI_(ULONG) CAnchorRef::AddRef() { return ++_cRef; } STDAPI_(ULONG) CAnchorRef::Release() { _cRef--; Assert(_cRef >= 0); if (_cRef == 0) { delete this; return 0; } return _cRef; } //+--------------------------------------------------------------------------- // // SetGravity // //---------------------------------------------------------------------------- STDAPI CAnchorRef::SetGravity(TsGravity gravity) { _fForwardGravity = (gravity == TS_GR_FORWARD ? 1 : 0); return S_OK; } //+--------------------------------------------------------------------------- // // GetGravity // //---------------------------------------------------------------------------- STDAPI CAnchorRef::GetGravity(TsGravity *pgravity) { if (pgravity == NULL) return E_INVALIDARG; *pgravity = _fForwardGravity ? TS_GR_FORWARD : TS_GR_BACKWARD; return S_OK; } //+--------------------------------------------------------------------------- // // IsEqual // //---------------------------------------------------------------------------- STDAPI CAnchorRef::IsEqual(IAnchor *paWith, BOOL *pfEqual) { LONG lResult; HRESULT hr; if (pfEqual == NULL) return E_INVALIDARG; *pfEqual = FALSE; // in our implementation, Compare is no less efficient, so just use that if ((hr = Compare(paWith, &lResult)) == S_OK) { *pfEqual = (lResult == 0); } return hr; } //+--------------------------------------------------------------------------- // // Compare // //---------------------------------------------------------------------------- STDAPI CAnchorRef::Compare(IAnchor *paWith, LONG *plResult) { CAnchorRef *parWith; LONG acpThis; LONG acpWith; CACPWrap *paw; if (plResult == NULL) return E_INVALIDARG; //_paw->_Dbg_AssertNoAppLock(); // can't assert this because we use it legitimately while updating the span set *plResult = 0; if ((parWith = GetCAnchorRef_NA(paWith)) == NULL) return E_FAIL; // quick test for equality // we still need to check for equality again below because of normalization if (_pa == parWith->_pa) { Assert(*plResult == 0); return S_OK; } acpThis = _pa->GetIch(); acpWith = parWith->_pa->GetIch(); paw = _pa->_GetWrap(); // we can't do a compare if either anchor is un-normalized // except when the app holds the lock (in which case we're being called from // a span set update which does not need to be normalized) if (!paw->_InOnTextChange()) { // we only actually have to normalize the anchor to the left if (acpThis < acpWith) { if (!_pa->IsNormalized()) { paw->_NormalizeAnchor(_pa); acpThis = _pa->GetIch(); acpWith = parWith->_pa->GetIch(); } } else if (acpThis > acpWith) { if (!parWith->_pa->IsNormalized()) { paw->_NormalizeAnchor(parWith->_pa); acpThis = _pa->GetIch(); acpWith = parWith->_pa->GetIch(); } } } if (acpThis < acpWith) { *plResult = -1; } else if (acpThis > acpWith) { *plResult = +1; } else { Assert(*plResult == 0); } return S_OK; } //+--------------------------------------------------------------------------- // // Shift // //---------------------------------------------------------------------------- STDAPI CAnchorRef::Shift(DWORD dwFlags, LONG cchReq, LONG *pcch, IAnchor *paHaltAnchor) { CAnchorRef *parHaltAnchor; CACPWrap *paw; LONG acpHalt; LONG acpThis; LONG dacp; HRESULT hr; Perf_IncCounter(PERF_ANCHOR_SHIFT); if (dwFlags & ~(TS_SHIFT_COUNT_HIDDEN | TS_SHIFT_HALT_HIDDEN | TS_SHIFT_HALT_VISIBLE | TS_SHIFT_COUNT_ONLY)) return E_INVALIDARG; if ((dwFlags & (TS_SHIFT_HALT_HIDDEN | TS_SHIFT_HALT_VISIBLE)) == (TS_SHIFT_HALT_HIDDEN | TS_SHIFT_HALT_VISIBLE)) return E_INVALIDARG; // illegal to set both flags if (dwFlags & (TS_SHIFT_COUNT_HIDDEN | TS_SHIFT_HALT_HIDDEN | TS_SHIFT_HALT_VISIBLE)) return E_NOTIMPL; // Issue: should support these if (pcch == NULL) return E_INVALIDARG; paw = _pa->_GetWrap(); paw->_Dbg_AssertNoAppLock(); if (paw->_IsDisconnected()) { *pcch = 0; return TF_E_DISCONNECTED; } *pcch = cchReq; // assume success if (cchReq == 0) return S_OK; acpThis = _pa->GetIch(); hr = E_FAIL; if (paHaltAnchor != NULL) { if ((parHaltAnchor = GetCAnchorRef_NA(paHaltAnchor)) == NULL) goto Exit; acpHalt = parHaltAnchor->_pa->GetIch(); // return now if the halt is our base acp // (we treat acpHalt == acpThis as a nop below, anything // more ticky has problems with over/underflow) if (acpHalt == acpThis) { *pcch = 0; return S_OK; } } else { // nop the acpHalt acpHalt = acpThis; } // we can initially bound cchReq by pretending acpHalt // is plain text, an upper bound if (cchReq < 0 && acpHalt < acpThis) { cchReq = max(cchReq, acpHalt - acpThis); } else if (cchReq > 0 && acpHalt > acpThis) { cchReq = min(cchReq, acpHalt - acpThis); } // do the expensive work if (FAILED(hr = AppTextOffset(paw->_GetTSI(), acpThis, cchReq, &dacp, ATO_SKIP_HIDDEN))) goto Exit; // now we can clip percisely if (cchReq < 0 && acpHalt < acpThis) { dacp = max(dacp, acpHalt - acpThis); hr = S_FALSE; } else if (cchReq > 0 && acpHalt > acpThis) { dacp = min(dacp, acpHalt - acpThis); hr = S_FALSE; } if (hr == S_FALSE) { // nb: if we remembered whether or not we actually truncated cchReq above // before and/or after the AppTextOffset call, we could avoid always calling // PlainTextOffset when paHaltAnchor != NULL // request got clipped, need to find the plain count PlainTextOffset(paw->_GetTSI(), acpThis, dacp, pcch); // perf: we could get this info by modifying AppTextOffset } if (!(dwFlags & TS_SHIFT_COUNT_ONLY)) { hr = _SetACP(acpThis + dacp) ? S_OK : E_FAIL; } else { // caller doesn't want the anchor updated, just wants a count hr = S_OK; } Exit: if (FAILED(hr)) { *pcch = 0; } // return value should never exceed what the caller requested! Assert((cchReq >= 0 && *pcch <= cchReq) || (cchReq < 0 && *pcch >= cchReq)); return hr; } //+--------------------------------------------------------------------------- // // ShiftTo // //---------------------------------------------------------------------------- STDAPI CAnchorRef::ShiftTo(IAnchor *paSite) { CAnchorRef *parSite; LONG acpSite; if (paSite == NULL) return E_INVALIDARG; //_paw->_Dbg_AssertNoAppLock(); // can't assert this because we use it legitimately while updating the span set if ((parSite = GetCAnchorRef_NA(paSite)) == NULL) return E_FAIL; acpSite = parSite->_pa->GetIch(); return _SetACP(acpSite) ? S_OK : E_FAIL; } //+--------------------------------------------------------------------------- // // ShiftRegion // //---------------------------------------------------------------------------- STDAPI CAnchorRef::ShiftRegion(DWORD dwFlags, TsShiftDir dir, BOOL *pfNoRegion) { LONG acp; ULONG cch; LONG i; ULONG ulRunInfoOut; LONG acpNext; ITextStoreACP *ptsi; CACPWrap *paw; WCHAR ch; DWORD dwATO; Perf_IncCounter(PERF_SHIFTREG_COUNTER); if (pfNoRegion == NULL) return E_INVALIDARG; *pfNoRegion = TRUE; if (dwFlags & ~(TS_SHIFT_COUNT_HIDDEN | TS_SHIFT_COUNT_ONLY)) return E_INVALIDARG; paw = _pa->_GetWrap(); if (paw->_IsDisconnected()) return TF_E_DISCONNECTED; acp = _GetACP(); ptsi = paw->_GetTSI(); if (dir == TS_SD_BACKWARD) { // scan backwards for the preceding char dwATO = ATO_IGNORE_REGIONS | ((dwFlags & TS_SHIFT_COUNT_HIDDEN) ? 0 : ATO_SKIP_HIDDEN); if (FAILED(AppTextOffset(ptsi, acp, -1, &i, dwATO))) return E_FAIL; if (i == 0) // bod return S_OK; acp += i; } else { // normalize this guy so we can just test the next char if (!_pa->IsNormalized()) { paw->_NormalizeAnchor(_pa); acp = _GetACP(); } // skip past any hidden text if (!(dwFlags & TS_SHIFT_COUNT_HIDDEN)) { acp = Normalize(paw->_GetTSI(), acp, NORM_SKIP_HIDDEN); } } // insure we're next to a TS_CHAR_REGION Perf_IncCounter(PERF_ANCHOR_REGION_GETTEXT); if (CProcessTextCache::GetText(ptsi, acp, -1, &ch, 1, &cch, NULL, 0, &ulRunInfoOut, &acpNext) != S_OK) return E_FAIL; if (cch == 0) // eod return S_OK; if (ch != TS_CHAR_REGION) return S_OK; // no region, so just report that in pfNoRegion if (!(dwFlags & TS_SHIFT_COUNT_ONLY)) // does caller want us to move the anchor? { if (dir == TS_SD_FORWARD) { // skip over the TS_CHAR_REGION acp += 1; } if (!_SetACP(acp)) return E_FAIL; } *pfNoRegion = FALSE; return S_OK; } //+--------------------------------------------------------------------------- // // SetChangeHistoryMask // //---------------------------------------------------------------------------- STDAPI CAnchorRef::SetChangeHistoryMask(DWORD dwMask) { Assert(0); // Issue: todo return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // GetChangeHistory // //---------------------------------------------------------------------------- STDAPI CAnchorRef::GetChangeHistory(DWORD *pdwHistory) { if (pdwHistory == NULL) return E_INVALIDARG; *pdwHistory = _dwHistory; return S_OK; } //+--------------------------------------------------------------------------- // // ClearChangeHistory // //---------------------------------------------------------------------------- STDAPI CAnchorRef::ClearChangeHistory() { _dwHistory = 0; return S_OK; } //+--------------------------------------------------------------------------- // // Clone // //---------------------------------------------------------------------------- STDAPI CAnchorRef::Clone(IAnchor **ppaClone) { if (ppaClone == NULL) return E_INVALIDARG; *ppaClone = _pa->_GetWrap()->_CreateAnchorAnchor(_pa, _fForwardGravity ? TS_GR_FORWARD : TS_GR_BACKWARD); return (*ppaClone != NULL) ? S_OK : E_FAIL; } //+--------------------------------------------------------------------------- // // _SetACP // //---------------------------------------------------------------------------- BOOL CAnchorRef::_SetACP(LONG acp) { CACPWrap *paw; if (_pa->GetIch() == acp) return TRUE; // already positioned here paw = _pa->_GetWrap(); paw->_Remove(this); if (FAILED(paw->_Insert(this, acp))) { // Issue: // we need to add a method the CACPWrap // that swaps a CAnchorRef, preserving the old // value if a new one cannot be inserted (prob. // because memory is low). Assert(0); // we have no code to handle this! return FALSE; } return TRUE; }