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