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

255 lines
7.1 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) 2003 Microsoft Corporation. All rights reserved.
//
// KeyHandler.cpp
//
// the handler routines for key events
//
//////////////////////////////////////////////////////////////////////
#include "Globals.h"
#include "EditSession.h"
#include "TextService.h"
//+---------------------------------------------------------------------------
//
// CKeyHandlerEditSession
//
//----------------------------------------------------------------------------
class CKeyHandlerEditSession : public CEditSessionBase
{
public:
CKeyHandlerEditSession(CTextService *pTextService, ITfContext *pContext, WPARAM wParam) : CEditSessionBase(pTextService, pContext)
{
_wParam = wParam;
}
// ITfEditSession
STDMETHODIMP DoEditSession(TfEditCookie ec);
private:
WPARAM _wParam;
};
//+---------------------------------------------------------------------------
//
// DoEditSession
//
//----------------------------------------------------------------------------
STDAPI CKeyHandlerEditSession::DoEditSession(TfEditCookie ec)
{
switch (_wParam)
{
case VK_LEFT:
case VK_RIGHT:
return _pTextService->_HandleArrowKey(ec, _pContext, _wParam);
case VK_RETURN:
return _pTextService->_HandleReturnKey(ec, _pContext);
default:
if (_wParam >= 'A' && _wParam <= 'Z')
return _pTextService->_HandleCharacterKey(ec, _pContext, _wParam);
break;
}
return S_OK;
}
//+---------------------------------------------------------------------------
//
// IsRangeCovered
//
// Returns TRUE if pRangeTest is entirely contained within pRangeCover.
//
//----------------------------------------------------------------------------
BOOL IsRangeCovered(TfEditCookie ec, ITfRange *pRangeTest, ITfRange *pRangeCover)
{
LONG lResult;
if (pRangeCover->CompareStart(ec, pRangeTest, TF_ANCHOR_START, &lResult) != S_OK ||
lResult > 0)
{
return FALSE;
}
if (pRangeCover->CompareEnd(ec, pRangeTest, TF_ANCHOR_END, &lResult) != S_OK ||
lResult < 0)
{
return FALSE;
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// _HandleCharacterKey
//
// If the keystroke happens within a composition, eat the key and return S_OK.
//
//----------------------------------------------------------------------------
HRESULT CTextService::_HandleCharacterKey(TfEditCookie ec, ITfContext *pContext, WPARAM wParam)
{
ITfRange *pRangeComposition;
TF_SELECTION tfSelection;
ULONG cFetched;
WCHAR ch;
BOOL fCovered;
// Start the new compositon if there is no composition.
if (!_IsComposing())
_StartComposition(pContext);
//
// Assign VK_ value to the char. So the inserted the character is always
// uppercase.
//
ch = (WCHAR)wParam;
// first, test where a keystroke would go in the document if we did an insert
if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK || cFetched != 1)
return S_FALSE;
// is the insertion point covered by a composition?
if (_pComposition->GetRange(&pRangeComposition) == S_OK)
{
fCovered = IsRangeCovered(ec, tfSelection.range, pRangeComposition);
pRangeComposition->Release();
if (!fCovered)
{
goto Exit;
}
}
// insert the text
// 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
if (tfSelection.range->SetText(ec, 0, &ch, 1) != S_OK)
goto Exit;
// 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);
Exit:
tfSelection.range->Release();
return S_OK;
}
//+---------------------------------------------------------------------------
//
// _HandleReturnKey
//
//----------------------------------------------------------------------------
HRESULT CTextService::_HandleReturnKey(TfEditCookie ec, ITfContext *pContext)
{
// just terminate the composition
_TerminateComposition(ec);
return S_OK;
}
//+---------------------------------------------------------------------------
//
// _HandleArrowKey
//
// Update the selection within a composition.
//
//----------------------------------------------------------------------------
HRESULT CTextService::_HandleArrowKey(TfEditCookie ec, ITfContext *pContext, WPARAM wParam)
{
ITfRange *pRangeComposition;
LONG cch;
BOOL fEqual;
TF_SELECTION tfSelection;
ULONG cFetched;
// get the selection
if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK ||
cFetched != 1)
{
// no selection?
return S_OK; // eat the keystroke
}
// get the composition range
if (_pComposition->GetRange(&pRangeComposition) != S_OK)
goto Exit;
// adjust the selection, we won't do anything fancy
if (wParam == VK_LEFT)
{
if (tfSelection.range->IsEqualStart(ec, pRangeComposition, TF_ANCHOR_START, &fEqual) == S_OK &&
!fEqual)
{
tfSelection.range->ShiftStart(ec, -1, &cch, NULL);
}
tfSelection.range->Collapse(ec, TF_ANCHOR_START);
}
else
{
// VK_RIGHT
if (tfSelection.range->IsEqualEnd(ec, pRangeComposition, TF_ANCHOR_END, &fEqual) == S_OK &&
!fEqual)
{
tfSelection.range->ShiftEnd(ec, +1, &cch, NULL);
}
tfSelection.range->Collapse(ec, TF_ANCHOR_END);
}
pContext->SetSelection(ec, 1, &tfSelection);
pRangeComposition->Release();
Exit:
tfSelection.range->Release();
return S_OK; // eat the keystroke
}
//+---------------------------------------------------------------------------
//
// _InvokeKeyHandler
//
// This text service is interested in handling keystrokes to demonstrate the
// use the compositions. Some apps will cancel compositions if they receive
// keystrokes while a compositions is ongoing.
//
//----------------------------------------------------------------------------
HRESULT CTextService::_InvokeKeyHandler(ITfContext *pContext, WPARAM wParam, LPARAM lParam)
{
CKeyHandlerEditSession *pEditSession;
HRESULT hr = E_FAIL;
// we'll insert a char ourselves in place of this keystroke
if ((pEditSession = new CKeyHandlerEditSession(this, pContext, wParam)) == NULL)
goto Exit;
// we need a lock to do our work
// nb: this method is one of the few places where it is legal to use
// the TF_ES_SYNC flag
hr = pContext->RequestEditSession(_tfClientId, pEditSession, TF_ES_SYNC | TF_ES_READWRITE, &hr);
pEditSession->Release();
Exit:
return hr;
}