Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1641 lines
46 KiB

/**************************************************************************\
* Module Name: imectl.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* IME Window Handling Routines
*
* History:
* 20-Dec-1995 wkwok
\**************************************************************************/
#include "precomp.h"
#pragma hdrstop
#include <intlshar.h>
#define LATE_CREATEUI 1
CONST WCHAR szIndicDLL[] = L"indicdll.dll";
FARPROC gpfnGetIMEMenuItemData = NULL;
BOOL IMEIndicatorGetMenuIDData(PUINT puMenuID, PDWORD pdwData);
/*
* Local Routines.
*/
LONG ImeWndCreateHandler(PIMEUI, LPCREATESTRUCT);
void ImeWndDestroyHandler(PIMEUI);
LRESULT ImeSystemHandler(PIMEUI, UINT, WPARAM, LPARAM);
LONG ImeSelectHandler(PIMEUI, UINT, WPARAM, LPARAM);
LRESULT ImeControlHandler(PIMEUI, UINT, WPARAM, LPARAM, BOOL);
LRESULT ImeSetContextHandler(PIMEUI, UINT, WPARAM, LPARAM);
LRESULT ImeNotifyHandler(PIMEUI, UINT, WPARAM, LPARAM);
HWND CreateIMEUI(PIMEUI, HKL);
VOID DestroyIMEUI(PIMEUI);
LRESULT SendMessageToUI(PIMEUI, UINT, WPARAM, LPARAM, BOOL);
VOID SendOpenStatusNotify(PIMEUI, HWND, BOOL);
VOID ImeSetImc(PIMEUI, HIMC);
VOID FocusSetIMCContext(HWND, BOOL);
BOOL ImeBroadCastMsg(PIMEUI, UINT, WPARAM, LPARAM);
VOID ImeMarkUsedContext(HWND, HIMC);
BOOL ImeIsUsableContext(HWND, HIMC);
BOOL GetIMEShowStatus(void);
/*
* Common macros for IME UI, HKL and IMC handlings
*/
#define GETHKL(pimeui) (KHKL_TO_HKL(pimeui->hKL))
#define SETHKL(pimeui, hkl) (pimeui->hKL=(hkl))
#define GETIMC(pimeui) (KHIMC_TO_HIMC(pimeui->hIMC))
#define SETIMC(pimeui, himc) (ImeSetImc(pimeui, KHIMC_TO_HIMC(himc)))
#define GETUI(pimeui) (KHWND_TO_HWND(pimeui->hwndUI))
#define SETUI(pimeui, hwndui) (pimeui->hwndUI=(hwndui))
LOOKASIDE ImeUILookaside;
/***************************************************************************\
* NtUserBroadcastImeShowStatusChange(), NtUserCheckImeShowStatusInThread()
*
* Stub for kernel mode routines
*
\***************************************************************************/
_inline void NtUserBroadcastImeShowStatusChange(HWND hwndDefIme, BOOL fShow)
{
NtUserCallHwndParamLock(hwndDefIme, fShow, SFI_XXXBROADCASTIMESHOWSTATUSCHANGE);
}
_inline void NtUserCheckImeShowStatusInThread(HWND hwndDefIme)
{
NtUserCallHwndLock(hwndDefIme, SFI_XXXCHECKIMESHOWSTATUSINTHREAD);
}
/***************************************************************************\
* ImeWndProc
*
* WndProc for IME class
*
* History:
\***************************************************************************/
LRESULT APIENTRY ImeWndProcWorker(
PWND pwnd,
UINT message,
WPARAM wParam,
LPARAM lParam,
DWORD fAnsi)
{
HWND hwnd = HWq(pwnd);
PIMEUI pimeui;
static BOOL fInit = TRUE;
CheckLock(pwnd);
VALIDATECLASSANDSIZE(pwnd, FNID_IME);
INITCONTROLLOOKASIDE(&ImeUILookaside, IMEUI, spwnd, 8);
#ifdef CUAS_ENABLE
if (IS_CICERO_ENABLED()) {
LRESULT lRet;
lRet = fpCtfImmDispatchDefImeMessage(hwnd, message, wParam, lParam);
if (lRet)
return lRet;
}
#endif
/*
* If the control is not interested in this message,
* pass it to DefWindowProc.
*/
if (!FWINDOWMSG(message, FNID_IME))
return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
/*
* Get the pimeui for the given window now since we will use it a lot in
* various handlers. This was stored using SetWindowLong(hwnd,0,pimeui) when
* we initially created the IME control.
*/
pimeui = ((PIMEWND)pwnd)->pimeui;
if (pimeui == NULL) {
/*
* Further processing not needed
*/
RIPMSG0(RIP_WARNING, "ImeWndProcWorker: pimeui == NULL\n");
return 0L;
}
/*
* This is necessary to avoid recursion call from IME UI.
*/
UserAssert(pimeui->nCntInIMEProc >= 0);
if (pimeui->nCntInIMEProc > 0) {
TAGMSG5(DBGTAG_IMM, "ImeWndProcWorker: Recursive for pwnd=%08p, msg=%08x, wp=%08x, lp=%08x, fAnsi=%d\n",
pwnd, message, wParam, lParam, fAnsi);
switch (message) {
case WM_IME_SYSTEM:
switch (wParam) {
case IMS_ISACTIVATED:
case IMS_SETOPENSTATUS:
// case IMS_SETCONVERSIONSTATUS:
case IMS_SETSOFTKBDONOFF:
/*
* Because these will not be pass to UI.
* We can do it.
*/
break;
default:
return 0L;
}
break;
case WM_IME_STARTCOMPOSITION:
case WM_IME_ENDCOMPOSITION:
case WM_IME_COMPOSITION:
case WM_IME_SETCONTEXT:
#if !defined(CUAS_ENABLE)
case WM_IME_NOTIFY:
#endif
case WM_IME_CONTROL:
case WM_IME_COMPOSITIONFULL:
case WM_IME_SELECT:
case WM_IME_CHAR:
case WM_IME_REQUEST:
return 0L;
#ifdef CUAS_ENABLE
case WM_IME_NOTIFY:
if (wParam >= IMN_PRIVATE &&
! IS_IME_KBDLAYOUT(GETHKL(pimeui)) && IS_CICERO_ENABLED()) {
//
// CUAS: IMN_PRIVATE always call UI window.
//
break;
}
return 0L;
#endif
default:
return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
}
}
if (TestWF(pwnd, WFINDESTROY) || TestWF(pwnd, WFDESTROYED)) {
switch (message) {
case WM_DESTROY:
case WM_NCDESTROY:
case WM_FINALDESTROY:
break;
default:
RIPMSG1(RIP_WARNING, "ImeWndProcWorker: message %x is sent to destroyed window.", message);
return 0L;
}
}
switch (message) {
case WM_ERASEBKGND:
return (LONG)TRUE;
case WM_PAINT:
break;
case WM_CREATE:
return ImeWndCreateHandler(pimeui, (LPCREATESTRUCT)lParam);
case WM_DESTROY:
/*
* We are destroying the IME window, destroy
* any UI window that it owns.
*/
ImeWndDestroyHandler(pimeui);
break;
case WM_NCDESTROY:
case WM_FINALDESTROY:
if (pimeui) {
Unlock(&pimeui->spwnd);
FreeLookasideEntry(&ImeUILookaside, pimeui);
}
NtUserSetWindowFNID(hwnd, FNID_CLEANEDUP_BIT);
goto CallDWP;
case WM_IME_SYSTEM:
UserAssert(pimeui->spwnd == pwnd);
return ImeSystemHandler(pimeui, message, wParam, lParam);
case WM_IME_SELECT:
return ImeSelectHandler(pimeui, message, wParam, lParam);
case WM_IME_CONTROL:
return ImeControlHandler(pimeui, message, wParam, lParam, fAnsi);
case WM_IME_SETCONTEXT:
return ImeSetContextHandler(pimeui, message, wParam, lParam);
case WM_IME_NOTIFY:
return ImeNotifyHandler(pimeui, message, wParam, lParam);
case WM_IME_REQUEST:
return 0;
case WM_IME_COMPOSITION:
case WM_IME_ENDCOMPOSITION:
case WM_IME_STARTCOMPOSITION:
return SendMessageToUI(pimeui, message, wParam, lParam, fAnsi);
default:
CallDWP:
return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
}
return 0L;
}
LRESULT WINAPI ImeWndProcA(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PWND pwnd;
if ((pwnd = ValidateHwnd(hwnd)) == NULL) {
return (0L);
}
return ImeWndProcWorker(pwnd, message, wParam, lParam, TRUE);
}
LRESULT WINAPI ImeWndProcW(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PWND pwnd;
if ((pwnd = ValidateHwnd(hwnd)) == NULL) {
return (0L);
}
return ImeWndProcWorker(pwnd, message, wParam, lParam, FALSE);
}
LONG ImeWndCreateHandler(
PIMEUI pimeui,
LPCREATESTRUCT lpcs)
{
PWND pwndParent;
HIMC hImc;
PWND pwndIme = pimeui->spwnd;
#if _DBG
static DWORD dwFirstWinlogonThreadId;
#endif
#if _DBG
/*
* For Winlogon, only the first thread can have IME processing.
*/
if (gfLogonProcess) {
UserAssert(dwFirstWinLogonThreadId == 0);
dwFirstWinlogonThreadId = GetCurrentThreadId();
}
#endif
if (!TestWF(pwndIme, WFPOPUP) || !TestWF(pwndIme, WFDISABLED)) {
RIPMSG0(RIP_WARNING, "IME window should have WS_POPUP and WS_DISABLE!!");
return -1L;
}
/*
* Check with parent window, if exists, try to get IMC.
* If this is top level window, wait for first WM_IME_SETCONTEXT.
*/
if ((pwndParent = ValidateHwndNoRip(lpcs->hwndParent)) != NULL) {
hImc = KHIMC_TO_HIMC(pwndParent->hImc);
if (hImc != NULL_HIMC && ImeIsUsableContext(HWq(pwndIme), hImc)) {
/*
* Store it for later use.
*/
SETIMC(pimeui, hImc);
}
else {
SETIMC(pimeui, NULL_HIMC);
}
}
else {
SETIMC(pimeui, NULL_HIMC);
}
/*
* Initialize status window show state
* The status window is not open yet.
*/
pimeui->fShowStatus = 0;
pimeui->nCntInIMEProc = 0;
pimeui->fActivate = 0;
pimeui->fDestroy = 0;
pimeui->hwndIMC = NULL;
pimeui->hKL = THREAD_HKL();
pimeui->fCtrlShowStatus = TRUE;
#if !defined(CUAS_ENABLE) // move to LoadThreadLayout
/*
* Load up the IME DLL of current keyboard layout.
*/
fpImmLoadIME(GETHKL(pimeui));
#ifdef LATE_CREATEUI
SETUI(pimeui, NULL);
#else
SETUI(pimeui, CreateIMEUI(pimeui, pimeui->hKL));
#endif
#else
// Cicero
pimeui->dwPrevToolbarStatus = 0;
#endif // CUAS_ENABLE // move to LoadThreadLayout
return 0L;
}
void ImeWndDestroyHandler(
PIMEUI pimeui)
{
DestroyIMEUI(pimeui);
}
#ifdef CUAS_ENABLE
VOID
CtfLoadThreadLayout(
PIMEUI pimeui)
{
#if 1
/*
* Cicero Unaware Support. Activate Thread Input Manager.
*/
fpCtfImmTIMActivate(pimeui->hKL);
/*
* Load up the IME DLL of current keyboard layout.
*/
pimeui->hKL = THREAD_HKL(); // Reload thread hKL if TIM activated, hKL changed.
fpImmLoadIME(GETHKL(pimeui));
#ifdef LATE_CREATEUI
SETUI(pimeui, NULL);
#else
SETUI(pimeui, CreateIMEUI(pimeui, pimeui->hKL));
#endif
#else
UNREFERENCED_PARAMETER(pimeui);
#endif
}
#endif // CUAS_ENABLE
/***************************************************************************\
* GetSystemModulePath
*
\***************************************************************************/
#define SYSTEM_DIR 0
#define WINDOWS_DIR 1
UINT GetSystemModulePath(DWORD dir, LPWSTR psz, DWORD cch, CONST WCHAR* pszModuleName)
{
UINT uRet;
if (! psz || ! pszModuleName || cch == 0) {
return 0;
}
if (! pszModuleName[0]) {
return 0;
}
if (dir == SYSTEM_DIR) {
uRet = GetSystemDirectory(psz, cch);
}
else {
uRet = GetSystemWindowsDirectory(psz, cch);
}
if (uRet >= cch) {
uRet = 0;
psz[0] = L'\0';
}
else if (uRet) {
UINT uLen;
if (psz[uRet - 1] != L'\\') {
psz[uRet] = L'\\';
uRet++;
}
if (uRet >= cch) {
uRet = 0;
psz[0] = L'\0';
}
else {
uLen = wcslen(pszModuleName);
if (cch - uRet > uLen) {
wcsncpy(&psz[uRet],
pszModuleName,
cch - uRet);
uRet += uLen;
}
else {
uRet = 0;
psz[0] = L'\0';
}
}
}
return uRet;
}
/***************************************************************************\
* ImeRunHelp
*
* Display Help file (HLP and CHM).
*
* History:
* 27-Oct-98 Hiroyama
\***************************************************************************/
void ImeRunHelp(LPWSTR wszHelpFile)
{
static const WCHAR wszHelpFileExt[] = L".HLP";
UINT cchLen = wcslen(wszHelpFile);
if (cchLen > 4 && _wcsicmp(wszHelpFile + cchLen - 4, wszHelpFileExt) == 0) {
#ifdef FYI
WinHelpW(NULL, wszHelpFile, HELP_CONTENTS, 0);
#else
WinHelpW(NULL, wszHelpFile, HELP_FINDER, 0);
#endif
} else {
//
// If it's not HLP file, try to run hh.exe, HTML based
// help tool. It should be in %windir%\hh.exe.
//
static const WCHAR wszHH[] = L"hh.exe ";
WCHAR wszCmdLine[MAX_PATH * 2];
DWORD idProcess;
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
UINT i;
WCHAR wszAppName[MAX_PATH + 1];
if (! (i=GetSystemModulePath(WINDOWS_DIR, wszAppName, ARRAYSIZE(wszAppName), wszHH)))
{
return;
}
if (i + cchLen >= ARRAYSIZE(wszCmdLine))
{
return;
}
wcsncpy(wszCmdLine,
wszAppName,
ARRAYSIZE(wszCmdLine));
wcsncpy(&wszCmdLine[i],
wszHelpFile,
ARRAYSIZE(wszCmdLine) - i);
/*
* Launch HTML Help.
*/
RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.wShowWindow = SW_SHOW;
StartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
TAGMSG2(DBGTAG_IMM, "Invoking help with '%S %S'", wszAppName, wszHelpFile);
idProcess = (DWORD)CreateProcessW(wszAppName, wszCmdLine,
NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &StartupInfo,
&ProcessInformation);
if (idProcess) {
WaitForInputIdle(ProcessInformation.hProcess, 10000);
NtClose(ProcessInformation.hProcess);
NtClose(ProcessInformation.hThread);
}
}
}
LRESULT ImeSystemHandler(
PIMEUI pimeui,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PINPUTCONTEXT pInputContext;
HIMC hImc = GETIMC(pimeui);
LRESULT dwRet = 0L;
UNREFERENCED_PARAMETER(message);
switch (wParam) {
case IMS_SETOPENCLOSE:
if (hImc != NULL_HIMC)
fpImmSetOpenStatus(hImc, (BOOL)lParam);
break;
case IMS_WINDOWPOS:
if (hImc != NULL_HIMC) {
BOOL f31Hidden = FALSE;
if ((pInputContext = fpImmLockIMC(hImc)) != NULL) {
f31Hidden = (pInputContext->fdw31Compat & F31COMPAT_MCWHIDDEN);
fpImmUnlockIMC(hImc);
}
if (IsWindow(KHWND_TO_HWND(pimeui->hwndIMC))) {
int i;
if (!f31Hidden) {
COMPOSITIONFORM cof;
if (fpImmGetCompositionWindow(hImc, &cof) && cof.dwStyle != CFS_DEFAULT) {
fpImmSetCompositionWindow(hImc, &cof);
}
}
for (i = 0; i < 4 ; i++) {
CANDIDATEFORM caf;
if (fpImmGetCandidateWindow(hImc, (DWORD)i, &caf) && caf.dwStyle != CFS_DEFAULT) {
fpImmSetCandidateWindow(hImc, &caf);
}
}
}
}
break;
case IMS_ACTIVATECONTEXT:
FocusSetIMCContext((HWND)(lParam), TRUE);
break;
case IMS_DEACTIVATECONTEXT:
FocusSetIMCContext((HWND)(lParam), FALSE);
break;
#ifdef CUAS_ENABLE
case IMS_LOADTHREADLAYOUT:
CtfLoadThreadLayout(pimeui);
break;
#endif // CUAS_ENABLE
case IMS_UNLOADTHREADLAYOUT:
return (LONG)(fpImmFreeLayout((DWORD)lParam));
case IMS_ACTIVATETHREADLAYOUT:
return (LONG)(fpImmActivateLayout((HKL)lParam));
case IMS_SETCANDIDATEPOS:
if ( (pInputContext = fpImmLockIMC( hImc )) != NULL ) {
LPCANDIDATEFORM lpcaf;
DWORD dwIndex = (DWORD)lParam;
lpcaf = &(pInputContext->cfCandForm[dwIndex]);
fpImmSetCandidateWindow( hImc, lpcaf );
fpImmUnlockIMC( hImc );
}
break;
case IMS_SETCOMPOSITIONWINDOW:
if ( (pInputContext = fpImmLockIMC( hImc )) != NULL ) {
LPCOMPOSITIONFORM lpcof;
lpcof = &(pInputContext->cfCompForm);
pInputContext->fdw31Compat |= F31COMPAT_CALLFROMWINNLS;
fpImmSetCompositionWindow( hImc, lpcof);
fpImmUnlockIMC( hImc );
}
break;
case IMS_SETCOMPOSITIONFONT:
if ( (pInputContext = fpImmLockIMC( hImc )) != NULL ) {
LPLOGFONT lplf;
lplf = &(pInputContext->lfFont.W);
fpImmSetCompositionFont( hImc, lplf );
fpImmUnlockIMC( hImc );
}
break;
case IMS_CONFIGUREIME:
fpImmConfigureIMEW( (HKL)lParam, KHWND_TO_HWND(pimeui->hwndIMC), IME_CONFIG_GENERAL, NULL);
break;
case IMS_CHANGE_SHOWSTAT:
// Private message from internat.exe
// Before it reaches here, the registry is already updated.
if (GetIMEShowStatus() == !lParam) {
#if 1
NtUserBroadcastImeShowStatusChange(HW(pimeui->spwnd), !!lParam);
#else
SystemParametersInfo(SPI_SETSHOWIMEUI, lParam, NULL, FALSE);
#endif
}
break;
case IMS_GETCONVERSIONMODE:
{
DWORD dwConv = 0;
DWORD dwTemp;
fpImmGetConversionStatus(hImc, &dwConv, &dwTemp);
return (dwConv);
break;
}
case IMS_SETSOFTKBDONOFF:
fpImmEnumInputContext(0, SyncSoftKbdState, lParam);
break;
case IMS_GETIMEMENU:
// new in NT50
// IMS_GETIEMMENU is used to handle Inter Process GetMenu.
// NOTE: This operation is only intended to internat.exe
return fpImmPutImeMenuItemsIntoMappedFile((HIMC)lParam);
case IMS_IMEHELP:
dwRet = IME_ESC_GETHELPFILENAME;
dwRet = fpImmEscapeW(GETHKL(pimeui), GETIMC(pimeui), IME_ESC_QUERY_SUPPORT, (LPVOID)&dwRet);
if (lParam) {
// try to run WinHelp
WCHAR wszHelpFile[MAX_PATH];
if (dwRet) {
if (fpImmEscapeW(GETHKL(pimeui), GETIMC(pimeui), IME_ESC_GETHELPFILENAME,
(LPVOID)wszHelpFile)) {
ImeRunHelp(wszHelpFile);
}
}
}
return dwRet;
case IMS_GETCONTEXT:
dwRet = (ULONG_PTR)fpImmGetContext((HWND)lParam);
return dwRet;
case IMS_ENDIMEMENU:
// New in NT5.0: Special support for Internat.exe
if (IsWindow((HWND)lParam)) {
HIMC hImc;
UINT uID;
DWORD dwData;
hImc = fpImmGetContext((HWND)lParam);
if (hImc != NULL) {
//
// Call Indicator to get IME menu data.
//
if (IMEIndicatorGetMenuIDData(&uID, &dwData)) {
fpImmNotifyIME(hImc, NI_IMEMENUSELECTED, uID, dwData);
}
fpImmReleaseContext((HWND)lParam, hImc);
}
}
break;
case IMS_SENDNOTIFICATION:
case IMS_FINALIZE_COMPSTR:
dwRet = fpImmSystemHandler(hImc, wParam, lParam);
break;
#ifdef CUAS_ENABLE
case IMS_SETLANGBAND:
case IMS_RESETLANGBAND:
dwRet = fpImmSystemHandler(hImc, wParam, lParam);
break;
#endif // CUAS_ENABLE
default:
break;
}
return dwRet;
}
LONG ImeSelectHandler(
PIMEUI pimeui,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HWND hwndUI;
/*
* Deliver this message to other IME windows in this thread.
*/
if (pimeui->fDefault)
ImeBroadCastMsg(pimeui, message, wParam, lParam);
/*
* We must re-create UI window of newly selected IME.
*/
if ((BOOL)wParam == TRUE) {
UserAssert(!IsWindow(GETUI(pimeui)));
SETHKL(pimeui, (HKL)lParam);
#ifdef LATE_CREATEUI
if (!pimeui->fActivate)
return 0L;
#endif
hwndUI = CreateIMEUI(pimeui, (HKL)lParam);
SETUI(pimeui, hwndUI);
if (hwndUI != NULL) {
SetWindowLongPtr(hwndUI, IMMGWLP_IMC, (LONG_PTR)GETIMC(pimeui));
SendMessageToUI(pimeui, message, wParam, lParam, FALSE);
}
if (GetIMEShowStatus() && pimeui->fCtrlShowStatus) {
if (!pimeui->fShowStatus && pimeui->fActivate &&
IsWindow(KHWND_TO_HWND(pimeui->hwndIMC))) {
/*
* This must be sent to an application as an app may want
* to hook this message to do its own UI.
*/
SendOpenStatusNotify(pimeui, KHWND_TO_HWND(pimeui->hwndIMC), TRUE);
}
}
}
else {
if (pimeui->fShowStatus && pimeui->fActivate &&
IsWindow(KHWND_TO_HWND(pimeui->hwndIMC))) {
/*
* This must be sent to an application as an app may want
* to hook this message to do its own UI.
*/
SendOpenStatusNotify(pimeui, KHWND_TO_HWND(pimeui->hwndIMC), FALSE);
}
SendMessageToUI(pimeui, message, wParam, lParam, FALSE);
DestroyIMEUI(pimeui);
SETHKL(pimeui, (HKL)NULL);
}
return 0L;
}
LRESULT ImeControlHandler(
PIMEUI pimeui,
UINT message,
WPARAM wParam,
LPARAM lParam,
BOOL fAnsi)
{
HIMC hImc;
DWORD dwConversion, dwSentence;
#ifdef CUAS_ENABLE
if (IS_CICERO_ENABLED()) {
if (wParam == IMC_OPENSTATUSWINDOW) {
fpCtfImmRestoreToolbarWnd(pimeui->dwPrevToolbarStatus);
pimeui->dwPrevToolbarStatus = 0;
}
else if (wParam == IMC_CLOSESTATUSWINDOW) {
pimeui->dwPrevToolbarStatus = fpCtfImmHideToolbarWnd();
}
}
#endif // CUAS_ENABLE
/*
* Do nothing with NULL hImc.
*/
if ((hImc = GETIMC(pimeui)) == NULL_HIMC)
return 0L;
switch (wParam) {
case IMC_OPENSTATUSWINDOW:
if (GetIMEShowStatus() && !pimeui->fShowStatus) {
pimeui->fShowStatus = TRUE;
SendMessageToUI(pimeui, WM_IME_NOTIFY,
IMN_OPENSTATUSWINDOW, 0L, FALSE);
}
pimeui->fCtrlShowStatus = TRUE;
break;
case IMC_CLOSESTATUSWINDOW:
if (GetIMEShowStatus() && pimeui->fShowStatus) {
pimeui->fShowStatus = FALSE;
SendMessageToUI(pimeui, WM_IME_NOTIFY,
IMN_CLOSESTATUSWINDOW, 0L, FALSE);
}
pimeui->fCtrlShowStatus = FALSE;
break;
/*
* ------------------------------------------------
* IMC_SETCOMPOSITIONFONT,
* IMC_SETCONVERSIONMODE,
* IMC_SETOPENSTATUS
* ------------------------------------------------
* Don't pass these WM_IME_CONTROLs to UI window.
* Call Imm in order to process these requests instead.
* It makes message flows simpler.
*/
case IMC_SETCOMPOSITIONFONT:
if (fAnsi) {
if (!fpImmSetCompositionFontA(hImc, (LPLOGFONTA)lParam))
return 1L;
}
else {
if (!fpImmSetCompositionFontW(hImc, (LPLOGFONTW)lParam))
return 1L;
}
break;
case IMC_SETCONVERSIONMODE:
if (!fpImmGetConversionStatus(hImc, &dwConversion, &dwSentence) ||
!fpImmSetConversionStatus(hImc, (DWORD)lParam, dwSentence))
return 1L;
break;
case IMC_SETSENTENCEMODE:
if (!fpImmGetConversionStatus(hImc, &dwConversion, &dwSentence) ||
!fpImmSetConversionStatus(hImc, dwConversion, (DWORD)lParam))
return 1L;
break;
case IMC_SETOPENSTATUS:
if (!fpImmSetOpenStatus(hImc, (BOOL)lParam))
return 1L;
break;
case IMC_GETCONVERSIONMODE:
if (!fpImmGetConversionStatus(hImc,&dwConversion, &dwSentence))
return 1L;
return (LONG)dwConversion;
case IMC_GETSENTENCEMODE:
if (!fpImmGetConversionStatus(hImc,&dwConversion, &dwSentence))
return 1L;
return (LONG)dwSentence;
case IMC_GETOPENSTATUS:
return (LONG)fpImmGetOpenStatus(hImc);
case IMC_GETCOMPOSITIONFONT:
if (fAnsi) {
if (!fpImmGetCompositionFontA(hImc, (LPLOGFONTA)lParam))
return 1L;
}
else {
if (!fpImmGetCompositionFontW(hImc, (LPLOGFONTW)lParam))
return 1L;
}
break;
case IMC_SETCOMPOSITIONWINDOW:
if (!fpImmSetCompositionWindow(hImc, (LPCOMPOSITIONFORM)lParam))
return 1L;
break;
case IMC_SETSTATUSWINDOWPOS:
{
POINT ppt;
ppt.x = (LONG)((LPPOINTS)&lParam)->x;
ppt.y = (LONG)((LPPOINTS)&lParam)->y;
if (!fpImmSetStatusWindowPos(hImc, &ppt))
return 1L;
}
break;
case IMC_SETCANDIDATEPOS:
if (!fpImmSetCandidateWindow(hImc, (LPCANDIDATEFORM)lParam))
return 1;
break;
/*
* Followings are the messsages to be sent to UI.
*/
case IMC_GETCANDIDATEPOS:
case IMC_GETSTATUSWINDOWPOS:
case IMC_GETCOMPOSITIONWINDOW:
case IMC_GETSOFTKBDPOS:
case IMC_SETSOFTKBDPOS:
return SendMessageToUI(pimeui, message, wParam, lParam, fAnsi);
default:
break;
}
return 0L;
}
LRESULT ImeSetContextHandler(
PIMEUI pimeui,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HWND hwndPrevIMC, hwndFocus; // focus window in the thread
HIMC hFocusImc; // focus window's IMC
LRESULT lRet;
pimeui->fActivate = (BOOL)wParam ? 1 : 0;
hwndPrevIMC = KHWND_TO_HWND(pimeui->hwndIMC);
if (wParam) {
/*
* if it's being activated
*/
#ifdef LATE_CREATEUI
if (!GETUI(pimeui))
SETUI(pimeui, CreateIMEUI(pimeui, GETHKL(pimeui)));
#endif
/*
* Check if this process a console IME ?
*/
if (gfConIme == UNKNOWN_CONIME) {
gfConIme = (DWORD)NtUserGetThreadState(UserThreadStateIsConImeThread);
if (gfConIme) {
RIPMSG0(RIP_VERBOSE, "ImmSetContextHandler: This thread is console IME.\n");
UserAssert(pimeui);
// Console IME will never show the IME status window.
pimeui->fCtrlShowStatus = FALSE;
}
}
if (gfConIme) {
/*
* Special handling for Console IME is needed
*/
PWND pwndOwner;
UserAssert(pimeui->spwnd);
pwndOwner = REBASEPWND(pimeui->spwnd, spwndOwner);
if (pwndOwner != NULL) {
/*
* Set current associated hIMC in IMEUI.
*/
SETIMC(pimeui, pwndOwner->hImc);
/*
* Store it to the window memory.
*/
if (GETUI(pimeui) != NULL)
SetWindowLongPtr(GETUI(pimeui), IMMGWLP_IMC, (LONG_PTR)pwndOwner->hImc);
}
hwndFocus = NtUserQueryWindow(HW(pimeui->spwnd), WindowFocusWindow);
hFocusImc = KHIMC_TO_HIMC(pwndOwner->hImc);
RIPMSG2(RIP_VERBOSE, "CONSOLE IME: hwndFocus = %x, hFocusImc = %x", hwndFocus, hFocusImc);
return SendMessageToUI(pimeui, message, wParam, lParam, FALSE);
}
else {
hwndFocus = NtUserQueryWindow(HW(pimeui->spwnd), WindowFocusWindow);
hFocusImc = fpImmGetContext(hwndFocus);
}
/*
* Cannot share input context with other IME window.
*/
if (hFocusImc != NULL_HIMC &&
!ImeIsUsableContext(HW(pimeui->spwnd), hFocusImc)) {
SETIMC(pimeui, NULL_HIMC);
return 0L;
}
SETIMC(pimeui, hFocusImc);
/*
* Store it to the window memory.
*/
if (GETUI(pimeui) != NULL)
SetWindowLongPtr(GETUI(pimeui), IMMGWLP_IMC, (LONG_PTR)hFocusImc);
/*
* When we're receiving context,
* it is necessary to set the owner to this window.
* This is for:
* Give the UI moving information.
* Give the UI automatic Z-ordering.
* Hide the UI when the owner is minimized.
*/
if (hFocusImc != NULL_HIMC) {
PINPUTCONTEXT pInputContext;
/*
* Get the window who's given the context.
*/
if ((pInputContext = fpImmLockIMC(hFocusImc)) != NULL) {
//UserAssert(hwndFocus == pInputContext->hWnd);
if (hwndFocus != pInputContext->hWnd) {
fpImmUnlockIMC(hFocusImc);
/*
* Pq->spwndFocus has been changed so far...
* All we can do is just to bail out.
*/
return 0L;
}
}
else
return 0L; // the context was broken
if ((pInputContext->fdw31Compat & F31COMPAT_ECSETCFS) &&
hwndPrevIMC != hwndFocus) {
COMPOSITIONFORM cf;
/*
* Set CFS_DEFAULT....
*/
RtlZeroMemory(&cf, sizeof(cf));
fpImmSetCompositionWindow(hFocusImc, &cf);
pInputContext->fdw31Compat &= ~F31COMPAT_ECSETCFS;
}
fpImmUnlockIMC(hFocusImc);
if (NtUserSetImeOwnerWindow(HW(pimeui->spwnd), hwndFocus))
pimeui->hwndIMC = hwndFocus;
}
else {
/*
* NULL IMC is getting activated
*/
pimeui->hwndIMC = hwndFocus;
NtUserSetImeOwnerWindow(HW(pimeui->spwnd), NULL);
}
}
lRet = SendMessageToUI(pimeui, message, wParam, lParam, FALSE);
if (pimeui->spwnd == NULL) {
// Unusual case in stress..
// IME window has been destroyed during the callback
RIPMSG0(RIP_WARNING, "ImmSetContextHandler: pimeui->spwnd is NULL after SendMessageToUI.");
return 0L;
}
if (pimeui->fCtrlShowStatus && GetIMEShowStatus()) {
PWND pwndFocus, pwndIMC, pwndPrevIMC;
HWND hwndActive;
hwndFocus = NtUserQueryWindow(HWq(pimeui->spwnd), WindowFocusWindow);
pwndFocus = ValidateHwndNoRip(hwndFocus);
if ((BOOL)wParam == TRUE) {
HWND hwndIme;
/*
* BOGUS BOGUS
* The following if statement is still insufficient
* it needs to think what WM_IME_SETCONTEXT:TRUE should do
* in the case of WINNLSEnableIME(true) - ref.win95c B#8548.
*/
UserAssert(pimeui->spwnd);
if (pwndFocus != NULL && GETPTI(pimeui->spwnd) == GETPTI(pwndFocus)) {
if (!pimeui->fShowStatus) {
/*
* We have never sent IMN_OPENSTATUSWINDOW yet....
*/
if (ValidateHwndNoRip(KHWND_TO_HWND(pimeui->hwndIMC))) {
SendOpenStatusNotify(pimeui, KHWND_TO_HWND(pimeui->hwndIMC), TRUE);
}
}
else if ((pwndIMC = ValidateHwndNoRip(KHWND_TO_HWND(pimeui->hwndIMC))) != NULL &&
(pwndPrevIMC = ValidateHwndNoRip(hwndPrevIMC)) != NULL &&
GetTopLevelWindow(pwndIMC) != GetTopLevelWindow(pwndPrevIMC)) {
/*
* Because the top level window of IME Wnd was changed.
*/
SendOpenStatusNotify(pimeui, hwndPrevIMC, FALSE);
SendOpenStatusNotify(pimeui, KHWND_TO_HWND(pimeui->hwndIMC), TRUE);
}
}
/*
* There may have other IME windows that have fShowStatus.
* We need to check the fShowStatus in the window list.
*/
hwndIme = HW(pimeui->spwnd);
if (hwndIme) {
NtUserCheckImeShowStatusInThread(hwndIme);
}
}
else {
/*
* When focus was removed from this thread, we close the
* status window.
* Because focus was already removed from whndPrevIMC,
* hwndPrevIMC may be destroyed but we need to close the
* status window.
*/
hwndActive = NtUserQueryWindow(HW(pimeui->spwnd), WindowActiveWindow);
UserAssert(pimeui->spwnd);
if (pwndFocus == NULL || GETPTI(pimeui->spwnd) != GETPTI(pwndFocus) ||
hwndActive == NULL) {
if (IsWindow(hwndPrevIMC)) {
RIPMSG1(RIP_VERBOSE, "ImeSetContextHandler: notifying OpenStatus (FALSE) to hwnd=%p", hwndPrevIMC);
SendOpenStatusNotify(pimeui, hwndPrevIMC, FALSE);
}
else {
RIPMSG1(RIP_VERBOSE, "ImeSetContextHandler: sending IMN_CLOSESTATUSWINDOW to UIwnd=%p", pimeui->hwndUI);
pimeui->fShowStatus = FALSE;
SendMessageToUI(pimeui, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0L, FALSE);
}
}
}
}
return lRet;
}
LRESULT ImeNotifyHandler(
PIMEUI pimeui,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HWND hwndUI;
LRESULT lRet = 0L;
HIMC hImc;
PINPUTCONTEXT pInputContext;
switch (wParam) {
case IMN_PRIVATE:
hwndUI = GETUI(pimeui);
if (IsWindow(hwndUI))
lRet = SendMessage(hwndUI, message, wParam, lParam);
break;
case IMN_SETCONVERSIONMODE:
case IMN_SETOPENSTATUS:
//
// notify shell and keyboard the conversion mode change
//
// if this message is sent from ImmSetOpenStatus or
// ImmSetConversionStatus, we have already notified
// kernel the change. This is a little bit redundant.
//
// if application has eaten the message, we won't be here.
// We need to think about the possibility later.
//
hImc = GETIMC(pimeui);
if ((pInputContext = fpImmLockIMC(hImc)) != NULL) {
if ( IsWindow(KHWND_TO_HWND(pimeui->hwndIMC)) ) {
NtUserNotifyIMEStatus( KHWND_TO_HWND(pimeui->hwndIMC),
(DWORD)pInputContext->fOpen,
pInputContext->fdwConversion );
}
else if (gfConIme == TRUE) {
/*
* Special handling for Console IME is needed
*/
if (pimeui->spwnd) { // If IME window is still there.
PWND pwndOwner = REBASEPWND(pimeui->spwnd, spwndOwner);
if (pwndOwner != NULL) {
NtUserNotifyIMEStatus(HWq(pwndOwner),
(DWORD)pInputContext->fOpen,
pInputContext->fdwConversion);
}
}
}
fpImmUnlockIMC(hImc);
}
/*** FALL THROUGH ***/
default:
TAGMSG4(DBGTAG_IMM, "ImeNotifyHandler: sending to pimeui->ui=%p, msg=%x, wParam=%x, lParam=%x\n", GETUI(pimeui), message, wParam, lParam);
lRet = SendMessageToUI(pimeui, message, wParam, lParam, FALSE);
}
return lRet;
}
HWND CreateIMEUI(
PIMEUI pimeui,
HKL hKL)
{
PWND pwndIme = pimeui->spwnd;
HWND hwndUI;
IMEINFOEX iiex;
PIMEDPI pimedpi;
WNDCLASS wndcls;
if (!fpImmGetImeInfoEx(&iiex, ImeInfoExKeyboardLayout, &hKL))
return (HWND)NULL;
if ((pimedpi = fpImmLockImeDpi(hKL)) == NULL) {
RIPMSG1(RIP_WARNING, "CreateIMEUI: ImmLockImeDpi(%lx) failed.", hKL);
return (HWND)NULL;
}
if (!GetClassInfoW(pimedpi->hInst, iiex.wszUIClass, &wndcls)) {
RIPMSG1(RIP_WARNING, "CreateIMEUI: GetClassInfoW(%ws) failed\n", iiex.wszUIClass);
fpImmUnlockImeDpi(pimedpi);
return (HWND)NULL;
}
// HACK HACK HACK
if ((wndcls.style & CS_IME) == 0) {
RIPMSG1(RIP_ERROR, "CreateIMEUI: the Window Class (%S) does not have CS_IME flag on !!!\n",
wndcls.lpszClassName);
}
if (iiex.ImeInfo.fdwProperty & IME_PROP_UNICODE) {
/*
* For Unicode IME, we create an Unicode IME UI window.
*/
hwndUI = CreateWindowExW(0L,
iiex.wszUIClass,
iiex.wszUIClass,
WS_POPUP|WS_DISABLED,
0, 0, 0, 0,
HWq(pwndIme), 0, wndcls.hInstance, NULL);
}
else {
/*
* For ANSI IME, we create an ANSI IME UI window.
*/
LPSTR pszClass;
int i;
i = WCSToMB(iiex.wszUIClass, -1, &pszClass, -1, TRUE);
if (i == 0) {
RIPMSG1(RIP_WARNING, "CreateIMEUI: failed in W->A conversion (%S)", iiex.wszUIClass);
fpImmUnlockImeDpi(pimedpi);
return (HWND)NULL;
}
pszClass[i] = '\0';
hwndUI = CreateWindowExA(0L,
pszClass,
pszClass,
WS_POPUP|WS_DISABLED,
0, 0, 0, 0,
HWq(pwndIme), 0, wndcls.hInstance, NULL);
UserLocalFree(pszClass);
}
if (hwndUI)
NtUserSetWindowLongPtr(hwndUI, IMMGWLP_IMC, (LONG_PTR)GETIMC(pimeui), FALSE);
fpImmUnlockImeDpi(pimedpi);
return hwndUI;
}
VOID DestroyIMEUI(
PIMEUI pimeui)
{
// This has currently nothing to do except for destroying the UI.
// Review: Need to notify the UI with WM_IME_SETCONTEXT ?
// Review: This doesn't support Multiple IME install yet.
HWND hwndUI = GETUI(pimeui);
if (IsWindow(hwndUI)) {
pimeui->fDestroy = TRUE;
/*
* We need this verify because the owner might have already
* killed it during its termination.
*/
NtUserDestroyWindow(hwndUI);
}
pimeui->fDestroy = FALSE;
/*
* Reinitialize show status of the IME status window so that
* notification message will be sent when needed.
*/
pimeui->fShowStatus = FALSE;
SETUI(pimeui, NULL);
return;
}
/***************************************************************************\
* SendMessageToUI
*
* History:
* 09-Apr-1996 wkwok Created
\***************************************************************************/
LRESULT SendMessageToUI(
PIMEUI pimeui,
UINT message,
WPARAM wParam,
LPARAM lParam,
BOOL fAnsi)
{
PWND pwndUI;
LRESULT lRet;
TAGMSG1(DBGTAG_IMM, "Sending to UI msg[%04X]\n", message);
pwndUI = ValidateHwndNoRip(GETUI(pimeui));
if (pwndUI == NULL || pimeui->spwnd == NULL)
return 0L;
if (TestWF(pimeui->spwnd, WFINDESTROY) || TestWF(pimeui->spwnd, WFDESTROYED) ||
TestWF(pwndUI, WFINDESTROY) || TestWF(pwndUI, WFDESTROYED)) {
return 0L;
}
InterlockedIncrement(&pimeui->nCntInIMEProc); // Mark to avoid recursion.
lRet = SendMessageWorker(pwndUI, message, wParam, lParam, fAnsi);
InterlockedDecrement(&pimeui->nCntInIMEProc); // Mark to avoid recursion.
return lRet;
}
/***************************************************************************\
* SendOpenStatusNotify
*
* History:
* 09-Apr-1996 wkwok Created
\***************************************************************************/
VOID SendOpenStatusNotify(
PIMEUI pimeui,
HWND hwndApp,
BOOL fOpen)
{
WPARAM wParam = fOpen ? IMN_OPENSTATUSWINDOW : IMN_CLOSESTATUSWINDOW;
pimeui->fShowStatus = fOpen;
if (Is400Compat(GetClientInfo()->dwExpWinVer)) {
TAGMSG2(DBGTAG_IMM, "SendOpenStatusNotify: sending to hwnd=%lx, wParam=%d\n", hwndApp, wParam);
SendMessage(hwndApp, WM_IME_NOTIFY, wParam, 0L);
}
else {
TAGMSG2(DBGTAG_IMM, "SendOpenStatusNotify:sending to imeui->UI=%p, wParam=%d\n", GETUI(pimeui), wParam);
SendMessageToUI(pimeui, WM_IME_NOTIFY, wParam, 0L, FALSE);
}
return;
}
VOID ImeSetImc(
PIMEUI pimeui,
HIMC hImc)
{
HWND hImeWnd = HW(pimeui->spwnd);
HIMC hOldImc = GETIMC(pimeui);
/*
* return if nothing to change.
*/
if (hImc == hOldImc)
return;
/*
* Unmark the old input context.
*/
if (hOldImc != NULL_HIMC)
ImeMarkUsedContext(NULL, hOldImc);
/*
* Update the in use input context for this IME window.
*/
pimeui->hIMC = hImc;
/*
* Mark the new input context.
*/
if (hImc != NULL_HIMC)
ImeMarkUsedContext(hImeWnd, hImc);
}
/***************************************************************************\
* FocusSetIMCContext()
*
* History:
* 21-Mar-1996 wkwok Created
\***************************************************************************/
VOID FocusSetIMCContext(
HWND hWnd,
BOOL fActivate)
{
HIMC hImc;
if (IsWindow(hWnd)) {
hImc = fpImmGetContext(hWnd);
fpImmSetActiveContext(hWnd, hImc, fActivate);
fpImmReleaseContext(hWnd, hImc);
}
else {
fpImmSetActiveContext(NULL, NULL_HIMC, fActivate);
}
return;
}
BOOL ImeBroadCastMsg(
PIMEUI pimeui,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
UNREFERENCED_PARAMETER(pimeui);
UNREFERENCED_PARAMETER(message);
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
return TRUE;
}
/***************************************************************************\
* ImeMarkUsedContext()
*
* Some IME windows can not share the same input context. This function
* marks the specified hImc to be in used by the specified IME window.
*
* History:
* 12-Mar-1996 wkwok Created
\***************************************************************************/
VOID ImeMarkUsedContext(
HWND hImeWnd,
HIMC hImc)
{
PIMC pImc;
pImc = HMValidateHandle((HANDLE)hImc, TYPE_INPUTCONTEXT);
if (pImc == NULL) {
RIPMSG1(RIP_WARNING, "ImeMarkUsedContext: Invalid hImc (=%lx).", hImc);
return;
}
UserAssert( ValidateHwndNoRip(pImc->hImeWnd) == NULL || hImeWnd == NULL );
/*
* Nothing to change?
*/
if (pImc->hImeWnd == hImeWnd)
return;
NtUserUpdateInputContext(hImc, UpdateInUseImeWindow, (ULONG_PTR)hImeWnd);
return;
}
/***************************************************************************\
* ImeIsUsableContext()
*
* Some IME windows can not share the same input context. This function
* checks whether the specified hImc can be used (means 'Set activated')
* by the specified IME window.
*
* Return: TRUE - OK to use the hImc by hImeWnd.
* FALSE - otherwise.
*
* History:
* 12-Mar-1996 wkwok Created
\***************************************************************************/
BOOL ImeIsUsableContext(
HWND hImeWnd,
HIMC hImc)
{
PIMC pImc;
UserAssert(hImeWnd != NULL);
pImc = HMValidateHandle((HANDLE)hImc, TYPE_INPUTCONTEXT);
if (pImc == NULL) {
RIPMSG1(RIP_WARNING, "ImeIsUsableContext: Invalid hImc (=%lx).", hImc);
return FALSE;
}
if ( pImc->hImeWnd == NULL ||
pImc->hImeWnd == hImeWnd ||
ValidateHwndNoRip(pImc->hImeWnd) == NULL )
{
return TRUE;
}
return FALSE;
}
/***************************************************************************\
* GetIMEShowStatus()
*
* Get the global IME show status from kernel.
*
* History:
* 19-Sep-1996 takaok Ported from internat.exe.
\***************************************************************************/
BOOL GetIMEShowStatus(void)
{
return (BOOL)NtUserCallNoParam(SFI__GETIMESHOWSTATUS);
}
/***************************************************************************\
* IMEIndicatorGetMenuIDData
*
* History:
* 3-Nov-97 Hiroyama
\***************************************************************************/
BOOL IMEIndicatorGetMenuIDData(PUINT puMenuID, PDWORD pdwData)
{
HANDLE hinstIndic;
WCHAR szModule[MAX_PATH + 1];
if (! GetSystemModulePath(SYSTEM_DIR, szModule, ARRAYSIZE(szModule), szIndicDLL)) {
gpfnGetIMEMenuItemData = NULL;
return FALSE;
}
hinstIndic = GetModuleHandle(szModule);
if (hinstIndic == NULL) {
gpfnGetIMEMenuItemData = NULL;
return FALSE;
}
if (!gpfnGetIMEMenuItemData) {
gpfnGetIMEMenuItemData = GetProcAddress(hinstIndic, (LPSTR)ORD_GETIMEMENUITEMDATA);
}
if (!gpfnGetIMEMenuItemData)
return FALSE;
(*(FPGETIMEMENUITEMDATA)gpfnGetIMEMenuItemData)(puMenuID, pdwData);
return TRUE;
}
BOOL SyncSoftKbdState(
HIMC hImc,
LPARAM lParam)
{
DWORD fdwConversion, fdwSentence, fdwNewConversion;
fpImmGetConversionStatus(hImc, &fdwConversion, &fdwSentence);
if (lParam) {
fdwNewConversion = fdwConversion | IME_CMODE_SOFTKBD;
} else {
fdwNewConversion = fdwConversion & ~IME_CMODE_SOFTKBD;
}
if (fdwNewConversion != fdwConversion) {
fpImmSetConversionStatus(hImc, fdwNewConversion, fdwSentence);
}
return TRUE;
}