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 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 localeObj = NULL; 74 jobject langObj = (*env)->NewStringUTF(env, language); 75 if (langObj != NULL) { 76 jobject ctryObj = (*env)->NewStringUTF(env, country); 77 if(ctryObj != NULL) { 78 jobject vrntObj = (*env)->NewStringUTF(env, variant); 79 if (vrntObj != NULL) { 80 localeObj = JNFNewObject(env, jm_localeCons,langObj, ctryObj, 81 vrntObj); 82 (*env)->DeleteLocalRef(env, vrntObj); 83 } 84 (*env)->DeleteLocalRef(env, ctryObj); 85 } 86 (*env)->DeleteLocalRef(env, langObj); 87 } 88 // Clean up and return. 89 free(language); 90 return localeObj; 91 } 92 93 static id inputMethodController = nil; 94 95 static void initializeInputMethodController() { 96 static BOOL checkedJRSInputMethodController = NO; 97 if (!checkedJRSInputMethodController && (inputMethodController == nil)) { 98 id jrsInputMethodController = objc_lookUpClass("JRSInputMethodController"); 99 if (jrsInputMethodController != nil) { 100 inputMethodController = [jrsInputMethodController performSelector:@selector(controller)]; 101 } 102 checkedJRSInputMethodController = YES; 103 } 104 } 105 106 107 @interface CInputMethod : NSObject {} 108 @end 109 110 @implementation CInputMethod 111 112 + (void) setKeyboardLayout:(NSString *)theLocale 113 { 114 AWT_ASSERT_APPKIT_THREAD; 115 if (!inputMethodController) return; 116 117 [inputMethodController performSelector:@selector(setCurrentInputMethodForLocale) withObject:theLocale]; 118 } 119 120 + (void) _nativeNotifyPeerWithView:(AWTView *)view inputMethod:(JNFJObjectWrapper *) inputMethod { 121 AWT_ASSERT_APPKIT_THREAD; 122 123 if (!view) return; 124 if (!inputMethod) return; 125 126 [view setInputMethod:[inputMethod jObject]]; 127 } 128 129 + (void) _nativeEndComposition:(AWTView *)view { 130 if (view == nil) return; 131 132 [view abandonInput]; 133 } 134 135 136 @end 137 138 /* 139 * Class: sun_lwawt_macosx_CInputMethod 140 * Method: nativeInit 141 * Signature: (); 142 */ 143 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeInit 144 (JNIEnv *env, jclass klass) 145 { 146 initializeInputMethodController(); 147 } 148 149 /* 150 * Class: sun_lwawt_macosx_CInputMethod 151 * Method: nativeGetCurrentInputMethodInfo 152 * Signature: ()Ljava/lang/String; 153 */ 154 JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeGetCurrentInputMethodInfo 155 (JNIEnv *env, jclass klass) 156 { 157 if (!inputMethodController) return NULL; 158 jobject returnValue = 0; 159 __block NSString *keyboardInfo = NULL; 160 JNF_COCOA_ENTER(env); 161 162 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 163 keyboardInfo = [inputMethodController performSelector:@selector(currentInputMethodName)]; 164 [keyboardInfo retain]; 165 }]; 166 167 if (keyboardInfo == nil) return NULL; 168 returnValue = JNFNSToJavaString(env, keyboardInfo); 169 [keyboardInfo release]; 170 171 JNF_COCOA_EXIT(env); 172 return returnValue; 173 } 174 175 /* 176 * Class: sun_lwawt_macosx_CInputMethod 177 * Method: nativeActivate 178 * Signature: (JLsun/lwawt/macosx/CInputMethod;)V 179 */ 180 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeNotifyPeer 181 (JNIEnv *env, jobject this, jlong nativePeer, jobject inputMethod) 182 { 183 JNF_COCOA_ENTER(env); 184 AWTView *view = (AWTView *)jlong_to_ptr(nativePeer); 185 JNFJObjectWrapper *inputMethodWrapper = [[JNFJObjectWrapper alloc] initWithJObject:inputMethod withEnv:env]; 186 [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ 187 [CInputMethod _nativeNotifyPeerWithView:view inputMethod:inputMethodWrapper]; 188 }]; 189 190 JNF_COCOA_EXIT(env); 191 192 } 193 194 /* 195 * Class: sun_lwawt_macosx_CInputMethod 196 * Method: nativeEndComposition 197 * Signature: (J)V 198 */ 199 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeEndComposition 200 (JNIEnv *env, jobject this, jlong nativePeer) 201 { 202 JNF_COCOA_ENTER(env); 203 AWTView *view = (AWTView *)jlong_to_ptr(nativePeer); 204 205 [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ 206 [CInputMethod _nativeEndComposition:view]; 207 }]; 208 209 JNF_COCOA_EXIT(env); 210 } 211 212 /* 213 * Class: sun_lwawt_macosx_CInputMethod 214 * Method: getNativeLocale 215 * Signature: ()Ljava/util/Locale; 216 */ 217 JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CInputMethod_getNativeLocale 218 (JNIEnv *env, jobject this) 219 { 220 if (!inputMethodController) return NULL; 221 jobject returnValue = 0; 222 __block NSString *isoAbbreviation; 223 JNF_COCOA_ENTER(env); 224 225 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 226 isoAbbreviation = (NSString *) [inputMethodController performSelector:@selector(currentInputMethodLocale)]; 227 [isoAbbreviation retain]; 228 }]; 229 230 if (isoAbbreviation == nil) return NULL; 231 232 static NSString *sLastKeyboardStr = nil; 233 static jobject sLastKeyboardLocaleObj = NULL; 234 235 if (![isoAbbreviation isEqualTo:sLastKeyboardStr]) { 236 [sLastKeyboardStr release]; 237 sLastKeyboardStr = [isoAbbreviation retain]; 238 jobject localObj = CreateLocaleObjectFromNSString(env, isoAbbreviation); 239 [isoAbbreviation release]; 240 241 if (sLastKeyboardLocaleObj) { 242 JNFDeleteGlobalRef(env, sLastKeyboardLocaleObj); 243 sLastKeyboardLocaleObj = NULL; 244 } 245 if (localObj != NULL) { 246 sLastKeyboardLocaleObj = JNFNewGlobalRef(env, localObj); 247 (*env)->DeleteLocalRef(env, localObj); 248 } 249 } 250 251 returnValue = sLastKeyboardLocaleObj; 252 253 JNF_COCOA_EXIT(env); 254 return returnValue; 255 } 256 257 258 /* 259 * Class: sun_lwawt_macosx_CInputMethod 260 * Method: setNativeLocale 261 * Signature: (Ljava/lang/String;Z)Z 262 */ 263 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CInputMethod_setNativeLocale 264 (JNIEnv *env, jobject this, jstring locale, jboolean isActivating) 265 { 266 JNF_COCOA_ENTER(env); 267 NSString *localeStr = JNFJavaToNSString(env, locale); 268 [localeStr retain]; 269 270 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 271 [CInputMethod setKeyboardLayout:localeStr]; 272 }]; 273 274 [localeStr release]; 275 JNF_COCOA_EXIT(env); 276 return JNI_TRUE; 277 } 278 279 /* 280 * Class: sun_lwawt_macosx_CInputMethodDescriptor 281 * Method: nativeInit 282 * Signature: (); 283 */ 284 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethodDescriptor_nativeInit 285 (JNIEnv *env, jclass klass) 286 { 287 initializeInputMethodController(); 288 } 289 290 /* 291 * Class: sun_lwawt_macosx_CInputMethodDescriptor 292 * Method: nativeGetAvailableLocales 293 * Signature: ()[Ljava/util/Locale; 294 */ 295 JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CInputMethodDescriptor_nativeGetAvailableLocales 296 (JNIEnv *env, jclass klass) 297 { 298 if (!inputMethodController) return NULL; 299 jobject returnValue = 0; 300 301 __block NSArray *selectableArray = nil; 302 JNF_COCOA_ENTER(env); 303 304 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 305 selectableArray = (NSArray *)[inputMethodController performSelector:@selector(availableInputMethodLocales)]; 306 [selectableArray retain]; 307 }]; 308 309 if (selectableArray == nil) return NULL; 310 311 // Create an ArrayList to return back to the caller. 312 returnValue = JNFNewObject(env, jm_arrayListCons); 313 314 for(NSString *locale in selectableArray) { 315 jobject localeObj = CreateLocaleObjectFromNSString(env, locale); 316 if (localeObj == NULL) { 317 break; 318 } 319 320 if (JNFCallBooleanMethod(env, returnValue, jm_listContains, localeObj) == JNI_FALSE) { 321 JNFCallBooleanMethod(env, returnValue, jm_listAdd, localeObj); 322 } 323 324 (*env)->DeleteLocalRef(env, localeObj); 325 } 326 [selectableArray release]; 327 JNF_COCOA_EXIT(env); 328 return returnValue; 329 } 330