/* * @doc INTERNAL * * @module CMSGFLT.CPP -- Text Message Implementation | * * Most everything to do with IME message handling. * * Original Author: * Hon Wah Chan * * History: * 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