1 /* 2 * Copyright (c) 2011, 2014, 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 #import <Cocoa/Cocoa.h> 27 #include <objc/objc-runtime.h> 28 29 #import "sun_lwawt_macosx_CInputMethod.h" 30 #import "sun_lwawt_macosx_CInputMethodDescriptor.h" 31 #import "ThreadUtilities.h" 32 #import "AWTView.h" 33 #import "JNIUtilities.h" 34 35 #import <JavaRuntimeSupport/JavaRuntimeSupport.h> 36 37 #define JAVA_LIST @"JAVA_LIST" 38 #define CURRENT_KB_DESCRIPTION @"CURRENT_KB_DESCRIPTION" 39 40 // 41 // NOTE: This returns a JNI Local Ref. Any code that calls must call DeleteLocalRef with the return value. 42 // 43 static jobject CreateLocaleObjectFromNSString(JNIEnv *env, NSString *name) 44 { 45 DECLARE_CLASS_RETURN(jc_localeClass, "java/util/Locale", NULL); 46 DECLARE_METHOD_RETURN(jm_localeCons, jc_localeClass, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", NULL); 47 // Break apart the string into its components. 48 // First, duplicate the NSString into a C string, since we're going to modify it. 49 char * language = strdup([name UTF8String]); 50 char * country; 51 char * variant; 52 53 // Convert _ to NULL -- this gives us three null terminated strings in place. 54 for (country = language; *country != '_' && *country != '\0'; country++); 55 if (*country == '_') { 56 *country++ = '\0'; 57 for (variant = country; *variant != '_' && *variant != '\0'; variant++); 58 if (*variant == '_') { 59 *variant++ = '\0'; 60 } 61 } else { 62 variant = country; 63 } 64 65 // Create the java.util.Locale object 66 jobject localeObj = NULL; 67 jobject langObj = (*env)->NewStringUTF(env, language); 68 if (langObj != NULL) { 69 jobject ctryObj = (*env)->NewStringUTF(env, country); 70 if(ctryObj != NULL) { 71 jobject vrntObj = (*env)->NewStringUTF(env, variant); 72 if (vrntObj != NULL) { 73 localeObj = (*env)->NewObject(env, jc_localeClass, jm_localeCons,langObj, ctryObj, 74 vrntObj); 75 CHECK_EXCEPTION(); 76 (*env)->DeleteLocalRef(env, vrntObj); 77 } 78 (*env)->DeleteLocalRef(env, ctryObj); 79 } 80 (*env)->DeleteLocalRef(env, langObj); 81 } 82 // Clean up and return. 83 free(language); 84 return localeObj; 85 } 86 87 static id inputMethodController = nil; 88 89 static void initializeInputMethodController() { 90 static BOOL checkedJRSInputMethodController = NO; 91 if (!checkedJRSInputMethodController && (inputMethodController == nil)) { 92 id jrsInputMethodController = objc_lookUpClass("JRSInputMethodController"); 93 if (jrsInputMethodController != nil) { 94 inputMethodController = [jrsInputMethodController performSelector:@selector(controller)]; 95 } 96 checkedJRSInputMethodController = YES; 97 } 98 } 99 100 101 @interface CInputMethod : NSObject {} 102 @end 103 104 @implementation CInputMethod 105 106 + (void) setKeyboardLayout:(NSString *)theLocale 107 { 108 AWT_ASSERT_APPKIT_THREAD; 109 if (!inputMethodController) return; 110 111 [inputMethodController performSelector:@selector(setCurrentInputMethodForLocale) withObject:theLocale]; 112 } 113 114 + (void) _nativeNotifyPeerWithView:(AWTView *)view inputMethod:(jobject) inputMethod { 115 AWT_ASSERT_APPKIT_THREAD; 116 117 if (!view) return; 118 if (!inputMethod) return; 119 120 [view setInputMethod:inputMethod]; // inputMethod is a GlobalRef 121 } 122 123 + (void) _nativeEndComposition:(AWTView *)view { 124 if (view == nil) return; 125 126 [view abandonInput]; 127 } 128 129 130 @end 131 132 /* 133 * Class: sun_lwawt_macosx_CInputMethod 134 * Method: nativeInit 135 * Signature: (); 136 */ 137 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeInit 138 (JNIEnv *env, jclass klass) 139 { 140 initializeInputMethodController(); 141 } 142 143 /* 144 * Class: sun_lwawt_macosx_CInputMethod 145 * Method: nativeGetCurrentInputMethodInfo 146 * Signature: ()Ljava/lang/String; 147 */ 148 JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeGetCurrentInputMethodInfo 149 (JNIEnv *env, jclass klass) 150 { 151 if (!inputMethodController) return NULL; 152 jobject returnValue = 0; 153 __block NSString *keyboardInfo = NULL; 154 JNI_COCOA_ENTER(env); 155 156 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 157 keyboardInfo = [inputMethodController performSelector:@selector(currentInputMethodName)]; 158 [keyboardInfo retain]; 159 }]; 160 161 if (keyboardInfo == nil) return NULL; 162 returnValue = NSStringToJavaString(env, keyboardInfo); 163 [keyboardInfo release]; 164 165 JNI_COCOA_EXIT(env); 166 return returnValue; 167 } 168 169 /* 170 * Class: sun_lwawt_macosx_CInputMethod 171 * Method: nativeActivate 172 * Signature: (JLsun/lwawt/macosx/CInputMethod;)V 173 */ 174 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeNotifyPeer 175 (JNIEnv *env, jobject this, jlong nativePeer, jobject inputMethod) 176 { 177 JNI_COCOA_ENTER(env); 178 AWTView *view = (AWTView *)jlong_to_ptr(nativePeer); 179 jobject inputMethodRef = (*env)->NewGlobalRef(env, inputMethod); 180 [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ 181 [CInputMethod _nativeNotifyPeerWithView:view inputMethod:inputMethodRef]; 182 }]; 183 184 JNI_COCOA_EXIT(env); 185 186 } 187 188 /* 189 * Class: sun_lwawt_macosx_CInputMethod 190 * Method: nativeEndComposition 191 * Signature: (J)V 192 */ 193 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeEndComposition 194 (JNIEnv *env, jobject this, jlong nativePeer) 195 { 196 JNI_COCOA_ENTER(env); 197 AWTView *view = (AWTView *)jlong_to_ptr(nativePeer); 198 199 [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ 200 [CInputMethod _nativeEndComposition:view]; 201 }]; 202 203 JNI_COCOA_EXIT(env); 204 } 205 206 /* 207 * Class: sun_lwawt_macosx_CInputMethod 208 * Method: getNativeLocale 209 * Signature: ()Ljava/util/Locale; 210 */ 211 JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CInputMethod_getNativeLocale 212 (JNIEnv *env, jobject this) 213 { 214 if (!inputMethodController) return NULL; 215 jobject returnValue = 0; 216 __block NSString *isoAbbreviation; 217 JNI_COCOA_ENTER(env); 218 219 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 220 isoAbbreviation = (NSString *) [inputMethodController performSelector:@selector(currentInputMethodLocale)]; 221 [isoAbbreviation retain]; 222 }]; 223 224 if (isoAbbreviation == nil) return NULL; 225 226 static NSString *sLastKeyboardStr = nil; 227 static jobject sLastKeyboardLocaleObj = NULL; 228 229 if (![isoAbbreviation isEqualTo:sLastKeyboardStr]) { 230 [sLastKeyboardStr release]; 231 sLastKeyboardStr = [isoAbbreviation retain]; 232 jobject localObj = CreateLocaleObjectFromNSString(env, isoAbbreviation); 233 [isoAbbreviation release]; 234 235 if (sLastKeyboardLocaleObj) { 236 (*env)->DeleteGlobalRef(env, sLastKeyboardLocaleObj); 237 sLastKeyboardLocaleObj = NULL; 238 } 239 if (localObj != NULL) { 240 sLastKeyboardLocaleObj = (*env)->NewGlobalRef(env, localObj); 241 (*env)->DeleteLocalRef(env, localObj); 242 } 243 } 244 245 returnValue = sLastKeyboardLocaleObj; 246 247 JNI_COCOA_EXIT(env); 248 return returnValue; 249 } 250 251 252 /* 253 * Class: sun_lwawt_macosx_CInputMethod 254 * Method: setNativeLocale 255 * Signature: (Ljava/lang/String;Z)Z 256 */ 257 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CInputMethod_setNativeLocale 258 (JNIEnv *env, jobject this, jstring locale, jboolean isActivating) 259 { 260 JNI_COCOA_ENTER(env); 261 NSString *localeStr = JavaStringToNSString(env, locale); 262 [localeStr retain]; 263 264 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 265 [CInputMethod setKeyboardLayout:localeStr]; 266 }]; 267 268 [localeStr release]; 269 JNI_COCOA_EXIT(env); 270 return JNI_TRUE; 271 } 272 273 /* 274 * Class: sun_lwawt_macosx_CInputMethodDescriptor 275 * Method: nativeInit 276 * Signature: (); 277 */ 278 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethodDescriptor_nativeInit 279 (JNIEnv *env, jclass klass) 280 { 281 initializeInputMethodController(); 282 } 283 284 /* 285 * Class: sun_lwawt_macosx_CInputMethodDescriptor 286 * Method: nativeGetAvailableLocales 287 * Signature: ()[Ljava/util/Locale; 288 */ 289 JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CInputMethodDescriptor_nativeGetAvailableLocales 290 (JNIEnv *env, jclass klass) 291 { 292 DECLARE_CLASS_RETURN(jc_arrayListClass, "java/util/ArrayList", NULL); 293 DECLARE_METHOD_RETURN(jm_arrayListCons, jc_arrayListClass, "<init>", "()V", NULL); 294 DECLARE_METHOD_RETURN(jm_listAdd, jc_arrayListClass, "add", "(Ljava/lang/Object;)Z", NULL); 295 DECLARE_METHOD_RETURN(jm_listContains, jc_arrayListClass, "contains", "(Ljava/lang/Object;)Z", NULL); 296 297 if (!inputMethodController) return NULL; 298 jobject returnValue = 0; 299 300 __block NSArray *selectableArray = nil; 301 JNI_COCOA_ENTER(env); 302 303 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 304 selectableArray = (NSArray *)[inputMethodController performSelector:@selector(availableInputMethodLocales)]; 305 [selectableArray retain]; 306 }]; 307 308 if (selectableArray == nil) return NULL; 309 310 // Create an ArrayList to return back to the caller. 311 returnValue = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons); 312 CHECK_EXCEPTION_NULL_RETURN(returnValue, NULL); 313 314 for(NSString *locale in selectableArray) { 315 jobject localeObj = CreateLocaleObjectFromNSString(env, locale); 316 if (localeObj == NULL) { 317 break; 318 } 319 320 if ((*env)->CallBooleanMethod(env, returnValue, jm_listContains, localeObj) == JNI_FALSE) { 321 if ((*env)->ExceptionOccurred(env)) (*env)->ExceptionClear(env); 322 (*env)->CallBooleanMethod(env, returnValue, jm_listAdd, localeObj); 323 } 324 if ((*env)->ExceptionOccurred(env)) (*env)->ExceptionClear(env); 325 326 (*env)->DeleteLocalRef(env, localeObj); 327 } 328 [selectableArray release]; 329 JNI_COCOA_EXIT(env); 330 return returnValue; 331 } 332