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.
939 lines
26 KiB
939 lines
26 KiB
//
|
|
// tsi.cpp
|
|
//
|
|
// CTextStoreImpl
|
|
//
|
|
|
|
#include "private.h"
|
|
#include "tsi.h"
|
|
#include "immxutil.h"
|
|
#include "tsdo.h"
|
|
#include "tsattrs.h"
|
|
#include "ic.h"
|
|
#include "rprop.h"
|
|
|
|
#define TSI_TOKEN 0x01010101
|
|
|
|
DBG_ID_INSTANCE(CTextStoreImpl);
|
|
|
|
/* 012313d4-b1e7-476a-bf88-173a316572fb */
|
|
extern const IID IID_PRIV_CTSI = { 0x012313d4, 0xb1e7, 0x476a, {0xbf, 0x88, 0x17, 0x3a, 0x31, 0x65, 0x72, 0xfb} };
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// ctor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CTextStoreImpl::CTextStoreImpl(CInputContext *pic)
|
|
{
|
|
Dbg_MemSetThisNameID(TEXT("CTextStoreImpl"));
|
|
|
|
Assert(_fPendingWriteReq == FALSE);
|
|
Assert(_dwlt == 0);
|
|
|
|
_pic = pic;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// dtor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CTextStoreImpl::~CTextStoreImpl()
|
|
{
|
|
cicMemFree(_pch);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// AdviseSink
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::AdviseSink(REFIID riid, IUnknown *punk, DWORD dwMask)
|
|
{
|
|
if (_ptss != NULL)
|
|
{
|
|
Assert(0); // cicero shouldn't do this
|
|
return CONNECT_E_ADVISELIMIT;
|
|
}
|
|
|
|
if (FAILED(punk->QueryInterface(IID_ITextStoreACPSink, (void **)&_ptss)))
|
|
return E_UNEXPECTED;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// UnadviseSink
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::UnadviseSink(IUnknown *punk)
|
|
{
|
|
Assert(_ptss == punk); // we're dealing with cicero, this should always hold
|
|
SafeReleaseClear(_ptss);
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetSelection
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::GetSelection(ULONG ulIndex, ULONG ulCount, TS_SELECTION_ACP *pSelection, ULONG *pcFetched)
|
|
{
|
|
if (pcFetched == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
*pcFetched = 0;
|
|
|
|
if (ulIndex > 1 && ulIndex != TS_DEFAULT_SELECTION)
|
|
return E_INVALIDARG; // index too high
|
|
|
|
if (ulCount == 0 || ulIndex == 1)
|
|
return S_OK;
|
|
|
|
if (pSelection == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
pSelection[0] = _Sel;
|
|
*pcFetched = 1;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// SetSelection
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::SetSelection(ULONG ulCount, const TS_SELECTION_ACP *pSelection)
|
|
{
|
|
Assert(ulCount > 0); // should have been caught by caller
|
|
Assert(pSelection != NULL); // should have been caught by caller
|
|
|
|
Assert(pSelection[0].acpStart >= 0);
|
|
Assert(pSelection[0].acpEnd >= pSelection[0].acpStart);
|
|
|
|
if (ulCount > 1)
|
|
return E_FAIL; // don't support disjoint sel
|
|
|
|
if (pSelection[0].acpEnd > _cch)
|
|
return TS_E_INVALIDPOS;
|
|
|
|
_Sel = pSelection[0];
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetText
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::GetText(LONG acpStart, LONG acpEnd,
|
|
WCHAR *pchPlain, ULONG cchPlainReq, ULONG *pcchPlainOut,
|
|
TS_RUNINFO *prgRunInfo, ULONG ulRunInfoReq, ULONG *pulRunInfoOut,
|
|
LONG *pacpNext)
|
|
{
|
|
ULONG cch;
|
|
|
|
*pcchPlainOut = 0;
|
|
*pulRunInfoOut = 0;
|
|
*pacpNext = acpStart;
|
|
|
|
if (acpStart < 0 || acpStart > _cch)
|
|
return TS_E_INVALIDPOS;
|
|
|
|
// get a count of acp chars requested
|
|
cch = (acpEnd >= acpStart) ? acpEnd - acpStart : _cch - acpStart;
|
|
// since we're plain text, we can also simply clip by the plaintext buffer len
|
|
if (cchPlainReq > 0) // if they don't want plain text we won't clip!
|
|
{
|
|
cch = min(cch, cchPlainReq);
|
|
}
|
|
|
|
// check for eod
|
|
if (acpStart + cch > (ULONG)_cch)
|
|
{
|
|
cch = _cch - acpStart;
|
|
}
|
|
|
|
if (ulRunInfoReq > 0 && cch > 0)
|
|
{
|
|
*pulRunInfoOut = 1;
|
|
prgRunInfo[0].uCount = cch;
|
|
prgRunInfo[0].type = TS_RT_PLAIN;
|
|
}
|
|
|
|
if (cchPlainReq > 0)
|
|
{
|
|
// we're a plain text buffer, so we always copy all the requested chars
|
|
*pcchPlainOut = cch;
|
|
memcpy(pchPlain, _pch + acpStart, cch*sizeof(WCHAR));
|
|
}
|
|
|
|
*pacpNext = acpStart + cch;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// SetText
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::SetText(DWORD dwFlags, LONG acpStart, LONG acpEnd, const WCHAR *pchText, ULONG cch, TS_TEXTCHANGE *pChange)
|
|
{
|
|
int iSizeRange;
|
|
int cchAdjust;
|
|
TS_STATUS tss;
|
|
WCHAR *pch = NULL;
|
|
|
|
// Since we know our only caller will be cicero, we can assert rather than
|
|
// returning failure codes.
|
|
Assert(acpStart >= 0);
|
|
Assert(acpStart <= acpEnd);
|
|
|
|
if (acpEnd > _cch)
|
|
return TS_E_INVALIDPOS;
|
|
|
|
if (_owner != NULL &&
|
|
_owner->GetStatus(&tss) == S_OK &&
|
|
(tss.dwDynamicFlags & TS_SD_READONLY))
|
|
{
|
|
return TS_E_READONLY;
|
|
}
|
|
|
|
//
|
|
// Check mapped app property for TSATTRID_Text_ReadOnly.
|
|
//
|
|
CProperty *pProp;
|
|
BOOL fReadOnly = FALSE;
|
|
if (SUCCEEDED(_pic->GetMappedAppProperty(TSATTRID_Text_ReadOnly, &pProp)))
|
|
{
|
|
ITfRangeACP *range;
|
|
if (SUCCEEDED(_pic->CreateRange(acpStart, acpEnd, &range)))
|
|
{
|
|
IEnumTfRanges *pEnumRanges;
|
|
|
|
if (SUCCEEDED(pProp->EnumRanges(BACKDOOR_EDIT_COOKIE,
|
|
&pEnumRanges,
|
|
range)))
|
|
{
|
|
ITfRange *rangeTmp;
|
|
while (pEnumRanges->Next(1, &rangeTmp, NULL) == S_OK)
|
|
{
|
|
VARIANT var;
|
|
if (pProp->GetValue(BACKDOOR_EDIT_COOKIE, rangeTmp, &var) == S_OK)
|
|
{
|
|
if (var.lVal != 0)
|
|
{
|
|
fReadOnly = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
rangeTmp->Release();
|
|
}
|
|
|
|
pEnumRanges->Release();
|
|
}
|
|
|
|
range->Release();
|
|
}
|
|
pProp->Release();
|
|
}
|
|
|
|
if (fReadOnly)
|
|
{
|
|
return TS_E_READONLY;
|
|
}
|
|
|
|
|
|
// this will all be rewritten for the gap buffer, so keep it simple for now.
|
|
// delete the ranage, then insert the new text.
|
|
|
|
iSizeRange = acpEnd - acpStart;
|
|
cchAdjust = (LONG)cch - iSizeRange;
|
|
|
|
if (cchAdjust > 0)
|
|
{
|
|
// if we need to alloc more memory, try now, to handle failure gracefully
|
|
if ((pch = (_pch == NULL) ? (WCHAR *)cicMemAlloc((_cch + cchAdjust)*sizeof(WCHAR)) :
|
|
(WCHAR *)cicMemReAlloc(_pch, (_cch + cchAdjust)*sizeof(WCHAR))) == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// we're all set
|
|
_pch = pch;
|
|
}
|
|
|
|
//
|
|
// shift existing text to the right of the range
|
|
//
|
|
memmove(_pch + acpStart + cch, _pch + acpStart + iSizeRange, (_cch - iSizeRange - acpStart)*sizeof(WCHAR));
|
|
|
|
//
|
|
// now fill in the gap
|
|
//
|
|
if (pchText != NULL)
|
|
{
|
|
memcpy(_pch + acpStart, pchText, cch*sizeof(WCHAR));
|
|
}
|
|
|
|
//
|
|
// update our buffer size
|
|
//
|
|
_cch += cchAdjust;
|
|
Assert(_cch >= 0);
|
|
|
|
// if we shrank, try to realloc a smaller buffer (otherwise we alloc'd above)
|
|
if (cchAdjust < 0)
|
|
{
|
|
if (_cch == 0)
|
|
{
|
|
cicMemFree(_pch);
|
|
_pch = NULL;
|
|
}
|
|
else if (pch = (WCHAR *)cicMemReAlloc(_pch, _cch*sizeof(WCHAR)))
|
|
{
|
|
_pch = pch;
|
|
}
|
|
}
|
|
|
|
// handle the out params
|
|
pChange->acpStart = acpStart;
|
|
pChange->acpOldEnd = acpEnd;
|
|
pChange->acpNewEnd = acpEnd + cchAdjust;
|
|
|
|
//
|
|
// update the selection
|
|
//
|
|
_Sel.acpStart = AdjustAnchor(acpStart, acpEnd, cch, _Sel.acpStart, FALSE);
|
|
_Sel.acpEnd = AdjustAnchor(acpStart, acpEnd, cch, _Sel.acpEnd, TRUE);
|
|
Assert(_Sel.acpStart >= 0);
|
|
Assert(_Sel.acpStart <= _Sel.acpEnd);
|
|
Assert(_Sel.acpEnd <= _cch);
|
|
|
|
// never need to call OnTextChange because we have only one adaptor
|
|
// and this class never calls SetText internally
|
|
// do the OnDelta
|
|
//_ptss->OnTextChange(acpStart, acpEnd, acpStart + cch);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetFormattedText
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::GetFormattedText(LONG acpStart, LONG acpEnd, IDataObject **ppDataObject)
|
|
{
|
|
CTFDataObject *pcdo;
|
|
|
|
Assert(acpStart >= 0 && acpEnd <= _cch);
|
|
Assert(acpStart <= acpEnd);
|
|
Assert(ppDataObject != NULL);
|
|
|
|
*ppDataObject = NULL;
|
|
|
|
pcdo = new CTFDataObject;
|
|
|
|
if (pcdo == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (FAILED(pcdo->_SetData(&_pch[acpStart], acpEnd - acpStart)))
|
|
{
|
|
Assert(0);
|
|
pcdo->Release();
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetEmbedded
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::GetEmbedded(LONG acpPos, REFGUID rguidService, REFIID riid, IUnknown **ppunk)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// InsertEmbedded
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::InsertEmbedded(DWORD dwFlags, LONG acpStart, LONG acpEnd, IDataObject *pDataObject, TS_TEXTCHANGE *pChange)
|
|
{
|
|
ULONG cch;
|
|
FORMATETC fe;
|
|
STGMEDIUM sm;
|
|
WCHAR *pch;
|
|
HRESULT hr;
|
|
|
|
Assert(acpStart <= acpEnd);
|
|
Assert((dwFlags & ~TS_IE_CORRECTION) == 0);
|
|
Assert(pDataObject != NULL);
|
|
Assert(pChange != NULL);
|
|
|
|
memset(pChange, 0, sizeof(*pChange));
|
|
|
|
fe.cfFormat = CF_UNICODETEXT;
|
|
fe.ptd = NULL;
|
|
fe.dwAspect = DVASPECT_CONTENT;
|
|
fe.lindex = -1;
|
|
fe.tymed = TYMED_HGLOBAL;
|
|
|
|
if (FAILED(pDataObject->GetData(&fe, &sm)))
|
|
return TS_E_FORMAT;
|
|
|
|
if (sm.hGlobal == NULL)
|
|
return E_FAIL;
|
|
|
|
pch = (WCHAR *)GlobalLock(sm.hGlobal);
|
|
cch = wcslen(pch);
|
|
|
|
hr = SetText(dwFlags, acpStart, acpEnd, pch, cch, pChange);
|
|
|
|
GlobalUnlock(sm.hGlobal);
|
|
ReleaseStgMedium(&sm);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// RequestLock
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#define TS_LF_WRITE (TS_LF_READWRITE & ~TS_LF_READ)
|
|
|
|
STDAPI CTextStoreImpl::RequestLock(DWORD dwLockFlags, HRESULT *phrSession)
|
|
{
|
|
Assert(phrSession != NULL); // caller should have caught this
|
|
|
|
if (_dwlt != 0)
|
|
{
|
|
*phrSession = E_UNEXPECTED;
|
|
|
|
// this is a reentrant call
|
|
// only one case is legal
|
|
if ((_dwlt & TS_LF_WRITE) ||
|
|
!(dwLockFlags & TS_LF_WRITE) ||
|
|
(dwLockFlags & TS_LF_SYNC))
|
|
{
|
|
Assert(0); // bogus reentrant lock req!
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
_fPendingWriteReq = TRUE;
|
|
*phrSession = TS_S_ASYNC;
|
|
return S_OK;
|
|
}
|
|
|
|
_dwlt = dwLockFlags;
|
|
|
|
*phrSession = _ptss->OnLockGranted(dwLockFlags);
|
|
|
|
if (_fPendingWriteReq)
|
|
{
|
|
_dwlt = TS_LF_READWRITE;
|
|
_fPendingWriteReq = FALSE;
|
|
if (_ptss != NULL) // might be NULL if we're disconnected during the OnLockGranted above
|
|
{
|
|
_ptss->OnLockGranted(TS_LF_READWRITE);
|
|
}
|
|
}
|
|
|
|
_dwlt = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetStatus
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::GetStatus(TS_STATUS *pdcs)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (_owner != NULL)
|
|
{
|
|
hr = _owner->GetStatus(pdcs);
|
|
|
|
// only let the owner ctl certain bits
|
|
if (hr == S_OK)
|
|
{
|
|
pdcs->dwDynamicFlags &= (TF_SD_READONLY | TF_SD_LOADING);
|
|
pdcs->dwStaticFlags &= (TF_SS_TRANSITORY);
|
|
}
|
|
else
|
|
{
|
|
memset(pdcs, 0, sizeof(*pdcs));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
memset(pdcs, 0, sizeof(*pdcs));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// QueryInsert
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::QueryInsert(LONG acpTestStart, LONG acpTestEnd, ULONG cch, LONG *pacpResultStart, LONG *pacpResultEnd)
|
|
{
|
|
Assert(acpTestStart >= 0);
|
|
Assert(acpTestStart <= acpTestEnd);
|
|
Assert(acpTestEnd <= _cch);
|
|
|
|
// default text store does not support overtype, and the selection is always replaced
|
|
*pacpResultStart = acpTestStart;
|
|
*pacpResultEnd = acpTestEnd;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Unlock
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::GetEndACP(LONG *pacp)
|
|
{
|
|
*pacp = _cch;
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetACPFromPoint
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::GetACPFromPoint(TsViewCookie vcView, const POINT *pt, DWORD dwFlags, LONG *pacp)
|
|
{
|
|
Assert(vcView == TSI_ACTIVE_VIEW_COOKIE); // default tsi only has a single view
|
|
|
|
if (_owner == NULL)
|
|
return E_FAIL; // who ever owns the ic hasn't bothered to give us a callback....
|
|
|
|
return _owner->GetACPFromPoint(pt, dwFlags, pacp);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetScreenExt
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::GetScreenExt(TsViewCookie vcView, RECT *prc)
|
|
{
|
|
Assert(vcView == TSI_ACTIVE_VIEW_COOKIE); // default tsi only has a single view
|
|
|
|
if (_owner == NULL)
|
|
return E_FAIL; // who ever owns the ic hasn't bothered to give us a callback....
|
|
|
|
return _owner->GetScreenExt(prc);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetTextExt
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::GetTextExt(TsViewCookie vcView, LONG acpStart, LONG acpEnd, RECT *prc, BOOL *pfClipped)
|
|
{
|
|
Assert(vcView == TSI_ACTIVE_VIEW_COOKIE); // default tsi only has a single view
|
|
|
|
if (_owner == NULL)
|
|
return E_FAIL; // who ever owns the ic hasn't bothered to give us a callback....
|
|
|
|
return _owner->GetTextExt(acpStart, acpEnd, prc, pfClipped);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetWnd
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::GetWnd(TsViewCookie vcView, HWND *phwnd)
|
|
{
|
|
Assert(vcView == TSI_ACTIVE_VIEW_COOKIE); // default tsi only has a single view
|
|
Assert(phwnd != NULL); // should have caught this in the ic
|
|
|
|
*phwnd = NULL;
|
|
|
|
if (_owner == NULL)
|
|
return E_FAIL; // who ever owns the ic hasn't bothered to give us a callback....
|
|
|
|
return _owner->GetWnd(phwnd);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _LoadAttr
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CTextStoreImpl::_LoadAttr(DWORD dwFlags, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs)
|
|
{
|
|
ULONG i;
|
|
HRESULT hr;
|
|
|
|
ClearAttrStore();
|
|
|
|
if (dwFlags & TS_ATTR_FIND_WANT_VALUE)
|
|
{
|
|
if (_owner == NULL)
|
|
return E_FAIL;
|
|
}
|
|
|
|
for (i=0; i<cFilterAttrs; i++)
|
|
{
|
|
VARIANT var;
|
|
QuickVariantInit(&var);
|
|
|
|
if (dwFlags & TS_ATTR_FIND_WANT_VALUE)
|
|
{
|
|
if (_owner->GetAttribute(paFilterAttrs[i], &var) != S_OK)
|
|
{
|
|
ClearAttrStore();
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Issue: benwest: I think these should be init'd to VT_EMPTY if caller doesn't specify TS_ATTR_FIND_WANT_VALUE
|
|
if (IsEqualGUID(paFilterAttrs[i], GUID_PROP_MODEBIAS))
|
|
{
|
|
var.vt = VT_I4;
|
|
var.lVal = TF_INVALID_GUIDATOM;
|
|
}
|
|
else if (IsEqualGUID(paFilterAttrs[i], TSATTRID_Text_Orientation))
|
|
{
|
|
var.vt = VT_I4;
|
|
var.lVal = 0;
|
|
}
|
|
else if (IsEqualGUID(paFilterAttrs[i], TSATTRID_Text_VerticalWriting))
|
|
{
|
|
var.vt = VT_BOOL;
|
|
var.lVal = 0;
|
|
}
|
|
}
|
|
|
|
if (var.vt != VT_EMPTY)
|
|
{
|
|
TSI_ATTRSTORE *pas = _rgAttrStore.Append(1);
|
|
if (!pas)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
pas->attrid = paFilterAttrs[i];
|
|
pas->var = var;
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// RequestSupportedAttrs
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::RequestSupportedAttrs(DWORD dwFlags, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs)
|
|
{
|
|
// note the return value is technically a default value, but since we have a value for every location
|
|
// this will never need to be used
|
|
return _LoadAttr(dwFlags, cFilterAttrs, paFilterAttrs);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// RequestAttrsAtPosition
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::RequestAttrsAtPosition(LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags)
|
|
{
|
|
return _LoadAttr(TS_ATTR_FIND_WANT_VALUE, cFilterAttrs, paFilterAttrs);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// RequestAttrsTransitioningAtPosition
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::RequestAttrsTransitioningAtPosition(LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags)
|
|
{
|
|
ClearAttrStore();
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// FindNextAttrTransition
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::FindNextAttrTransition(LONG acpStart, LONG acpHaltPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags, LONG *pacpNext, BOOL *pfFound, LONG *plFoundOffset)
|
|
{
|
|
// our attrs never transition
|
|
|
|
*pacpNext = acpStart;
|
|
*pfFound = FALSE;
|
|
plFoundOffset = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// RetrieveRequestedAttrs
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::RetrieveRequestedAttrs(ULONG ulCount, TS_ATTRVAL *paAttrVals, ULONG *pcFetched)
|
|
{
|
|
ULONG i = 0;
|
|
|
|
while((i < ulCount) && ((int)i < _rgAttrStore.Count()))
|
|
{
|
|
TSI_ATTRSTORE *pas = _rgAttrStore.GetPtr(i);
|
|
paAttrVals->idAttr = pas->attrid;
|
|
paAttrVals->dwOverlapId = 0;
|
|
QuickVariantInit(&paAttrVals->varValue);
|
|
paAttrVals->varValue = pas->var;
|
|
paAttrVals++;
|
|
i++;
|
|
}
|
|
|
|
*pcFetched = i;
|
|
ClearAttrStore();
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// QueryService
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::QueryService(REFGUID guidService, REFIID riid, void **ppv)
|
|
{
|
|
if (ppv == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
*ppv = NULL;
|
|
|
|
if (!IsEqualGUID(guidService, GUID_SERVICE_TF) ||
|
|
!IsEqualIID(riid, IID_PRIV_CTSI))
|
|
{
|
|
// 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
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
*ppv = this;
|
|
AddRef();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetActiveView
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::GetActiveView(TsViewCookie *pvcView)
|
|
{
|
|
// each CEditWnd has only a single view, so this can be constant.
|
|
*pvcView = TSI_ACTIVE_VIEW_COOKIE;
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// AdviseMouseSink
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::AdviseMouseSink(ITfRangeACP *range, ITfMouseSink *pSink, DWORD *pdwCookie)
|
|
{
|
|
ITfMouseTrackerACP *pTracker;
|
|
HRESULT hr;
|
|
|
|
Assert(range != NULL);
|
|
Assert(pSink != NULL);
|
|
Assert(pdwCookie != NULL);
|
|
|
|
*pdwCookie = 0;
|
|
|
|
if (_owner == NULL)
|
|
return E_FAIL;
|
|
|
|
if (_owner->QueryInterface(IID_ITfMouseTrackerACP, (void **)&pTracker) != S_OK)
|
|
return E_NOTIMPL;
|
|
|
|
hr = pTracker->AdviseMouseSink(range, pSink, pdwCookie);
|
|
|
|
pTracker->Release();
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// UnadviseMouseSink
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::UnadviseMouseSink(DWORD dwCookie)
|
|
{
|
|
ITfMouseTrackerACP *pTracker;
|
|
HRESULT hr;
|
|
|
|
if (_owner == NULL)
|
|
return E_FAIL;
|
|
|
|
if (_owner->QueryInterface(IID_ITfMouseTrackerACP, (void **)&pTracker) != S_OK)
|
|
return E_NOTIMPL;
|
|
|
|
hr = pTracker->UnadviseMouseSink(dwCookie);
|
|
|
|
pTracker->Release();
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// QueryInsertEmbedded
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::QueryInsertEmbedded(const GUID *pguidService, const FORMATETC *pFormatEtc, BOOL *pfInsertable)
|
|
{
|
|
Assert(pfInsertable != NULL); // cicero should have caught this
|
|
|
|
*pfInsertable = FALSE;
|
|
|
|
// only accept unicode text
|
|
if (pguidService == NULL &&
|
|
pFormatEtc != NULL &&
|
|
pFormatEtc->cfFormat == CF_UNICODETEXT &&
|
|
pFormatEtc->dwAspect == DVASPECT_CONTENT &&
|
|
pFormatEtc->lindex == -1 &&
|
|
pFormatEtc->tymed == TYMED_HGLOBAL)
|
|
{
|
|
*pfInsertable = TRUE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// InsertTextAtSelection
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::InsertTextAtSelection(DWORD dwFlags, const WCHAR *pchText,
|
|
ULONG cch, LONG *pacpStart, LONG *pacpEnd, TS_TEXTCHANGE *pChange)
|
|
{
|
|
HRESULT hr;
|
|
|
|
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(pacpStart != NULL && pacpEnd != NULL); // caller should have already caught this
|
|
Assert((dwFlags & (TS_IAS_NOQUERY | TS_IAS_QUERYONLY)) != (TS_IAS_NOQUERY | TS_IAS_QUERYONLY)); // caller should have already caught this
|
|
|
|
if (dwFlags & TS_IAS_QUERYONLY)
|
|
goto Exit;
|
|
|
|
*pacpStart = -1;
|
|
*pacpEnd = -1;
|
|
|
|
hr = SetText(0, _Sel.acpStart, _Sel.acpEnd, pchText, cch, pChange);
|
|
|
|
if (hr != S_OK)
|
|
return hr;
|
|
|
|
Exit:
|
|
// since this is cheap, always set the insert span even if caller sets TS_IAS_NOQUERY
|
|
*pacpStart = _Sel.acpStart;
|
|
*pacpEnd = _Sel.acpEnd;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// InsertEmbeddedAtSelection
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CTextStoreImpl::InsertEmbeddedAtSelection(DWORD dwFlags, IDataObject *pDataObject,
|
|
LONG *pacpStart, LONG *pacpEnd, TS_TEXTCHANGE *pChange)
|
|
{
|
|
HRESULT hr;
|
|
|
|
Assert((dwFlags & TS_IAS_QUERYONLY) || pDataObject != NULL); // caller should have already caught this
|
|
Assert(pacpStart != NULL && pacpEnd != NULL); // caller should have already caught this
|
|
Assert((dwFlags & (TS_IAS_NOQUERY | TS_IAS_QUERYONLY)) != (TS_IAS_NOQUERY | TS_IAS_QUERYONLY)); // caller should have already caught this
|
|
|
|
if (dwFlags & TS_IAS_QUERYONLY)
|
|
goto Exit;
|
|
|
|
*pacpStart = -1;
|
|
*pacpEnd = -1;
|
|
|
|
hr = InsertEmbedded(0, _Sel.acpStart, _Sel.acpEnd, pDataObject, pChange);
|
|
|
|
if (hr != S_OK)
|
|
return hr;
|
|
|
|
Exit:
|
|
// since this is cheap, always set the insert span even if caller sets TS_IAS_QUERYONLY
|
|
*pacpStart = _Sel.acpStart;
|
|
*pacpEnd = _Sel.acpEnd;
|
|
|
|
return S_OK;
|
|
}
|