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