1 /* 2 * Copyright (c) 2011, 2016, 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 // External Java Accessibility links: 27 // 28 // <https://docs.oracle.com/javase/8/docs/technotes/guides/access/index.html> 29 // <http://www-106.ibm.com/developerworks/library/j-access/?n-j-10172> 30 // <http://archives.java.sun.com/archives/java-access.html> (Sun's mailing list for Java accessibility) 31 32 #import "JavaComponentAccessibility.h" 33 34 #import "sun_lwawt_macosx_CAccessibility.h" 35 36 #import <AppKit/AppKit.h> 37 38 #import <JavaNativeFoundation/JavaNativeFoundation.h> 39 #import <JavaRuntimeSupport/JavaRuntimeSupport.h> 40 41 #import <dlfcn.h> 42 43 #import "JavaAccessibilityAction.h" 44 #import "JavaAccessibilityUtilities.h" 45 #import "JavaTextAccessibility.h" 46 #import "ThreadUtilities.h" 47 #import "AWTView.h" 48 49 50 // these constants are duplicated in CAccessibility.java 51 #define JAVA_AX_ALL_CHILDREN (-1) 52 #define JAVA_AX_SELECTED_CHILDREN (-2) 53 #define JAVA_AX_VISIBLE_CHILDREN (-3) 54 // If the value is >=0, it's an index 55 56 static JNF_STATIC_MEMBER_CACHE(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;"); 57 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleComponent, sjc_CAccessibility, "getAccessibleComponent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleComponent;"); 58 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleValue, sjc_CAccessibility, "getAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleValue;"); 59 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;"); 60 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleDescription, sjc_CAccessibility, "getAccessibleDescription", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;"); 61 static JNF_STATIC_MEMBER_CACHE(sjm_isFocusTraversable, sjc_CAccessibility, "isFocusTraversable", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z"); 62 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleIndexInParent, sjc_CAccessibility, "getAccessibleIndexInParent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)I"); 63 64 static JNF_CLASS_CACHE(sjc_CAccessible, "sun/lwawt/macosx/CAccessible"); 65 66 static JNF_MEMBER_CACHE(jf_ptr, sjc_CAccessible, "ptr", "J"); 67 static JNF_STATIC_MEMBER_CACHE(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;"); 68 69 70 static jobject sAccessibilityClass = NULL; 71 72 // sAttributeNamesForRoleCache holds the names of the attributes to which each java 73 // AccessibleRole responds (see AccessibleRole.java). 74 // This cache is queried before attempting to access a given attribute for a particular role. 75 static NSMutableDictionary *sAttributeNamesForRoleCache = nil; 76 static NSObject *sAttributeNamesLOCK = nil; 77 78 @interface TabGroupAccessibility : JavaComponentAccessibility { 79 NSInteger _numTabs; 80 } 81 82 - (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext; 83 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored; 84 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored; 85 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env; 86 87 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount; 88 - (NSArray *)accessibilityChildrenAttribute; 89 - (id) accessibilityTabsAttribute; 90 - (BOOL)accessibilityIsTabsAttributeSettable; 91 - (NSArray *)accessibilityContentsAttribute; 92 - (BOOL)accessibilityIsContentsAttributeSettable; 93 - (id) accessibilityValueAttribute; 94 95 @end 96 97 98 @interface TabGroupControlAccessibility : JavaComponentAccessibility { 99 jobject fTabGroupAxContext; 100 } 101 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole; 102 - (jobject)tabGroup; 103 - (void)getActionsWithEnv:(JNIEnv *)env; 104 105 - (id)accessibilityValueAttribute; 106 @end 107 108 109 @interface ScrollAreaAccessibility : JavaComponentAccessibility { 110 111 } 112 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env; 113 - (NSArray *)accessibilityContentsAttribute; 114 - (BOOL)accessibilityIsContentsAttributeSettable; 115 - (id)accessibilityVerticalScrollBarAttribute; 116 - (BOOL)accessibilityIsVerticalScrollBarAttributeSettable; 117 - (id)accessibilityHorizontalScrollBarAttribute; 118 - (BOOL)accessibilityIsHorizontalScrollBarAttributeSettable; 119 @end 120 121 122 @implementation JavaComponentAccessibility 123 124 - (NSString *)description 125 { 126 return [NSString stringWithFormat:@"%@(title:'%@', desc:'%@', value:'%@')", [self accessibilityRoleAttribute], 127 [self accessibilityTitleAttribute], [self accessibilityRoleDescriptionAttribute], [self accessibilityValueAttribute]]; 128 } 129 130 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole 131 { 132 self = [super init]; 133 if (self) 134 { 135 fParent = [parent retain]; 136 fView = [view retain]; 137 fJavaRole = [javaRole retain]; 138 139 fAccessible = (*env)->NewWeakGlobalRef(env, accessible); 140 141 jobject jcomponent = [(AWTView *)fView awtComponent:env]; 142 fComponent = (*env)->NewWeakGlobalRef(env, jcomponent); 143 (*env)->DeleteLocalRef(env, jcomponent); 144 145 fIndex = index; 146 147 fActions = nil; 148 fActionsLOCK = [[NSObject alloc] init]; 149 } 150 return self; 151 } 152 153 - (void)unregisterFromCocoaAXSystem 154 { 155 AWT_ASSERT_APPKIT_THREAD; 156 static dispatch_once_t initialize_unregisterUniqueId_once; 157 static void (*unregisterUniqueId)(id); 158 dispatch_once(&initialize_unregisterUniqueId_once, ^{ 159 void *jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL); 160 unregisterUniqueId = dlsym(jrsFwk, "JRSAccessibilityUnregisterUniqueIdForUIElement"); 161 }); 162 if (unregisterUniqueId) unregisterUniqueId(self); 163 } 164 165 - (void)dealloc 166 { 167 [self unregisterFromCocoaAXSystem]; 168 169 JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; 170 171 (*env)->DeleteWeakGlobalRef(env, fAccessible); 172 fAccessible = NULL; 173 174 (*env)->DeleteWeakGlobalRef(env, fComponent); 175 fComponent = NULL; 176 177 [fParent release]; 178 fParent = nil; 179 180 [fNSRole release]; 181 fNSRole = nil; 182 183 [fJavaRole release]; 184 fJavaRole = nil; 185 186 [fView release]; 187 fView = nil; 188 189 [fActions release]; 190 fActions = nil; 191 192 [fActionsLOCK release]; 193 fActionsLOCK = nil; 194 195 [super dealloc]; 196 } 197 198 - (void)postValueChanged 199 { 200 AWT_ASSERT_APPKIT_THREAD; 201 NSAccessibilityPostNotification(self, NSAccessibilityValueChangedNotification); 202 } 203 204 - (void)postSelectedTextChanged 205 { 206 AWT_ASSERT_APPKIT_THREAD; 207 NSAccessibilityPostNotification(self, NSAccessibilitySelectedTextChangedNotification); 208 } 209 210 - (void)postSelectionChanged 211 { 212 AWT_ASSERT_APPKIT_THREAD; 213 NSAccessibilityPostNotification(self, NSAccessibilitySelectedChildrenChangedNotification); 214 } 215 216 - (BOOL)isEqual:(id)anObject 217 { 218 if (![anObject isKindOfClass:[self class]]) return NO; 219 JavaComponentAccessibility *accessibility = (JavaComponentAccessibility *)anObject; 220 221 JNIEnv* env = [ThreadUtilities getJNIEnv]; 222 return (*env)->IsSameObject(env, accessibility->fAccessible, fAccessible); 223 } 224 225 - (BOOL)isAccessibleWithEnv:(JNIEnv *)env forAccessible:(jobject)accessible 226 { 227 return (*env)->IsSameObject(env, fAccessible, accessible); 228 } 229 230 + (void)initialize 231 { 232 if (sAttributeNamesForRoleCache == nil) { 233 sAttributeNamesLOCK = [[NSObject alloc] init]; 234 sAttributeNamesForRoleCache = [[NSMutableDictionary alloc] initWithCapacity:60]; 235 } 236 237 if (sRoles == nil) { 238 initializeRoles(); 239 } 240 241 if (sAccessibilityClass == NULL) { 242 JNF_STATIC_MEMBER_CACHE(jm_getAccessibility, sjc_CAccessibility, "getAccessibility", "([Ljava/lang/String;)Lsun/lwawt/macosx/CAccessibility;"); 243 244 #ifdef JAVA_AX_NO_IGNORES 245 NSArray *ignoredKeys = [NSArray array]; 246 #else 247 NSArray *ignoredKeys = [sRoles allKeysForObject:JavaAccessibilityIgnore]; 248 #endif 249 jobjectArray result = NULL; 250 jsize count = [ignoredKeys count]; 251 252 JNIEnv *env = [ThreadUtilities getJNIEnv]; 253 254 static JNF_CLASS_CACHE(jc_String, "java/lang/String"); 255 result = JNFNewObjectArray(env, &jc_String, count); 256 if (!result) { 257 NSLog(@"In %s, can't create Java array of String objects", __FUNCTION__); 258 return; 259 } 260 261 NSInteger i; 262 for (i = 0; i < count; i++) { 263 jstring jString = JNFNSToJavaString(env, [ignoredKeys objectAtIndex:i]); 264 (*env)->SetObjectArrayElement(env, result, i, jString); 265 (*env)->DeleteLocalRef(env, jString); 266 } 267 268 sAccessibilityClass = JNFCallStaticObjectMethod(env, jm_getAccessibility, result); // AWT_THREADING Safe (known object) 269 } 270 } 271 272 + (void)postFocusChanged:(id)message 273 { 274 AWT_ASSERT_APPKIT_THREAD; 275 NSAccessibilityPostNotification([NSApp accessibilityFocusedUIElement], NSAccessibilityFocusedUIElementChangedNotification); 276 } 277 278 + (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env { 279 if (JNFIsInstanceOf(env, jaccessible, &sjc_CAccessible)) { 280 return jaccessible; 281 } 282 else if (JNFIsInstanceOf(env, jaccessible, &sjc_Accessible)) { 283 return JNFCallStaticObjectMethod(env, sjm_getCAccessible, jaccessible); 284 } 285 return NULL; 286 } 287 288 + (NSArray *)childrenOfParent:(JavaComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored 289 { 290 if (parent->fAccessible == NULL) return nil; 291 jobjectArray jchildrenAndRoles = (jobjectArray)JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, parent->fAccessible, parent->fComponent, whichChildren, allowIgnored); // AWT_THREADING Safe (AWTRunLoop) 292 if (jchildrenAndRoles == NULL) return nil; 293 294 jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); 295 NSMutableArray *children = [NSMutableArray arrayWithCapacity:arrayLen/2]; //childrenAndRoles array contains two elements (child, role) for each child 296 297 NSInteger i; 298 NSUInteger childIndex = (whichChildren >= 0) ? whichChildren : 0; // if we're getting one particular child, make sure to set its index correctly 299 for(i = 0; i < arrayLen; i+=2) 300 { 301 jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); 302 jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1); 303 304 NSString *childJavaRole = nil; 305 if (jchildJavaRole != NULL) { 306 jobject jkey = JNFGetObjectField(env, jchildJavaRole, sjf_key); 307 childJavaRole = JNFJavaToNSString(env, jkey); 308 (*env)->DeleteLocalRef(env, jkey); 309 } 310 311 JavaComponentAccessibility *child = [self createWithParent:parent accessible:jchild role:childJavaRole index:childIndex withEnv:env withView:parent->fView]; 312 313 (*env)->DeleteLocalRef(env, jchild); 314 (*env)->DeleteLocalRef(env, jchildJavaRole); 315 316 [children addObject:child]; 317 childIndex++; 318 } 319 (*env)->DeleteLocalRef(env, jchildrenAndRoles); 320 321 return children; 322 } 323 324 + (JavaComponentAccessibility *)createWithAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env withView:(NSView *)view 325 { 326 jobject jcomponent = [(AWTView *)view awtComponent:env]; 327 jint index = JNFCallStaticIntMethod(env, sjm_getAccessibleIndexInParent, jaccessible, jcomponent); 328 NSString *javaRole = getJavaRole(env, jaccessible, jcomponent); 329 (*env)->DeleteLocalRef(env, jcomponent); 330 return [self createWithAccessible:jaccessible role:javaRole index:index withEnv:env withView:view]; 331 } 332 333 + (JavaComponentAccessibility *) createWithAccessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view 334 { 335 return [self createWithParent:nil accessible:jaccessible role:javaRole index:index withEnv:env withView:view]; 336 } 337 338 + (JavaComponentAccessibility *) createWithParent:(JavaComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view 339 { 340 // try to fetch the jCAX from Java, and return autoreleased 341 jobject jCAX = [JavaComponentAccessibility getCAccessible:jaccessible withEnv:env]; 342 if (jCAX == NULL) return nil; 343 JavaComponentAccessibility *value = (JavaComponentAccessibility *) jlong_to_ptr(JNFGetLongField(env, jCAX, jf_ptr)); 344 if (value != nil) { 345 (*env)->DeleteLocalRef(env, jCAX); 346 return [[value retain] autorelease]; 347 } 348 349 // otherwise, create a new instance 350 JavaComponentAccessibility *newChild = nil; 351 if ([javaRole isEqualToString:@"pagetablist"]) { 352 newChild = [TabGroupAccessibility alloc]; 353 } else if ([javaRole isEqualToString:@"scrollpane"]) { 354 newChild = [ScrollAreaAccessibility alloc]; 355 } else { 356 NSString *nsRole = [sRoles objectForKey:javaRole]; 357 if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] || [nsRole isEqualToString:NSAccessibilityTextAreaRole] || [nsRole isEqualToString:NSAccessibilityTextFieldRole]) { 358 newChild = [JavaTextAccessibility alloc]; 359 } else { 360 newChild = [JavaComponentAccessibility alloc]; 361 } 362 } 363 364 // must init freshly -alloc'd object 365 [newChild initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // must init new instance 366 367 // must hard retain pointer poked into Java object 368 [newChild retain]; 369 JNFSetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild)); 370 (*env)->DeleteLocalRef(env, jCAX); 371 372 // return autoreleased instance 373 return [newChild autorelease]; 374 } 375 376 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env 377 { 378 static JNF_STATIC_MEMBER_CACHE(jm_getInitialAttributeStates, sjc_CAccessibility, "getInitialAttributeStates", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)[Z"); 379 380 NSMutableArray *attributeNames = [NSMutableArray arrayWithCapacity:20]; 381 [attributeNames retain]; 382 383 // all elements respond to parent, role, role description, window, topLevelUIElement, help 384 [attributeNames addObject:NSAccessibilityParentAttribute]; 385 [attributeNames addObject:NSAccessibilityRoleAttribute]; 386 [attributeNames addObject:NSAccessibilityRoleDescriptionAttribute]; 387 [attributeNames addObject:NSAccessibilityHelpAttribute]; 388 389 // cmcnote: AXMenu usually doesn't respond to window / topLevelUIElement. But menus within a Java app's window 390 // probably should. Should we use some role other than AXMenu / AXMenuBar for Java menus? 391 [attributeNames addObject:NSAccessibilityWindowAttribute]; 392 [attributeNames addObject:NSAccessibilityTopLevelUIElementAttribute]; 393 394 // set accessible subrole 395 NSString *javaRole = [self javaRole]; 396 if (javaRole != nil && [javaRole isEqualToString:@"passwordtext"]) { 397 //cmcnote: should turn this into a constant 398 [attributeNames addObject:NSAccessibilitySubroleAttribute]; 399 } 400 401 // Get all the other accessibility attributes states we need in one swell foop. 402 // javaRole isn't pulled in because we need protected access to AccessibleRole.key 403 jbooleanArray attributeStates = (jbooleanArray)JNFCallStaticObjectMethod(env, jm_getInitialAttributeStates, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 404 if (attributeStates == NULL) return nil; 405 jboolean *attributeStatesArray = (*env)->GetBooleanArrayElements(env, attributeStates, 0); 406 if (attributeStatesArray == NULL) { 407 // Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck. 408 NSLog(@"%s failed calling GetBooleanArrayElements", __FUNCTION__); 409 return nil; 410 } 411 412 // if there's a component, it can be enabled and it has a size/position 413 if (attributeStatesArray[0]) { 414 [attributeNames addObject:NSAccessibilityEnabledAttribute]; 415 [attributeNames addObject:NSAccessibilitySizeAttribute]; 416 [attributeNames addObject:NSAccessibilityPositionAttribute]; 417 } 418 419 // According to javadoc, a component that is focusable will return true from isFocusTraversable, 420 // as well as having AccessibleState.FOCUSABLE in it's AccessibleStateSet. 421 // We use the former heuristic; if the component focus-traversable, add a focused attribute 422 // See also: accessibilityIsFocusedAttributeSettable 423 if (attributeStatesArray[1]) 424 { 425 [attributeNames addObject:NSAccessibilityFocusedAttribute]; 426 } 427 428 // if it's a pagetab / radiobutton, it has a value but no min/max value. 429 BOOL hasAxValue = attributeStatesArray[2]; 430 if ([javaRole isEqualToString:@"pagetab"] || [javaRole isEqualToString:@"radiobutton"]) { 431 [attributeNames addObject:NSAccessibilityValueAttribute]; 432 } else { 433 // if not a pagetab/radio button, and it has a value, it has a min/max/current value. 434 if (hasAxValue) { 435 // er, it has a min/max/current value if it's not a button. 436 // See AppKit/NSButtonCellAccessibility.m 437 if (![javaRole isEqualToString:@"pushbutton"]) { 438 //cmcnote: make this (and "passwordtext") constants instead of magic strings 439 [attributeNames addObject:NSAccessibilityMinValueAttribute]; 440 [attributeNames addObject:NSAccessibilityMaxValueAttribute]; 441 [attributeNames addObject:NSAccessibilityValueAttribute]; 442 } 443 } 444 } 445 446 // does it have an orientation? 447 if (attributeStatesArray[4]) { 448 [attributeNames addObject:NSAccessibilityOrientationAttribute]; 449 } 450 451 // name 452 if (attributeStatesArray[5]) { 453 [attributeNames addObject:NSAccessibilityTitleAttribute]; 454 } 455 456 // children 457 if (attributeStatesArray[6]) { 458 [attributeNames addObject:NSAccessibilityChildrenAttribute]; 459 if ([javaRole isEqualToString:@"list"]) { 460 [attributeNames addObject:NSAccessibilitySelectedChildrenAttribute]; 461 [attributeNames addObject:NSAccessibilityVisibleChildrenAttribute]; 462 } 463 // Just above, the below mentioned support has been added back in for lists. 464 // However, the following comments may still be useful for future fixes. 465 // [attributeNames addObject:NSAccessibilitySelectedChildrenAttribute]; 466 // [attributeNames addObject:NSAccessibilityVisibleChildrenAttribute]; 467 //According to AXRoles.txt: 468 //VisibleChildren: radio group, list, row, table row subrole 469 //SelectedChildren: list 470 } 471 472 // Cleanup 473 (*env)->ReleaseBooleanArrayElements(env, attributeStates, attributeStatesArray, JNI_ABORT); 474 475 return attributeNames; 476 } 477 478 - (NSDictionary *)getActions:(JNIEnv *)env 479 { 480 @synchronized(fActionsLOCK) { 481 if (fActions == nil) { 482 fActions = [[NSMutableDictionary alloc] initWithCapacity:3]; 483 [self getActionsWithEnv:env]; 484 } 485 } 486 487 return fActions; 488 } 489 490 - (void)getActionsWithEnv:(JNIEnv *)env 491 { 492 static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleAction, sjc_CAccessibility, "getAccessibleAction", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleAction;"); 493 494 // On MacOSX, text doesn't have actions, in java it does. 495 // cmcnote: NOT TRUE - Editable text has AXShowMenu. Textfields have AXConfirm. Static text has no actions. 496 jobject axAction = JNFCallStaticObjectMethod(env, jm_getAccessibleAction, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 497 if (axAction != NULL) { 498 //+++gdb NOTE: In MacOSX, there is just a single Action, not multiple. In java, 499 // the first one seems to be the most basic, so this will be used. 500 // cmcnote: NOT TRUE - Sometimes there are multiple actions, eg sliders have AXDecrement AND AXIncrement (radr://3893192) 501 JavaAxAction *action = [[JavaAxAction alloc] initWithEnv:env withAccessibleAction:axAction withIndex:0 withComponent:fComponent]; 502 [fActions setObject:action forKey:[self isMenu] ? NSAccessibilityPickAction : NSAccessibilityPressAction]; 503 [action release]; 504 (*env)->DeleteLocalRef(env, axAction); 505 } 506 } 507 508 - (jobject)axContextWithEnv:(JNIEnv *)env 509 { 510 return getAxContext(env, fAccessible, fComponent); 511 } 512 513 - (id)parent 514 { 515 static JNF_CLASS_CACHE(sjc_Window, "java/awt/Window"); 516 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleParent, sjc_CAccessibility, "getAccessibleParent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/Accessible;"); 517 static JNF_STATIC_MEMBER_CACHE(sjm_getSwingAccessible, sjc_CAccessible, "getSwingAccessible", "(Ljavax/accessibility/Accessible;)Ljavax/accessibility/Accessible;"); 518 519 if(fParent == nil) { 520 JNIEnv* env = [ThreadUtilities getJNIEnv]; 521 522 jobject jparent = JNFCallStaticObjectMethod(env, sjm_getAccessibleParent, fAccessible, fComponent); 523 524 if (jparent == NULL) { 525 fParent = fView; 526 } else { 527 AWTView *view = fView; 528 jobject jax = JNFCallStaticObjectMethod(env, sjm_getSwingAccessible, fAccessible); 529 530 if (JNFIsInstanceOf(env, jax, &sjc_Window)) { 531 // In this case jparent is an owner toplevel and we should retrieve its own view 532 view = [AWTView awtView:env ofAccessible:jparent]; 533 } 534 if (view != nil) { 535 fParent = [JavaComponentAccessibility createWithAccessible:jparent withEnv:env withView:view]; 536 } 537 if (fParent == nil) { 538 fParent = fView; 539 } 540 (*env)->DeleteLocalRef(env, jparent); 541 (*env)->DeleteLocalRef(env, jax ); 542 } 543 [fParent retain]; 544 } 545 return fParent; 546 } 547 548 - (NSView *)view 549 { 550 return fView; 551 } 552 553 - (NSWindow *)window 554 { 555 return [[self view] window]; 556 } 557 558 - (NSString *)javaRole 559 { 560 if(fJavaRole == nil) { 561 JNIEnv* env = [ThreadUtilities getJNIEnv]; 562 fJavaRole = getJavaRole(env, fAccessible, fComponent); 563 [fJavaRole retain]; 564 } 565 return fJavaRole; 566 } 567 568 - (BOOL)isMenu 569 { 570 id role = [self accessibilityRoleAttribute]; 571 return [role isEqualToString:NSAccessibilityMenuBarRole] || [role isEqualToString:NSAccessibilityMenuRole] || [role isEqualToString:NSAccessibilityMenuItemRole]; 572 } 573 574 - (BOOL)isSelected:(JNIEnv *)env 575 { 576 if (fIndex == -1) { 577 return NO; 578 } 579 580 return isChildSelected(env, ((JavaComponentAccessibility *)[self parent])->fAccessible, fIndex, fComponent); 581 } 582 583 - (BOOL)isSelectable:(JNIEnv *)env 584 { 585 jobject axContext = [self axContextWithEnv:env]; 586 BOOL selectable = isSelectable(env, axContext, fComponent); 587 (*env)->DeleteLocalRef(env, axContext); 588 return selectable; 589 } 590 591 - (BOOL)isVisible:(JNIEnv *)env 592 { 593 if (fIndex == -1) { 594 return NO; 595 } 596 597 jobject axContext = [self axContextWithEnv:env]; 598 BOOL showing = isShowing(env, axContext, fComponent); 599 (*env)->DeleteLocalRef(env, axContext); 600 return showing; 601 } 602 603 // the array of names for each role is cached in the sAttributeNamesForRoleCache 604 - (NSArray *)accessibilityAttributeNames 605 { 606 JNIEnv* env = [ThreadUtilities getJNIEnv]; 607 608 @synchronized(sAttributeNamesLOCK) { 609 NSString *javaRole = [self javaRole]; 610 NSArray *names = 611 (NSArray *)[sAttributeNamesForRoleCache objectForKey:javaRole]; 612 if (names == nil) { 613 names = [self initializeAttributeNamesWithEnv:env]; 614 #ifdef JAVA_AX_DEBUG 615 NSLog(@"Initializing: %s for %@: %@", __FUNCTION__, javaRole, names); 616 #endif 617 [sAttributeNamesForRoleCache setObject:names forKey:javaRole]; 618 } 619 // The above set of attributes is immutable per role, but some objects, if 620 // they are the child of a list, need to add the selected and index attributes. 621 id myParent = [self accessibilityParentAttribute]; 622 if ([myParent isKindOfClass:[JavaComponentAccessibility class]]) { 623 NSString *parentRole = [(JavaComponentAccessibility *)myParent javaRole]; 624 if ([parentRole isEqualToString:@"list"]) { 625 NSMutableArray *moreNames = 626 [[NSMutableArray alloc] initWithCapacity: [names count] + 2]; 627 [moreNames addObjectsFromArray: names]; 628 [moreNames addObject:NSAccessibilitySelectedAttribute]; 629 [moreNames addObject:NSAccessibilityIndexAttribute]; 630 return moreNames; 631 } 632 } 633 return names; 634 635 } // end @synchronized 636 637 #ifdef JAVA_AX_DEBUG 638 NSLog(@"Warning in %s: could not find attribute names for role: %@", __FUNCTION__, [self javaRole]); 639 #endif 640 641 return nil; 642 } 643 644 // -- accessibility attributes -- 645 646 - (BOOL)accessibilityShouldUseUniqueId { 647 return YES; 648 } 649 650 - (BOOL)accessibilitySupportsOverriddenAttributes { 651 return YES; 652 } 653 654 655 // generic getters & setters 656 // cmcnote: it would make more sense if these generic getters/setters were in JavaAccessibilityUtilities 657 - (id)accessibilityAttributeValue:(NSString *)attribute 658 { 659 AWT_ASSERT_APPKIT_THREAD; 660 661 // turns attribute "NSAccessibilityEnabledAttribute" into getter "accessibilityEnabledAttribute", 662 // calls getter on self 663 return JavaAccessibilityAttributeValue(self, attribute); 664 } 665 666 - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute 667 { 668 AWT_ASSERT_APPKIT_THREAD; 669 670 // turns attribute "NSAccessibilityParentAttribute" into selector "accessibilityIsParentAttributeSettable", 671 // calls selector on self 672 return JavaAccessibilityIsAttributeSettable(self, attribute); 673 } 674 675 - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute 676 { 677 AWT_ASSERT_APPKIT_THREAD; 678 679 if ([self accessibilityIsAttributeSettable:attribute]) { 680 // turns attribute "NSAccessibilityFocusAttribute" into setter "accessibilitySetFocusAttribute", 681 // calls setter on self 682 JavaAccessibilitySetAttributeValue(self, attribute, value); 683 } 684 } 685 686 687 // specific attributes, in alphabetical order a la 688 // http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html 689 690 // Elements that current element contains (NSArray) 691 - (NSArray *)accessibilityChildrenAttribute 692 { 693 JNIEnv* env = [ThreadUtilities getJNIEnv]; 694 NSArray *children = [JavaComponentAccessibility childrenOfParent:self 695 withEnv:env 696 withChildrenCode:JAVA_AX_ALL_CHILDREN 697 allowIgnored:NO]; 698 699 NSArray *value = nil; 700 if ([children count] > 0) { 701 value = children; 702 } 703 704 return value; 705 } 706 - (BOOL)accessibilityIsChildrenAttributeSettable 707 { 708 return NO; 709 } 710 711 - (NSUInteger)accessibilityIndexOfChild:(id)child 712 { 713 // Only special-casing for Lists, for now. This allows lists to be accessible, fixing radr://3856139 "JLists are broken". 714 // Will probably want to special-case for Tables when we implement them (radr://3096643 "Accessibility: Table"). 715 // In AppKit, NSMatrixAccessibility (which uses NSAccessibilityListRole), NSTableRowAccessibility, and NSTableViewAccessibility are the 716 // only ones that override the default implementation in NSAccessibility 717 if (![[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityListRole]) { 718 return [super accessibilityIndexOfChild:child]; 719 } 720 721 jint returnValue = 722 JNFCallStaticIntMethod( [ThreadUtilities getJNIEnv], 723 sjm_getAccessibleIndexInParent, 724 ((JavaComponentAccessibility *)child)->fAccessible, 725 ((JavaComponentAccessibility *)child)->fComponent ); 726 return (returnValue == -1) ? NSNotFound : returnValue; 727 } 728 729 // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children. 730 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount { 731 if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) { 732 // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child 733 NSArray *child = [JavaComponentAccessibility childrenOfParent:self withEnv:[ThreadUtilities getJNIEnv] withChildrenCode:(NSInteger)index allowIgnored:NO]; 734 if ([child count] > 0) { 735 return child; 736 } 737 } 738 return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount]; 739 } 740 741 // Flag indicating enabled state of element (NSNumber) 742 - (NSNumber *)accessibilityEnabledAttribute 743 { 744 static JNF_STATIC_MEMBER_CACHE(jm_isEnabled, sjc_CAccessibility, "isEnabled", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z"); 745 746 JNIEnv* env = [ThreadUtilities getJNIEnv]; 747 NSNumber *value = [NSNumber numberWithBool:JNFCallStaticBooleanMethod(env, jm_isEnabled, fAccessible, fComponent)]; // AWT_THREADING Safe (AWTRunLoop) 748 if (value == nil) { 749 NSLog(@"WARNING: %s called on component that has no accessible component: %@", __FUNCTION__, self); 750 } 751 return value; 752 } 753 754 - (BOOL)accessibilityIsEnabledAttributeSettable 755 { 756 return NO; 757 } 758 759 // Flag indicating presence of keyboard focus (NSNumber) 760 - (NSNumber *)accessibilityFocusedAttribute 761 { 762 if ([self accessibilityIsFocusedAttributeSettable]) { 763 return [NSNumber numberWithBool:[self isEqual:[NSApp accessibilityFocusedUIElement]]]; 764 } 765 return [NSNumber numberWithBool:NO]; 766 } 767 768 - (BOOL)accessibilityIsFocusedAttributeSettable 769 { 770 JNIEnv* env = [ThreadUtilities getJNIEnv]; 771 // According to javadoc, a component that is focusable will return true from isFocusTraversable, 772 // as well as having AccessibleState.FOCUSABLE in its AccessibleStateSet. 773 // We use the former heuristic; if the component focus-traversable, add a focused attribute 774 // See also initializeAttributeNamesWithEnv: 775 if (JNFCallStaticBooleanMethod(env, sjm_isFocusTraversable, fAccessible, fComponent)) { // AWT_THREADING Safe (AWTRunLoop) 776 return YES; 777 } 778 779 return NO; 780 } 781 782 - (void)accessibilitySetFocusedAttribute:(id)value 783 { 784 static JNF_STATIC_MEMBER_CACHE(jm_requestFocus, sjc_CAccessibility, "requestFocus", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V"); 785 786 if ([(NSNumber*)value boolValue]) 787 { 788 JNIEnv* env = [ThreadUtilities getJNIEnv]; 789 JNFCallStaticVoidMethod(env, jm_requestFocus, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 790 } 791 } 792 793 // Instance description, such as a help tag string (NSString) 794 - (NSString *)accessibilityHelpAttribute 795 { 796 JNIEnv* env = [ThreadUtilities getJNIEnv]; 797 798 jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleDescription, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 799 if (val == NULL) { 800 return nil; 801 } 802 NSString* str = JNFJavaToNSString(env, val); 803 (*env)->DeleteLocalRef(env, val); 804 return str; 805 } 806 807 - (BOOL)accessibilityIsHelpAttributeSettable 808 { 809 return NO; 810 } 811 812 - (NSValue *)accessibilityIndexAttribute 813 { 814 NSInteger index = fIndex; 815 NSValue *returnValue = [NSValue value:&index withObjCType:@encode(NSInteger)]; 816 return returnValue; 817 } 818 819 - (BOOL)accessibilityIsIndexAttributeSettable 820 { 821 return NO; 822 } 823 824 // Element's maximum value (id) 825 - (id)accessibilityMaxValueAttribute 826 { 827 static JNF_STATIC_MEMBER_CACHE(jm_getMaximumAccessibleValue, sjc_CAccessibility, "getMaximumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;"); 828 829 JNIEnv* env = [ThreadUtilities getJNIEnv]; 830 831 jobject axValue = JNFCallStaticObjectMethod(env, jm_getMaximumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 832 if (axValue == NULL) { 833 return [NSNumber numberWithInt:0]; 834 } 835 NSNumber* num = JNFJavaToNSNumber(env, axValue); 836 (*env)->DeleteLocalRef(env, axValue); 837 return num; 838 } 839 840 - (BOOL)accessibilityIsMaxValueAttributeSettable 841 { 842 return NO; 843 } 844 845 // Element's minimum value (id) 846 - (id)accessibilityMinValueAttribute 847 { 848 static JNF_STATIC_MEMBER_CACHE(jm_getMinimumAccessibleValue, sjc_CAccessibility, "getMinimumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;"); 849 850 JNIEnv* env = [ThreadUtilities getJNIEnv]; 851 852 jobject axValue = JNFCallStaticObjectMethod(env, jm_getMinimumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 853 if (axValue == NULL) { 854 return [NSNumber numberWithInt:0]; 855 } 856 NSNumber* num = JNFJavaToNSNumber(env, axValue); 857 (*env)->DeleteLocalRef(env, axValue); 858 return num; 859 } 860 861 - (BOOL)accessibilityIsMinValueAttributeSettable 862 { 863 return NO; 864 } 865 866 - (id)accessibilityOrientationAttribute 867 { 868 JNIEnv* env = [ThreadUtilities getJNIEnv]; 869 jobject axContext = [self axContextWithEnv:env]; 870 871 // cmcnote - should batch these two calls into one that returns an array of two bools, one for vertical and one for horiz 872 if (isVertical(env, axContext, fComponent)) { 873 (*env)->DeleteLocalRef(env, axContext); 874 return NSAccessibilityVerticalOrientationValue; 875 } 876 877 if (isHorizontal(env, axContext, fComponent)) { 878 (*env)->DeleteLocalRef(env, axContext); 879 return NSAccessibilityHorizontalOrientationValue; 880 } 881 882 (*env)->DeleteLocalRef(env, axContext); 883 return nil; 884 } 885 886 - (BOOL)accessibilityIsOrientationAttributeSettable 887 { 888 return NO; 889 } 890 891 // Element containing current element (id) 892 - (id)accessibilityParentAttribute 893 { 894 return NSAccessibilityUnignoredAncestor([self parent]); 895 } 896 897 - (BOOL)accessibilityIsParentAttributeSettable 898 { 899 return NO; 900 } 901 902 // Screen position of element's lower-left corner in lower-left relative screen coordinates (NSValue) 903 - (NSValue *)accessibilityPositionAttribute 904 { 905 JNIEnv* env = [ThreadUtilities getJNIEnv]; 906 jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 907 908 // NSAccessibility wants the bottom left point of the object in 909 // bottom left based screen coords 910 911 // Get the java screen coords, and make a NSPoint of the bottom left of the AxComponent. 912 NSSize size = getAxComponentSize(env, axComponent, fComponent); 913 NSPoint point = getAxComponentLocationOnScreen(env, axComponent, fComponent); 914 (*env)->DeleteLocalRef(env, axComponent); 915 916 point.y += size.height; 917 918 // Now make it into Cocoa screen coords. 919 point.y = [[[[self view] window] screen] frame].size.height - point.y; 920 921 return [NSValue valueWithPoint:point]; 922 } 923 924 - (BOOL)accessibilityIsPositionAttributeSettable 925 { 926 // In AppKit, position is only settable for a window (NSAccessibilityWindowRole). Our windows are taken care of natively, so we don't need to deal with this here 927 // We *could* make use of Java's AccessibleComponent.setLocation() method. Investigate. radr://3953869 928 return NO; 929 } 930 931 // Element type, such as NSAccessibilityRadioButtonRole (NSString). See the role table 932 // at http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html 933 - (NSString *)accessibilityRoleAttribute 934 { 935 if (fNSRole == nil) { 936 NSString *javaRole = [self javaRole]; 937 fNSRole = [sRoles objectForKey:javaRole]; 938 if (fNSRole == nil) { 939 // this component has assigned itself a custom AccessibleRole not in the sRoles array 940 fNSRole = javaRole; 941 } 942 [fNSRole retain]; 943 } 944 return fNSRole; 945 } 946 - (BOOL)accessibilityIsRoleAttributeSettable 947 { 948 return NO; 949 } 950 951 // Localized, user-readable description of role, such as radio button (NSString) 952 - (NSString *)accessibilityRoleDescriptionAttribute 953 { 954 // first ask AppKit for its accessible role description for a given AXRole 955 NSString *value = NSAccessibilityRoleDescription([self accessibilityRoleAttribute], nil); 956 957 if (value == nil) { 958 // query java if necessary 959 static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleRoleDisplayString, sjc_CAccessibility, "getAccessibleRoleDisplayString", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;"); 960 961 JNIEnv* env = [ThreadUtilities getJNIEnv]; 962 963 jobject axRole = JNFCallStaticObjectMethod(env, jm_getAccessibleRoleDisplayString, fAccessible, fComponent); 964 if (axRole != NULL) { 965 value = JNFJavaToNSString(env, axRole); 966 (*env)->DeleteLocalRef(env, axRole); 967 } else { 968 value = @"unknown"; 969 } 970 } 971 972 return value; 973 } 974 975 - (BOOL)accessibilityIsRoleDescriptionAttributeSettable 976 { 977 return NO; 978 } 979 980 // Currently selected children (NSArray) 981 - (NSArray *)accessibilitySelectedChildrenAttribute 982 { 983 JNIEnv* env = [ThreadUtilities getJNIEnv]; 984 NSArray *selectedChildren = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_SELECTED_CHILDREN allowIgnored:NO]; 985 if ([selectedChildren count] > 0) { 986 return selectedChildren; 987 } 988 989 return nil; 990 } 991 992 - (BOOL)accessibilityIsSelectedChildrenAttributeSettable 993 { 994 return NO; // cmcnote: actually it should be. so need to write accessibilitySetSelectedChildrenAttribute also 995 } 996 997 - (NSNumber *)accessibilitySelectedAttribute 998 { 999 return [NSNumber numberWithBool:[self isSelected:[ThreadUtilities getJNIEnv]]]; 1000 } 1001 1002 - (BOOL)accessibilityIsSelectedAttributeSettable 1003 { 1004 if ([self isSelectable:[ThreadUtilities getJNIEnv]]) { 1005 return YES; 1006 } else { 1007 return NO; 1008 } 1009 } 1010 1011 - (void)accessibilitySetSelectedAttribute:(id)value 1012 { 1013 static JNF_STATIC_MEMBER_CACHE( jm_requestSelection, 1014 sjc_CAccessibility, 1015 "requestSelection", 1016 "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V" ); 1017 1018 if ([(NSNumber*)value boolValue]) { 1019 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1020 JNFCallStaticVoidMethod(env, jm_requestSelection, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 1021 } 1022 } 1023 1024 // Element size (NSValue) 1025 - (NSValue *)accessibilitySizeAttribute { 1026 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1027 jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 1028 NSValue* size = [NSValue valueWithSize:getAxComponentSize(env, axComponent, fComponent)]; 1029 (*env)->DeleteLocalRef(env, axComponent); 1030 return size; 1031 } 1032 1033 - (BOOL)accessibilityIsSizeAttributeSettable 1034 { 1035 // SIZE is settable in windows if [self styleMask] & NSResizableWindowMask - but windows are heavyweight so we're ok here 1036 // SIZE is settable in columns if [[self tableValue] allowsColumnResizing - haven't dealt with columns yet 1037 return NO; 1038 } 1039 1040 // Element subrole type, such as NSAccessibilityTableRowSubrole (NSString). See the subrole attribute table at 1041 // http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html 1042 - (NSString *)accessibilitySubroleAttribute 1043 { 1044 NSString *value = nil; 1045 if ([[self javaRole] isEqualToString:@"passwordtext"]) 1046 { 1047 value = NSAccessibilitySecureTextFieldSubrole; 1048 } 1049 /* 1050 // other subroles. TableRow and OutlineRow may be relevant to us 1051 NSAccessibilityCloseButtonSubrole // no, heavyweight window takes care of this 1052 NSAccessibilityMinimizeButtonSubrole // " 1053 NSAccessibilityOutlineRowSubrole // maybe? 1054 NSAccessibilitySecureTextFieldSubrole // currently used 1055 NSAccessibilityTableRowSubrole // maybe? 1056 NSAccessibilityToolbarButtonSubrole // maybe? 1057 NSAccessibilityUnknownSubrole 1058 NSAccessibilityZoomButtonSubrole // no, heavyweight window takes care of this 1059 NSAccessibilityStandardWindowSubrole// no, heavyweight window takes care of this 1060 NSAccessibilityDialogSubrole // maybe? 1061 NSAccessibilitySystemDialogSubrole // no 1062 NSAccessibilityFloatingWindowSubrole // in 1.5 if we implement these, heavyweight will take care of them anyway 1063 NSAccessibilitySystemFloatingWindowSubrole 1064 NSAccessibilityIncrementArrowSubrole // no 1065 NSAccessibilityDecrementArrowSubrole // no 1066 NSAccessibilityIncrementPageSubrole // no 1067 NSAccessibilityDecrementPageSubrole // no 1068 NSAccessibilitySearchFieldSubrole //no 1069 */ 1070 return value; 1071 } 1072 1073 - (BOOL)accessibilityIsSubroleAttributeSettable 1074 { 1075 return NO; 1076 } 1077 1078 // Title of element, such as button text (NSString) 1079 - (NSString *)accessibilityTitleAttribute 1080 { 1081 // Return empty string for labels, since their value and tile end up being the same thing and this leads to repeated text. 1082 if ([[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityStaticTextRole]) { 1083 return @""; 1084 } 1085 1086 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1087 1088 jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleName, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 1089 if (val == NULL) { 1090 return nil; 1091 } 1092 NSString* str = JNFJavaToNSString(env, val); 1093 (*env)->DeleteLocalRef(env, val); 1094 return str; 1095 } 1096 1097 - (BOOL)accessibilityIsTitleAttributeSettable 1098 { 1099 return NO; 1100 } 1101 1102 - (NSWindow *)accessibilityTopLevelUIElementAttribute 1103 { 1104 return [self window]; 1105 } 1106 1107 - (BOOL)accessibilityIsTopLevelUIElementAttributeSettable 1108 { 1109 return NO; 1110 } 1111 1112 // Element's value (id) 1113 // note that the appKit meaning of "accessibilityValue" is different from the java 1114 // meaning of "accessibleValue", which is specific to numerical values 1115 // (https://docs.oracle.com/javase/8/docs/api/javax/accessibility/AccessibleValue.html#setCurrentAccessibleValue-java.lang.Number-) 1116 - (id)accessibilityValueAttribute 1117 { 1118 static JNF_STATIC_MEMBER_CACHE(jm_getCurrentAccessibleValue, sjc_CAccessibility, "getCurrentAccessibleValue", "(Ljavax/accessibility/AccessibleValue;Ljava/awt/Component;)Ljava/lang/Number;"); 1119 1120 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1121 1122 // ask Java for the component's accessibleValue. In java, the "accessibleValue" just means a numerical value 1123 // a text value is taken care of in JavaTextAccessibility 1124 1125 // cmcnote should coalesce these calls into one java call 1126 NSNumber *num = nil; 1127 jobject axValue = JNFCallStaticObjectMethod(env, sjm_getAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 1128 if (axValue != NULL) { 1129 jobject str = JNFCallStaticObjectMethod(env, jm_getCurrentAccessibleValue, axValue, fComponent); 1130 if (str != NULL) { 1131 num = JNFJavaToNSNumber(env, str); // AWT_THREADING Safe (AWTRunLoop) 1132 (*env)->DeleteLocalRef(env, str); 1133 } 1134 (*env)->DeleteLocalRef(env, axValue); 1135 } 1136 if (num == nil) { 1137 num = [NSNumber numberWithInt:0]; 1138 } 1139 return num; 1140 } 1141 1142 - (BOOL)accessibilityIsValueAttributeSettable 1143 { 1144 // according ot AppKit sources, in general the value attribute is not settable, except in the cases 1145 // of an NSScroller, an NSSplitView, and text that's both enabled & editable 1146 BOOL isSettable = NO; 1147 NSString *role = [self accessibilityRoleAttribute]; 1148 1149 if ([role isEqualToString:NSAccessibilityScrollBarRole] || // according to NSScrollerAccessibility 1150 [role isEqualToString:NSAccessibilitySplitGroupRole] ) // according to NSSplitViewAccessibility 1151 { 1152 isSettable = YES; 1153 } 1154 return isSettable; 1155 } 1156 1157 - (void)accessibilitySetValueAttribute:(id)value 1158 { 1159 #ifdef JAVA_AX_DEBUG 1160 NSLog(@"Not yet implemented: %s\n", __FUNCTION__); // radr://3954018 1161 #endif 1162 } 1163 1164 1165 // Child elements that are visible (NSArray) 1166 - (NSArray *)accessibilityVisibleChildrenAttribute 1167 { 1168 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1169 NSArray *visibleChildren = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_VISIBLE_CHILDREN allowIgnored:NO]; 1170 if ([visibleChildren count] <= 0) return nil; 1171 return visibleChildren; 1172 } 1173 1174 - (BOOL)accessibilityIsVisibleChildrenAttributeSettable 1175 { 1176 return NO; 1177 } 1178 1179 // Window containing current element (id) 1180 - (id)accessibilityWindowAttribute 1181 { 1182 return [self window]; 1183 } 1184 1185 - (BOOL)accessibilityIsWindowAttributeSettable 1186 { 1187 return NO; 1188 } 1189 1190 1191 // -- accessibility actions -- 1192 - (NSArray *)accessibilityActionNames 1193 { 1194 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1195 return [[self getActions:env] allKeys]; 1196 } 1197 1198 - (NSString *)accessibilityActionDescription:(NSString *)action 1199 { 1200 AWT_ASSERT_APPKIT_THREAD; 1201 1202 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1203 return [(id <JavaAccessibilityAction>)[[self getActions:env] objectForKey:action] getDescription]; 1204 } 1205 1206 - (void)accessibilityPerformAction:(NSString *)action 1207 { 1208 AWT_ASSERT_APPKIT_THREAD; 1209 1210 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1211 [(id <JavaAccessibilityAction>)[[self getActions:env] objectForKey:action] perform]; 1212 } 1213 1214 1215 // -- misc accessibility -- 1216 - (BOOL)accessibilityIsIgnored 1217 { 1218 #ifdef JAVA_AX_NO_IGNORES 1219 return NO; 1220 #else 1221 return [[self accessibilityRoleAttribute] isEqualToString:JavaAccessibilityIgnore]; 1222 #endif /* JAVA_AX_NO_IGNORES */ 1223 } 1224 1225 - (id)accessibilityHitTest:(NSPoint)point withEnv:(JNIEnv *)env 1226 { 1227 static JNF_CLASS_CACHE(jc_Container, "java/awt/Container"); 1228 static JNF_STATIC_MEMBER_CACHE(jm_accessibilityHitTest, sjc_CAccessibility, "accessibilityHitTest", "(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;"); 1229 1230 // Make it into java screen coords 1231 point.y = [[[[self view] window] screen] frame].size.height - point.y; 1232 1233 jobject jparent = fComponent; 1234 1235 id value = nil; 1236 if (JNFIsInstanceOf(env, jparent, &jc_Container)) { 1237 jobject jaccessible = JNFCallStaticObjectMethod(env, jm_accessibilityHitTest, jparent, (jfloat)point.x, (jfloat)point.y); // AWT_THREADING Safe (AWTRunLoop) 1238 if (jaccessible != NULL) { 1239 value = [JavaComponentAccessibility createWithAccessible:jaccessible withEnv:env withView:fView]; 1240 (*env)->DeleteLocalRef(env, jaccessible); 1241 } 1242 } 1243 1244 if (value == nil) { 1245 value = self; 1246 } 1247 1248 if ([value accessibilityIsIgnored]) { 1249 value = NSAccessibilityUnignoredAncestor(value); 1250 } 1251 1252 #ifdef JAVA_AX_DEBUG 1253 NSLog(@"%s: %@", __FUNCTION__, value); 1254 #endif 1255 return value; 1256 } 1257 1258 - (id)accessibilityFocusedUIElement 1259 { 1260 static JNF_STATIC_MEMBER_CACHE(jm_getFocusOwner, sjc_CAccessibility, "getFocusOwner", "(Ljava/awt/Component;)Ljavax/accessibility/Accessible;"); 1261 1262 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1263 id value = nil; 1264 1265 NSWindow* hostWindow = [[self->fView window] retain]; 1266 jobject focused = JNFCallStaticObjectMethod(env, jm_getFocusOwner, fComponent); // AWT_THREADING Safe (AWTRunLoop) 1267 [hostWindow release]; 1268 1269 if (focused != NULL) { 1270 if (JNFIsInstanceOf(env, focused, &sjc_Accessible)) { 1271 value = [JavaComponentAccessibility createWithAccessible:focused withEnv:env withView:fView]; 1272 } 1273 (*env)->DeleteLocalRef(env, focused); 1274 } 1275 1276 if (value == nil) { 1277 value = self; 1278 } 1279 #ifdef JAVA_AX_DEBUG 1280 NSLog(@"%s: %@", __FUNCTION__, value); 1281 #endif 1282 return value; 1283 } 1284 1285 @end 1286 1287 /* 1288 * Class: sun_lwawt_macosx_CAccessibility 1289 * Method: focusChanged 1290 * Signature: ()V 1291 */ 1292 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessibility_focusChanged 1293 (JNIEnv *env, jobject jthis) 1294 { 1295 JNF_COCOA_ENTER(env); 1296 [ThreadUtilities performOnMainThread:@selector(postFocusChanged:) on:[JavaComponentAccessibility class] withObject:nil waitUntilDone:NO]; 1297 JNF_COCOA_EXIT(env); 1298 } 1299 1300 /* 1301 * Class: sun_lwawt_macosx_CAccessible 1302 * Method: valueChanged 1303 * Signature: (I)V 1304 */ 1305 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_valueChanged 1306 (JNIEnv *env, jclass jklass, jlong element) 1307 { 1308 JNF_COCOA_ENTER(env); 1309 [ThreadUtilities performOnMainThread:@selector(postValueChanged) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO]; 1310 JNF_COCOA_EXIT(env); 1311 } 1312 1313 /* 1314 * Class: sun_lwawt_macosx_CAccessible 1315 * Method: selectedTextChanged 1316 * Signature: (I)V 1317 */ 1318 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectedTextChanged 1319 (JNIEnv *env, jclass jklass, jlong element) 1320 { 1321 JNF_COCOA_ENTER(env); 1322 [ThreadUtilities performOnMainThread:@selector(postSelectedTextChanged) 1323 on:(JavaComponentAccessibility *)jlong_to_ptr(element) 1324 withObject:nil 1325 waitUntilDone:NO]; 1326 JNF_COCOA_EXIT(env); 1327 } 1328 1329 /* 1330 * Class: sun_lwawt_macosx_CAccessible 1331 * Method: selectionChanged 1332 * Signature: (I)V 1333 */ 1334 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectionChanged 1335 (JNIEnv *env, jclass jklass, jlong element) 1336 { 1337 JNF_COCOA_ENTER(env); 1338 [ThreadUtilities performOnMainThread:@selector(postSelectionChanged) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO]; 1339 JNF_COCOA_EXIT(env); 1340 } 1341 1342 /* 1343 * Class: sun_lwawt_macosx_CAccessible 1344 * Method: unregisterFromCocoaAXSystem 1345 * Signature: (I)V 1346 */ 1347 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_unregisterFromCocoaAXSystem 1348 (JNIEnv *env, jclass jklass, jlong element) 1349 { 1350 JNF_COCOA_ENTER(env); 1351 [ThreadUtilities performOnMainThread:@selector(unregisterFromCocoaAXSystem) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO]; 1352 JNF_COCOA_EXIT(env); 1353 } 1354 1355 @implementation TabGroupAccessibility 1356 1357 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole 1358 { 1359 self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole]; 1360 if (self) { 1361 _numTabs = -1; //flag for uninitialized numTabs 1362 } 1363 return self; 1364 } 1365 1366 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env 1367 { 1368 NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env]; 1369 1370 [names addObject:NSAccessibilityTabsAttribute]; 1371 [names addObject:NSAccessibilityContentsAttribute]; 1372 [names addObject:NSAccessibilityValueAttribute]; 1373 1374 return names; 1375 } 1376 1377 - (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext 1378 { 1379 NSArray *tabs = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; 1380 1381 // Looking at the JTabbedPane sources, there is always one AccessibleSelection. 1382 jobject selAccessible = getAxContextSelection(env, axContext, 0, fComponent); 1383 if (selAccessible == NULL) return nil; 1384 1385 // Go through the tabs and find selAccessible 1386 _numTabs = [tabs count]; 1387 JavaComponentAccessibility *aTab; 1388 NSInteger i; 1389 for (i = 0; i < _numTabs; i++) { 1390 aTab = (JavaComponentAccessibility *)[tabs objectAtIndex:i]; 1391 if ([aTab isAccessibleWithEnv:env forAccessible:selAccessible]) { 1392 (*env)->DeleteLocalRef(env, selAccessible); 1393 return aTab; 1394 } 1395 } 1396 (*env)->DeleteLocalRef(env, selAccessible); 1397 return nil; 1398 } 1399 1400 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored 1401 { 1402 jobjectArray jtabsAndRoles = (jobjectArray)JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, fAccessible, fComponent, whichTabs, allowIgnored); // AWT_THREADING Safe (AWTRunLoop) 1403 if(jtabsAndRoles == NULL) return nil; 1404 1405 jsize arrayLen = (*env)->GetArrayLength(env, jtabsAndRoles); 1406 if (arrayLen == 0) { 1407 (*env)->DeleteLocalRef(env, jtabsAndRoles); 1408 return nil; 1409 } 1410 NSMutableArray *tabs = [NSMutableArray arrayWithCapacity:(arrayLen/2)]; 1411 1412 // all of the tabs have the same role, so we can just find out what that is here and use it for all the tabs 1413 jobject jtabJavaRole = (*env)->GetObjectArrayElement(env, jtabsAndRoles, 1); // the array entries alternate between tab/role, starting with tab. so the first role is entry 1. 1414 if (jtabJavaRole == NULL) { 1415 (*env)->DeleteLocalRef(env, jtabsAndRoles); 1416 return nil; 1417 } 1418 jobject jkey = JNFGetObjectField(env, jtabJavaRole, sjf_key); 1419 NSString *tabJavaRole = JNFJavaToNSString(env, jkey); 1420 (*env)->DeleteLocalRef(env, jkey); 1421 1422 NSInteger i; 1423 NSUInteger tabIndex = (whichTabs >= 0) ? whichTabs : 0; // if we're getting one particular child, make sure to set its index correctly 1424 for(i = 0; i < arrayLen; i+=2) { 1425 jobject jtab = (*env)->GetObjectArrayElement(env, jtabsAndRoles, i); 1426 JavaComponentAccessibility *tab = [[[TabGroupControlAccessibility alloc] initWithParent:self withEnv:env withAccessible:jtab withIndex:tabIndex withTabGroup:axContext withView:[self view] withJavaRole:tabJavaRole] autorelease]; 1427 (*env)->DeleteLocalRef(env, jtab); 1428 [tabs addObject:tab]; 1429 tabIndex++; 1430 } 1431 (*env)->DeleteLocalRef(env, jtabsAndRoles); 1432 return tabs; 1433 } 1434 1435 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored 1436 { 1437 // Contents are the children of the selected tab. 1438 id currentTab = [self currentTabWithEnv:env withAxContext:axContext]; 1439 if (currentTab == nil) return nil; 1440 1441 NSArray *contents = [JavaComponentAccessibility childrenOfParent:currentTab withEnv:env withChildrenCode:whichTabs allowIgnored:allowIgnored]; 1442 if ([contents count] <= 0) return nil; 1443 return contents; 1444 } 1445 1446 - (id) accessibilityTabsAttribute 1447 { 1448 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1449 jobject axContext = [self axContextWithEnv:env]; 1450 id tabs = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; 1451 (*env)->DeleteLocalRef(env, axContext); 1452 return tabs; 1453 } 1454 1455 - (BOOL)accessibilityIsTabsAttributeSettable 1456 { 1457 return NO; //cmcnote: not sure. 1458 } 1459 1460 - (NSInteger)numTabs 1461 { 1462 if (_numTabs == -1) { 1463 _numTabs = [[self accessibilityTabsAttribute] count]; 1464 } 1465 return _numTabs; 1466 } 1467 1468 - (NSArray *) accessibilityContentsAttribute 1469 { 1470 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1471 jobject axContext = [self axContextWithEnv:env]; 1472 NSArray* cont = [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; 1473 (*env)->DeleteLocalRef(env, axContext); 1474 return cont; 1475 } 1476 1477 - (BOOL)accessibilityIsContentsAttributeSettable 1478 { 1479 return NO; 1480 } 1481 1482 // axValue is the currently selected tab 1483 -(id) accessibilityValueAttribute 1484 { 1485 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1486 jobject axContext = [self axContextWithEnv:env]; 1487 id val = [self currentTabWithEnv:env withAxContext:axContext]; 1488 (*env)->DeleteLocalRef(env, axContext); 1489 return val; 1490 } 1491 1492 - (BOOL)accessibilityIsValueAttributeSettable 1493 { 1494 return YES; 1495 } 1496 1497 - (void)accessibilitySetValueAttribute:(id)value //cmcnote: not certain this is ever actually called. investigate. 1498 { 1499 // set the current tab 1500 NSNumber *number = (NSNumber *)value; 1501 if (![number boolValue]) return; 1502 1503 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1504 jobject axContext = [self axContextWithEnv:env]; 1505 setAxContextSelection(env, axContext, fIndex, fComponent); 1506 (*env)->DeleteLocalRef(env, axContext); 1507 } 1508 1509 - (NSArray *)accessibilityChildrenAttribute 1510 { 1511 //children = AXTabs + AXContents 1512 NSArray *tabs = [self accessibilityTabsAttribute]; 1513 NSArray *contents = [self accessibilityContentsAttribute]; 1514 1515 NSMutableArray *children = [NSMutableArray arrayWithCapacity:[tabs count] + [contents count]]; 1516 [children addObjectsFromArray:tabs]; 1517 [children addObjectsFromArray:contents]; 1518 1519 return (NSArray *)children; 1520 } 1521 1522 // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children. 1523 // See similar optimization in JavaComponentAccessibility. We have to extend the base implementation here, since 1524 // children of tabs are AXTabs + AXContents 1525 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount { 1526 NSArray *result = nil; 1527 if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) { 1528 // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child 1529 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1530 jobject axContext = [self axContextWithEnv:env]; 1531 1532 //children = AXTabs + AXContents 1533 NSArray *children = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:index allowIgnored:NO]; // first look at the tabs 1534 if ([children count] > 0) { 1535 result = children; 1536 } else { 1537 children= [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:(index-[self numTabs]) allowIgnored:NO]; 1538 if ([children count] > 0) { 1539 result = children; 1540 } 1541 } 1542 (*env)->DeleteLocalRef(env, axContext); 1543 } else { 1544 result = [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount]; 1545 } 1546 return result; 1547 } 1548 1549 @end 1550 1551 1552 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component); 1553 1554 @implementation TabGroupControlAccessibility 1555 1556 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole 1557 { 1558 self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole]; 1559 if (self) { 1560 if (tabGroup != NULL) { 1561 fTabGroupAxContext = JNFNewWeakGlobalRef(env, tabGroup); 1562 } else { 1563 fTabGroupAxContext = NULL; 1564 } 1565 } 1566 return self; 1567 } 1568 1569 - (void)dealloc 1570 { 1571 JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; 1572 1573 if (fTabGroupAxContext != NULL) { 1574 JNFDeleteWeakGlobalRef(env, fTabGroupAxContext); 1575 fTabGroupAxContext = NULL; 1576 } 1577 1578 [super dealloc]; 1579 } 1580 1581 - (id)accessibilityValueAttribute 1582 { 1583 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1584 jobject axContext = [self axContextWithEnv:env]; 1585 jobject selAccessible = getAxContextSelection(env, [self tabGroup], fIndex, fComponent); 1586 1587 // Returns the current selection of the page tab list 1588 id val = [NSNumber numberWithBool:ObjectEquals(env, axContext, selAccessible, fComponent)]; 1589 1590 (*env)->DeleteLocalRef(env, selAccessible); 1591 (*env)->DeleteLocalRef(env, axContext); 1592 return val; 1593 } 1594 1595 - (void)getActionsWithEnv:(JNIEnv *)env 1596 { 1597 TabGroupAction *action = [[TabGroupAction alloc] initWithEnv:env withTabGroup:[self tabGroup] withIndex:fIndex withComponent:fComponent]; 1598 [fActions setObject:action forKey:NSAccessibilityPressAction]; 1599 [action release]; 1600 } 1601 1602 - (jobject)tabGroup 1603 { 1604 if (fTabGroupAxContext == NULL) { 1605 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1606 jobject tabGroupAxContext = [(JavaComponentAccessibility *)[self parent] axContextWithEnv:env]; 1607 fTabGroupAxContext = JNFNewWeakGlobalRef(env, tabGroupAxContext); 1608 (*env)->DeleteLocalRef(env, tabGroupAxContext); 1609 } 1610 return fTabGroupAxContext; 1611 } 1612 1613 @end 1614 1615 1616 @implementation ScrollAreaAccessibility 1617 1618 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env 1619 { 1620 NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env]; 1621 1622 [names addObject:NSAccessibilityHorizontalScrollBarAttribute]; 1623 [names addObject:NSAccessibilityVerticalScrollBarAttribute]; 1624 [names addObject:NSAccessibilityContentsAttribute]; 1625 1626 return names; 1627 } 1628 1629 - (id)accessibilityHorizontalScrollBarAttribute 1630 { 1631 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1632 1633 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; 1634 if ([children count] <= 0) return nil; 1635 1636 // The scroll bars are in the children. 1637 JavaComponentAccessibility *aElement; 1638 NSEnumerator *enumerator = [children objectEnumerator]; 1639 while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) { 1640 if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) { 1641 jobject elementAxContext = [aElement axContextWithEnv:env]; 1642 if (isHorizontal(env, elementAxContext, fComponent)) { 1643 (*env)->DeleteLocalRef(env, elementAxContext); 1644 return aElement; 1645 } 1646 (*env)->DeleteLocalRef(env, elementAxContext); 1647 } 1648 } 1649 1650 return nil; 1651 } 1652 1653 - (BOOL)accessibilityIsHorizontalScrollBarAttributeSettable 1654 { 1655 return NO; 1656 } 1657 1658 - (id)accessibilityVerticalScrollBarAttribute 1659 { 1660 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1661 1662 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; 1663 if ([children count] <= 0) return nil; 1664 1665 // The scroll bars are in the children. 1666 NSEnumerator *enumerator = [children objectEnumerator]; 1667 JavaComponentAccessibility *aElement; 1668 while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) { 1669 if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) { 1670 jobject elementAxContext = [aElement axContextWithEnv:env]; 1671 if (isVertical(env, elementAxContext, fComponent)) { 1672 (*env)->DeleteLocalRef(env, elementAxContext); 1673 return aElement; 1674 } 1675 (*env)->DeleteLocalRef(env, elementAxContext); 1676 } 1677 } 1678 1679 return nil; 1680 } 1681 1682 - (BOOL)accessibilityIsVerticalScrollBarAttributeSettable 1683 { 1684 return NO; 1685 } 1686 1687 - (NSArray *)accessibilityContentsAttribute 1688 { 1689 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1690 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; 1691 1692 if ([children count] <= 0) return nil; 1693 NSArray *contents = [NSMutableArray arrayWithCapacity:[children count]]; 1694 1695 // The scroll bars are in the children. children less the scroll bars is the contents 1696 NSEnumerator *enumerator = [children objectEnumerator]; 1697 JavaComponentAccessibility *aElement; 1698 while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) { 1699 if (![[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) { 1700 // no scroll bars in contents 1701 [(NSMutableArray *)contents addObject:aElement]; 1702 } 1703 } 1704 1705 return contents; 1706 } 1707 1708 - (BOOL)accessibilityIsContentsAttributeSettable 1709 { 1710 return NO; 1711 } 1712 1713 @end 1714 1715 /* 1716 * Returns Object.equals for the two items 1717 * This may use LWCToolkit.invokeAndWait(); don't call while holding fLock 1718 * and try to pass a component so the event happens on the correct thread. 1719 */ 1720 static JNF_CLASS_CACHE(sjc_Object, "java/lang/Object"); 1721 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component) 1722 { 1723 static JNF_MEMBER_CACHE(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z"); 1724 1725 if ((a == NULL) && (b == NULL)) return YES; 1726 if ((a == NULL) || (b == NULL)) return NO; 1727 1728 if (pthread_main_np() != 0) { 1729 // If we are on the AppKit thread 1730 static JNF_CLASS_CACHE(sjc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit"); 1731 static JNF_STATIC_MEMBER_CACHE(jm_doEquals, sjc_LWCToolkit, "doEquals", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/awt/Component;)Z"); 1732 return JNFCallStaticBooleanMethod(env, jm_doEquals, a, b, component); // AWT_THREADING Safe (AWTRunLoopMode) 1733 } 1734 1735 return JNFCallBooleanMethod(env, a, jm_equals, b); // AWT_THREADING Safe (!appKit) 1736 }