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.
4595 lines
95 KiB
4595 lines
95 KiB
/*
|
|
* @doc INTERNAL
|
|
*
|
|
* @module CUIM.CPP -- Cicero Implementation
|
|
*
|
|
* Most everything to do with Cicero handling.
|
|
*
|
|
* Original Author: <nl>
|
|
* Hon Wah Chan
|
|
*
|
|
* History: <nl>
|
|
* 11/16/1999 honwch
|
|
*
|
|
* Copyright (c) 1995-2001, Microsoft Corporation. All rights reserved.
|
|
*/
|
|
#include "_common.h"
|
|
|
|
#ifndef NOFEPROCESSING
|
|
|
|
#ifndef NOPRIVATEMESSAGE
|
|
#include "_MSREMSG.H"
|
|
#endif
|
|
|
|
#include "_array.h"
|
|
#include "msctf.h"
|
|
#include "textstor.h"
|
|
#include "ctffunc.h"
|
|
|
|
#include "msctf_g.c"
|
|
#include "msctf_i.c"
|
|
#include "textstor_i.c"
|
|
#include "ctffunc_i.c"
|
|
#include "msctfp.h"
|
|
#include "msctfp_g.c"
|
|
|
|
#include "textserv.h"
|
|
#include "_cmsgflt.h"
|
|
#include "_ime.h"
|
|
|
|
#include "_cuim.h"
|
|
|
|
const IID IID_ITfContextRenderingMarkup = {
|
|
0xa305b1c0,
|
|
0xc776,
|
|
0x4523,
|
|
{0xbd, 0xa0, 0x7c, 0x5a, 0x2e, 0x0f, 0xef, 0x10}
|
|
};
|
|
|
|
const IID IID_ITfEnableService = {
|
|
0x3035d250,
|
|
0x43b4,
|
|
0x4253,
|
|
{0x81, 0xe6, 0xea, 0x87, 0xfd, 0x3e, 0xed, 0x43}
|
|
};
|
|
|
|
const IID IID_IServiceProvider = {
|
|
0x6d5140c1,
|
|
0x7436,
|
|
0x11ce,
|
|
{0x80, 0x34, 0x00, 0xaa, 0x00, 0x60, 0x09, 0xfa}
|
|
};
|
|
|
|
// {35D46968-01FF-4cd8-A379-9A87C9CC789F}
|
|
const GUID CLSID_MSFTEDIT = {
|
|
0x35d46968,
|
|
0x01ff,
|
|
0x4cd8,
|
|
{0xa3,0x79,0x9a,0x87, 0xc9,0xcc,0x78,0x9f}
|
|
};
|
|
|
|
#define CONNECT_E_NOCONNECTION MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, 0x0200) // from OLECTL.H
|
|
|
|
#undef DEFINE_GUID
|
|
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
|
const GUID name \
|
|
= { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
|
|
|
#include "dcattrs.h"
|
|
|
|
const TS_ATTRID *_arTSAttridSupported[] =
|
|
{
|
|
&DCATTRID_Font_FaceName, // iattrFacename
|
|
&DCATTRID_Font_SizePts, // iattrSize
|
|
&DCATTRID_Font_Style_Color, // iattrColor
|
|
&DCATTRID_Font_Style_Bold, // iattrBold
|
|
&DCATTRID_Font_Style_Italic, // iattrItalic
|
|
&DCATTRID_Font_Style_Underline, // iattrUnderline
|
|
&DCATTRID_Font_Style_Subscript, // iattrSubscript
|
|
&DCATTRID_Font_Style_Superscript, // iattrSuperscript
|
|
&DCATTRID_Text_RightToLeft, // iattrRTL
|
|
&DCATTRID_Text_VerticalWriting, // iattrVertical
|
|
&GUID_PROP_MODEBIAS, // iattrBias
|
|
&DCATTRID_Text_Orientation, // iattrTxtOrient
|
|
};
|
|
|
|
enum IATTR_INDEX
|
|
{
|
|
iattrFacename = 0,
|
|
iattrSize,
|
|
iattrColor,
|
|
iattrBold,
|
|
iattrItalic,
|
|
iattrUnderline,
|
|
iattrSubscript,
|
|
iattrSuperscript,
|
|
iattrRTL,
|
|
iattrVertical,
|
|
iattrBias,
|
|
iattrTxtOrient,
|
|
MAX_ATTR_SUPPORT
|
|
};
|
|
|
|
|
|
/* GUID_NULL */
|
|
const GUID GUID_NULL = {
|
|
0x00000000,
|
|
0x0000,
|
|
0x0000,
|
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
};
|
|
|
|
const GUID GUID_DCSERVICE_DATAOBJECT = {
|
|
0x6086fbb5,
|
|
0xe225,
|
|
0x46ce,
|
|
{0xa7, 0x70, 0xc1, 0xbb, 0xd3, 0xe0, 0x5d, 0x7b}
|
|
};
|
|
|
|
const GUID GUID_DCSERVICE_ACCESSIBLE = {
|
|
0xf9786200,
|
|
0xa5bf,
|
|
0x4a0f,
|
|
{0x8c, 0x24, 0xfb, 0x16, 0xf5, 0xd1, 0xaa, 0xbb}
|
|
};
|
|
|
|
const GUID GUID_DCSERVICE_ACTIVEX = {
|
|
0xea937a50,
|
|
0xc9a6,
|
|
0x4b7d,
|
|
{0x89, 0x4a, 0x49, 0xd9, 0x9b, 0x78, 0x48, 0x34}
|
|
};
|
|
|
|
// This array need to match the definition for EM_SETCTFMODEBIAS
|
|
const GUID *_arModeBiasSupported[] =
|
|
{
|
|
&GUID_MODEBIAS_NONE,
|
|
&GUID_MODEBIAS_FILENAME,
|
|
&GUID_MODEBIAS_NAME,
|
|
&GUID_MODEBIAS_READING,
|
|
&GUID_MODEBIAS_DATETIME,
|
|
&GUID_MODEBIAS_CONVERSATION,
|
|
&GUID_MODEBIAS_NUMERIC,
|
|
&GUID_MODEBIAS_HIRAGANA,
|
|
&GUID_MODEBIAS_KATAKANA,
|
|
&GUID_MODEBIAS_HANGUL,
|
|
&GUID_MODEBIAS_HALFWIDTHKATAKANA,
|
|
&GUID_MODEBIAS_FULLWIDTHALPHANUMERIC,
|
|
&GUID_MODEBIAS_HALFWIDTHALPHANUMERIC,
|
|
};
|
|
|
|
/*
|
|
* CUIM::CUIM ()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*
|
|
*/
|
|
CUIM::CUIM(CTextMsgFilter *pTextMsgFilter)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::CUIM");
|
|
|
|
_crefs = 1;
|
|
_pTextMsgFilter = pTextMsgFilter;
|
|
};
|
|
|
|
/*
|
|
* CUIM::~CUIM ()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*
|
|
*/
|
|
CUIM::~CUIM()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::~CUIM");
|
|
|
|
Uninit();
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::QueryInterface (riid, ppv)
|
|
*
|
|
* @mfunc
|
|
* IUnknown QueryInterface support
|
|
*
|
|
* @rdesc
|
|
* NOERROR if interface supported
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::QueryInterface (REFIID riid, void ** ppv)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::QueryInterface");
|
|
|
|
if( IsEqualIID(riid, IID_IUnknown) ||
|
|
IsEqualIID(riid, IID_ITextStoreACP) )
|
|
*ppv = (ITextStoreACP *)this;
|
|
|
|
else if(IsEqualIID(riid, IID_ITfContextOwnerCompositionSink) )
|
|
*ppv = (ITfContextOwnerCompositionSink *)this;
|
|
|
|
else if (IsEqualIID(riid, IID_ITfMouseTrackerACP))
|
|
*ppv = (ITfMouseTrackerACP *)this;
|
|
|
|
else if (IsEqualIID(riid, IID_ITfEnableService))
|
|
*ppv = (ITfEnableService *)this;
|
|
|
|
else if (IsEqualIID(riid, IID_IServiceProvider))
|
|
*ppv = (IServiceProvider *)this;
|
|
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
AddRef();
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP_(ULONG) CUIM::AddRef
|
|
*
|
|
* @mfunc
|
|
* IUnknown AddRef support
|
|
*
|
|
* @rdesc
|
|
* Reference count
|
|
*/
|
|
STDMETHODIMP_(ULONG) CUIM::AddRef()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::AddRef");
|
|
|
|
return ++_crefs;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP_(ULONG) CUIM::Release()
|
|
*
|
|
* @mfunc
|
|
* IUnknown Release support - delete object when reference count is 0
|
|
*
|
|
* @rdesc
|
|
* Reference count
|
|
*/
|
|
STDMETHODIMP_(ULONG) CUIM::Release()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::Release");
|
|
|
|
_crefs--;
|
|
|
|
if( _crefs == 0 )
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return _crefs;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::AdviseSink()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::AdviseSink(
|
|
REFIID riid,
|
|
IUnknown *punk,
|
|
DWORD dwMask)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::AdviseSink");
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (_fShutDown)
|
|
return E_UNEXPECTED;
|
|
|
|
Assert(_ptss == NULL);
|
|
|
|
if(IsEqualIID(riid, IID_ITextStoreACPSink))
|
|
hr = punk->QueryInterface(riid, (void **)&_ptss);
|
|
|
|
return hr == S_OK ? S_OK : E_UNEXPECTED;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::UnadviseSink()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::UnadviseSink(IUnknown *punk)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::UnadviseSink");
|
|
|
|
Assert(_ptss == punk); // we're dealing with cicero, this should always hold
|
|
_ptss->Release();
|
|
_ptss = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::RequestLock()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::RequestLock(
|
|
DWORD dwLockFlags,
|
|
HRESULT *phrSession)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::RequestLock");
|
|
|
|
if (!phrSession)
|
|
return E_POINTER;
|
|
|
|
if (_fShutDown)
|
|
{
|
|
*phrSession = TS_E_SYNCHRONOUS;
|
|
return S_OK;
|
|
}
|
|
|
|
Assert(_ptss);
|
|
|
|
if (_cCallMgrLevels && !_fAllowUIMLock || // Check if we are ready to grant locks
|
|
_fReadLockOn || _fWriteLockOn) // We don't allow re-entrance either.
|
|
{
|
|
// No lock allow
|
|
if (dwLockFlags & TS_LF_SYNC)
|
|
*phrSession = TS_E_SYNCHRONOUS;
|
|
else
|
|
{
|
|
if (dwLockFlags & TS_LF_READ)
|
|
_fReadLockPending = 1;
|
|
if ((dwLockFlags & TS_LF_READWRITE) == TS_LF_READWRITE)
|
|
_fWriteLockPending = 1;
|
|
|
|
*phrSession = TS_S_ASYNC;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
IUnknown *pIUnknown = NULL;
|
|
|
|
HRESULT hResult = _pTextMsgFilter->_pTextDoc->GetCallManager(&pIUnknown);
|
|
|
|
if ((dwLockFlags & TS_LF_READWRITE) == TS_LF_READWRITE)
|
|
{
|
|
_fReadLockPending = 0;
|
|
_fWriteLockPending = 0;
|
|
_fReadLockOn = 1;
|
|
_fWriteLockOn = 1;
|
|
}
|
|
else if ((dwLockFlags & TS_LF_READ) == TS_LF_READ)
|
|
{
|
|
_fReadLockPending = 0;
|
|
_fReadLockOn = 1;
|
|
}
|
|
|
|
if (_fWriteLockOn)
|
|
{
|
|
if (W32->IsFECodePage(_pTextMsgFilter->_uKeyBoardCodePage))
|
|
_pTextMsgFilter->_pTextDoc->IMEInProgress(tomTrue);
|
|
EnterCriticalSection(&g_CriticalSection);
|
|
}
|
|
|
|
*phrSession = _ptss->OnLockGranted(dwLockFlags);
|
|
|
|
if (_fWriteLockOn)
|
|
{
|
|
// Check if any text has been added
|
|
if (_parITfEnumRange && _parITfEnumRange->Count())
|
|
{
|
|
int idx;
|
|
int idxMax = _parITfEnumRange->Count();
|
|
|
|
for (idx = 0 ; idx < idxMax; idx++)
|
|
{
|
|
IEnumTfRanges **ppEnumRange = (IEnumTfRanges **)(_parITfEnumRange->Elem(idx));
|
|
if (ppEnumRange && *ppEnumRange)
|
|
{
|
|
HandleFocusRange(*ppEnumRange);
|
|
(*ppEnumRange)->Release();
|
|
}
|
|
}
|
|
_parITfEnumRange->Clear(AF_KEEPMEM);
|
|
}
|
|
}
|
|
|
|
if (_fEndTyping)
|
|
OnUIMTypingDone();
|
|
|
|
if (_fWriteLockOn)
|
|
{
|
|
_pTextMsgFilter->_pTextDoc->IMEInProgress(tomFalse);
|
|
LeaveCriticalSection(&g_CriticalSection);
|
|
}
|
|
|
|
_fEndTyping = 0;
|
|
_fWriteLockOn = 0;
|
|
_fReadLockOn = 0;
|
|
_fHoldCTFSelChangeNotify = 1;
|
|
|
|
if (pIUnknown)
|
|
hResult = _pTextMsgFilter->_pTextDoc->ReleaseCallManager(pIUnknown);
|
|
|
|
_fHoldCTFSelChangeNotify = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetStatus()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetStatus(
|
|
TS_STATUS *pdcs)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetStatus");
|
|
|
|
if (_fShutDown)
|
|
return S_OK;
|
|
|
|
if (pdcs)
|
|
{
|
|
LRESULT lresult = 0;
|
|
|
|
pdcs->dwStaticFlags = (TS_SS_REGIONS | TS_SS_NOHIDDENTEXT);
|
|
|
|
if ( S_OK == _pTextMsgFilter->_pTextService->TxSendMessage(
|
|
EM_GETDOCFLAGS, GDF_ALL, 0, &lresult))
|
|
{
|
|
if (lresult & GDF_READONLY)
|
|
pdcs->dwDynamicFlags = TS_SD_READONLY;
|
|
|
|
// Don't want to support overtyping in Cicero yet.
|
|
// if (lresult & GDF_OVERTYPE)
|
|
// dcs.dwDynamicFlags = TS_SD_OVERTYPE;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
/*
|
|
* STDMETHODIMP CUIM::QueryInsert()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::QueryInsert(
|
|
LONG acpTestStart,
|
|
LONG acpTestEnd,
|
|
ULONG cch,
|
|
LONG *pacpResultStart,
|
|
LONG *pacpResultEnd)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::QueryInsert");
|
|
|
|
HRESULT hResult;
|
|
ITextRange *pTextRange = NULL;
|
|
|
|
*pacpResultStart = -1;
|
|
*pacpResultEnd = -1;
|
|
|
|
if (_fShutDown)
|
|
return S_OK;
|
|
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(acpTestStart, acpTestEnd, &pTextRange);
|
|
|
|
if (hResult != S_OK)
|
|
return TS_E_READONLY;
|
|
|
|
Assert(pTextRange);
|
|
if(pTextRange->CanEdit(NULL) == S_FALSE)
|
|
{
|
|
hResult = TS_E_READONLY;
|
|
goto EXIT; // Cannot edit text
|
|
}
|
|
|
|
*pacpResultStart = acpTestStart;
|
|
*pacpResultEnd = acpTestEnd;
|
|
hResult = S_OK;
|
|
|
|
EXIT:
|
|
pTextRange->Release();
|
|
return hResult;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetSelection()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetSelection(
|
|
ULONG ulIndex,
|
|
ULONG ulCount,
|
|
TS_SELECTION_ACP *pSelection,
|
|
ULONG *pcFetched)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetSelection");
|
|
|
|
HRESULT hResult;
|
|
ITextSelection *pTextSel = NULL;
|
|
|
|
if (!pSelection || !pcFetched)
|
|
return E_POINTER;
|
|
|
|
if (!_fReadLockOn)
|
|
return TS_E_NOLOCK;
|
|
|
|
*pcFetched = 0;
|
|
|
|
if (_fShutDown)
|
|
return TS_E_NOSELECTION;
|
|
|
|
if (ulIndex == TS_DEFAULT_SELECTION)
|
|
ulIndex = 0;
|
|
else if (ulIndex > 1)
|
|
return E_INVALIDARG; // We donnot have discontiguous selection.
|
|
|
|
if (_fInterimChar)
|
|
{
|
|
pSelection[0].acpStart = _acpInterimStart;
|
|
pSelection[0].acpEnd = _acpInterimEnd;
|
|
pSelection[0].style.ase = (TsActiveSelEnd) _ase;
|
|
pSelection[0].style.fInterimChar = TRUE;
|
|
|
|
*pcFetched = 1;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
hResult = _pTextMsgFilter->_pTextDoc->GetSelectionEx(&pTextSel);
|
|
|
|
if (pTextSel)
|
|
{
|
|
long cpMin = 0, cpMax = 0;
|
|
long lFlags = 0;
|
|
hResult = pTextSel->GetStart(&cpMin);
|
|
hResult = pTextSel->GetEnd(&cpMax);
|
|
hResult = pTextSel->GetFlags(&lFlags);
|
|
|
|
pSelection[0].acpStart = cpMin;
|
|
pSelection[0].acpEnd = cpMax;
|
|
pSelection[0].style.ase = (lFlags & tomSelStartActive) ? TS_AE_START : TS_AE_END;
|
|
pSelection[0].style.fInterimChar = FALSE;
|
|
|
|
*pcFetched = 1;
|
|
pTextSel->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
return TS_E_NOSELECTION;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::SetSelection()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::SetSelection(
|
|
ULONG ulCount,
|
|
const TS_SELECTION_ACP *pSelection)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::SetSelection");
|
|
|
|
HRESULT hResult;
|
|
ITextRange *pTextRange = NULL;
|
|
|
|
if (!pSelection)
|
|
return E_POINTER;
|
|
|
|
if (ulCount <= 0)
|
|
return E_INVALIDARG;
|
|
|
|
if (!_fWriteLockOn)
|
|
return TS_E_NOLOCK;
|
|
|
|
if (_fShutDown)
|
|
return S_OK;
|
|
|
|
if (pSelection->style.fInterimChar)
|
|
{
|
|
_pTextMsgFilter->_pTextDoc->SetCaretType(tomKoreanBlockCaret); // Set Block caret mode
|
|
_acpInterimStart = pSelection[0].acpStart;
|
|
_acpInterimEnd = pSelection[0].acpEnd;
|
|
_fInterimChar = 1;
|
|
_ase = pSelection[0].style.ase;
|
|
}
|
|
else
|
|
{
|
|
if (_fInterimChar)
|
|
{
|
|
_fInterimChar = 0;
|
|
_pTextMsgFilter->_pTextDoc->SetCaretType(tomNormalCaret); // Reset Block caret mode
|
|
}
|
|
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(pSelection[0].acpStart, pSelection[0].acpEnd, &pTextRange);
|
|
|
|
if (pTextRange)
|
|
{
|
|
long lCount;
|
|
_pTextMsgFilter->_pTextDoc->Freeze(&lCount); // Turn off display
|
|
pTextRange->Select();
|
|
pTextRange->Release();
|
|
_pTextMsgFilter->_pTextDoc->Unfreeze(&lCount); // Turn on display
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetText()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetText(
|
|
LONG acpStart,
|
|
LONG acpEnd,
|
|
WCHAR *pchPlain,
|
|
ULONG cchPlainReq,
|
|
ULONG *pcchPlainOut,
|
|
TS_RUNINFO *prgRunInfo,
|
|
ULONG ulRunInfoReq,
|
|
ULONG *pulRunInfoOut,
|
|
LONG *pacpNext)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetText");
|
|
|
|
if (!_fReadLockOn)
|
|
return TS_E_NOLOCK;
|
|
|
|
if (pchPlain == NULL && cchPlainReq != 0 ||
|
|
prgRunInfo == NULL && ulRunInfoReq != 0)
|
|
return E_INVALIDARG;
|
|
|
|
BOOL fDoRunInfo = ulRunInfoReq > 0;
|
|
LONG acpMaxText = 0;
|
|
BOOL fEOP = FALSE;
|
|
|
|
GetStoryLength(&acpMaxText);
|
|
|
|
if (acpStart < 0 || acpStart > acpMaxText)
|
|
return TS_E_INVALIDPOS;
|
|
|
|
if (acpEnd < 0)
|
|
acpEnd = acpMaxText;
|
|
else if (acpEnd < acpStart)
|
|
return TS_E_INVALIDPOS;
|
|
|
|
if (pcchPlainOut)
|
|
*pcchPlainOut = 0;
|
|
if (pulRunInfoOut)
|
|
*pulRunInfoOut = 0;
|
|
if (pacpNext)
|
|
*pacpNext = acpStart;
|
|
|
|
if (_fShutDown)
|
|
return S_OK;
|
|
|
|
LRESULT lresult = 0;
|
|
if ( S_OK == _pTextMsgFilter->_pTextService->TxSendMessage(
|
|
EM_GETDOCFLAGS, GDF_ALL, 0, &lresult))
|
|
{
|
|
if ((lresult & GDF_RICHTEXT) && acpEnd == acpMaxText)
|
|
fEOP = TRUE;
|
|
}
|
|
|
|
if (cchPlainReq || ulRunInfoReq)
|
|
{
|
|
HRESULT hResult;
|
|
ITextRange *pTextRange = NULL;
|
|
long fHiddenTextInRange = tomFalse;
|
|
BOOL fCopyData = FALSE;
|
|
long *pHiddenTxtBlk = NULL;
|
|
long cHiddenTxtBlk = 0;
|
|
|
|
if (cchPlainReq && acpEnd > (long)cchPlainReq + acpStart)
|
|
acpEnd = cchPlainReq + acpStart;
|
|
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(acpStart, acpEnd, &pTextRange);
|
|
|
|
if (pTextRange)
|
|
{
|
|
BSTR bstr = NULL;
|
|
long cpMin, cpMax;
|
|
ULONG cch;
|
|
|
|
pTextRange->GetStart(&cpMin);
|
|
pTextRange->GetEnd(&cpMax);
|
|
|
|
if (fDoRunInfo)
|
|
{
|
|
ITextFont *pFont = NULL;
|
|
|
|
hResult = pTextRange->GetFont(&pFont);
|
|
|
|
if (pFont)
|
|
{
|
|
pFont->GetHidden(&fHiddenTextInRange);
|
|
pFont->Release();
|
|
|
|
if (fHiddenTextInRange == tomUndefined) // Some hidden text inside range
|
|
BuildHiddenTxtBlks(cpMin, cpMax, &pHiddenTxtBlk, cHiddenTxtBlk);
|
|
}
|
|
}
|
|
|
|
hResult = pTextRange->GetText(&bstr);
|
|
|
|
if (bstr)
|
|
{
|
|
cch = cpMax - cpMin;
|
|
|
|
if (cchPlainReq)
|
|
{
|
|
if (cchPlainReq > cch)
|
|
cchPlainReq = cch;
|
|
|
|
fCopyData = TRUE;
|
|
}
|
|
else
|
|
cchPlainReq = cch;
|
|
|
|
// Convert character into special Cicero char.
|
|
long cpCurrentStart = cpMin;
|
|
long cpCurrent = cpMin;
|
|
long idx = 0;
|
|
ULONG cRunInfo = 0;
|
|
BOOL fRunInfoNotEnough = FALSE;
|
|
|
|
long cpNextHiddenText = tomForward;
|
|
|
|
if (fDoRunInfo && pHiddenTxtBlk)
|
|
cpNextHiddenText = pHiddenTxtBlk[0];
|
|
|
|
if (fHiddenTextInRange != tomTrue)
|
|
{
|
|
WCHAR *pText = (WCHAR *)bstr;
|
|
while (cpCurrent < cpMax)
|
|
{
|
|
if (cpCurrent == cpNextHiddenText)
|
|
{
|
|
// setup run info for current good text
|
|
if (cpCurrent != cpCurrentStart)
|
|
{
|
|
if (cRunInfo >= ulRunInfoReq)
|
|
{
|
|
fRunInfoNotEnough = TRUE;
|
|
break;
|
|
}
|
|
prgRunInfo[cRunInfo].uCount = cpCurrent - cpCurrentStart;
|
|
prgRunInfo[cRunInfo].type = TS_RT_PLAIN;
|
|
cRunInfo++;
|
|
}
|
|
|
|
long cchHiddenText = pHiddenTxtBlk[idx+1];
|
|
|
|
// setup run info for hidden text block
|
|
if (cRunInfo >= ulRunInfoReq)
|
|
{
|
|
fRunInfoNotEnough = TRUE;
|
|
break;
|
|
}
|
|
prgRunInfo[cRunInfo].uCount = cchHiddenText;
|
|
prgRunInfo[cRunInfo].type = TS_RT_OPAQUE;
|
|
cRunInfo++;
|
|
|
|
idx += 2;
|
|
if (idx < cHiddenTxtBlk)
|
|
cpNextHiddenText = pHiddenTxtBlk[idx];
|
|
else
|
|
cpNextHiddenText = tomForward;
|
|
|
|
cpCurrent += cchHiddenText;
|
|
pText += cchHiddenText;
|
|
cpCurrentStart = cpCurrent;
|
|
}
|
|
else
|
|
{
|
|
switch (*pText)
|
|
{
|
|
case WCH_EMBEDDING:
|
|
*pText = TS_CHAR_EMBEDDED;
|
|
break;
|
|
|
|
case STARTFIELD:
|
|
case ENDFIELD:
|
|
*pText = TS_CHAR_REGION;
|
|
if (cpCurrent + 1 < cpMax)
|
|
{
|
|
pText++;
|
|
cpCurrent++;
|
|
Assert(*pText == 0x000d);
|
|
*pText = TS_CHAR_REGION;
|
|
}
|
|
break;
|
|
}
|
|
cpCurrent++;
|
|
// Convert EOP into TS_CHAR_REGION
|
|
if (fEOP && cpCurrent == acpMaxText && *pText == CR)
|
|
*pText = TS_CHAR_REGION;
|
|
pText++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fDoRunInfo)
|
|
{
|
|
// setup run info for last chunk of good text
|
|
if (cpCurrent != cpCurrentStart && cRunInfo < ulRunInfoReq)
|
|
{
|
|
prgRunInfo[cRunInfo].uCount = cpCurrent - cpCurrentStart;
|
|
prgRunInfo[cRunInfo].type = TS_RT_PLAIN;
|
|
cRunInfo++;
|
|
}
|
|
|
|
if (pulRunInfoOut)
|
|
*pulRunInfoOut = cRunInfo ? cRunInfo : 1;
|
|
|
|
// All the text belong to the same run
|
|
if (cRunInfo == 0)
|
|
{
|
|
prgRunInfo[0].uCount = cchPlainReq;
|
|
prgRunInfo[0].type = (fHiddenTextInRange == tomTrue) ? TS_RT_OPAQUE : TS_RT_PLAIN;
|
|
}
|
|
}
|
|
|
|
if (fRunInfoNotEnough)
|
|
{
|
|
// Runinfo too small. need to add cch from all valid runs
|
|
TS_RUNINFO *prgRunInfoData = prgRunInfo;
|
|
ULONG idx;
|
|
cchPlainReq = 0;
|
|
for (idx=0; idx < cRunInfo; idx++)
|
|
{
|
|
cchPlainReq += prgRunInfoData->uCount;
|
|
prgRunInfoData++;
|
|
}
|
|
}
|
|
|
|
if (fCopyData)
|
|
// fill in the buffer
|
|
memcpy(pchPlain, (LPSTR)bstr, cchPlainReq * sizeof(WCHAR));
|
|
|
|
if (pcchPlainOut)
|
|
*pcchPlainOut = cchPlainReq;
|
|
|
|
if (pacpNext)
|
|
*pacpNext = cpMin + cchPlainReq;
|
|
|
|
SysFreeString(bstr);
|
|
}
|
|
|
|
pTextRange->Release();
|
|
|
|
FreePv(pHiddenTxtBlk);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::SetText()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::SetText(
|
|
DWORD dwFlags,
|
|
LONG acpStart,
|
|
LONG acpEnd,
|
|
const WCHAR *pchText,
|
|
ULONG cch,
|
|
TS_TEXTCHANGE *pChange)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::SetText");
|
|
|
|
return InsertData(dwFlags, acpStart, acpEnd, pchText, cch, NULL, pChange);
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::InsertData()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::InsertData(
|
|
DWORD dwFlags,
|
|
LONG acpStart,
|
|
LONG acpEnd,
|
|
const WCHAR *pchText,
|
|
ULONG cch,
|
|
IDataObject *pDataObject,
|
|
TS_TEXTCHANGE *pChange)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::InsertData");
|
|
|
|
HRESULT hResult = S_OK;
|
|
ITextRange *pTextRange = NULL;
|
|
BOOL fInsertObject = pDataObject != NULL;
|
|
|
|
if (!_fWriteLockOn)
|
|
return TS_E_NOLOCK;
|
|
|
|
if (_fShutDown)
|
|
return S_OK;
|
|
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(acpStart, acpEnd, &pTextRange);
|
|
|
|
if (pTextRange)
|
|
{
|
|
BSTR bstr = NULL;
|
|
|
|
if(pTextRange->CanEdit(NULL) == S_FALSE)
|
|
{
|
|
pTextRange->Release();
|
|
return TS_E_READONLY; // Cannot edit text
|
|
}
|
|
|
|
LONG cchExced = 0;
|
|
BOOL fDelSelection = FALSE;
|
|
if ((LONG)cch > (acpEnd - acpStart) &&
|
|
_pTextMsgFilter->_pTextDoc->CheckTextLimit((LONG)cch - (acpEnd-acpStart), &cchExced) == NOERROR &&
|
|
cchExced > 0)
|
|
{
|
|
// We reach text limit, beep and exit
|
|
_pTextMsgFilter->_pTextDoc->SysBeep();
|
|
pTextRange->Release();
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (!fInsertObject)
|
|
{
|
|
bstr = SysAllocStringLen(pchText, cch);
|
|
|
|
if (!bstr)
|
|
{
|
|
pTextRange->Release();
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (!_fAnyWriteOperation)
|
|
{
|
|
// Start the UIM typing
|
|
ITextFont *pCurrentFont = NULL;
|
|
BOOL fRestFont = TRUE;
|
|
_fAnyWriteOperation = 1;
|
|
|
|
hResult = pTextRange->GetStart(&_cpMin);
|
|
|
|
// Hold notification if needed
|
|
if (!(_pTextMsgFilter->_fIMEAlwaysNotify))
|
|
_pTextMsgFilter->_pTextDoc->SetNotificationMode(tomFalse);
|
|
|
|
if (!_bstrComposition)
|
|
{
|
|
if (fRestFont && _pTextFont)
|
|
{
|
|
_pTextFont->Release();
|
|
_pTextFont = NULL;
|
|
}
|
|
|
|
if (acpStart != acpEnd)
|
|
{
|
|
if (_pTextFont == NULL)
|
|
{
|
|
ITextRange *pRange = NULL;
|
|
// Get font at cpStart+1
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(acpStart, acpStart+1, &pRange);
|
|
if (pRange)
|
|
{
|
|
hResult = pRange->GetFont(&pCurrentFont);
|
|
|
|
if (pCurrentFont)
|
|
{
|
|
hResult = pCurrentFont->GetDuplicate(&_pTextFont);
|
|
pCurrentFont->Release();
|
|
pCurrentFont = NULL;
|
|
}
|
|
pRange->Release();
|
|
}
|
|
}
|
|
|
|
// if any current selection, turn on Undo to delete it....
|
|
_pTextMsgFilter->_pTextDoc->Undo(tomResume, NULL);
|
|
pTextRange->SetText(NULL);
|
|
_pTextMsgFilter->_pTextDoc->Undo(tomSuspend, NULL);
|
|
fDelSelection = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ITextSelection *pTextSel = NULL;
|
|
hResult = _pTextMsgFilter->_pTextDoc->GetSelectionEx(&pTextSel);
|
|
if (pTextSel)
|
|
{
|
|
long cpMin = 0;
|
|
hResult = pTextSel->GetStart(&cpMin);
|
|
|
|
if (hResult == S_OK && cpMin == acpStart)
|
|
hResult = pTextSel->GetFont(&pCurrentFont);
|
|
|
|
if (!pCurrentFont)
|
|
hResult = pTextRange->GetFont(&pCurrentFont);
|
|
|
|
if (pCurrentFont)
|
|
{
|
|
hResult = pCurrentFont->GetDuplicate(&_pTextFont);
|
|
pCurrentFont->Release();
|
|
pCurrentFont = NULL;
|
|
}
|
|
|
|
pTextSel->Release();
|
|
}
|
|
}
|
|
}
|
|
Assert (_pTextFont);
|
|
if (_pTextFont)
|
|
{
|
|
long cpMin;
|
|
pTextRange->GetStart(&cpMin);
|
|
_pTextMsgFilter->_uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
|
|
CIme::CheckKeyboardFontMatching(cpMin, _pTextMsgFilter, _pTextFont);
|
|
}
|
|
}
|
|
|
|
if (fInsertObject)
|
|
{
|
|
LRESULT lresult;
|
|
CHARRANGE charRange = {acpStart, acpEnd};
|
|
|
|
if (fDelSelection)
|
|
charRange.cpMost = acpStart;
|
|
|
|
hResult = _pTextMsgFilter->_pTextService->TxSendMessage(EM_INSERTOBJ, (WPARAM)&charRange,
|
|
(LPARAM)pDataObject, &lresult);
|
|
|
|
if (hResult == NOERROR && pChange)
|
|
{
|
|
pChange->acpStart = acpStart;
|
|
pChange->acpOldEnd = acpEnd;
|
|
pChange->acpNewEnd = acpStart+1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
long lCount;
|
|
long cpMin, cpMax;
|
|
_pTextMsgFilter->_pTextDoc->Freeze(&lCount); // Turn off display
|
|
hResult = pTextRange->SetText(bstr);
|
|
_pTextMsgFilter->_pTextDoc->Unfreeze(&lCount); // Turn on display
|
|
|
|
pTextRange->GetStart(&cpMin);
|
|
pTextRange->GetEnd(&cpMax);
|
|
|
|
if (_pTextFont)
|
|
pTextRange->SetFont(_pTextFont);
|
|
|
|
POINT ptBottomPos;
|
|
|
|
if (_pTextMsgFilter->_uKeyBoardCodePage == CP_KOREAN)
|
|
{
|
|
if (pTextRange->GetPoint( tomEnd+TA_BOTTOM+TA_LEFT,
|
|
&(ptBottomPos.x), &(ptBottomPos.y) ) != NOERROR)
|
|
pTextRange->ScrollIntoView(tomEnd);
|
|
}
|
|
|
|
SysFreeString(bstr);
|
|
|
|
// out params
|
|
pChange->acpStart = cpMin;
|
|
pChange->acpOldEnd = acpEnd;
|
|
pChange->acpNewEnd = cpMax;
|
|
|
|
hResult = S_OK;
|
|
}
|
|
pTextRange->Release();
|
|
}
|
|
return hResult;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetFormattedText()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetFormattedText(
|
|
LONG acpStart,
|
|
LONG acpEnd,
|
|
IDataObject **ppDataObject)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetFormattedText");
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetEmbedded()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetEmbedded(
|
|
LONG acpPos,
|
|
REFGUID rguidService,
|
|
REFIID riid,
|
|
IUnknown **ppunk)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetEmbedded");
|
|
|
|
WORD wServiceRequested = 0;
|
|
|
|
if (!_fReadLockOn)
|
|
return TS_E_NOLOCK;
|
|
|
|
if (!ppunk)
|
|
return E_INVALIDARG;
|
|
|
|
if (IsEqualIID(rguidService, GUID_DCSERVICE_ACTIVEX))
|
|
wServiceRequested = 1;
|
|
else if (IsEqualIID(rguidService, GUID_DCSERVICE_DATAOBJECT))
|
|
wServiceRequested = 2;
|
|
else
|
|
return E_INVALIDARG;
|
|
|
|
ITextRange *pTextRange = NULL;
|
|
IUnknown *pIUnk = NULL;
|
|
HRESULT hResult = _pTextMsgFilter->_pTextDoc->Range(acpPos, acpPos+1, &pTextRange);
|
|
|
|
if (SUCCEEDED(hResult) && pTextRange)
|
|
{
|
|
hResult = pTextRange->GetEmbeddedObject(&pIUnk);
|
|
|
|
if (SUCCEEDED(hResult) && pIUnk)
|
|
hResult = pIUnk->QueryInterface(wServiceRequested == 1 ? riid : IID_IDataObject,
|
|
(LPVOID FAR *)ppunk);
|
|
else
|
|
hResult = E_FAIL;
|
|
|
|
if (pIUnk)
|
|
pIUnk->Release();
|
|
|
|
pTextRange->Release();
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::InsertEmbedded()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::InsertEmbedded(
|
|
DWORD dwFlags,
|
|
LONG acpStart,
|
|
LONG acpEnd,
|
|
IDataObject *pDataObject,
|
|
TS_TEXTCHANGE *pChange)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::InsertEmbedded");
|
|
|
|
if (!pDataObject)
|
|
return E_INVALIDARG;
|
|
|
|
if (_pTextMsgFilter->_fAllowEmbedded == 0)
|
|
return TS_E_FORMAT; // Client doesn't want insert embedded
|
|
|
|
return InsertData(dwFlags, acpStart, acpEnd, NULL, 1, pDataObject, pChange);
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::RequestSupportedAttrs()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::RequestSupportedAttrs(
|
|
DWORD dwFlags,
|
|
ULONG cFilterAttrs,
|
|
const TS_ATTRID *paFilterAttrs)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::RequestSupportedAttrs");
|
|
|
|
if (_fShutDown)
|
|
return E_NOTIMPL;
|
|
|
|
return GetAttrs(0, cFilterAttrs, paFilterAttrs, TRUE);
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::RequestAttrsAtPosition()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::RequestAttrsAtPosition(
|
|
LONG acpPos,
|
|
ULONG cFilterAttrs,
|
|
const TS_ATTRID *paFilterAttrs,
|
|
DWORD dwFlags)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::RequestAttrsAtPosition");
|
|
|
|
if (_fShutDown)
|
|
return E_NOTIMPL;
|
|
|
|
return GetAttrs(acpPos, cFilterAttrs, paFilterAttrs, FALSE);
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::RequestAttrsTransitioningAtPosition()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::RequestAttrsTransitioningAtPosition(
|
|
LONG acpPos,
|
|
ULONG cFilterAttrs,
|
|
const TS_ATTRID *paFilterAttrs,
|
|
DWORD dwFlags)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::RequestAttrsTransitioningAtPosition");
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::FindNextAttrTransition()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::FindNextAttrTransition(
|
|
LONG acpStart,
|
|
LONG acpHalt,
|
|
ULONG cFilterAttrs,
|
|
const TS_ATTRID *paFilterAttrs,
|
|
DWORD dwFlags,
|
|
LONG *pacpNext,
|
|
BOOL *pfFound,
|
|
LONG *plFoundOffset)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::FindNextAttrTransition");
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::RetrieveRequestedAttrs()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::RetrieveRequestedAttrs(
|
|
ULONG ulCount,
|
|
TS_ATTRVAL *paAttrVals,
|
|
ULONG *pcFetched)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::RetrieveRequestedAttrs");
|
|
|
|
if (!pcFetched)
|
|
return E_INVALIDARG;
|
|
|
|
if (_fShutDown)
|
|
return E_NOTIMPL;
|
|
|
|
*pcFetched = 0;
|
|
|
|
if (_parAttrsVal && _uAttrsValCurrent < _uAttrsValTotal)
|
|
{
|
|
ULONG cFetched = min(ulCount, _uAttrsValTotal - _uAttrsValCurrent);
|
|
|
|
if (cFetched)
|
|
{
|
|
memcpy(paAttrVals, &_parAttrsVal[_uAttrsValCurrent], cFetched * sizeof(TS_ATTRVAL));
|
|
memset(&_parAttrsVal[_uAttrsValCurrent], 0, cFetched * sizeof(TS_ATTRVAL));
|
|
_uAttrsValCurrent += cFetched;
|
|
*pcFetched = cFetched;
|
|
|
|
// If everything is fetched, clean up
|
|
if (_uAttrsValCurrent == _uAttrsValTotal)
|
|
InitAttrVarArray();
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetEndACP()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetEndACP(LONG *pacp)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetEndACP");
|
|
|
|
if (!_fReadLockOn)
|
|
return TS_E_NOLOCK;
|
|
|
|
if (!pacp)
|
|
return E_INVALIDARG;
|
|
|
|
if (_fShutDown)
|
|
return E_NOTIMPL;
|
|
|
|
return GetStoryLength(pacp);
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetActiveView()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetActiveView(TsViewCookie *pvcView)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetActiveView");
|
|
|
|
if (!pvcView)
|
|
return E_INVALIDARG;
|
|
|
|
*pvcView = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetACPFromPoint()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetACPFromPoint(
|
|
TsViewCookie vcView,
|
|
const POINT *pt,
|
|
DWORD dwFlags,
|
|
LONG *pacp)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetACPFromPoint");
|
|
|
|
if (!pt || !pacp)
|
|
return E_POINTER;
|
|
|
|
if (_fShutDown)
|
|
return E_NOTIMPL;
|
|
|
|
ITextRange *pTextRange = NULL;
|
|
|
|
HRESULT hResult = _pTextMsgFilter->_pTextDoc->RangeFromPoint(pt->x, pt->y, &pTextRange);
|
|
|
|
if (hResult == S_OK && pTextRange)
|
|
hResult = pTextRange->GetStart(pacp);
|
|
|
|
if (pTextRange)
|
|
pTextRange->Release();
|
|
|
|
return hResult;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetTextExt()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetTextExt(
|
|
TsViewCookie vcView,
|
|
LONG acpStart,
|
|
LONG acpEnd,
|
|
RECT *prc,
|
|
BOOL *pfClipped)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetTextExt");
|
|
|
|
if (!prc)
|
|
return E_POINTER;
|
|
|
|
if (_fShutDown)
|
|
return E_NOTIMPL;
|
|
|
|
if (pfClipped)
|
|
*pfClipped = TRUE;
|
|
|
|
ITextRange *pTextRange = NULL;
|
|
|
|
HRESULT hResult = _pTextMsgFilter->_pTextDoc->Range(acpStart, acpEnd, &pTextRange);
|
|
|
|
if (hResult == S_OK && pTextRange)
|
|
{
|
|
BOOL fClipped = FALSE;
|
|
|
|
POINT ptStart, ptEnd;
|
|
hResult = pTextRange->GetPoint( tomStart+TA_TOP+TA_LEFT,
|
|
&(ptStart.x), &(ptStart.y) );
|
|
|
|
if (hResult != S_OK)
|
|
{
|
|
hResult = pTextRange->GetPoint( tomStart+TA_TOP+TA_LEFT+tomAllowOffClient,
|
|
&(ptStart.x), &(ptStart.y) );
|
|
fClipped = TRUE;
|
|
}
|
|
|
|
if (hResult == S_OK)
|
|
{
|
|
hResult = pTextRange->GetPoint( acpStart == acpEnd ? tomStart+TA_BOTTOM+TA_LEFT :
|
|
tomEnd+TA_BOTTOM+TA_LEFT,
|
|
&(ptEnd.x), &(ptEnd.y) );
|
|
|
|
if (hResult != S_OK)
|
|
{
|
|
hResult = pTextRange->GetPoint( acpStart == acpEnd ? tomStart+TA_BOTTOM+TA_LEFT+tomAllowOffClient :
|
|
tomEnd+TA_BOTTOM+TA_LEFT+tomAllowOffClient,
|
|
&(ptEnd.x), &(ptEnd.y) );
|
|
fClipped = TRUE;
|
|
}
|
|
|
|
if (hResult == S_OK)
|
|
{
|
|
prc->left = ptStart.x;
|
|
prc->top = ptStart.y;
|
|
prc->right = ptEnd.x;
|
|
prc->bottom = ptEnd.y;
|
|
if (pfClipped)
|
|
*pfClipped = fClipped;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pTextRange)
|
|
pTextRange->Release();
|
|
|
|
return hResult;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetScreenExt()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetScreenExt(
|
|
TsViewCookie vcView,
|
|
RECT *prc)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetScreenExt");
|
|
|
|
if (!prc)
|
|
return E_POINTER;
|
|
|
|
return _pTextMsgFilter->_pTextDoc->GetClientRect(tomIncludeInset,
|
|
&(prc->left), &(prc->top), &(prc->right), &(prc->bottom));
|
|
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetWnd()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetWnd(
|
|
TsViewCookie vcView,
|
|
HWND *phwnd)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetWnd");
|
|
|
|
if (!phwnd)
|
|
return E_INVALIDARG;
|
|
|
|
if (_fShutDown)
|
|
return E_NOTIMPL;
|
|
|
|
*phwnd = _pTextMsgFilter->_hwnd;
|
|
if (!*phwnd) // Windowless mode...
|
|
{
|
|
long hWnd;
|
|
|
|
if (_pTextMsgFilter->_pTextDoc->GetWindow(&hWnd) != S_OK || !hWnd)
|
|
return E_NOTIMPL;
|
|
|
|
*phwnd = (HWND)(DWORD_PTR)hWnd;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::QueryInsertEmbedded()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::QueryInsertEmbedded(
|
|
const GUID *pguidService,
|
|
const FORMATETC *pFormatEtc,
|
|
BOOL *pfInsertable)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::QueryInsertEmbedded");
|
|
if (!pfInsertable)
|
|
return E_INVALIDARG;
|
|
|
|
// Check setting if client wants to support embedded
|
|
*pfInsertable = _pTextMsgFilter->_fAllowEmbedded ? TRUE : FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::InsertTextAtSelection()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::InsertTextAtSelection(
|
|
DWORD dwFlags,
|
|
const WCHAR *pchText,
|
|
ULONG cch,
|
|
LONG *pacpStart,
|
|
LONG *pacpEnd,
|
|
TS_TEXTCHANGE *pChange)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::InsertTextAtSelection");
|
|
|
|
TS_SELECTION_ACP acpSelection;
|
|
ULONG cFetched;
|
|
LONG acpResultStart;
|
|
LONG acpResultEnd;
|
|
HRESULT hr;
|
|
|
|
if (_fShutDown)
|
|
return E_NOTIMPL;
|
|
|
|
if ((dwFlags & TS_IAS_QUERYONLY) || !(dwFlags & TS_IAS_NOQUERY))
|
|
{
|
|
if (!pacpStart || !pacpEnd)
|
|
return E_POINTER;
|
|
}
|
|
|
|
hr = GetSelection(TS_DEFAULT_SELECTION, 1, &acpSelection, &cFetched);
|
|
if (hr != S_OK)
|
|
return hr;
|
|
|
|
hr = QueryInsert(acpSelection.acpStart, acpSelection.acpEnd, cch,
|
|
&acpResultStart, &acpResultEnd);
|
|
|
|
if (hr != S_OK)
|
|
return hr;
|
|
|
|
if (dwFlags & TS_IAS_QUERYONLY)
|
|
{
|
|
// Query only, return data
|
|
*pacpStart = acpResultStart;
|
|
*pacpEnd = acpResultEnd;
|
|
return S_OK;
|
|
}
|
|
|
|
if (!_fUIMTyping)
|
|
{
|
|
// special case where no OnStartComposition before this call
|
|
_fInsertTextAtSel = 1;
|
|
_pTextMsgFilter->_pTextDoc->Undo(tomSuspend, NULL); // turn off undo
|
|
}
|
|
|
|
hr = SetText(0, acpResultStart, acpResultEnd, pchText, cch, pChange);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
if (!_fUIMTyping)
|
|
{
|
|
// SetText fail, reset state before exit
|
|
_fInsertTextAtSel = 0;
|
|
_pTextMsgFilter->_pTextDoc->Undo(tomResume, NULL); // turn on undo
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
if (!(dwFlags & TS_IAS_NOQUERY) && pChange)
|
|
{
|
|
*pacpStart = pChange->acpStart;
|
|
*pacpEnd = pChange->acpNewEnd;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::InsertEmbeddedAtSelection()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::InsertEmbeddedAtSelection(
|
|
DWORD dwFlags,
|
|
IDataObject *pDataObject,
|
|
LONG *pacpStart,
|
|
LONG *pacpEnd,
|
|
TS_TEXTCHANGE *pChange)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::InsertEmbeddedAtSelection");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* void CUIM::OnPreReplaceRange()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
void CUIM::OnPreReplaceRange(
|
|
LONG cp, //@parm cp where ReplaceRange starts ("cpMin")
|
|
LONG cchDel, //@parm Count of chars after cp that are deleted
|
|
LONG cchNew, //@parm Count of chars inserted after cp
|
|
LONG cpFormatMin, //@parm cpMin for a formatting change
|
|
LONG cpFormatMax, //@parm cpMost for a formatting change
|
|
NOTIFY_DATA *pNotifyData) //@parm special data to indicate changes
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::OnPreReplaceRange");
|
|
|
|
return;
|
|
};
|
|
|
|
/*
|
|
* void CUIM::OnPostReplaceRange()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
void CUIM::OnPostReplaceRange(
|
|
LONG cp, //@parm cp where ReplaceRange starts ("cpMin")
|
|
LONG cchDel, //@parm Count of chars after cp that are deleted
|
|
LONG cchNew, //@parm Count of chars inserted after cp
|
|
LONG cpFormatMin, //@parm cpMin for a formatting change
|
|
LONG cpFormatMax, //@parm cpMost for a formatting change
|
|
NOTIFY_DATA *pNotifyData) //@parm special data to indicate changes
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::OnPostReplaceRange");
|
|
|
|
if (_fShutDown)
|
|
return;
|
|
|
|
if (cp != CONVERT_TO_PLAIN && cp != CP_INFINITE && _ptss && !_fWriteLockOn)
|
|
{
|
|
// Forward change notification to UIM
|
|
TS_TEXTCHANGE tsTxtChange;
|
|
tsTxtChange.acpStart = cp;
|
|
|
|
if (cchDel == cchNew)
|
|
{
|
|
// text modified
|
|
tsTxtChange.acpNewEnd =
|
|
tsTxtChange.acpOldEnd = cp + cchDel;
|
|
_ptss->OnTextChange(0, &tsTxtChange);
|
|
}
|
|
else
|
|
{
|
|
if (cchDel)
|
|
{
|
|
// text deleted
|
|
tsTxtChange.acpNewEnd = cp;
|
|
tsTxtChange.acpOldEnd = cp + cchDel;
|
|
_ptss->OnTextChange(0, &tsTxtChange);
|
|
}
|
|
|
|
if (cchNew)
|
|
{
|
|
// text added
|
|
tsTxtChange.acpOldEnd = cp;
|
|
tsTxtChange.acpNewEnd = cp + cchNew;
|
|
_ptss->OnTextChange(0, &tsTxtChange);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
};
|
|
|
|
/*
|
|
* void CUIM::Zombie()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
void CUIM::Zombie()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::Zombie");
|
|
|
|
return;
|
|
};
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::OnStartComposition()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDAPI CUIM::OnStartComposition(
|
|
ITfCompositionView *pComposition,
|
|
BOOL *pfOk)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::OnStartComposition");
|
|
if (_fUIMTyping)
|
|
*pfOk = FALSE;
|
|
else
|
|
{
|
|
BOOL fInsertTextCalled = _fInsertTextAtSel;
|
|
BOOL fRetainFont = _fEndTyping;
|
|
|
|
*pfOk = TRUE;
|
|
_fUIMTyping = 1;
|
|
_fAnyWriteOperation = _fAnyWriteOperation && (_fEndTyping || fInsertTextCalled);
|
|
_fEndTyping = 0;
|
|
_fInsertTextAtSel = 0;
|
|
if (!fInsertTextCalled)
|
|
_pTextMsgFilter->_pTextDoc->Undo(tomSuspend, NULL); // turn off undo
|
|
_cchComposition = 0;
|
|
_acpFocusRange = tomForward;
|
|
_cchFocusRange = 0;
|
|
|
|
CleanUpComposition();
|
|
|
|
if (!fInsertTextCalled && pComposition)
|
|
{
|
|
HRESULT hr;
|
|
ITfRange *pRangeNew = NULL;
|
|
|
|
hr = pComposition->GetRange(&pRangeNew);
|
|
|
|
if (pRangeNew)
|
|
{
|
|
LONG acpStart;
|
|
LONG cchStart;
|
|
|
|
GetExtentAcpPrange(pRangeNew, acpStart, cchStart);
|
|
pRangeNew->Release();
|
|
|
|
if (cchStart > 0)
|
|
{
|
|
// Save the original text
|
|
ITextRange *pTextRange = NULL;
|
|
ITextFont *pCurrentFont = NULL;
|
|
HRESULT hResult = _pTextMsgFilter->_pTextDoc->Range(acpStart, acpStart+cchStart, &pTextRange);
|
|
|
|
if (!fRetainFont && _pTextFont)
|
|
{
|
|
_pTextFont->Release();
|
|
_pTextFont = NULL;
|
|
}
|
|
|
|
if (pTextRange)
|
|
{
|
|
if (fRetainFont && _acpPreFocusRangeLast <= acpStart
|
|
&& (acpStart + cchStart) <= (_acpPreFocusRangeLast + _cchFocusRangeLast))
|
|
{
|
|
// Cont'u from previous composition
|
|
_acpFocusRange = _acpPreFocusRangeLast;
|
|
_cchFocusRange = _cchFocusRangeLast;
|
|
}
|
|
else
|
|
{
|
|
hResult = pTextRange->GetText(&_bstrComposition);
|
|
Assert(!_pObjects);
|
|
_cObjects = BuildObject(pTextRange, _bstrComposition, &_pObjects, 0);
|
|
_acpBstrStart = acpStart;
|
|
_cchComposition = cchStart;
|
|
GetEndACP(&_cpEnd);
|
|
}
|
|
|
|
if (!_pTextFont)
|
|
{
|
|
hResult = pTextRange->Collapse(tomTrue);
|
|
hResult = pTextRange->Move(1, tomCharacter, NULL);
|
|
hResult = pTextRange->GetFont(&pCurrentFont);
|
|
if (pCurrentFont)
|
|
{
|
|
hResult = pCurrentFont->GetDuplicate(&_pTextFont);
|
|
pCurrentFont->Release();
|
|
|
|
if (_pTextFont)
|
|
{
|
|
long cpMin;
|
|
pTextRange->GetStart(&cpMin);
|
|
_pTextMsgFilter->_uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
|
|
CIme::CheckKeyboardFontMatching(cpMin, _pTextMsgFilter, _pTextFont);
|
|
}
|
|
}
|
|
}
|
|
pTextRange->Release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::OnUpdateComposition()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDAPI CUIM::OnUpdateComposition(
|
|
ITfCompositionView *pComposition,
|
|
ITfRange *pRangeNew)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::OnUpdateComposition");
|
|
|
|
LONG acpStart;
|
|
LONG cchStart;
|
|
|
|
if (pRangeNew)
|
|
{
|
|
GetExtentAcpPrange(pRangeNew, acpStart, cchStart);
|
|
|
|
if (_bstrComposition)
|
|
{
|
|
long cpEnd;
|
|
|
|
GetEndACP(&cpEnd);
|
|
|
|
long cpCurrentCompEnd = _acpBstrStart + _cchComposition + cpEnd - _cpEnd;
|
|
long cchExtendAfter = acpStart + cchStart - cpCurrentCompEnd;
|
|
|
|
if (_acpBstrStart > acpStart)
|
|
{
|
|
LONG cchExtendBefore = _acpBstrStart - acpStart;
|
|
ITextRange *pTextRange = NULL;
|
|
HRESULT hResult = _pTextMsgFilter->_pTextDoc->Range(acpStart,
|
|
acpStart+cchExtendBefore, &pTextRange);
|
|
|
|
if (pTextRange)
|
|
{
|
|
BSTR bstrExtendBefore = NULL;
|
|
hResult = pTextRange->GetText(&bstrExtendBefore);
|
|
|
|
if (bstrExtendBefore)
|
|
{
|
|
BSTR bstrNew = SysAllocStringLen(NULL, _cchComposition+cchExtendBefore+1);
|
|
|
|
if (bstrNew)
|
|
{
|
|
WCHAR *pNewText = (WCHAR *)bstrNew;
|
|
WCHAR *pText = (WCHAR *)bstrExtendBefore;
|
|
|
|
memcpy(pNewText, pText, cchExtendBefore * sizeof(WCHAR));
|
|
|
|
pNewText += cchExtendBefore;
|
|
pText = (WCHAR *)_bstrComposition;
|
|
memcpy(pNewText, pText, _cchComposition * sizeof(WCHAR));
|
|
*(pNewText+_cchComposition) = L'\0';
|
|
|
|
SysFreeString(_bstrComposition);
|
|
_bstrComposition = bstrNew;
|
|
_cchComposition += cchExtendBefore;
|
|
_acpBstrStart = acpStart;
|
|
}
|
|
SysFreeString(bstrExtendBefore);
|
|
}
|
|
pTextRange->Release();
|
|
}
|
|
}
|
|
|
|
if (cchExtendAfter > 0)
|
|
{
|
|
// Extend beyond current composition, append new text to the original text
|
|
ITextRange *pTextRange = NULL;
|
|
HRESULT hResult = _pTextMsgFilter->_pTextDoc->Range(cpCurrentCompEnd,
|
|
cpCurrentCompEnd+cchExtendAfter, &pTextRange);
|
|
|
|
if (pTextRange)
|
|
{
|
|
BSTR bstrExtend = NULL;
|
|
|
|
hResult = pTextRange->GetText(&bstrExtend);
|
|
|
|
if (bstrExtend)
|
|
{
|
|
BSTR bstrNew = SysAllocStringLen(NULL, _cchComposition+cchExtendAfter+1);
|
|
|
|
if (bstrNew)
|
|
{
|
|
WCHAR *pNewText = (WCHAR *)bstrNew;
|
|
WCHAR *pText = (WCHAR *)_bstrComposition;
|
|
|
|
memcpy(pNewText, pText, _cchComposition * sizeof(WCHAR));
|
|
|
|
pNewText += _cchComposition;
|
|
pText = (WCHAR *)bstrExtend;
|
|
memcpy(pNewText, pText, cchExtendAfter * sizeof(WCHAR));
|
|
*(pNewText+cchExtendAfter) = L'\0';
|
|
|
|
SysFreeString(_bstrComposition);
|
|
_bstrComposition = bstrNew;
|
|
_cchComposition += cchExtendAfter;
|
|
}
|
|
SysFreeString(bstrExtend);
|
|
}
|
|
pTextRange->Release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pComposition)
|
|
{
|
|
HRESULT hr;
|
|
ITfRange *pRangeComp = NULL;
|
|
|
|
hr = pComposition->GetRange(&pRangeComp);
|
|
|
|
if (pRangeComp)
|
|
{
|
|
GetExtentAcpPrange(pRangeComp, _acpFocusRange, _cchFocusRange);
|
|
pRangeComp->Release();
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::OnEndComposition()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDAPI CUIM::OnEndComposition(
|
|
ITfCompositionView *pComposition)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::OnEndComposition");
|
|
_fUIMTyping = 0;
|
|
_fEndTyping = 1;
|
|
_acpPreFocusRangeLast = _acpFocusRange;
|
|
_cchFocusRangeLast = _cchFocusRange;
|
|
_acpFocusRange = tomForward;
|
|
_cchFocusRange = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::AdviseMouseSink()
|
|
*
|
|
* @mfunc
|
|
* Setup Mouse Sink to handle mouse operation
|
|
*
|
|
* @rdesc
|
|
* S_OK is mouse trap is added to link list
|
|
* CONNECT_E_NOCONNECTION is not added.
|
|
*/
|
|
STDAPI CUIM::AdviseMouseSink(
|
|
ITfRangeACP *pRangeACP,
|
|
ITfMouseSink *pSinkInput,
|
|
DWORD *pdwCookie)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::AdviseMouseSink");
|
|
|
|
if (_fShutDown)
|
|
return CONNECT_E_NOCONNECTION;
|
|
|
|
if (!pRangeACP || !pSinkInput || !pdwCookie)
|
|
return E_POINTER;
|
|
|
|
CTFMOUSETRAP *pSinkNew = NULL;
|
|
LONG cpMouseStart, cchMosueComp;
|
|
ITfMouseSink *pSinkMouseInput = NULL;
|
|
|
|
if (FAILED(pSinkInput->QueryInterface(IID_ITfMouseSink, (void **)&pSinkMouseInput)))
|
|
return E_FAIL;
|
|
|
|
if (GetExtentAcpPrange(pRangeACP, cpMouseStart, cchMosueComp))
|
|
{
|
|
if (!_pSinkList) // No first link
|
|
{
|
|
_pSinkList = new CTFMOUSETRAP;
|
|
pSinkNew = _pSinkList;
|
|
}
|
|
else
|
|
{
|
|
if (!(_pSinkList->pMouseSink)) // The first link is empty
|
|
pSinkNew = _pSinkList;
|
|
else
|
|
{
|
|
pSinkNew = new CTFMOUSETRAP;
|
|
|
|
if (pSinkNew) // Add new trap to the bottom of list
|
|
{
|
|
CTFMOUSETRAP *pSink = _pSinkList;
|
|
|
|
while (pSink->pNext) // Find the bottom of list
|
|
pSink = pSink->pNext;
|
|
|
|
pSink->pNext = pSinkNew;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pSinkNew)
|
|
{
|
|
pSinkNew->dwCookie = *pdwCookie = (DWORD)(DWORD_PTR)pSinkMouseInput;
|
|
pSinkNew->cpMouseStart = cpMouseStart;
|
|
pSinkNew->cchMosueComp = cchMosueComp;
|
|
pSinkNew->pMouseSink = pSinkMouseInput;
|
|
|
|
_fMosueSink = 1;
|
|
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
if (pSinkMouseInput)
|
|
pSinkMouseInput->Release();
|
|
|
|
return CONNECT_E_NOCONNECTION;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::UnadviseMouseSink()
|
|
*
|
|
* @mfunc
|
|
* Remove Mouse Sink
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDAPI CUIM::UnadviseMouseSink(
|
|
DWORD dwCookie)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::UnadviseMouseSink");
|
|
|
|
if (_fShutDown)
|
|
return CONNECT_E_NOCONNECTION;
|
|
|
|
if (_fMosueSink == 0)
|
|
return CONNECT_E_NOCONNECTION;
|
|
|
|
Assert(_pSinkList);
|
|
|
|
CTFMOUSETRAP *pSink = _pSinkList;
|
|
CTFMOUSETRAP *pSinkParent = NULL;
|
|
|
|
while (pSink->dwCookie != dwCookie) // Find the cookie
|
|
{
|
|
pSinkParent = pSink;
|
|
pSink = pSink->pNext;
|
|
|
|
if (!pSink) // Reach list bottom?
|
|
return CONNECT_E_NOCONNECTION; // cookie not found
|
|
}
|
|
|
|
Assert(pSink->pMouseSink);
|
|
if (pSink->pMouseSink)
|
|
pSink->pMouseSink->Release();
|
|
|
|
if (pSink == _pSinkList) // Match the first link?
|
|
{
|
|
if (pSink->pNext)
|
|
_pSinkList = pSink->pNext;
|
|
else
|
|
{
|
|
_fMosueSink = 0; // No more mouse trap left
|
|
memset(_pSinkList, 0, sizeof(CTFMOUSETRAP));
|
|
}
|
|
}
|
|
else
|
|
{ // Match link other than the first link
|
|
Assert(pSinkParent);
|
|
pSinkParent->pNext = pSink->pNext;
|
|
delete pSink;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::Init()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::Init()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::Init");
|
|
|
|
HRESULT hResult;
|
|
|
|
// Init some CUIM data
|
|
_cCallMgrLevels = 1;
|
|
_fAllowUIMLock = 1;
|
|
|
|
hResult = _pTextMsgFilter->_pTim->CreateDocumentMgr(&_pdim);
|
|
|
|
if (FAILED(hResult))
|
|
goto ExitError;
|
|
|
|
hResult = _pdim->CreateContext(_pTextMsgFilter->_tid, 0, (ITextStoreACP *)this, &_pic, &_editCookie);
|
|
if (FAILED(hResult))
|
|
goto ExitError;
|
|
|
|
hResult = _pdim->Push(_pic);
|
|
if (FAILED(hResult))
|
|
goto ExitError;
|
|
|
|
// Get the interface for rendering markup
|
|
if (_pic->QueryInterface(IID_ITfContextRenderingMarkup, (void **)&_pContextRenderingMarkup) != S_OK)
|
|
_pContextRenderingMarkup = NULL;
|
|
|
|
_pDAM = NULL;
|
|
_pCategoryMgr = NULL;
|
|
|
|
hResult = CoCreateInstance(CLSID_TF_DisplayAttributeMgr, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_ITfDisplayAttributeMgr, (void**)&(_pDAM));
|
|
|
|
hResult = CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_ITfCategoryMgr, (void**)&_pCategoryMgr);
|
|
|
|
_pTextEditSink = new CTextEditSink(EndEditCallback, this);
|
|
|
|
if (_pTextEditSink)
|
|
{
|
|
if (FAILED(_pTextEditSink->_Advise(_pic)))
|
|
{
|
|
delete _pTextEditSink;
|
|
_pTextEditSink = NULL;
|
|
}
|
|
}
|
|
|
|
LRESULT lresult;
|
|
_pTextMsgFilter->_pTextService->TxSendMessage(EM_SETUPNOTIFY, 1, (LPARAM)(ITxNotify *)this, &lresult);
|
|
|
|
_fAllowUIMLock = 0;
|
|
|
|
return S_OK;
|
|
|
|
ExitError:
|
|
Uninit();
|
|
return hResult;
|
|
}
|
|
|
|
/*
|
|
* void CUIM::Uninit()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
void CUIM::Uninit()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::Uninit");
|
|
|
|
if (_pTextFont)
|
|
{
|
|
_pTextFont->Release();
|
|
_pTextFont = NULL;
|
|
}
|
|
|
|
if (_parITfEnumRange)
|
|
{
|
|
int idx = _parITfEnumRange->Count();
|
|
|
|
for ( ; idx > 0; idx--)
|
|
{
|
|
IEnumTfRanges **ppEnumRange = (IEnumTfRanges **)(_parITfEnumRange->Elem(idx-1));
|
|
if (ppEnumRange && *ppEnumRange)
|
|
(*ppEnumRange)->Release();
|
|
}
|
|
|
|
_parITfEnumRange->Clear(AF_DELETEMEM);
|
|
delete _parITfEnumRange;
|
|
_parITfEnumRange = NULL;
|
|
}
|
|
|
|
if (_parAttrsVal)
|
|
{
|
|
InitAttrVarArray(FALSE);
|
|
FreePv (_parAttrsVal);
|
|
_parAttrsVal = NULL;
|
|
}
|
|
|
|
if (_pSinkList)
|
|
{
|
|
CTFMOUSETRAP *pSink = _pSinkList;
|
|
|
|
_pSinkList = NULL;
|
|
|
|
// Delete the Mouse sinks list
|
|
while (1)
|
|
{
|
|
CTFMOUSETRAP *pNext = pSink->pNext;
|
|
|
|
if(pSink->pMouseSink)
|
|
pSink->pMouseSink->Release();
|
|
|
|
delete pSink;
|
|
|
|
if (!pNext) // Any more?
|
|
break; // Done.
|
|
|
|
pSink = pNext;
|
|
}
|
|
}
|
|
|
|
if (_pContextRenderingMarkup)
|
|
{
|
|
_pContextRenderingMarkup->Release();
|
|
_pContextRenderingMarkup = NULL;
|
|
}
|
|
|
|
if (_pDAM)
|
|
{
|
|
_pDAM->Release();
|
|
_pDAM = NULL;
|
|
}
|
|
|
|
if (_pCategoryMgr)
|
|
{
|
|
_pCategoryMgr->Release();
|
|
_pCategoryMgr = NULL;
|
|
}
|
|
|
|
if (_pTextEditSink)
|
|
{
|
|
_pTextEditSink->_Unadvise();
|
|
delete _pTextEditSink;
|
|
_pTextEditSink = NULL;
|
|
}
|
|
|
|
if (_pdim && _pic)
|
|
_pdim->Pop(TF_POPF_ALL);
|
|
|
|
if (_pic)
|
|
{
|
|
_pic->Release();
|
|
_pic = NULL;
|
|
}
|
|
|
|
if (_pdim)
|
|
{
|
|
_pdim->Release();
|
|
_pdim = NULL;
|
|
}
|
|
|
|
if (_pacrUl)
|
|
{
|
|
_pacrUl->Clear(AF_DELETEMEM);
|
|
delete _pacrUl;
|
|
_pacrUl = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* void CreateUIM()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
BOOL CreateUIM(CTextMsgFilter *pTextMsgFilter)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CreateUIM");
|
|
|
|
BOOL fCreateUIM = FALSE;
|
|
|
|
HRESULT hResult = CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_ITfThreadMgr, (void**)&(pTextMsgFilter->_pTim));
|
|
|
|
|
|
if (hResult == S_OK)
|
|
{
|
|
// ready to start interacting
|
|
if (pTextMsgFilter->_pTim->Activate(&(pTextMsgFilter->_tid)) == S_OK)
|
|
{
|
|
pTextMsgFilter->_pCUIM = new CUIM(pTextMsgFilter);
|
|
|
|
if (pTextMsgFilter->_pCUIM)
|
|
{
|
|
hResult = pTextMsgFilter->_pCUIM->Init();
|
|
if (hResult == S_OK)
|
|
fCreateUIM = TRUE;
|
|
else
|
|
{
|
|
delete pTextMsgFilter->_pCUIM;
|
|
pTextMsgFilter->_pCUIM = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!fCreateUIM)
|
|
{
|
|
pTextMsgFilter->_pTim->Release();
|
|
pTextMsgFilter->_pTim = NULL;
|
|
}
|
|
else if (GetFocus() == pTextMsgFilter->_hwnd)
|
|
pTextMsgFilter->_pCUIM->OnSetFocus();
|
|
}
|
|
return fCreateUIM;
|
|
}
|
|
|
|
/*
|
|
* BOOL CUIM::GetExtentAcpPrange()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
BOOL CUIM::GetExtentAcpPrange(
|
|
ITfRange *ITfRangeIn,
|
|
long &cpFirst,
|
|
long &cpLim)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetExtentAcpPrange");
|
|
|
|
ITfRangeACP *prangeACP = NULL;
|
|
if (SUCCEEDED(ITfRangeIn->QueryInterface(IID_ITfRangeACP, (void **)&prangeACP)))
|
|
{
|
|
prangeACP->GetExtent(&cpFirst, &cpLim);
|
|
|
|
prangeACP->Release();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CUIM::EndEditCallback()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CUIM::EndEditCallback(ITfEditRecord *pEditRecord, void *pv)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::EndEditCallback");
|
|
|
|
HRESULT hr;
|
|
CUIM *_this = (CUIM *)pv;
|
|
IEnumTfRanges *pEnumRanges;
|
|
const GUID *rgGUID[1];
|
|
|
|
if (!(_this->_fWriteLockOn))
|
|
{
|
|
_this->HandleTempDispAttr(pEditRecord);
|
|
return S_OK;
|
|
}
|
|
|
|
// Get lid changes
|
|
rgGUID[0] = &GUID_PROP_LANGID;
|
|
hr = pEditRecord->GetTextAndPropertyUpdates(0, (const GUID**)rgGUID, 1, &pEnumRanges);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_this->HandleLangID (pEnumRanges);
|
|
pEnumRanges->Release();
|
|
}
|
|
|
|
// Get attribute changes
|
|
rgGUID[0] = &GUID_PROP_ATTRIBUTE;
|
|
hr = pEditRecord->GetTextAndPropertyUpdates(0, (const GUID**)rgGUID, 1, &pEnumRanges);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_this->HandlePropAttrib (pEnumRanges);
|
|
pEnumRanges->Release();
|
|
}
|
|
|
|
rgGUID[0] = &GUID_PROP_COMPOSING;
|
|
hr = pEditRecord->GetTextAndPropertyUpdates(0, (const GUID**)rgGUID, 1, &pEnumRanges);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Save the TextDelta to be process after the lock is off
|
|
if (!(_this->_parITfEnumRange))
|
|
_this->_parITfEnumRange = new CITfEnumRange();
|
|
|
|
if (_this->_parITfEnumRange)
|
|
{
|
|
LONG idxItem;
|
|
IEnumTfRanges **ppItem;
|
|
ppItem = _this->_parITfEnumRange->Add(1, &idxItem);
|
|
if (ppItem)
|
|
*ppItem = pEnumRanges;
|
|
else
|
|
pEnumRanges->Release(); // Add fail, forget it
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* void CUIM::HandleDispAttr(*pITfRangeProp, var, cp, cch)
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
void CUIM::HandleDispAttr(
|
|
ITfRange *pITfRangeProp,
|
|
VARIANT &var,
|
|
long acpStartRange,
|
|
long cch)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::HandleDispAttr");
|
|
|
|
HRESULT hResult = TRUE;
|
|
|
|
if (pITfRangeProp)
|
|
hResult = GetExtentAcpPrange(pITfRangeProp, acpStartRange, cch);
|
|
|
|
if (hResult && cch > 0)
|
|
{
|
|
ITextRange *pTextRange = NULL;
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(acpStartRange, acpStartRange+cch, &pTextRange);
|
|
if (pTextRange)
|
|
{
|
|
ITextFont *pFont = NULL;
|
|
|
|
if (_pTextFont)
|
|
_pTextFont->GetDuplicate(&pFont);
|
|
|
|
if (pFont)
|
|
{
|
|
if (var.vt == VT_I4)
|
|
{
|
|
GUID guid;
|
|
ITfDisplayAttributeInfo *pDAI = NULL;
|
|
TF_DISPLAYATTRIBUTE da;
|
|
|
|
if (_pCategoryMgr->GetGUID(var.ulVal, &guid) == S_OK &&
|
|
SUCCEEDED(_pDAM->GetDisplayAttributeInfo(guid, &pDAI, NULL)))
|
|
{
|
|
COLORREF cr;
|
|
long lUnderline;
|
|
long idx = 0;
|
|
|
|
Assert(pDAI);
|
|
pDAI->GetAttributeInfo(&da);
|
|
|
|
if (GetUIMAttributeColor(&da.crText, &cr))
|
|
pFont->SetForeColor(cr);
|
|
|
|
if (GetUIMAttributeColor(&da.crBk, &cr))
|
|
pFont->SetBackColor(cr);
|
|
|
|
lUnderline = GetUIMUnderline(da, idx, cr);
|
|
if (lUnderline != tomNone)
|
|
{
|
|
if (idx)
|
|
{
|
|
hResult = _pTextMsgFilter->_pTextDoc->SetEffectColor(idx, cr);
|
|
if (hResult == S_OK)
|
|
lUnderline += (idx << 8);
|
|
}
|
|
pFont->SetUnderline(lUnderline);
|
|
}
|
|
}
|
|
if (pDAI)
|
|
pDAI->Release();
|
|
}
|
|
pTextRange->SetFont(pFont);
|
|
pFont->Release();
|
|
}
|
|
pTextRange->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* HRESULT CUIM::HandlePropAttrib(ITfEnumTextDeltas *pEnumTextDeltas)
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CUIM::HandlePropAttrib(IEnumTfRanges *pEnumRanges)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::HandlePropAttrib");
|
|
|
|
ITfRange *pITfRange = NULL;
|
|
|
|
if (!_pDAM || !_pCategoryMgr || _fInterimChar)
|
|
return S_OK;
|
|
|
|
ITfProperty *pProp = NULL;
|
|
HRESULT hResult = _pic->GetProperty(GUID_PROP_ATTRIBUTE, &pProp);
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
long lCount;
|
|
TS_SELECTION_ACP acpSelection;
|
|
ULONG cFetched;
|
|
|
|
GetSelection(0, 0, &acpSelection, &cFetched);
|
|
|
|
_pTextMsgFilter->_pTextDoc->Freeze(&lCount); // Turn off display
|
|
while (pEnumRanges->Next(1, &pITfRange, NULL) == S_OK)
|
|
{
|
|
BOOL fAnyPropRange = FALSE;
|
|
IEnumTfRanges *pEnumPropRange = NULL;
|
|
long acpRangeStart, ccpRangeStart;
|
|
VARIANT var;
|
|
|
|
GetExtentAcpPrange(pITfRange, acpRangeStart, ccpRangeStart);
|
|
|
|
// Create a property Enum for ranges within pITfRange
|
|
if (pProp->EnumRanges(_editCookie, &pEnumPropRange, pITfRange) == S_OK)
|
|
{
|
|
ITfRange *pITfRangeProp = NULL;
|
|
|
|
while (pEnumPropRange->Next(1, &pITfRangeProp, NULL) == S_OK)
|
|
{
|
|
VariantInit(&var);
|
|
if (!fAnyPropRange)
|
|
{
|
|
long acpCurrentRange, ccpCurrent;
|
|
|
|
if (GetExtentAcpPrange(pITfRangeProp, acpCurrentRange, ccpCurrent))
|
|
{
|
|
if (acpCurrentRange > acpRangeStart)
|
|
HandleDispAttr(NULL, var, acpRangeStart, acpCurrentRange - acpRangeStart);
|
|
}
|
|
fAnyPropRange = TRUE;
|
|
}
|
|
|
|
pProp->GetValue(_editCookie, pITfRangeProp, &var);
|
|
HandleDispAttr(pITfRangeProp, var);
|
|
|
|
VariantClear(&var);
|
|
pITfRangeProp->Release();
|
|
}
|
|
pEnumPropRange->Release();
|
|
}
|
|
|
|
if (!fAnyPropRange)
|
|
{
|
|
// Whole string doesn't contain any disp. attribute.
|
|
VariantInit(&var);
|
|
HandleDispAttr(pITfRange, var);
|
|
}
|
|
|
|
pITfRange->Release();
|
|
}
|
|
pProp->Release();
|
|
|
|
// Only want to scroll back if its not a selection
|
|
if (acpSelection.acpStart == acpSelection.acpEnd)
|
|
{
|
|
ITextRange *pTextRange;
|
|
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(acpSelection.acpStart, acpSelection.acpEnd, &pTextRange);
|
|
if (pTextRange)
|
|
{
|
|
pTextRange->Select();
|
|
pTextRange->Release();
|
|
}
|
|
}
|
|
_pTextMsgFilter->_pTextDoc->Unfreeze(&lCount); // Turn on display
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* void CUIM::GetUIMUnderline()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
long CUIM::GetUIMUnderline(
|
|
TF_DISPLAYATTRIBUTE &da,
|
|
long &idx,
|
|
COLORREF &cr)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetUIMUnderline");
|
|
|
|
long lStyle = tomNone;
|
|
|
|
idx = 0;
|
|
|
|
if (da.lsStyle != TF_LS_NONE)
|
|
{
|
|
switch(da.lsStyle)
|
|
{
|
|
// case TFLS_SOLID:
|
|
default:
|
|
lStyle = da.fBoldLine ? tomThick : tomSingle;
|
|
break;
|
|
|
|
case TF_LS_DOT:
|
|
case TF_LS_DASH: // Dash line should show as dotted line
|
|
lStyle = tomDotted;
|
|
break;
|
|
|
|
case TF_LS_SQUIGGLE:
|
|
lStyle = tomWave;
|
|
break;
|
|
}
|
|
|
|
if (GetUIMAttributeColor(&da.crLine, &cr))
|
|
{
|
|
if (!_pacrUl) // Create the array if it is not there
|
|
_pacrUl = new CUlColorArray();
|
|
|
|
if (_pacrUl)
|
|
{
|
|
LONG idxMax = _pacrUl->Count();
|
|
LONG idxItem;
|
|
COLORREF *pCr;
|
|
|
|
// Check if this item is in the array
|
|
for (idxItem=0; idxItem < idxMax; idxItem++)
|
|
{
|
|
pCr = _pacrUl->Elem(idxItem);
|
|
Assert(pCr);
|
|
if (*pCr == cr)
|
|
idx = idxItem + 1; // found it
|
|
}
|
|
|
|
if (!idx)
|
|
{
|
|
// Add it to array
|
|
pCr = _pacrUl->Add(1, &idxItem);
|
|
|
|
if (pCr)
|
|
{
|
|
*pCr = cr;
|
|
idx = idxItem + 1; // return new idx
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return lStyle;
|
|
}
|
|
|
|
/*
|
|
* void CUIM::HandleFinalString(ITfRange *pPropRange, long acpStartRange, long cch)
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
void CUIM::HandleFinalString(
|
|
ITfRange *pPropRange,
|
|
long acpStartRange,
|
|
long cch,
|
|
BOOL fEndComposition)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::HandleFinalString");
|
|
|
|
HRESULT hResult = TRUE;
|
|
|
|
if (pPropRange)
|
|
hResult = GetExtentAcpPrange(pPropRange, acpStartRange, cch);
|
|
|
|
if (hResult == TRUE && cch)
|
|
{
|
|
if (_bstrComposition && !fEndComposition)
|
|
return;
|
|
|
|
ITextRange *pTextRange = NULL;
|
|
ITextSelection *pTextSel = NULL;
|
|
long cpSelMin = 0, cpSelMax = 0;
|
|
BOOL fTextSel = FALSE;
|
|
|
|
// Need to maintain current selection
|
|
hResult = _pTextMsgFilter->_pTextDoc->GetSelectionEx(&pTextSel);
|
|
|
|
if (pTextSel)
|
|
{
|
|
hResult = pTextSel->GetStart(&cpSelMin);
|
|
hResult = pTextSel->GetEnd(&cpSelMax);
|
|
pTextSel->Release();
|
|
fTextSel = TRUE;
|
|
}
|
|
|
|
if (_bstrComposition)
|
|
{
|
|
long cpEnd;
|
|
|
|
GetEndACP(&cpEnd);
|
|
|
|
cch = _cchComposition + cpEnd - _cpEnd;
|
|
acpStartRange = _acpBstrStart;
|
|
}
|
|
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(acpStartRange, acpStartRange+cch, &pTextRange);
|
|
if (pTextRange)
|
|
{
|
|
long cEmbeddedObjects = 0;
|
|
BSTR bstr = NULL;
|
|
|
|
if (cch)
|
|
hResult = pTextRange->GetText(&bstr);
|
|
|
|
if (SUCCEEDED(hResult) && (bstr || cch == 0))
|
|
{
|
|
long lCount;
|
|
BSTR bstrTemp = NULL;
|
|
|
|
if (!_fAnyWriteOperation) // No new string
|
|
goto IGNORE_STRING; // no need to insert
|
|
|
|
if (_bstrComposition)
|
|
{
|
|
if (bstr)
|
|
{
|
|
WCHAR *pStr1 = _bstrComposition;
|
|
WCHAR *pStr2 = bstr;
|
|
|
|
while (*pStr1 != 0 && *pStr1 == *pStr2)
|
|
{
|
|
pStr1++;
|
|
pStr2++;
|
|
}
|
|
|
|
if (*pStr1 == *pStr2) // Same data, no need to insert
|
|
{
|
|
if (acpStartRange == cpSelMin)
|
|
{
|
|
pTextRange->Collapse(tomFalse);
|
|
pTextRange->Select();
|
|
}
|
|
goto IGNORE_STRING;
|
|
}
|
|
}
|
|
|
|
bstrTemp = _bstrComposition;
|
|
}
|
|
|
|
// Build embed object data if necessary
|
|
EMBEDOBJECT arEmbeddObjects[5];
|
|
EMBEDOBJECT *pEmbeddObjects = arEmbeddObjects;
|
|
|
|
if (bstr)
|
|
cEmbeddedObjects =
|
|
BuildObject(pTextRange, bstr, &pEmbeddObjects, ARRAY_SIZE(arEmbeddObjects));
|
|
|
|
_pTextMsgFilter->_pTextDoc->Freeze(&lCount); // Turn off display
|
|
|
|
// We want the final text to be in the undo stack.
|
|
// So, we first delete the final string.
|
|
// Resume undo and add the final string back. Yuk!
|
|
if (bstrTemp && _cObjects)
|
|
{
|
|
long cpMin;
|
|
long cchBstr = SysStringLen(bstrTemp);
|
|
|
|
pTextRange->GetStart(&cpMin);
|
|
InsertTextandObject(pTextRange, bstrTemp, _pObjects, _cObjects);
|
|
pTextRange->SetRange(cpMin, cpMin+cchBstr);
|
|
}
|
|
else
|
|
pTextRange->SetText(bstrTemp);
|
|
|
|
if (_pTextFont)
|
|
pTextRange->SetFont(_pTextFont);
|
|
|
|
_pTextMsgFilter->_pTextDoc->Undo(tomResume, NULL);
|
|
_pTextMsgFilter->_pTextDoc->SetNotificationMode(tomTrue);
|
|
|
|
if (cEmbeddedObjects == 0)
|
|
pTextRange->SetText(bstr);
|
|
else
|
|
{
|
|
InsertTextandObject(pTextRange, bstr, pEmbeddObjects, cEmbeddedObjects);
|
|
CleanUpObjects(cEmbeddedObjects, pEmbeddObjects);
|
|
if (pEmbeddObjects != arEmbeddObjects)
|
|
delete pEmbeddObjects;
|
|
}
|
|
|
|
_pTextMsgFilter->_pTextDoc->Undo(tomSuspend, NULL);
|
|
|
|
// Hold notification if needed
|
|
if (!(_pTextMsgFilter->_fIMEAlwaysNotify))
|
|
_pTextMsgFilter->_pTextDoc->SetNotificationMode(tomFalse);
|
|
|
|
if (fTextSel)
|
|
{
|
|
ITextRange *pSelRange = NULL;
|
|
// restore previous selection.
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(cpSelMin, cpSelMax, &pSelRange);
|
|
if (pSelRange)
|
|
{
|
|
pSelRange->Select();
|
|
pSelRange->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pTextRange->Collapse(tomFalse);
|
|
pTextRange->Select();
|
|
}
|
|
|
|
_pTextMsgFilter->_pTextDoc->Unfreeze(&lCount); // Turn on display
|
|
}
|
|
IGNORE_STRING:
|
|
if (bstr)
|
|
SysFreeString(bstr);
|
|
pTextRange->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* HRESULT CUIM::HandleFocusRange(IEnumTfRanges *pEnumRanges)
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CUIM::HandleFocusRange(IEnumTfRanges *pEnumRanges)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::HandleFocusRange");
|
|
|
|
ITfProperty *pProp = NULL;
|
|
ITfRange *pITfRange;
|
|
HRESULT hResult = _pic->GetProperty(GUID_PROP_COMPOSING, &pProp);
|
|
BOOL fAnyPendingFocusRange = FALSE;
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
// Enumerate all the changes
|
|
pEnumRanges->Reset();
|
|
while (pEnumRanges->Next(1, &pITfRange, NULL) == S_OK)
|
|
{
|
|
BOOL fAnyPropRange = FALSE;
|
|
IEnumTfRanges *pEnumPropRange = NULL;
|
|
|
|
long acpStartRange, ccp;
|
|
|
|
GetExtentAcpPrange(pITfRange, acpStartRange, ccp);
|
|
|
|
|
|
// Create a property Enum for ranges within pITfRange
|
|
if (pProp->EnumRanges(_editCookie, &pEnumPropRange, pITfRange) == S_OK)
|
|
{
|
|
ITfRange *pPropRange = NULL;
|
|
|
|
// Try to get a value for the property
|
|
while (pEnumPropRange->Next(1, &pPropRange, NULL) == S_OK)
|
|
{
|
|
VARIANT var;
|
|
|
|
VariantInit(&var);
|
|
|
|
if (!fAnyPropRange)
|
|
{
|
|
long acpCurrentRange, ccpCurrent;
|
|
|
|
GetExtentAcpPrange(pPropRange, acpCurrentRange, ccpCurrent);
|
|
if (acpCurrentRange > acpStartRange)
|
|
{
|
|
// We have a final string before the new string.
|
|
HandleFinalString(NULL, acpStartRange, acpCurrentRange - acpStartRange);
|
|
}
|
|
fAnyPropRange = TRUE;
|
|
}
|
|
|
|
hResult = pProp->GetValue(_editCookie, pPropRange, &var);
|
|
|
|
if (SUCCEEDED(hResult) && var.vt == VT_I4 && var.ulVal == 0)
|
|
hResult = E_FAIL; // Just as good as not finding the range
|
|
else
|
|
fAnyPendingFocusRange = TRUE;
|
|
|
|
VariantClear(&var);
|
|
|
|
if (hResult != S_OK)
|
|
HandleFinalString(pPropRange);
|
|
|
|
pPropRange->Release();
|
|
}
|
|
pEnumPropRange->Release();
|
|
}
|
|
|
|
if (!fAnyPropRange) // Any focus range?
|
|
HandleFinalString(pITfRange); // No --> the whole string is final string
|
|
|
|
if (_fEndTyping && _bstrComposition && _acpBstrStart != tomForward)
|
|
HandleFinalString(NULL, _acpBstrStart, _cchComposition, TRUE);
|
|
|
|
pITfRange->Release();
|
|
}
|
|
pProp->Release();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CUIM::HandleLangID(IEnumTfRanges *pEnumRanges)
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CUIM::HandleLangID(IEnumTfRanges *pEnumRanges)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::HandleLangID");
|
|
|
|
ITfProperty *pProp = NULL;
|
|
ITfRange *pITfRange;
|
|
HRESULT hResult;
|
|
LCID lcid;
|
|
|
|
// TODO:
|
|
// if _pTextFont is NULL, setup _pTextFont to handle the langID.
|
|
if (!_pTextFont)
|
|
return S_OK;
|
|
|
|
hResult = _pic->GetProperty(GUID_PROP_LANGID, &pProp);
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
// Enumerate all the changes
|
|
pEnumRanges->Reset();
|
|
while (pEnumRanges->Next(1, &pITfRange, NULL) == S_OK)
|
|
{
|
|
IEnumTfRanges *pEnumPropRange = NULL;
|
|
|
|
// Create a property Enum for ranges within pITfRange
|
|
if (pProp->EnumRanges(_editCookie, &pEnumPropRange, pITfRange) == S_OK)
|
|
{
|
|
ITfRange *pPropRange = NULL;
|
|
if (pEnumPropRange->Next(1, &pPropRange, NULL) == S_OK)
|
|
{
|
|
VARIANT var;
|
|
|
|
VariantInit(&var);
|
|
|
|
hResult = pProp->GetValue(_editCookie, pITfRange, &var);
|
|
|
|
if (SUCCEEDED(hResult) && var.vt == VT_I4)
|
|
{
|
|
lcid = (LCID)var.ulVal;
|
|
|
|
UINT cpgProp = CodePageFromCharRep(CharRepFromLID(lcid));
|
|
ITextFont *pTextFont=NULL;
|
|
|
|
_pTextFont->GetDuplicate(&pTextFont);
|
|
if (pTextFont)
|
|
{
|
|
HRESULT hResult;
|
|
LONG acpStart, cchStart;
|
|
ITextRange *pTextRange;
|
|
UINT cpgTemp = _pTextMsgFilter->_uKeyBoardCodePage;
|
|
|
|
GetExtentAcpPrange(pITfRange, acpStart, cchStart);
|
|
if (cchStart)
|
|
{
|
|
_pTextMsgFilter->_uKeyBoardCodePage = cpgProp;
|
|
CIme::CheckKeyboardFontMatching(acpStart, _pTextMsgFilter, pTextFont);
|
|
_pTextMsgFilter->_uKeyBoardCodePage = cpgTemp;
|
|
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(acpStart, acpStart+cchStart, &pTextRange);
|
|
if (pTextRange)
|
|
{
|
|
pTextRange->SetFont(pTextFont);
|
|
pTextRange->Release();
|
|
}
|
|
}
|
|
pTextFont->Release();
|
|
}
|
|
}
|
|
VariantClear(&var);
|
|
pPropRange->Release();
|
|
}
|
|
pEnumPropRange->Release();
|
|
}
|
|
pITfRange->Release();
|
|
}
|
|
pProp->Release();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CUIM::OnSetFocus(BOOL fEnable)
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
*/
|
|
void CUIM::OnSetFocus(BOOL fEnable)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::OnSetFocus");
|
|
|
|
_pTextMsgFilter->_pTim->SetFocus(fEnable ? _pdim : NULL);
|
|
}
|
|
|
|
/*
|
|
* HRESULT CUIM::CompleteUIMText()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
*/
|
|
void CUIM::CompleteUIMText()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::CompleteUIMText");
|
|
|
|
HRESULT hResult;
|
|
ITfContextOwnerCompositionServices *pCompositionServices;
|
|
|
|
_fAllowUIMLock = 1;
|
|
|
|
Assert(_pic);
|
|
|
|
if (_pic->QueryInterface(IID_ITfContextOwnerCompositionServices, (void **)&pCompositionServices) == S_OK)
|
|
{
|
|
// passing in NULL means "all compositions"
|
|
hResult = pCompositionServices->TerminateComposition(NULL);
|
|
pCompositionServices->Release();
|
|
}
|
|
|
|
_fAllowUIMLock = 0;
|
|
|
|
}
|
|
|
|
/*
|
|
* BOOL CUIM::GetUIMAttributeColor()
|
|
*
|
|
* @mfunc
|
|
* Helper routine to get UIM color
|
|
*
|
|
* @rdesc
|
|
* TRUE if we setup input pcr with the UIM color
|
|
*
|
|
*/
|
|
BOOL CUIM::GetUIMAttributeColor(TF_DA_COLOR *pdac, COLORREF *pcr)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetUIMAttributeColor");
|
|
|
|
BOOL fRetCode = FALSE;
|
|
switch (pdac->type)
|
|
{
|
|
//case TFCT_NONE:
|
|
// return FALSE;
|
|
|
|
case TF_CT_SYSCOLOR:
|
|
*pcr = GetSysColor(pdac->nIndex);
|
|
fRetCode = TRUE;
|
|
break;
|
|
|
|
case TF_CT_COLORREF:
|
|
*pcr = pdac->cr;
|
|
fRetCode = TRUE;
|
|
break;
|
|
}
|
|
return fRetCode;
|
|
}
|
|
|
|
/*
|
|
* void CUIM::OnUIMTypingDone()
|
|
*
|
|
* @mfunc
|
|
* Helper routine to cleanup after UIM Typing is done
|
|
*
|
|
*/
|
|
void CUIM::OnUIMTypingDone()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::OnUIMTypingDone");
|
|
|
|
if (_pTextFont)
|
|
{
|
|
_pTextFont->Release();
|
|
_pTextFont = NULL;
|
|
}
|
|
|
|
CleanUpComposition();
|
|
|
|
// Reset Korean block caret if needed
|
|
if (_fInterimChar)
|
|
{
|
|
_fInterimChar = 0;
|
|
_pTextMsgFilter->_pTextDoc->SetCaretType(tomNormalCaret); // Reset Block caret mode
|
|
}
|
|
|
|
_fAnyWriteOperation = 0;
|
|
_pTextMsgFilter->_pTextDoc->Undo(tomResume, NULL);
|
|
_pTextMsgFilter->_pTextDoc->SetNotificationMode(tomTrue);
|
|
|
|
if (_pacrUl)
|
|
_pacrUl->Clear(AF_DELETEMEM);
|
|
};
|
|
|
|
/*
|
|
* BOOL CUIM::GetGUIDATOMFromGUID()
|
|
*
|
|
* @mfunc
|
|
* Helper routine to get GUIDATOM from UIM
|
|
*
|
|
*/
|
|
BOOL CUIM::GetGUIDATOMFromGUID(
|
|
REFGUID rguid,
|
|
TfGuidAtom *pguidatom)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetGUIDATOMFromGUID");
|
|
|
|
if (_pCategoryMgr && _pCategoryMgr->RegisterGUID(rguid, pguidatom) == S_OK)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
/*
|
|
* BOOL CUIM::GetAttrs()
|
|
*
|
|
* @mfunc
|
|
* Helper routine to get Attr
|
|
*
|
|
*/
|
|
HRESULT CUIM::GetAttrs(
|
|
LONG acpPos,
|
|
ULONG cFilterAttrs,
|
|
const TS_ATTRID *paFilterAttrs,
|
|
BOOL fGetDefault)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetAttrs");
|
|
|
|
HRESULT hResult;
|
|
ITextFont *pTextFont = NULL;
|
|
ITextPara *pTextPara = NULL;
|
|
int idx;
|
|
BOOL fRequestedAll = FALSE;
|
|
int idxAttr;
|
|
TS_ATTRVAL *pAttrVal;
|
|
ITextRange *pTextRange = NULL;
|
|
|
|
if (cFilterAttrs == 0)
|
|
{
|
|
fRequestedAll = TRUE;
|
|
cFilterAttrs = MAX_ATTR_SUPPORT;
|
|
}
|
|
|
|
InitAttrVarArray();
|
|
|
|
if (!_parAttrsVal)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (fGetDefault)
|
|
{
|
|
// Get document defaults font and para
|
|
hResult = _pTextMsgFilter->_pTextDoc->GetDocumentFont(&pTextFont);
|
|
if (FAILED(hResult))
|
|
goto EXIT;
|
|
|
|
hResult = _pTextMsgFilter->_pTextDoc->GetDocumentPara(&pTextPara);
|
|
if (FAILED(hResult))
|
|
goto EXIT;
|
|
}
|
|
else
|
|
{
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(acpPos, acpPos, &pTextRange);
|
|
if (FAILED(hResult))
|
|
goto EXIT;
|
|
|
|
hResult = pTextRange->GetFont(&pTextFont);
|
|
hResult = pTextRange->GetPara(&pTextPara);
|
|
}
|
|
|
|
pAttrVal = _parAttrsVal;
|
|
for (idx = 0; idx < (int)cFilterAttrs; idx++, paFilterAttrs++)
|
|
{
|
|
if (fRequestedAll)
|
|
idxAttr = idx;
|
|
else
|
|
idxAttr = FindGUID(*paFilterAttrs);
|
|
|
|
if (idxAttr >= 0)
|
|
{
|
|
if (PackAttrData(idxAttr, pTextFont, pTextPara, pAttrVal))
|
|
{
|
|
_uAttrsValTotal++;
|
|
pAttrVal++;
|
|
|
|
if (_uAttrsValTotal == MAX_ATTR_SUPPORT)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
hResult = S_OK;
|
|
|
|
EXIT:
|
|
if (pTextFont)
|
|
pTextFont->Release();
|
|
|
|
if (pTextPara)
|
|
pTextPara->Release();
|
|
|
|
if (pTextRange)
|
|
pTextRange->Release();
|
|
|
|
return hResult;
|
|
}
|
|
/*
|
|
* int CUIM::FindGUID
|
|
*
|
|
* @mfunc
|
|
* Helper routine to check if we supported the requested Attribute GUID
|
|
*
|
|
*/
|
|
int CUIM::FindGUID(REFGUID guid)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::FindGUID");
|
|
|
|
ULONG i;
|
|
|
|
for (i=0; i < MAX_ATTR_SUPPORT; i++)
|
|
{
|
|
if (IsEqualIID(*(_arTSAttridSupported[i]), guid))
|
|
return i;
|
|
}
|
|
|
|
// not found
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* int CUIM::PackAttrData
|
|
*
|
|
* @mfunc
|
|
* Helper routine to fill in data for the given Attrib index
|
|
*
|
|
*/
|
|
BOOL CUIM::PackAttrData(
|
|
LONG idx,
|
|
ITextFont *pITextFont,
|
|
ITextPara *pITextPara,
|
|
TS_ATTRVAL *pAttrVal)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::PackAttrData");
|
|
|
|
long lValue;
|
|
float x;
|
|
BSTR bstrName;
|
|
HRESULT hResult;
|
|
const GUID *pGUID;
|
|
TfGuidAtom guidatom;
|
|
|
|
|
|
if (idx < 0 || idx >= MAX_ATTR_SUPPORT)
|
|
return FALSE;
|
|
|
|
if (!pITextFont && idx <= iattrSuperscript)
|
|
return FALSE;
|
|
|
|
if (!pITextPara && idx == iattrRTL)
|
|
return FALSE;
|
|
|
|
pAttrVal->varValue.vt = VT_BOOL;
|
|
memcpy(&pAttrVal->idAttr, _arTSAttridSupported[idx], sizeof(TS_ATTRID));
|
|
|
|
|
|
switch(idx)
|
|
{
|
|
case iattrFacename:
|
|
hResult = pITextFont->GetName(&bstrName);
|
|
pAttrVal->varValue.vt = VT_BSTR;
|
|
pAttrVal->varValue.bstrVal = bstrName;
|
|
break;
|
|
|
|
case iattrSize:
|
|
x = 0.0;
|
|
hResult = pITextFont->GetSize(&x);
|
|
lValue = (long)x;
|
|
pAttrVal->varValue.vt = VT_I4;
|
|
pAttrVal->varValue.lVal = x;
|
|
break;
|
|
|
|
case iattrColor:
|
|
hResult = pITextFont->GetForeColor(&lValue);
|
|
pAttrVal->varValue.vt = VT_I4;
|
|
pAttrVal->varValue.lVal = lValue; // TODO: check for tomAutocolor
|
|
break;
|
|
|
|
case iattrBold:
|
|
hResult = pITextFont->GetBold(&lValue);
|
|
pAttrVal->varValue.boolVal = lValue == tomTrue ? tomTrue : VARIANT_FALSE;
|
|
break;
|
|
|
|
case iattrItalic:
|
|
hResult = pITextFont->GetItalic(&lValue);
|
|
pAttrVal->varValue.boolVal = lValue == tomTrue ? tomTrue : VARIANT_FALSE;
|
|
break;
|
|
|
|
case iattrUnderline:
|
|
hResult = pITextFont->GetUnderline(&lValue);
|
|
pAttrVal->varValue.boolVal = lValue == tomNone ? VARIANT_FALSE : tomTrue;
|
|
break;
|
|
|
|
case iattrSubscript:
|
|
hResult = pITextFont->GetSubscript(&lValue);
|
|
pAttrVal->varValue.boolVal = lValue == tomTrue ? tomTrue : VARIANT_FALSE;
|
|
break;
|
|
|
|
case iattrSuperscript:
|
|
hResult = pITextFont->GetSuperscript(&lValue);
|
|
pAttrVal->varValue.boolVal = lValue == tomTrue ? tomTrue : VARIANT_FALSE;
|
|
break;
|
|
|
|
case iattrRTL:
|
|
{
|
|
LRESULT lres = 0;
|
|
_pTextMsgFilter->_pTextService->TxSendMessage(
|
|
EM_GETPARATXTFLOW, 0, (LPARAM)pITextPara, &lres);
|
|
pAttrVal->varValue.boolVal = lres ? tomTrue : VARIANT_FALSE;
|
|
}
|
|
break;
|
|
|
|
case iattrVertical:
|
|
BOOL fAtFont;
|
|
_pTextMsgFilter->_pTextDoc->GetFEFlags(&lValue);
|
|
fAtFont = lValue & tomUseAtFont;
|
|
lValue &= tomTextFlowMask;
|
|
pAttrVal->varValue.boolVal = (fAtFont && lValue == tomTextFlowSW) ? tomTrue : VARIANT_FALSE;
|
|
break;
|
|
|
|
case iattrBias:
|
|
pGUID = &GUID_MODEBIAS_NONE;
|
|
if (IN_RANGE(CTFMODEBIAS_DEFAULT, _pTextMsgFilter->_wUIMModeBias, CTFMODEBIAS_HALFWIDTHALPHANUMERIC))
|
|
pGUID = _arModeBiasSupported[_pTextMsgFilter->_wUIMModeBias];
|
|
|
|
if (!GetGUIDATOMFromGUID(*pGUID, &guidatom))
|
|
guidatom = TF_INVALID_GUIDATOM;
|
|
|
|
pAttrVal->varValue.vt = VT_I4;
|
|
pAttrVal->varValue.lVal = guidatom;
|
|
break;
|
|
|
|
case iattrTxtOrient:
|
|
// Get Text flow and setup the text rotation
|
|
_pTextMsgFilter->_pTextDoc->GetFEFlags(&lValue);
|
|
lValue &= tomTextFlowMask;
|
|
pAttrVal->varValue.vt = VT_I4;
|
|
pAttrVal->varValue.lVal = 0;
|
|
if (lValue == tomTextFlowSW)
|
|
pAttrVal->varValue.lVal = 2700;
|
|
else if (lValue == tomTextFlowNE)
|
|
pAttrVal->varValue.lVal = 900;
|
|
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CUIM::GetStoryLength
|
|
*
|
|
* @mfunc
|
|
* Helper routine to check the attribute filters
|
|
*
|
|
*/
|
|
STDMETHODIMP CUIM::GetStoryLength(LONG *pacp)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetStoryLength");
|
|
|
|
ITextRange *pTextRange = NULL;
|
|
|
|
HRESULT hResult = _pTextMsgFilter->_pTextDoc->Range(0, 0, &pTextRange);
|
|
if (hResult == S_OK && pTextRange)
|
|
{
|
|
long Count;
|
|
|
|
hResult = pTextRange->GetStoryLength(&Count);
|
|
|
|
if (hResult == S_OK)
|
|
*pacp = Count;
|
|
|
|
pTextRange->Release();
|
|
}
|
|
return hResult;
|
|
}
|
|
|
|
/*
|
|
* void CUIM::InitAttrVarArray
|
|
*
|
|
* @mfunc
|
|
* Helper routine to setup AttrVar Array
|
|
*
|
|
*/
|
|
void CUIM::InitAttrVarArray(BOOL fAllocData)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::InitAttrVarArray");
|
|
|
|
if (_parAttrsVal)
|
|
{
|
|
USHORT uIdx;
|
|
TS_ATTRVAL *pAttrVal = _parAttrsVal;
|
|
|
|
for (uIdx = 0; uIdx < _uAttrsValTotal; uIdx++, pAttrVal++)
|
|
VariantClear(&(pAttrVal->varValue));
|
|
|
|
memset(_parAttrsVal, 0, _uAttrsValTotal * sizeof(TS_ATTRVAL));
|
|
|
|
}
|
|
else if (fAllocData)
|
|
{
|
|
_parAttrsVal= (TS_ATTRVAL *)PvAlloc(sizeof(TS_ATTRVAL) * MAX_ATTR_SUPPORT, GMEM_ZEROINIT);
|
|
Assert(_parAttrsVal);
|
|
}
|
|
_uAttrsValCurrent = 0;
|
|
_uAttrsValTotal = 0;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CUIM::MouseCheck(UINT *pmsg, WPARAM *pwparam, LPARAM *plparam, LRESULT *plres)
|
|
*
|
|
* @mfunc
|
|
* Perform UIM mouse check
|
|
*
|
|
* @rdesc
|
|
* int S_OK if handled
|
|
* S_FALSE Don't handle and should be ignored
|
|
*
|
|
*/
|
|
HRESULT CUIM::MouseCheck(
|
|
UINT *pmsg,
|
|
WPARAM *pwparam,
|
|
LPARAM *plparam,
|
|
LRESULT *plres)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::MouseCheck");
|
|
|
|
BOOL fRetCode = FALSE;
|
|
|
|
if (_fShutDown)
|
|
return S_FALSE;
|
|
|
|
if (_fMosueSink)
|
|
{
|
|
BOOL fTerminateIME;
|
|
long cpCusor = -1;
|
|
CTFMOUSETRAP *pSinkList = _pSinkList;
|
|
|
|
Assert(_pSinkList);
|
|
|
|
_fAllowUIMLock = 1;
|
|
|
|
// Get thru the list until one of the traps has handled the message
|
|
while(fRetCode == FALSE && pSinkList)
|
|
{
|
|
if (cpCusor == -1 || pSinkList->cpMouseStart < cpCusor &&
|
|
cpCusor <= pSinkList->cpMouseStart + pSinkList->cchMosueComp) // Within composition range?
|
|
{
|
|
fRetCode = _pTextMsgFilter->MouseOperation(*pmsg, pSinkList->cpMouseStart,
|
|
pSinkList->cchMosueComp, *pwparam, &(pSinkList->wParamBefore), &fTerminateIME,
|
|
NULL, &cpCusor, pSinkList->pMouseSink);
|
|
}
|
|
|
|
pSinkList = pSinkList->pNext;
|
|
}
|
|
|
|
_fAllowUIMLock = 0;
|
|
if ( !fRetCode && IsUIMTyping() && WM_MOUSEMOVE != *pmsg )
|
|
_pTextMsgFilter->CompleteUIMTyping(CIme::TERMINATE_NORMAL);
|
|
}
|
|
return fRetCode ? S_OK : S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CUIM::Reconverse
|
|
*
|
|
* @mfunc
|
|
* Perform UIM reconversion
|
|
*
|
|
* @rdesc
|
|
* int S_OK if handled
|
|
* S_FALSE Don't handle and should be ignored
|
|
* -1 Don't handle and try IME reconverse
|
|
*
|
|
*/
|
|
int CUIM::Reconverse()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::Reconverse");
|
|
|
|
HRESULT hResult;
|
|
ITfRange *pITfRange = NULL;
|
|
ITfFnReconversion *pITfReconverse = NULL;
|
|
ITfFunctionProvider *pITfFctProvider = NULL;
|
|
TF_SELECTION TFSel = {0};
|
|
ULONG cSel;
|
|
int retCode = -1;
|
|
int fConvertible = FALSE;
|
|
|
|
if (_fUIMTyping)
|
|
return S_FALSE;
|
|
|
|
_fAllowUIMLock = 1;
|
|
_fHoldCTFSelChangeNotify = 1;
|
|
|
|
hResult = _pTextMsgFilter->_pTim->GetFunctionProvider(GUID_SYSTEM_FUNCTIONPROVIDER, &pITfFctProvider);
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
hResult = pITfFctProvider->GetFunction(GUID_NULL, IID_ITfFnReconversion, (IUnknown **)&pITfReconverse);
|
|
pITfFctProvider->Release();
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
int fCurrentLock = _fReadLockOn;
|
|
|
|
if (!fCurrentLock)
|
|
_fReadLockOn = 1; // Setup internal read lock
|
|
|
|
hResult = _pic->GetSelection(_editCookie, 0, 1, &TFSel, &cSel);
|
|
|
|
if (!fCurrentLock)
|
|
_fReadLockOn = 0; // Clear internal read lock
|
|
|
|
if (hResult == S_OK && cSel == 1)
|
|
{
|
|
if (pITfReconverse->QueryRange(TFSel.range, &pITfRange, &fConvertible) == S_OK && fConvertible)
|
|
{
|
|
pITfReconverse->Reconvert(pITfRange);
|
|
retCode = S_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (TFSel.range)
|
|
TFSel.range->Release();
|
|
|
|
if (pITfRange)
|
|
pITfRange->Release();
|
|
|
|
if (pITfReconverse)
|
|
pITfReconverse->Release();
|
|
|
|
_fAllowUIMLock = 0;
|
|
|
|
return retCode;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CUIM::FindHiddenText
|
|
*
|
|
* @mfunc
|
|
* Find Hidden text and return the end of the range
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CUIM::FindHiddenText(
|
|
long cp,
|
|
long cpEnd,
|
|
long &cpRange)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::FindHiddenText");
|
|
|
|
HRESULT hResult;
|
|
long unitMoved;
|
|
ITextRange *pTextRange = NULL;
|
|
|
|
cpRange = cp;
|
|
if (cpRange >= cpEnd)
|
|
return S_OK;
|
|
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(cpRange, cpRange, &pTextRange);
|
|
if (!SUCCEEDED(hResult))
|
|
return hResult;
|
|
|
|
hResult = pTextRange->EndOf(tomHidden, tomExtend, &unitMoved);
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
Assert(unitMoved);
|
|
cpRange = 0;
|
|
hResult = pTextRange->GetEnd(&cpRange);
|
|
Assert(cpRange);
|
|
}
|
|
pTextRange->Release();
|
|
return hResult;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CUIM::FindUnhiddenText
|
|
*
|
|
* @mfunc
|
|
* Find Unhidden text and return the end of the range
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CUIM::FindUnhiddenText(
|
|
long cp,
|
|
long cpEnd,
|
|
long &cpRange)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::FindUnhiddenText");
|
|
|
|
HRESULT hResult;
|
|
long unitMoved;
|
|
ITextRange *pTextRange = NULL;
|
|
ITextFont *pTextFont = NULL;
|
|
long fHidden;
|
|
|
|
cpRange = cp;
|
|
if (cpRange >= cpEnd)
|
|
return S_OK;
|
|
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(cpRange, cpRange, &pTextRange);
|
|
if (!SUCCEEDED(hResult))
|
|
return hResult;
|
|
|
|
Assert(pTextRange);
|
|
while (cpRange < cpEnd)
|
|
{
|
|
hResult = pTextRange->MoveEnd(tomCharacter, 1, &unitMoved);
|
|
if (!SUCCEEDED(hResult))
|
|
break;
|
|
|
|
if (!unitMoved)
|
|
{
|
|
hResult = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
hResult = pTextRange->GetFont(&pTextFont);
|
|
if (!SUCCEEDED(hResult))
|
|
break;
|
|
|
|
Assert(pTextFont);
|
|
pTextFont->GetHidden(&fHidden);
|
|
pTextFont->Release();
|
|
|
|
if (fHidden)
|
|
break;
|
|
|
|
hResult = pTextRange->EndOf(tomCharFormat, tomMove, &unitMoved);
|
|
if (!SUCCEEDED(hResult))
|
|
break;
|
|
|
|
if (unitMoved > 0)
|
|
{
|
|
cpRange = 0;
|
|
hResult = pTextRange->GetEnd(&cpRange);
|
|
if (!SUCCEEDED(hResult))
|
|
break;
|
|
Assert(cpRange);
|
|
}
|
|
else
|
|
cpRange = cpEnd;
|
|
}
|
|
|
|
pTextRange->Release();
|
|
return hResult;
|
|
}
|
|
|
|
/*
|
|
* void CUIM::BuildHiddenTxtBlks
|
|
*
|
|
* @mfunc
|
|
* Build hidden text blocks
|
|
*
|
|
*
|
|
*/
|
|
void CUIM::BuildHiddenTxtBlks(
|
|
long &cpMin,
|
|
long &cpMax,
|
|
long **ppHiddenTxtBlk,
|
|
long &cHiddenTxtBlk)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::BuildHiddenTxtBlks");
|
|
|
|
long cHiddenTxtBlkAlloc = 0;
|
|
long *pHiddenTxtBlk;
|
|
long cpCurrent = cpMin;
|
|
long cpNext;
|
|
HRESULT hResult;
|
|
|
|
cHiddenTxtBlkAlloc = 20;
|
|
pHiddenTxtBlk = (long *)PvAlloc(cHiddenTxtBlkAlloc * sizeof(long), GMEM_ZEROINIT);
|
|
|
|
if (pHiddenTxtBlk)
|
|
{
|
|
pHiddenTxtBlk[0] = tomForward;
|
|
while (cpCurrent < cpMax)
|
|
{
|
|
hResult = FindUnhiddenText(cpCurrent, cpMax, cpNext);
|
|
if (!SUCCEEDED(hResult))
|
|
break;
|
|
|
|
if (cpNext >= cpMax)
|
|
break;
|
|
|
|
hResult = FindHiddenText(cpNext, cpMax, cpCurrent);
|
|
if (!SUCCEEDED(hResult))
|
|
break;
|
|
|
|
Assert(cpCurrent > cpNext);
|
|
|
|
// Save the hidden text block cp and length
|
|
pHiddenTxtBlk[cHiddenTxtBlk] = cpNext;
|
|
cpCurrent = min(cpCurrent, cpMax);
|
|
pHiddenTxtBlk[cHiddenTxtBlk+1] = cpCurrent - cpNext;
|
|
cHiddenTxtBlk += 2;
|
|
if (cHiddenTxtBlk >= cHiddenTxtBlkAlloc)
|
|
{
|
|
cHiddenTxtBlkAlloc += 20;
|
|
pHiddenTxtBlk = (long *)PvReAlloc(pHiddenTxtBlk, cHiddenTxtBlkAlloc * sizeof(long));
|
|
|
|
if (!pHiddenTxtBlk)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
*ppHiddenTxtBlk = pHiddenTxtBlk;
|
|
}
|
|
|
|
/*
|
|
* BOOL CUIM::CTFOpenStatus
|
|
*
|
|
* @mfunc
|
|
* Get/Set current CTF open status
|
|
*
|
|
* @rdesc
|
|
* For GetOpenStatus
|
|
* return 1 is Open, 0 if Close or fail
|
|
*
|
|
* For SetOpenStatus
|
|
* return TRUE is set status without problem, FALSE if fail
|
|
*
|
|
*/
|
|
BOOL CUIM::CTFOpenStatus(
|
|
BOOL fGetOpenStatus,
|
|
BOOL fOpen)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::CTFOpenStatus");
|
|
|
|
HRESULT hr;
|
|
VARIANT var;
|
|
BOOL fRet = FALSE;
|
|
ITfCompartment *pComp = NULL;
|
|
ITfCompartmentMgr *pCompMgr = NULL;
|
|
|
|
|
|
hr = _pTextMsgFilter->_pTim->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompMgr);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(pCompMgr);
|
|
|
|
hr = pCompMgr->GetCompartment(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, &pComp);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(pComp);
|
|
|
|
VariantInit(&var);
|
|
if (fGetOpenStatus)
|
|
{
|
|
if (pComp->GetValue(&var) == S_OK)
|
|
{
|
|
Assert(var.vt == VT_I4);
|
|
fRet = var.lVal ? TRUE : FALSE ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var.vt = VT_I4;
|
|
var.lVal = fOpen;
|
|
hr = pComp->SetValue(_pTextMsgFilter->_tid, &var);
|
|
fRet = SUCCEEDED(hr);
|
|
}
|
|
VariantClear(&var);
|
|
}
|
|
}
|
|
|
|
if (pComp)
|
|
pComp->Release();
|
|
|
|
if (pCompMgr)
|
|
pCompMgr->Release();
|
|
|
|
return fRet;
|
|
}
|
|
|
|
/*
|
|
* BOOL CUIM::BuildObject
|
|
*
|
|
* @mfunc
|
|
* Build an array of embedded objects
|
|
*
|
|
* @rdesc
|
|
* return Number of objects in the array returned in pEmbeddObjects
|
|
*
|
|
*/
|
|
int CUIM::BuildObject(
|
|
ITextRange *pTextRange,
|
|
BSTR bstr,
|
|
EMBEDOBJECT **ppEmbeddObjects,
|
|
int cSize)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::BuildObject");
|
|
|
|
long cpMin;
|
|
HRESULT hResult = pTextRange->GetStart(&cpMin);
|
|
WCHAR *pText = (WCHAR *)bstr;
|
|
EMBEDOBJECT *pEmbeddObjStart = *ppEmbeddObjects;
|
|
EMBEDOBJECT *pEmbeddObj = *ppEmbeddObjects;
|
|
BOOL fAllocateBuffer = FALSE;
|
|
|
|
long cObjects = 0;
|
|
long cchBstr = SysStringLen(bstr);
|
|
|
|
if (hResult == S_OK)
|
|
{
|
|
for(long i = 0; i < cchBstr; i++, pText++)
|
|
{
|
|
if (*pText == WCH_EMBEDDING)
|
|
{
|
|
// Get IDataObject
|
|
HRESULT hr;
|
|
IDataObject *pIDataObj = NULL;
|
|
BOOL fReadLockOld = _fReadLockOn;
|
|
|
|
_fReadLockOn = TRUE;
|
|
hr = GetEmbedded(cpMin+i, GUID_DCSERVICE_DATAOBJECT, IID_IDataObject, (IUnknown **)&pIDataObj);
|
|
|
|
_fReadLockOn = fReadLockOld;
|
|
// Store it in the memory
|
|
if (cObjects < cSize)
|
|
{
|
|
pEmbeddObj->cpOffset = i;
|
|
pEmbeddObj->pIDataObj = pIDataObj;
|
|
pEmbeddObj++;
|
|
cObjects++;
|
|
}
|
|
else
|
|
{
|
|
long cNewSize = cSize + 5;
|
|
EMBEDOBJECT *pEmbeddObjTemp;
|
|
if (fAllocateBuffer)
|
|
{
|
|
pEmbeddObjTemp = (EMBEDOBJECT *)PvReAlloc(pEmbeddObjStart, sizeof(EMBEDOBJECT) * cNewSize);
|
|
|
|
if (pEmbeddObjTemp)
|
|
{
|
|
pEmbeddObjStart = pEmbeddObjTemp;
|
|
pEmbeddObj = pEmbeddObjStart + cSize;
|
|
cSize = cNewSize;
|
|
pEmbeddObj->cpOffset = i;
|
|
pEmbeddObj->pIDataObj = pIDataObj;
|
|
pEmbeddObj++;
|
|
cObjects++;
|
|
}
|
|
else
|
|
{
|
|
// Cleanup here
|
|
pIDataObj->Release();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fAllocateBuffer = TRUE;
|
|
|
|
pEmbeddObjTemp = (EMBEDOBJECT *)PvAlloc(sizeof(EMBEDOBJECT) * cNewSize, GMEM_ZEROINIT);
|
|
if (pEmbeddObjTemp)
|
|
{
|
|
if (cSize)
|
|
{
|
|
// Copy previous data to new buffer
|
|
memcpy(pEmbeddObjTemp, pEmbeddObjStart, sizeof(EMBEDOBJECT) * cSize);
|
|
}
|
|
pEmbeddObjStart = pEmbeddObjTemp;
|
|
pEmbeddObj = pEmbeddObjStart + cSize;
|
|
cSize = cNewSize;
|
|
pEmbeddObj->cpOffset = i;
|
|
pEmbeddObj->pIDataObj = pIDataObj;
|
|
pEmbeddObj++;
|
|
cObjects++;
|
|
}
|
|
else
|
|
{
|
|
// Cleanup here
|
|
pIDataObj->Release();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*ppEmbeddObjects = pEmbeddObjStart;
|
|
|
|
return cObjects;
|
|
}
|
|
|
|
/*
|
|
* BOOL CUIM::InsertTextandObject
|
|
*
|
|
* @mfunc
|
|
* Insert text and embedded objects
|
|
*
|
|
*/
|
|
void CUIM::InsertTextandObject(
|
|
ITextRange *pTextRange,
|
|
BSTR bstr,
|
|
EMBEDOBJECT *pEmbeddObjects,
|
|
long cEmbeddedObjects)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::InsertTextandObject");
|
|
|
|
WCHAR *pText = (WCHAR *)bstr;
|
|
WCHAR *pTextStart = pText;
|
|
long cObjects = 0;
|
|
long cchBstr = SysStringLen(bstr);
|
|
HRESULT hr;
|
|
|
|
for(long i = 0; i < cchBstr; i++, pText++)
|
|
{
|
|
if (*pText == WCH_EMBEDDING)
|
|
{
|
|
// Insert Text if necessary
|
|
if (pTextStart != pText)
|
|
{
|
|
BSTR bstr = SysAllocStringLen(pTextStart, pText-pTextStart);
|
|
|
|
if (bstr)
|
|
{
|
|
hr = pTextRange->SetText(bstr);
|
|
|
|
SysFreeString(bstr);
|
|
pTextRange->Collapse(tomFalse);
|
|
}
|
|
}
|
|
|
|
if (cObjects < cEmbeddedObjects)
|
|
{
|
|
LRESULT lresult;
|
|
long cpMin = 0, cpMax = 0;
|
|
HRESULT hResult = pTextRange->GetStart(&cpMin);
|
|
hResult = pTextRange->GetEnd(&cpMax);
|
|
CHARRANGE charRange = {cpMin, cpMax};
|
|
|
|
hr = _pTextMsgFilter->_pTextService->TxSendMessage(EM_INSERTOBJ, (WPARAM)&charRange,
|
|
(LPARAM)(pEmbeddObjects->pIDataObj), &lresult);
|
|
|
|
hr = pTextRange->Move(tomCharacter, 1, NULL); // move over the embedded char
|
|
cObjects++;
|
|
pEmbeddObjects++;
|
|
}
|
|
|
|
// Setup for next string after the embedded object
|
|
pTextStart = pText + 1;
|
|
}
|
|
}
|
|
|
|
// Insert last Text if necessary
|
|
if (pTextStart != pText)
|
|
{
|
|
BSTR bstr = SysAllocStringLen(pTextStart, pText-pTextStart);
|
|
|
|
if (bstr)
|
|
{
|
|
hr = pTextRange->SetText(bstr);
|
|
SysFreeString(bstr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* BOOL CUIM::CleanUpObjects
|
|
*
|
|
* @mfunc
|
|
* Free the objects
|
|
*
|
|
*/
|
|
void CUIM::CleanUpObjects(
|
|
long cObjects,
|
|
EMBEDOBJECT *pObjects)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::CleanUpObjects");
|
|
|
|
for (long i = 0; i < cObjects; i++, pObjects++)
|
|
{
|
|
if (pObjects->pIDataObj)
|
|
pObjects->pIDataObj->Release();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* void CUIM::CleanUpComposition
|
|
*
|
|
* @mfunc
|
|
* Free the composition string and objects list
|
|
*
|
|
*/
|
|
void CUIM::CleanUpComposition()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::CleanUpComposition");
|
|
|
|
if (_bstrComposition)
|
|
{
|
|
SysFreeString (_bstrComposition);
|
|
_bstrComposition = NULL;
|
|
}
|
|
|
|
_acpBstrStart = tomForward;
|
|
if (_cObjects)
|
|
{
|
|
CleanUpObjects(_cObjects, _pObjects);
|
|
delete _pObjects;
|
|
_cObjects = 0;
|
|
_pObjects = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* BOOL CUIM::HandleTempDispAttr
|
|
*
|
|
* @mfunc
|
|
* This routine handle temp. display attribute that are set
|
|
* outside CTF composition. It is using ITfContextRenderingMarkup
|
|
* to get the range and display data.
|
|
*
|
|
*/
|
|
void CUIM::HandleTempDispAttr(
|
|
ITfEditRecord *pEditRecord)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::HandleTempDispAttr");
|
|
|
|
if (_pContextRenderingMarkup)
|
|
{
|
|
HRESULT hr;
|
|
const GUID *rgGUID[1];
|
|
IEnumTfRanges *pEnumRanges = NULL;
|
|
|
|
// Get attribute changes
|
|
rgGUID[0] = &GUID_PROP_ATTRIBUTE;
|
|
hr = pEditRecord->GetTextAndPropertyUpdates(0, (const GUID**)rgGUID, 1, &pEnumRanges);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ITfRange *pITfRange = NULL;
|
|
|
|
while (pEnumRanges->Next(1, &pITfRange, NULL) == S_OK)
|
|
{
|
|
IEnumTfRenderingMarkup *pEnumMarkup;
|
|
TF_RENDERINGMARKUP tfRenderingMarkup;
|
|
long acpStartRange, cch;
|
|
|
|
if (_pContextRenderingMarkup->GetRenderingMarkup(_editCookie, TF_GRM_INCLUDE_PROPERTY, pITfRange, &pEnumMarkup) == S_OK)
|
|
{
|
|
while (pEnumMarkup->Next(1, &tfRenderingMarkup, NULL) == S_OK)
|
|
{
|
|
HRESULT hResult;
|
|
|
|
hResult = GetExtentAcpPrange(tfRenderingMarkup.pRange, acpStartRange, cch);
|
|
if (hResult && cch > 0)
|
|
{
|
|
ITextRange *pTextRange = NULL;
|
|
hResult = _pTextMsgFilter->_pTextDoc->Range(acpStartRange, acpStartRange+cch, &pTextRange);
|
|
if (pTextRange)
|
|
{
|
|
ITextFont *pFont = NULL;
|
|
|
|
hResult = pTextRange->GetFont(&pFont);
|
|
|
|
if (pFont)
|
|
{
|
|
long lStyle;
|
|
COLORREF cr;
|
|
|
|
_pTextMsgFilter->_pTextDoc->Undo(tomSuspend, NULL);
|
|
|
|
TF_DISPLAYATTRIBUTE da = tfRenderingMarkup.tfDisplayAttr;
|
|
|
|
pFont->Reset(tomApplyTmp);
|
|
|
|
switch (da.lsStyle)
|
|
{
|
|
// case TFLS_SOLID:
|
|
default:
|
|
lStyle = da.fBoldLine ? tomThick : tomSingle;
|
|
break;
|
|
|
|
case TF_LS_DOT:
|
|
case TF_LS_DASH: // Dash line should show as dotted line
|
|
lStyle = tomDotted;
|
|
break;
|
|
|
|
case TF_LS_SQUIGGLE:
|
|
lStyle = tomWave;
|
|
break;
|
|
|
|
case TF_LS_NONE:
|
|
lStyle = tomNone;
|
|
break;
|
|
}
|
|
if (lStyle != tomNone)
|
|
{
|
|
pFont->SetUnderline(lStyle);
|
|
|
|
if (GetUIMAttributeColor(&da.crLine, &cr))
|
|
pFont->SetUnderline(cr | 0x0FF000000);
|
|
}
|
|
|
|
if (GetUIMAttributeColor(&da.crText, &cr))
|
|
pFont->SetForeColor(cr);
|
|
|
|
if (GetUIMAttributeColor(&da.crBk, &cr))
|
|
pFont->SetBackColor(cr);
|
|
|
|
pFont->Reset(tomApplyNow);
|
|
pFont->Release();
|
|
|
|
_pTextMsgFilter->_pTextDoc->Undo(tomResume, NULL);
|
|
}
|
|
pTextRange->Release();
|
|
}
|
|
}
|
|
}
|
|
pEnumMarkup->Release();
|
|
}
|
|
pITfRange->Release();
|
|
}
|
|
}
|
|
|
|
if (pEnumRanges)
|
|
pEnumRanges->Release();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* STDAPI CUIM::QueryService(REFGUID guidService, REFIID riid, void **ppv)
|
|
*
|
|
* @mfunc
|
|
* Handle ITfEnableService::QueryService. Cicero/tip call this interface to obtain
|
|
* IID_ITfEnableService i/f
|
|
*
|
|
* @rdesc
|
|
* S_OK if service supported
|
|
*
|
|
*/
|
|
STDAPI CUIM::QueryService(
|
|
REFGUID guidService,
|
|
REFIID riid,
|
|
void **ppv)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::QueryService");
|
|
|
|
if (ppv == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
*ppv = NULL;
|
|
|
|
// we support just one service
|
|
if (!IsEqualGUID(guidService, GUID_SERVICE_TEXTSTORE))
|
|
return E_NOINTERFACE;
|
|
|
|
if (IsEqualIID(riid, IID_IServiceProvider))
|
|
{
|
|
*ppv = (IServiceProvider *)this;
|
|
}
|
|
else if (IsEqualIID(riid, IID_ITfEnableService))
|
|
{
|
|
*ppv = (ITfEnableService *)this;
|
|
}
|
|
|
|
if (*ppv == NULL)
|
|
return E_NOINTERFACE;
|
|
|
|
AddRef();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDAPI CUIM::IsEnabled(REFGUID rguidServiceCategory, CLSID clsidService, IUnknown *punkService, BOOL *pfOkToRun)
|
|
*
|
|
* @mfunc
|
|
* Handle ITfEnableService::QueryService. Cicero/tip call this interface to check
|
|
* if we support the service
|
|
*
|
|
* @rdesc
|
|
* S_OK if service supported
|
|
*
|
|
*/
|
|
STDAPI CUIM::IsEnabled(
|
|
REFGUID rguidServiceCategory,
|
|
CLSID clsidService,
|
|
IUnknown *punkService,
|
|
BOOL *pfOkToRun)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::IsEnabled");
|
|
|
|
if (pfOkToRun == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
// default is disallow
|
|
*pfOkToRun = FALSE;
|
|
|
|
// clsidService identifies the particular tip, but we don't use it here
|
|
// punkService is a custom interface, probably for config, but we don't use it here yet
|
|
|
|
if (IsEqualGUID(rguidServiceCategory, GUID_TFCAT_TIP_SMARTTAG))
|
|
{
|
|
*pfOkToRun = _pTextMsgFilter->_fAllowSmartTag ? TRUE : FALSE;
|
|
}
|
|
else if (IsEqualGUID(rguidServiceCategory, GUID_TFCAT_TIP_PROOFING))
|
|
{
|
|
*pfOkToRun = _pTextMsgFilter->_fAllowProofing ? TRUE : FALSE;;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* STDAPI CUIM::GetId(GUID *pguidId)
|
|
*
|
|
* @mfunc
|
|
* get the RE clid
|
|
*
|
|
* @rdesc
|
|
* S_OK
|
|
*
|
|
*/
|
|
STDAPI CUIM::GetId(
|
|
GUID *pguidId)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::GetId");
|
|
|
|
if (pguidId == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
*pguidId = CLSID_MSFTEDIT;
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* void CUIM::NotifyService()
|
|
*
|
|
* @mfunc
|
|
* Notify Cicero about change in services.
|
|
*
|
|
*
|
|
*/
|
|
void CUIM::NotifyService()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CUIM::NotifyService");
|
|
|
|
ITfCompartmentMgr *pCompartmentMgr;
|
|
ITfCompartment *pCompartment;
|
|
VARIANT varValue;
|
|
|
|
if (_pic->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr) != S_OK)
|
|
return;
|
|
|
|
// give any waiting tips a heads up we've changed our state
|
|
if (pCompartmentMgr->GetCompartment(GUID_COMPARTMENT_ENABLESTATE, &pCompartment) == S_OK)
|
|
{
|
|
varValue.vt = VT_I4;
|
|
varValue.lVal = 1; // arbitrary value, we just want to generate a change event
|
|
|
|
pCompartment->SetValue(_pTextMsgFilter->_tid, &varValue);
|
|
pCompartment->Release();
|
|
}
|
|
|
|
pCompartmentMgr->Release();
|
|
}
|
|
|
|
/*
|
|
* STDAPI CTextEditSink::QueryInterface
|
|
*
|
|
* @mfunc
|
|
* IUnknown QueryInterface support
|
|
*
|
|
* @rdesc
|
|
* NOERROR if interface supported
|
|
*
|
|
*/
|
|
STDAPI CTextEditSink::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextEditSink::QueryInterface");
|
|
|
|
*ppvObj = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
|
IsEqualIID(riid, IID_ITfTextEditSink))
|
|
{
|
|
*ppvObj = (CTextEditSink *)this;
|
|
}
|
|
|
|
if (*ppvObj)
|
|
{
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP_(ULONG) CTextEditSink::AddRef
|
|
*
|
|
* @mfunc
|
|
* IUnknown AddRef support
|
|
*
|
|
* @rdesc
|
|
* Reference count
|
|
*/
|
|
STDAPI_(ULONG) CTextEditSink::AddRef()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextEditSink::AddRef");
|
|
|
|
return ++_cRef;
|
|
}
|
|
|
|
|
|
/*
|
|
* STDMETHODIMP_(ULONG) CTextEditSink::Release()
|
|
*
|
|
* @mfunc
|
|
* IUnknown Release support - delete object when reference count is 0
|
|
*
|
|
* @rdesc
|
|
* Reference count
|
|
*/
|
|
STDAPI_(ULONG) CTextEditSink::Release()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextEditSink::Release");
|
|
|
|
long cr;
|
|
|
|
cr = --_cRef;
|
|
Assert(cr >= 0);
|
|
|
|
if (cr == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
return cr;
|
|
}
|
|
|
|
/*
|
|
* CTextEditSink::CTextEditSink()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* ctor
|
|
*
|
|
*/
|
|
CTextEditSink::CTextEditSink(PTESCALLBACK pfnCallback, void *pv)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextEditSink::CTextEditSink");
|
|
|
|
_cRef = 1;
|
|
_dwCookie = 0x0FFFFFFFF;
|
|
|
|
_pfnCallback = pfnCallback;
|
|
_pv = pv;
|
|
}
|
|
|
|
/*
|
|
* STDAPI CTextEditSink::OnEndEdit()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDAPI CTextEditSink::OnEndEdit(
|
|
ITfContext *pic,
|
|
TfEditCookie ecReadOnly,
|
|
ITfEditRecord *pEditRecord)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextEditSink::OnEndEdit");
|
|
|
|
return _pfnCallback(pEditRecord, _pv);
|
|
}
|
|
|
|
/*
|
|
* HRESULT CTextEditSink::_Advise
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CTextEditSink::_Advise(ITfContext *pic)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextEditSink::_Advise");
|
|
|
|
HRESULT hr;
|
|
ITfSource *source = NULL;
|
|
|
|
_pic = NULL;
|
|
hr = E_FAIL;
|
|
|
|
if (FAILED(pic->QueryInterface(IID_ITfSource, (void **)&source)))
|
|
return E_FAIL;
|
|
|
|
if (FAILED(source->AdviseSink(IID_ITfTextEditSink, this, &_dwCookie)))
|
|
goto Exit;
|
|
|
|
_pic = pic;
|
|
_pic->AddRef();
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
source->Release();
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CTextEditSink::_Unadvise
|
|
*
|
|
* @mfunc
|
|
*
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CTextEditSink::_Unadvise()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextEditSink::_Unadvise");
|
|
|
|
HRESULT hr;
|
|
ITfSource *source = NULL;
|
|
|
|
hr = E_FAIL;
|
|
|
|
if (_pic == NULL)
|
|
return E_FAIL;
|
|
|
|
if (FAILED(_pic->QueryInterface(IID_ITfSource, (void **)&source)))
|
|
return E_FAIL;
|
|
|
|
if (FAILED(source->UnadviseSink(_dwCookie)))
|
|
goto Exit;
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
source->Release();
|
|
_pic->Release();
|
|
_pic = NULL;
|
|
return hr;
|
|
}
|
|
|
|
#endif // NOFEPROCESSING
|