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
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;
|
|
}
|
|
|