blob: b9c6088c5a86b2936bdc3e0ad1620822d34eddec [file] [log] [blame]
/*
* Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jlong.h"
#include "awtmsg.h"
#include "awt_AWTEvent.h"
#include "awt_Component.h"
#include "awt_Toolkit.h"
#include "locale_str.h"
#include <sun_awt_windows_WInputMethod.h>
#include <sun_awt_windows_WInputMethodDescriptor.h>
#include <java_awt_event_InputMethodEvent.h>
const UINT SYSCOMMAND_IMM = 0xF000 - 100;
/************************************************************************
* WInputMethod native methods
*/
extern "C" {
jobject CreateLocaleObject(JNIEnv *env, const char * name);
HKL getDefaultKeyboardLayout();
extern BOOL g_bUserHasChangedInputLang;
/*
* Class: sun_awt_windows_WInputMethod
* Method: createNativeContext
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_sun_awt_windows_WInputMethod_createNativeContext(JNIEnv *env, jobject self)
{
TRY;
// use special message to call ImmCreateContext() in main thread.
return (jint)AwtToolkit::GetInstance().SendMessage(WM_AWT_CREATECONTEXT);
CATCH_BAD_ALLOC_RET(0);
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: destroyNativeContext
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WInputMethod_destroyNativeContext(JNIEnv *env, jobject self, jint context)
{
TRY_NO_VERIFY;
// use special message to call ImmDestroyContext() in main thread.
AwtToolkit::GetInstance().SendMessage(WM_AWT_DESTROYCONTEXT, context, 0);
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: enableNativeIME
* Signature: (Lsun/awt/windows/WComponentPeer;I)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WInputMethod_enableNativeIME(JNIEnv *env, jobject self, jobject peer,
jint context, jboolean useNativeCompWindow)
{
TRY;
jobject selfGlobalRef = env->NewGlobalRef(self);
jobject peerGlobalRef = env->NewGlobalRef(peer);
EnableNativeIMEStruct *enis = new EnableNativeIMEStruct;
enis->self = selfGlobalRef;
enis->peer = peerGlobalRef;
enis->context = context;
enis->useNativeCompWindow = useNativeCompWindow;
AwtToolkit::GetInstance().SendMessage(WM_AWT_ASSOCIATECONTEXT,
reinterpret_cast<WPARAM>(enis), (LPARAM)0);
// global refs are deleted in message handler
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: disableNativeIME
* Signature: (Lsun/awt/windows/WComponentPeer;)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WInputMethod_disableNativeIME(JNIEnv *env, jobject self, jobject peer)
{
TRY_NO_VERIFY;
jobject peerGlobalRef = env->NewGlobalRef(peer);
// self reference is not used
EnableNativeIMEStruct *enis = new EnableNativeIMEStruct;
enis->self = NULL;
enis->peer = peerGlobalRef;
enis->context = NULL;
enis->useNativeCompWindow = JNI_TRUE;
AwtToolkit::GetInstance().SendMessage(WM_AWT_ASSOCIATECONTEXT,
reinterpret_cast<WPARAM>(enis), (LPARAM)0);
// global refs are deleted in message handler
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WComponentPeer
* Method: handleEvent
* Signature: (Lsun/awt/windows/WComponentPeer;Ljava/awt/AWTEvent;)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WInputMethod_handleNativeIMEEvent(JNIEnv *env, jobject self,
jobject peer, jobject event)
{
TRY;
PDATA pData;
JNI_CHECK_PEER_RETURN(peer);
AwtComponent* p = (AwtComponent *)pData;
JNI_CHECK_NULL_RETURN(event, "null AWTEvent");
if (env->EnsureLocalCapacity(1) < 0) {
return;
}
jbyteArray bdata = (jbyteArray)(env)->GetObjectField(event, AwtAWTEvent::bdataID);
if (bdata == 0) {
return;
}
MSG msg;
(env)->GetByteArrayRegion(bdata, 0, sizeof(MSG), (jbyte *)&msg);
(env)->DeleteLocalRef(bdata);
BOOL isConsumed =
(BOOL)(env)->GetBooleanField(event, AwtAWTEvent::consumedID);
int id = (env)->GetIntField(event, AwtAWTEvent::idID);
DASSERT(!safe_ExceptionOccurred(env));
if (isConsumed || p==NULL) return;
if (id >= java_awt_event_InputMethodEvent_INPUT_METHOD_FIRST &&
id <= java_awt_event_InputMethodEvent_INPUT_METHOD_LAST)
{
jobject peerGlobalRef = env->NewGlobalRef(peer);
// use special message to access pData on the toolkit thread
AwtToolkit::GetInstance().SendMessage(WM_AWT_HANDLE_NATIVE_IME_EVENT,
reinterpret_cast<WPARAM>(peerGlobalRef),
reinterpret_cast<LPARAM>(&msg));
// global ref is deleted in message handler
(env)->SetBooleanField(event, AwtAWTEvent::consumedID, JNI_TRUE);
}
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: notifyNativeIME
* Signature: (Lsun/awt/windows/WComponentPeer;I)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WInputMethod_endCompositionNative(JNIEnv *env, jobject self,
jint context, jboolean flag)
{
TRY;
// TODO: currently the flag parameter is ignored and the outstanding input is
// always discarded.
// If the flag value is Java_sun_awt_windows_WInputMethod_COMMIT_INPUT,
// then input text should be committed. Otherwise, should be discarded.
//
// 10/29/98 - Changed to commit it according to the flag.
// use special message to call ImmNotifyIME() in main thread.
AwtToolkit::GetInstance().SendMessage(WM_AWT_ENDCOMPOSITION, context,
(LPARAM)(flag != sun_awt_windows_WInputMethod_DISCARD_INPUT));
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: setConversionStatus
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WInputMethod_setConversionStatus(JNIEnv *env, jobject self, jint context, jint request)
{
TRY;
// use special message to call ImmSetConversionStatus() in main thread.
AwtToolkit::GetInstance().SendMessage(WM_AWT_SETCONVERSIONSTATUS,
context,
MAKELPARAM((WORD)request, (WORD)0));
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: getConversionStatus
* Signature: (I)I
*/
JNIEXPORT jint JNICALL
Java_sun_awt_windows_WInputMethod_getConversionStatus(JNIEnv *env, jobject self, jint context)
{
TRY;
// use special message to call ImmSetConversionStatus() in main thread.
return (jint) AwtToolkit::GetInstance().SendMessage(
WM_AWT_GETCONVERSIONSTATUS, context, 0);
CATCH_BAD_ALLOC_RET(0);
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: setOpenStatus
* Signature: (IZ)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WInputMethod_setOpenStatus(JNIEnv *env, jobject self, jint context, jboolean flag)
{
TRY;
// use special message to call ImmSetConversionStatus() in main thread.
AwtToolkit::GetInstance().SendMessage(WM_AWT_SETOPENSTATUS,
context, flag);
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: getConversionStatus
* Signature: (I)Z
*/
JNIEXPORT jboolean JNICALL
Java_sun_awt_windows_WInputMethod_getOpenStatus(JNIEnv *env, jobject self, jint context)
{
TRY;
// use special message to call ImmSetConversionStatus() in main thread.
return (jboolean)(AwtToolkit::GetInstance().SendMessage(
WM_AWT_GETOPENSTATUS,
context, 0));
CATCH_BAD_ALLOC_RET(0);
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: getNativeLocale
* Signature: ()Ljava/util/Locale;
*/
JNIEXPORT jobject JNICALL Java_sun_awt_windows_WInputMethod_getNativeLocale
(JNIEnv *env, jclass cls)
{
TRY;
const char * javaLocaleName = getJavaIDFromLangID(AwtComponent::GetInputLanguage());
if (javaLocaleName != NULL) {
// Now WInputMethod.currentLocale and AwtComponent::m_idLang are get sync'ed,
// so we can reset this flag.
g_bUserHasChangedInputLang = FALSE;
return CreateLocaleObject(env, javaLocaleName);
} else {
return NULL;
}
CATCH_BAD_ALLOC_RET(NULL);
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: setNativeLocale
* Signature: (Ljava/lang/String;Z)Z
*/
JNIEXPORT jboolean JNICALL Java_sun_awt_windows_WInputMethod_setNativeLocale
(JNIEnv *env, jclass cls, jstring localeString, jboolean onActivate)
{
TRY;
// check if current language ID is the requested one. Note that the
// current language ID (returned from 'getJavaIDFromLangID') is in
// ASCII encoding, so we use 'GetStringUTFChars' to retrieve requested
// language ID from the 'localeString' object.
const char * current = getJavaIDFromLangID(AwtComponent::GetInputLanguage());
jboolean isCopy;
const char * requested = env->GetStringUTFChars(localeString, &isCopy);
if ((current != NULL) && (strcmp(current, requested) == 0)) {
env->ReleaseStringUTFChars(localeString, requested);
return JNI_TRUE;
}
// get list of available HKLs. Adding the user's preferred layout on top of the layout
// list which is returned by GetKeyboardLayoutList ensures to match first when
// looking up suitable layout.
int layoutCount = ::GetKeyboardLayoutList(0, NULL) + 1; // +1 for user's preferred HKL
HKL FAR * hKLList = (HKL FAR *)safe_Malloc(sizeof(HKL)*layoutCount);
DASSERT(!safe_ExceptionOccurred(env));
::GetKeyboardLayoutList(layoutCount - 1, &(hKLList[1]));
hKLList[0] = getDefaultKeyboardLayout(); // put user's preferred layout on top of the list
// lookup matching LangID
jboolean retValue = JNI_FALSE;
for (int i = 0; i < layoutCount; i++) {
const char * supported = getJavaIDFromLangID(LOWORD(hKLList[i]));
if ((supported != NULL) && (strcmp(supported, requested) == 0)) {
// use special message to call ActivateKeyboardLayout() in main thread.
if (AwtToolkit::GetInstance().SendMessage(WM_AWT_ACTIVATEKEYBOARDLAYOUT, (WPARAM)onActivate, (LPARAM)hKLList[i])) {
//also need to change the same keyboard layout for the Java AWT-EventQueue thread
AwtToolkit::activateKeyboardLayout(hKLList[i]);
retValue = JNI_TRUE;
}
break;
}
}
env->ReleaseStringUTFChars(localeString, requested);
free(hKLList);
return retValue;
CATCH_BAD_ALLOC_RET(JNI_FALSE);
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: hideWindowsNative
* Signature: (Lsun/awt/windows/WComponentPeer;Z)V
*/
JNIEXPORT void JNICALL Java_sun_awt_windows_WInputMethod_setStatusWindowVisible
(JNIEnv *env, jobject self, jobject peer, jboolean visible)
{
/* Retrieve the default input method Window handler from AwtToolkit.
Windows system creates a default input method window for the
toolkit thread.
*/
HWND defaultIMEHandler = AwtToolkit::GetInstance().GetInputMethodWindow();
if (defaultIMEHandler == NULL)
{
jobject peerGlobalRef = env->NewGlobalRef(peer);
// use special message to access pData on the toolkit thread
LRESULT res = AwtToolkit::GetInstance().SendMessage(WM_AWT_GET_DEFAULT_IME_HANDLER,
reinterpret_cast<WPARAM>(peerGlobalRef), 0);
// global ref is deleted in message handler
if (res == TRUE) {
defaultIMEHandler = AwtToolkit::GetInstance().GetInputMethodWindow();
}
}
if (defaultIMEHandler != NULL) {
::SendMessage(defaultIMEHandler, WM_IME_CONTROL,
visible ? IMC_OPENSTATUSWINDOW : IMC_CLOSESTATUSWINDOW, 0);
}
}
/*
* Class: sun_awt_windows_WInputMethod
* Method: openCandidateWindow
* Signature: (Lsun/awt/windows/WComponentPeer;II)V
*/
JNIEXPORT void JNICALL Java_sun_awt_windows_WInputMethod_openCandidateWindow
(JNIEnv *env, jobject self, jobject peer, jint x, jint y)
{
TRY;
PDATA pData;
JNI_CHECK_PEER_RETURN(peer);
jobject peerGlobalRef = env->NewGlobalRef(peer);
// WARNING! MAKELONG macro treats the given values as unsigned.
// This may lead to some bugs in multiscreen configurations, as
// coordinates can be negative numbers. So, while handling
// WM_AWT_OPENCANDIDATEWINDOW message in AwtToolkit, we should
// carefully extract right x and y values using GET_X_LPARAM and
// GET_Y_LPARAM, not LOWORD and HIWORD
// See CR 4805862, AwtToolkit::WndProc
// use special message to open candidate window in main thread.
AwtToolkit::GetInstance().SendMessage(WM_AWT_OPENCANDIDATEWINDOW,
(WPARAM)peerGlobalRef, MAKELONG(x, y));
// global ref is deleted in message handler
CATCH_BAD_ALLOC;
}
/************************************************************************
* WInputMethodDescriptor native methods
*/
/*
* Class: sun_awt_windows_WInputMethodDescriptor
* Method: getNativeAvailableLocales
* Signature: ()[Ljava/util/Locale;
*/
JNIEXPORT jobjectArray JNICALL Java_sun_awt_windows_WInputMethodDescriptor_getNativeAvailableLocales
(JNIEnv *env, jclass self)
{
TRY;
// get list of available HKLs
int layoutCount = ::GetKeyboardLayoutList(0, NULL);
HKL FAR * hKLList = (HKL FAR *)safe_Malloc(sizeof(HKL)*layoutCount);
DASSERT(!safe_ExceptionOccurred(env));
::GetKeyboardLayoutList(layoutCount, hKLList);
// get list of Java locale names while getting rid of duplicates
int srcIndex = 0;
int destIndex = 0;
int javaLocaleNameCount = 0;
int current = 0;
const char ** javaLocaleNames = (const char **)safe_Malloc(sizeof(char *)*layoutCount);
DASSERT(!safe_ExceptionOccurred(env));
for (; srcIndex < layoutCount; srcIndex++) {
const char * srcLocaleName = getJavaIDFromLangID(LOWORD(hKLList[srcIndex]));
if (srcLocaleName == NULL) {
// could not find corresponding Java locale name for this HKL.
continue;
}
for (current = 0; current < destIndex; current++) {
if (strcmp(javaLocaleNames[current], srcLocaleName) == 0) {
// duplicated. ignore this HKL
break;
}
}
if (current == destIndex) {
javaLocaleNameCount++;
destIndex++;
javaLocaleNames[current] = srcLocaleName;
}
}
// convert it to an array of Java locale objects
jclass localeClass = env->FindClass("java/util/Locale");
jobjectArray locales = env->NewObjectArray(javaLocaleNameCount, localeClass, NULL);
for (current = 0; current < javaLocaleNameCount; current++) {
env->SetObjectArrayElement(locales,
current,
CreateLocaleObject(env, javaLocaleNames[current]));
}
DASSERT(!safe_ExceptionOccurred(env));
env->DeleteLocalRef(localeClass);
free(hKLList);
free(javaLocaleNames);
return locales;
CATCH_BAD_ALLOC_RET(NULL);
}
/**
* Class: sun_awt_windows_WInputMethod
* Method: getNativeIMMDescription
* Signature: ()Ljava/lang/String;
*
* This method tries to get the information about the input method associated with
* the current active thread.
*
*/
JNIEXPORT jstring JNICALL Java_sun_awt_windows_WInputMethod_getNativeIMMDescription
(JNIEnv *env, jobject self) {
TRY;
// Get the keyboard layout of the active thread.
HKL hkl = AwtComponent::GetKeyboardLayout();
LPTSTR szImmDescription = NULL;
UINT buffSize = 0;
jstring infojStr = NULL;
if ((buffSize = ::ImmGetDescription(hkl, szImmDescription, 0)) > 0) {
szImmDescription = (LPTSTR) safe_Malloc(buffSize * sizeof(TCHAR));
if (szImmDescription != NULL) {
ImmGetDescription(hkl, szImmDescription, buffSize);
infojStr = JNU_NewStringPlatform(env, szImmDescription);
free(szImmDescription);
}
}
return infojStr;
CATCH_BAD_ALLOC_RET(NULL);
}
/*
* Create a Java locale object from its name string
*/
jobject CreateLocaleObject(JNIEnv *env, const char * name)
{
TRY;
// get language, country, variant information
char * language = (char *)safe_Malloc(strlen(name) + 1);
char * country;
char * variant;
DASSERT(!safe_ExceptionOccurred(env));
strcpy(language, name);
for (country = language; *country != '_' && *country != '\0'; country++);
if (*country == '_') {
*country++ = '\0';
for (variant = country; *variant != '_' && *variant != '\0'; variant++);
if (*variant == '_') {
*variant++ = '\0';
}
} else {
variant = country;
}
// create Locale object
jobject langObj = env->NewStringUTF(language);
jobject ctryObj = env->NewStringUTF(country);
jobject vrntObj = env->NewStringUTF(variant);
jobject localeObj = JNU_NewObjectByName(env, "java/util/Locale",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
langObj, ctryObj, vrntObj);
free(language);
env->DeleteLocalRef(langObj);
env->DeleteLocalRef(ctryObj);
env->DeleteLocalRef(vrntObj);
return localeObj;
CATCH_BAD_ALLOC_RET(NULL);
}
/*
* Gets user's preferred keyboard layout
* Warning: This is version dependent code
*/
HKL getDefaultKeyboardLayout() {
LONG ret;
HKL hkl = 0;
HKEY hKey;
BYTE szHKL[16];
DWORD cbHKL = 16;
LPTSTR end;
ret = ::RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Keyboard Layout\\Preload"), NULL, KEY_READ, &hKey);
if (ret == ERROR_SUCCESS) {
ret = ::RegQueryValueEx(hKey, TEXT("1"), 0, 0, szHKL, &cbHKL);
if (ret == ERROR_SUCCESS) {
hkl = reinterpret_cast<HKL>(static_cast<INT_PTR>(
_tcstoul((LPCTSTR)szHKL, &end, 16)));
}
::RegCloseKey(hKey);
}
return hkl;
}
} /* extern "C" */