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.
3076 lines
71 KiB
3076 lines
71 KiB
/*
|
|
* @doc INTERNAL
|
|
*
|
|
* @module CMSGFLT.CPP -- Text Message Implementation |
|
|
*
|
|
* Most everything to do with IME message handling.
|
|
*
|
|
* Original Author: <nl>
|
|
* Hon Wah Chan
|
|
*
|
|
* History: <nl>
|
|
* 2/6/98 v-honwch
|
|
*
|
|
* Copyright (c) 1995-2000, 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 "msctfp.h"
|
|
|
|
#include "textserv.h"
|
|
#include "_cmsgflt.h"
|
|
#include "_ime.h"
|
|
|
|
#include "_cuim.h"
|
|
#include "imeapp.h"
|
|
|
|
|
|
#define MAX_RECONVERSION_SIZE 100
|
|
#define CONTROL(_ch) (_ch - 'A' + 1)
|
|
|
|
/*
|
|
* void CreateIMEMessageFilter(ITextMsgFilter **ppMsgFilter)
|
|
*
|
|
* @func
|
|
* TextMsgFilter class factory.
|
|
*/
|
|
void CreateIMEMessageFilter(ITextMsgFilter **ppMsgFilter)
|
|
{
|
|
CTextMsgFilter *pNewFilter = new CTextMsgFilter;
|
|
*ppMsgFilter = pNewFilter ? pNewFilter : NULL;
|
|
}
|
|
|
|
/*
|
|
* void CTextMsgFilter::~CTextMsgFilter
|
|
*
|
|
* @mfunc
|
|
* CTextMsgFilter Destructor
|
|
* Release objects being used.
|
|
*
|
|
*/
|
|
CTextMsgFilter::~CTextMsgFilter ()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::~CTextMsgFilter");
|
|
|
|
if (_nIMEMode)
|
|
{
|
|
SetIMESentenseMode(FALSE);
|
|
_nIMEMode = 0;
|
|
}
|
|
|
|
if (_hIMCContext)
|
|
ImmAssociateContext(_hwnd, _hIMCContext, _fUsingAIMM); // Restore IME before exit
|
|
|
|
if (_pMsgCallBack)
|
|
{
|
|
delete _pMsgCallBack;
|
|
_pMsgCallBack = NULL;
|
|
}
|
|
|
|
// Release various objects
|
|
TurnOffUIM(FALSE);
|
|
|
|
TurnOffAimm(FALSE);
|
|
|
|
if (_pFilter)
|
|
_pFilter->Release();
|
|
|
|
if (_pTextSel)
|
|
_pTextSel->Release();
|
|
|
|
_pFilter = NULL;
|
|
_pTextDoc = NULL;
|
|
_pTextSel = NULL;
|
|
_hwnd = NULL;
|
|
_hIMCContext = NULL;
|
|
FreePv(_pcrComp);
|
|
_pcrComp = NULL;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP CTextMsgFilter::QueryInterface (riid, ppv)
|
|
*
|
|
* @mfunc
|
|
* IUnknown QueryInterface support
|
|
*
|
|
* @rdesc
|
|
* NOERROR if interface supported
|
|
*
|
|
*/
|
|
STDMETHODIMP CTextMsgFilter::QueryInterface (REFIID riid, void ** ppv)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::QueryInterface");
|
|
|
|
if( IsEqualIID(riid, IID_IUnknown) )
|
|
{
|
|
*ppv = (IUnknown *)this;
|
|
}
|
|
else if( IsEqualIID(riid, IID_ITextMsgFilter) )
|
|
{
|
|
*ppv = (ITextMsgFilter *)this;
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
AddRef();
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP_(ULONG) CTextMsgFilter::AddRef
|
|
*
|
|
* @mfunc
|
|
* IUnknown AddRef support
|
|
*
|
|
* @rdesc
|
|
* Reference count
|
|
*/
|
|
STDMETHODIMP_(ULONG) CTextMsgFilter::AddRef()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::AddRef");
|
|
|
|
return ++_crefs;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP_(ULONG) CTextMsgFilter::Release()
|
|
*
|
|
* @mfunc
|
|
* IUnknown Release support - delete object when reference count is 0
|
|
*
|
|
* @rdesc
|
|
* Reference count
|
|
*/
|
|
STDMETHODIMP_(ULONG) CTextMsgFilter::Release()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::Release");
|
|
|
|
_crefs--;
|
|
|
|
if( _crefs == 0 )
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return _crefs;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP_(HRESULT) CTextMsgFilter::AttachDocument(HWND, ITextDocument2)
|
|
*
|
|
* @mfunc
|
|
* Attach message filter. Perform genral initialization
|
|
*
|
|
* @rdesc
|
|
* NOERROR
|
|
*/
|
|
STDMETHODIMP_(HRESULT) CTextMsgFilter::AttachDocument( HWND hwnd, ITextDocument2 *pTextDoc, IUnknown *punk)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::AttachDocument");
|
|
|
|
// Cache the values for possible later use.
|
|
// The TextDocument interface pointer is not AddRefed because it is a back pointer
|
|
// and the lifetime of message filters is assumed to be nested inside text documents
|
|
_hwnd = hwnd;
|
|
_pTextDoc = pTextDoc;
|
|
_pTextService = (ITextServices *)punk;
|
|
|
|
// Don't get selection until it is needed
|
|
_pTextSel = NULL;
|
|
|
|
_fUnicodeWindow = 0;
|
|
if (hwnd)
|
|
_fUnicodeWindow = IsWindowUnicode(hwnd);
|
|
|
|
_fUsingAIMM = 0;
|
|
|
|
_pTim = NULL;
|
|
_pCUIM = NULL;
|
|
_fUsingUIM = 0;
|
|
|
|
// Check if current keyboard is MSIME98 or later.
|
|
CheckIMEType(NULL);
|
|
|
|
// Initialize some member data
|
|
_fHangulToHanja = FALSE;
|
|
_fIMECancelComplete = FALSE;
|
|
_fIMEAlwaysNotify = FALSE;
|
|
_hIMCContext = NULL;
|
|
_pcrComp = NULL;
|
|
_pMsgCallBack = NULL;
|
|
|
|
_pTextDoc->GetFEFlags(&_lFEFlags);
|
|
_fRE10Mode = (_lFEFlags & tomRE10Mode);
|
|
|
|
_uSystemCodePage = GetACP();
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* STDMETHODIMP_(HRESULT) CTextMsgFilter::HandleMessage(UINT *, WPARAM *, LPARAM *, LRESULT *)
|
|
*
|
|
* @mfunc
|
|
* Main Message filter message loop handling
|
|
*
|
|
* @rdesc
|
|
* S_OK if we have handled the message
|
|
* S_FALSE if we want the caller to process the message
|
|
*/
|
|
STDMETHODIMP_(HRESULT) CTextMsgFilter::HandleMessage(
|
|
UINT * pmsg,
|
|
WPARAM * pwparam,
|
|
LPARAM * plparam,
|
|
LRESULT * plres)
|
|
{
|
|
//TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::HandleMessage");
|
|
|
|
HRESULT hr = S_FALSE;
|
|
BOOL bReleaseSelction = FALSE;
|
|
HRESULT hResult;
|
|
|
|
// Give other message filters a chance to handle message
|
|
// Stop with the first guy that handles the message
|
|
if (_pFilter)
|
|
hr = _pFilter->HandleMessage(pmsg, pwparam, plparam, plres);
|
|
|
|
if (hr == S_OK)
|
|
return hr;
|
|
|
|
if (IsIMEComposition() || _pCUIM && _pCUIM->IsUIMTyping())
|
|
{
|
|
// During IME Composition, there are some messages we should
|
|
// not handle. Also, there are other messages we need to handle by
|
|
// terminating the IME composition first.
|
|
// For WM_KEYDOWN, this is handled inside edit.c OnTxKeyDown().
|
|
switch( *pmsg )
|
|
{
|
|
case WM_COPY:
|
|
case WM_CUT:
|
|
case WM_DROPFILES:
|
|
case EM_REDO:
|
|
case EM_SETCHARFORMAT:
|
|
case WM_SETFONT:
|
|
return S_OK; // Just ignore these
|
|
|
|
|
|
case EM_UNDO:
|
|
case WM_UNDO:
|
|
// just terminate and exist for undo cases
|
|
CompleteUIMTyping(CIme::TERMINATE_NORMAL);
|
|
return S_OK;
|
|
|
|
case WM_SETTEXT:
|
|
case WM_CLEAR:
|
|
case EM_STREAMIN:
|
|
// these messages are used to reset our state, so reset
|
|
// IME as well
|
|
CompleteUIMTyping(CIme::TERMINATE_FORCECANCEL);
|
|
break;
|
|
|
|
case EM_SETTEXTEX:
|
|
if (!_fRE10Mode) // Don't terminate if running in 10 mode
|
|
CompleteUIMTyping(CIme::TERMINATE_FORCECANCEL);
|
|
break;
|
|
|
|
case WM_SYSKEYDOWN:
|
|
// Don't terminate IME composition on VK_PROCESSKEY (F10) since Japanese
|
|
// IME will process the F10 key
|
|
if (*pwparam != VK_PROCESSKEY)
|
|
CompleteUIMTyping(CIme::TERMINATE_NORMAL); // otherwise we want to terminate the IME
|
|
break;
|
|
|
|
case WM_CHAR:
|
|
case WM_UNICHAR:
|
|
if (IsIMEComposition() && _ime->GetIMELevel() == IME_LEVEL_3 && !_fReceivedKeyDown)
|
|
return S_OK; // Ignore this during IME composition and we haven't seen
|
|
// any keydown message,
|
|
// else fall thru to terminate composition
|
|
_fReceivedKeyDown = 0;
|
|
|
|
case EM_SETWORDBREAKPROC:
|
|
case WM_PASTE:
|
|
case EM_PASTESPECIAL:
|
|
case EM_SCROLL:
|
|
case EM_SCROLLCARET:
|
|
case WM_VSCROLL:
|
|
case WM_HSCROLL:
|
|
case EM_SETREADONLY:
|
|
case EM_SETPARAFORMAT:
|
|
case WM_INPUTLANGCHANGEREQUEST:
|
|
case EM_REPLACESEL:
|
|
case EM_STREAMOUT:
|
|
CompleteUIMTyping(CIme::TERMINATE_NORMAL);
|
|
break;
|
|
|
|
case WM_KILLFOCUS:
|
|
CompleteUIMTyping(CIme::TERMINATE_NORMAL, FALSE);
|
|
break;
|
|
|
|
case EM_SETSEL:
|
|
if (IsIMEComposition())
|
|
CompleteUIMTyping(CIme::TERMINATE_NORMAL);
|
|
else
|
|
return S_OK; // Ignore this during Cicero typing
|
|
|
|
case WM_KEYUP:
|
|
_fReceivedKeyDown = 0;
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
_fReceivedKeyDown = 1;
|
|
if(GetKeyState(VK_CONTROL) & 0x8000)
|
|
{
|
|
// During IME Composition, there are some key events we should
|
|
// not handle. Also, there are other key events we need to handle by
|
|
// terminating the IME composition first.
|
|
switch((WORD) *pwparam)
|
|
{
|
|
case VK_TAB:
|
|
case VK_CLEAR:
|
|
case VK_NUMPAD5:
|
|
case 'A': // Ctrl-A => select all
|
|
case 'C': // Ctrl-C => copy
|
|
case 'X': // Ctrl-X => cut
|
|
case 'Y': // Ctrl-Y => redo
|
|
return S_OK; // Just ignore these
|
|
|
|
case 'V': // Ctrl-V => paste
|
|
case 'Z': // Ctrl-Z => undo
|
|
CompleteUIMTyping(CIme::TERMINATE_NORMAL);
|
|
if ((WORD) *pwparam == 'Z') // Early exist for undo case
|
|
return S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch((WORD) *pwparam)
|
|
{
|
|
case VK_F16:
|
|
return S_OK; // Just ignore these
|
|
|
|
case VK_BACK:
|
|
case VK_INSERT: // Ins
|
|
case VK_LEFT: // Left arrow
|
|
case VK_RIGHT: // Right arrow
|
|
case VK_UP: // Up arrow
|
|
case VK_DOWN: // Down arrow
|
|
case VK_HOME: // Home
|
|
case VK_END: // End
|
|
case VK_PRIOR: // PgUp
|
|
case VK_NEXT: // PgDn
|
|
case VK_DELETE: // Del
|
|
case CONTROL('J'):
|
|
case VK_RETURN:
|
|
CompleteUIMTyping(CIme::TERMINATE_NORMAL);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Only need to handle mouse related msgs during composition
|
|
if (IN_RANGE(WM_MOUSEFIRST, *pmsg, WM_MBUTTONDBLCLK) || *pmsg == WM_SETCURSOR)
|
|
{
|
|
if (IsIMEComposition())
|
|
{
|
|
bReleaseSelction = GetTxSelection();
|
|
if (_pTextSel)
|
|
hr = IMEMouseCheck( *this, pmsg, pwparam, plparam, plres);
|
|
goto Exit;
|
|
}
|
|
|
|
// Cicero composition
|
|
if (_pCUIM->_fMosueSink)
|
|
{
|
|
bReleaseSelction = GetTxSelection();
|
|
if (_pTextSel)
|
|
hr = _pCUIM->MouseCheck(pmsg, pwparam, plparam, plres);
|
|
goto Exit;
|
|
}
|
|
if (IN_RANGE(WM_LBUTTONDOWN, *pmsg, WM_MOUSELAST) && !(*pmsg == WM_LBUTTONUP || *pmsg == WM_RBUTTONUP || *pmsg == WM_MBUTTONUP))
|
|
CompleteUIMTyping(CIme::TERMINATE_NORMAL); // Terminate on Mouse down and double-click messages
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get Fe Flags for ES_NOIME or ES_SELFIME setting
|
|
_lFEFlags = 0;
|
|
|
|
// ... Local mucking with msg, params, etc, ...
|
|
switch ( *pmsg )
|
|
{
|
|
case WM_CHAR:
|
|
hr = OnWMChar (pmsg, pwparam, plparam, plres);
|
|
break;
|
|
|
|
case WM_IME_CHAR:
|
|
_uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
|
|
hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
|
|
if ((_lFEFlags & ES_NOIME))
|
|
hr = S_OK;
|
|
else
|
|
hr = OnWMIMEChar (pmsg, pwparam, plparam, plres);
|
|
break;
|
|
|
|
case WM_IME_STARTCOMPOSITION:
|
|
_fReceivedKeyDown = 0;
|
|
_uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
|
|
hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
|
|
if (!(_lFEFlags & ES_SELFIME))
|
|
{
|
|
bReleaseSelction = GetTxSelection();
|
|
if (_pTextSel)
|
|
hr = StartCompositionGlue (*this);
|
|
}
|
|
break;
|
|
|
|
case WM_IME_COMPOSITION:
|
|
_fReceivedKeyDown = 0;
|
|
_uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
|
|
hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
|
|
|
|
if ((_lFEFlags & ES_NOIME) && !IsIMEComposition())
|
|
hr = S_OK;
|
|
else if (!(_lFEFlags & ES_SELFIME))
|
|
{
|
|
bReleaseSelction = GetTxSelection();
|
|
if (_pTextSel)
|
|
{
|
|
hr = CompositionStringGlue ( *plparam, *this );
|
|
// Turn off Result string bit to avoid WM_IME_CHAR message.
|
|
*plparam &= ~GCS_RESULTSTR;
|
|
}
|
|
}
|
|
|
|
if (_hwnd && IsIMEComposition() && _ime->IgnoreIMECharMsg())
|
|
{
|
|
_ime->AcceptIMECharMsg();
|
|
if (fHaveAIMM)
|
|
hr = CallAIMMDefaultWndProc(_hwnd, *pmsg, *pwparam, *plparam, plres);
|
|
else
|
|
*plres = ::DefWindowProc(_hwnd, *pmsg, *pwparam, *plparam);
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_IME_ENDCOMPOSITION:
|
|
_fReceivedKeyDown = 0;
|
|
hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
|
|
if (!(_lFEFlags & ES_SELFIME))
|
|
{
|
|
bReleaseSelction = GetTxSelection();
|
|
if (_pTextSel)
|
|
hr = EndCompositionGlue ( *this, FALSE );
|
|
}
|
|
break;
|
|
|
|
case WM_IME_NOTIFY:
|
|
hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
|
|
if (!(_lFEFlags & (ES_SELFIME | ES_NOIME)))
|
|
{
|
|
bReleaseSelction = GetTxSelection();
|
|
if (_pTextSel)
|
|
hr = IMENotifyGlue ( *pwparam, *plparam, *this );
|
|
}
|
|
break;
|
|
|
|
case WM_IME_COMPOSITIONFULL: // Level 2 comp string about to overflow.
|
|
hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
|
|
if (!(_lFEFlags & ES_SELFIME))
|
|
{
|
|
IMECompositionFull ( *this );
|
|
}
|
|
hr = S_FALSE;
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
if (*pwparam == VK_KANJI)
|
|
{
|
|
hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
|
|
|
|
_uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
|
|
// for Korean, need to convert the next Korean Hangul character to Hanja
|
|
if(CP_KOREAN == _uKeyBoardCodePage && !(_lFEFlags & (ES_SELFIME | ES_NOIME)))
|
|
{
|
|
bReleaseSelction = GetTxSelection();
|
|
if (_pTextSel)
|
|
hr = IMEHangeulToHanja ( *this );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_INPUTLANGCHANGE:
|
|
CheckIMEType((HKL)*plparam);
|
|
if (_nIMEMode && GetFocus() == _hwnd)
|
|
SetIMESentenseMode(TRUE, (HKL)*plparam);
|
|
hr = S_FALSE;
|
|
break;
|
|
|
|
case WM_INPUTLANGCHANGEREQUEST:
|
|
if (_nIMEMode && GetFocus() == _hwnd)
|
|
SetIMESentenseMode(FALSE);
|
|
break;
|
|
|
|
case WM_KILLFOCUS:
|
|
OnKillFocus();
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
OnSetFocus();
|
|
break;
|
|
|
|
case EM_SETIMEOPTIONS:
|
|
*plres = OnSetIMEOptions(*pwparam, *plparam);
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case EM_GETIMEOPTIONS:
|
|
*plres = OnGetIMEOptions();
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case WM_IME_REQUEST:
|
|
hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
|
|
if (!(_lFEFlags & (ES_SELFIME | ES_NOIME)))
|
|
{
|
|
bReleaseSelction = GetTxSelection();
|
|
if (_pTextSel)
|
|
{
|
|
_uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
|
|
if (*pwparam == IMR_RECONVERTSTRING || *pwparam == IMR_CONFIRMRECONVERTSTRING
|
|
|| *pwparam == IMR_DOCUMENTFEED)
|
|
hr = OnIMEReconvert(pmsg, pwparam, plparam, plres, _fUnicodeWindow);
|
|
else if (*pwparam == IMR_QUERYCHARPOSITION)
|
|
hr = OnIMEQueryPos(pmsg, pwparam, plparam, plres, _fUnicodeWindow);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EM_RECONVERSION:
|
|
hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
|
|
if (!(_lFEFlags & (ES_SELFIME | ES_NOIME)))
|
|
{
|
|
// Application initiates reconversion
|
|
bReleaseSelction = GetTxSelection();
|
|
if (_pTextSel)
|
|
{
|
|
if (!(IsIMEComposition() || _pCUIM && _pCUIM->IsUIMTyping()))
|
|
{
|
|
if (_pCUIM && _pCUIM->Reconverse() >= 0)
|
|
break;
|
|
|
|
if (_fMSIME && MSIMEReconvertRequestMsg)
|
|
IMEMessage( *this, MSIMEReconvertRequestMsg, 0, (LPARAM)_hwnd, TRUE );
|
|
else
|
|
{
|
|
hr = OnIMEReconvert(pmsg, pwparam, plparam, plres, TRUE);
|
|
*plres = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case EM_SETLANGOPTIONS:
|
|
// Setup IME related setting.
|
|
// hr is not S_OK so textserv could handle other language setting
|
|
_fIMEAlwaysNotify = (*plparam & IMF_IMEALWAYSSENDNOTIFY) != 0;
|
|
_fIMECancelComplete = (*plparam & IMF_IMECANCELCOMPLETE) != 0;
|
|
*plres = 1;
|
|
break;
|
|
|
|
case EM_GETLANGOPTIONS:
|
|
// Report IME related setting.
|
|
// hr is not S_OK so textserv could fill in other language setting
|
|
if ( _fIMECancelComplete )
|
|
*plres |= IMF_IMECANCELCOMPLETE;
|
|
if ( _fIMEAlwaysNotify )
|
|
*plres |= IMF_IMEALWAYSSENDNOTIFY;
|
|
break;
|
|
|
|
case EM_GETIMECOMPMODE:
|
|
// Get current IME level
|
|
if (_pCUIM && _pCUIM->IsUIMTyping())
|
|
*plres = ICM_CTF;
|
|
else
|
|
*plres = OnGetIMECompositionMode( *this );
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case EM_SETUIM:
|
|
// This is RE private message equivalent to EM_SETEDITSTYLE
|
|
if (!_fNoIme) // Ignore if no IME
|
|
{
|
|
if (!_fUsingUIM && !_fUsingAIMM) // Ignore if we already using something
|
|
{
|
|
if (*pwparam == SES_USEAIMM11 || *pwparam == SES_USEAIMM12)
|
|
{
|
|
if (!_fTurnOffAIMM)
|
|
StartAimm(*pwparam == SES_USEAIMM12);
|
|
}
|
|
else if (!_fTurnOffUIM) // Client doesn't want UIM?
|
|
StartUIM();
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case EM_SETEDITSTYLE:
|
|
if (*plparam & SES_USECTF)
|
|
{
|
|
if ((*pwparam & SES_USECTF))
|
|
{
|
|
if (!_fRE10Mode)
|
|
{
|
|
if (_fUsingAIMM)
|
|
TurnOffAimm(TRUE);
|
|
|
|
// Turn on Cicero
|
|
if (!_fUsingUIM)
|
|
StartUIM();
|
|
|
|
goto SKIP_AIMM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Turn off Cicero
|
|
_fTurnOffUIM = 1; // Flag to ignore in EM_SETUIM
|
|
if (_fUsingUIM)
|
|
TurnOffUIM(TRUE);
|
|
}
|
|
}
|
|
|
|
if ((*pwparam & SES_USEAIMM) && ((*plparam & SES_USEAIMM) || *plparam == 0))
|
|
{
|
|
if (_fUsingUIM)
|
|
TurnOffUIM(TRUE);
|
|
|
|
if (!_fUsingAIMM)
|
|
{
|
|
hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
|
|
if (!(_lFEFlags & ES_NOIME)) // No IME style on?
|
|
StartAimm(TRUE);
|
|
}
|
|
}
|
|
else if ((*plparam & SES_USEAIMM))
|
|
{
|
|
_fTurnOffAIMM = 1; // Flag to ignore in EM_SETUIM
|
|
TurnOffAimm(TRUE);
|
|
}
|
|
|
|
SKIP_AIMM:
|
|
if ((*plparam == 0 || *plparam & SES_NOIME) && _hwnd)
|
|
{
|
|
if (*pwparam & SES_NOIME)
|
|
{
|
|
_fNoIme = 1;
|
|
TurnOffUIM(TRUE);
|
|
TurnOffAimm(TRUE);
|
|
|
|
if (!_hIMCContext)
|
|
_hIMCContext = ImmAssociateContext(_hwnd, NULL, _fUsingAIMM); // turn off IME
|
|
}
|
|
else if (*plparam & SES_NOIME)
|
|
{
|
|
_fNoIme = 0;
|
|
if (_hIMCContext)
|
|
ImmAssociateContext(_hwnd, _hIMCContext, _fUsingAIMM); // turn on IME
|
|
_hIMCContext = NULL;
|
|
}
|
|
}
|
|
|
|
if (*plparam & SES_CTFALLOWEMBED)
|
|
_fAllowEmbedded = (*pwparam & SES_CTFALLOWEMBED) ? 1 : 0;
|
|
|
|
if (*plparam & (SES_CTFALLOWSMARTTAG | SES_CTFALLOWPROOFING))
|
|
HandleCTFService(*pwparam, *plparam);
|
|
|
|
// remove settings that are handled.
|
|
*pwparam &= ~(SES_NOIME | SES_USEAIMM | SES_USECTF | SES_CTFALLOWEMBED | SES_CTFALLOWSMARTTAG | SES_CTFALLOWPROOFING);
|
|
*plparam &= ~(SES_NOIME | SES_USEAIMM | SES_USECTF | SES_CTFALLOWEMBED | SES_CTFALLOWSMARTTAG | SES_CTFALLOWPROOFING);
|
|
|
|
// fall thru to return the edit style
|
|
|
|
case EM_GETEDITSTYLE:
|
|
if (_hIMCContext)
|
|
*plres = SES_NOIME; // IME has been turned off
|
|
if (_fUsingAIMM)
|
|
*plres |= SES_USEAIMM; // AIMM is on
|
|
if (_fUsingUIM)
|
|
*plres |= SES_USECTF; // Cicero is on
|
|
|
|
// Cicero services
|
|
if (_fAllowEmbedded)
|
|
*plres |= SES_CTFALLOWEMBED;
|
|
if (_fAllowSmartTag)
|
|
*plres |= SES_CTFALLOWSMARTTAG;
|
|
if (_fAllowProofing)
|
|
*plres |= SES_CTFALLOWPROOFING;
|
|
|
|
break;
|
|
|
|
case EM_SETIMECOLOR:
|
|
if (_fRE10Mode)
|
|
{
|
|
COMPCOLOR* pcrComp = GetIMECompAttributes();
|
|
|
|
if (pcrComp)
|
|
{
|
|
memcpy(pcrComp, (const void *)(*plparam), sizeof(COMPCOLOR) * 4);
|
|
*plres = 1;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case EM_GETIMECOLOR:
|
|
if (_fRE10Mode)
|
|
{
|
|
COMPCOLOR* pcrComp = GetIMECompAttributes();
|
|
|
|
if (pcrComp)
|
|
{
|
|
memcpy((void *)(*plparam), pcrComp, sizeof(COMPCOLOR) * 4);
|
|
*plres = 1;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case EM_SETIMEMODEBIAS:
|
|
OnSetIMEMode(*pwparam, *plparam);
|
|
// following thru to return EM_GETIMEMODEBIAS
|
|
case EM_GETIMEMODEBIAS:
|
|
*plres = OnGetIMEMode();
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case EM_SETCTFMODEBIAS:
|
|
OnSetUIMMode(*pwparam);
|
|
// following thru to return EM_GETCTFMODEBIAS
|
|
case EM_GETCTFMODEBIAS:
|
|
*plres = OnGetUIMMode();
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case EM_SETCTFOPENSTATUS:
|
|
case EM_GETCTFOPENSTATUS:
|
|
*plres = 0;
|
|
if (_pCUIM)
|
|
*plres = _pCUIM->CTFOpenStatus(*pmsg == EM_GETCTFOPENSTATUS, *pwparam != 0);
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case EM_ISIME:
|
|
*plres = CheckIMEType(NULL, 0);
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case EM_GETIMEPROPERTY:
|
|
*plres = ImmGetProperty(GetKeyboardLayout(0x0FFFFFFFF), *pwparam, _fUsingAIMM);
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case EM_GETIMECOMPTEXT:
|
|
*plres = OnGetIMECompText(*pwparam, *plparam);
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
case WM_MOVE:
|
|
if (_pMsgCallBack)
|
|
_pMsgCallBack->NotifyEvents(NE_LAYOUTCHANGE);
|
|
break;
|
|
|
|
case EM_GETOLEINTERFACE:
|
|
if(*plparam && *pwparam == 0x0435446) // 'CTF'
|
|
{
|
|
if (_pCUIM && _pCUIM->GetITfContext())
|
|
{
|
|
*(ITfContext **)(*plparam) = _pCUIM->GetITfContext();
|
|
_pCUIM->GetITfContext()->AddRef();
|
|
}
|
|
else
|
|
*(IUnknown **)(*plparam) = 0;
|
|
|
|
*plres = TRUE;
|
|
hr = S_OK;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (*pmsg)
|
|
{
|
|
// Look for IME messages
|
|
if (*pmsg == MSIMEReconvertMsg || *pmsg == MSIMEDocFeedMsg
|
|
|| *pmsg == MSIMEQueryPositionMsg)
|
|
{
|
|
hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
|
|
if (!(_lFEFlags & (ES_SELFIME | ES_NOIME)))
|
|
{
|
|
bReleaseSelction = GetTxSelection();
|
|
if (_pTextSel)
|
|
{
|
|
if (*pmsg == MSIMEQueryPositionMsg)
|
|
hr = OnIMEQueryPos(pmsg, pwparam, plparam, plres, TRUE);
|
|
else
|
|
hr = OnIMEReconvert(pmsg, pwparam, plparam, plres, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_pCUIM && _pCUIM->_fMosueSink &&
|
|
(IN_RANGE(WM_MOUSEFIRST, *pmsg, WM_MBUTTONDBLCLK) || *pmsg == WM_SETCURSOR))
|
|
{
|
|
bReleaseSelction = GetTxSelection();
|
|
|
|
if (_pTextSel)
|
|
hr = _pCUIM->MouseCheck(pmsg, pwparam, plparam, plres);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
Exit:
|
|
// Release Selection if we get it for this message
|
|
if (bReleaseSelction && _pTextSel)
|
|
{
|
|
_pTextSel->Release();
|
|
_pTextSel = NULL;
|
|
}
|
|
|
|
// Return the value that will cause message to be processed normally
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CTextMsgFilter::AttachMsgFilter(ITextMsgFilter *)
|
|
*
|
|
* @mfunc
|
|
* Add another message filter to the chain
|
|
*
|
|
* @rdesc
|
|
* NOERROR if added
|
|
*/
|
|
HRESULT STDMETHODCALLTYPE CTextMsgFilter::AttachMsgFilter( ITextMsgFilter *pMsgFilter)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::AttachMsgFilter");
|
|
|
|
HRESULT hr = NOERROR;
|
|
if (_pFilter)
|
|
hr = _pFilter->AttachMsgFilter( pMsgFilter );
|
|
else
|
|
{
|
|
_pFilter = pMsgFilter;
|
|
_pFilter->AddRef();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CTextMsgFilter::OnWMChar(UINT *, WPARAM *, LPARAM *, LRESULT *)
|
|
*
|
|
* @mfunc
|
|
* Handle WM_CHAR message - look for Japanese keyboard with Kana key on
|
|
* Convert the SB Kana to Unicode if needed.
|
|
*
|
|
* @rdesc
|
|
* S_FALSE so caller will handle the modified character in wparam
|
|
*/
|
|
HRESULT CTextMsgFilter::OnWMChar(
|
|
UINT * pmsg,
|
|
WPARAM * pwparam,
|
|
LPARAM * plparam,
|
|
LRESULT * plres)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::OnWMChar");
|
|
|
|
// For Japanese keyboard, if Kana mode is on,
|
|
// Kana characters (single byte Japanese chars) are coming in via WM_CHAR.
|
|
if ( GetKeyState(VK_KANA) & 0x1 )
|
|
{
|
|
_uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
|
|
|
|
if (_uKeyBoardCodePage == CP_JAPAN)
|
|
{
|
|
// check if this is a single byte character.
|
|
TCHAR unicodeConvert;
|
|
BYTE bytes[2];
|
|
bytes[0] = (BYTE)(*pwparam >> 8); // Interchange DBCS bytes in endian
|
|
bytes[1] = (BYTE)*pwparam; // independent fashion (use byte array)
|
|
|
|
if (!bytes[0])
|
|
{
|
|
if(UnicodeFromMbcs((LPWSTR)&unicodeConvert, 1,
|
|
(LPCSTR)&bytes[1], 1, _uKeyBoardCodePage) == 1)
|
|
*pwparam = unicodeConvert;
|
|
}
|
|
|
|
return InputFEChar(*pwparam);
|
|
}
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CTextMsgFilter::OnWMIMEChar(UINT *, WPARAM *, LPARAM *, LRESULT *)
|
|
*
|
|
* @mfunc
|
|
* Handle WM_IMECHAR message - convert the character to unicode.
|
|
*
|
|
* @rdesc
|
|
* S_OK - caller to ignore the message
|
|
* S_FALSE - caller to handle the message. wparam may contains a new char
|
|
*/
|
|
HRESULT CTextMsgFilter::OnWMIMEChar(
|
|
UINT * pmsg,
|
|
WPARAM * pwparam,
|
|
LPARAM * plparam,
|
|
LRESULT * plres)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::OnWMIMEChar");
|
|
|
|
TCHAR unicodeConvert;
|
|
BYTE bytes[2];
|
|
|
|
// We may receive IMECHAR even if we have handled the composition char already.
|
|
// This is the case when the host does not call the DefWinProc with the composition
|
|
// bit masked off. So, we need to ignore this message to avoid double entry.
|
|
if (IsIMEComposition() && _ime->IgnoreIMECharMsg())
|
|
{
|
|
_ime->SkipIMECharMsg(); // Skip this ime char msg
|
|
return S_OK;
|
|
}
|
|
|
|
if (_fUnicodeWindow && !W32->OnWin9x())
|
|
return S_FALSE;
|
|
|
|
bytes[0] = *pwparam >> 8; // Interchange DBCS bytes in endian
|
|
bytes[1] = *pwparam; // independent fashion (use byte array)
|
|
|
|
// need to convert both single-byte KANA and DBC
|
|
if (!bytes[0] || GetTrailBytesCount(bytes[0], _uKeyBoardCodePage))
|
|
{
|
|
if( UnicodeFromMbcs((LPWSTR)&unicodeConvert, 1,
|
|
bytes[0] == 0 ? (LPCSTR)&bytes[1] : (LPCSTR)bytes,
|
|
bytes[0] == 0 ? 1 : 2,
|
|
_uKeyBoardCodePage) == 1 )
|
|
*pwparam = unicodeConvert;
|
|
|
|
return InputFEChar(*pwparam);
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CTextMsgFilter::OnIMEReconvert(UINT *, WPARAM *, LPARAM *, LRESULT *)
|
|
*
|
|
* @mfunc
|
|
* Handle IME Reconversion and Document feed. We only handle Unicode messages.
|
|
* We use a limit of MAX_RECONVERSION_SIZE(100) characters in both cases.
|
|
*
|
|
* @rdesc
|
|
* S_OK if we have handled the message
|
|
*/
|
|
HRESULT CTextMsgFilter::OnIMEReconvert(
|
|
UINT * pmsg,
|
|
WPARAM * pwparam,
|
|
LPARAM * plparam,
|
|
LRESULT * plres,
|
|
BOOL fUnicode)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::OnIMEReconvert");
|
|
|
|
HRESULT hr = S_OK;
|
|
LPRECONVERTSTRING lpRCS = (LPRECONVERTSTRING)(*plparam);
|
|
long cbStringSize;
|
|
long cpMin, cpMax;
|
|
long cpParaStart, cpParaEnd;
|
|
HRESULT hResult;
|
|
ITextRange *pTextRange, *pTempTextRange;
|
|
long cbAdded;
|
|
BOOL bDocumentFeed;
|
|
long cLastChar;
|
|
BOOL fAdjustedRange = FALSE;
|
|
|
|
*plres = 0;
|
|
|
|
// NT doesn't support Ansi window when the CP_ACP isn't the same
|
|
// as keyboard codepage.
|
|
if (!fUnicode && !(W32->OnWin9x()) && _uKeyBoardCodePage != _uSystemCodePage)
|
|
return S_OK;
|
|
|
|
bDocumentFeed = (MSIMEDocFeedMsg && *pmsg == MSIMEDocFeedMsg)
|
|
|| (*pmsg == WM_IME_REQUEST && *pwparam == IMR_DOCUMENTFEED);
|
|
|
|
if (bDocumentFeed && IsIMEComposition() && _ime->GetIMELevel() == IME_LEVEL_3)
|
|
{
|
|
// Composition in progress, use composition string as selection
|
|
cpMin = ((CIme_Lev3 *)_ime)->GetIMECompositionStart();
|
|
cpMax = ((CIme_Lev3 *)_ime)->GetIMECompositionLen() + cpMin;
|
|
}
|
|
else
|
|
{
|
|
// Get current selection
|
|
hResult = _pTextSel->GetStart(&cpMin);
|
|
hResult = _pTextSel->GetEnd(&cpMax);
|
|
}
|
|
|
|
// Expand to include the current paragraph
|
|
hResult = _pTextDoc->Range(cpMin, cpMax, &pTextRange);
|
|
Assert (pTextRange != NULL);
|
|
if (hResult != NOERROR)
|
|
return S_OK;
|
|
|
|
hResult = pTextRange->Expand(tomParagraph, &cbAdded);
|
|
|
|
// Fail to get Paragraph, get the story
|
|
// Note:- Expand will return S_FALSE for plain text when
|
|
// the whole story is selected
|
|
if (hResult != NOERROR)
|
|
hResult = pTextRange->Expand(tomStory, &cbAdded);
|
|
|
|
hResult = pTextRange->GetStart(&cpParaStart);
|
|
hResult = pTextRange->GetEnd(&cpParaEnd);
|
|
|
|
if (*pwparam == IMR_CONFIRMRECONVERTSTRING)
|
|
{
|
|
*plres = CheckIMEChange(lpRCS, cpParaStart, cpParaEnd, cpMin, cpMax, fUnicode);
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize to hugh number
|
|
_cpReconvertStart = tomForward;
|
|
|
|
// Check if Par included
|
|
hResult = _pTextDoc->Range(cpParaEnd-1, cpParaEnd, &pTempTextRange);
|
|
if (hResult != NOERROR)
|
|
goto Exit;
|
|
Assert (pTempTextRange != NULL);
|
|
|
|
hResult = pTempTextRange->GetChar(&cLastChar);
|
|
pTempTextRange->Release();
|
|
|
|
if (hResult == NOERROR && (WCHAR)cLastChar == CR)
|
|
{
|
|
if (cpMax == cpParaEnd)
|
|
{
|
|
// Par is selected, change selection to exclude the par char
|
|
cpMax--;
|
|
_pTextSel->SetEnd(cpMax);
|
|
|
|
if (cpMin > cpMax)
|
|
{
|
|
// Adjust cpMin as well
|
|
cpMin = cpMax;
|
|
_pTextSel->SetStart(cpMin);
|
|
}
|
|
}
|
|
|
|
// Get rid of par char
|
|
cpParaEnd--;
|
|
fAdjustedRange = TRUE;
|
|
}
|
|
|
|
// Check for MAX_RECONVERSION_SIZE since we don't want to pass a hugh buffer
|
|
// to IME
|
|
long cchSelected;
|
|
|
|
cchSelected = cpMax - cpMin;
|
|
if (cpParaEnd - cpParaStart > MAX_RECONVERSION_SIZE)
|
|
{
|
|
// Too many character selected, forget it
|
|
if (cchSelected > MAX_RECONVERSION_SIZE)
|
|
goto Exit;
|
|
|
|
if (cchSelected == MAX_RECONVERSION_SIZE)
|
|
{
|
|
// Selection reaches the limit
|
|
cpParaStart = cpMin;
|
|
cpParaEnd = cpMax;
|
|
}
|
|
else
|
|
{
|
|
long cchBeforeSelection = cpMin - cpParaStart;
|
|
long cchAfterSelection = cpParaEnd - cpMax;
|
|
long cchNeeded = MAX_RECONVERSION_SIZE - cchSelected;
|
|
|
|
if (cchBeforeSelection < cchNeeded/2)
|
|
{
|
|
// Put in all characters from the Par start
|
|
// and move Par end
|
|
cpParaEnd = cpParaStart + MAX_RECONVERSION_SIZE - 1;
|
|
}
|
|
else if (cchAfterSelection < cchNeeded/2)
|
|
{
|
|
// Put in all character to the Par end
|
|
// and move Par start
|
|
cpParaStart = cpParaEnd - MAX_RECONVERSION_SIZE + 1;
|
|
|
|
}
|
|
else
|
|
{
|
|
// Adjust both end
|
|
cpParaStart = cpMin - cchNeeded/2;
|
|
cpParaEnd = cpParaStart + MAX_RECONVERSION_SIZE - 1;
|
|
}
|
|
}
|
|
fAdjustedRange = TRUE;
|
|
}
|
|
|
|
if (fAdjustedRange)
|
|
{
|
|
// Adjust the text range
|
|
hResult = pTextRange->SetRange(cpParaStart, cpParaEnd);
|
|
|
|
if (hResult != NOERROR)
|
|
goto Exit;
|
|
}
|
|
|
|
cbStringSize = (cpParaEnd - cpParaStart) * 2;
|
|
|
|
// No char in current par, forget it.
|
|
if (cbStringSize <= 0)
|
|
goto Exit;
|
|
|
|
if (EM_RECONVERSION == *pmsg)
|
|
{
|
|
// RE reconversion msg, allocate the Reconversion buffer
|
|
lpRCS = (LPRECONVERTSTRING) PvAlloc(sizeof(RECONVERTSTRING) + cbStringSize + 2, GMEM_ZEROINIT);
|
|
Assert(lpRCS != NULL);
|
|
|
|
if (lpRCS)
|
|
lpRCS->dwSize = sizeof(RECONVERTSTRING) + cbStringSize + 2;
|
|
}
|
|
|
|
if (lpRCS)
|
|
{
|
|
BSTR bstr = NULL;
|
|
LPSTR lpReconvertBuff;
|
|
|
|
hResult = pTextRange->GetText(&bstr);
|
|
|
|
if (hResult != NOERROR || bstr == NULL)
|
|
{
|
|
if (EM_RECONVERSION == *pmsg)
|
|
FreePv(lpRCS);
|
|
goto Exit; // forget it
|
|
}
|
|
|
|
if (lpRCS->dwSize - sizeof(RECONVERTSTRING) - 2 < (DWORD)cbStringSize)
|
|
cbStringSize = lpRCS->dwSize - sizeof(RECONVERTSTRING) - 2;
|
|
|
|
lpReconvertBuff = (LPSTR)(lpRCS) + sizeof(RECONVERTSTRING);
|
|
|
|
if (fUnicode)
|
|
{
|
|
// fill in the buffer
|
|
memcpy(lpReconvertBuff, (LPSTR)bstr, cbStringSize);
|
|
|
|
*(lpReconvertBuff+cbStringSize) = '\0';
|
|
*(lpReconvertBuff+cbStringSize+1) = '\0';
|
|
|
|
lpRCS->dwStrLen = (cpParaEnd - cpParaStart);
|
|
lpRCS->dwCompStrLen = (cpMax - cpMin);
|
|
lpRCS->dwCompStrOffset = (cpMin - cpParaStart)*2; // byte offset from beginning of string
|
|
}
|
|
else
|
|
{
|
|
// Ansi case, need to find byte offset and Ansi string
|
|
long cch = WideCharToMultiByte(_uKeyBoardCodePage, 0, bstr, -1, lpReconvertBuff, cbStringSize+1, NULL, NULL);
|
|
Assert (cch > 0);
|
|
if (cch > 0)
|
|
{
|
|
CTempCharBuf tcb;
|
|
char *psz = tcb.GetBuf(cch);
|
|
|
|
if (!psz) // No memory
|
|
goto CleanUp; // forget it.
|
|
|
|
if (cch > 1 && lpReconvertBuff[cch-1] == '\0')
|
|
cch--; // Get rid of the null char
|
|
|
|
int cpOffset = cpMin - cpParaStart;
|
|
|
|
Assert(cpOffset >= 0);
|
|
lpRCS->dwStrLen = cch;
|
|
lpRCS->dwCompStrOffset = WideCharToMultiByte(_uKeyBoardCodePage, 0,
|
|
bstr, cpOffset, psz, cch, NULL, NULL);
|
|
|
|
lpRCS->dwCompStrLen = 0;
|
|
if (cpMax > cpMin)
|
|
lpRCS->dwCompStrLen = WideCharToMultiByte(_uKeyBoardCodePage, 0,
|
|
bstr+cpOffset, cpMax - cpMin, psz, cch, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
CleanUp:
|
|
SysFreeString (bstr);
|
|
if (EM_RECONVERSION == *pmsg)
|
|
FreePv(lpRCS);
|
|
goto Exit; // forget it
|
|
}
|
|
}
|
|
|
|
// Fill in the rest of the RCS struct
|
|
lpRCS->dwVersion = 0;
|
|
lpRCS->dwStrOffset = sizeof(RECONVERTSTRING); // byte offset from beginning of struct
|
|
lpRCS->dwTargetStrLen = lpRCS->dwCompStrLen;
|
|
lpRCS->dwTargetStrOffset = lpRCS->dwCompStrOffset;
|
|
|
|
*plres = sizeof(RECONVERTSTRING) + cbStringSize + 2;
|
|
|
|
// Save this for the CONFIRMRECONVERTSTRING handling
|
|
_cpReconvertStart = cpParaStart;
|
|
_cpReconvertEnd = cpParaEnd;
|
|
|
|
SysFreeString (bstr);
|
|
|
|
if (EM_RECONVERSION == *pmsg)
|
|
{
|
|
HIMC hIMC = LocalGetImmContext(*this);
|
|
|
|
if (hIMC)
|
|
{
|
|
DWORD imeProperties = ImmGetProperty(GetKeyboardLayout(0x0FFFFFFFF), IGP_SETCOMPSTR, _fUsingAIMM);
|
|
|
|
if ((imeProperties & (SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD))
|
|
== (SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD))
|
|
{
|
|
if (ImmSetCompositionStringW(hIMC, SCS_QUERYRECONVERTSTRING, lpRCS, *plres, NULL, 0, _fUsingAIMM))
|
|
{
|
|
// Check if there is any change in selection
|
|
CheckIMEChange(lpRCS, cpParaStart, cpParaEnd, cpMin, cpMax, TRUE);
|
|
ImmSetCompositionStringW(hIMC, SCS_SETRECONVERTSTRING, lpRCS, *plres, NULL, 0, _fUsingAIMM);
|
|
}
|
|
}
|
|
LocalReleaseImmContext(*this, hIMC);
|
|
}
|
|
|
|
FreePv(lpRCS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// return size for IME to allocate the buffer
|
|
*plres = sizeof(RECONVERTSTRING) + cbStringSize + 2;
|
|
}
|
|
|
|
Exit:
|
|
pTextRange->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* BOOL CTextMsgFilter::CheckIMEChange(LPRECONVERTSTRING,long,long,long,long)
|
|
*
|
|
* @mfunc
|
|
* Verify if IME wants to re-adjust the selection
|
|
*
|
|
* @rdesc
|
|
* TRUE - allow IME to change the selection
|
|
*/
|
|
BOOL CTextMsgFilter::CheckIMEChange(
|
|
LPRECONVERTSTRING lpRCS,
|
|
long cpParaStart,
|
|
long cpParaEnd,
|
|
long cpMin,
|
|
long cpMax,
|
|
BOOL fUnicode)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::CheckIMEChange");
|
|
|
|
long cpImeSelectStart = 0;
|
|
long cpImeSelectEnd = 0;
|
|
HRESULT hResult;
|
|
|
|
if (!lpRCS || _cpReconvertStart == tomForward)
|
|
// Never initialize, forget it
|
|
return FALSE;
|
|
|
|
if (fUnicode)
|
|
{
|
|
cpImeSelectStart = _cpReconvertStart + lpRCS->dwCompStrOffset / 2;
|
|
cpImeSelectEnd = cpImeSelectStart + lpRCS->dwCompStrLen;
|
|
}
|
|
else
|
|
{
|
|
// Need to convert the byte offset to char offset.
|
|
ITextRange *pTextRange;
|
|
BSTR bstr = NULL;
|
|
|
|
hResult = _pTextDoc->Range(_cpReconvertStart, _cpReconvertEnd, &pTextRange);
|
|
if (hResult != NOERROR)
|
|
return FALSE;
|
|
|
|
// Get the text
|
|
hResult = pTextRange->GetText(&bstr);
|
|
|
|
if (hResult == S_OK)
|
|
{
|
|
long cchReconvert = _cpReconvertEnd - _cpReconvertStart + 1;
|
|
CTempCharBuf tcb;
|
|
char *psz = tcb.GetBuf((cchReconvert)*2);
|
|
|
|
hResult = S_FALSE;
|
|
if (psz)
|
|
{
|
|
long cch = WideCharToMultiByte(_uKeyBoardCodePage, 0,
|
|
bstr, -1, psz, (cchReconvert)*2, NULL, NULL);
|
|
|
|
if (cch > 0)
|
|
{
|
|
long dwCompStrOffset, dwCompStrLen;
|
|
CTempWcharBuf twcb;
|
|
WCHAR *pwsz = twcb.GetBuf(cchReconvert);
|
|
|
|
if (pwsz)
|
|
{
|
|
dwCompStrOffset = MultiByteToWideChar(_uKeyBoardCodePage, 0,
|
|
psz, lpRCS->dwCompStrOffset, pwsz, cchReconvert);
|
|
|
|
dwCompStrLen = MultiByteToWideChar(_uKeyBoardCodePage, 0,
|
|
psz+lpRCS->dwCompStrOffset, lpRCS->dwCompStrLen, pwsz, cchReconvert);
|
|
|
|
Assert(dwCompStrOffset > 0 || dwCompStrLen > 0);
|
|
|
|
cpImeSelectStart = _cpReconvertStart + dwCompStrOffset;
|
|
cpImeSelectEnd = cpImeSelectStart + dwCompStrLen;
|
|
hResult = S_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bstr)
|
|
SysFreeString (bstr);
|
|
|
|
pTextRange->Release();
|
|
|
|
if (hResult != S_OK)
|
|
return FALSE;
|
|
}
|
|
|
|
if (cpParaStart <= cpImeSelectStart && cpImeSelectEnd <= cpParaEnd)
|
|
{
|
|
if (_pTextSel && (cpImeSelectStart != cpMin || cpImeSelectEnd != cpMax))
|
|
{
|
|
// IME changes selection.
|
|
hResult = _pTextSel->SetRange(cpImeSelectStart, cpImeSelectEnd);
|
|
|
|
if (hResult != NOERROR)
|
|
return FALSE;
|
|
}
|
|
return TRUE; // Allow Ime to change selection
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* BOOL CTextMsgFilter::GetTxSelection()
|
|
*
|
|
* @mfunc
|
|
* Get Selection if we haven't got it before
|
|
*
|
|
* @rdesc
|
|
* TRUE if this is first time getting the selection
|
|
* FALSE if it is already exist or no selection available.
|
|
*/
|
|
BOOL CTextMsgFilter::GetTxSelection()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::GetTxSelection");
|
|
|
|
HRESULT hResult;
|
|
|
|
if (_pTextSel)
|
|
return FALSE; // Already there
|
|
|
|
hResult = _pTextDoc->GetSelectionEx(&_pTextSel);
|
|
|
|
return _pTextSel ? TRUE : FALSE;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CTextMsgFilter::OnIMEQueryPos(UINT *, WPARAM *, LPARAM *, LRESULT *, BOOL)
|
|
*
|
|
* @mfunc
|
|
* Fill in the current character size and window rect. size.
|
|
*
|
|
* @rdesc
|
|
* S_OK
|
|
* *plres = 0 if we do not filled in data
|
|
*/
|
|
HRESULT CTextMsgFilter::OnIMEQueryPos(
|
|
UINT * pmsg,
|
|
WPARAM * pwparam,
|
|
LPARAM * plparam,
|
|
LRESULT * plres,
|
|
BOOL fUnicode)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::OnIMEQueryPos");
|
|
|
|
HRESULT hResult;
|
|
PIMECHARPOSITION pIMECharPos = (PIMECHARPOSITION)*plparam;
|
|
long cpRequest;
|
|
RECT rcArea;
|
|
ITextRange *pTextRange = NULL;
|
|
POINT ptTopPos, ptBottomPos = {0, 0};
|
|
bool fGetBottomPosFail = false;
|
|
|
|
if (pIMECharPos->dwSize != sizeof(IMECHARPOSITION))
|
|
goto Exit;
|
|
|
|
// NT doesn't support Ansi window when the CP_ACP isn't the same
|
|
// as keyboard codepage.
|
|
if (!fUnicode && !(W32->OnWin9x()) && _uKeyBoardCodePage != _uSystemCodePage)
|
|
goto Exit;
|
|
|
|
if (IsIMEComposition() && _ime->GetIMELevel() == IME_LEVEL_3)
|
|
{
|
|
cpRequest = ((CIme_Lev3 *)_ime)->GetIMECompositionStart();
|
|
if (fUnicode)
|
|
cpRequest += pIMECharPos->dwCharPos;
|
|
else if (pIMECharPos->dwCharPos > 0)
|
|
{
|
|
// Need to convert pIMECharPos->dwCharPos from Acp to Cp
|
|
long cchComp = ((CIme_Lev3 *)_ime)->GetIMECompositionLen();
|
|
long cchAcp = (long)(pIMECharPos->dwCharPos);
|
|
BSTR bstr;
|
|
WCHAR *pChar;
|
|
|
|
if (cchComp)
|
|
{
|
|
hResult = _pTextDoc->Range(cpRequest, cpRequest+cchComp, &pTextRange);
|
|
|
|
Assert (pTextRange != NULL);
|
|
if (hResult != NOERROR || !pTextRange)
|
|
goto Exit;
|
|
|
|
hResult = pTextRange->GetText(&bstr);
|
|
if (hResult != NOERROR )
|
|
goto Exit;
|
|
|
|
// The algorithm assumes that for a DBCS charset any character
|
|
// above 128 has two bytes, except for the halfwidth KataKana,
|
|
// which are single bytes in ShiftJis.
|
|
pChar = (WCHAR *)bstr;
|
|
Assert (pChar);
|
|
|
|
while (cchAcp > 0 && cchComp > 0)
|
|
{
|
|
cchAcp--;
|
|
if(*pChar >= 128 && (CP_JAPAN != _uKeyBoardCodePage ||
|
|
!IN_RANGE(0xFF61, *pChar, 0xFF9F)))
|
|
cchAcp--;
|
|
|
|
pChar++;
|
|
cchComp--;
|
|
cpRequest++;
|
|
}
|
|
|
|
SysFreeString (bstr);
|
|
pTextRange->Release();
|
|
pTextRange = NULL;
|
|
}
|
|
}
|
|
}
|
|
else if (pIMECharPos->dwCharPos == 0)
|
|
{
|
|
// Get current selection
|
|
hResult = _pTextSel->GetStart(&cpRequest);
|
|
if (hResult != NOERROR)
|
|
goto Exit;
|
|
}
|
|
else
|
|
goto Exit;
|
|
|
|
// Get requested cp location in screen coordinates
|
|
hResult = _pTextDoc->Range(cpRequest, cpRequest+1, &pTextRange);
|
|
Assert (pTextRange != NULL);
|
|
if (hResult != NOERROR || !pTextRange)
|
|
goto Exit;
|
|
|
|
long lTextFlow;
|
|
long lTopType;
|
|
long lBottomType;
|
|
|
|
lTextFlow = _lFEFlags & tomTextFlowMask;
|
|
lTopType = tomStart+TA_TOP+TA_LEFT;
|
|
lBottomType = tomStart+TA_BOTTOM+TA_LEFT;
|
|
|
|
if (lTextFlow == tomTextFlowWN)
|
|
{
|
|
lTopType = tomStart+TA_TOP+TA_RIGHT;
|
|
lBottomType = tomStart+TA_BOTTOM+TA_RIGHT ;
|
|
}
|
|
|
|
hResult = pTextRange->GetPoint( lTopType,
|
|
&(ptTopPos.x), &(ptTopPos.y) );
|
|
|
|
if (hResult != NOERROR)
|
|
{
|
|
// Scroll and try again
|
|
hResult = pTextRange->ScrollIntoView(tomStart);
|
|
if (hResult == NOERROR)
|
|
hResult = pTextRange->GetPoint( lTopType,
|
|
&(ptTopPos.x), &(ptTopPos.y) );
|
|
}
|
|
|
|
if (hResult == NOERROR)
|
|
{
|
|
hResult = pTextRange->GetPoint( lBottomType,
|
|
&(ptBottomPos.x), &(ptBottomPos.y) );
|
|
if (hResult != NOERROR)
|
|
fGetBottomPosFail = true;
|
|
}
|
|
|
|
pIMECharPos->pt = ptTopPos;
|
|
|
|
// Get application rect in screen coordinates
|
|
hResult = _pTextDoc->GetClientRect(tomIncludeInset,
|
|
&(rcArea.left), &(rcArea.top),
|
|
&(rcArea.right), &(rcArea.bottom));
|
|
|
|
if (hResult != NOERROR)
|
|
goto Exit;
|
|
|
|
// Get line height in pixel
|
|
if (fGetBottomPosFail)
|
|
pIMECharPos->cLineHeight = rcArea.bottom - ptTopPos.y;
|
|
else
|
|
{
|
|
if (lTextFlow == tomTextFlowSW || lTextFlow == tomTextFlowNE)
|
|
pIMECharPos->cLineHeight = abs(ptTopPos.x - ptBottomPos.x);
|
|
else
|
|
pIMECharPos->cLineHeight = abs(ptBottomPos.y - ptTopPos.y);
|
|
|
|
if (lTextFlow == tomTextFlowWN)
|
|
pIMECharPos->pt = ptBottomPos;
|
|
}
|
|
|
|
pIMECharPos->rcDocument = rcArea;
|
|
|
|
*plres = TRUE;
|
|
|
|
Exit:
|
|
if (pTextRange)
|
|
pTextRange->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::CheckIMEType(HKL hKL, DWORD dwFlags)
|
|
*
|
|
* @mfunc
|
|
* Check for FE IME keyboard and/or MSIME98 or later
|
|
*
|
|
* @rdesc
|
|
* TRUE if FE IME keyboard
|
|
*/
|
|
BOOL CTextMsgFilter::CheckIMEType(
|
|
HKL hKL,
|
|
DWORD dwFlags)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::CheckIMEType");
|
|
|
|
BOOL fFEKeyboard = FALSE;
|
|
|
|
if (!hKL)
|
|
hKL = GetKeyboardLayout(0x0FFFFFFFF); // Get default HKL if caller pass in NULL
|
|
|
|
// initialize to non MS IME
|
|
if (dwFlags & CHECK_IME_SERVICE) // Check MSIME98?
|
|
_fMSIME = 0;
|
|
|
|
if (IsFELCID((WORD)hKL) && ImmIsIME(hKL, _fUsingAIMM))
|
|
{
|
|
fFEKeyboard = TRUE;
|
|
|
|
if (dwFlags & CHECK_IME_SERVICE) // Check MSIME98?
|
|
{
|
|
if (MSIMEServiceMsg && IMEMessage( *this, MSIMEServiceMsg, 0, 0, FALSE ))
|
|
_fMSIME = 1;
|
|
}
|
|
}
|
|
return fFEKeyboard;
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::InputFEChar(WCHAR wchFEChar)
|
|
*
|
|
* @mfunc
|
|
* Input the FE character and ensure we have a correct font.
|
|
*
|
|
* @rdesc
|
|
* S_OK if handled
|
|
*/
|
|
HRESULT CTextMsgFilter::InputFEChar(
|
|
WCHAR wchFEChar)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::InputFEChar");
|
|
|
|
BOOL bReleaseSelction = GetTxSelection();
|
|
long cchExced;
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (wchFEChar > 256
|
|
&& _pTextSel->CanEdit(NULL) == NOERROR
|
|
&& _pTextDoc->CheckTextLimit(1, &cchExced) == NOERROR
|
|
&& cchExced == 0)
|
|
{
|
|
// setup FE font to handle the FE character
|
|
long cpMin, cpMax;
|
|
TCHAR wchFE[2];
|
|
BOOL fSelect = FALSE;
|
|
ITextRange *pTextRange = NULL;
|
|
ITextFont *pTextFont = NULL;
|
|
ITextFont *pFEFont = NULL;
|
|
HRESULT hResult = S_FALSE;
|
|
BSTR bstr = NULL;
|
|
|
|
// Inform client IME compostion is on to by-pass some font setting
|
|
// problem in Arabic systems
|
|
_pTextDoc->IMEInProgress(tomTrue);
|
|
|
|
wchFE[0] = wchFEChar;
|
|
wchFE[1] = L'\0';
|
|
|
|
_pTextSel->GetStart(&cpMin);
|
|
_pTextSel->GetEnd(&cpMax);
|
|
|
|
// For selection case, we want font to the right of first character
|
|
if (cpMin != cpMax)
|
|
{
|
|
hResult = _pTextDoc->Range(cpMin, cpMin, &pTextRange);
|
|
if (hResult != S_OK)
|
|
goto ERROR_EXIT;
|
|
|
|
hResult = pTextRange->GetFont(&pTextFont);
|
|
|
|
cpMin++;
|
|
fSelect = TRUE;
|
|
}
|
|
else
|
|
hResult = _pTextSel->GetFont(&pTextFont);
|
|
|
|
// Get a duplicate font and setup the correct FE font
|
|
hResult = pTextFont->GetDuplicate(&pFEFont);
|
|
|
|
if (hResult != S_OK)
|
|
goto ERROR_EXIT;
|
|
|
|
CIme::CheckKeyboardFontMatching (cpMin, this, pFEFont);
|
|
|
|
if (fSelect)
|
|
_pTextSel->SetText(NULL); // Delete the selection
|
|
|
|
bstr = SysAllocString(wchFE);
|
|
if (!bstr)
|
|
{
|
|
hResult = E_OUTOFMEMORY;
|
|
goto ERROR_EXIT;
|
|
}
|
|
|
|
_pTextSel->SetFont(pFEFont); // Setup FE font
|
|
_pTextSel->TypeText(bstr); // Input the new FE character
|
|
|
|
ERROR_EXIT:
|
|
if (hResult == S_OK)
|
|
hr = S_OK;
|
|
|
|
if (pFEFont)
|
|
pFEFont->Release();
|
|
|
|
if (pTextFont)
|
|
pTextFont->Release();
|
|
|
|
if (pTextRange)
|
|
pTextRange->Release();
|
|
|
|
if (bstr)
|
|
SysFreeString(bstr);
|
|
|
|
// Inform client IME compostion is done
|
|
_pTextDoc->IMEInProgress(tomFalse);
|
|
}
|
|
|
|
|
|
if (bReleaseSelction && _pTextSel)
|
|
{
|
|
_pTextSel->Release();
|
|
_pTextSel = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::OnSetFocus()
|
|
*
|
|
* @mfunc
|
|
* Restore the previous keyboard if we are in FORCEREMEMBER mode.
|
|
* Otherwise, setup the FE keyboard.
|
|
*
|
|
*/
|
|
void CTextMsgFilter::OnSetFocus()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::OnSetFocus");
|
|
|
|
if (_fUsingUIM && _pCUIM)
|
|
{
|
|
_pCUIM->OnSetFocus();
|
|
}
|
|
else if (_fForceRemember && _fIMEHKL)
|
|
{
|
|
// Restore previous keyboard
|
|
ActivateKeyboardLayout(_fIMEHKL, 0);
|
|
if (IsFELCID((WORD)_fIMEHKL))
|
|
{
|
|
// Set Open status and Conversion mode
|
|
HIMC hIMC = LocalGetImmContext(*this);
|
|
if (hIMC)
|
|
{
|
|
if (ImmSetOpenStatus(hIMC, _fIMEEnable, _fUsingAIMM) && _fIMEEnable)
|
|
ImmSetConversionStatus(hIMC, _fIMEConversion, _fIMESentence, _fUsingAIMM); // Set conversion status
|
|
|
|
LocalReleaseImmContext(*this, hIMC);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
SetupIMEOptions();
|
|
|
|
if (_nIMEMode)
|
|
SetIMESentenseMode(TRUE);
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::OnKillFocus()
|
|
*
|
|
* @mfunc
|
|
* If we are in FORCE_REMEMBER mode, save the current keyboard
|
|
* and conversion setting.
|
|
*
|
|
*/
|
|
void CTextMsgFilter::OnKillFocus()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::OnKillFocus");
|
|
|
|
// Windowless mode, need to inform Cicero
|
|
if (!_hwnd && _fUsingUIM && _pCUIM)
|
|
_pCUIM->OnSetFocus(FALSE);
|
|
|
|
if (_fForceRemember)
|
|
{
|
|
// Get current keyboard
|
|
_fIMEHKL = GetKeyboardLayout(0x0FFFFFFFF);
|
|
|
|
if (IsFELCID((WORD)_fIMEHKL))
|
|
{
|
|
// Get Open status
|
|
HIMC hIMC = LocalGetImmContext(*this);
|
|
if (hIMC)
|
|
{
|
|
_fIMEEnable = ImmGetOpenStatus(hIMC, _fUsingAIMM);
|
|
|
|
if (_fIMEEnable)
|
|
ImmGetConversionStatus(hIMC, &_fIMEConversion, &_fIMESentence, _fUsingAIMM); // get conversion status
|
|
|
|
LocalReleaseImmContext(*this, hIMC);
|
|
}
|
|
}
|
|
}
|
|
if (_nIMEMode)
|
|
SetIMESentenseMode(FALSE);
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::OnSetIMEOptions(WPARAM wparam, LPARAM lparam)
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*/
|
|
LRESULT CTextMsgFilter::OnSetIMEOptions(
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::OnSetIMEOptions");
|
|
|
|
LRESULT lIMEOptionCurrent = OnGetIMEOptions();
|
|
LRESULT lIMEOptionNew = 0;
|
|
|
|
// Mask off bits that we will support for now
|
|
lparam &= (IMF_FORCEACTIVE | IMF_FORCEENABLE | IMF_FORCEREMEMBER);
|
|
|
|
switch(wparam)
|
|
{
|
|
case ECOOP_SET:
|
|
lIMEOptionNew = lparam;
|
|
break;
|
|
|
|
case ECOOP_OR:
|
|
lIMEOptionNew = lIMEOptionCurrent | lparam;
|
|
break;
|
|
|
|
case ECOOP_AND:
|
|
lIMEOptionNew = lIMEOptionCurrent & lparam;
|
|
break;
|
|
|
|
case ECOOP_XOR:
|
|
lIMEOptionNew = lIMEOptionCurrent ^ lparam;
|
|
break;
|
|
|
|
default:
|
|
return 0; // Bad option
|
|
}
|
|
|
|
if (lIMEOptionNew == lIMEOptionCurrent) // Nothing change
|
|
return 1;
|
|
|
|
_fForceActivate = FALSE;
|
|
if (lIMEOptionNew & IMF_FORCEACTIVE)
|
|
_fForceActivate = TRUE;
|
|
|
|
_fForceEnable = FALSE;
|
|
if (lIMEOptionNew & IMF_FORCEENABLE)
|
|
_fForceEnable = TRUE;
|
|
|
|
_fForceRemember = FALSE;
|
|
if (lIMEOptionNew & IMF_FORCEREMEMBER)
|
|
_fForceRemember = TRUE;
|
|
|
|
SetupIMEOptions();
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::OnGetIMEOptions()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*/
|
|
LRESULT CTextMsgFilter::OnGetIMEOptions()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::OnGetIMEOptions");
|
|
|
|
LRESULT lres = 0;
|
|
|
|
if (_fForceActivate)
|
|
lres |= IMF_FORCEACTIVE;
|
|
|
|
if (_fForceEnable)
|
|
lres |= IMF_FORCEENABLE;
|
|
|
|
if (_fForceRemember)
|
|
lres |= IMF_FORCEREMEMBER;
|
|
|
|
return lres;
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::SetupIMEOptions()
|
|
*
|
|
* @mfunc
|
|
*
|
|
*/
|
|
void CTextMsgFilter::SetupIMEOptions()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::SetupIMEOptions");
|
|
|
|
if (!_hwnd)
|
|
return;
|
|
|
|
_uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
|
|
|
|
if (_fForceEnable)
|
|
{
|
|
LONG cpgLocale = GetACP();
|
|
INT iCharRep = CharRepFromCodePage(cpgLocale);
|
|
|
|
if (W32->IsFECodePage(cpgLocale))
|
|
{
|
|
if (_uKeyBoardCodePage != (UINT)cpgLocale)
|
|
W32->CheckChangeKeyboardLayout(iCharRep);
|
|
|
|
HIMC hIMC = LocalGetImmContext(*this);
|
|
|
|
if (hIMC)
|
|
{
|
|
if (ImmSetOpenStatus(hIMC, TRUE, _fUsingAIMM) && _fForceActivate)
|
|
{
|
|
// Activate native input mode
|
|
DWORD dwConversion;
|
|
DWORD dwSentence;
|
|
|
|
if (ImmGetConversionStatus(hIMC, &dwConversion, &dwSentence, _fUsingAIMM))
|
|
{
|
|
dwConversion |= IME_CMODE_NATIVE;
|
|
if (iCharRep == SHIFTJIS_INDEX)
|
|
dwConversion |= IME_CMODE_FULLSHAPE;
|
|
ImmSetConversionStatus(hIMC, dwConversion, dwSentence, _fUsingAIMM);
|
|
}
|
|
}
|
|
LocalReleaseImmContext(*this, hIMC);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::OnSetIMEMode(WPARAM wparam, LPARAM lparam)
|
|
*
|
|
* @mfunc
|
|
* Handle EM_SETIMEMODE message to setup or clear the IMF_SMODE_PHRASEPREDICT mode
|
|
*
|
|
*/
|
|
void CTextMsgFilter::OnSetIMEMode(
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::OnSetIMEMode");
|
|
|
|
BOOL fNotifyUIM = FALSE;
|
|
|
|
if (!(lparam & (IMF_SMODE_PLAURALCLAUSE | IMF_SMODE_NONE))) // Only IMF_SMODE_PHRASEPREDICT for now
|
|
return; // Bad mask option
|
|
|
|
if ((wparam & (IMF_SMODE_PLAURALCLAUSE | IMF_SMODE_NONE)) == _nIMEMode) // Nothing change...
|
|
return; // Done.
|
|
|
|
_nIMEMode = wparam & (IMF_SMODE_PLAURALCLAUSE | IMF_SMODE_NONE);
|
|
|
|
if (_hwnd && GetFocus() == _hwnd)
|
|
SetIMESentenseMode(_nIMEMode);
|
|
|
|
// Setup UIM mode bias
|
|
if (_nIMEMode)
|
|
{
|
|
if (_nIMEMode == IMF_SMODE_PLAURALCLAUSE && _wUIMModeBias != CTFMODEBIAS_NAME)
|
|
{
|
|
_wUIMModeBias = CTFMODEBIAS_NAME;
|
|
fNotifyUIM = TRUE;
|
|
}
|
|
else if (_nIMEMode == IMF_SMODE_NONE && _wUIMModeBias != CTFMODEBIAS_DEFAULT)
|
|
{
|
|
_wUIMModeBias = CTFMODEBIAS_DEFAULT;
|
|
fNotifyUIM = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_wUIMModeBias = 0;
|
|
fNotifyUIM = TRUE;
|
|
}
|
|
|
|
if (fNotifyUIM && _pMsgCallBack)
|
|
_pMsgCallBack->NotifyEvents(NE_MODEBIASCHANGE);
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::SetIMESentenseMode()
|
|
*
|
|
* @mfunc
|
|
* Setup phrase mode or restore previous sentence mode
|
|
*/
|
|
void CTextMsgFilter::SetIMESentenseMode(
|
|
BOOL fSetup,
|
|
HKL hKL)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::SetIMESentenseMode");
|
|
|
|
if (_hwnd && CheckIMEType(hKL, 0) && // FE IME Keyboard?
|
|
(fSetup || _fRestoreOLDIME))
|
|
{
|
|
HIMC hIMC = LocalGetImmContext(*this);
|
|
|
|
if (hIMC)
|
|
{
|
|
DWORD dwConversion;
|
|
DWORD dwSentence;
|
|
|
|
if (ImmGetConversionStatus(hIMC, &dwConversion, &dwSentence, _fUsingAIMM))
|
|
{
|
|
if (fSetup)
|
|
{
|
|
if (!_fRestoreOLDIME)
|
|
{
|
|
// Setup IME Mode
|
|
_wOldIMESentence = dwSentence & 0x0FFFF;
|
|
_fRestoreOLDIME = 1;
|
|
}
|
|
dwSentence &= 0x0FFFF0000;
|
|
if (_nIMEMode == IMF_SMODE_PLAURALCLAUSE)
|
|
dwSentence |= IME_SMODE_PLAURALCLAUSE;
|
|
else
|
|
dwSentence |= IME_SMODE_NONE;
|
|
}
|
|
else
|
|
{
|
|
// Restore previous mode
|
|
dwSentence &= 0x0FFFF0000;
|
|
dwSentence |= _wOldIMESentence;
|
|
_fRestoreOLDIME = 0;
|
|
}
|
|
|
|
ImmSetConversionStatus(hIMC, dwConversion, dwSentence, _fUsingAIMM);
|
|
}
|
|
|
|
LocalReleaseImmContext(*this, hIMC);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::OnGetIMECompText(WPARAM wparam, LPARAM lparam)
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*/
|
|
int CTextMsgFilter::OnGetIMECompText(
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::OnGetIMECompText");
|
|
if (_ime)
|
|
{
|
|
HRESULT hr;
|
|
IMECOMPTEXT *pIMECompText = (IMECOMPTEXT *)wparam;
|
|
|
|
if (pIMECompText->flags == ICT_RESULTREADSTR)
|
|
{
|
|
int cbSize = pIMECompText->cb;
|
|
hr = CIme::CheckInsertResultString(0, *this, NULL, &cbSize, (WCHAR *)lparam);
|
|
|
|
if (hr == S_OK)
|
|
return cbSize/2;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::NoIMEProcess()
|
|
*
|
|
* @mfunc
|
|
* check if you should handle IME
|
|
*/
|
|
BOOL CTextMsgFilter::NoIMEProcess()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::NoIMEProcess");
|
|
|
|
if (_fNoIme)
|
|
return TRUE;
|
|
|
|
_pTextDoc->GetFEFlags(&_lFEFlags);
|
|
|
|
if (_lFEFlags & (ES_NOIME | tomUsePassword))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::MouseOperation(UINT msg, long ichStart, long cchComp, WPARAM wParam,
|
|
* WPARAM *pwParamBefore, BOOL *pfTerminateIME, HWND hwndIME)
|
|
*
|
|
* @mfunc
|
|
* handle mouse operation for CTF or IME
|
|
*
|
|
* @rdesc
|
|
* BOOL-TRUE if CTF or IME handled the mouse events
|
|
*/
|
|
BOOL CTextMsgFilter::MouseOperation(
|
|
UINT msg,
|
|
long ichStart,
|
|
long cchComp,
|
|
WPARAM wParam,
|
|
WPARAM *pwParamBefore,
|
|
BOOL *pfTerminateIME,
|
|
HWND hwndIME,
|
|
long *pCpCursor,
|
|
ITfMouseSink *pMouseSink)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::MouseOperation");
|
|
BOOL fRetCode = FALSE;
|
|
BOOL fButtonPressed = FALSE;
|
|
WORD wButtons = 0;
|
|
POINT ptCursor;
|
|
WPARAM wParamIME;
|
|
WPARAM fwkeys = wParam;
|
|
BOOL fHandleIME = hwndIME ? TRUE : FALSE;
|
|
HWND hHostWnd = _hwnd;
|
|
long hWnd;
|
|
|
|
if (!hHostWnd) // Windowless mode...
|
|
{
|
|
if (_pTextDoc->GetWindow(&hWnd) != S_OK || !hWnd)
|
|
return FALSE;
|
|
|
|
hHostWnd = (HWND)(DWORD_PTR)hWnd;
|
|
}
|
|
|
|
*pfTerminateIME = TRUE;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_MOUSEMOVE:
|
|
goto LCheckButton;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
fButtonPressed = TRUE;
|
|
case WM_LBUTTONDBLCLK:
|
|
fwkeys |= MK_LBUTTON;
|
|
goto LCheckButton;
|
|
|
|
case WM_LBUTTONUP:
|
|
fwkeys &= (~MK_LBUTTON);
|
|
goto LCheckButton;
|
|
|
|
case WM_RBUTTONDOWN:
|
|
fButtonPressed = TRUE;
|
|
case WM_RBUTTONDBLCLK:
|
|
fwkeys |= MK_RBUTTON;
|
|
goto LCheckButton;
|
|
|
|
case WM_RBUTTONUP:
|
|
fwkeys &= (~MK_RBUTTON);
|
|
goto LCheckButton;
|
|
|
|
case WM_MBUTTONUP:
|
|
fwkeys &= (~MK_MBUTTON);
|
|
goto LCheckButton;
|
|
|
|
case WM_MBUTTONDOWN:
|
|
fButtonPressed = TRUE;
|
|
case WM_MBUTTONDBLCLK:
|
|
fwkeys |= MK_MBUTTON;
|
|
LCheckButton:
|
|
if (fwkeys & MK_LBUTTON)
|
|
wButtons |= IMEMOUSE_LDOWN;
|
|
if (fwkeys & MK_RBUTTON)
|
|
wButtons |= IMEMOUSE_RDOWN;
|
|
if (fwkeys & MK_MBUTTON)
|
|
wButtons |= IMEMOUSE_MDOWN;
|
|
break;
|
|
|
|
case WM_SETCURSOR:
|
|
wButtons = LOBYTE(*pwParamBefore);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
// Kor special - click should terminate IME
|
|
if (fHandleIME && fButtonPressed && _uKeyBoardCodePage == CP_KOREAN)
|
|
{
|
|
*pfTerminateIME = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
// Change in button since last message?
|
|
if ((wButtons != LOBYTE(LOWORD(*pwParamBefore))) && GetCapture() == hHostWnd)
|
|
{
|
|
fButtonPressed = FALSE;
|
|
wButtons = 0;
|
|
ReleaseCapture();
|
|
}
|
|
|
|
if (GetCursorPos(&ptCursor))
|
|
{
|
|
ITextRange *pTextRange;
|
|
HRESULT hResult;
|
|
long ichCursor;
|
|
long lTextFlow;
|
|
POINT ptCPTop = {0, 0};
|
|
POINT ptCPBottom = {0, 0};
|
|
POINT ptCenterTop = {0, 0};
|
|
POINT ptCenterBottom = {0, 0};
|
|
BOOL fWithinCompText = FALSE;
|
|
|
|
// Get cp at current Cursor position
|
|
hResult = _pTextDoc->RangeFromPoint(ptCursor.x, ptCursor.y,
|
|
&pTextRange);
|
|
|
|
if (hResult != NOERROR)
|
|
return FALSE;
|
|
|
|
_pTextDoc->GetFEFlags(&lTextFlow);
|
|
lTextFlow &= tomTextFlowMask;
|
|
|
|
hResult = pTextRange->GetStart(&ichCursor);
|
|
pTextRange->GetPoint(TA_TOP, &(ptCPTop.x), &(ptCPTop.y));
|
|
pTextRange->GetPoint(TA_BOTTOM, &(ptCPBottom.x), &(ptCPBottom.y));
|
|
pTextRange->Release();
|
|
pTextRange = NULL;
|
|
if (hResult != NOERROR)
|
|
return FALSE;
|
|
|
|
if (pCpCursor)
|
|
*pCpCursor = ichCursor;
|
|
|
|
// Click within composition text?
|
|
if (ichStart <= ichCursor && ichCursor <= ichStart + cchComp)
|
|
{
|
|
WORD wPos = 0;
|
|
|
|
LONG lTestCursor = TestPoint(ptCPTop, ptCPBottom, ptCursor, TEST_ALL, lTextFlow);
|
|
|
|
if (lTestCursor & (TEST_TOP | TEST_BOTTOM))
|
|
goto HIT_OUTSIDE;
|
|
|
|
// Cursor locates to the left of the first composition character
|
|
// or cursor locates to the right of the last composition character
|
|
if (ichStart == ichCursor && (lTestCursor & TEST_LEFT) ||
|
|
ichCursor == ichStart + cchComp && (lTestCursor & TEST_RIGHT))
|
|
goto HIT_OUTSIDE;
|
|
|
|
// Need to calculate the relative position of the Cursor and the center of character:
|
|
//
|
|
// If Cursor locates to the Left of the cp,
|
|
// If Cursor is more than 1/4 the character width from the cp
|
|
// wPos = 0;
|
|
// Otherwise
|
|
// wPos = 1;
|
|
//
|
|
// If Cursor locates to the Right of the cp,
|
|
// If Cursor is less than 1/4 the character width from the cp
|
|
// wPos = 2;
|
|
// Otherwise
|
|
// wPos = 3;
|
|
//
|
|
if (lTestCursor & TEST_LEFT)
|
|
hResult = _pTextDoc->Range(ichCursor-1, ichCursor, &pTextRange);
|
|
else
|
|
hResult = _pTextDoc->Range(ichCursor, ichCursor+1, &pTextRange);
|
|
|
|
if (pTextRange)
|
|
{
|
|
LONG lTestCenter = 0;
|
|
LONG uMouse = 0;
|
|
LONG uHalfCenter = 0;
|
|
|
|
pTextRange->GetPoint(tomStart + TA_TOP + TA_CENTER, &(ptCenterTop.x), &(ptCenterTop.y));
|
|
pTextRange->GetPoint(tomStart + TA_BOTTOM + TA_CENTER, &(ptCenterBottom.x), &(ptCenterBottom.y));
|
|
pTextRange->Release();
|
|
|
|
lTestCenter = TestPoint(ptCPTop, ptCPBottom, ptCenterBottom, TEST_ALL, lTextFlow);
|
|
|
|
if (lTestCenter & (TEST_TOP | TEST_BOTTOM))
|
|
goto HIT_OUTSIDE; // Not on the same line
|
|
|
|
if (lTextFlow == tomTextFlowES || lTextFlow == tomTextFlowWN)
|
|
{
|
|
uMouse = ptCursor.x - ptCPBottom.x;
|
|
uHalfCenter = ptCenterBottom.x - ptCPBottom.x;
|
|
}
|
|
else
|
|
{
|
|
uMouse = ptCursor.y - ptCPBottom.y;
|
|
uHalfCenter = ptCenterBottom.y - ptCPBottom.y;
|
|
}
|
|
|
|
uMouse = abs(uMouse);
|
|
uHalfCenter = abs(uHalfCenter) / 2;
|
|
|
|
if (lTestCursor & TEST_LEFT)
|
|
{
|
|
if (lTestCenter & TEST_LEFT)
|
|
wPos = uMouse > uHalfCenter ? 0: 1;
|
|
}
|
|
else if (lTestCenter & TEST_RIGHT)
|
|
wPos = uMouse >= uHalfCenter ? 3: 2;
|
|
|
|
wButtons = MAKEWORD(wButtons, wPos);
|
|
}
|
|
|
|
wParamIME = MAKEWPARAM(wButtons, ichCursor - ichStart);
|
|
fButtonPressed &= (*pwParamBefore & 0xff) == 0;
|
|
|
|
if (*pwParamBefore != wParamIME || fHandleIME && msg == WM_MOUSEMOVE && !fButtonPressed)
|
|
{
|
|
*pwParamBefore = wParamIME;
|
|
if (fHandleIME) // IME case
|
|
{
|
|
HIMC hIMC = LocalGetImmContext(*this);
|
|
|
|
if (hIMC)
|
|
{
|
|
fRetCode = SendMessage(hwndIME, MSIMEMouseMsg, *pwParamBefore, hIMC);
|
|
LocalReleaseImmContext(*this, hIMC);
|
|
}
|
|
}
|
|
else // Cicero case
|
|
{
|
|
BOOL fEaten = FALSE;
|
|
DWORD dwBtn = 0;
|
|
|
|
dwBtn |= wButtons & IMEMOUSE_LDOWN ? MK_LBUTTON : 0;
|
|
dwBtn |= wButtons & IMEMOUSE_MDOWN ? MK_MBUTTON : 0;
|
|
dwBtn |= wButtons & IMEMOUSE_RDOWN ? MK_RBUTTON : 0;
|
|
|
|
if (S_OK == pMouseSink->OnMouseEvent(ichCursor - ichStart, wPos, dwBtn, &fEaten) && fEaten)
|
|
fRetCode = TRUE;
|
|
}
|
|
}
|
|
else
|
|
fRetCode = TRUE; // No change from last time, no need to send message to IME
|
|
|
|
fWithinCompText = TRUE;
|
|
if (fHandleIME && fRetCode && fButtonPressed && GetCapture() != hHostWnd)
|
|
SetCapture(hHostWnd);
|
|
}
|
|
|
|
HIT_OUTSIDE:
|
|
if (!fWithinCompText && (GetCapture() == hHostWnd || msg == WM_LBUTTONUP)) //We don't want to determine while dragging...
|
|
fRetCode = TRUE;
|
|
}
|
|
|
|
*pfTerminateIME = !fRetCode;
|
|
return fRetCode;
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::CompleteUIMTyping(LONG mode, BOOL fTransaction)
|
|
*
|
|
* @mfunc
|
|
* Terminate IME or UIM composition
|
|
*
|
|
*/
|
|
void CTextMsgFilter::CompleteUIMTyping(
|
|
LONG mode,
|
|
BOOL fTransaction)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::CompleteUIMTyping");
|
|
|
|
if (_ime)
|
|
{
|
|
Assert(!(_pCUIM && _pCUIM->IsUIMTyping()));
|
|
_ime->TerminateIMEComposition(*this, (CIme::TerminateMode)mode);
|
|
}
|
|
else
|
|
{
|
|
Assert (_pCUIM);
|
|
if (_pCUIM && _fSendTransaction == 0)
|
|
{
|
|
if (fTransaction)
|
|
{
|
|
ITextStoreACPSink *ptss = _pCUIM->_ptss;
|
|
|
|
if (ptss)
|
|
{
|
|
_fSendTransaction = 1;
|
|
ptss->OnStartEditTransaction();
|
|
}
|
|
}
|
|
_pCUIM->CompleteUIMText();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::GetIMECompAttributes()
|
|
*
|
|
* @mfunc
|
|
* Get the 1.0 mode IME color and underline for displaying cmposition strings
|
|
*
|
|
* @rdesc
|
|
* COMPCOLOR *. Could be NULL if PvAlloc failed
|
|
*
|
|
*/
|
|
COMPCOLOR* CTextMsgFilter::GetIMECompAttributes()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::GetIMECompAttributes");
|
|
|
|
// For 1.0 mode IME color
|
|
if (!_pcrComp)
|
|
{
|
|
_pcrComp = (COMPCOLOR *)PvAlloc(sizeof(COMPCOLOR) * 4, GMEM_ZEROINIT);
|
|
|
|
if (_pcrComp)
|
|
{
|
|
// Init. IME composition color/underline the same way as RE1.0
|
|
_pcrComp[0].crBackground = 0x0ffffff;
|
|
_pcrComp[0].dwEffects = CFE_UNDERLINE;
|
|
_pcrComp[1].crBackground = 0x0808080;
|
|
_pcrComp[2].crBackground = 0x0ffffff;
|
|
_pcrComp[2].dwEffects = CFE_UNDERLINE;
|
|
_pcrComp[3].crText = 0x0ffffff;
|
|
}
|
|
}
|
|
|
|
return _pcrComp;
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::SetupCallback()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
void CTextMsgFilter::SetupCallback()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::SetupCallback");
|
|
|
|
if (!_pMsgCallBack)
|
|
_pMsgCallBack = new CMsgCallBack(this);
|
|
if (_pMsgCallBack)
|
|
{
|
|
LRESULT lresult;
|
|
_pTextService->TxSendMessage(EM_SETCALLBACK, 0, (LPARAM)_pMsgCallBack, &lresult);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::SetupLangSink()
|
|
*
|
|
* @mfunc
|
|
* Setup the Language sink to catch the keyboard changing event. We are not
|
|
* getting WM_INPUTLANGCHANGEREQUEST and thus need this sink.
|
|
*
|
|
*/
|
|
void CTextMsgFilter::SetupLangSink()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::SetupLangSink");
|
|
|
|
if (!_pITfIPP)
|
|
{
|
|
CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_ITfInputProcessorProfiles, (void**)&_pITfIPP);
|
|
|
|
if (_pITfIPP)
|
|
{
|
|
_pCLangProfileSink = new CLangProfileSink();
|
|
if (_pCLangProfileSink)
|
|
{
|
|
if (_pCLangProfileSink->_Advise(this, _pITfIPP) != S_OK)
|
|
{
|
|
_pCLangProfileSink->Release();
|
|
_pCLangProfileSink = NULL;
|
|
_pITfIPP->Release();
|
|
_pITfIPP = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_pITfIPP->Release();
|
|
_pITfIPP = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::ReleaseLangSink()
|
|
*
|
|
* @mfunc
|
|
* Release the lang sink object
|
|
*
|
|
*/
|
|
void CTextMsgFilter::ReleaseLangSink()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::ReleaseLangSink");
|
|
|
|
if (_pITfIPP)
|
|
{
|
|
Assert(_pCLangProfileSink);
|
|
|
|
_pCLangProfileSink->_Unadvise();
|
|
_pCLangProfileSink->Release();
|
|
_pCLangProfileSink = NULL;
|
|
|
|
_pITfIPP->Release();
|
|
_pITfIPP = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::StartUIM()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
void CTextMsgFilter::StartUIM()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::StartUIM");
|
|
|
|
if (NoIMEProcess())
|
|
return;
|
|
|
|
_fUsingUIM = CreateUIM(this);
|
|
|
|
if (_fUsingUIM)
|
|
{
|
|
SetupCallback();
|
|
SetupLangSink();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::StartAimm()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
void CTextMsgFilter::StartAimm(BOOL fUseAimm12)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::StartAimm");
|
|
|
|
if (!_hwnd || NoIMEProcess())
|
|
return;
|
|
|
|
if (LoadAIMM(fUseAimm12))
|
|
{
|
|
HRESULT hResult = ActivateAIMM(FALSE);
|
|
|
|
if (hResult == NOERROR)
|
|
{
|
|
DWORD dwAtom;
|
|
ATOM aClass;
|
|
|
|
// filter client windows
|
|
if (dwAtom = GetClassLong(_hwnd, GCW_ATOM))
|
|
{
|
|
aClass = dwAtom;
|
|
hResult = FilterClientWindowsAIMM(&aClass, 1, _hwnd);
|
|
}
|
|
_fUsingAIMM = 1;
|
|
SetupCallback();
|
|
|
|
if (!fLoadAIMM10)
|
|
SetupLangSink();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::TurnOffUIM()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
void CTextMsgFilter::TurnOffUIM(BOOL fSafeToSendMessage)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::TurnOffUIM");
|
|
|
|
if (fSafeToSendMessage && _fUsingUIM && _pCUIM && _pCUIM->IsUIMTyping())
|
|
CompleteUIMTyping(CIme::TERMINATE_NORMAL);
|
|
|
|
_fUsingUIM = FALSE;
|
|
|
|
ReleaseLangSink();
|
|
|
|
// Release various objects
|
|
if (_pCUIM)
|
|
{
|
|
CUIM *pCUIM = _pCUIM;
|
|
LRESULT lresult;
|
|
|
|
_pCUIM = NULL;
|
|
|
|
if (fSafeToSendMessage)
|
|
_pTextService->TxSendMessage(EM_SETUPNOTIFY, 0, (LPARAM)(ITxNotify *)pCUIM, &lresult);
|
|
else
|
|
pCUIM->_fShutDown = 1;
|
|
|
|
pCUIM->Uninit();
|
|
pCUIM->Release();
|
|
|
|
}
|
|
|
|
if (_pTim)
|
|
{
|
|
ITfThreadMgr *pTim = _pTim;
|
|
|
|
_pTim = NULL;
|
|
pTim->Deactivate();
|
|
pTim->Release();
|
|
}
|
|
|
|
// Turn off Callback
|
|
if (fSafeToSendMessage && _pMsgCallBack)
|
|
{
|
|
LRESULT lresult;
|
|
_pTextService->TxSendMessage(EM_SETCALLBACK, 0, (LPARAM)0, &lresult);
|
|
delete _pMsgCallBack;
|
|
_pMsgCallBack = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::HandleCTFService(wparam, lparam)
|
|
*
|
|
* @mfunc
|
|
* Setup Cicero setting to handle or disable smarttag and proofing services
|
|
*
|
|
*/
|
|
void CTextMsgFilter::HandleCTFService(
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::HandleCTFService");
|
|
|
|
BOOL fChangeInSetting = FALSE;
|
|
|
|
if (lparam & SES_CTFALLOWSMARTTAG)
|
|
{
|
|
BOOL fAllowSmartTagLocal = (wparam & SES_CTFALLOWSMARTTAG) ? 1 : 0;
|
|
|
|
if ((BOOL)_fAllowSmartTag != fAllowSmartTagLocal)
|
|
{
|
|
_fAllowSmartTag = fAllowSmartTagLocal;
|
|
fChangeInSetting = TRUE;
|
|
}
|
|
}
|
|
if (lparam & SES_CTFALLOWPROOFING)
|
|
{
|
|
BOOL fAllowProofLocal = (wparam & SES_CTFALLOWPROOFING) ? 1 : 0;
|
|
|
|
if ((BOOL)_fAllowProofing != fAllowProofLocal)
|
|
{
|
|
_fAllowProofing = fAllowProofLocal;
|
|
fChangeInSetting = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fChangeInSetting)
|
|
{
|
|
if (_fUsingUIM && _pCUIM)
|
|
_pCUIM->NotifyService();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTextMsgFilter::TurnOffAimm()
|
|
*
|
|
* @mfunc
|
|
* Turn off Aimm.
|
|
*
|
|
*/
|
|
void CTextMsgFilter::TurnOffAimm(BOOL fSafeToSendMessage)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::TurnOffAimm");
|
|
|
|
if (_fUsingAIMM)
|
|
{
|
|
if (IsIMEComposition())
|
|
{
|
|
if (fSafeToSendMessage)
|
|
CompleteUIMTyping(CIme::TERMINATE_NORMAL);
|
|
else
|
|
{
|
|
delete _ime;
|
|
_ime = NULL;
|
|
}
|
|
}
|
|
|
|
_fUsingAIMM = FALSE;
|
|
|
|
UnfilterClientWindowsAIMM(_hwnd);
|
|
DeactivateAIMM();
|
|
|
|
ReleaseLangSink();
|
|
|
|
// Turn off Callback
|
|
if (fSafeToSendMessage && _pMsgCallBack)
|
|
{
|
|
LRESULT lresult;
|
|
_pTextService->TxSendMessage(EM_SETCALLBACK, 0, (LPARAM)0, &lresult);
|
|
delete _pMsgCallBack;
|
|
_pMsgCallBack = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* void CTextMsgFilter::OnSetUIMMode()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
void CTextMsgFilter::OnSetUIMMode(WORD wUIMModeBias)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CTextMsgFilter::OnSetUIMMode");
|
|
|
|
if (_wUIMModeBias != wUIMModeBias &&
|
|
IN_RANGE(CTFMODEBIAS_DEFAULT, wUIMModeBias, CTFMODEBIAS_HALFWIDTHALPHANUMERIC))
|
|
{
|
|
_wUIMModeBias = wUIMModeBias;
|
|
|
|
if (_pMsgCallBack)
|
|
_pMsgCallBack->NotifyEvents(NE_MODEBIASCHANGE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* HRESULT CMsgCallBack::HandlePostMessage()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CMsgCallBack::HandlePostMessage(
|
|
HWND hWnd,
|
|
UINT msg,
|
|
WPARAM wparam,
|
|
LPARAM lparam,
|
|
LRESULT *plres)
|
|
{
|
|
//TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CMsgCallBack::HandlePostMessage");
|
|
|
|
if (_pTextMsgFilter->_fUsingAIMM)
|
|
return CallAIMMDefaultWndProc(hWnd, msg, wparam, lparam, plres);
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* HRESULT CMsgCallBack::NotifyEvents()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CMsgCallBack::NotifyEvents(DWORD dwEvents)
|
|
{
|
|
//TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CMsgCallBack::NotifyEvents");
|
|
|
|
CUIM *pCUIM = _pTextMsgFilter->_pCUIM;
|
|
|
|
if (pCUIM)
|
|
{
|
|
ITextStoreACPSink *ptss = pCUIM->_ptss;
|
|
|
|
if (dwEvents & NE_ENTERTOPLEVELCALLMGR)
|
|
{
|
|
pCUIM->_cCallMgrLevels++;
|
|
}
|
|
else if (dwEvents & NE_EXITTOPLEVELCALLMGR)
|
|
{
|
|
Assert (pCUIM->_cCallMgrLevels > 0);
|
|
pCUIM->_cCallMgrLevels--;
|
|
}
|
|
|
|
if (pCUIM->_cCallMgrLevels)
|
|
{
|
|
// Save events to be sent later
|
|
if ((dwEvents & NE_CALLMGRSELCHANGE) && !pCUIM->_fReadLockOn)
|
|
pCUIM->_fSelChangeEventPending = 1;
|
|
|
|
if (dwEvents & (NE_CALLMGRCHANGE | NE_LAYOUTCHANGE))
|
|
pCUIM->_fLayoutEventPending = 1;
|
|
|
|
if (dwEvents & NE_MODEBIASCHANGE)
|
|
pCUIM->_fModeBiasPending = 1;
|
|
}
|
|
else
|
|
{
|
|
if (pCUIM->_fSelChangeEventPending || (dwEvents & NE_CALLMGRSELCHANGE))
|
|
{
|
|
pCUIM->_fSelChangeEventPending = 0;
|
|
if (ptss && !pCUIM->_fHoldCTFSelChangeNotify && !pCUIM->_fReadLockOn)
|
|
ptss->OnSelectionChange();
|
|
}
|
|
|
|
if (pCUIM->_fLayoutEventPending || (dwEvents & (NE_CALLMGRCHANGE | NE_LAYOUTCHANGE)))
|
|
{
|
|
pCUIM->_fLayoutEventPending = 0;
|
|
if (ptss)
|
|
ptss->OnLayoutChange(TS_LC_CHANGE, 0);
|
|
}
|
|
|
|
if (pCUIM->_fModeBiasPending || (dwEvents & NE_MODEBIASCHANGE))
|
|
{
|
|
pCUIM->_fModeBiasPending = 0;
|
|
if (ptss)
|
|
{
|
|
LONG ccpMax = 0;
|
|
|
|
if (pCUIM->GetStoryLength(&ccpMax) != S_OK)
|
|
ccpMax = tomForward;
|
|
|
|
ptss->OnAttrsChange(0, ccpMax, 1, &GUID_PROP_MODEBIAS); // only ModeBias for now
|
|
}
|
|
}
|
|
|
|
// Probably safe to let UIM to lock data now
|
|
if (ptss && (pCUIM->_fReadLockPending || pCUIM->_fWriteLockPending))
|
|
{
|
|
HRESULT hResult;
|
|
HRESULT hResult1;
|
|
|
|
hResult = pCUIM->RequestLock(pCUIM->_fWriteLockPending ? TS_LF_READWRITE : TS_LF_READ, &hResult1);
|
|
}
|
|
|
|
if (_pTextMsgFilter->_fSendTransaction)
|
|
{
|
|
_pTextMsgFilter->_fSendTransaction = 0;
|
|
if (ptss)
|
|
ptss->OnEndEditTransaction();
|
|
}
|
|
|
|
pCUIM->_fHoldCTFSelChangeNotify = 0;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CLangProfileSink::QueryInterface()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDAPI CLangProfileSink::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CLangProfileSink::QueryInterface");
|
|
|
|
*ppvObj = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
|
IsEqualIID(riid, IID_ITfLanguageProfileNotifySink))
|
|
*ppvObj = this;
|
|
|
|
if (*ppvObj)
|
|
{
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
/*
|
|
* CLangProfileSink::AddRef()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDAPI_(ULONG) CLangProfileSink::AddRef()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CLangProfileSink::AddRef");
|
|
|
|
return ++_cRef;
|
|
}
|
|
|
|
/*
|
|
* CLangProfileSink::Release()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDAPI_(ULONG) CLangProfileSink::Release()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CLangProfileSink::Release");
|
|
|
|
long cr;
|
|
|
|
cr = --_cRef;
|
|
Assert(cr >= 0);
|
|
|
|
if (cr == 0)
|
|
delete this;
|
|
|
|
return cr;
|
|
}
|
|
|
|
/*
|
|
* CLangProfileSink::CLangProfileSink()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
CLangProfileSink::CLangProfileSink()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CLangProfileSink::CLangProfileSink");
|
|
|
|
_cRef = 1;
|
|
_dwCookie = (DWORD)(-1);
|
|
}
|
|
|
|
/*
|
|
* CLangProfileSink::OnLanguageChange()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CLangProfileSink::OnLanguageChange(LANGID langid, BOOL *pfAccept)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CLangProfileSink::OnLanguageChange");
|
|
|
|
Assert (pfAccept);
|
|
|
|
*pfAccept = TRUE;
|
|
|
|
if (_pTextMsgFilter->_hwnd && GetFocus() == _pTextMsgFilter->_hwnd)
|
|
{
|
|
LRESULT lresult = 0;
|
|
if ( S_OK == _pTextMsgFilter->_pTextService->TxSendMessage(
|
|
EM_GETDOCFLAGS, GDF_ALL, 0, &lresult))
|
|
{
|
|
if (lresult & GDF_SINGLECPG)
|
|
{
|
|
LCID syslcid = GetSysLCID();
|
|
|
|
// Check if new langid supported by the system
|
|
if (langid != syslcid)
|
|
{
|
|
LOCALESIGNATURE ls;
|
|
|
|
if(GetLocaleInfoA(langid, LOCALE_FONTSIGNATURE, (LPSTR)&ls, sizeof(ls)))
|
|
{
|
|
CHARSETINFO cs;
|
|
HDC hdc = GetDC(_pTextMsgFilter->_hwnd);
|
|
TranslateCharsetInfo((DWORD *)(DWORD_PTR)GetTextCharsetInfo(hdc, NULL, 0), &cs, TCI_SRCCHARSET);
|
|
ReleaseDC(_pTextMsgFilter->_hwnd, hdc);
|
|
DWORD fsShell = cs.fs.fsCsb[0];
|
|
if (!(fsShell & ls.lsCsbSupported[0]))
|
|
*pfAccept = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*pfAccept == TRUE && _pTextMsgFilter-> _nIMEMode)
|
|
_pTextMsgFilter->SetIMESentenseMode(FALSE);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CLangProfileSink::OnLanguageChanged()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
STDMETHODIMP CLangProfileSink::OnLanguageChanged()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CLangProfileSink::OnLanguageChanged");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CLangProfileSink::_Advise()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CLangProfileSink::_Advise(
|
|
CTextMsgFilter *pTextMsgFilter,
|
|
ITfInputProcessorProfiles *pipp)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CLangProfileSink::_Advise");
|
|
|
|
HRESULT hr;
|
|
ITfSource *pSource = NULL;
|
|
|
|
_pTextMsgFilter = pTextMsgFilter;
|
|
_pITFIPP = pipp;
|
|
hr = E_FAIL;
|
|
|
|
if (FAILED(_pITFIPP->QueryInterface(IID_ITfSource, (void **)&pSource)))
|
|
goto Exit;
|
|
|
|
if (FAILED(pSource->AdviseSink(IID_ITfLanguageProfileNotifySink, this, &_dwCookie)))
|
|
goto Exit;
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
pSource->Release();
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CLangProfileSink::_Unadvise()
|
|
*
|
|
* @mfunc
|
|
*
|
|
* @rdesc
|
|
*
|
|
*/
|
|
HRESULT CLangProfileSink::_Unadvise()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CLangProfileSink::_Unadvise");
|
|
|
|
HRESULT hr;
|
|
ITfSource *pSource = NULL;
|
|
|
|
hr = E_FAIL;
|
|
|
|
if (_pITFIPP == NULL)
|
|
return hr;
|
|
|
|
if (FAILED(_pITFIPP->QueryInterface(IID_ITfSource, (void **)&pSource)))
|
|
return hr;
|
|
|
|
if (FAILED(pSource->UnadviseSink(_dwCookie)))
|
|
goto Exit;
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
pSource->Release();
|
|
return hr;
|
|
}
|
|
|
|
#endif // NOFEPROCESSING
|