2025-11-28 00:35:46 +09:00

378 lines
9.8 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#include "Private.h"
#include "Globals.h"
#include "SampleIME.h"
#include "CompositionProcessorEngine.h"
//+---------------------------------------------------------------------------
//
// ITfCompositionSink::OnCompositionTerminated
//
// Callback for ITfCompositionSink. The system calls this method whenever
// someone other than this service ends a composition.
//----------------------------------------------------------------------------
STDAPI CSampleIME::OnCompositionTerminated(TfEditCookie ecWrite, _In_ ITfComposition *pComposition)
{
// Clear dummy composition
_RemoveDummyCompositionForComposing(ecWrite, pComposition);
// Clear display attribute and end composition, _EndComposition will release composition for us
ITfContext* pContext = _pContext;
if (pContext)
{
pContext->AddRef();
}
_EndComposition(_pContext);
_DeleteCandidateList(FALSE, pContext);
if (pContext)
{
pContext->Release();
pContext = nullptr;
}
return S_OK;
}
//+---------------------------------------------------------------------------
//
// _IsComposing
//
//----------------------------------------------------------------------------
BOOL CSampleIME::_IsComposing()
{
return _pComposition != nullptr;
}
//+---------------------------------------------------------------------------
//
// _SetComposition
//
//----------------------------------------------------------------------------
void CSampleIME::_SetComposition(_In_ ITfComposition *pComposition)
{
_pComposition = pComposition;
}
//+---------------------------------------------------------------------------
//
// _AddComposingAndChar
//
//----------------------------------------------------------------------------
HRESULT CSampleIME::_AddComposingAndChar(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString)
{
HRESULT hr = S_OK;
ULONG fetched = 0;
TF_SELECTION tfSelection;
if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched) != S_OK || fetched == 0)
return S_FALSE;
//
// make range start to selection
//
ITfRange* pAheadSelection = nullptr;
hr = pContext->GetStart(ec, &pAheadSelection);
if (SUCCEEDED(hr))
{
hr = pAheadSelection->ShiftEndToRange(ec, tfSelection.range, TF_ANCHOR_START);
if (SUCCEEDED(hr))
{
ITfRange* pRange = nullptr;
BOOL exist_composing = _FindComposingRange(ec, pContext, pAheadSelection, &pRange);
_SetInputString(ec, pContext, pRange, pstrAddString, exist_composing);
if (pRange)
{
pRange->Release();
}
}
}
tfSelection.range->Release();
if (pAheadSelection)
{
pAheadSelection->Release();
}
return S_OK;
}
//+---------------------------------------------------------------------------
//
// _AddCharAndFinalize
//
//----------------------------------------------------------------------------
HRESULT CSampleIME::_AddCharAndFinalize(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString)
{
HRESULT hr = E_FAIL;
ULONG fetched = 0;
TF_SELECTION tfSelection;
if ((hr = pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) != S_OK || fetched != 1)
return hr;
// we use SetText here instead of InsertTextAtSelection because we've already started a composition
// we don't want to the app to adjust the insertion point inside our composition
hr = tfSelection.range->SetText(ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength());
if (hr == S_OK)
{
// update the selection, we'll make it an insertion point just past
// the inserted text.
tfSelection.range->Collapse(ec, TF_ANCHOR_END);
pContext->SetSelection(ec, 1, &tfSelection);
}
tfSelection.range->Release();
return hr;
}
//+---------------------------------------------------------------------------
//
// _FindComposingRange
//
//----------------------------------------------------------------------------
BOOL CSampleIME::_FindComposingRange(TfEditCookie ec, _In_ ITfContext *pContext, _In_ ITfRange *pSelection, _Outptr_result_maybenull_ ITfRange **ppRange)
{
if (ppRange == nullptr)
{
return FALSE;
}
*ppRange = nullptr;
// find GUID_PROP_COMPOSING
ITfProperty* pPropComp = nullptr;
IEnumTfRanges* enumComp = nullptr;
HRESULT hr = pContext->GetProperty(GUID_PROP_COMPOSING, &pPropComp);
if (FAILED(hr) || pPropComp == nullptr)
{
return FALSE;
}
hr = pPropComp->EnumRanges(ec, &enumComp, pSelection);
if (FAILED(hr) || enumComp == nullptr)
{
pPropComp->Release();
return FALSE;
}
BOOL isCompExist = FALSE;
VARIANT var;
ULONG fetched = 0;
while (enumComp->Next(1, ppRange, &fetched) == S_OK && fetched == 1)
{
hr = pPropComp->GetValue(ec, *ppRange, &var);
if (hr == S_OK)
{
if (var.vt == VT_I4 && var.lVal != 0)
{
isCompExist = TRUE;
break;
}
}
(*ppRange)->Release();
*ppRange = nullptr;
}
pPropComp->Release();
enumComp->Release();
return isCompExist;
}
//+---------------------------------------------------------------------------
//
// _SetInputString
//
//----------------------------------------------------------------------------
HRESULT CSampleIME::_SetInputString(TfEditCookie ec, _In_ ITfContext *pContext, _Out_opt_ ITfRange *pRange, _In_ CStringRange *pstrAddString, BOOL exist_composing)
{
ITfRange* pRangeInsert = nullptr;
if (!exist_composing)
{
_InsertAtSelection(ec, pContext, pstrAddString, &pRangeInsert);
if (pRangeInsert == nullptr)
{
return S_OK;
}
pRange = pRangeInsert;
}
if (pRange != nullptr)
{
pRange->SetText(ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength());
}
_SetCompositionLanguage(ec, pContext);
_SetCompositionDisplayAttributes(ec, pContext, _gaDisplayAttributeInput);
// update the selection, we'll make it an insertion point just past
// the inserted text.
ITfRange* pSelection = nullptr;
TF_SELECTION sel;
if ((pRange != nullptr) && (pRange->Clone(&pSelection) == S_OK))
{
pSelection->Collapse(ec, TF_ANCHOR_END);
sel.range = pSelection;
sel.style.ase = TF_AE_NONE;
sel.style.fInterimChar = FALSE;
pContext->SetSelection(ec, 1, &sel);
pSelection->Release();
}
if (pRangeInsert)
{
pRangeInsert->Release();
}
return S_OK;
}
//+---------------------------------------------------------------------------
//
// _InsertAtSelection
//
//----------------------------------------------------------------------------
HRESULT CSampleIME::_InsertAtSelection(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString, _Outptr_ ITfRange **ppCompRange)
{
ITfRange* rangeInsert = nullptr;
ITfInsertAtSelection* pias = nullptr;
HRESULT hr = S_OK;
if (ppCompRange == nullptr)
{
hr = E_INVALIDARG;
goto Exit;
}
*ppCompRange = nullptr;
hr = pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pias);
if (FAILED(hr))
{
goto Exit;
}
hr = pias->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, pstrAddString->Get(), (LONG)pstrAddString->GetLength(), &rangeInsert);
if ( FAILED(hr) || rangeInsert == nullptr)
{
rangeInsert = nullptr;
pias->Release();
goto Exit;
}
*ppCompRange = rangeInsert;
pias->Release();
hr = S_OK;
Exit:
return hr;
}
//+---------------------------------------------------------------------------
//
// _RemoveDummyCompositionForComposing
//
//----------------------------------------------------------------------------
HRESULT CSampleIME::_RemoveDummyCompositionForComposing(TfEditCookie ec, _In_ ITfComposition *pComposition)
{
HRESULT hr = S_OK;
ITfRange* pRange = nullptr;
if (pComposition)
{
hr = pComposition->GetRange(&pRange);
if (SUCCEEDED(hr))
{
pRange->SetText(ec, 0, nullptr, 0);
pRange->Release();
}
}
return hr;
}
//+---------------------------------------------------------------------------
//
// _SetCompositionLanguage
//
//----------------------------------------------------------------------------
BOOL CSampleIME::_SetCompositionLanguage(TfEditCookie ec, _In_ ITfContext *pContext)
{
HRESULT hr = S_OK;
BOOL ret = TRUE;
CCompositionProcessorEngine* pCompositionProcessorEngine = nullptr;
pCompositionProcessorEngine = _pCompositionProcessorEngine;
LANGID langidProfile = 0;
pCompositionProcessorEngine->GetLanguageProfile(&langidProfile);
ITfRange* pRangeComposition = nullptr;
ITfProperty* pLanguageProperty = nullptr;
// we need a range and the context it lives in
hr = _pComposition->GetRange(&pRangeComposition);
if (FAILED(hr))
{
ret = FALSE;
goto Exit;
}
// get our the language property
hr = pContext->GetProperty(GUID_PROP_LANGID, &pLanguageProperty);
if (FAILED(hr))
{
ret = FALSE;
goto Exit;
}
VARIANT var;
var.vt = VT_I4; // we're going to set DWORD
var.lVal = langidProfile;
hr = pLanguageProperty->SetValue(ec, pRangeComposition, &var);
if (FAILED(hr))
{
ret = FALSE;
goto Exit;
}
pLanguageProperty->Release();
pRangeComposition->Release();
Exit:
return ret;
}