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