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