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(sjm_getAccessibleComponent, sjc_CAccessibility, "getAccessibleComponent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleComponent;");
58 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleValue, sjc_CAccessibility, "getAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleValue;");
59 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;");
60 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleDescription, sjc_CAccessibility, "getAccessibleDescription", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;");
61 static JNF_STATIC_MEMBER_CACHE(sjm_isFocusTraversable, sjc_CAccessibility, "isFocusTraversable", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z");
62 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleIndexInParent, sjc_CAccessibility, "getAccessibleIndexInParent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)I");
63
64 static JNF_CLASS_CACHE(sjc_CAccessible, "sun/lwawt/macosx/CAccessible");
65
66 static JNF_MEMBER_CACHE(jf_ptr, sjc_CAccessible, "ptr", "J");
67 static JNF_STATIC_MEMBER_CACHE(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;");
68
69 static jobject sAccessibilityClass = NULL;
70
71 // sAttributeNamesForRoleCache holds the names of the attributes to which each java
72 // AccessibleRole responds (see AccessibleRole.java).
73 // This cache is queried before attempting to access a given attribute for a particular role.
74 static NSMutableDictionary *sAttributeNamesForRoleCache = nil;
75 static NSObject *sAttributeNamesLOCK = nil;
76
77 @interface TabGroupAccessibility : JavaComponentAccessibility {
78 NSInteger _numTabs;
79 }
80
81 - (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext;
82 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored;
83 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored;
84 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env;
85
86 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;
87 - (NSArray *)accessibilityChildrenAttribute;
239 return (*env)->IsSameObject(env, accessibility->fAccessible, fAccessible);
240 }
241
242 - (BOOL)isAccessibleWithEnv:(JNIEnv *)env forAccessible:(jobject)accessible
243 {
244 return (*env)->IsSameObject(env, fAccessible, accessible);
245 }
246
247 + (void)initialize
248 {
249 if (sAttributeNamesForRoleCache == nil) {
250 sAttributeNamesLOCK = [[NSObject alloc] init];
251 sAttributeNamesForRoleCache = [[NSMutableDictionary alloc] initWithCapacity:60];
252 }
253
254 if (sRoles == nil) {
255 initializeRoles();
256 }
257
258 if (sAccessibilityClass == NULL) {
259 JNF_STATIC_MEMBER_CACHE(jm_getAccessibility, sjc_CAccessibility, "getAccessibility", "([Ljava/lang/String;)Lsun/lwawt/macosx/CAccessibility;");
260
261 #ifdef JAVA_AX_NO_IGNORES
262 NSArray *ignoredKeys = [NSArray array];
263 #else
264 NSArray *ignoredKeys = [sRoles allKeysForObject:JavaAccessibilityIgnore];
265 #endif
266 jobjectArray result = NULL;
267 jsize count = [ignoredKeys count];
268
269 JNIEnv *env = [ThreadUtilities getJNIEnv];
270
271 static JNF_CLASS_CACHE(jc_String, "java/lang/String");
272 result = JNFNewObjectArray(env, &jc_String, count);
273 if (!result) {
274 NSLog(@"In %s, can't create Java array of String objects", __FUNCTION__);
275 return;
276 }
277
278 NSInteger i;
279 for (i = 0; i < count; i++) {
280 jstring jString = JNFNSToJavaString(env, [ignoredKeys objectAtIndex:i]);
281 (*env)->SetObjectArrayElement(env, result, i, jString);
282 (*env)->DeleteLocalRef(env, jString);
283 }
284
285 sAccessibilityClass = JNFCallStaticObjectMethod(env, jm_getAccessibility, result); // AWT_THREADING Safe (known object)
286 }
287 }
288
289 + (void)postFocusChanged:(id)message
290 {
291 AWT_ASSERT_APPKIT_THREAD;
292 NSAccessibilityPostNotification([NSApp accessibilityFocusedUIElement], NSAccessibilityFocusedUIElementChangedNotification);
293 }
294
295 + (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env {
296 if (JNFIsInstanceOf(env, jaccessible, &sjc_CAccessible)) {
297 return jaccessible;
298 } else if (JNFIsInstanceOf(env, jaccessible, &sjc_Accessible)) {
299 return JNFCallStaticObjectMethod(env, sjm_getCAccessible, jaccessible);
300 }
301 return NULL;
302 }
303
304 + (NSArray *)childrenOfParent:(JavaComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored
305 {
306 if (parent->fAccessible == NULL) return nil;
307 jobjectArray jchildrenAndRoles = (jobjectArray)JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, parent->fAccessible, parent->fComponent, whichChildren, allowIgnored); // AWT_THREADING Safe (AWTRunLoop)
308 if (jchildrenAndRoles == NULL) return nil;
309
310 jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles);
311 NSMutableArray *children = [NSMutableArray arrayWithCapacity:arrayLen/2]; //childrenAndRoles array contains two elements (child, role) for each child
312
313 NSInteger i;
314 NSUInteger childIndex = (whichChildren >= 0) ? whichChildren : 0; // if we're getting one particular child, make sure to set its index correctly
315 for(i = 0; i < arrayLen; i+=2)
316 {
317 jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i);
318 jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1);
319
320 NSString *childJavaRole = nil;
321 if (jchildJavaRole != NULL) {
322 jobject jkey = JNFGetObjectField(env, jchildJavaRole, sjf_key);
323 childJavaRole = JNFJavaToNSString(env, jkey);
324 (*env)->DeleteLocalRef(env, jkey);
325 }
326
327 JavaComponentAccessibility *child = [self createWithParent:parent accessible:jchild role:childJavaRole index:childIndex withEnv:env withView:parent->fView];
328
329 (*env)->DeleteLocalRef(env, jchild);
330 (*env)->DeleteLocalRef(env, jchildJavaRole);
331
332 [children addObject:child];
333 childIndex++;
334 }
335 (*env)->DeleteLocalRef(env, jchildrenAndRoles);
336
337 return children;
338 }
339
340 + (JavaComponentAccessibility *)createWithAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env withView:(NSView *)view
341 {
342 JavaComponentAccessibility *ret = nil;
343 jobject jcomponent = [(AWTView *)view awtComponent:env];
344 jint index = JNFCallStaticIntMethod(env, sjm_getAccessibleIndexInParent, jaccessible, jcomponent);
345 if (index >= 0) {
346 NSString *javaRole = getJavaRole(env, jaccessible, jcomponent);
347 ret = [self createWithAccessible:jaccessible role:javaRole index:index withEnv:env withView:view];
348 }
349 (*env)->DeleteLocalRef(env, jcomponent);
350 return ret;
351 }
352
353 + (JavaComponentAccessibility *) createWithAccessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view
354 {
355 return [self createWithParent:nil accessible:jaccessible role:javaRole index:index withEnv:env withView:view];
356 }
357
358 + (JavaComponentAccessibility *) createWithParent:(JavaComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view
359 {
360 // try to fetch the jCAX from Java, and return autoreleased
361 jobject jCAX = [JavaComponentAccessibility getCAccessible:jaccessible withEnv:env];
362 if (jCAX == NULL) return nil;
363 JavaComponentAccessibility *value = (JavaComponentAccessibility *) jlong_to_ptr(JNFGetLongField(env, jCAX, jf_ptr));
364 if (value != nil) {
365 (*env)->DeleteLocalRef(env, jCAX);
366 return [[value retain] autorelease];
367 }
368
369 // otherwise, create a new instance
370 JavaComponentAccessibility *newChild = nil;
371 if ([javaRole isEqualToString:@"pagetablist"]) {
372 newChild = [TabGroupAccessibility alloc];
373 } else if ([javaRole isEqualToString:@"scrollpane"]) {
374 newChild = [ScrollAreaAccessibility alloc];
375 } else {
376 NSString *nsRole = [sRoles objectForKey:javaRole];
377 if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] || [nsRole isEqualToString:NSAccessibilityTextAreaRole] || [nsRole isEqualToString:NSAccessibilityTextFieldRole]) {
378 newChild = [JavaTextAccessibility alloc];
379 } else {
380 newChild = [JavaComponentAccessibility alloc];
381 }
382 }
383
384 // must init freshly -alloc'd object
385 [newChild initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // must init new instance
386
387 // If creating a JPopupMenu (not a combobox popup list) need to fire menuOpened.
388 // This is the only way to know if the menu is opening; visible state change
389 // can't be caught because the listeners are not set up in time.
390 if ( [javaRole isEqualToString:@"popupmenu"] &&
391 ![[parent javaRole] isEqualToString:@"combobox"] ) {
392 [newChild postMenuOpened];
393 }
394
395 // must hard retain pointer poked into Java object
396 [newChild retain];
397 JNFSetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild));
398 (*env)->DeleteLocalRef(env, jCAX);
399
400 // return autoreleased instance
401 return [newChild autorelease];
402 }
403
404 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
405 {
406 static JNF_STATIC_MEMBER_CACHE(jm_getInitialAttributeStates, sjc_CAccessibility, "getInitialAttributeStates", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)[Z");
407
408 NSMutableArray *attributeNames = [NSMutableArray arrayWithCapacity:20];
409 [attributeNames retain];
410
411 // all elements respond to parent, role, role description, window, topLevelUIElement, help
412 [attributeNames addObject:NSAccessibilityParentAttribute];
413 [attributeNames addObject:NSAccessibilityRoleAttribute];
414 [attributeNames addObject:NSAccessibilityRoleDescriptionAttribute];
415 [attributeNames addObject:NSAccessibilityHelpAttribute];
416
417 // cmcnote: AXMenu usually doesn't respond to window / topLevelUIElement. But menus within a Java app's window
418 // probably should. Should we use some role other than AXMenu / AXMenuBar for Java menus?
419 [attributeNames addObject:NSAccessibilityWindowAttribute];
420 [attributeNames addObject:NSAccessibilityTopLevelUIElementAttribute];
421
422 // set accessible subrole
423 NSString *javaRole = [self javaRole];
424 if (javaRole != nil && [javaRole isEqualToString:@"passwordtext"]) {
425 //cmcnote: should turn this into a constant
426 [attributeNames addObject:NSAccessibilitySubroleAttribute];
427 }
428
429 // Get all the other accessibility attributes states we need in one swell foop.
430 // javaRole isn't pulled in because we need protected access to AccessibleRole.key
431 jbooleanArray attributeStates = (jbooleanArray)JNFCallStaticObjectMethod(env, jm_getInitialAttributeStates, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
432 if (attributeStates == NULL) return nil;
433 jboolean *attributeStatesArray = (*env)->GetBooleanArrayElements(env, attributeStates, 0);
434 if (attributeStatesArray == NULL) {
435 // Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck.
436 NSLog(@"%s failed calling GetBooleanArrayElements", __FUNCTION__);
437 return nil;
438 }
439
440 // if there's a component, it can be enabled and it has a size/position
441 if (attributeStatesArray[0]) {
442 [attributeNames addObject:NSAccessibilityEnabledAttribute];
443 [attributeNames addObject:NSAccessibilitySizeAttribute];
444 [attributeNames addObject:NSAccessibilityPositionAttribute];
445 }
446
447 // According to javadoc, a component that is focusable will return true from isFocusTraversable,
448 // as well as having AccessibleState.FOCUSABLE in it's AccessibleStateSet.
449 // We use the former heuristic; if the component focus-traversable, add a focused attribute
450 // See also: accessibilityIsFocusedAttributeSettable
451 if (attributeStatesArray[1])
500 // Cleanup
501 (*env)->ReleaseBooleanArrayElements(env, attributeStates, attributeStatesArray, JNI_ABORT);
502
503 return attributeNames;
504 }
505
506 - (NSDictionary *)getActions:(JNIEnv *)env
507 {
508 @synchronized(fActionsLOCK) {
509 if (fActions == nil) {
510 fActions = [[NSMutableDictionary alloc] initWithCapacity:3];
511 [self getActionsWithEnv:env];
512 }
513 }
514
515 return fActions;
516 }
517
518 - (void)getActionsWithEnv:(JNIEnv *)env
519 {
520 static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleAction, sjc_CAccessibility, "getAccessibleAction", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleAction;");
521
522 // On MacOSX, text doesn't have actions, in java it does.
523 // cmcnote: NOT TRUE - Editable text has AXShowMenu. Textfields have AXConfirm. Static text has no actions.
524 jobject axAction = JNFCallStaticObjectMethod(env, jm_getAccessibleAction, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
525 if (axAction != NULL) {
526 //+++gdb NOTE: In MacOSX, there is just a single Action, not multiple. In java,
527 // the first one seems to be the most basic, so this will be used.
528 // cmcnote: NOT TRUE - Sometimes there are multiple actions, eg sliders have AXDecrement AND AXIncrement (radr://3893192)
529 JavaAxAction *action = [[JavaAxAction alloc] initWithEnv:env withAccessibleAction:axAction withIndex:0 withComponent:fComponent];
530 [fActions setObject:action forKey:[self isMenu] ? NSAccessibilityPickAction : NSAccessibilityPressAction];
531 [action release];
532 (*env)->DeleteLocalRef(env, axAction);
533 }
534 }
535
536 - (jobject)axContextWithEnv:(JNIEnv *)env
537 {
538 return getAxContext(env, fAccessible, fComponent);
539 }
540
541 - (id)parent
542 {
543 static JNF_CLASS_CACHE(sjc_Window, "java/awt/Window");
544 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleParent, sjc_CAccessibility, "getAccessibleParent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/Accessible;");
545 static JNF_STATIC_MEMBER_CACHE(sjm_getSwingAccessible, sjc_CAccessible, "getSwingAccessible", "(Ljavax/accessibility/Accessible;)Ljavax/accessibility/Accessible;");
546
547 if(fParent == nil) {
548 JNIEnv* env = [ThreadUtilities getJNIEnv];
549
550 jobject jparent = JNFCallStaticObjectMethod(env, sjm_getAccessibleParent, fAccessible, fComponent);
551
552 if (jparent == NULL) {
553 fParent = fView;
554 } else {
555 AWTView *view = fView;
556 jobject jax = JNFCallStaticObjectMethod(env, sjm_getSwingAccessible, fAccessible);
557
558 if (JNFIsInstanceOf(env, jax, &sjc_Window)) {
559 // In this case jparent is an owner toplevel and we should retrieve its own view
560 view = [AWTView awtView:env ofAccessible:jparent];
561 }
562 if (view != nil) {
563 fParent = [JavaComponentAccessibility createWithAccessible:jparent withEnv:env withView:view];
564 }
565 if (fParent == nil) {
566 fParent = fView;
567 }
568 (*env)->DeleteLocalRef(env, jparent);
569 (*env)->DeleteLocalRef(env, jax );
570 }
571 [fParent retain];
572 }
573 return fParent;
574 }
575
576 - (NSView *)view
577 {
578 return fView;
742 }
743
744 return value;
745 }
746
747 - (BOOL)accessibilityIsChildrenAttributeSettable
748 {
749 return NO;
750 }
751
752 - (NSUInteger)accessibilityIndexOfChild:(id)child
753 {
754 // Only special-casing for Lists, for now. This allows lists to be accessible, fixing radr://3856139 "JLists are broken".
755 // Will probably want to special-case for Tables when we implement them (radr://3096643 "Accessibility: Table").
756 // In AppKit, NSMatrixAccessibility (which uses NSAccessibilityListRole), NSTableRowAccessibility, and NSTableViewAccessibility are the
757 // only ones that override the default implementation in NSAccessibility
758 if (![[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityListRole]) {
759 return [super accessibilityIndexOfChild:child];
760 }
761
762 jint returnValue =
763 JNFCallStaticIntMethod( [ThreadUtilities getJNIEnv],
764 sjm_getAccessibleIndexInParent,
765 ((JavaComponentAccessibility *)child)->fAccessible,
766 ((JavaComponentAccessibility *)child)->fComponent );
767 return (returnValue == -1) ? NSNotFound : returnValue;
768 }
769
770 // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children.
771 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount {
772 if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
773 // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child
774 NSArray *child = [JavaComponentAccessibility childrenOfParent:self withEnv:[ThreadUtilities getJNIEnv] withChildrenCode:(NSInteger)index allowIgnored:NO];
775 if ([child count] > 0) {
776 return child;
777 }
778 }
779 return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
780 }
781
782 // Flag indicating enabled state of element (NSNumber)
783 - (NSNumber *)accessibilityEnabledAttribute
784 {
785 static JNF_STATIC_MEMBER_CACHE(jm_isEnabled, sjc_CAccessibility, "isEnabled", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z");
786
787 JNIEnv* env = [ThreadUtilities getJNIEnv];
788 NSNumber *value = [NSNumber numberWithBool:JNFCallStaticBooleanMethod(env, jm_isEnabled, fAccessible, fComponent)]; // AWT_THREADING Safe (AWTRunLoop)
789 if (value == nil) {
790 NSLog(@"WARNING: %s called on component that has no accessible component: %@", __FUNCTION__, self);
791 }
792 return value;
793 }
794
795 - (BOOL)accessibilityIsEnabledAttributeSettable
796 {
797 return NO;
798 }
799
800 // Flag indicating presence of keyboard focus (NSNumber)
801 - (NSNumber *)accessibilityFocusedAttribute
802 {
803 if ([self accessibilityIsFocusedAttributeSettable]) {
804 return [NSNumber numberWithBool:[self isEqual:[NSApp accessibilityFocusedUIElement]]];
805 }
806 return [NSNumber numberWithBool:NO];
807 }
808
809 - (BOOL)accessibilityIsFocusedAttributeSettable
810 {
811 JNIEnv* env = [ThreadUtilities getJNIEnv];
812 // According to javadoc, a component that is focusable will return true from isFocusTraversable,
813 // as well as having AccessibleState.FOCUSABLE in its AccessibleStateSet.
814 // We use the former heuristic; if the component focus-traversable, add a focused attribute
815 // See also initializeAttributeNamesWithEnv:
816 if (JNFCallStaticBooleanMethod(env, sjm_isFocusTraversable, fAccessible, fComponent)) { // AWT_THREADING Safe (AWTRunLoop)
817 return YES;
818 }
819
820 return NO;
821 }
822
823 - (void)accessibilitySetFocusedAttribute:(id)value
824 {
825 static JNF_STATIC_MEMBER_CACHE(jm_requestFocus, sjc_CAccessibility, "requestFocus", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V");
826
827 if ([(NSNumber*)value boolValue])
828 {
829 JNIEnv* env = [ThreadUtilities getJNIEnv];
830 JNFCallStaticVoidMethod(env, jm_requestFocus, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
831 }
832 }
833
834 // Instance description, such as a help tag string (NSString)
835 - (NSString *)accessibilityHelpAttribute
836 {
837 JNIEnv* env = [ThreadUtilities getJNIEnv];
838
839 jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleDescription, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
840 if (val == NULL) {
841 return nil;
842 }
843 NSString* str = JNFJavaToNSString(env, val);
844 (*env)->DeleteLocalRef(env, val);
845 return str;
846 }
847
848 - (BOOL)accessibilityIsHelpAttributeSettable
849 {
850 return NO;
851 }
852
853 - (NSValue *)accessibilityIndexAttribute
854 {
855 NSInteger index = fIndex;
856 NSValue *returnValue = [NSValue value:&index withObjCType:@encode(NSInteger)];
857 return returnValue;
858 }
859
860 - (BOOL)accessibilityIsIndexAttributeSettable
861 {
862 return NO;
863 }
864
865 // Element's maximum value (id)
866 - (id)accessibilityMaxValueAttribute
867 {
868 static JNF_STATIC_MEMBER_CACHE(jm_getMaximumAccessibleValue, sjc_CAccessibility, "getMaximumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;");
869
870 JNIEnv* env = [ThreadUtilities getJNIEnv];
871
872 jobject axValue = JNFCallStaticObjectMethod(env, jm_getMaximumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
873 if (axValue == NULL) {
874 return [NSNumber numberWithInt:0];
875 }
876 NSNumber* num = JNFJavaToNSNumber(env, axValue);
877 (*env)->DeleteLocalRef(env, axValue);
878 return num;
879 }
880
881 - (BOOL)accessibilityIsMaxValueAttributeSettable
882 {
883 return NO;
884 }
885
886 // Element's minimum value (id)
887 - (id)accessibilityMinValueAttribute
888 {
889 static JNF_STATIC_MEMBER_CACHE(jm_getMinimumAccessibleValue, sjc_CAccessibility, "getMinimumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;");
890
891 JNIEnv* env = [ThreadUtilities getJNIEnv];
892
893 jobject axValue = JNFCallStaticObjectMethod(env, jm_getMinimumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
894 if (axValue == NULL) {
895 return [NSNumber numberWithInt:0];
896 }
897 NSNumber* num = JNFJavaToNSNumber(env, axValue);
898 (*env)->DeleteLocalRef(env, axValue);
899 return num;
900 }
901
902 - (BOOL)accessibilityIsMinValueAttributeSettable
903 {
904 return NO;
905 }
906
907 - (id)accessibilityOrientationAttribute
908 {
909 JNIEnv* env = [ThreadUtilities getJNIEnv];
910 jobject axContext = [self axContextWithEnv:env];
911
912 // cmcnote - should batch these two calls into one that returns an array of two bools, one for vertical and one for horiz
913 if (isVertical(env, axContext, fComponent)) {
914 (*env)->DeleteLocalRef(env, axContext);
915 return NSAccessibilityVerticalOrientationValue;
916 }
917
927 - (BOOL)accessibilityIsOrientationAttributeSettable
928 {
929 return NO;
930 }
931
932 // Element containing current element (id)
933 - (id)accessibilityParentAttribute
934 {
935 return NSAccessibilityUnignoredAncestor([self parent]);
936 }
937
938 - (BOOL)accessibilityIsParentAttributeSettable
939 {
940 return NO;
941 }
942
943 // Screen position of element's lower-left corner in lower-left relative screen coordinates (NSValue)
944 - (NSValue *)accessibilityPositionAttribute
945 {
946 JNIEnv* env = [ThreadUtilities getJNIEnv];
947 jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
948
949 // NSAccessibility wants the bottom left point of the object in
950 // bottom left based screen coords
951
952 // Get the java screen coords, and make a NSPoint of the bottom left of the AxComponent.
953 NSSize size = getAxComponentSize(env, axComponent, fComponent);
954 NSPoint point = getAxComponentLocationOnScreen(env, axComponent, fComponent);
955 (*env)->DeleteLocalRef(env, axComponent);
956
957 point.y += size.height;
958
959 // Now make it into Cocoa screen coords.
960 point.y = [[[[self view] window] screen] frame].size.height - point.y;
961
962 return [NSValue valueWithPoint:point];
963 }
964
965 - (BOOL)accessibilityIsPositionAttributeSettable
966 {
967 // 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
988 fNSRole = javaRole;
989 }
990 [fNSRole retain];
991 }
992 return fNSRole;
993 }
994
995 - (BOOL)accessibilityIsRoleAttributeSettable
996 {
997 return NO;
998 }
999
1000 // Localized, user-readable description of role, such as radio button (NSString)
1001 - (NSString *)accessibilityRoleDescriptionAttribute
1002 {
1003 // first ask AppKit for its accessible role description for a given AXRole
1004 NSString *value = NSAccessibilityRoleDescription([self accessibilityRoleAttribute], nil);
1005
1006 if (value == nil) {
1007 // query java if necessary
1008 static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleRoleDisplayString, sjc_CAccessibility, "getAccessibleRoleDisplayString", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;");
1009
1010 JNIEnv* env = [ThreadUtilities getJNIEnv];
1011
1012 jobject axRole = JNFCallStaticObjectMethod(env, jm_getAccessibleRoleDisplayString, fAccessible, fComponent);
1013 if (axRole != NULL) {
1014 value = JNFJavaToNSString(env, axRole);
1015 (*env)->DeleteLocalRef(env, axRole);
1016 } else {
1017 value = @"unknown";
1018 }
1019 }
1020
1021 return value;
1022 }
1023
1024 - (BOOL)accessibilityIsRoleDescriptionAttributeSettable
1025 {
1026 return NO;
1027 }
1028
1029 // Currently selected children (NSArray)
1030 - (NSArray *)accessibilitySelectedChildrenAttribute
1031 {
1032 JNIEnv* env = [ThreadUtilities getJNIEnv];
1033 NSArray *selectedChildren = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_SELECTED_CHILDREN allowIgnored:NO];
1034 if ([selectedChildren count] > 0) {
1042 {
1043 return NO; // cmcnote: actually it should be. so need to write accessibilitySetSelectedChildrenAttribute also
1044 }
1045
1046 - (NSNumber *)accessibilitySelectedAttribute
1047 {
1048 return [NSNumber numberWithBool:[self isSelected:[ThreadUtilities getJNIEnv]]];
1049 }
1050
1051 - (BOOL)accessibilityIsSelectedAttributeSettable
1052 {
1053 if ([self isSelectable:[ThreadUtilities getJNIEnv]]) {
1054 return YES;
1055 } else {
1056 return NO;
1057 }
1058 }
1059
1060 - (void)accessibilitySetSelectedAttribute:(id)value
1061 {
1062 static JNF_STATIC_MEMBER_CACHE( jm_requestSelection,
1063 sjc_CAccessibility,
1064 "requestSelection",
1065 "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V" );
1066
1067 if ([(NSNumber*)value boolValue]) {
1068 JNIEnv* env = [ThreadUtilities getJNIEnv];
1069 JNFCallStaticVoidMethod(env, jm_requestSelection, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1070 }
1071 }
1072
1073 // Element size (NSValue)
1074 - (NSValue *)accessibilitySizeAttribute {
1075 JNIEnv* env = [ThreadUtilities getJNIEnv];
1076 jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1077 NSValue* size = [NSValue valueWithSize:getAxComponentSize(env, axComponent, fComponent)];
1078 (*env)->DeleteLocalRef(env, axComponent);
1079 return size;
1080 }
1081
1082 - (BOOL)accessibilityIsSizeAttributeSettable
1083 {
1084 // SIZE is settable in windows if [self styleMask] & NSResizableWindowMask - but windows are heavyweight so we're ok here
1085 // SIZE is settable in columns if [[self tableValue] allowsColumnResizing - haven't dealt with columns yet
1086 return NO;
1087 }
1088
1089 // Element subrole type, such as NSAccessibilityTableRowSubrole (NSString). See the subrole attribute table at
1090 // http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html
1091 - (NSString *)accessibilitySubroleAttribute
1092 {
1093 NSString *value = nil;
1094 if ([[self javaRole] isEqualToString:@"passwordtext"]) {
1095 value = NSAccessibilitySecureTextFieldSubrole;
1096 }
1116 NSAccessibilitySearchFieldSubrole //no
1117 */
1118 return value;
1119 }
1120
1121 - (BOOL)accessibilityIsSubroleAttributeSettable
1122 {
1123 return NO;
1124 }
1125
1126 // Title of element, such as button text (NSString)
1127 - (NSString *)accessibilityTitleAttribute
1128 {
1129 // Return empty string for labels, since their value and tile end up being the same thing and this leads to repeated text.
1130 if ([[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityStaticTextRole]) {
1131 return @"";
1132 }
1133
1134 JNIEnv* env = [ThreadUtilities getJNIEnv];
1135
1136 jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleName, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1137 if (val == NULL) {
1138 return nil;
1139 }
1140 NSString* str = JNFJavaToNSString(env, val);
1141 (*env)->DeleteLocalRef(env, val);
1142 return str;
1143 }
1144
1145 - (BOOL)accessibilityIsTitleAttributeSettable
1146 {
1147 return NO;
1148 }
1149
1150 - (NSWindow *)accessibilityTopLevelUIElementAttribute
1151 {
1152 return [self window];
1153 }
1154
1155 - (BOOL)accessibilityIsTopLevelUIElementAttributeSettable
1156 {
1157 return NO;
1158 }
1159
1160 // Element's value (id)
1161 // note that the appKit meaning of "accessibilityValue" is different from the java
1162 // meaning of "accessibleValue", which is specific to numerical values
1163 // (https://docs.oracle.com/javase/8/docs/api/javax/accessibility/AccessibleValue.html#setCurrentAccessibleValue-java.lang.Number-)
1164 - (id)accessibilityValueAttribute
1165 {
1166 static JNF_STATIC_MEMBER_CACHE(jm_getCurrentAccessibleValue, sjc_CAccessibility, "getCurrentAccessibleValue", "(Ljavax/accessibility/AccessibleValue;Ljava/awt/Component;)Ljava/lang/Number;");
1167
1168 JNIEnv* env = [ThreadUtilities getJNIEnv];
1169
1170 // Need to handle popupmenus differently.
1171 //
1172 // At least for now don't handle combo box menus.
1173 // This may change when later fixing issues which currently
1174 // exist for combo boxes, but for now the following is only
1175 // for JPopupMenus, not for combobox menus.
1176 id parent = [self parent];
1177 if ( [[self javaRole] isEqualToString:@"popupmenu"] &&
1178 ![[parent javaRole] isEqualToString:@"combobox"] ) {
1179 NSArray *children =
1180 [JavaComponentAccessibility childrenOfParent:self
1181 withEnv:env
1182 withChildrenCode:JAVA_AX_ALL_CHILDREN
1183 allowIgnored:YES];
1184 if ([children count] > 0) {
1185 // handle case of AXMenuItem
1186 // need to ask menu what is selected
1187 NSArray *selectedChildrenOfMenu =
1188 [self accessibilitySelectedChildrenAttribute];
1189 JavaComponentAccessibility *selectedMenuItem =
1190 [selectedChildrenOfMenu objectAtIndex:0];
1191 if (selectedMenuItem != nil) {
1192 jobject itemValue =
1193 JNFCallStaticObjectMethod( env,
1194 sjm_getAccessibleName,
1195 selectedMenuItem->fAccessible,
1196 selectedMenuItem->fComponent ); // AWT_THREADING Safe (AWTRunLoop)
1197 if (itemValue == NULL) {
1198 return nil;
1199 }
1200 NSString* itemString = JNFJavaToNSString(env, itemValue);
1201 (*env)->DeleteLocalRef(env, itemValue);
1202 return itemString;
1203 } else {
1204 return nil;
1205 }
1206 }
1207 }
1208
1209 // ask Java for the component's accessibleValue. In java, the "accessibleValue" just means a numerical value
1210 // a text value is taken care of in JavaTextAccessibility
1211
1212 // cmcnote should coalesce these calls into one java call
1213 NSNumber *num = nil;
1214 jobject axValue = JNFCallStaticObjectMethod(env, sjm_getAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1215 if (axValue != NULL) {
1216 jobject str = JNFCallStaticObjectMethod(env, jm_getCurrentAccessibleValue, axValue, fComponent);
1217 if (str != NULL) {
1218 num = JNFJavaToNSNumber(env, str); // AWT_THREADING Safe (AWTRunLoop)
1219 (*env)->DeleteLocalRef(env, str);
1220 }
1221 (*env)->DeleteLocalRef(env, axValue);
1222 }
1223 if (num == nil) {
1224 num = [NSNumber numberWithInt:0];
1225 }
1226 return num;
1227 }
1228
1229 - (BOOL)accessibilityIsValueAttributeSettable
1230 {
1231 // according ot AppKit sources, in general the value attribute is not settable, except in the cases
1232 // of an NSScroller, an NSSplitView, and text that's both enabled & editable
1233 BOOL isSettable = NO;
1234 NSString *role = [self accessibilityRoleAttribute];
1235
1236 if ([role isEqualToString:NSAccessibilityScrollBarRole] || // according to NSScrollerAccessibility
1237 [role isEqualToString:NSAccessibilitySplitGroupRole] ) // according to NSSplitViewAccessibility
1238 {
1294 {
1295 AWT_ASSERT_APPKIT_THREAD;
1296
1297 JNIEnv *env = [ThreadUtilities getJNIEnv];
1298 [(id <JavaAccessibilityAction>)[[self getActions:env] objectForKey:action] perform];
1299 }
1300
1301
1302 // -- misc accessibility --
1303 - (BOOL)accessibilityIsIgnored
1304 {
1305 #ifdef JAVA_AX_NO_IGNORES
1306 return NO;
1307 #else
1308 return [[self accessibilityRoleAttribute] isEqualToString:JavaAccessibilityIgnore];
1309 #endif /* JAVA_AX_NO_IGNORES */
1310 }
1311
1312 - (id)accessibilityHitTest:(NSPoint)point withEnv:(JNIEnv *)env
1313 {
1314 static JNF_CLASS_CACHE(jc_Container, "java/awt/Container");
1315 static JNF_STATIC_MEMBER_CACHE(jm_accessibilityHitTest, sjc_CAccessibility, "accessibilityHitTest", "(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;");
1316
1317 // Make it into java screen coords
1318 point.y = [[[[self view] window] screen] frame].size.height - point.y;
1319
1320 jobject jparent = fComponent;
1321
1322 id value = nil;
1323 if (JNFIsInstanceOf(env, jparent, &jc_Container)) {
1324 jobject jaccessible = JNFCallStaticObjectMethod(env, jm_accessibilityHitTest, jparent, (jfloat)point.x, (jfloat)point.y); // AWT_THREADING Safe (AWTRunLoop)
1325 if (jaccessible != NULL) {
1326 value = [JavaComponentAccessibility createWithAccessible:jaccessible withEnv:env withView:fView];
1327 (*env)->DeleteLocalRef(env, jaccessible);
1328 }
1329 }
1330
1331 if (value == nil) {
1332 value = self;
1333 }
1334
1335 if ([value accessibilityIsIgnored]) {
1336 value = NSAccessibilityUnignoredAncestor(value);
1337 }
1338
1339 #ifdef JAVA_AX_DEBUG
1340 NSLog(@"%s: %@", __FUNCTION__, value);
1341 #endif
1342 return value;
1343 }
1344
1345 - (id)accessibilityFocusedUIElement
1346 {
1347 static JNF_STATIC_MEMBER_CACHE(jm_getFocusOwner, sjc_CAccessibility, "getFocusOwner", "(Ljava/awt/Component;)Ljavax/accessibility/Accessible;");
1348
1349 JNIEnv *env = [ThreadUtilities getJNIEnv];
1350 id value = nil;
1351
1352 NSWindow* hostWindow = [[self->fView window] retain];
1353 jobject focused = JNFCallStaticObjectMethod(env, jm_getFocusOwner, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1354 [hostWindow release];
1355
1356 if (focused != NULL) {
1357 if (JNFIsInstanceOf(env, focused, &sjc_Accessible)) {
1358 value = [JavaComponentAccessibility createWithAccessible:focused withEnv:env withView:fView];
1359 }
1360 (*env)->DeleteLocalRef(env, focused);
1361 }
1362
1363 if (value == nil) {
1364 value = self;
1365 }
1366 #ifdef JAVA_AX_DEBUG
1367 NSLog(@"%s: %@", __FUNCTION__, value);
1368 #endif
1369 return value;
1370 }
1371
1372 @end
1373
1374 /*
1375 * Class: sun_lwawt_macosx_CAccessibility
1376 * Method: focusChanged
1377 * Signature: ()V
1378 */
1379 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessibility_focusChanged
1380 (JNIEnv *env, jobject jthis)
1381 {
1382 JNF_COCOA_ENTER(env);
1383 [ThreadUtilities performOnMainThread:@selector(postFocusChanged:) on:[JavaComponentAccessibility class] withObject:nil waitUntilDone:NO];
1384 JNF_COCOA_EXIT(env);
1385 }
1386
1387 /*
1388 * Class: sun_lwawt_macosx_CAccessible
1389 * Method: valueChanged
1390 * Signature: (I)V
1391 */
1392 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_valueChanged
1393 (JNIEnv *env, jclass jklass, jlong element)
1394 {
1395 JNF_COCOA_ENTER(env);
1396 [ThreadUtilities performOnMainThread:@selector(postValueChanged) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO];
1397 JNF_COCOA_EXIT(env);
1398 }
1399
1400 /*
1401 * Class: sun_lwawt_macosx_CAccessible
1402 * Method: selectedTextChanged
1403 * Signature: (I)V
1404 */
1405 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectedTextChanged
1406 (JNIEnv *env, jclass jklass, jlong element)
1407 {
1408 JNF_COCOA_ENTER(env);
1409 [ThreadUtilities performOnMainThread:@selector(postSelectedTextChanged)
1410 on:(JavaComponentAccessibility *)jlong_to_ptr(element)
1411 withObject:nil
1412 waitUntilDone:NO];
1413 JNF_COCOA_EXIT(env);
1414 }
1415
1416 /*
1417 * Class: sun_lwawt_macosx_CAccessible
1418 * Method: selectionChanged
1419 * Signature: (I)V
1420 */
1421 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectionChanged
1422 (JNIEnv *env, jclass jklass, jlong element)
1423 {
1424 JNF_COCOA_ENTER(env);
1425 [ThreadUtilities performOnMainThread:@selector(postSelectionChanged) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO];
1426 JNF_COCOA_EXIT(env);
1427 }
1428
1429 /*
1430 * Class: sun_lwawt_macosx_CAccessible
1431 * Method: menuOpened
1432 * Signature: (I)V
1433 */
1434 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuOpened
1435 (JNIEnv *env, jclass jklass, jlong element)
1436 {
1437 JNF_COCOA_ENTER(env);
1438 [ThreadUtilities performOnMainThread:@selector(postMenuOpened)
1439 on:(JavaComponentAccessibility *)jlong_to_ptr(element)
1440 withObject:nil
1441 waitUntilDone:NO];
1442 JNF_COCOA_EXIT(env);
1443 }
1444
1445 /*
1446 * Class: sun_lwawt_macosx_CAccessible
1447 * Method: menuClosed
1448 * Signature: (I)V
1449 */
1450 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuClosed
1451 (JNIEnv *env, jclass jklass, jlong element)
1452 {
1453 JNF_COCOA_ENTER(env);
1454 [ThreadUtilities performOnMainThread:@selector(postMenuClosed)
1455 on:(JavaComponentAccessibility *)jlong_to_ptr(element)
1456 withObject:nil
1457 waitUntilDone:NO];
1458 JNF_COCOA_EXIT(env);
1459 }
1460
1461 /*
1462 * Class: sun_lwawt_macosx_CAccessible
1463 * Method: menuItemSelected
1464 * Signature: (I)V
1465 */
1466 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuItemSelected
1467 (JNIEnv *env, jclass jklass, jlong element)
1468 {
1469 JNF_COCOA_ENTER(env);
1470 [ThreadUtilities performOnMainThread:@selector(postMenuItemSelected)
1471 on:(JavaComponentAccessibility *)jlong_to_ptr(element)
1472 withObject:nil
1473 waitUntilDone:NO];
1474 JNF_COCOA_EXIT(env);
1475 }
1476
1477 /*
1478 * Class: sun_lwawt_macosx_CAccessible
1479 * Method: unregisterFromCocoaAXSystem
1480 * Signature: (I)V
1481 */
1482 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_unregisterFromCocoaAXSystem
1483 (JNIEnv *env, jclass jklass, jlong element)
1484 {
1485 JNF_COCOA_ENTER(env);
1486 [ThreadUtilities performOnMainThread:@selector(unregisterFromCocoaAXSystem) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO];
1487 JNF_COCOA_EXIT(env);
1488 }
1489
1490 @implementation TabGroupAccessibility
1491
1492 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole
1493 {
1494 self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole];
1495 if (self) {
1496 _numTabs = -1; //flag for uninitialized numTabs
1497 }
1498 return self;
1499 }
1500
1501 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
1502 {
1503 NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env];
1504
1505 [names addObject:NSAccessibilityTabsAttribute];
1506 [names addObject:NSAccessibilityContentsAttribute];
1507 [names addObject:NSAccessibilityValueAttribute];
1517 jobject selAccessible = getAxContextSelection(env, axContext, 0, fComponent);
1518 if (selAccessible == NULL) return nil;
1519
1520 // Go through the tabs and find selAccessible
1521 _numTabs = [tabs count];
1522 JavaComponentAccessibility *aTab;
1523 NSInteger i;
1524 for (i = 0; i < _numTabs; i++) {
1525 aTab = (JavaComponentAccessibility *)[tabs objectAtIndex:i];
1526 if ([aTab isAccessibleWithEnv:env forAccessible:selAccessible]) {
1527 (*env)->DeleteLocalRef(env, selAccessible);
1528 return aTab;
1529 }
1530 }
1531 (*env)->DeleteLocalRef(env, selAccessible);
1532 return nil;
1533 }
1534
1535 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored
1536 {
1537 jobjectArray jtabsAndRoles = (jobjectArray)JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, fAccessible, fComponent, whichTabs, allowIgnored); // AWT_THREADING Safe (AWTRunLoop)
1538 if(jtabsAndRoles == NULL) return nil;
1539
1540 jsize arrayLen = (*env)->GetArrayLength(env, jtabsAndRoles);
1541 if (arrayLen == 0) {
1542 (*env)->DeleteLocalRef(env, jtabsAndRoles);
1543 return nil;
1544 }
1545 NSMutableArray *tabs = [NSMutableArray arrayWithCapacity:(arrayLen/2)];
1546
1547 // 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
1548 jobject jtabJavaRole = (*env)->GetObjectArrayElement(env, jtabsAndRoles, 1); // the array entries alternate between tab/role, starting with tab. so the first role is entry 1.
1549 if (jtabJavaRole == NULL) {
1550 (*env)->DeleteLocalRef(env, jtabsAndRoles);
1551 return nil;
1552 }
1553 jobject jkey = JNFGetObjectField(env, jtabJavaRole, sjf_key);
1554 NSString *tabJavaRole = JNFJavaToNSString(env, jkey);
1555 (*env)->DeleteLocalRef(env, jkey);
1556
1557 NSInteger i;
1558 NSUInteger tabIndex = (whichTabs >= 0) ? whichTabs : 0; // if we're getting one particular child, make sure to set its index correctly
1559 for(i = 0; i < arrayLen; i+=2) {
1560 jobject jtab = (*env)->GetObjectArrayElement(env, jtabsAndRoles, i);
1561 JavaComponentAccessibility *tab = [[[TabGroupControlAccessibility alloc] initWithParent:self withEnv:env withAccessible:jtab withIndex:tabIndex withTabGroup:axContext withView:[self view] withJavaRole:tabJavaRole] autorelease];
1562 (*env)->DeleteLocalRef(env, jtab);
1563 [tabs addObject:tab];
1564 tabIndex++;
1565 }
1566 (*env)->DeleteLocalRef(env, jtabsAndRoles);
1567 return tabs;
1568 }
1569
1570 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored
1571 {
1572 // Contents are the children of the selected tab.
1573 id currentTab = [self currentTabWithEnv:env withAxContext:axContext];
1574 if (currentTab == nil) return nil;
1676 }
1677 (*env)->DeleteLocalRef(env, axContext);
1678 } else {
1679 result = [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
1680 }
1681 return result;
1682 }
1683
1684 @end
1685
1686
1687 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component);
1688
1689 @implementation TabGroupControlAccessibility
1690
1691 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole
1692 {
1693 self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole];
1694 if (self) {
1695 if (tabGroup != NULL) {
1696 fTabGroupAxContext = JNFNewWeakGlobalRef(env, tabGroup);
1697 } else {
1698 fTabGroupAxContext = NULL;
1699 }
1700 }
1701 return self;
1702 }
1703
1704 - (void)dealloc
1705 {
1706 JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
1707
1708 if (fTabGroupAxContext != NULL) {
1709 JNFDeleteWeakGlobalRef(env, fTabGroupAxContext);
1710 fTabGroupAxContext = NULL;
1711 }
1712
1713 [super dealloc];
1714 }
1715
1716 - (id)accessibilityValueAttribute
1717 {
1718 JNIEnv *env = [ThreadUtilities getJNIEnv];
1719 jobject axContext = [self axContextWithEnv:env];
1720 jobject selAccessible = getAxContextSelection(env, [self tabGroup], fIndex, fComponent);
1721
1722 // Returns the current selection of the page tab list
1723 id val = [NSNumber numberWithBool:ObjectEquals(env, axContext, selAccessible, fComponent)];
1724
1725 (*env)->DeleteLocalRef(env, selAccessible);
1726 (*env)->DeleteLocalRef(env, axContext);
1727 return val;
1728 }
1729
1730 - (void)getActionsWithEnv:(JNIEnv *)env
1731 {
1732 TabGroupAction *action = [[TabGroupAction alloc] initWithEnv:env withTabGroup:[self tabGroup] withIndex:fIndex withComponent:fComponent];
1733 [fActions setObject:action forKey:NSAccessibilityPressAction];
1734 [action release];
1735 }
1736
1737 - (jobject)tabGroup
1738 {
1739 if (fTabGroupAxContext == NULL) {
1740 JNIEnv* env = [ThreadUtilities getJNIEnv];
1741 jobject tabGroupAxContext = [(JavaComponentAccessibility *)[self parent] axContextWithEnv:env];
1742 fTabGroupAxContext = JNFNewWeakGlobalRef(env, tabGroupAxContext);
1743 (*env)->DeleteLocalRef(env, tabGroupAxContext);
1744 }
1745 return fTabGroupAxContext;
1746 }
1747
1748 @end
1749
1750
1751 @implementation ScrollAreaAccessibility
1752
1753 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
1754 {
1755 NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env];
1756
1757 [names addObject:NSAccessibilityHorizontalScrollBarAttribute];
1758 [names addObject:NSAccessibilityVerticalScrollBarAttribute];
1759 [names addObject:NSAccessibilityContentsAttribute];
1760
1761 return names;
1762 }
1835 // no scroll bars in contents
1836 [(NSMutableArray *)contents addObject:aElement];
1837 }
1838 }
1839
1840 return contents;
1841 }
1842
1843 - (BOOL)accessibilityIsContentsAttributeSettable
1844 {
1845 return NO;
1846 }
1847
1848 @end
1849
1850 /*
1851 * Returns Object.equals for the two items
1852 * This may use LWCToolkit.invokeAndWait(); don't call while holding fLock
1853 * and try to pass a component so the event happens on the correct thread.
1854 */
1855 static JNF_CLASS_CACHE(sjc_Object, "java/lang/Object");
1856 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component)
1857 {
1858 static JNF_MEMBER_CACHE(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z");
1859
1860 if ((a == NULL) && (b == NULL)) return YES;
1861 if ((a == NULL) || (b == NULL)) return NO;
1862
1863 if (pthread_main_np() != 0) {
1864 // If we are on the AppKit thread
1865 static JNF_CLASS_CACHE(sjc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit");
1866 static JNF_STATIC_MEMBER_CACHE(jm_doEquals, sjc_LWCToolkit, "doEquals", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/awt/Component;)Z");
1867 return JNFCallStaticBooleanMethod(env, jm_doEquals, a, b, component); // AWT_THREADING Safe (AWTRunLoopMode)
1868 }
1869
1870 return JNFCallBooleanMethod(env, a, jm_equals, b); // AWT_THREADING Safe (!appKit)
1871 }
|
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;
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])
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;
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
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
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) {
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 }
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 {
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];
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;
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 }
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 }
|