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