1 /* 2 * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "jlong.h" 27 #include "awtmsg.h" 28 #include "awt_AWTEvent.h" 29 #include "awt_Component.h" 30 #include "awt_Toolkit.h" 31 #include "locale_str.h" 32 #include <sun_awt_windows_WInputMethod.h> 33 #include <sun_awt_windows_WInputMethodDescriptor.h> 34 #include <java_awt_event_InputMethodEvent.h> 35 36 const UINT SYSCOMMAND_IMM = 0xF000 - 100; 37 38 /************************************************************************ 39 * WInputMethod native methods 40 */ 41 42 extern "C" { 43 44 jobject CreateLocaleObject(JNIEnv *env, const char * name); 45 HKL getDefaultKeyboardLayout(); 46 47 extern BOOL g_bUserHasChangedInputLang; 48 49 /* 50 * Class: sun_awt_windows_WInputMethod 51 * Method: createNativeContext 52 * Signature: ()I 53 */ 54 JNIEXPORT jint JNICALL 55 Java_sun_awt_windows_WInputMethod_createNativeContext(JNIEnv *env, jobject self) 56 { 57 TRY; 58 59 // use special message to call ImmCreateContext() in main thread. 60 return (jint)AwtToolkit::GetInstance().SendMessage(WM_AWT_CREATECONTEXT); 61 62 CATCH_BAD_ALLOC_RET(0); 63 } 64 65 66 /* 67 * Class: sun_awt_windows_WInputMethod 68 * Method: destroyNativeContext 69 * Signature: (I)V 70 */ 71 JNIEXPORT void JNICALL 72 Java_sun_awt_windows_WInputMethod_destroyNativeContext(JNIEnv *env, jobject self, jint context) 73 { 74 TRY_NO_VERIFY; 75 76 // use special message to call ImmDestroyContext() in main thread. 77 AwtToolkit::GetInstance().SendMessage(WM_AWT_DESTROYCONTEXT, context, 0); 78 79 CATCH_BAD_ALLOC; 80 } 81 82 83 /* 84 * Class: sun_awt_windows_WInputMethod 85 * Method: enableNativeIME 86 * Signature: (Lsun/awt/windows/WComponentPeer;I)V 87 */ 88 JNIEXPORT void JNICALL 89 Java_sun_awt_windows_WInputMethod_enableNativeIME(JNIEnv *env, jobject self, jobject peer, 90 jint context, jboolean useNativeCompWindow) 91 { 92 TRY; 93 94 //get C++ Class of Focused Component 95 if (peer == 0) return; 96 AwtComponent* p = (AwtComponent*)JNI_GET_PDATA(peer); 97 if (p == 0) return; 98 99 p->SetInputMethod(self, useNativeCompWindow); 100 101 // use special message to call ImmAssociateContext() in main thread. 102 AwtToolkit::GetInstance().SendMessage(WM_AWT_ASSOCIATECONTEXT, 103 reinterpret_cast<WPARAM>(p->GetHWnd()), context); 104 105 CATCH_BAD_ALLOC; 106 } 107 108 109 /* 110 * Class: sun_awt_windows_WInputMethod 111 * Method: disableNativeIME 112 * Signature: (Lsun/awt/windows/WComponentPeer;)V 113 */ 114 JNIEXPORT void JNICALL 115 Java_sun_awt_windows_WInputMethod_disableNativeIME(JNIEnv *env, jobject self, jobject peer) 116 { 117 TRY_NO_VERIFY; 118 119 //get C++ Class of Focused Component 120 if (peer == 0) return; 121 AwtComponent* p = (AwtComponent*)JNI_GET_PDATA(peer); 122 if (p == 0) return; 123 124 p->SetInputMethod(NULL, TRUE); 125 126 // use special message to call ImmAssociateContext() in main thread. 127 AwtToolkit::GetInstance().SendMessage(WM_AWT_ASSOCIATECONTEXT, 128 reinterpret_cast<WPARAM>(p->GetHWnd()), NULL); 129 130 CATCH_BAD_ALLOC; 131 } 132 133 134 /* 135 * Class: sun_awt_windows_WComponentPeer 136 * Method: handleEvent 137 * Signature: (Lsun/awt/windows/WComponentPeer;Ljava/awt/AWTEvent;)V 138 */ 139 JNIEXPORT void JNICALL 140 Java_sun_awt_windows_WInputMethod_handleNativeIMEEvent(JNIEnv *env, jobject self, 141 jobject peer, jobject event) 142 { 143 TRY; 144 145 PDATA pData; 146 JNI_CHECK_PEER_RETURN(peer); 147 AwtComponent* p = (AwtComponent *)pData; 148 149 JNI_CHECK_NULL_RETURN(event, "null AWTEvent"); 150 if (env->EnsureLocalCapacity(1) < 0) { 151 return; 152 } 153 jbyteArray bdata = (jbyteArray)(env)->GetObjectField(event, AwtAWTEvent::bdataID); 154 if (bdata == 0) { 155 return; 156 } 157 MSG msg; 158 (env)->GetByteArrayRegion(bdata, 0, sizeof(MSG), (jbyte *)&msg); 159 (env)->DeleteLocalRef(bdata); 160 BOOL isConsumed = 161 (BOOL)(env)->GetBooleanField(event, AwtAWTEvent::consumedID); 162 int id = (env)->GetIntField(event, AwtAWTEvent::idID); 163 DASSERT(!safe_ExceptionOccurred(env)); 164 165 if (isConsumed || p==NULL) return; 166 167 if (id >= java_awt_event_InputMethodEvent_INPUT_METHOD_FIRST && 168 id <= java_awt_event_InputMethodEvent_INPUT_METHOD_LAST) 169 { 170 long modifiers = p->GetJavaModifiers(); 171 if (msg.message==WM_CHAR || msg.message==WM_SYSCHAR) { 172 WCHAR unicodeChar = L'\0'; 173 unicodeChar = (WCHAR)msg.wParam; 174 p->SendKeyEvent(java_awt_event_KeyEvent_KEY_TYPED, 175 0, //to be fixed nowMillis(), 176 java_awt_event_KeyEvent_CHAR_UNDEFINED, 177 unicodeChar, 178 modifiers, 179 java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN, 180 &msg); 181 } else { 182 MSG* pCopiedMsg = new MSG; 183 *pCopiedMsg = msg; 184 p->SendMessage(WM_AWT_HANDLE_EVENT, (WPARAM) FALSE, 185 (LPARAM) pCopiedMsg); 186 } 187 (env)->SetBooleanField(event, AwtAWTEvent::consumedID, JNI_TRUE); 188 } 189 190 CATCH_BAD_ALLOC; 191 } 192 193 /* 194 * Class: sun_awt_windows_WInputMethod 195 * Method: notifyNativeIME 196 * Signature: (Lsun/awt/windows/WComponentPeer;I)V 197 */ 198 JNIEXPORT void JNICALL 199 Java_sun_awt_windows_WInputMethod_endCompositionNative(JNIEnv *env, jobject self, 200 jint context, jboolean flag) 201 { 202 TRY; 203 204 // TODO: currently the flag parameter is ignored and the outstanding input is 205 // always discarded. 206 // If the flag value is Java_sun_awt_windows_WInputMethod_COMMIT_INPUT, 207 // then input text should be committed. Otherwise, should be discarded. 208 // 209 // 10/29/98 - Changed to commit it according to the flag. 210 211 // use special message to call ImmNotifyIME() in main thread. 212 AwtToolkit::GetInstance().SendMessage(WM_AWT_ENDCOMPOSITION, context, 213 (LPARAM)(flag != sun_awt_windows_WInputMethod_DISCARD_INPUT)); 214 215 CATCH_BAD_ALLOC; 216 } 217 218 /* 219 * Class: sun_awt_windows_WInputMethod 220 * Method: setConversionStatus 221 * Signature: (II)V 222 */ 223 JNIEXPORT void JNICALL 224 Java_sun_awt_windows_WInputMethod_setConversionStatus(JNIEnv *env, jobject self, jint context, jint request) 225 { 226 TRY; 227 228 // use special message to call ImmSetConversionStatus() in main thread. 229 AwtToolkit::GetInstance().SendMessage(WM_AWT_SETCONVERSIONSTATUS, 230 context, 231 MAKELPARAM((WORD)request, (WORD)0)); 232 233 CATCH_BAD_ALLOC; 234 } 235 236 /* 237 * Class: sun_awt_windows_WInputMethod 238 * Method: getConversionStatus 239 * Signature: (I)I 240 */ 241 JNIEXPORT jint JNICALL 242 Java_sun_awt_windows_WInputMethod_getConversionStatus(JNIEnv *env, jobject self, jint context) 243 { 244 TRY; 245 246 // use special message to call ImmSetConversionStatus() in main thread. 247 return (jint) AwtToolkit::GetInstance().SendMessage( 248 WM_AWT_GETCONVERSIONSTATUS, context, 0); 249 250 CATCH_BAD_ALLOC_RET(0); 251 } 252 253 /* 254 * Class: sun_awt_windows_WInputMethod 255 * Method: setOpenStatus 256 * Signature: (IZ)V 257 */ 258 JNIEXPORT void JNICALL 259 Java_sun_awt_windows_WInputMethod_setOpenStatus(JNIEnv *env, jobject self, jint context, jboolean flag) 260 { 261 TRY; 262 263 // use special message to call ImmSetConversionStatus() in main thread. 264 AwtToolkit::GetInstance().SendMessage(WM_AWT_SETOPENSTATUS, 265 context, flag); 266 267 CATCH_BAD_ALLOC; 268 } 269 270 /* 271 * Class: sun_awt_windows_WInputMethod 272 * Method: getConversionStatus 273 * Signature: (I)Z 274 */ 275 JNIEXPORT jboolean JNICALL 276 Java_sun_awt_windows_WInputMethod_getOpenStatus(JNIEnv *env, jobject self, jint context) 277 { 278 TRY; 279 280 // use special message to call ImmSetConversionStatus() in main thread. 281 return (jboolean)(AwtToolkit::GetInstance().SendMessage( 282 WM_AWT_GETOPENSTATUS, 283 context, 0)); 284 CATCH_BAD_ALLOC_RET(0); 285 } 286 287 /* 288 * Class: sun_awt_windows_WInputMethod 289 * Method: getNativeLocale 290 * Signature: ()Ljava/util/Locale; 291 */ 292 JNIEXPORT jobject JNICALL Java_sun_awt_windows_WInputMethod_getNativeLocale 293 (JNIEnv *env, jclass cls) 294 { 295 TRY; 296 297 const char * javaLocaleName = getJavaIDFromLangID(AwtComponent::GetInputLanguage()); 298 if (javaLocaleName != NULL) { 299 // Now WInputMethod.currentLocale and AwtComponent::m_idLang are get sync'ed, 300 // so we can reset this flag. 301 g_bUserHasChangedInputLang = FALSE; 302 303 return CreateLocaleObject(env, javaLocaleName); 304 } else { 305 return NULL; 306 } 307 308 CATCH_BAD_ALLOC_RET(NULL); 309 } 310 311 /* 312 * Class: sun_awt_windows_WInputMethod 313 * Method: setNativeLocale 314 * Signature: (Ljava/lang/String;Z)Z 315 */ 316 JNIEXPORT jboolean JNICALL Java_sun_awt_windows_WInputMethod_setNativeLocale 317 (JNIEnv *env, jclass cls, jstring localeString, jboolean onActivate) 318 { 319 TRY; 320 321 // check if current language ID is the requested one. Note that the 322 // current language ID (returned from 'getJavaIDFromLangID') is in 323 // ASCII encoding, so we use 'GetStringUTFChars' to retrieve requested 324 // language ID from the 'localeString' object. 325 const char * current = getJavaIDFromLangID(AwtComponent::GetInputLanguage()); 326 jboolean isCopy; 327 const char * requested = env->GetStringUTFChars(localeString, &isCopy); 328 if ((current != NULL) && (strcmp(current, requested) == 0)) { 329 env->ReleaseStringUTFChars(localeString, requested); 330 return JNI_TRUE; 331 } 332 333 // get list of available HKLs. Adding the user's preferred layout on top of the layout 334 // list which is returned by GetKeyboardLayoutList ensures to match first when 335 // looking up suitable layout. 336 int layoutCount = ::GetKeyboardLayoutList(0, NULL) + 1; // +1 for user's preferred HKL 337 HKL FAR * hKLList = (HKL FAR *)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(HKL), layoutCount); 338 DASSERT(!safe_ExceptionOccurred(env)); 339 ::GetKeyboardLayoutList(layoutCount - 1, &(hKLList[1])); 340 hKLList[0] = getDefaultKeyboardLayout(); // put user's preferred layout on top of the list 341 342 // lookup matching LangID 343 jboolean retValue = JNI_FALSE; 344 for (int i = 0; i < layoutCount; i++) { 345 const char * supported = getJavaIDFromLangID(LOWORD(hKLList[i])); 346 if ((supported != NULL) && (strcmp(supported, requested) == 0)) { 347 // use special message to call ActivateKeyboardLayout() in main thread. 348 if (AwtToolkit::GetInstance().SendMessage(WM_AWT_ACTIVATEKEYBOARDLAYOUT, (WPARAM)onActivate, (LPARAM)hKLList[i])) { 349 //also need to change the same keyboard layout for the Java AWT-EventQueue thread 350 AwtToolkit::activateKeyboardLayout(hKLList[i]); 351 retValue = JNI_TRUE; 352 } 353 break; 354 } 355 } 356 357 env->ReleaseStringUTFChars(localeString, requested); 358 free(hKLList); 359 return retValue; 360 361 CATCH_BAD_ALLOC_RET(JNI_FALSE); 362 } 363 364 /* 365 * Class: sun_awt_windows_WInputMethod 366 * Method: hideWindowsNative 367 * Signature: (Lsun/awt/windows/WComponentPeer;Z)V 368 */ 369 JNIEXPORT void JNICALL Java_sun_awt_windows_WInputMethod_setStatusWindowVisible 370 (JNIEnv *env, jobject self, jobject peer, jboolean visible) 371 { 372 /* Retrieve the default input method Window handler from AwtToolkit. 373 Windows system creates a default input method window for the 374 toolkit thread. 375 */ 376 HWND hwndIME = AwtToolkit::GetInstance().GetInputMethodWindow(); 377 if (hwndIME == NULL) { 378 if (peer == NULL) { 379 return; 380 } 381 382 AwtComponent* p = (AwtComponent*)JNI_GET_PDATA(peer); 383 if (p == NULL || (hwndIME = ImmGetDefaultIMEWnd(p->GetHWnd())) == NULL) { 384 return; 385 } 386 387 AwtToolkit::GetInstance().SetInputMethodWindow(hwndIME); 388 } 389 390 ::SendMessage(hwndIME, WM_IME_CONTROL, 391 visible ? IMC_OPENSTATUSWINDOW : IMC_CLOSESTATUSWINDOW, 0); 392 } 393 394 /* 395 * Class: sun_awt_windows_WInputMethod 396 * Method: openCandidateWindow 397 * Signature: (Lsun/awt/windows/WComponentPeer;II)V 398 */ 399 JNIEXPORT void JNICALL Java_sun_awt_windows_WInputMethod_openCandidateWindow 400 (JNIEnv *env, jobject self, jobject peer, jint x, jint y) 401 { 402 TRY; 403 404 PDATA pData; 405 JNI_CHECK_PEER_RETURN(peer); 406 407 jobject peerGlobalRef = env->NewGlobalRef(peer); 408 409 // WARNING! MAKELONG macro treats the given values as unsigned. 410 // This may lead to some bugs in multiscreen configurations, as 411 // coordinates can be negative numbers. So, while handling 412 // WM_AWT_OPENCANDIDATEWINDOW message in AwtToolkit, we should 413 // carefully extract right x and y values using GET_X_LPARAM and 414 // GET_Y_LPARAM, not LOWORD and HIWORD 415 // See CR 4805862, AwtToolkit::WndProc 416 417 // use special message to open candidate window in main thread. 418 AwtToolkit::GetInstance().SendMessage(WM_AWT_OPENCANDIDATEWINDOW, 419 (WPARAM)peerGlobalRef, MAKELONG(x, y)); 420 421 CATCH_BAD_ALLOC; 422 } 423 424 425 /************************************************************************ 426 * WInputMethodDescriptor native methods 427 */ 428 429 /* 430 * Class: sun_awt_windows_WInputMethodDescriptor 431 * Method: getNativeAvailableLocales 432 * Signature: ()[Ljava/util/Locale; 433 */ 434 JNIEXPORT jobjectArray JNICALL Java_sun_awt_windows_WInputMethodDescriptor_getNativeAvailableLocales 435 (JNIEnv *env, jclass self) 436 { 437 TRY; 438 439 // get list of available HKLs 440 int layoutCount = ::GetKeyboardLayoutList(0, NULL); 441 HKL FAR * hKLList = (HKL FAR *)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(HKL), layoutCount); 442 DASSERT(!safe_ExceptionOccurred(env)); 443 ::GetKeyboardLayoutList(layoutCount, hKLList); 444 445 // get list of Java locale names while getting rid of duplicates 446 int srcIndex = 0; 447 int destIndex = 0; 448 int javaLocaleNameCount = 0; 449 int current = 0; 450 const char ** javaLocaleNames = (const char **)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(char *), layoutCount); 451 DASSERT(!safe_ExceptionOccurred(env)); 452 for (; srcIndex < layoutCount; srcIndex++) { 453 const char * srcLocaleName = getJavaIDFromLangID(LOWORD(hKLList[srcIndex])); 454 455 if (srcLocaleName == NULL) { 456 // could not find corresponding Java locale name for this HKL. 457 continue; 458 } 459 460 for (current = 0; current < destIndex; current++) { 461 if (strcmp(javaLocaleNames[current], srcLocaleName) == 0) { 462 // duplicated. ignore this HKL 463 break; 464 } 465 } 466 467 if (current == destIndex) { 468 javaLocaleNameCount++; 469 destIndex++; 470 javaLocaleNames[current] = srcLocaleName; 471 } 472 } 473 474 // convert it to an array of Java locale objects 475 jclass localeClass = env->FindClass("java/util/Locale"); 476 jobjectArray locales = env->NewObjectArray(javaLocaleNameCount, localeClass, NULL); 477 for (current = 0; current < javaLocaleNameCount; current++) { 478 env->SetObjectArrayElement(locales, 479 current, 480 CreateLocaleObject(env, javaLocaleNames[current])); 481 } 482 DASSERT(!safe_ExceptionOccurred(env)); 483 484 env->DeleteLocalRef(localeClass); 485 free(hKLList); 486 free(javaLocaleNames); 487 return locales; 488 489 CATCH_BAD_ALLOC_RET(NULL); 490 } 491 492 /** 493 * Class: sun_awt_windows_WInputMethod 494 * Method: getNativeIMMDescription 495 * Signature: ()Ljava/lang/String; 496 * 497 * This method tries to get the information about the input method associated with 498 * the current active thread. 499 * 500 */ 501 JNIEXPORT jstring JNICALL Java_sun_awt_windows_WInputMethod_getNativeIMMDescription 502 (JNIEnv *env, jobject self) { 503 504 TRY; 505 506 // Get the keyboard layout of the active thread. 507 HKL hkl = AwtComponent::GetKeyboardLayout(); 508 LPTSTR szImmDescription = NULL; 509 UINT buffSize = 0; 510 jstring infojStr = NULL; 511 512 if ((buffSize = ::ImmGetDescription(hkl, szImmDescription, 0)) > 0) { 513 szImmDescription = (LPTSTR) SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, (buffSize+1), sizeof(TCHAR)); 514 515 if (szImmDescription != NULL) { 516 ImmGetDescription(hkl, szImmDescription, buffSize); 517 518 infojStr = JNU_NewStringPlatform(env, szImmDescription); 519 520 free(szImmDescription); 521 } 522 } 523 524 return infojStr; 525 526 CATCH_BAD_ALLOC_RET(NULL); 527 } 528 529 /* 530 * Create a Java locale object from its name string 531 */ 532 jobject CreateLocaleObject(JNIEnv *env, const char * name) 533 { 534 TRY; 535 536 // get language, country, variant information 537 char * language = (char *)safe_Malloc(strlen(name) + 1); 538 char * country; 539 char * variant; 540 DASSERT(!safe_ExceptionOccurred(env)); 541 strcpy(language, name); 542 for (country = language; *country != '_' && *country != '\0'; country++); 543 if (*country == '_') { 544 *country++ = '\0'; 545 for (variant = country; *variant != '_' && *variant != '\0'; variant++); 546 if (*variant == '_') { 547 *variant++ = '\0'; 548 } 549 } else { 550 variant = country; 551 } 552 553 // create Locale object 554 jobject langObj = env->NewStringUTF(language); 555 jobject ctryObj = env->NewStringUTF(country); 556 jobject vrntObj = env->NewStringUTF(variant); 557 jobject localeObj = JNU_NewObjectByName(env, "java/util/Locale", 558 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", 559 langObj, ctryObj, vrntObj); 560 free(language); 561 env->DeleteLocalRef(langObj); 562 env->DeleteLocalRef(ctryObj); 563 env->DeleteLocalRef(vrntObj); 564 565 return localeObj; 566 567 CATCH_BAD_ALLOC_RET(NULL); 568 } 569 570 571 /* 572 * Gets user's preferred keyboard layout 573 * Warning: This is version dependent code 574 */ 575 HKL getDefaultKeyboardLayout() { 576 LONG ret; 577 HKL hkl = 0; 578 HKEY hKey; 579 BYTE szHKL[16]; 580 DWORD cbHKL = 16; 581 LPTSTR end; 582 583 if (IS_NT) { 584 ret = ::RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Keyboard Layout\\Preload"), NULL, KEY_READ, &hKey); 585 } else { 586 ret = ::RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("keyboard layout\\preload\\1"), NULL, KEY_READ, &hKey); 587 } 588 589 if (ret == ERROR_SUCCESS) { 590 if (IS_NT) { 591 ret = ::RegQueryValueEx(hKey, TEXT("1"), 0, 0, szHKL, &cbHKL); 592 } else { 593 ret = ::RegQueryValueEx(hKey, NULL, 0, 0, szHKL, &cbHKL); 594 } 595 596 if (ret == ERROR_SUCCESS) { 597 hkl = reinterpret_cast<HKL>(static_cast<INT_PTR>( 598 _tcstoul((LPCTSTR)szHKL, &end, 16))); 599 } 600 601 ::RegCloseKey(hKey); 602 } 603 604 return hkl; 605 } 606 } /* extern "C" */