|
|
/*
* @doc INTERNAL * * @module ime.cpp -- support for Win95 IME API | * * Most everything to do with FE composition string editing passes * through here. * * Authors: <nl> * Jon Matousek <nl> * Hon Wah Chan <nl> * Justin Voskuhl <nl> * * History: <nl> * 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
|