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.
2514 lines
75 KiB
2514 lines
75 KiB
/*++
|
|
|
|
Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
editses.cpp
|
|
|
|
Abstract:
|
|
|
|
This file implements the EditSession Class.
|
|
|
|
Author:
|
|
|
|
Revision History:
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
|
|
#include "private.h"
|
|
|
|
#include "ctffunc.h"
|
|
|
|
#include "editses.h"
|
|
#include "globals.h"
|
|
#include "resource.h"
|
|
#include "candpos.h"
|
|
|
|
#include "proputil.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfEditSessionCallBack
|
|
|
|
HRESULT
|
|
ImmIfEditSessionCallBack::GetAllTextRange(
|
|
TfEditCookie ec,
|
|
Interface_Attach<ITfContext>& ic,
|
|
Interface<ITfRange>* range,
|
|
LONG* lpTextLength,
|
|
TF_HALTCOND* lpHaltCond
|
|
)
|
|
{
|
|
ITfRange *rangeFull = NULL;
|
|
HRESULT hr;
|
|
BOOL fFound = FALSE;
|
|
BOOL fIsReadOnlyRange = FALSE;
|
|
ITfProperty *prop;
|
|
ITfRange *rangeTmp;
|
|
LONG cch;
|
|
|
|
//
|
|
// init lpTextLength first.
|
|
//
|
|
*lpTextLength = 0;
|
|
|
|
//
|
|
// Create the range that covers all the text.
|
|
//
|
|
if (FAILED(hr=ic->GetStart(ec, &rangeFull)))
|
|
return hr;
|
|
|
|
if (FAILED(hr=rangeFull->ShiftEnd(ec, LONG_MAX, &cch, lpHaltCond)))
|
|
return hr;
|
|
|
|
//
|
|
// find the first non readonly range in the text store.
|
|
//
|
|
if (SUCCEEDED(ic->GetProperty(GUID_PROP_MSIMTF_READONLY, &prop)))
|
|
{
|
|
IEnumTfRanges *enumranges;
|
|
if (SUCCEEDED(prop->EnumRanges(ec, &enumranges, rangeFull)))
|
|
{
|
|
while(!fFound && (enumranges->Next(1, &rangeTmp, NULL) == S_OK))
|
|
{
|
|
VARIANT var;
|
|
QuickVariantInit(&var);
|
|
prop->GetValue(ec, rangeTmp, &var);
|
|
if ((var.vt == VT_EMPTY) || (var.lVal == 0))
|
|
{
|
|
fFound = TRUE;
|
|
hr = rangeTmp->Clone(*range);
|
|
rangeTmp->Release();
|
|
break;
|
|
}
|
|
|
|
fIsReadOnlyRange = TRUE;
|
|
rangeTmp->Release();
|
|
}
|
|
enumranges->Release();
|
|
}
|
|
prop->Release();
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (!fFound)
|
|
{
|
|
if (fIsReadOnlyRange)
|
|
{
|
|
//
|
|
// all text store is readonly. So we just return an empty range.
|
|
//
|
|
if (FAILED(hr = GetSelectionSimple(ec, ic.GetPtr(), *range)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = (*range)->Collapse(ec, TF_ANCHOR_START)))
|
|
return hr;
|
|
|
|
}
|
|
else
|
|
{
|
|
if (FAILED(hr = rangeFull->Clone(*range)))
|
|
return hr;
|
|
|
|
*lpTextLength = cch;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*lpTextLength = 0;
|
|
|
|
if (SUCCEEDED((*range)->Clone(&rangeTmp)))
|
|
{
|
|
BOOL fEmpty;
|
|
WCHAR wstr[256 + 1];
|
|
ULONG ul = 0;
|
|
|
|
while (rangeTmp->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty)
|
|
{
|
|
ULONG ulcch;
|
|
rangeTmp->GetText(ec,
|
|
TF_TF_MOVESTART,
|
|
wstr,
|
|
ARRAYSIZE(wstr) - 1,
|
|
&ulcch);
|
|
ul += ulcch;
|
|
}
|
|
|
|
rangeTmp->Release();
|
|
|
|
*lpTextLength = (LONG)ul;
|
|
}
|
|
}
|
|
|
|
SafeRelease(rangeFull);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImmIfEditSessionCallBack::SetTextInRange(
|
|
TfEditCookie ec,
|
|
ITfRange* range,
|
|
LPWSTR psz,
|
|
DWORD len,
|
|
CAImeContext* pAImeContext
|
|
)
|
|
{
|
|
pAImeContext->_fModifyingDoc = TRUE;
|
|
|
|
//
|
|
// Set the text in Cicero TOM
|
|
//
|
|
HRESULT hr = range->SetText(ec, 0, psz, len);
|
|
|
|
pAImeContext->_fModifyingDoc = FALSE;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImmIfEditSessionCallBack::ClearTextInRange(
|
|
TfEditCookie ec,
|
|
ITfRange* range,
|
|
CAImeContext* pAImeContext
|
|
)
|
|
{
|
|
//
|
|
// Clear the text in Cicero TOM
|
|
//
|
|
return SetTextInRange(ec, range, NULL, 0, pAImeContext);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImmIfEditSessionCallBack::GetReadingString(
|
|
TfEditCookie ec,
|
|
Interface_Attach<ITfContext>& ic,
|
|
CWCompString& reading_string,
|
|
CWCompClause& reading_clause
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
Interface<ITfRange> range;
|
|
LONG l;
|
|
if (FAILED(hr=GetAllTextRange(ec, ic, &range, &l)))
|
|
return hr;
|
|
|
|
return GetReadingString(ec, ic, range, reading_string, reading_clause);
|
|
}
|
|
|
|
HRESULT
|
|
ImmIfEditSessionCallBack::GetReadingString(
|
|
TfEditCookie ec,
|
|
Interface_Attach<ITfContext>& ic,
|
|
ITfRange* range,
|
|
CWCompString& reading_string,
|
|
CWCompClause& reading_clause
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
EnumReadingPropertyArgs args;
|
|
if (FAILED(hr=ic->GetProperty(GUID_PROP_READING, args.Property)))
|
|
return hr;
|
|
|
|
Interface<IEnumTfRanges> EnumReadingProperty;
|
|
hr = args.Property->EnumRanges(ec, EnumReadingProperty, range);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
args.ec = ec;
|
|
args.reading_string = &reading_string;
|
|
args.reading_clause = &reading_clause;
|
|
args.ulClausePos = (reading_clause.GetSize() > 0 ? reading_clause[ reading_clause.GetSize() - 1]
|
|
: 0);
|
|
|
|
CEnumrateInterface<IEnumTfRanges,
|
|
ITfRange,
|
|
EnumReadingPropertyArgs> Enumrate(EnumReadingProperty,
|
|
EnumReadingPropertyCallback,
|
|
&args); // Argument of callback func.
|
|
Enumrate.DoEnumrate();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Enumrate callbacks
|
|
//
|
|
|
|
/* static */
|
|
ENUM_RET
|
|
ImmIfEditSessionCallBack::EnumReadingPropertyCallback(
|
|
ITfRange* pRange,
|
|
EnumReadingPropertyArgs *pargs
|
|
)
|
|
{
|
|
ENUM_RET ret = ENUM_CONTINUE;
|
|
VARIANT var;
|
|
QuickVariantInit(&var);
|
|
|
|
HRESULT hr = pargs->Property->GetValue(pargs->ec, pRange, &var);
|
|
if (SUCCEEDED(hr)) {
|
|
if (V_VT(&var) == VT_BSTR) {
|
|
BSTR bstr = V_BSTR(&var);
|
|
LONG cch = SysStringLen(bstr);
|
|
pargs->reading_string->AddCompData(bstr, cch);
|
|
|
|
if (pargs->reading_clause->GetSize() == 0)
|
|
pargs->reading_clause->Add(0);
|
|
|
|
pargs->reading_clause->Add( pargs->ulClausePos += cch );
|
|
}
|
|
VariantClear(&var);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImmIfEditSessionCallBack::CompClauseToResultClause(
|
|
IMCLock& imc,
|
|
CWCompClause& result_clause,
|
|
UINT cch
|
|
)
|
|
{
|
|
LONG num_of_written;
|
|
IMTLS *ptls = IMTLS_GetOrAlloc();
|
|
|
|
if (ptls && ptls->pAImm) {
|
|
|
|
// Check GCS_COMPCLAUSE office set
|
|
IMCCLock<COMPOSITIONSTRING> lpCompStr(imc->hCompStr);
|
|
if (lpCompStr.Invalid())
|
|
return E_FAIL;
|
|
|
|
if (lpCompStr->dwCompClauseOffset > lpCompStr->dwSize)
|
|
return E_FAIL;
|
|
|
|
if (lpCompStr->dwCompClauseOffset + lpCompStr->dwCompClauseLen > lpCompStr->dwSize)
|
|
return E_FAIL;
|
|
|
|
if (SUCCEEDED(ptls->pAImm->GetCompositionStringW((HIMC)imc,
|
|
GCS_COMPCLAUSE,
|
|
0, &num_of_written, NULL))) {
|
|
DWORD* buffer = new DWORD[ num_of_written / sizeof(DWORD) ];
|
|
if (buffer != NULL) {
|
|
ptls->pAImm->GetCompositionStringW((HIMC)imc,
|
|
GCS_COMPCLAUSE,
|
|
num_of_written, &num_of_written, buffer);
|
|
|
|
int idx = num_of_written / sizeof(DWORD);
|
|
if (idx > 1)
|
|
{
|
|
idx -= 1;
|
|
if (buffer[idx] == cch)
|
|
{
|
|
result_clause.WriteCompData(buffer, num_of_written / sizeof(DWORD));
|
|
}
|
|
else
|
|
{
|
|
// this is the case comp clause is corrupted
|
|
// it is the least we can do for the failure case
|
|
buffer[0] = 0;
|
|
buffer[1] = cch;
|
|
result_clause.WriteCompData(buffer, 2);
|
|
}
|
|
}
|
|
|
|
delete [] buffer;
|
|
}
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// Get cursor position
|
|
//
|
|
HRESULT
|
|
ImmIfEditSessionCallBack::_GetCursorPosition(
|
|
TfEditCookie ec,
|
|
IMCLock& imc,
|
|
Interface_Attach<ITfContext>& ic,
|
|
CWCompCursorPos& CompCursorPos,
|
|
CWCompAttribute& CompAttr
|
|
)
|
|
{
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
if (_pAImeContext == NULL)
|
|
return E_FAIL;
|
|
|
|
CAImeContext::IME_QUERY_POS qpos = CAImeContext::IME_QUERY_POS_UNKNOWN;
|
|
|
|
if (_pAImeContext->m_fStartComposition) {
|
|
//
|
|
// This method should not call before sending WM_IME_STARTCOMPOSITION
|
|
// because some apps confusing to receive QUERYCHARPOSITION due to
|
|
// no composing.
|
|
//
|
|
_pAImeContext->InquireIMECharPosition(imc, &qpos);
|
|
}
|
|
|
|
//
|
|
// Is apps support "query positioning" ?
|
|
//
|
|
if ((g_fInLegacyClsid || g_fAIMM12Trident) &&
|
|
qpos != CAImeContext::IME_QUERY_POS_YES) {
|
|
|
|
//
|
|
// IE5.0 candidate window positioning code.
|
|
// They except of it position from COMPOSITIONSTRING.dwCursorPos.
|
|
//
|
|
INT_PTR ich = 0;
|
|
INT_PTR attr_size;
|
|
|
|
if (attr_size = CompAttr.GetSize()) {
|
|
while (ich < attr_size && CompAttr[ich] != ATTR_TARGET_CONVERTED)
|
|
ich++;
|
|
if (ich < attr_size) {
|
|
CompCursorPos.Set((DWORD)ich);
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
HRESULT hr;
|
|
Interface_TFSELECTION sel;
|
|
ULONG cFetched;
|
|
|
|
if (SUCCEEDED(hr = ic->GetSelection(ec, TF_DEFAULT_SELECTION, 1, sel, &cFetched))) {
|
|
Interface<ITfRange> start;
|
|
LONG ich;
|
|
TF_HALTCOND hc;
|
|
|
|
hc.pHaltRange = sel->range;
|
|
hc.aHaltPos = (sel->style.ase == TF_AE_START) ? TF_ANCHOR_START : TF_ANCHOR_END;
|
|
hc.dwFlags = 0;
|
|
|
|
if (SUCCEEDED(hr=GetAllTextRange(ec, ic, &start, &ich, &hc))) {
|
|
CompCursorPos.Set(ich);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Get text and attribute in given range
|
|
//
|
|
// ITfRange::range
|
|
// TF_ANCHOR_START
|
|
// |======================================================================|
|
|
// +--------------------+ #+----------+
|
|
// |ITfRange::pPropRange| #|pPropRange|
|
|
// +--------------------+ #+----------+
|
|
// | GUID_ATOM | #
|
|
// +--------------------+ #
|
|
// ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^#
|
|
// ITfRange::gap_range gap_range #
|
|
// #
|
|
// V
|
|
// ITfRange::no_display_attribute_range
|
|
// result_comp
|
|
// +1 <- 0 -> -1
|
|
//
|
|
|
|
HRESULT
|
|
ImmIfEditSessionCallBack::_GetTextAndAttribute(
|
|
LIBTHREAD *pLibTLS,
|
|
TfEditCookie ec,
|
|
IMCLock& imc,
|
|
Interface_Attach<ITfContext>& ic,
|
|
Interface<ITfRange>& rangeIn,
|
|
CWCompString& CompStr,
|
|
CWCompAttribute& CompAttr,
|
|
CWCompClause& CompClause,
|
|
CWCompTfGuidAtom& CompGuid,
|
|
CWCompString& CompReadStr,
|
|
CWCompClause& CompReadClause,
|
|
CWCompString& ResultStr,
|
|
CWCompClause& ResultClause,
|
|
CWCompString& ResultReadStr,
|
|
CWCompClause& ResultReadClause,
|
|
BOOL bInWriteSession
|
|
)
|
|
{
|
|
//
|
|
// Get no display attribute range if there exist.
|
|
// Otherwise, result range is the same to input range.
|
|
//
|
|
LONG result_comp;
|
|
Interface<ITfRange> no_display_attribute_range;
|
|
if (FAILED(rangeIn->Clone(no_display_attribute_range)))
|
|
return E_FAIL;
|
|
|
|
if (FAILED(_GetNoDisplayAttributeRange(pLibTLS,
|
|
ec,
|
|
ic,
|
|
rangeIn,
|
|
no_display_attribute_range)))
|
|
return E_FAIL;
|
|
|
|
|
|
Interface<ITfProperty> propComp;
|
|
Interface<IEnumTfRanges> enumComp;
|
|
VARIANT var;
|
|
|
|
HRESULT hr;
|
|
if (FAILED(hr = ic->GetProperty(GUID_PROP_COMPOSING, propComp)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = propComp->EnumRanges(ec, enumComp, rangeIn)))
|
|
return hr;
|
|
|
|
CompClause.Add(0); // setup composition clause at 0
|
|
ResultClause.Add(0); // setup result clause at 0
|
|
|
|
Interface<ITfRange> range;
|
|
while(enumComp->Next(1, range, NULL) == S_OK)
|
|
{
|
|
|
|
BOOL fCompExist = FALSE;
|
|
|
|
hr = propComp->GetValue(ec, range, &var);
|
|
if (S_OK == hr)
|
|
{
|
|
if (var.vt == VT_I4 && var.lVal != 0)
|
|
fCompExist = TRUE;
|
|
}
|
|
|
|
ULONG ulNumProp;
|
|
|
|
//
|
|
// Get display attribute track property range
|
|
//
|
|
Interface<IEnumTfRanges> enumProp;
|
|
Interface<ITfReadOnlyProperty> prop;
|
|
if (FAILED(GetDisplayAttributeTrackPropertyRange(ec, ic.GetPtr(), range, prop, enumProp, &ulNumProp))) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
// use text range for get text
|
|
Interface<ITfRange> textRange;
|
|
if (FAILED(range->Clone(textRange)))
|
|
return E_FAIL;
|
|
|
|
// use text range for gap text (no property range).
|
|
Interface<ITfRange> gap_range;
|
|
if (FAILED(range->Clone(gap_range)))
|
|
return E_FAIL;
|
|
|
|
|
|
ITfRange* pPropRange = NULL;
|
|
while (enumProp->Next(1, &pPropRange, NULL) == S_OK) {
|
|
|
|
// pick up the gap up to the next property
|
|
gap_range->ShiftEndToRange(ec, pPropRange, TF_ANCHOR_START);
|
|
|
|
//
|
|
// GAP range
|
|
//
|
|
no_display_attribute_range->CompareStart(ec,
|
|
gap_range,
|
|
TF_ANCHOR_START,
|
|
&result_comp);
|
|
_GetTextAndAttributeGapRange(pLibTLS,
|
|
ec,
|
|
imc,
|
|
gap_range,
|
|
result_comp,
|
|
CompStr, CompAttr, CompClause, CompGuid,
|
|
ResultStr, ResultClause);
|
|
|
|
//
|
|
// Get display attribute data if some GUID_ATOM exist.
|
|
//
|
|
TF_DISPLAYATTRIBUTE da;
|
|
TfGuidAtom guidatom = TF_INVALID_GUIDATOM;
|
|
|
|
GetDisplayAttributeData(pLibTLS, ec, prop, pPropRange, &da, &guidatom, ulNumProp);
|
|
|
|
|
|
//
|
|
// Property range
|
|
//
|
|
no_display_attribute_range->CompareStart(ec,
|
|
pPropRange,
|
|
TF_ANCHOR_START,
|
|
&result_comp);
|
|
|
|
// Adjust GAP range's start anchor to the end of proprty range.
|
|
gap_range->ShiftStartToRange(ec, pPropRange, TF_ANCHOR_END);
|
|
|
|
//
|
|
// Get reading string from property.
|
|
//
|
|
if (fCompExist == TRUE && result_comp <= 0)
|
|
GetReadingString(ec, ic, pPropRange, CompReadStr, CompReadClause);
|
|
else
|
|
GetReadingString(ec, ic, pPropRange, ResultReadStr, ResultReadClause);
|
|
|
|
//
|
|
// Get property text
|
|
//
|
|
_GetTextAndAttributePropertyRange(pLibTLS,
|
|
ec,
|
|
imc,
|
|
pPropRange,
|
|
fCompExist,
|
|
result_comp,
|
|
bInWriteSession,
|
|
da,
|
|
guidatom,
|
|
CompStr, CompAttr, CompClause, CompGuid,
|
|
ResultStr, ResultClause);
|
|
|
|
SafeReleaseClear(pPropRange);
|
|
|
|
} // while
|
|
|
|
// the last non-attr
|
|
textRange->ShiftStartToRange(ec, gap_range, TF_ANCHOR_START);
|
|
textRange->ShiftEndToRange(ec, range, TF_ANCHOR_END);
|
|
|
|
LONG ulClausePos = 0;
|
|
BOOL fEmpty;
|
|
while (textRange->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty)
|
|
{
|
|
WCHAR wstr0[256 + 1];
|
|
ULONG ulcch0 = ARRAYSIZE(wstr0) - 1;
|
|
textRange->GetText(ec, TF_TF_MOVESTART, wstr0, ulcch0, &ulcch0);
|
|
|
|
TfGuidAtom guidatom;
|
|
guidatom = TF_INVALID_GUIDATOM;
|
|
|
|
TF_DISPLAYATTRIBUTE da;
|
|
da.bAttr = TF_ATTR_INPUT;
|
|
|
|
CompGuid.AddCompData(guidatom, ulcch0);
|
|
CompAttr.AddCompData(_ConvertAttributeToImm32(da.bAttr), ulcch0);
|
|
CompStr.AddCompData(wstr0, ulcch0);
|
|
ulClausePos += ulcch0;
|
|
}
|
|
|
|
if (ulClausePos) {
|
|
DWORD last_clause;
|
|
if (CompClause.GetSize() > 0) {
|
|
last_clause = CompClause[ CompClause.GetSize() - 1 ];
|
|
CompClause.Add( last_clause + ulClausePos );
|
|
}
|
|
}
|
|
textRange->Collapse(ec, TF_ANCHOR_END);
|
|
|
|
range->Release();
|
|
*(ITfRange **)(range) = NULL;
|
|
|
|
} // out-most while for GUID_PROP_COMPOSING
|
|
|
|
//
|
|
// Fix up empty clause
|
|
//
|
|
if (CompClause.GetSize() <= 1)
|
|
CompClause.RemoveAll();
|
|
if (ResultClause.GetSize() <= 1)
|
|
ResultClause.RemoveAll();
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Retrieve text from gap range
|
|
//
|
|
|
|
HRESULT
|
|
ImmIfEditSessionCallBack::_GetTextAndAttributeGapRange(
|
|
LIBTHREAD *pLibTLS,
|
|
TfEditCookie ec,
|
|
IMCLock& imc,
|
|
Interface<ITfRange>& gap_range,
|
|
LONG result_comp,
|
|
CWCompString& CompStr,
|
|
CWCompAttribute& CompAttr,
|
|
CWCompClause& CompClause,
|
|
CWCompTfGuidAtom& CompGuid,
|
|
CWCompString& ResultStr,
|
|
CWCompClause& ResultClause
|
|
)
|
|
{
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
ASSERT(_pAImeContext != NULL);
|
|
|
|
TfGuidAtom guidatom;
|
|
guidatom = TF_INVALID_GUIDATOM;
|
|
|
|
TF_DISPLAYATTRIBUTE da;
|
|
da.bAttr = TF_ATTR_INPUT;
|
|
|
|
ULONG ulClausePos = 0;
|
|
BOOL fEmpty;
|
|
WCHAR wstr0[256 + 1];
|
|
ULONG ulcch0;
|
|
|
|
|
|
while (gap_range->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty)
|
|
{
|
|
Interface<ITfRange> backup_range;
|
|
if (FAILED(gap_range->Clone(backup_range)))
|
|
return E_FAIL;
|
|
|
|
//
|
|
// Retrieve gap text if there exist.
|
|
//
|
|
ulcch0 = ARRAYSIZE(wstr0) - 1;
|
|
if (FAILED(gap_range->GetText(ec,
|
|
TF_TF_MOVESTART, // Move range to next after get text.
|
|
wstr0,
|
|
ulcch0, &ulcch0)))
|
|
return E_FAIL;
|
|
|
|
ulClausePos += ulcch0;
|
|
|
|
if (result_comp <= 0) {
|
|
CompGuid.AddCompData(guidatom, ulcch0);
|
|
CompAttr.AddCompData(_ConvertAttributeToImm32(da.bAttr), ulcch0);
|
|
CompStr.AddCompData(wstr0, ulcch0);
|
|
}
|
|
else {
|
|
ResultStr.AddCompData(wstr0, ulcch0);
|
|
if (_pAImeContext)
|
|
ClearTextInRange(ec, backup_range, _pAImeContext);
|
|
}
|
|
}
|
|
|
|
if (ulClausePos) {
|
|
DWORD last_clause;
|
|
if (result_comp <= 0) {
|
|
if (CompClause.GetSize() > 0) {
|
|
last_clause = CompClause[ CompClause.GetSize() - 1 ];
|
|
CompClause.Add( last_clause + ulClausePos );
|
|
}
|
|
}
|
|
else {
|
|
if (ResultClause.GetSize() > 0) {
|
|
last_clause = ResultClause[ ResultClause.GetSize() - 1 ];
|
|
ResultClause.Add( last_clause + ulClausePos );
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Retrieve text from property range
|
|
//
|
|
|
|
HRESULT
|
|
ImmIfEditSessionCallBack::_GetTextAndAttributePropertyRange(
|
|
LIBTHREAD *pLibTLS,
|
|
TfEditCookie ec,
|
|
IMCLock& imc,
|
|
ITfRange* pPropRange,
|
|
BOOL fCompExist,
|
|
LONG result_comp,
|
|
BOOL bInWriteSession,
|
|
TF_DISPLAYATTRIBUTE da,
|
|
TfGuidAtom guidatom,
|
|
CWCompString& CompStr,
|
|
CWCompAttribute& CompAttr,
|
|
CWCompClause& CompClause,
|
|
CWCompTfGuidAtom& CompGuid,
|
|
CWCompString& ResultStr,
|
|
CWCompClause& ResultClause
|
|
)
|
|
{
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
ASSERT(_pAImeContext != NULL);
|
|
|
|
ULONG ulClausePos = 0;
|
|
BOOL fEmpty;
|
|
WCHAR wstr0[256 + 1];
|
|
ULONG ulcch0;
|
|
|
|
while (pPropRange->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty)
|
|
{
|
|
Interface<ITfRange> backup_range;
|
|
if (FAILED(pPropRange->Clone(backup_range)))
|
|
return E_FAIL;
|
|
|
|
//
|
|
// Retrieve property text if there exist.
|
|
//
|
|
ulcch0 = ARRAYSIZE(wstr0) - 1;
|
|
if (FAILED(pPropRange->GetText(ec,
|
|
TF_TF_MOVESTART, // Move range to next after get text.
|
|
wstr0,
|
|
ulcch0, &ulcch0)))
|
|
return E_FAIL;
|
|
|
|
ulClausePos += ulcch0; // we only need to addup the char position for clause info
|
|
|
|
// see if there is a valid disp attribute
|
|
if (fCompExist == TRUE && result_comp <= 0)
|
|
{
|
|
if (guidatom == TF_INVALID_GUIDATOM) {
|
|
da.bAttr = TF_ATTR_INPUT;
|
|
}
|
|
CompGuid.AddCompData(guidatom, ulcch0);
|
|
CompAttr.AddCompData(_ConvertAttributeToImm32(da.bAttr), ulcch0);
|
|
CompStr.AddCompData(wstr0, ulcch0);
|
|
}
|
|
else if (bInWriteSession)
|
|
{
|
|
// if there's no disp attribute attached, it probably means
|
|
// the part of string is finalized.
|
|
//
|
|
ResultStr.AddCompData(wstr0, ulcch0);
|
|
|
|
// it was a 'determined' string
|
|
// so the doc has to shrink
|
|
//
|
|
if (_pAImeContext)
|
|
ClearTextInRange(ec, backup_range, _pAImeContext);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Prevent infinite loop
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ulClausePos) {
|
|
DWORD last_clause;
|
|
if (fCompExist == TRUE && result_comp <= 0) {
|
|
if (CompClause.GetSize() > 0) {
|
|
last_clause = CompClause[ CompClause.GetSize() - 1 ];
|
|
CompClause.Add( last_clause + ulClausePos );
|
|
}
|
|
}
|
|
else if (result_comp == 0) {
|
|
//
|
|
// Copy CompClause data to ResultClause
|
|
//
|
|
CompClauseToResultClause(imc, ResultClause, ulcch0);
|
|
}
|
|
else {
|
|
if (ResultClause.GetSize() > 0) {
|
|
last_clause = ResultClause[ ResultClause.GetSize() - 1 ];
|
|
ResultClause.Add( last_clause + ulClausePos );
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ImmIfEditSessionCallBack::_GetNoDisplayAttributeRange(
|
|
LIBTHREAD *pLibTLS,
|
|
TfEditCookie ec,
|
|
Interface_Attach<ITfContext>& ic,
|
|
Interface<ITfRange>& rangeIn,
|
|
Interface<ITfRange>& no_display_attribute_range
|
|
)
|
|
{
|
|
Interface<ITfProperty> propComp;
|
|
Interface<IEnumTfRanges> enumComp;
|
|
VARIANT var;
|
|
|
|
HRESULT hr = ic->GetProperty(GUID_PROP_COMPOSING, propComp);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = propComp->EnumRanges(ec, enumComp, rangeIn);
|
|
}
|
|
else
|
|
return hr;
|
|
|
|
ITfRange *pRange;
|
|
|
|
while(enumComp->Next(1, &pRange, NULL) == S_OK)
|
|
{
|
|
|
|
BOOL fCompExist = FALSE;
|
|
|
|
hr = propComp->GetValue(ec, pRange, &var);
|
|
if (S_OK == hr)
|
|
{
|
|
if (var.vt == VT_I4 && var.lVal != 0)
|
|
fCompExist = TRUE;
|
|
}
|
|
|
|
if (!fCompExist) {
|
|
|
|
// Adjust GAP range's start anchor to the end of proprty range.
|
|
no_display_attribute_range->ShiftStartToRange(ec, pRange, TF_ANCHOR_START);
|
|
}
|
|
pRange->Release();
|
|
}
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfEditSession
|
|
|
|
ImmIfEditSession::ImmIfEditSession(
|
|
ESCB escb,
|
|
TfClientId tid,
|
|
Interface_Attach<ImmIfIME> ImmIfIme,
|
|
IMCLock& imc
|
|
) : m_ImmIfIme(ImmIfIme), m_ic(ImmIfIme->GetInputContext(imc)), m_tid(tid),
|
|
m_state((HIMC)imc)
|
|
{
|
|
_Init(escb);
|
|
}
|
|
|
|
ImmIfEditSession::ImmIfEditSession(
|
|
ESCB escb,
|
|
TfClientId tid,
|
|
Interface_Attach<ImmIfIME> ImmIfIme,
|
|
IMCLock& imc,
|
|
Interface_Attach<ITfContext> pic
|
|
) : m_ImmIfIme(ImmIfIme), m_ic(pic), m_tid(tid),
|
|
m_state((HIMC)imc)
|
|
{
|
|
_Init(escb);
|
|
}
|
|
|
|
void
|
|
ImmIfEditSession::_Init(
|
|
ESCB escb
|
|
)
|
|
{
|
|
m_pfnCallback = EditSessionCallBack;
|
|
m_cRef = 1;
|
|
|
|
m_ImmIfCallBack = NULL;
|
|
|
|
switch(escb) {
|
|
case ESCB_HANDLETHISKEY:
|
|
m_ImmIfCallBack = new ImmIfHandleThisKey;
|
|
break;
|
|
case ESCB_COMPCOMPLETE:
|
|
m_ImmIfCallBack = new ImmIfCompositionComplete;
|
|
break;
|
|
case ESCB_COMPCANCEL:
|
|
m_ImmIfCallBack = new ImmIfCompositionCancel;
|
|
break;
|
|
case ESCB_UPDATECOMPOSITIONSTRING:
|
|
m_ImmIfCallBack = new ImmIfUpdateCompositionString;
|
|
break;
|
|
case ESCB_REPLACEWHOLETEXT:
|
|
m_ImmIfCallBack = new ImmIfReplaceWholeText;
|
|
break;
|
|
case ESCB_RECONVERTSTRING:
|
|
m_ImmIfCallBack = new ImmIfReconvertString;
|
|
break;
|
|
case ESCB_CLEARDOCFEEDBUFFER:
|
|
m_ImmIfCallBack = new ImmIfClearDocFeedBuffer;
|
|
break;
|
|
case ESCB_GETTEXTANDATTRIBUTE:
|
|
m_ImmIfCallBack = new ImmIfGetTextAndAttribute;
|
|
break;
|
|
case ESCB_QUERYRECONVERTSTRING:
|
|
m_ImmIfCallBack = new ImmIfQueryReconvertString;
|
|
break;
|
|
case ESCB_CALCRANGEPOS:
|
|
m_ImmIfCallBack = new ImmIfCalcRangePos;
|
|
break;
|
|
case ESCB_GETSELECTION:
|
|
m_ImmIfCallBack = new ImmIfGetSelection;
|
|
break;
|
|
case ESCB_GET_READONLY_PROP_MARGIN:
|
|
m_ImmIfCallBack = new ImmIfGetReadOnlyPropMargin;
|
|
break;
|
|
case ESCB_GET_CURSOR_POSITION:
|
|
m_ImmIfCallBack = new ImmIfGetCursorPosition;
|
|
break;
|
|
case ESCB_GET_ALL_TEXT_RANGE:
|
|
m_ImmIfCallBack = new ImmIfGetAllTextRange;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ImmIfEditSession::~ImmIfEditSession(
|
|
)
|
|
{
|
|
if (m_ImmIfCallBack)
|
|
delete m_ImmIfCallBack;
|
|
}
|
|
|
|
bool
|
|
ImmIfEditSession::Valid(
|
|
)
|
|
{
|
|
return (m_ImmIfIme.Valid() && m_ic.Valid()) ? true : false;
|
|
}
|
|
|
|
|
|
// ImmIfEditSession::ITfEditCallback method
|
|
|
|
STDAPI
|
|
ImmIfEditSession::DoEditSession(
|
|
TfEditCookie ec
|
|
)
|
|
{
|
|
return m_pfnCallback(ec, this);
|
|
}
|
|
|
|
|
|
// ImmIfEditSession::IUnknown
|
|
|
|
STDAPI
|
|
ImmIfEditSession::QueryInterface(
|
|
REFIID riid,
|
|
void** ppvObj
|
|
)
|
|
{
|
|
*ppvObj = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
|
IsEqualIID(riid, IID_ITfEditSession)) {
|
|
*ppvObj = SAFECAST(this, ImmIfEditSession*);
|
|
}
|
|
|
|
if (*ppvObj) {
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDAPI_(ULONG)
|
|
ImmIfEditSession::AddRef(
|
|
)
|
|
{
|
|
return ++m_cRef;
|
|
}
|
|
|
|
STDAPI_(ULONG)
|
|
ImmIfEditSession::Release(
|
|
)
|
|
{
|
|
long cr;
|
|
|
|
cr = --m_cRef;
|
|
Assert(cr >= 0);
|
|
|
|
if (cr == 0) {
|
|
delete this;
|
|
}
|
|
|
|
return cr;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfHandleThisKey
|
|
|
|
HRESULT
|
|
ImmIfHandleThisKey::HandleThisKey(
|
|
TfEditCookie ec,
|
|
UINT uVKey,
|
|
HIMC hIMC,
|
|
Interface_Attach<ITfContext> ic,
|
|
Interface_Attach<ImmIfIME> ImmIfIme
|
|
)
|
|
{
|
|
Interface_TFSELECTION sel;
|
|
BOOL fEmpty;
|
|
ULONG cFetched;
|
|
|
|
HRESULT hr;
|
|
IMCLock imc(hIMC);
|
|
if (FAILED(hr=imc.GetResult()))
|
|
return hr;
|
|
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
ASSERT(_pAImeContext != NULL);
|
|
|
|
//
|
|
// Finalize the composition string
|
|
//
|
|
if (_pAImeContext &&
|
|
_pAImeContext->IsVKeyInKeyList(uVKey, EDIT_ID_FINALIZE)) {
|
|
return ImmIfIme->_CompComplete(imc);
|
|
}
|
|
|
|
//
|
|
// Keys that change its behavior on selection
|
|
//
|
|
|
|
if (ic->GetSelection(ec, TF_DEFAULT_SELECTION, 1, sel, &cFetched) != S_OK)
|
|
return E_FAIL;
|
|
|
|
sel->range->IsEmpty(ec, &fEmpty);
|
|
|
|
if (!fEmpty) {
|
|
//
|
|
// Selection is not empty.
|
|
//
|
|
|
|
switch (uVKey) {
|
|
case VK_BACK:
|
|
case VK_DELETE:
|
|
//
|
|
// Delete the selection
|
|
//
|
|
ClearTextInRange(ec, sel->range, _pAImeContext);
|
|
return ImmIfIme->_UpdateCompositionString();
|
|
}
|
|
}
|
|
|
|
switch (uVKey) {
|
|
case VK_BACK:
|
|
// Make selection
|
|
if (SUCCEEDED(ShiftSelectionToLeft(ec, sel->range, false))) {
|
|
// Clear the current selection
|
|
if (SUCCEEDED(ClearTextInRange(ec, sel->range, _pAImeContext))) {
|
|
return ImmIfIme->_UpdateCompositionString();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_DELETE:
|
|
if (SUCCEEDED(ShiftSelectionToRight(ec, sel->range, false))) {
|
|
if (SUCCEEDED(ClearTextInRange(ec, sel->range, _pAImeContext))) {
|
|
return ImmIfIme->_UpdateCompositionString();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_LEFT:
|
|
sel->style.ase = TF_AE_START;
|
|
|
|
if (::GetKeyState(VK_SHIFT) >= 0 &&
|
|
::GetKeyState(VK_CONTROL) >= 0) {
|
|
if (SUCCEEDED(ShiftSelectionToLeft(ec, sel->range)) &&
|
|
SUCCEEDED(ic->SetSelection(ec, 1, sel))) {
|
|
return ImmIfIme->_UpdateCompositionString();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_RIGHT:
|
|
sel->style.ase = TF_AE_END;
|
|
|
|
if (::GetKeyState(VK_SHIFT) >= 0 &&
|
|
::GetKeyState(VK_CONTROL) >= 0) {
|
|
if (SUCCEEDED(ShiftSelectionToRight(ec, sel->range)) &&
|
|
SUCCEEDED(ic->SetSelection(ec, 1, sel))) {
|
|
return ImmIfIme->_UpdateCompositionString();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT
|
|
ImmIfHandleThisKey::ShiftSelectionToLeft(
|
|
TfEditCookie ec,
|
|
ITfRange *range,
|
|
bool fShiftEnd
|
|
)
|
|
{
|
|
LONG cch;
|
|
|
|
if (SUCCEEDED(range->ShiftStart(ec, -1, &cch, NULL))) {
|
|
HRESULT hr = S_OK;
|
|
if (fShiftEnd) {
|
|
hr = range->Collapse(ec, TF_ANCHOR_START);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT
|
|
ImmIfHandleThisKey::ShiftSelectionToRight(
|
|
TfEditCookie ec,
|
|
ITfRange *range,
|
|
bool fShiftStart
|
|
)
|
|
{
|
|
LONG cch;
|
|
|
|
if (SUCCEEDED(range->ShiftEnd(ec, 1, &cch, NULL))) {
|
|
HRESULT hr = S_OK;
|
|
if (fShiftStart) {
|
|
hr = range->Collapse(ec, TF_ANCHOR_END);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfCompositionComplete
|
|
|
|
HRESULT
|
|
ImmIfCompositionComplete::CompComplete(
|
|
TfEditCookie ec,
|
|
HIMC hIMC,
|
|
BOOL fTerminateComp,
|
|
Interface_Attach<ITfContext> ic,
|
|
Interface_Attach<ImmIfIME> ImmIfIme
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (fTerminateComp == TRUE)
|
|
{
|
|
Interface<ITfContextOwnerCompositionServices> icocs;
|
|
|
|
hr = ic->QueryInterface(IID_ITfContextOwnerCompositionServices, (void **)icocs);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
icocs->TerminateComposition(NULL);
|
|
}
|
|
}
|
|
|
|
IMCLock imc(hIMC);
|
|
if (FAILED(hr = imc.GetResult()))
|
|
return hr;
|
|
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
if (_pAImeContext == NULL)
|
|
return E_FAIL;
|
|
|
|
ASSERT(_pAImeContext != NULL);
|
|
|
|
//
|
|
// Get the whole text, finalize it, and set empty string in TOM
|
|
//
|
|
Interface<ITfRange> start;
|
|
LONG cch;
|
|
|
|
if (SUCCEEDED(hr=GetAllTextRange(ec, ic, &start, &cch))) {
|
|
|
|
//
|
|
// If there is no string in TextStore and we havenot sent
|
|
// WM_IME_STARTCOMPOSITION, we don't have to do anything.
|
|
//
|
|
if (!cch) {
|
|
if (_pAImeContext && !(_pAImeContext->m_fStartComposition))
|
|
return S_OK;
|
|
}
|
|
|
|
LPWSTR wstr = new WCHAR[ cch + 1 ];
|
|
Interface<ITfProperty> prop;
|
|
//
|
|
// Get the whole text, finalize it, and erase the whole text.
|
|
//
|
|
if (SUCCEEDED(start->GetText(ec, TF_TF_IGNOREEND, wstr, (ULONG)cch, (ULONG*)&cch))) {
|
|
//
|
|
// Make Result String.
|
|
//
|
|
UINT cp = CP_ACP;
|
|
ImmIfIme->GetCodePageA(&cp);
|
|
|
|
CWCompString ResultStr(cp, hIMC, wstr, cch);
|
|
|
|
CWCompString ResultReadStr;
|
|
CWCompClause ResultReadClause;
|
|
CWCompClause ResultClause;
|
|
|
|
if (cch) {
|
|
|
|
//
|
|
// Get reading string from property.
|
|
//
|
|
GetReadingString(ec, ic, ResultReadStr, ResultReadClause);
|
|
|
|
|
|
//
|
|
// Copy CompClause data to ResultClause
|
|
//
|
|
CompClauseToResultClause(imc, ResultClause, cch);
|
|
}
|
|
|
|
#ifdef CICERO_4732
|
|
if (_pAImeContext && ! _pAImeContext->m_fInCompComplete)
|
|
{
|
|
//
|
|
// Prevent reentrance call of CPS_COMPLETE.
|
|
//
|
|
_pAImeContext->m_fInCompComplete = TRUE;
|
|
#endif
|
|
|
|
//
|
|
// set composition string
|
|
//
|
|
hr = _SetCompositionString(imc,
|
|
&ResultStr, &ResultClause,
|
|
&ResultReadStr, &ResultReadClause);
|
|
|
|
#ifdef CICERO_4732
|
|
_pAImeContext->m_fInCompComplete = FALSE;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Clear the TOM
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (_pAImeContext)
|
|
hr = ClearTextInRange(ec, start, _pAImeContext);
|
|
}
|
|
|
|
}
|
|
delete [] wstr;
|
|
}
|
|
else {
|
|
ImmIfIme->_CompCancel(imc);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfCompositionCancel
|
|
|
|
|
|
HRESULT
|
|
ImmIfCompositionCancel::CompCancel(
|
|
TfEditCookie ec,
|
|
HIMC hIMC,
|
|
Interface_Attach<ITfContext> ic
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
IMCLock imc(hIMC);
|
|
if (FAILED(hr = imc.GetResult()))
|
|
return hr;
|
|
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
if (_pAImeContext == NULL)
|
|
return E_FAIL;
|
|
|
|
ASSERT(_pAImeContext != NULL);
|
|
|
|
if (_pAImeContext &&
|
|
_pAImeContext->m_fStartComposition) {
|
|
IMCCLock<COMPOSITIONSTRING> comp(imc->hCompStr);
|
|
|
|
if (SUCCEEDED(hr = comp.GetResult())) {
|
|
TRANSMSG msg;
|
|
msg.message = WM_IME_COMPOSITION;
|
|
msg.wParam = (WPARAM)VK_ESCAPE;
|
|
msg.lParam = (LPARAM)(GCS_COMPREAD | GCS_COMP | GCS_CURSORPOS | GCS_DELTASTART);
|
|
if (_pAImeContext->m_pMessageBuffer)
|
|
_pAImeContext->m_pMessageBuffer->SetData(msg);
|
|
|
|
_pAImeContext->m_fStartComposition = FALSE;
|
|
|
|
msg.message = WM_IME_ENDCOMPOSITION;
|
|
msg.wParam = (WPARAM) 0;
|
|
msg.lParam = (LPARAM) 0;
|
|
if (_pAImeContext->m_pMessageBuffer)
|
|
_pAImeContext->m_pMessageBuffer->SetData(msg);
|
|
|
|
//
|
|
// Clear the text in Cicero TOM
|
|
//
|
|
Interface<ITfRange> range;
|
|
LONG l;
|
|
if (SUCCEEDED(GetAllTextRange(ec, ic, &range, &l))) {
|
|
hr = ClearTextInRange(ec, range, _pAImeContext);
|
|
}
|
|
}
|
|
|
|
imc.GenerateMessage();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfUpdateCompositionString
|
|
|
|
HRESULT
|
|
ImmIfUpdateCompositionString::UpdateCompositionString(
|
|
TfEditCookie ec,
|
|
HIMC hIMC,
|
|
Interface_Attach<ITfContext> ic,
|
|
DWORD dwDeltaStart,
|
|
Interface_Attach<ImmIfIME> ImmIfIme
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
IMCLock imc(hIMC);
|
|
if (FAILED(hr = imc.GetResult()))
|
|
return hr;
|
|
|
|
BOOL bInWriteSession;
|
|
if (FAILED(hr = ic->InWriteSession(ImmIfIme->GetClientId(), &bInWriteSession)))
|
|
return hr;
|
|
|
|
Interface<ITfRange> FullTextRange;
|
|
LONG lTextLength;
|
|
if (FAILED(hr=GetAllTextRange(ec, ic, &FullTextRange, &lTextLength)))
|
|
return hr;
|
|
|
|
Interface<ITfRange> InterimRange;
|
|
BOOL fInterim = FALSE;
|
|
if (FAILED(hr = _IsInterimSelection(ec, ic, &InterimRange, &fInterim)))
|
|
return hr;
|
|
|
|
if (fInterim) {
|
|
UINT cp = CP_ACP;
|
|
ImmIfIme->GetCodePageA(&cp);
|
|
|
|
return _MakeInterimString(ImmIfIme->_GetLibTLS(),
|
|
ec, imc, ic, FullTextRange,
|
|
InterimRange, lTextLength, cp,
|
|
bInWriteSession);
|
|
}
|
|
else {
|
|
return _MakeCompositionString(ImmIfIme->_GetLibTLS(),
|
|
ec, imc, ic, FullTextRange,
|
|
dwDeltaStart,
|
|
bInWriteSession);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImmIfUpdateCompositionString::_IsInterimSelection(
|
|
TfEditCookie ec,
|
|
Interface_Attach<ITfContext>& ic,
|
|
Interface<ITfRange>* pInterimRange,
|
|
BOOL *pfInterim
|
|
)
|
|
{
|
|
Interface_TFSELECTION sel;
|
|
ULONG cFetched;
|
|
|
|
*pfInterim = FALSE;
|
|
if (ic->GetSelection(ec, TF_DEFAULT_SELECTION, 1, sel, &cFetched) != S_OK)
|
|
{
|
|
// no selection. we can return S_OK.
|
|
return S_OK;
|
|
}
|
|
|
|
if (sel->style.fInterimChar) {
|
|
HRESULT hr;
|
|
if (FAILED(hr = sel->range->Clone(*pInterimRange)))
|
|
return hr;
|
|
|
|
*pfInterim = TRUE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImmIfUpdateCompositionString::_MakeCompositionString(
|
|
LIBTHREAD *pLibTLS,
|
|
TfEditCookie ec,
|
|
IMCLock& imc,
|
|
Interface_Attach<ITfContext>& ic,
|
|
Interface<ITfRange>& FullTextRange,
|
|
DWORD dwDeltaStart,
|
|
BOOL bInWriteSession
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
CWCompString CompStr;
|
|
CWCompAttribute CompAttr;
|
|
CWCompClause CompClause;
|
|
CWCompTfGuidAtom CompGuid;
|
|
CWCompCursorPos CompCursorPos;
|
|
CWCompDeltaStart CompDeltaStart;
|
|
CWCompString CompReadStr;
|
|
CWCompClause CompReadClause;
|
|
CWCompString ResultStr;
|
|
CWCompClause ResultClause;
|
|
CWCompString ResultReadStr;
|
|
CWCompClause ResultReadClause;
|
|
|
|
if (FAILED(hr = _GetTextAndAttribute(pLibTLS, ec, imc, ic, FullTextRange,
|
|
CompStr, CompAttr, CompClause,
|
|
CompGuid,
|
|
CompReadStr, CompReadClause,
|
|
ResultStr, ResultClause,
|
|
ResultReadStr, ResultReadClause,
|
|
bInWriteSession
|
|
))) {
|
|
return hr;
|
|
}
|
|
|
|
if (FAILED(hr = _GetCursorPosition(ec, imc, ic, CompCursorPos, CompAttr))) {
|
|
return hr;
|
|
}
|
|
|
|
if (FAILED(hr = _GetDeltaStart(CompDeltaStart, CompStr, dwDeltaStart))) {
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Clear the GUID attribute map array
|
|
//
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
if (_pAImeContext == NULL)
|
|
return E_FAIL;
|
|
|
|
ASSERT(_pAImeContext != NULL);
|
|
|
|
_pAImeContext->usGuidMapSize = 0;
|
|
memset(&_pAImeContext->aGuidMap, 0, sizeof _pAImeContext->aGuidMap);
|
|
|
|
BOOL bBufferOverflow = FALSE;
|
|
|
|
// handle result string
|
|
hr = _SetCompositionString(imc,
|
|
&CompStr, &CompAttr, &CompClause,
|
|
&CompCursorPos, &CompDeltaStart,
|
|
&CompGuid,
|
|
&bBufferOverflow,
|
|
&CompReadStr,
|
|
&ResultStr, &ResultClause,
|
|
&ResultReadStr, &ResultReadClause);
|
|
|
|
if (SUCCEEDED(hr) && bBufferOverflow) {
|
|
//
|
|
// Buffer overflow in COMPOSITIONSTRING.compstr[NMAXKEY],
|
|
// Then, Clear the TOM
|
|
//
|
|
//
|
|
// Get the whole text, finalize it, and set empty string in TOM
|
|
//
|
|
Interface<ITfRange> start;
|
|
LONG cch;
|
|
if (SUCCEEDED(hr=GetAllTextRange(ec, ic, &start, &cch))) {
|
|
hr = ClearTextInRange(ec, start, _pAImeContext);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImmIfUpdateCompositionString::_MakeInterimString(
|
|
LIBTHREAD *pLibTLS,
|
|
TfEditCookie ec,
|
|
IMCLock& imc,
|
|
Interface_Attach<ITfContext>& ic,
|
|
Interface<ITfRange>& FullTextRange,
|
|
Interface<ITfRange>& InterimRange,
|
|
LONG lTextLength,
|
|
UINT cp,
|
|
BOOL bInWriteSession
|
|
)
|
|
{
|
|
LONG lStartResult;
|
|
LONG lEndResult;
|
|
|
|
FullTextRange->CompareStart(ec, InterimRange, TF_ANCHOR_START, &lStartResult);
|
|
if (lStartResult > 0) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
FullTextRange->CompareEnd(ec, InterimRange, TF_ANCHOR_END, &lEndResult);
|
|
if (lEndResult < 0) {
|
|
return E_FAIL;
|
|
}
|
|
if (lEndResult > 1) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CWInterimString InterimStr(cp, (HIMC)imc);
|
|
|
|
if (lStartResult < 0) {
|
|
//
|
|
// Make result string.
|
|
//
|
|
#if 0
|
|
BOOL fEqual;
|
|
do {
|
|
LONG cch;
|
|
FullTextRange->ShiftEnd(ec, -1, &cch, NULL);
|
|
if (cch == 0) {
|
|
return E_FAIL;
|
|
}
|
|
lTextLength -= abs(cch);
|
|
FullTextRange->IsEqualEnd(ec, InterimRange, TF_ANCHOR_START, &fEqual);
|
|
} while(! fEqual);
|
|
#endif
|
|
if (FAILED(hr=FullTextRange->ShiftEndToRange(ec, InterimRange, TF_ANCHOR_START)))
|
|
return hr;
|
|
|
|
//
|
|
// Interim char assume 1 char length.
|
|
// Full text length - 1 means result string length.
|
|
//
|
|
lTextLength --;
|
|
ASSERT(lTextLength > 0);
|
|
|
|
if (lTextLength > 0) {
|
|
|
|
LPWSTR wstr = new WCHAR[ lTextLength + 1 ];
|
|
|
|
//
|
|
// Get the result text, finalize it, and erase the result text.
|
|
//
|
|
if (SUCCEEDED(FullTextRange->GetText(ec, TF_TF_IGNOREEND, wstr, (ULONG)lTextLength, (ULONG*)&lTextLength))) {
|
|
//
|
|
// Clear the TOM
|
|
//
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
ASSERT(_pAImeContext != NULL);
|
|
|
|
if (_pAImeContext) {
|
|
if (SUCCEEDED(hr = ClearTextInRange(ec, FullTextRange, _pAImeContext))) {
|
|
InterimStr.WriteCompData(wstr, lTextLength);
|
|
}
|
|
}
|
|
}
|
|
delete [] wstr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make interim character
|
|
//
|
|
CWCompString CompStr;
|
|
CWCompAttribute CompAttr;
|
|
CWCompClause CompClause;
|
|
CWCompTfGuidAtom CompGuid;
|
|
CWCompString CompReadStr;
|
|
CWCompClause CompReadClause;
|
|
CWCompString ResultStr;
|
|
CWCompClause ResultClause;
|
|
CWCompString ResultReadStr;
|
|
CWCompClause ResultReadClause;
|
|
|
|
if (FAILED(hr = _GetTextAndAttribute(pLibTLS, ec, imc, ic, InterimRange,
|
|
CompStr, CompAttr, CompClause,
|
|
CompGuid,
|
|
CompReadStr, CompReadClause,
|
|
ResultStr, ResultClause,
|
|
ResultReadStr, ResultReadClause,
|
|
bInWriteSession
|
|
))) {
|
|
return hr;
|
|
}
|
|
|
|
WCHAR ch = L'\0';
|
|
BYTE attr = 0;
|
|
if (CompStr.GetSize() > 0) {
|
|
CompStr.ReadCompData(&ch, 1);
|
|
}
|
|
if (CompAttr.GetSize() > 0) {
|
|
CompAttr.ReadCompData(&attr, 1);
|
|
}
|
|
|
|
InterimStr.WriteInterimChar(ch, attr);
|
|
hr = _SetCompositionString(imc, &InterimStr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Get delta start
|
|
//
|
|
HRESULT
|
|
ImmIfUpdateCompositionString::_GetDeltaStart(
|
|
CWCompDeltaStart& CompDeltaStart,
|
|
CWCompString& CompStr,
|
|
DWORD dwDeltaStart
|
|
)
|
|
{
|
|
if (dwDeltaStart < (DWORD)CompStr.GetSize())
|
|
CompDeltaStart.Set(dwDeltaStart); // Set COMPOSITIONSTRING.dwDeltaStart.
|
|
else
|
|
CompDeltaStart.Set(0);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfReplaceWholeText
|
|
|
|
HRESULT
|
|
ImmIfReplaceWholeText::ReplaceWholeText(
|
|
TfEditCookie ec,
|
|
HIMC hIMC,
|
|
Interface_Attach<ITfContext> ic,
|
|
CWCompString* lpwCompStr
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
IMCLock imc(hIMC);
|
|
if (FAILED(hr = imc.GetResult()))
|
|
return hr;
|
|
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
ASSERT(_pAImeContext != NULL);
|
|
|
|
DWORD dwSize = (DWORD)lpwCompStr->GetSize();
|
|
LPWSTR lpszComp = new WCHAR[dwSize + 1];
|
|
|
|
if (!lpszComp)
|
|
return hr;
|
|
|
|
lpwCompStr->ReadCompData(lpszComp, dwSize + 1);
|
|
lpszComp[dwSize] = L'\0';
|
|
|
|
Interface<ITfRange> whole;
|
|
LONG cch;
|
|
if (SUCCEEDED(hr=GetAllTextRange(ec, ic, &whole, &cch))) {
|
|
if (_pAImeContext)
|
|
hr = SetTextInRange(ec, whole, lpszComp, 0, _pAImeContext);
|
|
}
|
|
delete [] lpszComp;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void SetReadOnlyRange(TfEditCookie ec,
|
|
Interface_Attach<ITfContext> ic,
|
|
ITfRange *range,
|
|
BOOL fSet)
|
|
{
|
|
ITfProperty *prop;
|
|
if (SUCCEEDED(ic->GetProperty(GUID_PROP_MSIMTF_READONLY, &prop)))
|
|
{
|
|
if (fSet)
|
|
{
|
|
VARIANT var;
|
|
var.vt = VT_I4;
|
|
var.lVal = 1;
|
|
prop->SetValue(ec, range, &var);
|
|
}
|
|
else
|
|
{
|
|
prop->Clear(ec, range);
|
|
}
|
|
prop->Release();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfReconvertString
|
|
|
|
HRESULT
|
|
ImmIfReconvertString::ReconvertString(
|
|
TfEditCookie ec,
|
|
HIMC hIMC,
|
|
Interface_Attach<ITfContext> ic,
|
|
Interface<ITfRange>* rangeSrc,
|
|
BOOL fDocFeedOnly,
|
|
CWReconvertString* lpwReconvStr,
|
|
Interface_Attach<ImmIfIME> ImmIfIme
|
|
)
|
|
|
|
/*+++
|
|
|
|
LPRECONVERTSTRING structure:
|
|
+00 DWORD dwSize // Sizeof data (include this structure size) with byte count.
|
|
+04 DWORD dwVersion
|
|
+08 DWORD dwStrLen // String length with character count.
|
|
+0C DWORD dwStrOffset // Offset from start of this structure with byte count.
|
|
+10 DWORD dwCompStrLen // Comp Str length with character count.
|
|
+14 DWORD dwCompStrOffset // Offset from this->dwStrOffset with byte count.
|
|
+18 DWORD dwTargetStrLen // Target Str length with character count.
|
|
+1C DWORD dwTargetStrOffset // Offset from this->dwStrOffset with byte count.
|
|
+20
|
|
|
|
---*/
|
|
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IMCLock imc(hIMC);
|
|
if (FAILED(hr = imc.GetResult()))
|
|
return hr;
|
|
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
if (_pAImeContext == NULL)
|
|
return E_FAIL;
|
|
|
|
DWORD dwLen;
|
|
dwLen = lpwReconvStr->ReadCompData();
|
|
if (dwLen) {
|
|
LPRECONVERTSTRING lpReconvertString;
|
|
lpReconvertString = (LPRECONVERTSTRING) new BYTE[ dwLen ];
|
|
|
|
if (lpReconvertString == NULL)
|
|
return hr;
|
|
|
|
lpwReconvStr->ReadCompData(lpReconvertString, dwLen);
|
|
|
|
|
|
if (lpReconvertString->dwStrLen) {
|
|
hr = ic->GetStart(ec, *rangeSrc);
|
|
if (SUCCEEDED(hr)) {
|
|
LONG cch;
|
|
hr = (*rangeSrc)->ShiftEnd(ec, LONG_MAX, &cch, NULL);
|
|
SetReadOnlyRange(ec, ic, *rangeSrc, FALSE);
|
|
BOOL fSkipSetText = FALSE;
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
Interface<ITfRange> rangeOrgSelection;
|
|
|
|
if (lpReconvertString->dwCompStrLen)
|
|
{
|
|
WCHAR *pwstr = NULL;
|
|
ULONG ul = 0;
|
|
Interface<ITfRange> rangeTmp;
|
|
|
|
if (SUCCEEDED((*rangeSrc)->Clone(rangeTmp)))
|
|
{
|
|
UINT uSize = lpReconvertString->dwCompStrLen + 2;
|
|
pwstr = new WCHAR[uSize + 1];
|
|
if (pwstr)
|
|
{
|
|
ULONG ulcch;
|
|
rangeTmp->GetText(ec, 0, pwstr, uSize, &ulcch);
|
|
|
|
if ((lpReconvertString->dwCompStrLen == ulcch) &&
|
|
!memcmp(((LPBYTE)lpReconvertString +
|
|
lpReconvertString->dwStrOffset +
|
|
lpReconvertString->dwCompStrOffset),
|
|
pwstr,
|
|
lpReconvertString->dwCompStrLen * sizeof(WCHAR)))
|
|
fSkipSetText = TRUE;
|
|
|
|
delete [] pwstr;
|
|
}
|
|
}
|
|
|
|
if (!fSkipSetText && fDocFeedOnly)
|
|
{
|
|
TraceMsg(TF_WARNING, "ImmIfReconvertString::ReconvertString the current text store does not match with the string from App.");
|
|
goto Exit;
|
|
}
|
|
|
|
if (!fSkipSetText)
|
|
hr = SetTextInRange(ec,
|
|
*rangeSrc,
|
|
(LPWSTR)((LPBYTE)lpReconvertString +
|
|
lpReconvertString->dwStrOffset +
|
|
lpReconvertString->dwCompStrOffset),
|
|
lpReconvertString->dwCompStrLen,
|
|
_pAImeContext);
|
|
else
|
|
{
|
|
GetSelectionSimple(ec, ic.GetPtr(), rangeOrgSelection);
|
|
hr = S_OK;
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// set disp attribute for the reconvert
|
|
//
|
|
if (S_OK == hr)
|
|
{
|
|
|
|
ITfProperty *pProp = NULL;
|
|
|
|
hr = ic->GetProperty(GUID_PROP_ATTRIBUTE, &pProp);
|
|
if (S_OK == hr)
|
|
{
|
|
SetAttrPropertyData(ImmIfIme->_GetLibTLS(),
|
|
ec,
|
|
pProp,
|
|
*rangeSrc,
|
|
GUID_ATTR_MSIMTF_INPUT);
|
|
}
|
|
pProp->Release();
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
BOOL fEmpty;
|
|
|
|
if (SUCCEEDED((*rangeSrc)->IsEmpty(ec, &fEmpty)) && !fEmpty)
|
|
{
|
|
if (fDocFeedOnly)
|
|
{
|
|
TraceMsg(TF_WARNING, "ImmIfReconvertString::ReconvertString the current text store does not match with the string from App.");
|
|
goto Exit;
|
|
}
|
|
|
|
hr = ClearTextInRange(ec, *rangeSrc, _pAImeContext);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// set read only string
|
|
//
|
|
if (lpReconvertString->dwCompStrOffset)
|
|
{
|
|
//
|
|
// keep rangeSrc and rangeOrgSelection
|
|
//
|
|
(*rangeSrc)->SetGravity(ec,
|
|
TF_GRAVITY_FORWARD,
|
|
TF_GRAVITY_FORWARD);
|
|
|
|
if (rangeOrgSelection.Valid())
|
|
rangeOrgSelection->SetGravity(ec,
|
|
TF_GRAVITY_FORWARD,
|
|
TF_GRAVITY_FORWARD);
|
|
Interface<ITfRange> rangeStart;
|
|
if (SUCCEEDED((*rangeSrc)->Clone(rangeStart)))
|
|
{
|
|
if (SUCCEEDED(rangeStart->Collapse(ec, TF_ANCHOR_START)))
|
|
{
|
|
rangeStart->SetGravity(ec,
|
|
TF_GRAVITY_BACKWARD,
|
|
TF_GRAVITY_FORWARD);
|
|
hr = SetTextInRange(ec,
|
|
rangeStart,
|
|
(LPWSTR)((LPBYTE)lpReconvertString +
|
|
lpReconvertString->dwStrOffset),
|
|
lpReconvertString->dwCompStrOffset / sizeof(WCHAR),
|
|
_pAImeContext);
|
|
|
|
if (SUCCEEDED(hr))
|
|
SetReadOnlyRange(ec, ic, rangeStart, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if ((lpReconvertString->dwCompStrOffset + lpReconvertString->dwCompStrLen * sizeof(WCHAR)) < lpReconvertString->dwStrLen * sizeof(WCHAR))
|
|
{
|
|
//
|
|
// keep rangeSrc and rangeOrgSelection
|
|
//
|
|
(*rangeSrc)->SetGravity(ec,
|
|
TF_GRAVITY_BACKWARD,
|
|
TF_GRAVITY_BACKWARD);
|
|
|
|
if (rangeOrgSelection.Valid())
|
|
rangeOrgSelection->SetGravity(ec,
|
|
TF_GRAVITY_BACKWARD,
|
|
TF_GRAVITY_BACKWARD);
|
|
|
|
Interface<ITfRange> rangeEnd;
|
|
if (SUCCEEDED((*rangeSrc)->Clone(rangeEnd)))
|
|
{
|
|
if (SUCCEEDED(rangeEnd->Collapse(ec, TF_ANCHOR_END)))
|
|
{
|
|
rangeEnd->SetGravity(ec,
|
|
TF_GRAVITY_BACKWARD,
|
|
TF_GRAVITY_FORWARD);
|
|
|
|
hr = SetTextInRange(ec,
|
|
rangeEnd,
|
|
(LPWSTR)((LPBYTE)lpReconvertString +
|
|
lpReconvertString->dwStrOffset +
|
|
lpReconvertString->dwCompStrOffset +
|
|
(lpReconvertString->dwCompStrLen * sizeof(WCHAR))),
|
|
((lpReconvertString->dwStrLen * sizeof(WCHAR)) -
|
|
(lpReconvertString->dwCompStrOffset +
|
|
(lpReconvertString->dwCompStrLen * sizeof(WCHAR)))) / sizeof(WCHAR),
|
|
_pAImeContext);
|
|
if (SUCCEEDED(hr))
|
|
SetReadOnlyRange(ec, ic, rangeEnd, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
(*rangeSrc)->SetGravity(ec,
|
|
TF_GRAVITY_FORWARD,
|
|
TF_GRAVITY_BACKWARD);
|
|
|
|
|
|
//
|
|
// we just set a selection to the target string.
|
|
//
|
|
Interface<ITfRange> range;
|
|
if (fSkipSetText)
|
|
{
|
|
if (rangeOrgSelection.Valid())
|
|
{
|
|
//
|
|
//
|
|
//
|
|
TF_SELECTION sel;
|
|
sel.range = rangeOrgSelection;
|
|
sel.style.ase = TF_AE_NONE;
|
|
sel.style.fInterimChar = FALSE;
|
|
ic->SetSelection(ec, 1, &sel);
|
|
}
|
|
}
|
|
else if (SUCCEEDED((*rangeSrc)->Clone(range)))
|
|
{
|
|
LONG cchStart;
|
|
LONG cchEnd;
|
|
|
|
if (lpReconvertString->dwTargetStrOffset == 0 &&
|
|
lpReconvertString->dwTargetStrLen == 0 ) {
|
|
cchStart = lpReconvertString->dwCompStrOffset / sizeof(WCHAR);
|
|
cchEnd = lpReconvertString->dwCompStrOffset / sizeof(WCHAR) + lpReconvertString->dwCompStrLen;
|
|
}
|
|
else {
|
|
cchStart = (lpReconvertString->dwTargetStrOffset - lpReconvertString->dwCompStrOffset) / sizeof(WCHAR);
|
|
cchEnd = (lpReconvertString->dwTargetStrOffset - lpReconvertString->dwCompStrOffset) / sizeof(WCHAR) + lpReconvertString->dwTargetStrLen;
|
|
// cchStart = (lpReconvertString->dwTargetStrOffset) / sizeof(WCHAR);
|
|
// cchEnd = (lpReconvertString->dwTargetStrOffset) / sizeof(WCHAR) + lpReconvertString->dwTargetStrLen;
|
|
}
|
|
|
|
range->Collapse(ec, TF_ANCHOR_START);
|
|
|
|
//
|
|
// shift end first then shift start.
|
|
//
|
|
if ((SUCCEEDED(range->ShiftEnd(ec,
|
|
cchEnd,
|
|
&cch,
|
|
NULL))) &&
|
|
(SUCCEEDED(range->ShiftStart(ec,
|
|
cchStart,
|
|
&cch,
|
|
NULL))))
|
|
{
|
|
//
|
|
//
|
|
//
|
|
TF_SELECTION sel;
|
|
sel.range = range;
|
|
sel.style.ase = TF_AE_NONE;
|
|
sel.style.fInterimChar = FALSE;
|
|
ic->SetSelection(ec, 1, &sel);
|
|
}
|
|
}
|
|
|
|
//
|
|
// it's time to generate WM_IME_COMPOSITION.
|
|
//
|
|
ImmIfIme->_UpdateCompositionString();
|
|
}
|
|
}
|
|
}
|
|
Exit:
|
|
delete [] lpReconvertString;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfClearDocFeedBuffer
|
|
|
|
HRESULT
|
|
ImmIfClearDocFeedBuffer::ClearDocFeedBuffer(
|
|
TfEditCookie ec,
|
|
HIMC hIMC,
|
|
Interface_Attach<ITfContext> ic,
|
|
Interface_Attach<ImmIfIME> ImmIfIme
|
|
)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IMCLock imc(hIMC);
|
|
if (FAILED(hr = imc.GetResult()))
|
|
return hr;
|
|
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
if (_pAImeContext == NULL)
|
|
return E_FAIL;
|
|
|
|
ITfRange *rangeFull = NULL;
|
|
ITfProperty *prop;
|
|
ITfRange *rangeTmp;
|
|
LONG cch;
|
|
|
|
//
|
|
// Create the range that covers all the text.
|
|
//
|
|
if (FAILED(hr=ic->GetStart(ec, &rangeFull)))
|
|
return hr;
|
|
|
|
if (FAILED(hr=rangeFull->ShiftEnd(ec, LONG_MAX, &cch, NULL)))
|
|
return hr;
|
|
|
|
//
|
|
// find the first non readonly range in the text store.
|
|
//
|
|
if (SUCCEEDED(ic->GetProperty(GUID_PROP_MSIMTF_READONLY, &prop)))
|
|
{
|
|
IEnumTfRanges *enumranges;
|
|
if (SUCCEEDED(prop->EnumRanges(ec, &enumranges, rangeFull)))
|
|
{
|
|
while (enumranges->Next(1, &rangeTmp, NULL) == S_OK)
|
|
{
|
|
VARIANT var;
|
|
QuickVariantInit(&var);
|
|
prop->GetValue(ec, rangeTmp, &var);
|
|
if ((var.vt == VT_I4) && (var.lVal != 0))
|
|
{
|
|
prop->Clear(ec, rangeTmp);
|
|
ClearTextInRange(ec, rangeTmp, _pAImeContext);
|
|
}
|
|
rangeTmp->Release();
|
|
}
|
|
enumranges->Release();
|
|
}
|
|
prop->Release();
|
|
}
|
|
|
|
|
|
rangeFull->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfGetTextAndAttribute
|
|
|
|
HRESULT
|
|
ImmIfGetTextAndAttribute::GetTextAndAttribute(
|
|
TfEditCookie ec,
|
|
HIMC hIMC,
|
|
Interface_Attach<ITfContext> ic,
|
|
CWCompString* lpwCompString,
|
|
CWCompAttribute* lpwCompAttribute,
|
|
Interface_Attach<ImmIfIME> ImmIfIme
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
LIBTHREAD *pLibTLS = ImmIfIme->_GetLibTLS();
|
|
|
|
IMCLock imc(hIMC);
|
|
if (FAILED(hr = imc.GetResult()))
|
|
return hr;
|
|
|
|
BOOL bInWriteSession;
|
|
if (FAILED(hr = ic->InWriteSession(ImmIfIme->GetClientId(), &bInWriteSession)))
|
|
return hr;
|
|
|
|
Interface<ITfRange> FullTextRange;
|
|
LONG lTextLength;
|
|
if (FAILED(hr=GetAllTextRange(ec, ic, &FullTextRange, &lTextLength)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = _GetTextAndAttribute(pLibTLS, ec, imc, ic,
|
|
FullTextRange,
|
|
*lpwCompString,
|
|
*lpwCompAttribute,
|
|
bInWriteSession))) {
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfQueryReconvertString
|
|
|
|
HRESULT
|
|
ImmIfQueryReconvertString::QueryReconvertString(
|
|
TfEditCookie ec,
|
|
HIMC hIMC,
|
|
Interface_Attach<ITfContext> ic,
|
|
Interface<ITfRange>* rangeQuery,
|
|
CWReconvertString* lpwReconvStr,
|
|
Interface_Attach<ImmIfIME> ImmIfIme
|
|
)
|
|
|
|
/*+++
|
|
|
|
LPRECONVERTSTRING structure:
|
|
+00 DWORD dwSize // Sizeof data (include this structure size) with byte count.
|
|
+04 DWORD dwVersion
|
|
+08 DWORD dwStrLen // String length with character count.
|
|
+0C DWORD dwStrOffset // Offset from start of this structure with byte count.
|
|
+10 DWORD dwCompStrLen // Comp Str length with character count.
|
|
+14 DWORD dwCompStrOffset // Offset from this->dwStrOffset with byte count.
|
|
+18 DWORD dwTargetStrLen // Target Str length with character count.
|
|
+1C DWORD dwTargetStrOffset // Offset from this->dwStrOffset with byte count.
|
|
+20
|
|
|
|
---*/
|
|
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IMCLock imc(hIMC);
|
|
if (FAILED(hr = imc.GetResult()))
|
|
return hr;
|
|
|
|
CAImeContext* _pAImeContext = imc->m_pAImeContext;
|
|
if (_pAImeContext == NULL)
|
|
return E_FAIL;
|
|
|
|
DWORD dwLen;
|
|
dwLen = lpwReconvStr->ReadCompData();
|
|
if (dwLen) {
|
|
LPRECONVERTSTRING lpReconvertString;
|
|
lpReconvertString = (LPRECONVERTSTRING) new BYTE[ dwLen ];
|
|
|
|
if (lpReconvertString == NULL)
|
|
return hr;
|
|
|
|
lpwReconvStr->ReadCompData(lpReconvertString, dwLen);
|
|
|
|
if (lpReconvertString->dwStrLen) {
|
|
Interface<ITfRange> rangeSrc;
|
|
hr = ic->GetStart(ec, rangeSrc);
|
|
if (SUCCEEDED(hr)) {
|
|
LONG cch;
|
|
hr = rangeSrc->ShiftEnd(ec, LONG_MAX, &cch, NULL);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
hr = SetTextInRange(ec,
|
|
rangeSrc,
|
|
(LPWSTR)((LPBYTE)lpReconvertString +
|
|
lpReconvertString->dwStrOffset),
|
|
lpReconvertString->dwStrLen,
|
|
_pAImeContext);
|
|
|
|
#if 0
|
|
//
|
|
// set disp attribute for the query reconvert
|
|
//
|
|
if (S_OK == hr)
|
|
{
|
|
|
|
ITfProperty *pProp = NULL;
|
|
|
|
hr = ic->GetProperty(GUID_PROP_ATTRIBUTE, &pProp);
|
|
if (S_OK == hr)
|
|
{
|
|
SetAttrPropertyData(ImmIfIme->_GetLibTLS(),
|
|
ec,
|
|
pProp,
|
|
rangeSrc,
|
|
GUID_ATTR_MSIMTF_INPUT);
|
|
}
|
|
pProp->Release();
|
|
}
|
|
else
|
|
{
|
|
BOOL fEmpty;
|
|
|
|
if (SUCCEEDED(rangeSrc->IsEmpty(ec, &fEmpty)) && !fEmpty)
|
|
{
|
|
hr = ClearTextInRange(ec, rangeSrc, _pAImeContext);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// we just set a selection to the target string.
|
|
//
|
|
Interface<ITfRange> range;
|
|
if (SUCCEEDED(rangeSrc->Clone(range)))
|
|
{
|
|
LONG cchStart;
|
|
LONG cchEnd;
|
|
|
|
if (lpReconvertString->dwTargetStrOffset == 0 &&
|
|
lpReconvertString->dwTargetStrLen == 0 ) {
|
|
cchStart = lpReconvertString->dwCompStrOffset / sizeof(WCHAR);
|
|
cchEnd = lpReconvertString->dwCompStrOffset / sizeof(WCHAR) + lpReconvertString->dwCompStrLen;
|
|
}
|
|
else {
|
|
cchStart = lpReconvertString->dwTargetStrOffset / sizeof(WCHAR);
|
|
cchEnd = lpReconvertString->dwTargetStrOffset / sizeof(WCHAR) + lpReconvertString->dwTargetStrLen;
|
|
}
|
|
|
|
range->Collapse(ec, TF_ANCHOR_START);
|
|
|
|
//
|
|
// shift end first then shift start.
|
|
//
|
|
if ((SUCCEEDED(range->ShiftEnd(ec,
|
|
cchEnd,
|
|
&cch,
|
|
NULL))) &&
|
|
(SUCCEEDED(range->ShiftStart(ec,
|
|
cchStart,
|
|
&cch,
|
|
NULL))))
|
|
{
|
|
//
|
|
//
|
|
//
|
|
TF_SELECTION sel;
|
|
sel.range = range;
|
|
sel.style.ase = TF_AE_NONE;
|
|
sel.style.fInterimChar = FALSE;
|
|
ic->SetSelection(ec, 1, &sel);
|
|
|
|
hr = range->Clone(*rangeQuery);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete [] lpReconvertString;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfCalcRangePos
|
|
|
|
HRESULT
|
|
ImmIfCalcRangePos::CalcRangePos(
|
|
TfEditCookie ec,
|
|
Interface_Attach<ITfContext> ic,
|
|
Interface<ITfRange>* rangeSrc,
|
|
CWReconvertString* lpwReconvStr,
|
|
Interface_Attach<ImmIfIME> ImmIfIme
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
Interface<ITfRange> pFullRange;
|
|
|
|
hr = ic->GetStart(ec, pFullRange);
|
|
if (SUCCEEDED(hr)) {
|
|
lpwReconvStr->m_CompStrIndex = 0;
|
|
lpwReconvStr->m_TargetStrIndex = 0;
|
|
|
|
BOOL equal = FALSE;
|
|
while (SUCCEEDED(hr=(*rangeSrc)->IsEqualStart(ec, pFullRange, TF_ANCHOR_START, &equal)) &&
|
|
! equal) {
|
|
LONG cch;
|
|
hr = pFullRange->ShiftStart(ec, 1, &cch, NULL);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
lpwReconvStr->m_CompStrIndex++;
|
|
lpwReconvStr->m_TargetStrIndex++;
|
|
}
|
|
|
|
if (S_OK == hr) {
|
|
lpwReconvStr->m_CompStrLen = 0;
|
|
lpwReconvStr->m_TargetStrLen = 0;
|
|
|
|
Interface<ITfRange> rangeTmp;
|
|
|
|
if (SUCCEEDED(hr=(*rangeSrc)->Clone(rangeTmp)))
|
|
{
|
|
BOOL fEmpty;
|
|
WCHAR wstr[256 + 1];
|
|
ULONG ul = 0;
|
|
|
|
while (rangeTmp->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty)
|
|
{
|
|
ULONG ulcch;
|
|
rangeTmp->GetText(ec,
|
|
TF_TF_MOVESTART,
|
|
wstr,
|
|
ARRAYSIZE(wstr) - 1,
|
|
&ulcch);
|
|
ul += ulcch;
|
|
}
|
|
|
|
//
|
|
// Hack for Satori
|
|
// Satori receives empty range with Reconversion->QueryRange().
|
|
// Apps couldn't call reconversion.
|
|
//
|
|
if (ul == 0) {
|
|
ul++;
|
|
}
|
|
|
|
lpwReconvStr->m_CompStrLen = (LONG)ul;
|
|
lpwReconvStr->m_TargetStrLen = (LONG)ul;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfGetSelection
|
|
|
|
HRESULT
|
|
ImmIfGetSelection::GetSelection(
|
|
TfEditCookie ec,
|
|
Interface_Attach<ITfContext> ic,
|
|
Interface<ITfRange>* rangeSrc,
|
|
Interface_Attach<ImmIfIME> ImmIfIme
|
|
)
|
|
{
|
|
Interface_TFSELECTION sel;
|
|
ULONG cFetched;
|
|
|
|
if (ic->GetSelection(ec, TF_DEFAULT_SELECTION, 1, sel, &cFetched) != S_OK)
|
|
return E_FAIL;
|
|
|
|
return sel->range->Clone(*rangeSrc);
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfGetReadOnlyPropMargin
|
|
|
|
HRESULT
|
|
ImmIfGetReadOnlyPropMargin::GetReadOnlyPropMargin(
|
|
TfEditCookie ec,
|
|
Interface_Attach<ITfContext> ic,
|
|
Interface<ITfRangeACP>* rangeSrc,
|
|
INT_PTR* cch,
|
|
Interface_Attach<ImmIfIME> ImmIfIme
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
Interface<ITfRange> range_readonly_prop;
|
|
|
|
*cch = 0;
|
|
|
|
//
|
|
// Create the range that covers start of text to rangeSrc.
|
|
//
|
|
if (FAILED(hr=ic->GetStart(ec, range_readonly_prop)))
|
|
return hr;
|
|
|
|
if (FAILED(hr=range_readonly_prop->ShiftEndToRange(ec, *rangeSrc, TF_ANCHOR_START)))
|
|
return hr;
|
|
|
|
//
|
|
// same ?
|
|
//
|
|
BOOL empty;
|
|
if (FAILED(hr=range_readonly_prop->IsEmpty(ec, &empty)))
|
|
return hr;
|
|
|
|
if (empty)
|
|
return S_OK;
|
|
|
|
//
|
|
// find the first non readonly range in the text store.
|
|
//
|
|
ITfProperty *prop;
|
|
if (SUCCEEDED(ic->GetProperty(GUID_PROP_MSIMTF_READONLY, &prop)))
|
|
{
|
|
IEnumTfRanges *enumranges;
|
|
if (SUCCEEDED(prop->EnumRanges(ec, &enumranges, range_readonly_prop)))
|
|
{
|
|
ITfRange *rangeTmp;
|
|
while (enumranges->Next(1, &rangeTmp, NULL) == S_OK)
|
|
{
|
|
VARIANT var;
|
|
QuickVariantInit(&var);
|
|
prop->GetValue(ec, rangeTmp, &var);
|
|
if ((var.vt == VT_I4) && (var.lVal != 0))
|
|
{
|
|
while (rangeTmp->IsEmpty(ec, &empty) == S_OK && !empty)
|
|
{
|
|
WCHAR wstr[256 + 1];
|
|
ULONG ulcch;
|
|
rangeTmp->GetText(ec,
|
|
TF_TF_MOVESTART,
|
|
wstr,
|
|
ARRAYSIZE(wstr) - 1,
|
|
&ulcch);
|
|
*cch += ulcch;
|
|
}
|
|
}
|
|
rangeTmp->Release();
|
|
}
|
|
enumranges->Release();
|
|
}
|
|
prop->Release();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ImmIfGetCursorPosition
|
|
|
|
HRESULT
|
|
ImmIfGetCursorPosition::GetCursorPosition(
|
|
TfEditCookie ec,
|
|
HIMC hIMC,
|
|
Interface_Attach<ITfContext> ic,
|
|
CWCompCursorPos* lpwCursorPosition,
|
|
Interface_Attach<ImmIfIME> ImmIfIme
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
LIBTHREAD *pLibTLS = ImmIfIme->_GetLibTLS();
|
|
|
|
IMCLock imc(hIMC);
|
|
if (FAILED(hr = imc.GetResult()))
|
|
return hr;
|
|
|
|
BOOL bInWriteSession;
|
|
if (FAILED(hr = ic->InWriteSession(ImmIfIme->GetClientId(), &bInWriteSession)))
|
|
return hr;
|
|
|
|
Interface<ITfRange> FullTextRange;
|
|
LONG lTextLength;
|
|
if (FAILED(hr=GetAllTextRange(ec, ic, &FullTextRange, &lTextLength)))
|
|
return hr;
|
|
|
|
CWCompString CompStr;
|
|
CWCompAttribute CompAttr;
|
|
|
|
if (FAILED(hr = _GetTextAndAttribute(pLibTLS, ec, imc, ic,
|
|
FullTextRange,
|
|
CompStr,
|
|
CompAttr,
|
|
bInWriteSession))) {
|
|
return hr;
|
|
}
|
|
|
|
if (FAILED(hr = _GetCursorPosition(ec, imc, ic, *lpwCursorPosition, CompAttr))) {
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|