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