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.
356 lines
10 KiB
356 lines
10 KiB
//
|
|
// keys.cpp
|
|
//
|
|
// ITfKeyEventSink implementation.
|
|
//
|
|
|
|
#include "globals.h"
|
|
#include "mark.h"
|
|
#include "editsess.h"
|
|
|
|
class CKeystrokeEditSession : public CEditSessionBase
|
|
{
|
|
public:
|
|
CKeystrokeEditSession(CMarkTextService *pMark, ITfContext *pContext, WPARAM wParam) : CEditSessionBase(pContext)
|
|
{
|
|
_pMark = pMark;
|
|
_pMark->AddRef();
|
|
_wParam = wParam;
|
|
}
|
|
~CKeystrokeEditSession()
|
|
{
|
|
_pMark->Release();
|
|
}
|
|
|
|
// ITfEditSession
|
|
STDMETHODIMP DoEditSession(TfEditCookie ec);
|
|
|
|
private:
|
|
CMarkTextService *_pMark;
|
|
WPARAM _wParam;
|
|
};
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _HandleReturn
|
|
//
|
|
// Returns S_OK to eat the keystroke, S_FALSE otherwise.
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CMarkTextService::_HandleReturn(TfEditCookie ec, ITfContext *pContext)
|
|
{
|
|
// just terminate the composition
|
|
_TerminateComposition(ec);
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _HandleArrowKey
|
|
//
|
|
// Update the selection within a composition.
|
|
// Returns S_OK to eat the keystroke, S_FALSE otherwise.
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CMarkTextService::_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
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _HandleKeyDown
|
|
//
|
|
// If the keystroke happens within a composition, eat the key and return S_OK.
|
|
// Otherwise, do nothing and return S_FALSE.
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CMarkTextService::_HandleKeyDown(TfEditCookie ec, ITfContext *pContext, WPARAM wParam)
|
|
{
|
|
ITfRange *pRangeComposition;
|
|
TF_SELECTION tfSelection;
|
|
ULONG cFetched;
|
|
HRESULT hr;
|
|
WCHAR ch;
|
|
BOOL fCovered;
|
|
|
|
if (wParam < 'A' || wParam > 'Z')
|
|
return S_OK; // just eat the key if it's not in a range we know how to handle
|
|
|
|
hr = S_OK; // return S_FALSE to NOT eat the key
|
|
|
|
// convert the wParam to a WCHAR
|
|
if (GetKeyState(VK_SHIFT) & 0x8000)
|
|
{
|
|
// shift-key, leave it uppercase
|
|
ch = (WCHAR)wParam;
|
|
}
|
|
else
|
|
{
|
|
// else make it lowercase
|
|
ch = (WCHAR)(wParam | 32);
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
hr = S_FALSE; // don't eat the key, it's outside our composition
|
|
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);
|
|
|
|
// apply our dislay attribute property to the inserted text
|
|
// we need to apply it to the entire composition, since the
|
|
// display attribute property is static, not static compact
|
|
_SetCompositionDisplayAttributes(ec);
|
|
|
|
Exit:
|
|
tfSelection.range->Release();
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _InitKeystrokeSink
|
|
//
|
|
// Advise a keystroke sink.
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CMarkTextService::_InitKeystrokeSink()
|
|
{
|
|
ITfKeystrokeMgr *pKeystrokeMgr;
|
|
HRESULT hr;
|
|
|
|
if (_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr) != S_OK)
|
|
return FALSE;
|
|
|
|
hr = pKeystrokeMgr->AdviseKeyEventSink(_tfClientId, (ITfKeyEventSink *)this, TRUE);
|
|
|
|
pKeystrokeMgr->Release();
|
|
|
|
return (hr == S_OK);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _UninitKeystrokeSink
|
|
//
|
|
// Unadvise a keystroke sink. Assumes we have advised one already.
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CMarkTextService::_UninitKeystrokeSink()
|
|
{
|
|
ITfKeystrokeMgr *pKeystrokeMgr;
|
|
|
|
if (_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr) != S_OK)
|
|
return;
|
|
|
|
pKeystrokeMgr->UnadviseKeyEventSink(_tfClientId);
|
|
|
|
pKeystrokeMgr->Release();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// OnSetFocus
|
|
//
|
|
// Called by the system whenever this service gets the keystroke device focus.
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CMarkTextService::OnSetFocus(BOOL fForeground)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// OnTestKeyDown
|
|
//
|
|
// Called by the system to query if this service wants a potential keystroke.
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CMarkTextService::OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
|
|
{
|
|
*pfEaten = (_pComposition != NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// OnKeyDown
|
|
//
|
|
// Called by the system to offer this service a keystroke. If *pfEaten == TRUE
|
|
// on exit, the application will not handle the keystroke.
|
|
//
|
|
// 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.
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CMarkTextService::OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
|
|
{
|
|
CKeystrokeEditSession *pEditSession;
|
|
HRESULT hr;
|
|
|
|
hr = E_FAIL;
|
|
*pfEaten = FALSE;
|
|
|
|
if (_pComposition != NULL) // only eat keys while composing
|
|
{
|
|
// we'll insert a char ourselves in place of this keystroke
|
|
if ((pEditSession = new CKeystrokeEditSession(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
|
|
if (pContext->RequestEditSession(_tfClientId, pEditSession, TF_ES_SYNC | TF_ES_READWRITE, &hr) != S_OK)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
pEditSession->Release();
|
|
}
|
|
|
|
Exit:
|
|
// if we made it all the way to the RequestEditSession, then hr is ultimately the
|
|
// return code from CKeystrokeEditSession::DoEditSession. Our DoEditSession method
|
|
// return S_OK to signal that the keystroke should be eaten, S_FALSE otherwise.
|
|
if (hr == S_OK)
|
|
{
|
|
*pfEaten = TRUE;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// DoEditSession
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CKeystrokeEditSession::DoEditSession(TfEditCookie ec)
|
|
{
|
|
switch (_wParam)
|
|
{
|
|
case VK_LEFT:
|
|
case VK_RIGHT:
|
|
return _pMark->_HandleArrowKey(ec, _pContext, _wParam);
|
|
|
|
case VK_RETURN:
|
|
return _pMark->_HandleReturn(ec, _pContext);
|
|
|
|
case VK_SPACE:
|
|
return S_OK;
|
|
}
|
|
|
|
return _pMark->_HandleKeyDown(ec, _pContext, _wParam);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// OnTestKeyUp
|
|
//
|
|
// Called by the system to query this service wants a potential keystroke.
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CMarkTextService::OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
|
|
{
|
|
*pfEaten = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// OnKeyUp
|
|
//
|
|
// Called by the system to offer this service a keystroke. If *pfEaten == TRUE
|
|
// on exit, the application will not handle the keystroke.
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CMarkTextService::OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
|
|
{
|
|
*pfEaten = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// OnPreservedKey
|
|
//
|
|
// Called when a hotkey (registered by us, or by the system) is typed.
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CMarkTextService::OnPreservedKey(ITfContext *pContext, REFGUID rguid, BOOL *pfEaten)
|
|
{
|
|
*pfEaten = FALSE;
|
|
return S_OK;
|
|
}
|