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