/* * @doc INTERNAL * * @module ime.cpp -- support for Win95 IME API | * * Most everything to do with FE composition string editing passes * through here. * * Authors: * Jon Matousek * Hon Wah Chan * Justin Voskuhl * * History: * 10/18/1995 jonmat Cleaned up level 2 code and converted it into * a class hierarchy supporting level 3. * * Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved. */ #include "_common.h" #ifndef NOFEPROCESSING #include "msctf.h" #include "textserv.h" #include "_cmsgflt.h" #include "_ime.h" #include "imeapp.h" #define HAVE_COMPOSITION_STRING() ( 0 != (lparam & (GCS_COMPSTR | GCS_COMPATTR))) #define CLEANUP_COMPOSITION_STRING() ( 0 == lparam ) #define HAVE_RESULT_STRING() ( 0 != (lparam & GCS_RESULTSTR)) ASSERTDATA /* * HRESULT StartCompositionGlue (CTextMsgFilter &TextMsgFilter) * * @func * Initiates an IME composition string edit. * @comm * Called from the message loop to handle WM_IME_STARTCOMPOSITION. * This is a glue routine into the IME object hierarchy. * * @devnote * We decide if we are going to do a level 2 or level 3 IME * composition string edit. Currently, the only reason to * create a level 2 IME is if the IME has a special UI, or it is * a "near caret" IME, such as the ones found in PRC and Taiwan. * Near caret simply means that a very small window opens up * near the caret, but not on or at the caret. * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. */ HRESULT StartCompositionGlue ( CTextMsgFilter &TextMsgFilter) // @parm containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "StartCompositionGlue"); if(TextMsgFilter.IsIMEComposition() && TextMsgFilter._ime->IsTerminated() && !TextMsgFilter._ime->_compMessageRefCount && !(TextMsgFilter._fHangulToHanja)) { delete TextMsgFilter._ime; TextMsgFilter._ime = NULL; } if(!TextMsgFilter.IsIMEComposition()) { if(TextMsgFilter._pTextSel->CanEdit(NULL) == NOERROR && !TextMsgFilter.NoIMEProcess()) { // Hold notification if needed if (!(TextMsgFilter._fIMEAlwaysNotify)) TextMsgFilter._pTextDoc->SetNotificationMode(tomFalse); // If a special UI, or IME is "near caret", then drop into lev. 2 mode. DWORD imeProperties = ImmGetProperty(GetKeyboardLayout(0x0FFFFFFFF), IGP_PROPERTY, TextMsgFilter._fUsingAIMM); // use Unicode if not running under Win95 TextMsgFilter._fUnicodeIME = (imeProperties & IME_PROP_UNICODE) && !W32->OnWin95(); if ((imeProperties & IME_PROP_SPECIAL_UI) || !(imeProperties & IME_PROP_AT_CARET)) { TextMsgFilter._ime = new CIme_Lev2(TextMsgFilter); // level 2 IME. } else TextMsgFilter._ime = new CIme_Lev3(TextMsgFilter); // level 3 IME->TrueInline. } else // Protect or read-only or NOFEPROCESSING: TextMsgFilter._ime = new CIme_Protected; // Ignore all ime input } else { // Ignore further StartCompositionMsg. // Hanin 5.1 CHT symbol could cause multiple StartCompoisitonMsg. return S_OK; } if(TextMsgFilter.IsIMEComposition()) { long lSelFlags; HRESULT hResult; hResult = TextMsgFilter._pTextSel->GetFlags(&lSelFlags); if (hResult == NOERROR) { TextMsgFilter._fOvertypeMode = !!(lSelFlags & tomSelOvertype); if (TextMsgFilter._fOvertypeMode) TextMsgFilter._pTextSel->SetFlags(lSelFlags & ~tomSelOvertype); // Turn off overtype mode } TextMsgFilter._pTextDoc->IMEInProgress(tomTrue); // Inform client IME compostion in progress return TextMsgFilter._ime->StartComposition(TextMsgFilter); // Make the method call. } else TextMsgFilter._pTextDoc->SetNotificationMode(tomTrue); return S_FALSE; } /* * HRESULT CompositionStringGlue (const LPARAM lparam, CTextMsgFilter &TextMsgFilter) * * @func * Handle all intermediary and final composition strings. * * @comm * Called from the message loop to handle WM_IME_COMPOSITION. * This is a glue routine into the IME object hierarchy. * We may be called independently of a WM_IME_STARTCOMPOSITION * message, in which case we return S_FALSE to allow the * DefWindowProc to return WM_IME_CHAR messages. * * @devnote * Side Effect: the _ime object may be deleted if composition * string processing is finished. * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. */ HRESULT CompositionStringGlue ( const LPARAM lparam, // @parm associated with message. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CompositionStringGlue"); HRESULT hr = S_FALSE; if(TextMsgFilter.IsIMEComposition()) // A priori fHaveIMMProcs. { TextMsgFilter._ime->_compMessageRefCount++; // For proper deletion. // Make the method call. hr = TextMsgFilter._ime->CompositionString(lparam, TextMsgFilter); TextMsgFilter._ime->_compMessageRefCount--; // For proper deletion. Assert (TextMsgFilter._ime->_compMessageRefCount >= 0); CheckDestroyIME (TextMsgFilter); // Finished processing? } else // Even when not in composition mode, we may receive a result string. { DWORD imeProperties = ImmGetProperty(GetKeyboardLayout(0x0FFFFFFFF), IGP_PROPERTY, TextMsgFilter._fUsingAIMM); long lSelFlags; HRESULT hResult; long cpMin, cpMax; hResult = TextMsgFilter._pTextSel->GetFlags(&lSelFlags); if (hResult == NOERROR) { TextMsgFilter._fOvertypeMode = !!(lSelFlags & tomSelOvertype); if (TextMsgFilter._fOvertypeMode) TextMsgFilter._pTextSel->SetFlags(lSelFlags & ~tomSelOvertype); // Turn off overtype mode } // Use Unicode if not running under Win95 TextMsgFilter._fUnicodeIME = (imeProperties & IME_PROP_UNICODE) && !W32->OnWin95(); TextMsgFilter._pTextSel->GetStart(&cpMin); TextMsgFilter._pTextSel->GetEnd(&cpMax); if (cpMin != cpMax) TextMsgFilter._pTextSel->SetText(NULL); // Delete current selection CIme::CheckKeyboardFontMatching (cpMin, &TextMsgFilter, NULL); TextMsgFilter._pTextDoc->IMEInProgress(tomTrue); // Inform client IME compostion in progress hr = CIme::CheckInsertResultString(lparam, TextMsgFilter); if(TextMsgFilter._fOvertypeMode) TextMsgFilter._pTextSel->SetFlags(lSelFlags | tomSelOvertype); // Turn on overtype mode TextMsgFilter._pTextDoc->IMEInProgress(tomFalse); // Inform client IME compostion is done } return hr; } /* * HRESULT EndCompositionGlue (CTextMsgFilter &TextMsgFilter, BOOL fForceDelete) * * @func * Composition string processing is about to end. * * @comm * Called from the message loop to handle WM_IME_ENDCOMPOSITION. * This is a glue routine into the IME object hierarchy. * * @devnote * The only time we have to handle WM_IME_ENDCOMPOSITION is when the * user changes input method during typing. For such case, we will get * a WM_IME_ENDCOMPOSITION message without getting a WM_IME_COMPOSITION * message with GCS_RESULTSTR later. So, we will call CompositionStringGlue * with GCS_RESULTSTR to let CompositionString to get rid of the string. * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. */ HRESULT EndCompositionGlue ( CTextMsgFilter &TextMsgFilter, // @parm the containing message filter. BOOL fForceDelete) // @parm forec to terminate { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "EndCompositionGlue"); if(TextMsgFilter.IsIMEComposition()) { // ignore the EndComposition message if necessary. We may // get this from 3rd party IME - EGBRIGDE after we have received // both result and composition strings. if ( !(TextMsgFilter._ime->_fIgnoreEndComposition) ) { // Set this flag. If we are still in composition mode, then // let the CompositionStringGlue() to destroy the ime object. TextMsgFilter._ime->_fDestroy = TRUE; if (!fForceDelete) CompositionStringGlue(GCS_COMPSTR , TextMsgFilter); // Remove any remaining composition string. // Finished with IME, destroy it. CheckDestroyIME(TextMsgFilter); // Turn on undo TextMsgFilter._pTextDoc->Undo(tomResume, NULL); // Inform client IME compostion is done TextMsgFilter._pTextDoc->IMEInProgress(tomFalse); } else { // reset this so we will handle next EndComp msg TextMsgFilter._ime->_fIgnoreEndComposition = FALSE; } if(!TextMsgFilter.IsIMEComposition() && TextMsgFilter._fOvertypeMode) { long lSelFlags; HRESULT hResult; ITextSelection *pLocalTextSel = TextMsgFilter._pTextSel; BOOL fRelease = FALSE; if (!pLocalTextSel) { // Get the selection TextMsgFilter._pTextDoc->GetSelectionEx(&pLocalTextSel); fRelease = TRUE; } if (pLocalTextSel) { hResult = pLocalTextSel->GetFlags(&lSelFlags); if (hResult == NOERROR) pLocalTextSel->SetFlags(lSelFlags | tomSelOvertype); // Turn on overtype mode if (fRelease) pLocalTextSel->Release(); } } } return S_FALSE; } /* * HIMC LocalGetImmContext ( CTextMsgFilter &TextMsgFilter ) * * @func * Get Imm Context from host * */ HIMC LocalGetImmContext( CTextMsgFilter &TextMsgFilter) { //TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "LocalGetImmContext"); HIMC hIMC = NULL; // Host's IME context. HRESULT hResult; hResult = TextMsgFilter._pTextDoc->GetImmContext((long *)&hIMC); if (hResult != NOERROR) hIMC = ImmGetContext(TextMsgFilter._hwnd, TextMsgFilter._fUsingAIMM); // Get host's IME context. return hIMC; } /* * void LocalReleaseImmContext ( CTextMsgFilter &TextMsgFilter, HIMC hIMC ) * * @func * call host to Release Imm Context * */ void LocalReleaseImmContext( CTextMsgFilter &TextMsgFilter, HIMC hIMC ) { //TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "LocalReleaseImmContext"); HRESULT hResult; hResult = TextMsgFilter._pTextDoc->ReleaseImmContext((long)hIMC); if (hResult != NOERROR) ImmReleaseContext(TextMsgFilter._hwnd, hIMC, TextMsgFilter._fUsingAIMM); } /* * long IMEShareToTomUL ( UINT ulID ) * * @func * Convert IMEShare underline to Tom underline. * * @rdesc * Tom underline value */ long IMEShareToTomUL ( UINT ulID ) { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMEShareToTomUL"); long lTomUnderline; switch (ulID) { case IMESTY_UL_NONE: lTomUnderline = tomNone; break; case IMESTY_UL_DOTTED: lTomUnderline = tomDotted; break; case IMESTY_UL_THICK: case IMESTY_UL_THICKLOWER: lTomUnderline = tomThick; break; case IMESTY_UL_DITHLOWER: case IMESTY_UL_THICKDITHLOWER: lTomUnderline = tomWave; break; // case IMESTY_UL_SINGLE: // case IMESTY_UL_LOWER: default: lTomUnderline = tomSingle; break; } return lTomUnderline; } /* * void IMEMessage (CTextMsgFilter &TextMsgFilter , UINT uMsg, BOOL bPostMessage) * * @func * Either post or send message to IME * */ BOOL IMEMessage( CTextMsgFilter &TextMsgFilter, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL bPostMessage) { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMEMessage"); HIMC hIMC; // Host's IME context. HWND hwndIME; BOOL fRetCode = FALSE; HWND hHostWnd = TextMsgFilter._hwnd; long hWnd; if (!hHostWnd) // Windowless mode... { if (TextMsgFilter._pTextDoc->GetWindow(&hWnd) != S_OK || !hWnd) return FALSE; hHostWnd = (HWND)(DWORD_PTR)hWnd; } hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context. if(hIMC) { hwndIME = ImmGetDefaultIMEWnd(hHostWnd, TextMsgFilter._fUsingAIMM); LocalReleaseImmContext(TextMsgFilter, hIMC); // check if we want to send or post message if (hwndIME) { if (bPostMessage) fRetCode = PostMessage(hwndIME, uMsg, wParam, lParam); else fRetCode = SendMessage(hwndIME, uMsg, wParam, lParam); } } return fRetCode; } /* * void CheckDestroyIME (CTextMsgFilter &TextMsgFilter) * * @func * Check for IME and see detroy if it needs it.. * */ void CheckDestroyIME ( CTextMsgFilter &TextMsgFilter) { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CheckDestroyIME"); if(TextMsgFilter.IsIMEComposition() && TextMsgFilter._ime->_fDestroy) { if(0 == TextMsgFilter._ime->_compMessageRefCount) { if (TextMsgFilter._uKeyBoardCodePage == CP_KOREAN) { TextMsgFilter._pTextDoc->SetCaretType(tomNormalCaret); // Reset Block caret mode TextMsgFilter._fHangulToHanja = FALSE; // Reset korean conversion mode } delete TextMsgFilter._ime; // All done with object. TextMsgFilter._ime = NULL; TextMsgFilter._pTextDoc->SetNotificationMode(tomTrue); // Turn on Notification } } } /* * void PostIMECharGlue (CTextMsgFilter &TextMsgFilter) * * @func * Called after processing a single WM_IME_CHAR in order to * update the position of the IME's composition window. This * is glue code to call the CIME virtual equivalent. */ void PostIMECharGlue ( CTextMsgFilter &TextMsgFilter) // @parm containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "PostIMECharGlue"); if(TextMsgFilter.IsIMEComposition()) TextMsgFilter._ime->PostIMEChar(TextMsgFilter); } /* * BOOL IMEMouseCheck(CTextMsgFilter &TextMsgFilter, UINT *pmsg, * WPARAM *pwparam, LPARAM *plparam, LRESULT *plres) * * @func * Called when receiving a mouse event. Need to pass this event * to MSIME98 for composition handling * */ HRESULT IMEMouseCheck( CTextMsgFilter &TextMsgFilter, // @parm MsgFilter UINT *pmsg, // @parm the message WPARAM *pwparam, // @parm WParam LPARAM *plparam, // @parm LParam LRESULT *plres) // @parm Lresult { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMEMouseCheck"); BOOL fRetCode = FALSE; if(TextMsgFilter.IsIMEComposition()) { BOOL fTerminateIME; fRetCode = TextMsgFilter._ime->IMEMouseOperation(TextMsgFilter, *pmsg, *pwparam, fTerminateIME); if ( fTerminateIME && WM_MOUSEMOVE != *pmsg ) TextMsgFilter._ime->TerminateIMEComposition(TextMsgFilter, CIme::TERMINATE_NORMAL); } return fRetCode ? S_OK : S_FALSE; } /* * HRESULT IMENotifyGlue (const WPARAM wparam, const LPARAM lparam, * CTextMsgFilter &TextMsgFilter) * * @func * IME is going to change some state. * * @comm * Currently we are interested in knowing when the candidate * window is about to be opened. * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. */ HRESULT IMENotifyGlue ( const WPARAM wparam, // @parm associated with message. const LPARAM lparam, // @parm associated with message. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMENotifyGlue"); if (TextMsgFilter._fRE10Mode && (wparam == IMN_SETCONVERSIONMODE || wparam == IMN_SETSENTENCEMODE || wparam == IMN_SETOPENSTATUS)) { TextMsgFilter._pTextDoc->Notify(EN_IMECHANGE); } else if(TextMsgFilter.IsIMEComposition()) // A priori fHaveIMMProcs. return TextMsgFilter._ime->IMENotify(wparam, lparam, TextMsgFilter, FALSE);// Make the method call return S_FALSE; } /* * void IMECompositionFull (&TextMsgFilter) * * @func * Current IME Composition window is full. * * @comm * Called from the message loop to handle WM_IME_COMPOSITIONFULL. * This message applied to Level 2 only. We will use the default * IME Composition window. */ void IMECompositionFull ( CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMECompositionFull"); if(TextMsgFilter.IsIMEComposition()) { HIMC hIMC = LocalGetImmContext(TextMsgFilter); COMPOSITIONFORM cf; if(hIMC) { // No room for text input in the current level 2 IME window, // fall back to use the default IME window for input. cf.dwStyle = CFS_DEFAULT; ImmSetCompositionWindow(hIMC, &cf, TextMsgFilter._fUsingAIMM); // Set composition window. LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context. } } } /* * LRESULT OnGetIMECompositionMode (CTextMsgFilter &TextMsgFilter) * * @mfunc * Returns whether or not IME composition is being handled by RE, * and if so, what level of processing. * * @rdesc * One of ICM_NOTOPEN, ICM_LEVEL2_5, ICM_LEVEL2_SUI, ICM_LEVEL2, ICM_LEVEL3. */ LRESULT OnGetIMECompositionMode ( CTextMsgFilter &TextMsgFilter) // @parm containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "OnGetIMECompositionMode"); if(TextMsgFilter.IsIMEComposition()) return TextMsgFilter._ime->GetIMECompositionMode(TextMsgFilter); return ICM_NOTOPEN; } /* * LRESULT TestPoint (&pt1, &pt2, &ptTest, lTestOption, lTextFlow) * * @mfunc * Returns which side the ptTest is relative to the line (pt1, pt2) based on the lTextFlow. * * @rdesc * Sides detected */ LONG TestPoint( POINT &pt1, POINT &pt2, POINT &ptTest, LONG lTestOption, LONG lTextFlow) { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "TestPoint"); LONG lSidesDetect = 0; switch (lTextFlow) { case tomTextFlowES: if (lTestOption & TEST_LEFT) lSidesDetect |= (ptTest.x < pt1.x) ? TEST_LEFT : 0; if (lTestOption & TEST_RIGHT) lSidesDetect |= (ptTest.x > pt1.x) ? TEST_RIGHT : 0; if (lTestOption & TEST_TOP) lSidesDetect |= (ptTest.y < pt1.y) ? TEST_TOP : 0; if (lTestOption & TEST_BOTTOM) lSidesDetect |= (ptTest.y > pt2.y) ? TEST_BOTTOM : 0; break; case tomTextFlowSW: if (lTestOption & TEST_LEFT) lSidesDetect |= (ptTest.y < pt1.y) ? TEST_LEFT : 0; if (lTestOption & TEST_RIGHT) lSidesDetect |= (ptTest.y > pt1.y) ? TEST_RIGHT : 0; if (lTestOption & TEST_TOP) lSidesDetect |= (ptTest.x > pt1.x) ? TEST_TOP : 0; if (lTestOption & TEST_BOTTOM) lSidesDetect |= (ptTest.x < pt2.x) ? TEST_BOTTOM : 0; break; case tomTextFlowWN: if (lTestOption & TEST_LEFT) lSidesDetect |= (ptTest.x > pt1.x) ? TEST_LEFT : 0; if (lTestOption & TEST_RIGHT) lSidesDetect |= (ptTest.x < pt1.x) ? TEST_RIGHT : 0; if (lTestOption & TEST_TOP) lSidesDetect |= (ptTest.y > pt1.y) ? TEST_TOP : 0; if (lTestOption & TEST_BOTTOM) lSidesDetect |= (ptTest.y < pt2.y) ? TEST_BOTTOM : 0; break; case tomTextFlowNE: if (lTestOption & TEST_LEFT) lSidesDetect |= (ptTest.y > pt1.y) ? TEST_LEFT : 0; if (lTestOption & TEST_RIGHT) lSidesDetect |= (ptTest.y < pt1.y) ? TEST_RIGHT : 0; if (lTestOption & TEST_TOP) lSidesDetect |= (ptTest.x < pt1.x) ? TEST_TOP : 0; if (lTestOption & TEST_BOTTOM) lSidesDetect |= (ptTest.x > pt2.x) ? TEST_BOTTOM : 0; break; } return lSidesDetect; } /* * void CIme::CheckKeyboardFontMatching (long cp, CTextMsgFilter &TextMsgFilter, ITextFont *pTextFont) * * @mfunc * Setup current font to matches the keyboard Codepage. * * @comm * Called from CIme_Lev2::CIme_Lev2 and CompositionStringGlue * * @devnote * We need to switch to a preferred font for the keyboard during IME input. * Otherwise, we will display garbage. * */ void CIme::CheckKeyboardFontMatching ( long cp, CTextMsgFilter *pTextMsgFilter, ITextFont *pTextFont) { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::CheckKeyboardFontMatching"); long lPitchAndFamily; HRESULT hResult; BSTR bstr = NULL; long lValue; long lNewFontSize=0; float nFontSize; ITextFont *pLocalFont = NULL; if (!pTextMsgFilter) return; if (!pTextFont) { // No font supplied, get current font from selection hResult = pTextMsgFilter->_pTextSel->GetFont(&pLocalFont); if (hResult != S_OK || !pLocalFont) // Can't get font, forget it return; pTextFont = pLocalFont; } // Check if current font matches the keyboard lValue = tomCharset; hResult = pTextFont->GetLanguageID(&lValue); BYTE bCharSet = (BYTE)lValue; BYTE bCharSetKB = GetCharSet(pTextMsgFilter->_uKeyBoardCodePage); if (hResult == S_OK && bCharSet == bCharSetKB) goto Exit; // Current font is fine hResult = pTextFont->GetSize(&nFontSize); if (hResult != S_OK) goto Exit; hResult = pTextMsgFilter->_pTextDoc->GetPreferredFont(cp, pTextMsgFilter->_uKeyBoardCodePage, tomMatchFontCharset, CodePageFromCharRep(CharRepFromCharSet(bCharSet)), (long)nFontSize, &bstr, &lPitchAndFamily, &lNewFontSize); if (hResult == S_OK) { pTextFont->Reset(tomApplyLater); if (bstr) pTextFont->SetName(bstr); // Set the font charset and Pitch&Family by overloading the SetLanguageID i/f lValue = tomCharset + ((BYTE)lPitchAndFamily << 8) + bCharSetKB; pTextFont->SetLanguageID(lValue); if (lNewFontSize) pTextFont->SetSize((float)lNewFontSize); pTextFont->Reset(tomApplyNow); } Exit: if (pLocalFont) pLocalFont->Release(); if (bstr) SysFreeString(bstr); } /* * INT CIme::GetCompositionStringInfo(HIMC hIMC, DWORD dwIndex, * WCHAR *szCompStr, INT cchMax, BYTE *attrib, INT cbAttrib * LONG cchAttrib, UINT kbCodePage, BOOL bUnicodeIME) * * @mfunc * For WM_IME_COMPOSITION string processing to get the requested * composition string, by type, and convert it to Unicode. * * @devnote * We must use ImmGetCompositionStringA because W is not supported * on Win95. * * @rdesc * INT-cch of the Unicode composition string. * Out param in szCompStr. */ INT CIme::GetCompositionStringInfo( HIMC hIMC, // @parm IME context provided by host. DWORD dwIndex, // @parm The type of composition string. WCHAR *szCompStr, // @parm Out param, unicode result string. INT cchMax, // @parm The cch for the Out param. BYTE *attrib, // @parm Out param, If attribute info is needed. INT cbMax, // @parm The cb of the attribute info. LONG *cpCursor, // @parm Out param, returns the CP of cusor. LONG *cchAttrib, // @parm how many attributes returned. UINT kbCodePage, // @parm codepage BOOL bUnicodeIME, // @parm Unciode IME BOOL bUsingAimm) // @parm Using Aimm { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::GetCompositionStringInfo"); BYTE compStr[256], attribInfo[256]; INT i, j, iMax, cchCompStr=0, cbAttrib, cursor; INT cchAnsiCompStr=0; Assert(hIMC && szCompStr); if(cpCursor) // Init cursor out param. *cpCursor = -1; if(cchAttrib) *cchAttrib = 0; // Get composition string. if (bUnicodeIME) cchCompStr = ImmGetCompositionStringW(hIMC, dwIndex, szCompStr, cchMax, bUsingAimm )/sizeof(WCHAR); else cchAnsiCompStr = ImmGetCompositionStringA(hIMC, dwIndex, compStr, 255, bUsingAimm); if(cchAnsiCompStr > 0 || cchCompStr > 0) // If valid data. { if (!bUnicodeIME) { Assert(cchAnsiCompStr >> 1 < cchMax - 1); // Convert to Unicode. cchCompStr = UnicodeFromMbcs(szCompStr, cchMax, (CHAR *) compStr, cchAnsiCompStr, kbCodePage); } if(attrib || cpCursor) // Need cursor or attribs? { if (bUnicodeIME) { // Get Unicode Cursor cp. cursor = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0, bUsingAimm); // Get Unicode attributes. cbAttrib = ImmGetCompositionStringW(hIMC, GCS_COMPATTR, attribInfo, 255, bUsingAimm); iMax = max(cursor, cbAttrib); iMax = min(iMax, cchCompStr); } else { // Get DBCS Cursor cp. cursor = ImmGetCompositionStringA(hIMC, GCS_CURSORPOS, NULL, 0, bUsingAimm); // Get DBCS attributes. cbAttrib = ImmGetCompositionStringA(hIMC, GCS_COMPATTR, attribInfo, 255, bUsingAimm); iMax = max(cursor, cbAttrib); iMax = min(iMax, cchAnsiCompStr); } if(NULL == attrib) cbMax = cbAttrib; for(i = 0, j = 0; i <= iMax && j < cbMax; i++, j++) { if(cursor == i) cursor = j; if(!bUnicodeIME && GetTrailBytesCount(compStr[i], kbCodePage)) i++; if(attrib && i < cbAttrib) *attrib++ = attribInfo[i]; } // attrib cch==unicode cch Assert(0 >= cbAttrib || j-1 == cchCompStr); if(cursor >= 0 && cpCursor) // If client needs cursor *cpCursor = cursor; // or cchAttrib. if(cbAttrib >= 0 && cchAttrib) *cchAttrib = j-1; } } else { if(cpCursor) *cpCursor = 0; cchCompStr = 0; } return cchCompStr; } /* * void CIme::SetCompositionFont (CTextMsgFilter &TextMsgFilter, ITextFont *pTextFont) * * @mfunc * Important for level 2 IME so that the composition window * has the correct font. The lfw to lfa copy is due to the fact that * Win95 does not support the W)ide call. * It is also important for both level 2 and level 3 IME so that * the candidate list window has the proper. font. */ void CIme::SetCompositionFont ( CTextMsgFilter &TextMsgFilter, // @parm the containing message filter. ITextFont *pTextFont) // @parm ITextFont for setting lfa. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::SetCompositionFont"); HIMC hIMC; LOGFONTA lfa; if (pTextFont) { hIMC = LocalGetImmContext(TextMsgFilter); if (hIMC) { // Build the LOGFONT based on pTextFont float FontSize; long lValue; BSTR bstr; long lTextFlow; memset (&lfa, 0, sizeof(lfa)); if (pTextFont->GetSize(&FontSize) == NOERROR) lfa.lfHeight = (LONG) FontSize; if (pTextFont->GetBold(&lValue) == NOERROR && lValue == tomTrue) lfa.lfWeight = FW_BOLD; if (pTextFont->GetItalic(&lValue) == NOERROR && lValue == tomTrue) lfa.lfItalic = TRUE; lfa.lfCharSet = (BYTE)GetCharSet(TextMsgFilter._uKeyBoardCodePage); lValue = tomCharset; if (pTextFont->GetLanguageID(&lValue) == NOERROR && lfa.lfCharSet == (BYTE)lValue) lfa.lfPitchAndFamily = (BYTE)(lValue >> 8); if (pTextFont->GetName(&bstr) == NOERROR) { if ((TextMsgFilter._lFEFlags & tomUseAtFont) && bstr[0] != L'@') { lfa.lfFaceName[0] = '@'; MbcsFromUnicode(&(lfa.lfFaceName[1]), sizeof(lfa.lfFaceName)-1, bstr, -1, CP_ACP, UN_NOOBJECTS); } else MbcsFromUnicode(lfa.lfFaceName, sizeof(lfa.lfFaceName), bstr, -1, CP_ACP, UN_NOOBJECTS); SysFreeString(bstr); } lTextFlow = TextMsgFilter._lFEFlags & tomTextFlowMask; if (lTextFlow) { DWORD imeUIProperties = ImmGetProperty(GetKeyboardLayout(0x0FFFFFFFF), IGP_UI, TextMsgFilter._fUsingAIMM); if (imeUIProperties & (UI_CAP_2700 | UI_CAP_ROT90 | UI_CAP_ROTANY)) { if (lTextFlow == tomTextFlowSW) lfa.lfOrientation = lfa.lfEscapement = 2700; else if (lTextFlow == tomTextFlowNE) lfa.lfOrientation = lfa.lfEscapement = 900; } } ImmSetCompositionFontA( hIMC, &lfa, TextMsgFilter._fUsingAIMM ); LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context. } } } /* * void CIme::SetCompositionForm (CTextMsgFilter &TextMsgFilter) * * @mfunc * Important for level 2 IME so that the composition window * is positioned correctly. * * @comm * We go through a lot of work to get the correct height. This requires * getting information from the font cache and the selection. */ void CIme::SetCompositionForm ( CTextMsgFilter &TextMsgFilter) // @parm the containing text edit. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::SetCompositionForm"); HIMC hIMC; COMPOSITIONFORM cf; if(IME_LEVEL_2 == GetIMELevel()) { hIMC = LocalGetImmContext(TextMsgFilter); // Get IME context. if(hIMC) { // get the location of cpMin cf.ptCurrentPos.x = cf.ptCurrentPos.y = 0; TextMsgFilter._pTextSel->GetPoint( tomStart+tomClientCoord+TA_BOTTOM+TA_LEFT, &(cf.ptCurrentPos.x), &(cf.ptCurrentPos.y) ); // Set-up bounding rect. for the IME (lev 2) composition window, causing // composition text to be wrapped within it. cf.dwStyle = CFS_RECT; TextMsgFilter._pTextDoc->GetClientRect(tomIncludeInset+tomClientCoord, &(cf.rcArea.left), &(cf.rcArea.top), &(cf.rcArea.right), &(cf.rcArea.bottom)); // Make sure the starting point is not // outside the rcArea. This happens when // there is no text on the current line and the user // has selected a large font size. if(cf.ptCurrentPos.y < cf.rcArea.top) cf.ptCurrentPos.y = cf.rcArea.top; else if(cf.ptCurrentPos.y > cf.rcArea.bottom) cf.ptCurrentPos.y = cf.rcArea.bottom; if(cf.ptCurrentPos.x < cf.rcArea.left) cf.ptCurrentPos.x = cf.rcArea.left; else if(cf.ptCurrentPos.x > cf.rcArea.right) cf.ptCurrentPos.x = cf.rcArea.right; ImmSetCompositionWindow(hIMC, &cf, TextMsgFilter._fUsingAIMM); // Set composition window. LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context. } } } /* * * CIme::TerminateIMEComposition (CTextMsgFilter &TextMsgFilter) * * @mfunc Terminate the IME Composition mode using CPS_COMPLETE * @comm The IME will generate WM_IME_COMPOSITION with the result string * */ void CIme::TerminateIMEComposition( CTextMsgFilter &TextMsgFilter, // @parm the containing message filter. TerminateMode mode) { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::TerminateIMEComposition"); DWORD dwTerminateMethod; HIMC hIMC = LocalGetImmContext(TextMsgFilter); if(TextMsgFilter.IsIMEComposition() && TextMsgFilter._ime->IsTerminated()) { // Turn if off now EndCompositionGlue(TextMsgFilter, TRUE); return; } _fIMETerminated = TRUE; if (mode == TERMINATE_FORCECANCEL) TextMsgFilter._pTextDoc->IMEInProgress(tomFalse); // Inform client IME compostion is done dwTerminateMethod = CPS_COMPLETE; if (IME_LEVEL_2 == GetIMELevel() || // force cancel for near-caret IME mode == TERMINATE_FORCECANCEL || // caller wants force cancel TextMsgFilter._fIMECancelComplete) // Client wants force cancel { dwTerminateMethod = CPS_CANCEL; } // force the IME to terminate the current session if(hIMC) { BOOL fRetCode; fRetCode = ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, dwTerminateMethod, 0, TextMsgFilter._fUsingAIMM); if(!fRetCode && !TextMsgFilter._fIMECancelComplete) { // CPS_COMPLETE fail, try CPS_CANCEL. This happen with some ime which do not support // CPS_COMPLETE option (e.g. ABC IME version 4 with Win95 simplified Chinese) fRetCode = ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0, TextMsgFilter._fUsingAIMM); } LocalReleaseImmContext(TextMsgFilter, hIMC); } else { // for some reason, we didn't have a context, yet we thought we were still in IME // compostition mode. Just force a shutdown here. EndCompositionGlue(TextMsgFilter, TRUE); } } /* * CIme_Lev2::CIme_Lev2() * * @mfunc * CIme_Lev2 Constructor/Destructor. * * @comm * Needed to make sure _iFormatSave was handled properly. * */ CIme_Lev2::CIme_Lev2( CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::CIme_Lev2"); long cpMin, cpMax, cpLoc; HRESULT hResult; ITextFont *pCurrentFont = NULL; _pTextFont = NULL; _cIgnoreIMECharMsg = 0; // setup base Font format for later use during composition hResult = TextMsgFilter._pTextSel->GetStart(&cpMin); cpLoc = cpMin; if (TextMsgFilter._fHangulToHanja) cpMax = cpMin + 1; // Select the Hangul character else hResult = TextMsgFilter._pTextSel->GetEnd(&cpMax); _fSkipFirstOvertype = FALSE; if (cpMax != cpMin) { // selection case, get format for at cpMin ITextRange *pTextRange; HRESULT hResult; hResult = TextMsgFilter._pTextDoc->Range(cpMin, cpMin+1, &pTextRange); Assert (pTextRange != NULL); if (hResult == NOERROR && pTextRange) { pTextRange->GetFont(&pCurrentFont); Assert(pCurrentFont != NULL); pTextRange->Release(); cpLoc = cpMin+1; } if (!TextMsgFilter._fHangulToHanja) _fSkipFirstOvertype = TRUE; // For Korean Overtype support } if (!pCurrentFont) TextMsgFilter._pTextSel->GetFont(&pCurrentFont); Assert(pCurrentFont != NULL); pCurrentFont->GetDuplicate(&_pTextFont); // duplicate the base format for later use pCurrentFont->Release(); Assert(_pTextFont != NULL); // setup font to match current keyboard CIme::CheckKeyboardFontMatching (cpLoc, &TextMsgFilter, _pTextFont); _fIgnoreEndComposition = FALSE; _fIMETerminated = FALSE; } CIme_Lev2::~CIme_Lev2() { if ( _pTextFont ) _pTextFont->Release(); } /* * HRESULT CIme_Lev2::StartComposition(CTextMsgFilter &TextMsgFilter) * * @mfunc * Begin IME Level 2 composition string processing. * * @comm * Set the font, and location of the composition window which includes * a bounding rect and the start position of the cursor. Also, reset * the candidate window to allow the IME to set its position. * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. */ HRESULT CIme_Lev2::StartComposition( CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::StartComposition"); _imeLevel = IME_LEVEL_2; SetCompositionFont(TextMsgFilter, _pTextFont); // Set font, & comp window. SetCompositionForm(TextMsgFilter); return S_FALSE; // Allow DefWindowProc } // processing. /* * HRESULT CIme_Lev2::CompositionString(const LPARAM lparam, CTextMsgFilter &TextMsgFilter) * * @mfunc * Handle Level 2 WM_IME_COMPOSITION messages. * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing. * * Side effect: * The Host needs to mask out the lparam before calling DefWindowProc to * prevent unnessary WM_IME_CHAR messages. */ HRESULT CIme_Lev2::CompositionString ( const LPARAM lparam, // @parm associated with message. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::CompositionString"); _cIgnoreIMECharMsg = 0; if(HAVE_RESULT_STRING()) { _fGotFinalString = TRUE; if (_pTextFont) { // setup the font before insert final string ITextFont *pFETextFont=NULL; _pTextFont->GetDuplicate(&pFETextFont); Assert(pFETextFont != NULL); TextMsgFilter._pTextSel->SetFont(pFETextFont); pFETextFont->Release(); } TextMsgFilter._pTextDoc->SetNotificationMode(tomTrue); CheckInsertResultString(lparam, TextMsgFilter, &_cIgnoreIMECharMsg); SetCompositionForm(TextMsgFilter); // Move Composition window. } // Always return S_FALSE so the DefWindowProc will handle the rest. // Host has to mask out the ResultString bit to avoid WM_IME_CHAR coming in. return S_FALSE; } /* * HRESULT CIme::CheckInsertResultString (const LPARAM lparam, CTextMsgFilter &TextMsgFilter) * * @mfunc * handle inserting of GCS_RESULTSTR text, the final composed text. * * @comm * When the final composition string arrives we grab it and set it into the text. * * @devnote * A GCS_RESULTSTR message can arrive and the IME will *still* be in * composition string mode. This occurs because the IME's internal * buffers overflowed and it needs to convert the beginning of the buffer * to clear out some room. When this happens we need to insert the * converted text as normal, but remain in composition processing mode. * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. */ HRESULT CIme::CheckInsertResultString ( const LPARAM lparam, // @parm associated with message. CTextMsgFilter &TextMsgFilter, // @parm the containing message filter. short *pcch, // @parm number of character read int *pcbOutBuff, // @parm byte size of the output buffer WCHAR *pOutBuff) // @parm buffer to receive the text { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::CheckInsertResultString"); HRESULT hr = S_FALSE; HIMC hIMC; INT cch; WCHAR szCompStr[256]; if(CLEANUP_COMPOSITION_STRING() || HAVE_RESULT_STRING()) // If result string.. { hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context. cch = 0; if(hIMC) // Get result string. { cch = GetCompositionStringInfo(hIMC, pOutBuff ? GCS_RESULTREADSTR : GCS_RESULTSTR, szCompStr, sizeof(szCompStr)/sizeof(szCompStr[0]), NULL, 0, NULL, NULL, TextMsgFilter._uKeyBoardCodePage, TextMsgFilter._fUnicodeIME, TextMsgFilter._fUsingAIMM); if (pcch) *pcch = (short)cch; cch = min (cch, 255); szCompStr[cch] = L'\0'; LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context. } // Don't need to replace range when there isn't any text. Otherwise, the character format is // reset to previous run. if(cch) { if (pOutBuff) { if (*pcbOutBuff > (int) ((cch + 1) * sizeof(WCHAR))) *pcbOutBuff = (cch + 1) * sizeof(WCHAR); memcpy(pOutBuff, szCompStr, *pcbOutBuff); } else { BSTR bstr = SysAllocString(szCompStr); if (!bstr) return E_OUTOFMEMORY; TextMsgFilter._pTextSel->TypeText(bstr); SysFreeString(bstr); } } else if (pOutBuff) *pcbOutBuff = 0; hr = S_OK; // Don't want WM_IME_CHARs. } return hr; } /* * HRESULT CIme_Lev2::IMENotify(const WPARAM wparam, const LPARAM lparam, * CTextMsgFilter &TextMsgFilter) * * @mfunc * Handle Level 2 WM_IME_NOTIFY messages. * * @comm * Currently we are only interested in knowing when to reset * the candidate window's position. * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. */ HRESULT CIme_Lev2::IMENotify( const WPARAM wparam, // @parm associated with message. const LPARAM lparam, // @parm associated with message. CTextMsgFilter &TextMsgFilter, // @parm the containing message filter. BOOL fIgnore) // @parm Level3 Chinese Composition window only { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::IMENotify"); if(IMN_OPENCANDIDATE == wparam) { Assert (0 != lparam); HIMC hIMC; // Host's IME context. INT index; // Candidate window index. CANDIDATEFORM cdCandForm; hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context. if(hIMC) { // Convert bitID to INDEX. for (index = 0; index < 32; index++) // because *stupid* API. { if((1 << index) & lparam) break; } Assert (((1 << index) & lparam) == lparam); // Only 1 set? Assert (index < 32); // Reset to CFS_DEFAULT if(ImmGetCandidateWindow(hIMC, index, &cdCandForm, TextMsgFilter._fUsingAIMM) && CFS_DEFAULT != cdCandForm.dwStyle) { cdCandForm.dwStyle = CFS_DEFAULT; ImmSetCandidateWindow(hIMC, &cdCandForm, TextMsgFilter._fUsingAIMM); } LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context. } } return S_FALSE; // Allow DefWindowProc } // processing. /* * void CIme_Lev2::PostIMEChar (CTextMsgFilter &TextMsgFilter) * * @mfunc * Called after processing a single WM_IME_CHAR in order to * update the position of the IME's composition window. * */ void CIme_Lev2::PostIMEChar ( CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::PostIMEChar"); SetCompositionForm(TextMsgFilter); // Move Composition window. } /* * * CIme_Lev2::IMEMouseOperation (CTextMsgFilter &TextMsgFilter, UINT msg, BOOL &fTerminateIME) * * @mfunc Level 2 IME does not handle the mouse events, we need to check if * we should terminate IME. * * @rdesc * BOOL-FALSE since Level 2 IME does not handle the mouse events * fTermineateIME-TRUE if we want to terminateIME * */ BOOL CIme_Lev2::IMEMouseOperation( CTextMsgFilter &TextMsgFilter, // @parm the containing message filter. UINT msg, // @parm message id WPARAM wParam, // @parm wparam BOOL &fTerminateIME) // @parm need to terminate IME { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::IMEMouseOperation"); // Level 2 IME, check if we need to terminate IME fTerminateIME = FALSE; switch(msg) { case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: fTerminateIME = TRUE; } return FALSE; } /* * * CIme_Lev2::GetIMECompositionMode (CTextMsgFilter &TextMsgFilter) * * @mfunc Return the current IME composition mode when we haven't received any final string * * @rdesc * IME Level * */ LRESULT CIme_Lev2::GetIMECompositionMode( CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::GetIMECompositionMode"); LRESULT lres = ICM_NOTOPEN; if (!_fGotFinalString) { DWORD imeProperties; imeProperties = ImmGetProperty(GetKeyboardLayout(0x0FFFFFFFF), IGP_PROPERTY, TextMsgFilter._fUsingAIMM); if(imeProperties & IME_PROP_AT_CARET) lres = ICM_LEVEL2_5; // level 2.5. else if (imeProperties & IME_PROP_SPECIAL_UI) lres = ICM_LEVEL2_SUI; // special UI. else lres = ICM_LEVEL2; // stock level 2. } return lres; } /* * CIme_Lev3::CIme_Lev3() * * @mfunc * CIme_Lev3 Constructor/Destructor. * */ CIme_Lev3::CIme_Lev3( CTextMsgFilter &TextMsgFilter) : CIme_Lev2 ( TextMsgFilter ) { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::CIme_Lev3"); _sIMESuportMouse = 0; // initial to 0 so we will check mouse operation if need _wParamBefore = 0; _fUpdateWindow = FALSE; } /* * HRESULT CIme_Lev3::StartComposition(CTextMsgFilter &TextMsgFilter) * * @mfunc * Begin IME Level 3 composition string processing. * * @comm * For rudimentary processing, remember the start and * length of the selection. Set the font in case the * candidate window actually uses this information. * * @rdesc * This is a rudimentary solution for remembering were * the composition is in the text. There needs to be work * to replace this with a composition "range". * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. */ HRESULT CIme_Lev3::StartComposition( CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::StartComposition"); long cpMin; TextMsgFilter._pTextSel->GetStart(&cpMin); _ichStart = cpMin; _cchCompStr = 0; _imeLevel = IME_LEVEL_3; SetCompositionFont (TextMsgFilter, _pTextFont); // Delete current selection TextMsgFilter._pTextSel->SetText(NULL); // turn off undo TextMsgFilter._pTextDoc->Undo(tomSuspend, NULL); if (_pTextFont) { _pTextFont->GetForeColor(&_crTextColor); _pTextFont->GetBackColor(&_crBkColor); } // Setup IMEShare Lid if necessary if (!TextMsgFilter._fRE10Mode && TextMsgFilter._uKeyBoardCodePage != CP_KOREAN && W32->HaveIMEShare()) { CIMEShare *pIMEShare; if (W32->getIMEShareObject(&pIMEShare)) { LID hKL = (LID)GetKeyboardLayout(0x0FFFFFFFF); if (pIMEShare->LidGetLid() != hKL) pIMEShare->LidSetLid(hKL); } } return S_OK; // No DefWindowProc } // processing. /* * HRESULT CIme_Lev3::CompositionString(const LPARAM lparam, CTextMsgFilter &TextMsgFilter) * * @mfunc * Handle Level 3 WM_IME_COMPOSITION messages. * * @comm * Display all of the intermediary composition text as well as the final * reading. * * @devnote * This is a rudimentary solution for replacing text in the backing store. * Work is left to do with the undo list, underlining, and hiliting with * colors and the selection. * * @devnote * A GCS_RESULTSTR message can arrive and the IME will *still* be in * composition string mode. This occurs because the IME's internal * buffers overflowed and it needs to convert the beginning of the buffer * to clear out some room. When this happens we need to insert the * converted text as normal, but remain in composition processing mode. * * Another reason, GCS_RESULTSTR can occur while in composition mode * for Korean because there is only 1 correct choice and no additional * user intervention is necessary, meaning that the converted string can * be sent as the result before composition mode is finished. * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. */ HRESULT CIme_Lev3::CompositionString( const LPARAM lparam, // @parm associated with message. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::CompositionString"); long cpMin; _fIgnoreEndComposition = FALSE; if (_fUpdateWindow) { TextMsgFilter._pTextDoc->UpdateWindow(); _fUpdateWindow = FALSE; } if(CLEANUP_COMPOSITION_STRING() || HAVE_RESULT_STRING()) // Any final readings? { long lCount; if (!_fHandlingFinalString) { _fHandlingFinalString = TRUE; if (HAVE_RESULT_STRING()) _fGotFinalString = TRUE; if (!CLEANUP_COMPOSITION_STRING()) TextMsgFilter._pTextDoc->Freeze(&lCount); // Turn off display if (_cchCompStr) { ITextRange *pTextRange = NULL; // Create a range to delete composition text TextMsgFilter._pTextDoc->Range(_ichStart, _ichStart + _cchCompStr, &pTextRange); Assert (pTextRange != NULL); // delete composition text pTextRange->SetText(NULL); pTextRange->Release(); _cchCompStr = 0; // be in composition mode. }; // setup the font before insert final string ITextFont *pFETextFont; _pTextFont->GetDuplicate(&pFETextFont); Assert(pFETextFont != NULL); TextMsgFilter._pTextSel->SetFont(pFETextFont); pFETextFont->Release(); // turn on undo TextMsgFilter._pTextDoc->Undo(tomResume, NULL); // Turn on Notification again TextMsgFilter._pTextDoc->SetNotificationMode(tomTrue); // get final string CheckInsertResultString(lparam, TextMsgFilter); if (!CLEANUP_COMPOSITION_STRING()) TextMsgFilter._pTextDoc->Unfreeze(&lCount); // Turn on display // Reset as we may still in Composition TextMsgFilter._pTextSel->GetStart(&cpMin); _ichStart = cpMin; // turn off undo for Korean IME since we will get Composition string message // again without getting EndComposition if (TextMsgFilter._uKeyBoardCodePage == CP_KOREAN) TextMsgFilter._pTextDoc->Undo(tomSuspend, NULL); _fHandlingFinalString = FALSE; } } if(HAVE_COMPOSITION_STRING()) // In composition mode? { HIMC hIMC; INT cchOld = _cchCompStr; LONG cpCursor = 0, cchAttrib = 0; LONG i, j; // For applying attrib effects. WCHAR szCompStr[256]; BYTE startAttrib, attrib[256]; BSTR bstr = NULL; ITextRange *pTextRange = NULL; long cpMax; long lCount; _cchCompStr = 0; if (!_fDestroy) { hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context. if(hIMC) // Get composition string. { _cchCompStr = GetCompositionStringInfo(hIMC, GCS_COMPSTR, szCompStr, sizeof(szCompStr)/sizeof(szCompStr[0]), attrib, sizeof(attrib)/sizeof(attrib[0]), &cpCursor, &cchAttrib, TextMsgFilter._uKeyBoardCodePage, TextMsgFilter._fUnicodeIME, TextMsgFilter._fUsingAIMM); _cchCompStr = min (_cchCompStr, 255); szCompStr[_cchCompStr] = L'\0'; LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context. } } // any new composition string? if(_cchCompStr) { long cchExced = 0; if (TextMsgFilter._pTextDoc->CheckTextLimit(_cchCompStr-cchOld, &cchExced) == NOERROR && cchExced > 0) { // We reach text limit, beep... TextMsgFilter._pTextDoc->SysBeep(); if (_cchCompStr > cchExced) _cchCompStr -= cchExced; else _cchCompStr = 0; szCompStr[_cchCompStr] = L'\0'; if (!_cchCompStr && TextMsgFilter._uKeyBoardCodePage == CP_KOREAN) TextMsgFilter._pTextDoc->SetCaretType(tomNormalCaret); // Turn off Block caret mode } bstr = SysAllocString(szCompStr); if (!bstr) return E_OUTOFMEMORY; if (HAVE_RESULT_STRING()) { // ignore next end composition _fIgnoreEndComposition = TRUE; // turn off undo TextMsgFilter._pTextDoc->Undo(tomSuspend, NULL); // Get the new format that may have changed by apps (e.g. Outlook) _pTextFont->Release(); ITextFont *pCurrentFont = NULL; TextMsgFilter._pTextSel->GetFont(&pCurrentFont); Assert(pCurrentFont != NULL); pCurrentFont->GetDuplicate(&_pTextFont); // duplicate the base format for later use pCurrentFont->Release(); Assert(_pTextFont != NULL); CIme::CheckKeyboardFontMatching (_ichStart, &TextMsgFilter, _pTextFont); } } if (cchOld || _cchCompStr) { bool fFreezeDisplay = false; // Hold notification if needed if (!(TextMsgFilter._fIMEAlwaysNotify) && !HAVE_RESULT_STRING()) TextMsgFilter._pTextDoc->SetNotificationMode(tomFalse); // We only support overtype mode in Korean IME if (!cchOld && TextMsgFilter._uKeyBoardCodePage == CP_KOREAN && TextMsgFilter._fOvertypeMode && !_fSkipFirstOvertype) { long cCurrentChar; HRESULT hResult; // Create a range using the next character hResult = TextMsgFilter._pTextDoc->Range(_ichStart, _ichStart+1, &pTextRange); Assert (pTextRange != NULL); // Check if it is par character. If so, we don't want to // delete it. hResult = pTextRange->GetChar(&cCurrentChar); if (hResult == NOERROR) { if (cCurrentChar != (long)'\r' && cCurrentChar != (long)'\n') { TextMsgFilter._pTextDoc->Undo(tomResume, NULL); // Turn on undo pTextRange->SetText(NULL); // Delete the character TextMsgFilter._pTextDoc->Undo(tomSuspend, NULL); // Turn off undo } else { // Unselect the par character hResult = pTextRange->SetRange(_ichStart, _ichStart); } } } else { // Create a range using the preivous composition text and delete the text TextMsgFilter._pTextDoc->Range(_ichStart, _ichStart+cchOld, &pTextRange); Assert (pTextRange != NULL); if (cchOld) { if (cpCursor >= 0) { TextMsgFilter._pTextDoc->Freeze(&lCount); // Turn off display fFreezeDisplay = true; } pTextRange->SetText(NULL); } } _fSkipFirstOvertype = FALSE; if (cpCursor >= 0 && !fFreezeDisplay) TextMsgFilter._pTextDoc->Freeze(&lCount); // Turn off display // Make sure the composition string is formatted with the base font ITextFont *pFETextFont; HRESULT hResult; hResult = _pTextFont->GetDuplicate(&pFETextFont); Assert(pFETextFont != NULL); if (!(hResult != NOERROR || pFETextFont == NULL)) { if (TextMsgFilter._fHangulToHanja && !_cchCompStr) // Hangul to Hanja mode, setup font for selection to // handle the Hanja character the come in after the end composition // message TextMsgFilter._pTextSel->SetFont(pFETextFont); else pTextRange->SetFont(pFETextFont); } pTextRange->SetText(bstr); // Replace with the new text if (pFETextFont) pFETextFont->Release(); // update how many composition characters have been added pTextRange->GetEnd(&cpMax); _cchCompStr = cpMax - _ichStart; if (TextMsgFilter._uKeyBoardCodePage == CP_KOREAN) { // no formatting for Korean POINT ptBottomPos; if (cpCursor == 0) TextMsgFilter._pTextDoc->Unfreeze(&lCount); // Turn on display if (pTextRange->GetPoint( tomEnd+TA_BOTTOM+TA_RIGHT, &(ptBottomPos.x), &(ptBottomPos.y) ) != NOERROR) pTextRange->ScrollIntoView(tomEnd); // Setup Block caret mode only when there is One char and the caret pos is 0. TextMsgFilter._pTextDoc->SetCaretType((_cchCompStr == 1 && !cpCursor) ? tomKoreanBlockCaret : tomNormalCaret); } else if (_cchCompStr && _cchCompStr <= cchAttrib) { for ( i = 0; i < _cchCompStr; ) // Parse the attributes... { // to apply styles. ITextFont *pFETextFont; HRESULT hResult; hResult = _pTextFont->GetDuplicate(&pFETextFont); Assert(pFETextFont != NULL); if (hResult != NOERROR || pFETextFont == NULL) break; // Rsest the clone font so we will only apply effects returned // from SetCompositionStyle pFETextFont->Reset(tomUndefined); startAttrib = attrib[i]; // Get attrib's run length. for ( j = i+1; j < _cchCompStr; j++ ) { if ( startAttrib != attrib[j] ) // Same run until diff. break; } SetCompositionStyle(TextMsgFilter, startAttrib, pFETextFont); // Apply FE clause's style pTextRange->SetRange(_ichStart+i, _ichStart+j); pTextRange->SetFont(pFETextFont); pFETextFont->Release(); i = j; } } pTextRange->Release(); } else if (TextMsgFilter._uKeyBoardCodePage == CP_KOREAN) TextMsgFilter._pTextDoc->Update(tomTrue); // Force an Update // setup caret pos if ( !(TextMsgFilter._uKeyBoardCodePage == CP_KOREAN) || cpCursor > 0) { if ( cpCursor >= 0 ) { HRESULT hResult; int cpLocal = min(cpCursor, _cchCompStr) + _ichStart; hResult = TextMsgFilter._pTextDoc->Range(cpLocal, cpLocal, &pTextRange); Assert (pTextRange != NULL); if (hResult == NO_ERROR) { pTextRange->Select(); pTextRange->Release(); } TextMsgFilter._pTextDoc->Unfreeze(&lCount); // Turn on display } } if (bstr) SysFreeString(bstr); // setup composition window for Chinese in-caret IME if (!_fDestroy && (TextMsgFilter._uKeyBoardCodePage == CP_CHINESE_TRAD || TextMsgFilter._uKeyBoardCodePage == CP_CHINESE_SIM)) IMENotify ( IMN_OPENCANDIDATE, 0x01, TextMsgFilter, TRUE ); } return S_OK; // No DefWindowProc } // processing. /* * void CIme_Lev3::SetCompositionStyle (CTextMsgFilter &TextMsgFilter, CCharFormat &CF) * * @mfunc * Set up a composition clause's character formmatting. * * @comm * If we loaded Office's IMEShare.dll, then we ask it what the formatting * should be, otherwise we use our own, hardwired default formatting. * * @devnote * Note the use of pointers to functions when dealing with IMEShare funcs. * This is because we dynamically load the IMEShare.dll. * */ void CIme_Lev3::SetCompositionStyle ( CTextMsgFilter &TextMsgFilter, UINT attribute, ITextFont *pTextFont) { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::SetCompositionStyle"); UINT ulID = 0; COLORREF crText = UINTIMEBOGUS; COLORREF crBackground = UINTIMEBOGUS; BOOL fBold = FALSE; BOOL fItalic = FALSE; BOOL fStrikeThru = FALSE; long lUnderlineStyle = tomNone; if (TextMsgFilter._fRE10Mode) { COMPCOLOR* pcrComp = TextMsgFilter.GetIMECompAttributes(); if (!pcrComp) goto defaultStyle; if (attribute > ATTR_TARGET_NOTCONVERTED) attribute = ATTR_CONVERTED; // IME input for 1.0 mode, need to use IME Color fBold = (pcrComp[attribute].dwEffects & CFE_BOLD); fItalic = (pcrComp[attribute].dwEffects & CFE_ITALIC); fStrikeThru = (pcrComp[attribute].dwEffects & CFE_STRIKEOUT); if (pcrComp[attribute].dwEffects & CFE_UNDERLINE) lUnderlineStyle = tomSingle; crText = pcrComp[attribute].crText; crBackground = pcrComp[attribute].crBackground; } else if (W32->HaveIMEShare()) { CIMEShare *pIMEShare; if (W32->getIMEShareObject(&pIMEShare)) { if (TextMsgFilter._fUsingAIMM) { HIMC hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context. if (hIMC) { attribute = W32->GetDisplayGUID (hIMC, attribute); LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context. } } // IMEShare 98 interface fBold = pIMEShare->DwGetIMEStyle(attribute, IdstyIMEShareFBold); fItalic = pIMEShare->DwGetIMEStyle(attribute, IdstyIMEShareFItalic); if (pIMEShare->DwGetIMEStyle(attribute, IdstyIMEShareFUl)) { ulID = pIMEShare->DwGetIMEStyle(attribute, IdstyIMEShareUKul); if(UINTIMEBOGUS != ulID) { long lUnderlineCrIdx = 0; COLORREF crUl; // get color for underline crUl = GetIMEShareColor(pIMEShare, attribute, IdstyIMEShareSubUl); if(UINTIMEBOGUS != crUl) { // NOTE:- attribute is 0 based and index for EffectColor is 1 based, HRESULT hResult = TextMsgFilter._pTextDoc->SetEffectColor(attribute+1, crUl); // setup the high nibble for color index if (hResult == NOERROR) lUnderlineCrIdx = (attribute+1) << 8; } lUnderlineStyle = IMEShareToTomUL(ulID) + lUnderlineCrIdx; } } crText = GetIMEShareColor(pIMEShare, attribute, IdstyIMEShareSubText); crBackground = GetIMEShareColor(pIMEShare, attribute, IdstyIMEShareSubBack); } else { // IMEShare 96 interface const IMESTYLE *pIMEStyle = PIMEStyleFromAttr(attribute); if (NULL == pIMEStyle) goto defaultStyle; fBold = FBoldIMEStyle(pIMEStyle); fItalic = FItalicIMEStyle(pIMEStyle); if (FUlIMEStyle(pIMEStyle)) { ulID = IdUlIMEStyle (pIMEStyle); if(UINTIMEBOGUS != ulID) lUnderlineStyle = IMEShareToTomUL(ulID); } crText = RGBFromIMEColorStyle(PColorStyleTextFromIMEStyle(pIMEStyle)); crBackground = RGBFromIMEColorStyle(PColorStyleBackFromIMEStyle(pIMEStyle)); } } else // default styles when no IMEShare.dll exist. { defaultStyle: switch(attribute) { // Apply underline style. case ATTR_INPUT: case ATTR_CONVERTED: pTextFont->SetUnderline(tomDotted); break; case ATTR_TARGET_NOTCONVERTED: pTextFont->SetUnderline(tomSingle); break; case ATTR_TARGET_CONVERTED: // Target *is* selection. { pTextFont->SetForeColor(::GetSysColor(COLOR_HIGHLIGHTTEXT)); pTextFont->SetBackColor(::GetSysColor(COLOR_HIGHLIGHT)); } break; } return; // Done } // Now setup all the attribute pTextFont->Reset(tomApplyLater); if (fBold) pTextFont->SetBold(tomTrue); else if (TextMsgFilter._fRE10Mode) pTextFont->SetBold(tomFalse); if (fItalic) pTextFont->SetItalic(tomTrue); else if (TextMsgFilter._fRE10Mode) pTextFont->SetItalic(tomFalse); if (fStrikeThru) pTextFont->SetStrikeThrough(tomTrue); else if (TextMsgFilter._fRE10Mode) pTextFont->SetStrikeThrough(tomFalse); pTextFont->SetUnderline(lUnderlineStyle); // ignore case where text color is same as background color if (crText != crBackground) { if(UINTIMEBOGUS != crText) pTextFont->SetForeColor(crText); if(UINTIMEBOGUS != crBackground) pTextFont->SetBackColor(crBackground); } pTextFont->Reset(tomApplyNow); } /* * COLORREF CIme_Lev3::GetIMEShareColor (CIMEShare *pIMEShare, DWORD dwAttribute, DWORD dwProperty) * * @mfunc * Get the IME share color for the given dwAttribute and property * * * @rdesc * COLORREF of the color * */ COLORREF CIme_Lev3::GetIMEShareColor( CIMEShare *pIMEShare, DWORD dwAttribute, DWORD dwProperty) { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::GetIMEShareColor"); if (pIMEShare->DwGetIMEStyle(dwAttribute,IdstyIMEShareFSpecCol | dwProperty)) { if (pIMEShare->DwGetIMEStyle(dwAttribute,IdstyIMEShareFSpecColText | dwProperty)) return (COLORREF) _crTextColor; else return (COLORREF) _crBkColor; } else return (COLORREF) (pIMEShare->DwGetIMEStyle(dwAttribute, IdstyIMEShareRGBCol | dwProperty)); } /* * HRESULT CIme_Lev3::IMENotify(const WPARAM wparam, const LPARAM lparam, * CTextMsgFilter &TextMsgFilter) * * @mfunc * Handle Level 3 WM_IME_NOTIFY messages. * * @comm * Currently we are only interested in knowing when to update * the n window's position. * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. */ HRESULT CIme_Lev3::IMENotify( const WPARAM wparam, // @parm associated with message. const LPARAM lparam, // @parm associated with message. CTextMsgFilter &TextMsgFilter, // @parm the containing message filter. BOOL fCCompWindow) // @parm Level3 Chinese Composition window { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::IMENotify"); if(IMN_OPENCANDIDATE == wparam || IMN_CLOSECANDIDATE == wparam ) { Assert (0 != lparam); INT index; // Candidate window index. CANDIDATEFORM cdCandForm; POINT ptCaret; HIMC hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context. if(hIMC) { for (index = 0; index < 32; index++) // Convert bitID to INDEX { // because *stupid* API if((1 << index) & lparam) break; } Assert(((1 << index) & lparam) == lparam); // Only 1 set? Assert(index < 32); if(IMN_OPENCANDIDATE == wparam && !(TextMsgFilter._uKeyBoardCodePage == CP_KOREAN)) // Set candidate to caret. { HRESULT hResult; POINT ptCurrentBottomPos; long lTextFlow; long lTomType = tomStart+tomClientCoord+TA_BOTTOM+TA_LEFT; TextMsgFilter._pTextDoc->GetFEFlags(&(TextMsgFilter._lFEFlags)); lTextFlow = TextMsgFilter._lFEFlags & tomTextFlowMask; if (lTextFlow == tomTextFlowWN) lTomType = tomStart+tomClientCoord+TA_TOP+TA_LEFT; GetCaretPos(&ptCaret); // Start at caret. ptCaret.x = max(0, ptCaret.x); ptCaret.y = max(0, ptCaret.y); cdCandForm.dwStyle = CFS_CANDIDATEPOS; if ( !fCCompWindow ) // Not positioning the Chinese composition { // Window. hResult = TextMsgFilter._pTextSel->GetPoint( lTomType, &(ptCurrentBottomPos.x), &(ptCurrentBottomPos.y) ); if (hResult != NOERROR) { RECT rcArea; // GetPoint fails, use application rect in screen coordinates hResult = TextMsgFilter._pTextDoc->GetClientRect(tomIncludeInset+tomClientCoord, &(rcArea.left), &(rcArea.top), &(rcArea.right), &(rcArea.bottom)); ptCurrentBottomPos.x = ptCaret.x; ptCurrentBottomPos.y = rcArea.bottom; } if (hResult == NOERROR) { if (TextMsgFilter._uKeyBoardCodePage == CP_JAPAN) { // Change style to CFS_EXCLUDE, this is to // prevent the candidate window from covering // the current selection. cdCandForm.dwStyle = CFS_EXCLUDE; cdCandForm.rcArea.left = ptCaret.x; // FUTURE: for verticle text, need to adjust // the rcArea to include the character width. cdCandForm.rcArea.right = (lTextFlow == tomTextFlowNE) ? ptCurrentBottomPos.x : cdCandForm.rcArea.left + 2; cdCandForm.rcArea.top = ptCaret.y; ptCaret.y = ptCurrentBottomPos.y + 4; cdCandForm.rcArea.bottom = ptCaret.y; } else ptCaret.y = ptCurrentBottomPos.y + 4; } } // Most IMEs will have only 1, #0, candidate window. However, some IMEs // may want to have a window organized alphabetically, by stroke, and // by radical. cdCandForm.dwIndex = index; cdCandForm.ptCurrentPos = ptCaret; ImmSetCandidateWindow(hIMC, &cdCandForm, TextMsgFilter._fUsingAIMM); } else // Reset back to CFS_DEFAULT. { if(ImmGetCandidateWindow(hIMC, index, &cdCandForm, TextMsgFilter._fUsingAIMM) && CFS_DEFAULT != cdCandForm.dwStyle) { cdCandForm.dwStyle = CFS_DEFAULT; ImmSetCandidateWindow(hIMC, &cdCandForm, TextMsgFilter._fUsingAIMM); } } LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context. if (TextMsgFilter._fHangulToHanja == TRUE && IMN_CLOSECANDIDATE == wparam && W32->OnWinNT4() && OnWinNTFE() && !TextMsgFilter._fUsingAIMM) { // By pass NT4.0 Kor Bug where we didn't get EndComposition message // when user toggle the VK_HANJA key to terminate the reconversion. TerminateIMEComposition(TextMsgFilter, CIme::TERMINATE_NORMAL); } if (IMN_CLOSECANDIDATE == wparam && CP_JAPAN == TextMsgFilter._uKeyBoardCodePage) _fUpdateWindow = TRUE; } } return S_FALSE; // Allow DefWindowProc } // processing. /* * * CIme_Lev3::IMEMouseOperation (CTextMsgFilter &TextMsgFilter, UINT msg, BOOL &fTerminateIME) * * @mfunc if current IME support Mouse operation, need to pass * mouse events to IME for processing * * @rdesc * BOOL-TRUE if IME handled the mouse events * fTermineateIME-TRUE if we want to terminateIME * */ BOOL CIme_Lev3::IMEMouseOperation( CTextMsgFilter &TextMsgFilter, // @parm the containing message filter. UINT msg, // @parm message id WPARAM wParam, // @parm wparam BOOL &fTerminateIME) // @parm need to terminate IME { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::IMEMouseOperation"); if (IMESupportMouse(TextMsgFilter)) return TextMsgFilter.MouseOperation(msg, _ichStart, _cchCompStr, wParam, &_wParamBefore, &fTerminateIME, _hwndIME); fTerminateIME = TRUE; return FALSE; } /* * * CIme_Lev3::IMESupportMouse (CTextMsgFilter &TextMsgFilter) * * @mfunc check if current IME supports Mouse events. This should be * a feature for IME Level 3. * * @comm _sIMESupportMouse is a flag with the following values: * == 0 if we haven't checked IME mouse support * == -1 if we have checked and IME doesn't support mouse events * == 1 if we have checked and IME supports mouse events and we have * retrieved the IME hWnd */ BOOL CIme_Lev3::IMESupportMouse( CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::IMESupportMouse"); HIMC hIMC; // Host's IME context. HWND hHostWnd; long hWnd; if (!MSIMEMouseMsg || _sIMESuportMouse == -1) return FALSE; // No mouse operation support if (_sIMESuportMouse == 1) return TRUE; // IME supports mouse operation hHostWnd = TextMsgFilter._hwnd; if (!hHostWnd) // Windowless mode... { if (TextMsgFilter._pTextDoc->GetWindow(&hWnd) != S_OK || !hWnd) return FALSE; hHostWnd = (HWND)(DWORD_PTR)hWnd; } // Check if this IME supports mouse operation hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context. _sIMESuportMouse = -1; // Init. to no support if(hIMC) { _hwndIME = ImmGetDefaultIMEWnd(hHostWnd, TextMsgFilter._fUsingAIMM); LocalReleaseImmContext(TextMsgFilter, hIMC); // SendMessage returns TRUE if IME supports mouse operation if (_hwndIME && SendMessage(_hwndIME, MSIMEMouseMsg, (WPARAM)IMEMOUSE_VERSION, hIMC) ) _sIMESuportMouse = 1; } return (_sIMESuportMouse == 1); } /* * * CIme_Lev3::GetIMECompositionMode (CTextMsgFilter &TextMsgFilter) * * @mfunc Return the current IME composition mode when we have composition characters * or when we haven't received any final string * */ LRESULT CIme_Lev3::GetIMECompositionMode( CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::GetIMECompositionMode"); if (_cchCompStr || !_fGotFinalString) return ICM_LEVEL3; if (!_fDestroy) { HIMC hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context. int cchCompStr = 0; if(hIMC) // Check if there is composition string. { if (TextMsgFilter._fUnicodeIME) cchCompStr = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0, TextMsgFilter._fUsingAIMM); else cchCompStr = ImmGetCompositionStringA(hIMC, GCS_COMPSTR, NULL, 0, TextMsgFilter._fUsingAIMM); LocalReleaseImmContext(TextMsgFilter, hIMC); } if (cchCompStr) return ICM_LEVEL3; } return ICM_NOTOPEN; } /* * BOOL IMEHangeulToHanja (&TextMsgFilter) * * @func * Initiates an IME composition string edit to convert Korean Hanguel to Hanja. * @comm * Called from the message loop to handle VK_KANJI_KEY. * * @devnote * We decide if we need to do a conversion by checking: * - the Fonot is a Korean font, * - the character is a valid SBC or DBC, * - ImmEscape accepts the character and bring up a candidate window * * @rdesc * BOOL - FALSE for no conversion. TRUE if OK. */ BOOL IMEHangeulToHanja ( CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMEHangeulToHanja"); if(!TextMsgFilter.IsIMEComposition()) { if(TextMsgFilter._pTextSel->CanEdit(NULL) == NOERROR) { WCHAR szCurrentChar; long cCurrentChar; HRESULT hResult; HKL hKL = GetKeyboardLayout(0x0FFFFFFFF); HIMC hIMC; if (!hKL) goto Exit; hIMC = LocalGetImmContext(TextMsgFilter); if (!hIMC) goto Exit; // Collapse to cpMin hResult = TextMsgFilter._pTextSel->Collapse(tomTrue); // get the current character hResult = TextMsgFilter._pTextSel->GetChar(&cCurrentChar); if (hResult != NOERROR) goto Exit; szCurrentChar = (WCHAR)cCurrentChar; // Check if the IME has a conversion for this Hangeul character. if (ImmEscape(hKL, hIMC, IME_ESC_HANJA_MODE, (LPVOID)&szCurrentChar, TextMsgFilter._fUsingAIMM) != FALSE) { ITextRange *pTextRange; POINT ptMiddlePos; LONG cpCurrent; hResult = TextMsgFilter._pTextSel->GetStart(&cpCurrent); if (hResult == S_OK) { hResult = TextMsgFilter._pTextDoc->Range(cpCurrent, cpCurrent+1, &pTextRange); if (hResult == S_OK && pTextRange) { // Check if the character is in view if (pTextRange->GetPoint( tomEnd+TA_BASELINE+TA_LEFT, &(ptMiddlePos.x), &(ptMiddlePos.y) ) != NOERROR) pTextRange->ScrollIntoView(tomEnd); pTextRange->Release(); } } TextMsgFilter._fHangulToHanja = TRUE; TextMsgFilter._ime = new CIme_HangeulToHanja(TextMsgFilter); if(TextMsgFilter.IsIMEComposition()) { // start IME composition for the conversion LocalReleaseImmContext(TextMsgFilter, hIMC); return TextMsgFilter._ime->StartComposition(TextMsgFilter); } else TextMsgFilter._fHangulToHanja = FALSE; } LocalReleaseImmContext(TextMsgFilter, hIMC); } } Exit: return S_FALSE; } /* * CIme_HangeulToHanja::CIme_HangeulToHanja() * * @mfunc * CIme_HangeulToHanja Constructor. * * */ CIme_HangeulToHanja::CIme_HangeulToHanja(CTextMsgFilter &TextMsgFilter) : CIme_Lev3(TextMsgFilter) { } /* * HRESULT CIme_HangeulToHanja::StartComposition(CTextMsgFilter &TextMsgFilter) * * @mfunc * Begin CIme_HangeulToHanja composition string processing. * * @comm * Call Level3::StartComposition. Then setup the Korean block * caret for the Hanguel character. * * @rdesc * Need to adjust _ichStart and _cchCompStr to make the Hanguel character * "become" a composition character. * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. */ HRESULT CIme_HangeulToHanja::StartComposition( CTextMsgFilter &TextMsgFilter ) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_HangeulToHanja::StartComposition"); HRESULT hr; hr = CIme_Lev3::StartComposition(TextMsgFilter); // initialize to 1 so Composition string will get rid of the selected Hangeul _cchCompStr = 1; // turn on undo TextMsgFilter._pTextDoc->Undo(tomResume, NULL); // Setup Block caret mode TextMsgFilter._pTextDoc->SetCaretType(tomKoreanBlockCaret); return hr; } /* * HRESULT CIme_HangeulToHanja::CompositionString(const LPARAM lparam, CTextMsgFilter &TextMsgFilter) * * @mfunc * Handle CIme_HangeulToHanja WM_IME_COMPOSITION messages. * * @comm * call CIme_Lev3::CompositionString to get rid of the selected Hanguel character, * then setup the format for the next Composition message. * * @devnote * When the next Composition message comes in and that we are no longer in IME, * the new character will use the format as set here. */ HRESULT CIme_HangeulToHanja::CompositionString( const LPARAM lparam, // @parm associated with message CTextMsgFilter &TextMsgFilter) { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_HangeulToHanja::CompositionString"); CIme_Lev3::CompositionString(lparam, TextMsgFilter); return S_OK; } /* * HRESULT CIme_Protected::CompositionString(const LPARAM lparam, CTextMsgFilter &TextMsgFilter) * * @mfunc * Handle CIme_Protected WM_IME_COMPOSITION messages. * * @comm * Just throw away the result string since we are * in read-only or protected mode * * * @rdesc * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not. * */ HRESULT CIme_Protected::CompositionString ( const LPARAM lparam, // @parm associated with message. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter. { TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Protected::CompositionString"); if(CLEANUP_COMPOSITION_STRING() || HAVE_RESULT_STRING()) // If result string.. { LONG cch = 0; HIMC hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context. WCHAR szCompStr[256]; if(hIMC) // Get result string. { cch = GetCompositionStringInfo(hIMC, GCS_RESULTSTR, szCompStr, sizeof(szCompStr)/sizeof(szCompStr[0]), NULL, 0, NULL, NULL, TextMsgFilter._uKeyBoardCodePage, FALSE, TextMsgFilter._fUsingAIMM); LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context. } return NOERROR; // Don't want WM_IME_CHARs. } // Terminate composition to force a end composition message TerminateIMEComposition(TextMsgFilter, CIme::TERMINATE_FORCECANCEL); return S_FALSE; } #endif // NOFEPROCESSING