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 56 static JNF_STATIC_MEMBER_CACHE(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;"); 57 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleComponent, sjc_CAccessibility, "getAccessibleComponent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleComponent;"); 58 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleValue, sjc_CAccessibility, "getAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleValue;"); 59 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;"); 60 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleDescription, sjc_CAccessibility, "getAccessibleDescription", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;"); 61 static JNF_STATIC_MEMBER_CACHE(sjm_isFocusTraversable, sjc_CAccessibility, "isFocusTraversable", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z"); 62 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleIndexInParent, sjc_CAccessibility, "getAccessibleIndexInParent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)I"); 63 64 static JNF_CLASS_CACHE(sjc_CAccessible, "sun/lwawt/macosx/CAccessible"); 65 66 static JNF_MEMBER_CACHE(jf_ptr, sjc_CAccessible, "ptr", "J"); 67 static JNF_STATIC_MEMBER_CACHE(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;"); 68 69 static jobject sAccessibilityClass = NULL; 70 71 // sAttributeNamesForRoleCache holds the names of the attributes to which each java 72 // AccessibleRole responds (see AccessibleRole.java). 73 // This cache is queried before attempting to access a given attribute for a particular role. 74 static NSMutableDictionary *sAttributeNamesForRoleCache = nil; 75 static NSObject *sAttributeNamesLOCK = nil; 76 77 @interface TabGroupAccessibility : JavaComponentAccessibility { 78 NSInteger _numTabs; 79 } 80 81 - (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext; 82 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored; 83 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored; 84 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env; 85 86 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount; 87 - (NSArray *)accessibilityChildrenAttribute; 88 - (id) accessibilityTabsAttribute; 89 - (BOOL)accessibilityIsTabsAttributeSettable; 90 - (NSArray *)accessibilityContentsAttribute; 91 - (BOOL)accessibilityIsContentsAttributeSettable; 92 - (id) accessibilityValueAttribute; 93 94 @end 95 96 97 @interface TabGroupControlAccessibility : JavaComponentAccessibility { 98 jobject fTabGroupAxContext; 99 } 100 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole; 101 - (jobject)tabGroup; 102 - (void)getActionsWithEnv:(JNIEnv *)env; 103 104 - (id)accessibilityValueAttribute; 105 @end 106 107 108 @interface ScrollAreaAccessibility : JavaComponentAccessibility { 109 110 } 111 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env; 112 - (NSArray *)accessibilityContentsAttribute; 113 - (BOOL)accessibilityIsContentsAttributeSettable; 114 - (id)accessibilityVerticalScrollBarAttribute; 115 - (BOOL)accessibilityIsVerticalScrollBarAttributeSettable; 116 - (id)accessibilityHorizontalScrollBarAttribute; 117 - (BOOL)accessibilityIsHorizontalScrollBarAttributeSettable; 118 @end 119 120 121 @implementation JavaComponentAccessibility 122 123 - (NSString *)description 124 { 125 return [NSString stringWithFormat:@"%@(title:'%@', desc:'%@', value:'%@')", [self accessibilityRoleAttribute], 126 [self accessibilityTitleAttribute], [self accessibilityRoleDescriptionAttribute], [self accessibilityValueAttribute]]; 127 } 128 129 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole 130 { 131 self = [super init]; 132 if (self) 133 { 134 fParent = [parent retain]; 135 fView = [view retain]; 136 fJavaRole = [javaRole retain]; 137 138 fAccessible = (*env)->NewWeakGlobalRef(env, accessible); 139 (*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 if ([self accessibilityIsIgnored]) { 650 return names; 651 } 652 id myParent = [self accessibilityParentAttribute]; 653 if ([myParent isKindOfClass:[JavaComponentAccessibility class]]) { 654 NSString *parentRole = [(JavaComponentAccessibility *)myParent javaRole]; 655 if ([parentRole isEqualToString:@"list"]) { 656 NSMutableArray *moreNames = 657 [[NSMutableArray alloc] initWithCapacity: [names count] + 2]; 658 [moreNames addObjectsFromArray: names]; 659 [moreNames addObject:NSAccessibilitySelectedAttribute]; 660 [moreNames addObject:NSAccessibilityIndexAttribute]; 661 return moreNames; 662 } 663 } 664 // popupmenu's return values not selected children 665 if ( [javaRole isEqualToString:@"popupmenu"] && 666 ![[[self parent] javaRole] isEqualToString:@"combobox"] ) { 667 NSMutableArray *moreNames = 668 [[NSMutableArray alloc] initWithCapacity: [names count] + 1]; 669 [moreNames addObjectsFromArray: names]; 670 [moreNames addObject:NSAccessibilityValueAttribute]; 671 return moreNames; 672 } 673 return names; 674 675 } // end @synchronized 676 677 #ifdef JAVA_AX_DEBUG 678 NSLog(@"Warning in %s: could not find attribute names for role: %@", __FUNCTION__, [self javaRole]); 679 #endif 680 681 return nil; 682 } 683 684 // -- accessibility attributes -- 685 686 - (BOOL)accessibilityShouldUseUniqueId { 687 return YES; 688 } 689 690 - (BOOL)accessibilitySupportsOverriddenAttributes { 691 return YES; 692 } 693 694 695 // generic getters & setters 696 // cmcnote: it would make more sense if these generic getters/setters were in JavaAccessibilityUtilities 697 - (id)accessibilityAttributeValue:(NSString *)attribute 698 { 699 AWT_ASSERT_APPKIT_THREAD; 700 701 // turns attribute "NSAccessibilityEnabledAttribute" into getter "accessibilityEnabledAttribute", 702 // calls getter on self 703 return JavaAccessibilityAttributeValue(self, attribute); 704 } 705 706 - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute 707 { 708 AWT_ASSERT_APPKIT_THREAD; 709 710 // turns attribute "NSAccessibilityParentAttribute" into selector "accessibilityIsParentAttributeSettable", 711 // calls selector on self 712 return JavaAccessibilityIsAttributeSettable(self, attribute); 713 } 714 715 - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute 716 { 717 AWT_ASSERT_APPKIT_THREAD; 718 719 if ([self accessibilityIsAttributeSettable:attribute]) { 720 // turns attribute "NSAccessibilityFocusAttribute" into setter "accessibilitySetFocusAttribute", 721 // calls setter on self 722 JavaAccessibilitySetAttributeValue(self, attribute, value); 723 } 724 } 725 726 727 // specific attributes, in alphabetical order a la 728 // http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html 729 730 // Elements that current element contains (NSArray) 731 - (NSArray *)accessibilityChildrenAttribute 732 { 733 JNIEnv* env = [ThreadUtilities getJNIEnv]; 734 NSArray *children = [JavaComponentAccessibility childrenOfParent:self 735 withEnv:env 736 withChildrenCode:JAVA_AX_ALL_CHILDREN 737 allowIgnored:NO]; 738 739 NSArray *value = nil; 740 if ([children count] > 0) { 741 value = children; 742 } 743 744 return value; 745 } 746 747 - (BOOL)accessibilityIsChildrenAttributeSettable 748 { 749 return NO; 750 } 751 752 - (NSUInteger)accessibilityIndexOfChild:(id)child 753 { 754 // Only special-casing for Lists, for now. This allows lists to be accessible, fixing radr://3856139 "JLists are broken". 755 // Will probably want to special-case for Tables when we implement them (radr://3096643 "Accessibility: Table"). 756 // In AppKit, NSMatrixAccessibility (which uses NSAccessibilityListRole), NSTableRowAccessibility, and NSTableViewAccessibility are the 757 // only ones that override the default implementation in NSAccessibility 758 if (![[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityListRole]) { 759 return [super accessibilityIndexOfChild:child]; 760 } 761 762 jint returnValue = 763 JNFCallStaticIntMethod( [ThreadUtilities getJNIEnv], 764 sjm_getAccessibleIndexInParent, 765 ((JavaComponentAccessibility *)child)->fAccessible, 766 ((JavaComponentAccessibility *)child)->fComponent ); 767 return (returnValue == -1) ? NSNotFound : returnValue; 768 } 769 770 // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children. 771 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount { 772 if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) { 773 // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child 774 NSArray *child = [JavaComponentAccessibility childrenOfParent:self withEnv:[ThreadUtilities getJNIEnv] withChildrenCode:(NSInteger)index allowIgnored:NO]; 775 if ([child count] > 0) { 776 return child; 777 } 778 } 779 return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount]; 780 } 781 782 // Flag indicating enabled state of element (NSNumber) 783 - (NSNumber *)accessibilityEnabledAttribute 784 { 785 static JNF_STATIC_MEMBER_CACHE(jm_isEnabled, sjc_CAccessibility, "isEnabled", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z"); 786 787 JNIEnv* env = [ThreadUtilities getJNIEnv]; 788 NSNumber *value = [NSNumber numberWithBool:JNFCallStaticBooleanMethod(env, jm_isEnabled, fAccessible, fComponent)]; // AWT_THREADING Safe (AWTRunLoop) 789 if (value == nil) { 790 NSLog(@"WARNING: %s called on component that has no accessible component: %@", __FUNCTION__, self); 791 } 792 return value; 793 } 794 795 - (BOOL)accessibilityIsEnabledAttributeSettable 796 { 797 return NO; 798 } 799 800 // Flag indicating presence of keyboard focus (NSNumber) 801 - (NSNumber *)accessibilityFocusedAttribute 802 { 803 if ([self accessibilityIsFocusedAttributeSettable]) { 804 return [NSNumber numberWithBool:[self isEqual:[NSApp accessibilityFocusedUIElement]]]; 805 } 806 return [NSNumber numberWithBool:NO]; 807 } 808 809 - (BOOL)accessibilityIsFocusedAttributeSettable 810 { 811 JNIEnv* env = [ThreadUtilities getJNIEnv]; 812 // According to javadoc, a component that is focusable will return true from isFocusTraversable, 813 // as well as having AccessibleState.FOCUSABLE in its AccessibleStateSet. 814 // We use the former heuristic; if the component focus-traversable, add a focused attribute 815 // See also initializeAttributeNamesWithEnv: 816 if (JNFCallStaticBooleanMethod(env, sjm_isFocusTraversable, fAccessible, fComponent)) { // AWT_THREADING Safe (AWTRunLoop) 817 return YES; 818 } 819 820 return NO; 821 } 822 823 - (void)accessibilitySetFocusedAttribute:(id)value 824 { 825 static JNF_STATIC_MEMBER_CACHE(jm_requestFocus, sjc_CAccessibility, "requestFocus", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V"); 826 827 if ([(NSNumber*)value boolValue]) 828 { 829 JNIEnv* env = [ThreadUtilities getJNIEnv]; 830 JNFCallStaticVoidMethod(env, jm_requestFocus, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 831 } 832 } 833 834 // Instance description, such as a help tag string (NSString) 835 - (NSString *)accessibilityHelpAttribute 836 { 837 JNIEnv* env = [ThreadUtilities getJNIEnv]; 838 839 jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleDescription, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 840 if (val == NULL) { 841 return nil; 842 } 843 NSString* str = JNFJavaToNSString(env, val); 844 (*env)->DeleteLocalRef(env, val); 845 return str; 846 } 847 848 - (BOOL)accessibilityIsHelpAttributeSettable 849 { 850 return NO; 851 } 852 853 - (NSValue *)accessibilityIndexAttribute 854 { 855 NSInteger index = fIndex; 856 NSValue *returnValue = [NSValue value:&index withObjCType:@encode(NSInteger)]; 857 return returnValue; 858 } 859 860 - (BOOL)accessibilityIsIndexAttributeSettable 861 { 862 return NO; 863 } 864 865 // Element's maximum value (id) 866 - (id)accessibilityMaxValueAttribute 867 { 868 static JNF_STATIC_MEMBER_CACHE(jm_getMaximumAccessibleValue, sjc_CAccessibility, "getMaximumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;"); 869 870 JNIEnv* env = [ThreadUtilities getJNIEnv]; 871 872 jobject axValue = JNFCallStaticObjectMethod(env, jm_getMaximumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 873 if (axValue == NULL) { 874 return [NSNumber numberWithInt:0]; 875 } 876 NSNumber* num = JNFJavaToNSNumber(env, axValue); 877 (*env)->DeleteLocalRef(env, axValue); 878 return num; 879 } 880 881 - (BOOL)accessibilityIsMaxValueAttributeSettable 882 { 883 return NO; 884 } 885 886 // Element's minimum value (id) 887 - (id)accessibilityMinValueAttribute 888 { 889 static JNF_STATIC_MEMBER_CACHE(jm_getMinimumAccessibleValue, sjc_CAccessibility, "getMinimumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;"); 890 891 JNIEnv* env = [ThreadUtilities getJNIEnv]; 892 893 jobject axValue = JNFCallStaticObjectMethod(env, jm_getMinimumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 894 if (axValue == NULL) { 895 return [NSNumber numberWithInt:0]; 896 } 897 NSNumber* num = JNFJavaToNSNumber(env, axValue); 898 (*env)->DeleteLocalRef(env, axValue); 899 return num; 900 } 901 902 - (BOOL)accessibilityIsMinValueAttributeSettable 903 { 904 return NO; 905 } 906 907 - (id)accessibilityOrientationAttribute 908 { 909 JNIEnv* env = [ThreadUtilities getJNIEnv]; 910 jobject axContext = [self axContextWithEnv:env]; 911 912 // cmcnote - should batch these two calls into one that returns an array of two bools, one for vertical and one for horiz 913 if (isVertical(env, axContext, fComponent)) { 914 (*env)->DeleteLocalRef(env, axContext); 915 return NSAccessibilityVerticalOrientationValue; 916 } 917 918 if (isHorizontal(env, axContext, fComponent)) { 919 (*env)->DeleteLocalRef(env, axContext); 920 return NSAccessibilityHorizontalOrientationValue; 921 } 922 923 (*env)->DeleteLocalRef(env, axContext); 924 return nil; 925 } 926 927 - (BOOL)accessibilityIsOrientationAttributeSettable 928 { 929 return NO; 930 } 931 932 // Element containing current element (id) 933 - (id)accessibilityParentAttribute 934 { 935 return NSAccessibilityUnignoredAncestor([self parent]); 936 } 937 938 - (BOOL)accessibilityIsParentAttributeSettable 939 { 940 return NO; 941 } 942 943 // Screen position of element's lower-left corner in lower-left relative screen coordinates (NSValue) 944 - (NSValue *)accessibilityPositionAttribute 945 { 946 JNIEnv* env = [ThreadUtilities getJNIEnv]; 947 jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 948 949 // NSAccessibility wants the bottom left point of the object in 950 // bottom left based screen coords 951 952 // Get the java screen coords, and make a NSPoint of the bottom left of the AxComponent. 953 NSSize size = getAxComponentSize(env, axComponent, fComponent); 954 NSPoint point = getAxComponentLocationOnScreen(env, axComponent, fComponent); 955 (*env)->DeleteLocalRef(env, axComponent); 956 957 point.y += size.height; 958 959 // Now make it into Cocoa screen coords. 960 point.y = [[[[self view] window] screen] frame].size.height - point.y; 961 962 return [NSValue valueWithPoint:point]; 963 } 964 965 - (BOOL)accessibilityIsPositionAttributeSettable 966 { 967 // 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 968 // We *could* make use of Java's AccessibleComponent.setLocation() method. Investigate. radr://3953869 969 return NO; 970 } 971 972 // Element type, such as NSAccessibilityRadioButtonRole (NSString). See the role table 973 // at http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html 974 - (NSString *)accessibilityRoleAttribute 975 { 976 if (fNSRole == nil) { 977 NSString *javaRole = [self javaRole]; 978 fNSRole = [sRoles objectForKey:javaRole]; 979 // The sRoles NSMutableDictionary maps popupmenu to Mac's popup button. 980 // JComboBox behavior currently relies on this. However this is not the 981 // proper mapping for a JPopupMenu so fix that. 982 if ( [javaRole isEqualToString:@"popupmenu"] && 983 ![[[self parent] javaRole] isEqualToString:@"combobox"] ) { 984 fNSRole = NSAccessibilityMenuRole; 985 } 986 if (fNSRole == nil) { 987 // this component has assigned itself a custom AccessibleRole not in the sRoles array 988 fNSRole = javaRole; 989 } 990 [fNSRole retain]; 991 } 992 return fNSRole; 993 } 994 995 - (BOOL)accessibilityIsRoleAttributeSettable 996 { 997 return NO; 998 } 999 1000 // Localized, user-readable description of role, such as radio button (NSString) 1001 - (NSString *)accessibilityRoleDescriptionAttribute 1002 { 1003 // first ask AppKit for its accessible role description for a given AXRole 1004 NSString *value = NSAccessibilityRoleDescription([self accessibilityRoleAttribute], nil); 1005 1006 if (value == nil) { 1007 // query java if necessary 1008 static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleRoleDisplayString, sjc_CAccessibility, "getAccessibleRoleDisplayString", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;"); 1009 1010 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1011 1012 jobject axRole = JNFCallStaticObjectMethod(env, jm_getAccessibleRoleDisplayString, fAccessible, fComponent); 1013 if (axRole != NULL) { 1014 value = JNFJavaToNSString(env, axRole); 1015 (*env)->DeleteLocalRef(env, axRole); 1016 } else { 1017 value = @"unknown"; 1018 } 1019 } 1020 1021 return value; 1022 } 1023 1024 - (BOOL)accessibilityIsRoleDescriptionAttributeSettable 1025 { 1026 return NO; 1027 } 1028 1029 // Currently selected children (NSArray) 1030 - (NSArray *)accessibilitySelectedChildrenAttribute 1031 { 1032 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1033 NSArray *selectedChildren = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_SELECTED_CHILDREN allowIgnored:NO]; 1034 if ([selectedChildren count] > 0) { 1035 return selectedChildren; 1036 } 1037 1038 return nil; 1039 } 1040 1041 - (BOOL)accessibilityIsSelectedChildrenAttributeSettable 1042 { 1043 return NO; // cmcnote: actually it should be. so need to write accessibilitySetSelectedChildrenAttribute also 1044 } 1045 1046 - (NSNumber *)accessibilitySelectedAttribute 1047 { 1048 return [NSNumber numberWithBool:[self isSelected:[ThreadUtilities getJNIEnv]]]; 1049 } 1050 1051 - (BOOL)accessibilityIsSelectedAttributeSettable 1052 { 1053 if ([self isSelectable:[ThreadUtilities getJNIEnv]]) { 1054 return YES; 1055 } else { 1056 return NO; 1057 } 1058 } 1059 1060 - (void)accessibilitySetSelectedAttribute:(id)value 1061 { 1062 static JNF_STATIC_MEMBER_CACHE( jm_requestSelection, 1063 sjc_CAccessibility, 1064 "requestSelection", 1065 "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V" ); 1066 1067 if ([(NSNumber*)value boolValue]) { 1068 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1069 JNFCallStaticVoidMethod(env, jm_requestSelection, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 1070 } 1071 } 1072 1073 // Element size (NSValue) 1074 - (NSValue *)accessibilitySizeAttribute { 1075 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1076 jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 1077 NSValue* size = [NSValue valueWithSize:getAxComponentSize(env, axComponent, fComponent)]; 1078 (*env)->DeleteLocalRef(env, axComponent); 1079 return size; 1080 } 1081 1082 - (BOOL)accessibilityIsSizeAttributeSettable 1083 { 1084 // SIZE is settable in windows if [self styleMask] & NSResizableWindowMask - but windows are heavyweight so we're ok here 1085 // SIZE is settable in columns if [[self tableValue] allowsColumnResizing - haven't dealt with columns yet 1086 return NO; 1087 } 1088 1089 // Element subrole type, such as NSAccessibilityTableRowSubrole (NSString). See the subrole attribute table at 1090 // http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html 1091 - (NSString *)accessibilitySubroleAttribute 1092 { 1093 NSString *value = nil; 1094 if ([[self javaRole] isEqualToString:@"passwordtext"]) { 1095 value = NSAccessibilitySecureTextFieldSubrole; 1096 } 1097 /* 1098 // other subroles. TableRow and OutlineRow may be relevant to us 1099 NSAccessibilityCloseButtonSubrole // no, heavyweight window takes care of this 1100 NSAccessibilityMinimizeButtonSubrole // " 1101 NSAccessibilityOutlineRowSubrole // maybe? 1102 NSAccessibilitySecureTextFieldSubrole // currently used 1103 NSAccessibilityTableRowSubrole // maybe? 1104 NSAccessibilityToolbarButtonSubrole // maybe? 1105 NSAccessibilityUnknownSubrole 1106 NSAccessibilityZoomButtonSubrole // no, heavyweight window takes care of this 1107 NSAccessibilityStandardWindowSubrole// no, heavyweight window takes care of this 1108 NSAccessibilityDialogSubrole // maybe? 1109 NSAccessibilitySystemDialogSubrole // no 1110 NSAccessibilityFloatingWindowSubrole // in 1.5 if we implement these, heavyweight will take care of them anyway 1111 NSAccessibilitySystemFloatingWindowSubrole 1112 NSAccessibilityIncrementArrowSubrole // no 1113 NSAccessibilityDecrementArrowSubrole // no 1114 NSAccessibilityIncrementPageSubrole // no 1115 NSAccessibilityDecrementPageSubrole // no 1116 NSAccessibilitySearchFieldSubrole //no 1117 */ 1118 return value; 1119 } 1120 1121 - (BOOL)accessibilityIsSubroleAttributeSettable 1122 { 1123 return NO; 1124 } 1125 1126 // Title of element, such as button text (NSString) 1127 - (NSString *)accessibilityTitleAttribute 1128 { 1129 // Return empty string for labels, since their value and tile end up being the same thing and this leads to repeated text. 1130 if ([[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityStaticTextRole]) { 1131 return @""; 1132 } 1133 1134 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1135 1136 jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleName, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 1137 if (val == NULL) { 1138 return nil; 1139 } 1140 NSString* str = JNFJavaToNSString(env, val); 1141 (*env)->DeleteLocalRef(env, val); 1142 return str; 1143 } 1144 1145 - (BOOL)accessibilityIsTitleAttributeSettable 1146 { 1147 return NO; 1148 } 1149 1150 - (NSWindow *)accessibilityTopLevelUIElementAttribute 1151 { 1152 return [self window]; 1153 } 1154 1155 - (BOOL)accessibilityIsTopLevelUIElementAttributeSettable 1156 { 1157 return NO; 1158 } 1159 1160 // Element's value (id) 1161 // note that the appKit meaning of "accessibilityValue" is different from the java 1162 // meaning of "accessibleValue", which is specific to numerical values 1163 // (https://docs.oracle.com/javase/8/docs/api/javax/accessibility/AccessibleValue.html#setCurrentAccessibleValue-java.lang.Number-) 1164 - (id)accessibilityValueAttribute 1165 { 1166 static JNF_STATIC_MEMBER_CACHE(jm_getCurrentAccessibleValue, sjc_CAccessibility, "getCurrentAccessibleValue", "(Ljavax/accessibility/AccessibleValue;Ljava/awt/Component;)Ljava/lang/Number;"); 1167 1168 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1169 1170 // Need to handle popupmenus differently. 1171 // 1172 // At least for now don't handle combo box menus. 1173 // This may change when later fixing issues which currently 1174 // exist for combo boxes, but for now the following is only 1175 // for JPopupMenus, not for combobox menus. 1176 id parent = [self parent]; 1177 if ( [[self javaRole] isEqualToString:@"popupmenu"] && 1178 ![[parent javaRole] isEqualToString:@"combobox"] ) { 1179 NSArray *children = 1180 [JavaComponentAccessibility childrenOfParent:self 1181 withEnv:env 1182 withChildrenCode:JAVA_AX_ALL_CHILDREN 1183 allowIgnored:YES]; 1184 if ([children count] > 0) { 1185 // handle case of AXMenuItem 1186 // need to ask menu what is selected 1187 NSArray *selectedChildrenOfMenu = 1188 [self accessibilitySelectedChildrenAttribute]; 1189 JavaComponentAccessibility *selectedMenuItem = 1190 [selectedChildrenOfMenu objectAtIndex:0]; 1191 if (selectedMenuItem != nil) { 1192 jobject itemValue = 1193 JNFCallStaticObjectMethod( env, 1194 sjm_getAccessibleName, 1195 selectedMenuItem->fAccessible, 1196 selectedMenuItem->fComponent ); // AWT_THREADING Safe (AWTRunLoop) 1197 if (itemValue == NULL) { 1198 return nil; 1199 } 1200 NSString* itemString = JNFJavaToNSString(env, itemValue); 1201 (*env)->DeleteLocalRef(env, itemValue); 1202 return itemString; 1203 } else { 1204 return nil; 1205 } 1206 } 1207 } 1208 1209 // ask Java for the component's accessibleValue. In java, the "accessibleValue" just means a numerical value 1210 // a text value is taken care of in JavaTextAccessibility 1211 1212 // cmcnote should coalesce these calls into one java call 1213 NSNumber *num = nil; 1214 jobject axValue = JNFCallStaticObjectMethod(env, sjm_getAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) 1215 if (axValue != NULL) { 1216 jobject str = JNFCallStaticObjectMethod(env, jm_getCurrentAccessibleValue, axValue, fComponent); 1217 if (str != NULL) { 1218 num = JNFJavaToNSNumber(env, str); // AWT_THREADING Safe (AWTRunLoop) 1219 (*env)->DeleteLocalRef(env, str); 1220 } 1221 (*env)->DeleteLocalRef(env, axValue); 1222 } 1223 if (num == nil) { 1224 num = [NSNumber numberWithInt:0]; 1225 } 1226 return num; 1227 } 1228 1229 - (BOOL)accessibilityIsValueAttributeSettable 1230 { 1231 // according ot AppKit sources, in general the value attribute is not settable, except in the cases 1232 // of an NSScroller, an NSSplitView, and text that's both enabled & editable 1233 BOOL isSettable = NO; 1234 NSString *role = [self accessibilityRoleAttribute]; 1235 1236 if ([role isEqualToString:NSAccessibilityScrollBarRole] || // according to NSScrollerAccessibility 1237 [role isEqualToString:NSAccessibilitySplitGroupRole] ) // according to NSSplitViewAccessibility 1238 { 1239 isSettable = YES; 1240 } 1241 return isSettable; 1242 } 1243 1244 - (void)accessibilitySetValueAttribute:(id)value 1245 { 1246 #ifdef JAVA_AX_DEBUG 1247 NSLog(@"Not yet implemented: %s\n", __FUNCTION__); // radr://3954018 1248 #endif 1249 } 1250 1251 1252 // Child elements that are visible (NSArray) 1253 - (NSArray *)accessibilityVisibleChildrenAttribute 1254 { 1255 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1256 NSArray *visibleChildren = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_VISIBLE_CHILDREN allowIgnored:NO]; 1257 if ([visibleChildren count] <= 0) return nil; 1258 return visibleChildren; 1259 } 1260 1261 - (BOOL)accessibilityIsVisibleChildrenAttributeSettable 1262 { 1263 return NO; 1264 } 1265 1266 // Window containing current element (id) 1267 - (id)accessibilityWindowAttribute 1268 { 1269 return [self window]; 1270 } 1271 1272 - (BOOL)accessibilityIsWindowAttributeSettable 1273 { 1274 return NO; 1275 } 1276 1277 1278 // -- accessibility actions -- 1279 - (NSArray *)accessibilityActionNames 1280 { 1281 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1282 return [[self getActions:env] allKeys]; 1283 } 1284 1285 - (NSString *)accessibilityActionDescription:(NSString *)action 1286 { 1287 AWT_ASSERT_APPKIT_THREAD; 1288 1289 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1290 return [(id <JavaAccessibilityAction>)[[self getActions:env] objectForKey:action] getDescription]; 1291 } 1292 1293 - (void)accessibilityPerformAction:(NSString *)action 1294 { 1295 AWT_ASSERT_APPKIT_THREAD; 1296 1297 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1298 [(id <JavaAccessibilityAction>)[[self getActions:env] objectForKey:action] perform]; 1299 } 1300 1301 1302 // -- misc accessibility -- 1303 - (BOOL)accessibilityIsIgnored 1304 { 1305 #ifdef JAVA_AX_NO_IGNORES 1306 return NO; 1307 #else 1308 return [[self accessibilityRoleAttribute] isEqualToString:JavaAccessibilityIgnore]; 1309 #endif /* JAVA_AX_NO_IGNORES */ 1310 } 1311 1312 - (id)accessibilityHitTest:(NSPoint)point withEnv:(JNIEnv *)env 1313 { 1314 static JNF_CLASS_CACHE(jc_Container, "java/awt/Container"); 1315 static JNF_STATIC_MEMBER_CACHE(jm_accessibilityHitTest, sjc_CAccessibility, "accessibilityHitTest", "(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;"); 1316 1317 // Make it into java screen coords 1318 point.y = [[[[self view] window] screen] frame].size.height - point.y; 1319 1320 jobject jparent = fComponent; 1321 1322 id value = nil; 1323 if (JNFIsInstanceOf(env, jparent, &jc_Container)) { 1324 jobject jaccessible = JNFCallStaticObjectMethod(env, jm_accessibilityHitTest, jparent, (jfloat)point.x, (jfloat)point.y); // AWT_THREADING Safe (AWTRunLoop) 1325 if (jaccessible != NULL) { 1326 value = [JavaComponentAccessibility createWithAccessible:jaccessible withEnv:env withView:fView]; 1327 (*env)->DeleteLocalRef(env, jaccessible); 1328 } 1329 } 1330 1331 if (value == nil) { 1332 value = self; 1333 } 1334 1335 if ([value accessibilityIsIgnored]) { 1336 value = NSAccessibilityUnignoredAncestor(value); 1337 } 1338 1339 #ifdef JAVA_AX_DEBUG 1340 NSLog(@"%s: %@", __FUNCTION__, value); 1341 #endif 1342 return value; 1343 } 1344 1345 - (id)accessibilityFocusedUIElement 1346 { 1347 static JNF_STATIC_MEMBER_CACHE(jm_getFocusOwner, sjc_CAccessibility, "getFocusOwner", "(Ljava/awt/Component;)Ljavax/accessibility/Accessible;"); 1348 1349 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1350 id value = nil; 1351 1352 NSWindow* hostWindow = [[self->fView window] retain]; 1353 jobject focused = JNFCallStaticObjectMethod(env, jm_getFocusOwner, fComponent); // AWT_THREADING Safe (AWTRunLoop) 1354 [hostWindow release]; 1355 1356 if (focused != NULL) { 1357 if (JNFIsInstanceOf(env, focused, &sjc_Accessible)) { 1358 value = [JavaComponentAccessibility createWithAccessible:focused withEnv:env withView:fView]; 1359 } 1360 (*env)->DeleteLocalRef(env, focused); 1361 } 1362 1363 if (value == nil) { 1364 value = self; 1365 } 1366 #ifdef JAVA_AX_DEBUG 1367 NSLog(@"%s: %@", __FUNCTION__, value); 1368 #endif 1369 return value; 1370 } 1371 1372 @end 1373 1374 /* 1375 * Class: sun_lwawt_macosx_CAccessibility 1376 * Method: focusChanged 1377 * Signature: ()V 1378 */ 1379 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessibility_focusChanged 1380 (JNIEnv *env, jobject jthis) 1381 { 1382 JNF_COCOA_ENTER(env); 1383 [ThreadUtilities performOnMainThread:@selector(postFocusChanged:) on:[JavaComponentAccessibility class] withObject:nil waitUntilDone:NO]; 1384 JNF_COCOA_EXIT(env); 1385 } 1386 1387 /* 1388 * Class: sun_lwawt_macosx_CAccessible 1389 * Method: valueChanged 1390 * Signature: (I)V 1391 */ 1392 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_valueChanged 1393 (JNIEnv *env, jclass jklass, jlong element) 1394 { 1395 JNF_COCOA_ENTER(env); 1396 [ThreadUtilities performOnMainThread:@selector(postValueChanged) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO]; 1397 JNF_COCOA_EXIT(env); 1398 } 1399 1400 /* 1401 * Class: sun_lwawt_macosx_CAccessible 1402 * Method: selectedTextChanged 1403 * Signature: (I)V 1404 */ 1405 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectedTextChanged 1406 (JNIEnv *env, jclass jklass, jlong element) 1407 { 1408 JNF_COCOA_ENTER(env); 1409 [ThreadUtilities performOnMainThread:@selector(postSelectedTextChanged) 1410 on:(JavaComponentAccessibility *)jlong_to_ptr(element) 1411 withObject:nil 1412 waitUntilDone:NO]; 1413 JNF_COCOA_EXIT(env); 1414 } 1415 1416 /* 1417 * Class: sun_lwawt_macosx_CAccessible 1418 * Method: selectionChanged 1419 * Signature: (I)V 1420 */ 1421 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectionChanged 1422 (JNIEnv *env, jclass jklass, jlong element) 1423 { 1424 JNF_COCOA_ENTER(env); 1425 [ThreadUtilities performOnMainThread:@selector(postSelectionChanged) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO]; 1426 JNF_COCOA_EXIT(env); 1427 } 1428 1429 /* 1430 * Class: sun_lwawt_macosx_CAccessible 1431 * Method: menuOpened 1432 * Signature: (I)V 1433 */ 1434 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuOpened 1435 (JNIEnv *env, jclass jklass, jlong element) 1436 { 1437 JNF_COCOA_ENTER(env); 1438 [ThreadUtilities performOnMainThread:@selector(postMenuOpened) 1439 on:(JavaComponentAccessibility *)jlong_to_ptr(element) 1440 withObject:nil 1441 waitUntilDone:NO]; 1442 JNF_COCOA_EXIT(env); 1443 } 1444 1445 /* 1446 * Class: sun_lwawt_macosx_CAccessible 1447 * Method: menuClosed 1448 * Signature: (I)V 1449 */ 1450 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuClosed 1451 (JNIEnv *env, jclass jklass, jlong element) 1452 { 1453 JNF_COCOA_ENTER(env); 1454 [ThreadUtilities performOnMainThread:@selector(postMenuClosed) 1455 on:(JavaComponentAccessibility *)jlong_to_ptr(element) 1456 withObject:nil 1457 waitUntilDone:NO]; 1458 JNF_COCOA_EXIT(env); 1459 } 1460 1461 /* 1462 * Class: sun_lwawt_macosx_CAccessible 1463 * Method: menuItemSelected 1464 * Signature: (I)V 1465 */ 1466 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuItemSelected 1467 (JNIEnv *env, jclass jklass, jlong element) 1468 { 1469 JNF_COCOA_ENTER(env); 1470 [ThreadUtilities performOnMainThread:@selector(postMenuItemSelected) 1471 on:(JavaComponentAccessibility *)jlong_to_ptr(element) 1472 withObject:nil 1473 waitUntilDone:NO]; 1474 JNF_COCOA_EXIT(env); 1475 } 1476 1477 /* 1478 * Class: sun_lwawt_macosx_CAccessible 1479 * Method: unregisterFromCocoaAXSystem 1480 * Signature: (I)V 1481 */ 1482 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_unregisterFromCocoaAXSystem 1483 (JNIEnv *env, jclass jklass, jlong element) 1484 { 1485 JNF_COCOA_ENTER(env); 1486 [ThreadUtilities performOnMainThread:@selector(unregisterFromCocoaAXSystem) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO]; 1487 JNF_COCOA_EXIT(env); 1488 } 1489 1490 @implementation TabGroupAccessibility 1491 1492 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole 1493 { 1494 self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole]; 1495 if (self) { 1496 _numTabs = -1; //flag for uninitialized numTabs 1497 } 1498 return self; 1499 } 1500 1501 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env 1502 { 1503 NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env]; 1504 1505 [names addObject:NSAccessibilityTabsAttribute]; 1506 [names addObject:NSAccessibilityContentsAttribute]; 1507 [names addObject:NSAccessibilityValueAttribute]; 1508 1509 return names; 1510 } 1511 1512 - (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext 1513 { 1514 NSArray *tabs = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; 1515 1516 // Looking at the JTabbedPane sources, there is always one AccessibleSelection. 1517 jobject selAccessible = getAxContextSelection(env, axContext, 0, fComponent); 1518 if (selAccessible == NULL) return nil; 1519 1520 // Go through the tabs and find selAccessible 1521 _numTabs = [tabs count]; 1522 JavaComponentAccessibility *aTab; 1523 NSInteger i; 1524 for (i = 0; i < _numTabs; i++) { 1525 aTab = (JavaComponentAccessibility *)[tabs objectAtIndex:i]; 1526 if ([aTab isAccessibleWithEnv:env forAccessible:selAccessible]) { 1527 (*env)->DeleteLocalRef(env, selAccessible); 1528 return aTab; 1529 } 1530 } 1531 (*env)->DeleteLocalRef(env, selAccessible); 1532 return nil; 1533 } 1534 1535 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored 1536 { 1537 jobjectArray jtabsAndRoles = (jobjectArray)JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, fAccessible, fComponent, whichTabs, allowIgnored); // AWT_THREADING Safe (AWTRunLoop) 1538 if(jtabsAndRoles == NULL) return nil; 1539 1540 jsize arrayLen = (*env)->GetArrayLength(env, jtabsAndRoles); 1541 if (arrayLen == 0) { 1542 (*env)->DeleteLocalRef(env, jtabsAndRoles); 1543 return nil; 1544 } 1545 NSMutableArray *tabs = [NSMutableArray arrayWithCapacity:(arrayLen/2)]; 1546 1547 // 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 1548 jobject jtabJavaRole = (*env)->GetObjectArrayElement(env, jtabsAndRoles, 1); // the array entries alternate between tab/role, starting with tab. so the first role is entry 1. 1549 if (jtabJavaRole == NULL) { 1550 (*env)->DeleteLocalRef(env, jtabsAndRoles); 1551 return nil; 1552 } 1553 jobject jkey = JNFGetObjectField(env, jtabJavaRole, sjf_key); 1554 NSString *tabJavaRole = JNFJavaToNSString(env, jkey); 1555 (*env)->DeleteLocalRef(env, jkey); 1556 1557 NSInteger i; 1558 NSUInteger tabIndex = (whichTabs >= 0) ? whichTabs : 0; // if we're getting one particular child, make sure to set its index correctly 1559 for(i = 0; i < arrayLen; i+=2) { 1560 jobject jtab = (*env)->GetObjectArrayElement(env, jtabsAndRoles, i); 1561 JavaComponentAccessibility *tab = [[[TabGroupControlAccessibility alloc] initWithParent:self withEnv:env withAccessible:jtab withIndex:tabIndex withTabGroup:axContext withView:[self view] withJavaRole:tabJavaRole] autorelease]; 1562 (*env)->DeleteLocalRef(env, jtab); 1563 [tabs addObject:tab]; 1564 tabIndex++; 1565 } 1566 (*env)->DeleteLocalRef(env, jtabsAndRoles); 1567 return tabs; 1568 } 1569 1570 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored 1571 { 1572 // Contents are the children of the selected tab. 1573 id currentTab = [self currentTabWithEnv:env withAxContext:axContext]; 1574 if (currentTab == nil) return nil; 1575 1576 NSArray *contents = [JavaComponentAccessibility childrenOfParent:currentTab withEnv:env withChildrenCode:whichTabs allowIgnored:allowIgnored]; 1577 if ([contents count] <= 0) return nil; 1578 return contents; 1579 } 1580 1581 - (id) accessibilityTabsAttribute 1582 { 1583 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1584 jobject axContext = [self axContextWithEnv:env]; 1585 id tabs = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; 1586 (*env)->DeleteLocalRef(env, axContext); 1587 return tabs; 1588 } 1589 1590 - (BOOL)accessibilityIsTabsAttributeSettable 1591 { 1592 return NO; //cmcnote: not sure. 1593 } 1594 1595 - (NSInteger)numTabs 1596 { 1597 if (_numTabs == -1) { 1598 _numTabs = [[self accessibilityTabsAttribute] count]; 1599 } 1600 return _numTabs; 1601 } 1602 1603 - (NSArray *) accessibilityContentsAttribute 1604 { 1605 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1606 jobject axContext = [self axContextWithEnv:env]; 1607 NSArray* cont = [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; 1608 (*env)->DeleteLocalRef(env, axContext); 1609 return cont; 1610 } 1611 1612 - (BOOL)accessibilityIsContentsAttributeSettable 1613 { 1614 return NO; 1615 } 1616 1617 // axValue is the currently selected tab 1618 -(id) accessibilityValueAttribute 1619 { 1620 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1621 jobject axContext = [self axContextWithEnv:env]; 1622 id val = [self currentTabWithEnv:env withAxContext:axContext]; 1623 (*env)->DeleteLocalRef(env, axContext); 1624 return val; 1625 } 1626 1627 - (BOOL)accessibilityIsValueAttributeSettable 1628 { 1629 return YES; 1630 } 1631 1632 - (void)accessibilitySetValueAttribute:(id)value //cmcnote: not certain this is ever actually called. investigate. 1633 { 1634 // set the current tab 1635 NSNumber *number = (NSNumber *)value; 1636 if (![number boolValue]) return; 1637 1638 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1639 jobject axContext = [self axContextWithEnv:env]; 1640 setAxContextSelection(env, axContext, fIndex, fComponent); 1641 (*env)->DeleteLocalRef(env, axContext); 1642 } 1643 1644 - (NSArray *)accessibilityChildrenAttribute 1645 { 1646 //children = AXTabs + AXContents 1647 NSArray *tabs = [self accessibilityTabsAttribute]; 1648 NSArray *contents = [self accessibilityContentsAttribute]; 1649 1650 NSMutableArray *children = [NSMutableArray arrayWithCapacity:[tabs count] + [contents count]]; 1651 [children addObjectsFromArray:tabs]; 1652 [children addObjectsFromArray:contents]; 1653 1654 return (NSArray *)children; 1655 } 1656 1657 // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children. 1658 // See similar optimization in JavaComponentAccessibility. We have to extend the base implementation here, since 1659 // children of tabs are AXTabs + AXContents 1660 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount { 1661 NSArray *result = nil; 1662 if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) { 1663 // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child 1664 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1665 jobject axContext = [self axContextWithEnv:env]; 1666 1667 //children = AXTabs + AXContents 1668 NSArray *children = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:index allowIgnored:NO]; // first look at the tabs 1669 if ([children count] > 0) { 1670 result = children; 1671 } else { 1672 children= [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:(index-[self numTabs]) allowIgnored:NO]; 1673 if ([children count] > 0) { 1674 result = children; 1675 } 1676 } 1677 (*env)->DeleteLocalRef(env, axContext); 1678 } else { 1679 result = [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount]; 1680 } 1681 return result; 1682 } 1683 1684 @end 1685 1686 1687 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component); 1688 1689 @implementation TabGroupControlAccessibility 1690 1691 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole 1692 { 1693 self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole]; 1694 if (self) { 1695 if (tabGroup != NULL) { 1696 fTabGroupAxContext = JNFNewWeakGlobalRef(env, tabGroup); 1697 } else { 1698 fTabGroupAxContext = NULL; 1699 } 1700 } 1701 return self; 1702 } 1703 1704 - (void)dealloc 1705 { 1706 JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; 1707 1708 if (fTabGroupAxContext != NULL) { 1709 JNFDeleteWeakGlobalRef(env, fTabGroupAxContext); 1710 fTabGroupAxContext = NULL; 1711 } 1712 1713 [super dealloc]; 1714 } 1715 1716 - (id)accessibilityValueAttribute 1717 { 1718 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1719 jobject axContext = [self axContextWithEnv:env]; 1720 jobject selAccessible = getAxContextSelection(env, [self tabGroup], fIndex, fComponent); 1721 1722 // Returns the current selection of the page tab list 1723 id val = [NSNumber numberWithBool:ObjectEquals(env, axContext, selAccessible, fComponent)]; 1724 1725 (*env)->DeleteLocalRef(env, selAccessible); 1726 (*env)->DeleteLocalRef(env, axContext); 1727 return val; 1728 } 1729 1730 - (void)getActionsWithEnv:(JNIEnv *)env 1731 { 1732 TabGroupAction *action = [[TabGroupAction alloc] initWithEnv:env withTabGroup:[self tabGroup] withIndex:fIndex withComponent:fComponent]; 1733 [fActions setObject:action forKey:NSAccessibilityPressAction]; 1734 [action release]; 1735 } 1736 1737 - (jobject)tabGroup 1738 { 1739 if (fTabGroupAxContext == NULL) { 1740 JNIEnv* env = [ThreadUtilities getJNIEnv]; 1741 jobject tabGroupAxContext = [(JavaComponentAccessibility *)[self parent] axContextWithEnv:env]; 1742 fTabGroupAxContext = JNFNewWeakGlobalRef(env, tabGroupAxContext); 1743 (*env)->DeleteLocalRef(env, tabGroupAxContext); 1744 } 1745 return fTabGroupAxContext; 1746 } 1747 1748 @end 1749 1750 1751 @implementation ScrollAreaAccessibility 1752 1753 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env 1754 { 1755 NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env]; 1756 1757 [names addObject:NSAccessibilityHorizontalScrollBarAttribute]; 1758 [names addObject:NSAccessibilityVerticalScrollBarAttribute]; 1759 [names addObject:NSAccessibilityContentsAttribute]; 1760 1761 return names; 1762 } 1763 1764 - (id)accessibilityHorizontalScrollBarAttribute 1765 { 1766 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1767 1768 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; 1769 if ([children count] <= 0) return nil; 1770 1771 // The scroll bars are in the children. 1772 JavaComponentAccessibility *aElement; 1773 NSEnumerator *enumerator = [children objectEnumerator]; 1774 while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) { 1775 if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) { 1776 jobject elementAxContext = [aElement axContextWithEnv:env]; 1777 if (isHorizontal(env, elementAxContext, fComponent)) { 1778 (*env)->DeleteLocalRef(env, elementAxContext); 1779 return aElement; 1780 } 1781 (*env)->DeleteLocalRef(env, elementAxContext); 1782 } 1783 } 1784 1785 return nil; 1786 } 1787 1788 - (BOOL)accessibilityIsHorizontalScrollBarAttributeSettable 1789 { 1790 return NO; 1791 } 1792 1793 - (id)accessibilityVerticalScrollBarAttribute 1794 { 1795 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1796 1797 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; 1798 if ([children count] <= 0) return nil; 1799 1800 // The scroll bars are in the children. 1801 NSEnumerator *enumerator = [children objectEnumerator]; 1802 JavaComponentAccessibility *aElement; 1803 while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) { 1804 if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) { 1805 jobject elementAxContext = [aElement axContextWithEnv:env]; 1806 if (isVertical(env, elementAxContext, fComponent)) { 1807 (*env)->DeleteLocalRef(env, elementAxContext); 1808 return aElement; 1809 } 1810 (*env)->DeleteLocalRef(env, elementAxContext); 1811 } 1812 } 1813 1814 return nil; 1815 } 1816 1817 - (BOOL)accessibilityIsVerticalScrollBarAttributeSettable 1818 { 1819 return NO; 1820 } 1821 1822 - (NSArray *)accessibilityContentsAttribute 1823 { 1824 JNIEnv *env = [ThreadUtilities getJNIEnv]; 1825 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; 1826 1827 if ([children count] <= 0) return nil; 1828 NSArray *contents = [NSMutableArray arrayWithCapacity:[children count]]; 1829 1830 // The scroll bars are in the children. children less the scroll bars is the contents 1831 NSEnumerator *enumerator = [children objectEnumerator]; 1832 JavaComponentAccessibility *aElement; 1833 while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) { 1834 if (![[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) { 1835 // no scroll bars in contents 1836 [(NSMutableArray *)contents addObject:aElement]; 1837 } 1838 } 1839 1840 return contents; 1841 } 1842 1843 - (BOOL)accessibilityIsContentsAttributeSettable 1844 { 1845 return NO; 1846 } 1847 1848 @end 1849 1850 /* 1851 * Returns Object.equals for the two items 1852 * This may use LWCToolkit.invokeAndWait(); don't call while holding fLock 1853 * and try to pass a component so the event happens on the correct thread. 1854 */ 1855 static JNF_CLASS_CACHE(sjc_Object, "java/lang/Object"); 1856 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component) 1857 { 1858 static JNF_MEMBER_CACHE(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z"); 1859 1860 if ((a == NULL) && (b == NULL)) return YES; 1861 if ((a == NULL) || (b == NULL)) return NO; 1862 1863 if (pthread_main_np() != 0) { 1864 // If we are on the AppKit thread 1865 static JNF_CLASS_CACHE(sjc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit"); 1866 static JNF_STATIC_MEMBER_CACHE(jm_doEquals, sjc_LWCToolkit, "doEquals", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/awt/Component;)Z"); 1867 return JNFCallStaticBooleanMethod(env, jm_doEquals, a, b, component); // AWT_THREADING Safe (AWTRunLoopMode) 1868 } 1869 1870 return JNFCallBooleanMethod(env, a, jm_equals, b); // AWT_THREADING Safe (!appKit) 1871 }