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