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.
1748 lines
46 KiB
1748 lines
46 KiB
//
|
|
// 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:
|
|
// "----<a1>ABC<a2>" -> "XX<a1><a2>", where "-" is formatting and <a1> has backwards gravity, <a2> forwards
|
|
// in this case we'd like to see "<a1>XX<a2>" 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; i<cSpans; i++)
|
|
{
|
|
parStart = GetCAnchorRef_NA(pSpan->paStart);
|
|
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<ulCount; i++)
|
|
{
|
|
if ((par = GetCAnchorRef_NA(pSelection[i].paStart)) == NULL)
|
|
goto Exit;
|
|
pSelACP[i].acpStart = par->_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<ulRunInfoOut; i++)
|
|
{
|
|
switch (rgRunInfo[i].type)
|
|
{
|
|
case TS_RT_PLAIN:
|
|
Assert(pchDst != NULL);
|
|
if (pchSrc != pchDst)
|
|
{
|
|
memmove(pchDst, pchSrc, rgRunInfo[i].uCount*sizeof(WCHAR));
|
|
}
|
|
pchSrc += rgRunInfo[i].uCount;
|
|
pchDst += rgRunInfo[i].uCount;
|
|
break;
|
|
|
|
case TS_RT_HIDDEN:
|
|
pchSrc += rgRunInfo[i].uCount;
|
|
*pcch -= rgRunInfo[i].uCount;
|
|
Assert((int)(*pcch) >= 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:
|
|
//
|
|
// "----<paStart>ABC<paEnd>" -> "XX<paStart><paEnd>"
|
|
//
|
|
// 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;
|
|
}
|