// // acp2anch.cpp // #include "private.h" #include "acp2anch.h" #include "ic.h" #include "normal.h" #include "ic.h" #include "range.h" #include "anchoref.h" #include "txtcache.h" /* 4eb058b0-34ae-11d3-a745-0050040ab407 */ const IID IID_PRIV_ACPWRAP = { 0x4eb058b0, 0x34ae, 0x11d3, {0xa7, 0x45, 0x00, 0x50, 0x04, 0x0a, 0xb4, 0x07} }; DBG_ID_INSTANCE(CLoaderACPWrap); DBG_ID_INSTANCE(CACPWrap); void NormalizeAnchor(CAnchorRef *par) { CACPWrap *paw; CAnchor *pa; paw = par->_GetWrap(); pa = par->_GetAnchor(); if (!pa->IsNormalized()) { paw->_NormalizeAnchor(pa); } } void NormalizeAnchor(IAnchor *pa) { CAnchorRef *par; if ((par = GetCAnchorRef_NA(pa)) == NULL) { Assert(0); // should never get here return; } NormalizeAnchor(par); } //+--------------------------------------------------------------------------- // // ctor // //---------------------------------------------------------------------------- CLoaderACPWrap::CLoaderACPWrap(ITfPersistentPropertyLoaderACP *loader) { Dbg_MemSetThisNameIDCounter(TEXT("CLoaderACPWrap"), PERF_LOADERACP_COUNTER); _loader = loader; _loader->AddRef(); } //+--------------------------------------------------------------------------- // // dtor // //---------------------------------------------------------------------------- CLoaderACPWrap::~CLoaderACPWrap() { _loader->Release(); } //+--------------------------------------------------------------------------- // // LoadProperty // //---------------------------------------------------------------------------- STDAPI CLoaderACPWrap::LoadProperty(const TF_PERSISTENT_PROPERTY_HEADER_ANCHOR *pHdr, IStream **ppStream) { TF_PERSISTENT_PROPERTY_HEADER_ACP phacp; // always normalize before unserializing NormalizeAnchor(pHdr->paStart); NormalizeAnchor(pHdr->paEnd); if (!CACPWrap::_AnchorHdrToACP(pHdr, &phacp)) return E_FAIL; return _loader->LoadProperty(&phacp, ppStream); } //+--------------------------------------------------------------------------- // // ctor // //---------------------------------------------------------------------------- CACPWrap::CACPWrap(ITextStoreACP *ptsi) { Dbg_MemSetThisNameIDCounter(TEXT("CACPWrap"), PERF_ACPWRAP_COUNTER); _ptsi = ptsi; ptsi->AddRef(); _cRef = 1; } //+--------------------------------------------------------------------------- // // dtor // //---------------------------------------------------------------------------- CACPWrap::~CACPWrap() { Assert(_ptsi == NULL); // cleared in Release Assert(_rgAnchors.Count() == 0); // all anchors should be removed } //+--------------------------------------------------------------------------- // // IUnknown // //---------------------------------------------------------------------------- STDAPI CACPWrap::QueryInterface(REFIID riid, void **ppvObj) { *ppvObj = NULL; if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITextStoreAnchor)) { *ppvObj = SAFECAST(this, ITextStoreAnchor *); } else if (IsEqualIID(riid, IID_ITextStoreACPSink)) { *ppvObj = SAFECAST(this, ITextStoreACPSink *); } else if (IsEqualIID(riid, IID_ITextStoreACPServices)) { *ppvObj = SAFECAST(this, ITextStoreACPServices *); } else if (IsEqualIID(riid, IID_PRIV_ACPWRAP)) { *ppvObj = SAFECAST(this, CACPWrap *); } else if (IsEqualIID(riid, IID_ITfMouseTrackerACP)) { *ppvObj = SAFECAST(this, ITfMouseTrackerACP *); } else if (IsEqualIID(riid, IID_IServiceProvider)) { *ppvObj = SAFECAST(this, IServiceProvider *); } if (*ppvObj) { AddRef(); return S_OK; } return E_NOINTERFACE; } STDAPI_(ULONG) CACPWrap::AddRef() { return ++_cRef; } STDAPI_(ULONG) CACPWrap::Release() { _cRef--; Assert(_cRef >= 0); // nb: this obj has 2 ref counters: // _cRef -> external clients // _GetAnchorRef -> CAnchorRef's. // we won't delete until both reach 0 if (_cRef == 0) { // clear out text cache before releasing _ptsi // the memory may be reallocated (this happened!) for a different text store CProcessTextCache::Invalidate(_ptsi); // disconnect the ITextStoreACP SafeReleaseClear(_ptsi); if (_GetAnchorRef() == 0) // internal ref count { delete this; } return 0; } return _cRef; } //+--------------------------------------------------------------------------- // // OnTextChange // //---------------------------------------------------------------------------- STDAPI CACPWrap::OnTextChange(DWORD dwFlags, const TS_TEXTCHANGE *pChange) { IAnchor *paStart = NULL; IAnchor *paEnd = NULL; HRESULT hr; HRESULT hr2; if (pChange == NULL) return E_INVALIDARG; if (_pic->_IsInEditSession()) { Assert(0); // someone other than cicero is editing the doc while cicero holds a lock return TS_E_NOLOCK; } #ifdef DEBUG _Dbg_fAppHasLock = TRUE; #endif // we never call this internally, so the caller must be the app. // nb: we aren't normalizing the anchors here! They can't be // normalized until the app releases its lock in OnLockReleased. // Not a bad thing for perf though! We will merge spans before // normalizing... // Issue: we aren't handling the case like: // "----ABC" -> "XX", where "-" is formatting and has backwards gravity, forwards // in this case we'd like to see "XX" as the final result if (pChange->acpStart == pChange->acpOldEnd && pChange->acpOldEnd == pChange->acpNewEnd) { // nothing happened return S_OK; } hr = E_OUTOFMEMORY; if ((paStart = _CreateAnchorACP(pChange->acpStart, TS_GR_BACKWARD)) == NULL) goto Exit; if ((paEnd = _CreateAnchorACP(pChange->acpOldEnd, TS_GR_FORWARD)) == NULL) { paStart->Release(); goto Exit; } _fInOnTextChange = TRUE; // this flag stops us from trying to normalize anchors // do the alist update _Update(pChange); hr = _pic->_OnTextChangeInternal(dwFlags, paStart, paEnd, OWN_ANCHORS); _fInOnTextChange = FALSE; // get a lock eventually so we can deal with the changes _ptsi->RequestLock(TS_LF_READ, &hr2); Exit: return hr; } //+--------------------------------------------------------------------------- // // OnSelectionChange // //---------------------------------------------------------------------------- STDAPI CACPWrap::OnSelectionChange() { // we never call this internally, so the caller must be the app. return _ptss->OnSelectionChange(); } //+--------------------------------------------------------------------------- // // OnLockGranted // //---------------------------------------------------------------------------- STDAPI CACPWrap::OnLockGranted(DWORD dwLockFlags) { int i; SPAN *pSpan; int cSpans; CAnchorRef *parStart; CAnchorRef *parEnd; CSpanSet *pSpanSet; #ifdef DEBUG _Dbg_fAppHasLock = FALSE; #endif // // After IC is popped, we may be granted the lock... // if (!_pic || !_pic->_GetEditRecord()) return E_UNEXPECTED; // generally the pic's er can contain app or tip changes // BUT, it will never hold both at the same time. And it will // only hold app changes (if any) when OnLockGranted is called pSpanSet = _pic->_GetEditRecord()->_GetTextSpanSet(); // empty out our change cache if ((cSpans = pSpanSet->GetCount()) > 0) { // clean up the anchorlist! pSpan = pSpanSet->GetSpans(); for (i=0; ipaStart); parEnd = GetCAnchorRef_NA(pSpan->paEnd); _Renormalize(parStart->_GetACP(), parEnd->_GetACP()); pSpan++; } } // then pass along the release to the uim return _ptss->OnLockGranted(dwLockFlags); } //+--------------------------------------------------------------------------- // // OnLayoutChange // //---------------------------------------------------------------------------- STDAPI CACPWrap::OnLayoutChange(TsLayoutCode lcode, TsViewCookie vcView) { return _ptss->OnLayoutChange(lcode, vcView); } //+--------------------------------------------------------------------------- // // OnStatusChange // //---------------------------------------------------------------------------- STDAPI CACPWrap::OnStatusChange(DWORD dwFlags) { return _ptss->OnStatusChange(dwFlags); } //+--------------------------------------------------------------------------- // // OnStatusChange // //---------------------------------------------------------------------------- STDAPI CACPWrap::OnAttrsChange(LONG acpStart, LONG acpEnd, ULONG cAttrs, const TS_ATTRID *paAttrs) { IAnchor *paStart = NULL; IAnchor *paEnd = NULL; HRESULT hr; hr = E_OUTOFMEMORY; if ((paStart = _CreateAnchorACP(acpStart, TS_GR_BACKWARD)) == NULL) goto Exit; if ((paEnd = _CreateAnchorACP(acpEnd, TS_GR_FORWARD)) == NULL) goto Exit; hr = _ptss->OnAttrsChange(paStart, paEnd, cAttrs, paAttrs); Exit: SafeRelease(paStart); SafeRelease(paEnd); return hr; } //+--------------------------------------------------------------------------- // // OnStartEditTransaction // //---------------------------------------------------------------------------- STDAPI CACPWrap::OnStartEditTransaction() { return _ptss->OnStartEditTransaction(); } //+--------------------------------------------------------------------------- // // OnEndEditTransaction // //---------------------------------------------------------------------------- STDAPI CACPWrap::OnEndEditTransaction() { return _ptss->OnEndEditTransaction(); } //+--------------------------------------------------------------------------- // // AdviseSink // //---------------------------------------------------------------------------- STDAPI CACPWrap::AdviseSink(REFIID riid, IUnknown *punk, DWORD dwMask) { IServiceProvider *psp; HRESULT hr; Assert(_ptss == NULL); Assert(_pic == NULL); if (punk->QueryInterface(IID_ITextStoreAnchorSink, (void **)&_ptss) != S_OK) return E_FAIL; // use QueryService to get the ic since msaa may be wrapping it if (punk->QueryInterface(IID_IServiceProvider, (void **)&psp) != S_OK) { hr = E_FAIL; goto ErrorExit; } hr = psp->QueryService(GUID_SERVICE_TF, IID_PRIV_CINPUTCONTEXT, (void **)&_pic); psp->Release(); if (hr != S_OK) { hr = E_FAIL; goto ErrorExit; } // advise our wrapped acp if ((hr = _ptsi->AdviseSink(IID_ITextStoreACPSink, SAFECAST(this, ITextStoreACPSink *), dwMask)) != S_OK) goto ErrorExit; return S_OK; ErrorExit: SafeReleaseClear(_ptss); SafeReleaseClear(_pic); return hr; } //+--------------------------------------------------------------------------- // // UnadviseSink // //---------------------------------------------------------------------------- STDAPI CACPWrap::UnadviseSink(IUnknown *punk) { Assert(_ptss == punk); // we're dealing with cicero, this should always hold _ptsi->UnadviseSink(SAFECAST(this, ITextStoreACPSink *)); SafeReleaseClear(_ptss); SafeReleaseClear(_pic); return S_OK; } //+--------------------------------------------------------------------------- // // RequestLock // //---------------------------------------------------------------------------- STDAPI CACPWrap::RequestLock(DWORD dwLockFlags, HRESULT *phrSession) { return _ptsi->RequestLock(dwLockFlags, phrSession); } //+--------------------------------------------------------------------------- // // GetSelection // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetSelection(ULONG ulIndex, ULONG ulCount, TS_SELECTION_ANCHOR *pSelection, ULONG *pcFetched) { TS_SELECTION_ACP *pSelACP; HRESULT hr; ULONG i; TS_SELECTION_ACP sel; Assert(pcFetched != NULL); // caller should have caught this *pcFetched = 0; if (ulCount == 1) { pSelACP = &sel; } else if ((pSelACP = (TS_SELECTION_ACP *)cicMemAlloc(ulCount*sizeof(TS_SELECTION_ACP))) == NULL) return E_OUTOFMEMORY; hr = _ptsi->GetSelection(ulIndex, ulCount, pSelACP, pcFetched); if (hr != S_OK) goto Exit; _Dbg_AssertNoAppLock(); for (i=0; i<*pcFetched; i++) { if ((pSelection[i].paStart = _CreateAnchorACP(pSelACP[i].acpStart, TS_GR_FORWARD)) == NULL || (pSelection[i].paEnd = _CreateAnchorACP(pSelACP[i].acpEnd, TS_GR_BACKWARD)) == NULL) { SafeRelease(pSelection[i].paStart); while (i>0) { i--; pSelection[i].paStart->Release(); pSelection[i].paEnd->Release(); } hr = E_FAIL; goto Exit; } pSelection[i].style = pSelACP[i].style; } Exit: if (pSelACP != &sel) { cicMemFree(pSelACP); } return hr; } //+--------------------------------------------------------------------------- // // SetSelection // //---------------------------------------------------------------------------- STDAPI CACPWrap::SetSelection(ULONG ulCount, const TS_SELECTION_ANCHOR *pSelection) { CAnchorRef *par; TS_SELECTION_ACP *pSelACP; ULONG i; HRESULT hr; TS_SELECTION_ACP sel; _Dbg_AssertNoAppLock(); if (ulCount == 1) { pSelACP = &sel; } else if ((pSelACP = (TS_SELECTION_ACP *)cicMemAlloc(ulCount*sizeof(TS_SELECTION_ACP))) == NULL) return E_OUTOFMEMORY; hr = E_FAIL; for (i=0; i_GetACP(); if (pSelection[i].paEnd == NULL) { // implies paEnd is same as paStart pSelACP[i].acpEnd = pSelACP[i].acpStart; } else { if ((par = GetCAnchorRef_NA(pSelection[i].paEnd)) == NULL) goto Exit; pSelACP[i].acpEnd = par->_GetACP(); } pSelACP[i].style = pSelection[i].style; } hr = _ptsi->SetSelection(ulCount, pSelACP); Exit: if (pSelACP != &sel) { cicMemFree(pSelACP); } return hr; } //+--------------------------------------------------------------------------- // // GetText // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetText(DWORD dwFlags, IAnchor *paStart, IAnchor *paEnd, WCHAR *pchText, ULONG cchReq, ULONG *pcch, BOOL fUpdateAnchor) { CAnchorRef *parStart; CAnchorRef *parEnd; LONG acpStart; LONG acpEnd; LONG acpNext; ULONG cchTotal; ULONG cchAdjust; ULONG ulRunInfoOut; HRESULT hr; ULONG i; WCHAR ch; WCHAR *pchSrc; WCHAR *pchDst; TS_RUNINFO rgRunInfo[16]; _Dbg_AssertNoAppLock(); Perf_IncCounter(PERF_ACPWRAP_GETTEXT); // this upfront check for a nop saves us from // 1) copying non-existant text based on non-zero run-info // 2) repositioning the start anchor based on non-zero run-info if (cchReq == 0) { *pcch = 0; return S_OK; } if ((parStart = GetCAnchorRef_NA(paStart)) == NULL) return E_FAIL; acpStart = parStart->_GetACP(); acpEnd = -1; if (paEnd != NULL) { if ((parEnd = GetCAnchorRef_NA(paEnd)) == NULL) { hr = E_FAIL; goto Exit; } acpEnd = parEnd->_GetACP(); } cchTotal = 0; while (TRUE) { Perf_IncCounter(PERF_ACPWRAP_GETTEXT_LOOP); hr = CProcessTextCache::GetText(_ptsi, acpStart, acpEnd, pchText, cchReq, pcch, rgRunInfo, ARRAYSIZE(rgRunInfo), &ulRunInfoOut, &acpNext); if (hr != S_OK) goto Exit; if (ulRunInfoOut == 0) // prevent a loop at eod break; // prune out any hidden text pchSrc = pchText; pchDst = pchText; for (i=0; i= 0); // app bug if this is less than zero break; case TS_RT_OPAQUE: break; } } // prune out any TS_CHAR_REGIONs pchSrc = pchText; pchDst = pchText; for (i=0; i<*pcch; i++) { ch = *pchSrc; if (ch != TS_CHAR_REGION) { if (pchSrc != pchDst) { *pchDst = ch; } pchDst++; } pchSrc++; } // dec the count by the number of TS_CHAR_REGIONs we removed cchAdjust = *pcch - (ULONG)(pchSrc - pchDst); cchTotal += cchAdjust; // done? cchReq -= cchAdjust; if (cchReq <= 0) break; acpStart = acpNext; if (acpEnd >= 0 && acpStart >= acpEnd) break; pchText += cchAdjust; } *pcch = cchTotal; if (fUpdateAnchor) { parStart->_SetACP(acpNext); } Exit: return hr; } //+--------------------------------------------------------------------------- // // _PostInsertUpdate // //---------------------------------------------------------------------------- void CACPWrap::_PostInsertUpdate(LONG acpStart, LONG acpEnd, ULONG cch, const TS_TEXTCHANGE *ptsTextChange) { Assert(ptsTextChange->acpStart <= acpStart); // bogus output from app? if (ptsTextChange->acpStart < acpStart && cch > 0 && acpStart != acpEnd) { // this is unusual. The original text was like: // ----ABC "-" is formatting, we replace with "XX" // // the new text is like: // XX problem! // or possibly // ----XX no problem // // if "----ABC" -> "XX", then paStart will be placed after the ABC // because it was normalized to start with: // // "----ABC" -> "XX" // // We need to fix this up. _DragAnchors(acpStart, ptsTextChange->acpStart); } _Update(ptsTextChange); _Renormalize(ptsTextChange->acpStart, ptsTextChange->acpNewEnd); } //+--------------------------------------------------------------------------- // // SetText // //---------------------------------------------------------------------------- STDAPI CACPWrap::SetText(DWORD dwFlags, IAnchor *paStart, IAnchor *paEnd, const WCHAR *pchText, ULONG cch) { CAnchorRef *parStart; CAnchorRef *parEnd; LONG acpStart; LONG acpEnd; TS_TEXTCHANGE dctc; HRESULT hr; _Dbg_AssertNoAppLock(); if ((parStart = GetCAnchorRef_NA(paStart)) == NULL) return E_FAIL; acpStart = parStart->_GetACP(); if ((parEnd = GetCAnchorRef_NA(paEnd)) == NULL) return E_FAIL; acpEnd = parEnd->_GetACP(); // for perf, filter out the nop if (acpStart == acpEnd && cch == 0) return S_OK; // do the work hr = _ptsi->SetText(dwFlags, acpStart, acpEnd, pchText, cch, &dctc); // we'll handle the anchor updates -- the app won't give us an OnTextChange callback for our // own changes if (hr == S_OK) { _PostInsertUpdate(acpStart, acpEnd, cch, &dctc); } return hr; } //+--------------------------------------------------------------------------- // // GetFormattedText // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetFormattedText(IAnchor *paStart, IAnchor *paEnd, IDataObject **ppDataObject) { CAnchorRef *par; LONG acpStart; LONG acpEnd; Assert(*ppDataObject == NULL); _Dbg_AssertNoAppLock(); if ((par = GetCAnchorRef_NA(paStart)) == NULL) return E_FAIL; acpStart = par->_GetACP(); if ((par = GetCAnchorRef_NA(paEnd)) == NULL) return E_FAIL; acpEnd = par->_GetACP(); // do the work return _ptsi->GetFormattedText(acpStart, acpEnd, ppDataObject); } //+--------------------------------------------------------------------------- // // GetEmbedded // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetEmbedded(DWORD dwFlags, IAnchor *paPos, REFGUID rguidService, REFIID riid, IUnknown **ppunk) { CAnchorRef *par; LONG acpPos; if (ppunk == NULL) return E_INVALIDARG; *ppunk = NULL; if (paPos == NULL) return E_INVALIDARG; if ((par = GetCAnchorRef_NA(paPos)) == NULL) return E_FAIL; if (!par->_GetAnchor()->IsNormalized()) { // we need to be positioned just before the next char _NormalizeAnchor(par->_GetAnchor()); } acpPos = par->_GetACP(); if (!(dwFlags & TS_GEA_HIDDEN)) { // skip past any hidden text acpPos = Normalize(_ptsi, acpPos, NORM_SKIP_HIDDEN); } return _ptsi->GetEmbedded(acpPos, rguidService, riid, ppunk); } //+--------------------------------------------------------------------------- // // QueryInsertEmbedded // //---------------------------------------------------------------------------- STDAPI CACPWrap::QueryInsertEmbedded(const GUID *pguidService, const FORMATETC *pFormatEtc, BOOL *pfInsertable) { return _ptsi->QueryInsertEmbedded(pguidService, pFormatEtc, pfInsertable); } //+--------------------------------------------------------------------------- // // InsertEmbedded // //---------------------------------------------------------------------------- STDAPI CACPWrap::InsertEmbedded(DWORD dwFlags, IAnchor *paStart, IAnchor *paEnd, IDataObject *pDataObject) { CAnchorRef *par; LONG acpStart; LONG acpEnd; TS_TEXTCHANGE dctc; HRESULT hr; if (paStart == NULL || paEnd == NULL || pDataObject == NULL) return E_INVALIDARG; if ((par = GetCAnchorRef_NA(paStart)) == NULL) return E_FAIL; acpStart = par->_GetACP(); if ((par = GetCAnchorRef_NA(paEnd)) == NULL) return E_FAIL; acpEnd = par->_GetACP(); hr = _ptsi->InsertEmbedded(dwFlags, acpStart, acpEnd, pDataObject, &dctc); // we'll handle the anchor updates -- the app won't give us an OnTextChange callback for our // own changes if (hr == S_OK) { _PostInsertUpdate(acpStart, acpEnd, 1 /* cch */, &dctc); } return hr; } //+--------------------------------------------------------------------------- // // GetStart // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetStart(IAnchor **ppaStart) { _Dbg_AssertNoAppLock(); if (ppaStart == NULL) return E_INVALIDARG; *ppaStart = NULL; return (*ppaStart = _CreateAnchorACP(0, TS_GR_FORWARD)) ? S_OK : E_OUTOFMEMORY; } //+--------------------------------------------------------------------------- // // GetEnd // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetEnd(IAnchor **ppaEnd) { LONG acpEnd; HRESULT hr; _Dbg_AssertNoAppLock(); if (ppaEnd == NULL) return E_INVALIDARG; *ppaEnd = NULL; if (FAILED(hr = _ptsi->GetEndACP(&acpEnd))) return hr; return (*ppaEnd = _CreateAnchorACP(acpEnd, TS_GR_FORWARD)) ? S_OK : E_OUTOFMEMORY; } //+--------------------------------------------------------------------------- // // GetStatus // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetStatus(TS_STATUS *pdcs) { return _ptsi->GetStatus(pdcs); } //+--------------------------------------------------------------------------- // // QueryInsert // //---------------------------------------------------------------------------- STDAPI CACPWrap::QueryInsert(IAnchor *paTestStart, IAnchor *paTestEnd, ULONG cch, IAnchor **ppaResultStart, IAnchor **ppaResultEnd) { LONG acpTestStart; LONG acpTestEnd; LONG acpResultStart; LONG acpResultEnd; CAnchorRef *par; HRESULT hr; if (ppaResultStart != NULL) { *ppaResultStart = NULL; } if (ppaResultEnd != NULL) { *ppaResultEnd = NULL; } if (ppaResultStart == NULL || ppaResultEnd == NULL) return E_INVALIDARG; if ((par = GetCAnchorRef_NA(paTestStart)) == NULL) return E_INVALIDARG; acpTestStart = par->_GetACP(); if ((par = GetCAnchorRef_NA(paTestEnd)) == NULL) return E_INVALIDARG; acpTestEnd = par->_GetACP(); hr = _ptsi->QueryInsert(acpTestStart, acpTestEnd, cch, &acpResultStart, &acpResultEnd); if (hr != S_OK) return E_FAIL; if (acpResultStart < 0) { *ppaResultStart = NULL; } else if ((*ppaResultStart = _CreateAnchorACP(acpResultStart, TS_GR_BACKWARD)) == NULL) return E_OUTOFMEMORY; if (acpResultEnd < 0) { *ppaResultEnd = NULL; } else if ((*ppaResultEnd = _CreateAnchorACP(acpResultEnd, TS_GR_FORWARD)) == NULL) { SafeRelease(*ppaResultStart); return E_OUTOFMEMORY; } return S_OK; } //+--------------------------------------------------------------------------- // // GetAnchorFromPoint // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetAnchorFromPoint(TsViewCookie vcView, const POINT *pt, DWORD dwFlags, IAnchor **ppaSite) { LONG acp; if (ppaSite != NULL) { *ppaSite = NULL; } if (pt == NULL || ppaSite == NULL) return E_INVALIDARG; if (dwFlags & ~(GXFPF_ROUND_NEAREST | GXFPF_NEAREST)) return E_INVALIDARG; if (FAILED(_ptsi->GetACPFromPoint(vcView, pt, dwFlags, &acp))) return E_FAIL; _Dbg_AssertNoAppLock(); return (*ppaSite = _CreateAnchorACP(acp, TS_GR_FORWARD)) ? S_OK : E_OUTOFMEMORY; } //+--------------------------------------------------------------------------- // // GetTextExt // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetTextExt(TsViewCookie vcView, IAnchor *paStart, IAnchor *paEnd, RECT *prc, BOOL *pfClipped) { CAnchorRef *par; LONG acpStart; LONG acpEnd; _Dbg_AssertNoAppLock(); if (prc != NULL) { memset(prc, 0, sizeof(*prc)); } if (pfClipped != NULL) { *pfClipped = FALSE; } if (paStart == NULL || paEnd == NULL || prc == NULL || pfClipped == NULL) return E_INVALIDARG; if ((par = GetCAnchorRef_NA(paStart)) == NULL) return E_FAIL; acpStart = par->_GetACP(); if ((par = GetCAnchorRef_NA(paEnd)) == NULL) return E_FAIL; acpEnd = par->_GetACP(); return _ptsi->GetTextExt(vcView, acpStart, acpEnd, prc, pfClipped); } //+--------------------------------------------------------------------------- // // GetScreenExt // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetScreenExt(TsViewCookie vcView, RECT *prc) { if (prc == NULL) return E_INVALIDARG; return _ptsi->GetScreenExt(vcView, prc); } //+--------------------------------------------------------------------------- // // GetWnd // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetWnd(TsViewCookie vcView, HWND *phwnd) { if (phwnd == NULL) return E_INVALIDARG; return _ptsi->GetWnd(vcView, phwnd); } //+--------------------------------------------------------------------------- // // Serialize // //---------------------------------------------------------------------------- STDAPI CACPWrap::Serialize(ITfProperty *pProp, ITfRange *pRange, TF_PERSISTENT_PROPERTY_HEADER_ACP *pHdr, IStream *pStream) { #ifdef LATER // word won't grant us a sync lock here even though we need one SERIALIZE_ACP_PARAMS params; HRESULT hr; params.pWrap = this; params.pProp = pProp; params.pRange = pRange; params.pHdr = pHdr; params.pStream = pStream; // need a sync read lock to do our work if (_pic->_DoPseudoSyncEditSession(TF_ES_READ, PSEUDO_ESCB_SERIALIZE_ACP, ¶ms, &hr) != S_OK) { Assert(0); // app won't give us a sync read lock return E_FAIL; } return hr; #else return _Serialize(pProp, pRange, pHdr, pStream); #endif } //+--------------------------------------------------------------------------- // // _Serialize // //---------------------------------------------------------------------------- HRESULT CACPWrap::_Serialize(ITfProperty *pProp, ITfRange *pRange, TF_PERSISTENT_PROPERTY_HEADER_ACP *pHdr, IStream *pStream) { TF_PERSISTENT_PROPERTY_HEADER_ANCHOR phanch; CProperty *pPropP = NULL; CRange *pRangeP; HRESULT hr = E_FAIL; if ((pPropP = GetCProperty(pProp)) == NULL) goto Exit; if ((pRangeP = GetCRange_NA(pRange)) == NULL) goto Exit; if (!VerifySameContext(_pic, pRangeP)) goto Exit; hr = pPropP->_Serialize(pRangeP, &phanch, pStream); if (hr == S_OK) { if (!_AnchorHdrToACP(&phanch, pHdr)) { memset(pHdr, 0, sizeof(TF_PERSISTENT_PROPERTY_HEADER_ACP)); hr = E_FAIL; Assert(0); } } else { memset(pHdr, 0, sizeof(TF_PERSISTENT_PROPERTY_HEADER_ACP)); } Assert(pHdr->ichStart >= 0); Assert(pHdr->cch >= 0); SafeRelease(phanch.paStart); SafeRelease(phanch.paEnd); Exit: SafeRelease(pPropP); return hr; } //+--------------------------------------------------------------------------- // // Unserialize // //---------------------------------------------------------------------------- STDAPI CACPWrap::Unserialize(ITfProperty *pProp, const TF_PERSISTENT_PROPERTY_HEADER_ACP *pHdr, IStream *pStream, ITfPersistentPropertyLoaderACP *pLoaderACP) { UNSERIALIZE_ACP_PARAMS params; HRESULT hr; params.pWrap = this; params.pProp = pProp; params.pHdr = pHdr; params.pStream = pStream; params.pLoaderACP = pLoaderACP; // need a sync read lock to do our work if (_pic->_DoPseudoSyncEditSession(TF_ES_READ, PSEUDO_ESCB_UNSERIALIZE_ACP, ¶ms, &hr) != S_OK) { Assert(0); // app won't give us a sync read lock return E_FAIL; } return hr; } //+--------------------------------------------------------------------------- // // _Unserialize // //---------------------------------------------------------------------------- HRESULT CACPWrap::_Unserialize(ITfProperty *pProp, const TF_PERSISTENT_PROPERTY_HEADER_ACP *pHdr, IStream *pStream, ITfPersistentPropertyLoaderACP *pLoaderACP) { TF_PERSISTENT_PROPERTY_HEADER_ANCHOR hdrAnchor; CProperty *pPropP = NULL; CLoaderACPWrap *pLoader; HRESULT hr = E_FAIL; Assert(pHdr->ichStart >= 0); Assert(pHdr->cch > 0); hdrAnchor.paStart = NULL; hdrAnchor.paEnd = NULL; if (pHdr->ichStart < 0) goto Exit; if (pHdr->cch <= 0) goto Exit; if (_ACPHdrToAnchor(pHdr, &hdrAnchor) != S_OK) { Assert(0); goto Exit; } if ((pPropP = GetCProperty(pProp)) == NULL) goto Exit; pLoader = NULL; if (pLoaderACP != NULL && (pLoader = new CLoaderACPWrap(pLoaderACP)) == NULL) { hr = E_OUTOFMEMORY; goto Exit; } hr = pPropP->_Unserialize(&hdrAnchor, pStream, pLoader); SafeRelease(pLoader); Exit: SafeRelease(pPropP); SafeRelease(hdrAnchor.paStart); SafeRelease(hdrAnchor.paEnd); return hr; } //+--------------------------------------------------------------------------- // // ForceLoadProperty // //---------------------------------------------------------------------------- STDAPI CACPWrap::ForceLoadProperty(ITfProperty *pProp) { CProperty *pPropP; HRESULT hr; if ((pPropP = GetCProperty(pProp)) == NULL) return E_FAIL; hr = pPropP->ForceLoad(); pPropP->Release(); return hr; } //+--------------------------------------------------------------------------- // // CreateRange // //---------------------------------------------------------------------------- STDAPI CACPWrap::CreateRange(LONG acpStart, LONG acpEnd, ITfRangeACP **ppRange) { ITfRangeAnchor *rangeAnchor; CAnchorRef *paStart; CAnchorRef *paEnd; HRESULT hr; ITextStoreAnchorServices *pserv; if (ppRange == NULL) return E_INVALIDARG; *ppRange = NULL; hr = E_FAIL; paEnd = NULL; Perf_IncCounter(PERF_CREATERANGE_ACP); if ((paStart = _CreateAnchorACP(acpStart, TS_GR_BACKWARD)) == NULL) goto Exit; if ((paEnd = _CreateAnchorACP(acpEnd, TS_GR_BACKWARD)) == NULL) goto Exit; if ((hr = _ptss->QueryInterface(IID_ITextStoreAnchorServices, (void **)&pserv)) == S_OK) { hr = pserv->CreateRange(paStart, paEnd, &rangeAnchor); pserv->Release(); } if (hr == S_OK) { *ppRange = (ITfRangeACP *)(CRange *)rangeAnchor; } Exit: SafeRelease(paStart); SafeRelease(paEnd); return hr; } //+--------------------------------------------------------------------------- // // _CreateAnchorACP // //---------------------------------------------------------------------------- CAnchorRef *CACPWrap::_CreateAnchorACP(LONG acp, TsGravity gravity) { CAnchorRef *pa; if ((pa = new CAnchorRef) == NULL) return NULL; if (!pa->_Init(this, acp, gravity)) { pa->Release(); return NULL; } return pa; } //+--------------------------------------------------------------------------- // // _CreateAnchorACP // //---------------------------------------------------------------------------- CAnchorRef *CACPWrap::_CreateAnchorAnchor(CAnchor *paAnchor, TsGravity gravity) { CAnchorRef *pa; if ((pa = new CAnchorRef) == NULL) return NULL; if (!pa->_Init(this, paAnchor, gravity)) { pa->Release(); return NULL; } return pa; } //+--------------------------------------------------------------------------- // // _ACPHdrToAnchor // //---------------------------------------------------------------------------- HRESULT CACPWrap::_ACPHdrToAnchor(const TF_PERSISTENT_PROPERTY_HEADER_ACP *pHdr, TF_PERSISTENT_PROPERTY_HEADER_ANCHOR *phanch) { phanch->paStart = NULL; if ((phanch->paStart = _CreateAnchorACP(pHdr->ichStart, TS_GR_FORWARD)) == NULL) goto ExitError; if ((phanch->paEnd = _CreateAnchorACP(pHdr->ichStart + pHdr->cch, TS_GR_BACKWARD)) == NULL) goto ExitError; phanch->guidType = pHdr->guidType; phanch->cb = pHdr->cb; phanch->dwPrivate = pHdr->dwPrivate; phanch->clsidTIP = pHdr->clsidTIP; return S_OK; ExitError: SafeRelease(phanch->paStart); return E_FAIL; } //+--------------------------------------------------------------------------- // // _AnchorHdrToACP // //---------------------------------------------------------------------------- /* static */ BOOL CACPWrap::_AnchorHdrToACP(const TF_PERSISTENT_PROPERTY_HEADER_ANCHOR *phanch, TF_PERSISTENT_PROPERTY_HEADER_ACP *phacp) { CAnchorRef *par; if ((par = GetCAnchorRef_NA(phanch->paStart)) == NULL) return FALSE; NormalizeAnchor(par); phacp->ichStart = par->_GetACP(); if ((par = GetCAnchorRef_NA(phanch->paEnd)) == NULL) return FALSE; NormalizeAnchor(par); phacp->cch = par->_GetACP() - phacp->ichStart; phacp->guidType = phanch->guidType; phacp->cb = phanch->cb; phacp->dwPrivate = phanch->dwPrivate; phacp->clsidTIP = phanch->clsidTIP; return TRUE; } //+--------------------------------------------------------------------------- // // RequestSupportedAttrs // //---------------------------------------------------------------------------- STDAPI CACPWrap::RequestSupportedAttrs(DWORD dwFlags, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs) { return _ptsi->RequestSupportedAttrs(dwFlags, cFilterAttrs, paFilterAttrs); } //+--------------------------------------------------------------------------- // // RequestAttrsAtPosition // //---------------------------------------------------------------------------- STDAPI CACPWrap::RequestAttrsAtPosition(IAnchor *paPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags) { CAnchorRef *par; LONG acpPos; if ((par = GetCAnchorRef_NA(paPos)) == NULL) return E_INVALIDARG; acpPos = par->_GetACP(); return _ptsi->RequestAttrsAtPosition(acpPos, cFilterAttrs, paFilterAttrs, dwFlags); } //+--------------------------------------------------------------------------- // // RequestAttrsTransitioningAtPosition // //---------------------------------------------------------------------------- STDAPI CACPWrap::RequestAttrsTransitioningAtPosition(IAnchor *paPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags) { CAnchorRef *par; LONG acpPos; if ((par = GetCAnchorRef_NA(paPos)) == NULL) return E_INVALIDARG; acpPos = par->_GetACP(); return _ptsi->RequestAttrsTransitioningAtPosition(acpPos, cFilterAttrs, paFilterAttrs, dwFlags); } //+--------------------------------------------------------------------------- // // FindNextAttrTransition // //---------------------------------------------------------------------------- STDAPI CACPWrap::FindNextAttrTransition(IAnchor *paStart, IAnchor *paHalt, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags, BOOL *pfFound, LONG *plFoundOffset) { CAnchorRef *parStart; CAnchorRef *parHalt; LONG acpStart; LONG acpHalt; LONG acpNext; HRESULT hr; if ((parStart = GetCAnchorRef_NA(paStart)) == NULL) return E_INVALIDARG; acpStart = parStart->_GetACP(); acpHalt = -1; if (paHalt != NULL) { hr = E_INVALIDARG; if ((parHalt = GetCAnchorRef_NA(paHalt)) == NULL) goto Exit; acpHalt = parHalt->_GetACP(); } hr = _ptsi->FindNextAttrTransition(acpStart, acpHalt, cFilterAttrs, paFilterAttrs, dwFlags, &acpNext, pfFound, plFoundOffset); if (hr == S_OK && (dwFlags & TS_ATTR_FIND_UPDATESTART)) { parStart->_SetACP(acpNext); } Exit: return hr; } //+--------------------------------------------------------------------------- // // RetrieveRequestedAttrs // //---------------------------------------------------------------------------- STDAPI CACPWrap::RetrieveRequestedAttrs(ULONG ulCount, TS_ATTRVAL *paAttrVals, ULONG *pcFetched) { return _ptsi->RetrieveRequestedAttrs(ulCount, paAttrVals, pcFetched); } //+--------------------------------------------------------------------------- // // AdviseMouseSink // //---------------------------------------------------------------------------- STDAPI CACPWrap::AdviseMouseSink(ITfRangeACP *range, ITfMouseSink *pSink, DWORD *pdwCookie) { ITfMouseTrackerACP *pTrackerACP; HRESULT hr; if (pdwCookie == NULL) return E_INVALIDARG; *pdwCookie = 0; if (_ptsi->QueryInterface(IID_ITfMouseTrackerACP, (void **)&pTrackerACP) != S_OK) return E_NOTIMPL; hr = pTrackerACP->AdviseMouseSink(range, pSink, pdwCookie); pTrackerACP->Release(); return hr; } //+--------------------------------------------------------------------------- // // UnadviseMouseSink // //---------------------------------------------------------------------------- STDAPI CACPWrap::UnadviseMouseSink(DWORD dwCookie) { ITfMouseTrackerACP *pTrackerACP; HRESULT hr; if (_ptsi->QueryInterface(IID_ITfMouseTrackerACP, (void **)&pTrackerACP) != S_OK) return E_NOTIMPL; hr = pTrackerACP->UnadviseMouseSink(dwCookie); pTrackerACP->Release(); return hr; } //+--------------------------------------------------------------------------- // // QueryService // //---------------------------------------------------------------------------- STDAPI CACPWrap::QueryService(REFGUID guidService, REFIID riid, void **ppv) { IServiceProvider *psp; HRESULT hr; if (ppv == NULL) return E_INVALIDARG; *ppv = NULL; // SVC_E_NOSERVICE is proper return code for wrong service.... // but it's not defined anywhere. So use E_NOINTERFACE for both // cases as trident is rumored to do hr = E_NOINTERFACE; if (IsEqualGUID(guidService, GUID_SERVICE_TF) && IsEqualIID(riid, IID_PRIV_ACPWRAP)) { *ppv = this; AddRef(); hr = S_OK; } else if (_ptsi->QueryInterface(IID_IServiceProvider, (void **)&psp) == S_OK) { // we just pass the request along to the wrapped obj hr = psp->QueryService(guidService, riid, ppv); psp->Release(); } return hr; } //+--------------------------------------------------------------------------- // // GetActiveView // //---------------------------------------------------------------------------- STDAPI CACPWrap::GetActiveView(TsViewCookie *pvcView) { if (pvcView == NULL) return E_INVALIDARG; return _ptsi->GetActiveView(pvcView); } //+--------------------------------------------------------------------------- // // InsertTextAtSelection // //---------------------------------------------------------------------------- STDAPI CACPWrap::InsertTextAtSelection(DWORD dwFlags, const WCHAR *pchText, ULONG cch, IAnchor **ppaStart, IAnchor **ppaEnd) { LONG acpStart; LONG acpEnd; TS_TEXTCHANGE dctc; HRESULT hr; _Dbg_AssertNoAppLock(); Assert(ppaStart != NULL && ppaEnd != NULL); Assert((dwFlags & TS_IAS_QUERYONLY) || pchText != NULL); // caller should have already caught this Assert((dwFlags & TS_IAS_QUERYONLY) || cch > 0); // caller should have already caught this Assert((dwFlags & (TS_IAS_NOQUERY | TS_IAS_QUERYONLY)) != (TS_IAS_NOQUERY | TS_IAS_QUERYONLY)); *ppaStart = NULL; *ppaEnd = NULL; hr = _ptsi->InsertTextAtSelection(dwFlags, pchText, cch, &acpStart, &acpEnd, &dctc); // we'll handle the anchor updates -- the app won't give us an OnTextChange callback for our // own changes if (hr != S_OK) return hr; if (!(dwFlags & TF_IAS_QUERYONLY)) { _PostInsertUpdate(acpStart, acpEnd, cch, &dctc); } if (!(dwFlags & TF_IAS_NOQUERY)) { if ((*ppaStart = _CreateAnchorACP(acpStart, TS_GR_BACKWARD)) == NULL) goto ExitError; if ((*ppaEnd = _CreateAnchorACP(acpEnd, TS_GR_FORWARD)) == NULL) goto ExitError; } return S_OK; ExitError: SafeReleaseClear(*ppaStart); return E_FAIL; } //+--------------------------------------------------------------------------- // // InsertEmbeddedAtSelection // //---------------------------------------------------------------------------- STDAPI CACPWrap::InsertEmbeddedAtSelection(DWORD dwFlags, IDataObject *pDataObject, IAnchor **ppaStart, IAnchor **ppaEnd) { LONG acpStart; LONG acpEnd; TS_TEXTCHANGE dctc; HRESULT hr; _Dbg_AssertNoAppLock(); Assert(ppaStart != NULL && ppaEnd != NULL); Assert((dwFlags & (TS_IAS_NOQUERY | TS_IAS_QUERYONLY)) != (TS_IAS_NOQUERY | TS_IAS_QUERYONLY)); Assert((dwFlags & TS_IAS_QUERYONLY) || pDataObject == NULL); *ppaStart = NULL; *ppaEnd = NULL; hr = _ptsi->InsertEmbeddedAtSelection(dwFlags, pDataObject, &acpStart, &acpEnd, &dctc); // we'll handle the anchor updates -- the app won't give us an OnTextChange callback for our // own changes if (hr != S_OK) return hr; if (!(dwFlags & TF_IAS_QUERYONLY)) { _PostInsertUpdate(acpStart, acpEnd, 1 /* cch */, &dctc); } if (!(dwFlags & TF_IAS_NOQUERY)) { if ((*ppaStart = _CreateAnchorACP(acpStart, TS_GR_BACKWARD)) == NULL) goto ExitError; if ((*ppaEnd = _CreateAnchorACP(acpEnd, TS_GR_FORWARD)) == NULL) goto ExitError; } return S_OK; ExitError: SafeReleaseClear(*ppaStart); return E_FAIL; }