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