1 /* 2 * Copyright (c) 2011, 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 34 #import <JavaNativeFoundation/JavaNativeFoundation.h> 35 #import <JavaRuntimeSupport/JavaRuntimeSupport.h> 36 37 #define JAVA_LIST @"JAVA_LIST" 38 #define CURRENT_KB_DESCRIPTION @"CURRENT_KB_DESCRIPTION" 39 40 static JNF_CLASS_CACHE(jc_localeClass, "java/util/Locale"); 41 static JNF_CTOR_CACHE(jm_localeCons, jc_localeClass, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); 42 static JNF_CLASS_CACHE(jc_arrayListClass, "java/util/ArrayList"); 43 static JNF_CTOR_CACHE(jm_arrayListCons, jc_arrayListClass, "()V"); 44 static JNF_MEMBER_CACHE(jm_listAdd, jc_arrayListClass, "add", "(Ljava/lang/Object;)Z"); 45 static JNF_MEMBER_CACHE(jm_listContains, jc_arrayListClass, "contains", "(Ljava/lang/Object;)Z"); 46 47 48 49 // 50 // NOTE: This returns a JNI Local Ref. Any code that calls must call DeleteLocalRef with the return value. 51 // 52 static jobject CreateLocaleObjectFromNSString(JNIEnv *env, NSString *name) 53 { 54 // Break apart the string into its components. 55 // First, duplicate the NSString into a C string, since we're going to modify it. 56 char * language = strdup([name UTF8String]); 57 char * country; 58 char * variant; 59 60 // Convert _ to NULL -- this gives us three null terminated strings in place. 61 for (country = language; *country != '_' && *country != '\0'; country++); 62 if (*country == '_') { 63 *country++ = '\0'; 64 for (variant = country; *variant != '_' && *variant != '\0'; variant++); 65 if (*variant == '_') { 66 *variant++ = '\0'; 67 } 68 } else { 69 variant = country; 70 } 71 72 // Create the java.util.Locale object 73 jobject langObj = (*env)->NewStringUTF(env, language); 74 jobject ctryObj = (*env)->NewStringUTF(env, country); 75 jobject vrntObj = (*env)->NewStringUTF(env, variant); 76 jobject localeObj = JNFNewObject(env, jm_localeCons, langObj, ctryObj, vrntObj); // AWT_THREADING Safe (known object) 77 78 // Clean up and return. 79 free(language); 80 (*env)->DeleteLocalRef(env, langObj); 81 (*env)->DeleteLocalRef(env, ctryObj); 82 (*env)->DeleteLocalRef(env, vrntObj); 83 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:(JNFJObjectWrapper *) inputMethod { 115 AWT_ASSERT_APPKIT_THREAD; 116 117 if (!view) return; 118 if (!inputMethod) return; 119 120 [view setInputMethod:[inputMethod jObject]]; 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 JNF_COCOA_ENTER(env); 155 156 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 157 keyboardInfo = [inputMethodController performSelector:@selector(currentInputMethodName)]; 158 [keyboardInfo retain]; 159 }]; 160 161 if (keyboardInfo == nil) return NULL; 162 returnValue = JNFNSToJavaString(env, keyboardInfo); 163 [keyboardInfo release]; 164 165 JNF_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 JNF_COCOA_ENTER(env); 178 AWTView *view = (AWTView *)jlong_to_ptr(nativePeer); 179 JNFJObjectWrapper *inputMethodWrapper = [[JNFJObjectWrapper alloc] initWithJObject:inputMethod withEnv:env]; 180 [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ 181 [CInputMethod _nativeNotifyPeerWithView:view inputMethod:inputMethodWrapper]; 182 }]; 183 184 JNF_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 JNF_COCOA_ENTER(env); 197 AWTView *view = (AWTView *)jlong_to_ptr(nativePeer); 198 199 [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ 200 [CInputMethod _nativeEndComposition:view]; 201 }]; 202 203 JNF_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 JNF_COCOA_ENTER(env); 218 219 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 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 JNFDeleteGlobalRef(env, sLastKeyboardLocaleObj); 237 } 238 239 sLastKeyboardLocaleObj = JNFNewGlobalRef(env, localObj); 240 (*env)->DeleteLocalRef(env, localObj); 241 } 242 243 returnValue = sLastKeyboardLocaleObj; 244 245 JNF_COCOA_EXIT(env); 246 return returnValue; 247 } 248 249 250 /* 251 * Class: sun_lwawt_macosx_CInputMethod 252 * Method: setNativeLocale 253 * Signature: (Ljava/lang/String;Z)Z 254 */ 255 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CInputMethod_setNativeLocale 256 (JNIEnv *env, jobject this, jstring locale, jboolean isActivating) 257 { 258 JNF_COCOA_ENTER(env); 259 NSString *localeStr = JNFJavaToNSString(env, locale); 260 [localeStr retain]; 261 262 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 263 [CInputMethod setKeyboardLayout:localeStr]; 264 }]; 265 266 [localeStr release]; 267 JNF_COCOA_EXIT(env); 268 return JNI_TRUE; 269 } 270 271 /* 272 * Class: sun_lwawt_macosx_CInputMethodDescriptor 273 * Method: nativeInit 274 * Signature: (); 275 */ 276 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethodDescriptor_nativeInit 277 (JNIEnv *env, jclass klass) 278 { 279 initializeInputMethodController(); 280 } 281 282 /* 283 * Class: sun_lwawt_macosx_CInputMethodDescriptor 284 * Method: nativeGetAvailableLocales 285 * Signature: ()[Ljava/util/Locale; 286 */ 287 JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CInputMethodDescriptor_nativeGetAvailableLocales 288 (JNIEnv *env, jclass klass) 289 { 290 if (!inputMethodController) return NULL; 291 jobject returnValue = 0; 292 293 __block NSArray *selectableArray = nil; 294 JNF_COCOA_ENTER(env); 295 296 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 297 selectableArray = (NSArray *)[inputMethodController performSelector:@selector(availableInputMethodLocales)]; 298 [selectableArray retain]; 299 }]; 300 301 if (selectableArray == nil) return NULL; 302 303 // Create an ArrayList to return back to the caller. 304 returnValue = JNFNewObject(env, jm_arrayListCons); 305 306 for(NSString *locale in selectableArray) { 307 jobject localeObj = CreateLocaleObjectFromNSString(env, locale); 308 309 if (JNFCallBooleanMethod(env, returnValue, jm_listContains, localeObj) == JNI_FALSE) { // AWT_THREADING Safe (known object) 310 JNFCallBooleanMethod(env, returnValue, jm_listAdd, localeObj); // AWT_THREADING Safe (known object) 311 } 312 313 (*env)->DeleteLocalRef(env, localeObj); 314 } 315 [selectableArray release]; 316 JNF_COCOA_EXIT(env); 317 return returnValue; 318 } 319