121
122
123 @implementation JavaComponentAccessibility
124
125 - (NSString *)description
126 {
127 return [NSString stringWithFormat:@"%@(title:'%@', desc:'%@', value:'%@')", [self accessibilityRoleAttribute],
128 [self accessibilityTitleAttribute], [self accessibilityRoleDescriptionAttribute], [self accessibilityValueAttribute]];
129 }
130
131 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole
132 {
133 self = [super init];
134 if (self)
135 {
136 fParent = [parent retain];
137 fView = [view retain];
138 fJavaRole = [javaRole retain];
139
140 fAccessible = JNFNewGlobalRef(env, accessible);
141 fComponent = JNFNewGlobalRef(env, [(AWTView *)fView awtComponent:env]);
142
143 fIndex = index;
144
145 fActions = nil;
146 fActionsLOCK = [[NSObject alloc] init];
147 }
148 return self;
149 }
150
151 - (void)unregisterFromCocoaAXSystem
152 {
153 AWT_ASSERT_APPKIT_THREAD;
154 static dispatch_once_t initialize_unregisterUniqueId_once;
155 static void (*unregisterUniqueId)(id);
156 dispatch_once(&initialize_unregisterUniqueId_once, ^{
157 void *jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL);
158 unregisterUniqueId = dlsym(jrsFwk, "JRSAccessibilityUnregisterUniqueIdForUIElement");
159 });
160 if (unregisterUniqueId) unregisterUniqueId(self);
161 }
270 + (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env {
271 if (JNFIsInstanceOf(env, jaccessible, &sjc_CAccessible)) {
272 return jaccessible;
273 }
274 else if (JNFIsInstanceOf(env, jaccessible, &sjc_Accessible)) {
275 return JNFCallStaticObjectMethod(env, sjm_getCAccessible, jaccessible);
276 }
277 return NULL;
278 }
279
280 + (NSArray *)childrenOfParent:(JavaComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored
281 {
282 jobjectArray jchildrenAndRoles = JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, parent->fAccessible, parent->fComponent, whichChildren, allowIgnored); // AWT_THREADING Safe (AWTRunLoop)
283 if (jchildrenAndRoles == NULL) return nil;
284
285 jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles);
286 NSMutableArray *children = [NSMutableArray arrayWithCapacity:arrayLen/2]; //childrenAndRoles array contains two elements (child, role) for each child
287
288 NSInteger i;
289 NSUInteger childIndex = (whichChildren >= 0) ? whichChildren : 0; // if we're getting one particular child, make sure to set its index correctly
290 for(i = 0; i < arrayLen; i+=2)
291 {
292 jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i);
293 jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1);
294
295 NSString *childJavaRole = nil;
296 if (jchildJavaRole != NULL) {
297 childJavaRole = JNFJavaToNSString(env, JNFGetObjectField(env, jchildJavaRole, sjf_key));
298 }
299
300 JavaComponentAccessibility *child = [self createWithParent:parent accessible:jchild role:childJavaRole index:childIndex withEnv:env withView:parent->fView];
301 [children addObject:child];
302 childIndex++;
303 }
304
305 return children;
306 }
307
308 + (JavaComponentAccessibility *)createWithAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env withView:(NSView *)view
309 {
310 jobject jcomponent = [(AWTView *)view awtComponent:env];
311 jint index = JNFCallStaticIntMethod(env, sjm_getAccessibleIndexInParent, jaccessible, jcomponent);
312 NSString *javaRole = getJavaRole(env, jaccessible, jcomponent);
313
314 return [self createWithAccessible:jaccessible role:javaRole index:index withEnv:env withView:view];
315 }
316
317 + (JavaComponentAccessibility *) createWithAccessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view
318 {
319 return [self createWithParent:nil accessible:jaccessible role:javaRole index:index withEnv:env withView:view];
320 }
321
322 + (JavaComponentAccessibility *) createWithParent:(JavaComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view
323 {
324 // try to fetch the jCAX from Java, and return autoreleased
325 jobject jCAX = [JavaComponentAccessibility getCAccessible:jaccessible withEnv:env];
326 if (jCAX == NULL) return nil;
327 JavaComponentAccessibility *value = (JavaComponentAccessibility *) jlong_to_ptr(JNFGetLongField(env, jCAX, jf_ptr));
328 if (value != nil) return [[value retain] autorelease];
329
330 // otherwise, create a new instance
331 JavaComponentAccessibility *newChild = nil;
332 if ([javaRole isEqualToString:@"pagetablist"]) {
333 newChild = [TabGroupAccessibility alloc];
334 } else if ([javaRole isEqualToString:@"scrollpane"]) {
335 newChild = [ScrollAreaAccessibility alloc];
336 } else {
337 NSString *nsRole = [sRoles objectForKey:javaRole];
338 if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] || [nsRole isEqualToString:NSAccessibilityTextAreaRole] || [nsRole isEqualToString:NSAccessibilityTextFieldRole]) {
339 newChild = [JavaTextAccessibility alloc];
340 } else {
341 newChild = [JavaComponentAccessibility alloc];
342 }
343 }
344
345 // must init freshly -alloc'd object
346 [newChild initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // must init new instance
347
348 // must hard retain pointer poked into Java object
349 [newChild retain];
350 JNFSetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild));
351
352 // return autoreleased instance
353 return [newChild autorelease];
354 }
355
356 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
357 {
358 static JNF_STATIC_MEMBER_CACHE(jm_getInitialAttributeStates, sjc_CAccessibility, "getInitialAttributeStates", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)[Z");
359
360 NSMutableArray *attributeNames = [NSMutableArray arrayWithCapacity:10];
361 [attributeNames retain];
362
363 // all elements respond to parent, role, role description, window, topLevelUIElement, help
364 [attributeNames addObject:NSAccessibilityParentAttribute];
365 [attributeNames addObject:NSAccessibilityRoleAttribute];
366 [attributeNames addObject:NSAccessibilityRoleDescriptionAttribute];
367 [attributeNames addObject:NSAccessibilityHelpAttribute];
368
369 // cmcnote: AXMenu usually doesn't respond to window / topLevelUIElement. But menus within a Java app's window
370 // probably should. Should we use some role other than AXMenu / AXMenuBar for Java menus?
479 }
480
481 - (jobject)axContextWithEnv:(JNIEnv *)env
482 {
483 return getAxContext(env, fAccessible, fComponent);
484 }
485
486 - (id)parent
487 {
488 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleParent, sjc_CAccessibility, "getAccessibleParent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/Accessible;");
489
490 if(fParent == nil) {
491 JNIEnv* env = [ThreadUtilities getJNIEnv];
492
493 jobject jparent = JNFCallStaticObjectMethod(env, sjm_getAccessibleParent, fAccessible, fComponent);
494
495 if (jparent == NULL) {
496 fParent = fView;
497 } else {
498 fParent = [JavaComponentAccessibility createWithAccessible:jparent withEnv:env withView:fView];
499 if (fParent == nil) {
500 fParent = fView;
501 }
502 }
503 [fParent retain];
504 }
505 return fParent;
506 }
507
508 - (NSView *)view
509 {
510 return fView;
511 }
512
513 - (NSWindow *)window
514 {
515 return [[self view] window];
516 }
517
518 - (NSString *)javaRole
529 {
530 id role = [self accessibilityRoleAttribute];
531 return [role isEqualToString:NSAccessibilityMenuBarRole] || [role isEqualToString:NSAccessibilityMenuRole] || [role isEqualToString:NSAccessibilityMenuItemRole];
532 }
533
534 - (BOOL)isSelected:(JNIEnv *)env
535 {
536 if (fIndex == -1) {
537 return NO;
538 }
539
540 return isChildSelected(env, ((JavaComponentAccessibility *)[self parent])->fAccessible, fIndex, fComponent);
541 }
542
543 - (BOOL)isVisible:(JNIEnv *)env
544 {
545 if (fIndex == -1) {
546 return NO;
547 }
548
549 return isShowing(env, [self axContextWithEnv:env], fComponent);
550 }
551
552 // the array of names for each role is cached in the sAttributeNamesForRoleCache
553 - (NSArray *)accessibilityAttributeNames
554 {
555 JNIEnv* env = [ThreadUtilities getJNIEnv];
556
557 @synchronized(sAttributeNamesLOCK) {
558 NSString *javaRole = [self javaRole];
559 NSArray *names = (NSArray *)[sAttributeNamesForRoleCache objectForKey:javaRole];
560 if (names != nil) return names;
561
562 names = [self initializeAttributeNamesWithEnv:env];
563 if (names != nil) {
564 #ifdef JAVA_AX_DEBUG
565 NSLog(@"Initializing: %s for %@: %@", __FUNCTION__, javaRole, names);
566 #endif
567 [sAttributeNamesForRoleCache setObject:names forKey:javaRole];
568 return names;
569 }
753 static JNF_STATIC_MEMBER_CACHE(jm_getMinimumAccessibleValue, sjc_CAccessibility, "getMinimumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;");
754
755 JNIEnv* env = [ThreadUtilities getJNIEnv];
756
757 jobject axValue = JNFCallStaticObjectMethod(env, jm_getMinimumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
758 return JNFJavaToNSNumber(env, axValue);
759 }
760
761 - (BOOL)accessibilityIsMinValueAttributeSettable
762 {
763 return NO;
764 }
765
766 - (id)accessibilityOrientationAttribute
767 {
768 JNIEnv* env = [ThreadUtilities getJNIEnv];
769 jobject axContext = [self axContextWithEnv:env];
770
771 // cmcnote - should batch these two calls into one that returns an array of two bools, one for vertical and one for horiz
772 if (isVertical(env, axContext, fComponent)) {
773 return NSAccessibilityVerticalOrientationValue;
774 }
775
776 if (isHorizontal(env, axContext, fComponent)) {
777 return NSAccessibilityHorizontalOrientationValue;
778 }
779
780 return nil;
781 }
782
783 - (BOOL)accessibilityIsOrientationAttributeSettable
784 {
785 return NO;
786 }
787
788 // Element containing current element (id)
789 - (id)accessibilityParentAttribute
790 {
791 return NSAccessibilityUnignoredAncestor([self parent]);
792 }
793
794 - (BOOL)accessibilityIsParentAttributeSettable
795 {
796 return NO;
797 }
798
799 // Screen position of element's lower-left corner in lower-left relative screen coordinates (NSValue)
800 - (NSValue *)accessibilityPositionAttribute
801 {
802 JNIEnv* env = [ThreadUtilities getJNIEnv];
803 jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
804
805 // NSAccessibility wants the bottom left point of the object in
806 // bottom left based screen coords
807
808 // Get the java screen coords, and make a NSPoint of the bottom left of the AxComponent.
809 NSSize size = getAxComponentSize(env, axComponent, fComponent);
810 NSPoint point = getAxComponentLocationOnScreen(env, axComponent, fComponent);
811
812 point.y += size.height;
813
814 // Now make it into Cocoa screen coords.
815 point.y = [[[[self view] window] screen] frame].size.height - point.y;
816
817 return [NSValue valueWithPoint:point];
818 }
819
820 - (BOOL)accessibilityIsPositionAttributeSettable
821 {
822 // In AppKit, position is only settable for a window (NSAccessibilityWindowRole). Our windows are taken care of natively, so we don't need to deal with this here
823 // We *could* make use of Java's AccessibleComponent.setLocation() method. Investigate. radr://3953869
824 return NO;
825 }
826
827 // Element type, such as NSAccessibilityRadioButtonRole (NSString). See the role table
828 // at http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html
829 - (NSString *)accessibilityRoleAttribute
830 {
876 - (NSArray *)accessibilitySelectedChildrenAttribute
877 {
878 JNIEnv* env = [ThreadUtilities getJNIEnv];
879 NSArray *selectedChildren = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_SELECTED_CHILDREN allowIgnored:NO];
880 if ([selectedChildren count] > 0) {
881 return selectedChildren;
882 }
883
884 return nil;
885 }
886
887 - (BOOL)accessibilityIsSelectedChildrenAttributeSettable
888 {
889 return NO; // cmcnote: actually it should be. so need to write accessibilitySetSelectedChildrenAttribute also
890 }
891
892 // Element size (NSValue)
893 - (NSValue *)accessibilitySizeAttribute {
894 JNIEnv* env = [ThreadUtilities getJNIEnv];
895 jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
896 return [NSValue valueWithSize:getAxComponentSize(env, axComponent, fComponent)];
897 }
898
899 - (BOOL)accessibilityIsSizeAttributeSettable
900 {
901 // SIZE is settable in windows if [self styleMask] & NSResizableWindowMask - but windows are heavyweight so we're ok here
902 // SIZE is settable in columns if [[self tableValue] allowsColumnResizing - haven't dealt with columns yet
903 return NO;
904 }
905
906 // Element subrole type, such as NSAccessibilityTableRowSubrole (NSString). See the subrole attribute table at
907 // http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html
908 - (NSString *)accessibilitySubroleAttribute
909 {
910 NSString *value = nil;
911 if ([[self javaRole] isEqualToString:@"passwordtext"])
912 {
913 value = NSAccessibilitySecureTextFieldSubrole;
914 }
915 /*
916 // other subroles. TableRow and OutlineRow may be relevant to us
1068 return NO;
1069 #else
1070 return [[self accessibilityRoleAttribute] isEqualToString:JavaAccessibilityIgnore];
1071 #endif /* JAVA_AX_NO_IGNORES */
1072 }
1073
1074 - (id)accessibilityHitTest:(NSPoint)point withEnv:(JNIEnv *)env
1075 {
1076 static JNF_CLASS_CACHE(jc_Container, "java/awt/Container");
1077 static JNF_STATIC_MEMBER_CACHE(jm_accessibilityHitTest, sjc_CAccessibility, "accessibilityHitTest", "(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;");
1078
1079 // Make it into java screen coords
1080 point.y = [[[[self view] window] screen] frame].size.height - point.y;
1081
1082 jobject jparent = fComponent;
1083
1084 id value = nil;
1085 if (JNFIsInstanceOf(env, jparent, &jc_Container)) {
1086 jobject jaccessible = JNFCallStaticObjectMethod(env, jm_accessibilityHitTest, jparent, (jfloat)point.x, (jfloat)point.y); // AWT_THREADING Safe (AWTRunLoop)
1087 value = [JavaComponentAccessibility createWithAccessible:jaccessible withEnv:env withView:fView];
1088 }
1089
1090 if (value == nil) {
1091 value = self;
1092 }
1093
1094 if ([value accessibilityIsIgnored]) {
1095 value = NSAccessibilityUnignoredAncestor(value);
1096 }
1097
1098 #ifdef JAVA_AX_DEBUG
1099 NSLog(@"%s: %@", __FUNCTION__, value);
1100 #endif
1101 return value;
1102 }
1103
1104 - (id)accessibilityFocusedUIElement
1105 {
1106 static JNF_STATIC_MEMBER_CACHE(jm_getFocusOwner, sjc_CAccessibility, "getFocusOwner", "(Ljava/awt/Component;)Ljavax/accessibility/Accessible;");
1107
1108 JNIEnv *env = [ThreadUtilities getJNIEnv];
1109 id value = nil;
1110
1111 NSWindow* hostWindow = [[self->fView window] retain];
1112 jobject focused = JNFCallStaticObjectMethod(env, jm_getFocusOwner, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1113 [hostWindow release];
1114
1115 if (focused != NULL) {
1116 if (JNFIsInstanceOf(env, focused, &sjc_Accessible)) {
1117 value = [JavaComponentAccessibility createWithAccessible:focused withEnv:env withView:fView];
1118 }
1119 }
1120
1121 if (value == nil) {
1122 value = self;
1123 }
1124 #ifdef JAVA_AX_DEBUG
1125 NSLog(@"%s: %@", __FUNCTION__, value);
1126 #endif
1127 return value;
1128 }
1129
1130 @end
1131
1132 /*
1133 * Class: sun_lwawt_macosx_CAccessibility
1134 * Method: focusChanged
1135 * Signature: ()V
1136 */
1137 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessibility_focusChanged
1138 (JNIEnv *env, jobject jthis)
1218 // Go through the tabs and find selAccessible
1219 _numTabs = [tabs count];
1220 JavaComponentAccessibility *aTab;
1221 NSInteger i;
1222 for (i = 0; i < _numTabs; i++) {
1223 aTab = (JavaComponentAccessibility *)[tabs objectAtIndex:i];
1224 if ([aTab isAccessibleWithEnv:env forAccessible:selAccessible]) {
1225 return aTab;
1226 }
1227 }
1228
1229 return nil;
1230 }
1231
1232 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored
1233 {
1234 jobjectArray jtabsAndRoles = JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, fAccessible, fComponent, whichTabs, allowIgnored); // AWT_THREADING Safe (AWTRunLoop)
1235 if(jtabsAndRoles == NULL) return nil;
1236
1237 jsize arrayLen = (*env)->GetArrayLength(env, jtabsAndRoles);
1238 if (arrayLen == 0) return nil;
1239
1240 NSMutableArray *tabs = [NSMutableArray arrayWithCapacity:(arrayLen/2)];
1241
1242 // all of the tabs have the same role, so we can just find out what that is here and use it for all the tabs
1243 jobject jtabJavaRole = (*env)->GetObjectArrayElement(env, jtabsAndRoles, 1); // the array entries alternate between tab/role, starting with tab. so the first role is entry 1.
1244 if (jtabJavaRole == NULL) return nil;
1245
1246 NSString *tabJavaRole = JNFJavaToNSString(env, JNFGetObjectField(env, jtabJavaRole, sjf_key));
1247
1248 NSInteger i;
1249 NSUInteger tabIndex = (whichTabs >= 0) ? whichTabs : 0; // if we're getting one particular child, make sure to set its index correctly
1250 for(i = 0; i < arrayLen; i+=2) {
1251 jobject jtab = (*env)->GetObjectArrayElement(env, jtabsAndRoles, i);
1252 JavaComponentAccessibility *tab = [[[TabGroupControlAccessibility alloc] initWithParent:self withEnv:env withAccessible:jtab withIndex:tabIndex withTabGroup:axContext withView:[self view] withJavaRole:tabJavaRole] autorelease];
1253 [tabs addObject:tab];
1254 tabIndex++;
1255 }
1256
1257 return tabs;
1258 }
1259
1260 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored
1261 {
1262 // Contents are the children of the selected tab.
1263 id currentTab = [self currentTabWithEnv:env withAxContext:axContext];
1264 if (currentTab == nil) return nil;
1265
1266 NSArray *contents = [JavaComponentAccessibility childrenOfParent:currentTab withEnv:env withChildrenCode:whichTabs allowIgnored:allowIgnored];
1267 if ([contents count] <= 0) return nil;
1268 return contents;
1269 }
1270
1271 - (id) accessibilityTabsAttribute
1272 {
1273 JNIEnv *env = [ThreadUtilities getJNIEnv];
1274 jobject axContext = [self axContextWithEnv:env];
1275 return [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO];
1276 }
1277
1278 - (BOOL)accessibilityIsTabsAttributeSettable
1279 {
1280 return NO; //cmcnote: not sure.
1281 }
1282
1283 - (NSInteger)numTabs
1284 {
1285 if (_numTabs == -1) {
1286 _numTabs = [[self accessibilityTabsAttribute] count];
1287 }
1288 return _numTabs;
1289 }
1290
1291 - (NSArray *) accessibilityContentsAttribute
1292 {
1293 JNIEnv *env = [ThreadUtilities getJNIEnv];
1294 jobject axContext = [self axContextWithEnv:env];
1295 return [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO];
1296 }
1297
1298 - (BOOL)accessibilityIsContentsAttributeSettable
1299 {
1300 return NO;
1301 }
1302
1303 // axValue is the currently selected tab
1304 -(id) accessibilityValueAttribute
1305 {
1306 JNIEnv *env = [ThreadUtilities getJNIEnv];
1307 jobject axContext = [self axContextWithEnv:env];
1308 return [self currentTabWithEnv:env withAxContext:axContext];
1309 }
1310
1311 - (BOOL)accessibilityIsValueAttributeSettable
1312 {
1313 return YES;
1314 }
1315
1316 - (void)accessibilitySetValueAttribute:(id)value //cmcnote: not certain this is ever actually called. investigate.
1317 {
1318 // set the current tab
1319 NSNumber *number = (NSNumber *)value;
1320 if (![number boolValue]) return;
1321
1322 JNIEnv *env = [ThreadUtilities getJNIEnv];
1323 jobject axContext = [self axContextWithEnv:env];
1324 setAxContextSelection(env, axContext, fIndex, fComponent);
1325 }
1326
1327 - (NSArray *)accessibilityChildrenAttribute
1328 {
1329 //children = AXTabs + AXContents
1330 NSArray *tabs = [self accessibilityTabsAttribute];
1331 NSArray *contents = [self accessibilityContentsAttribute];
1332
1333 NSMutableArray *children = [NSMutableArray arrayWithCapacity:[tabs count] + [contents count]];
1334 [children addObjectsFromArray:tabs];
1335 [children addObjectsFromArray:contents];
1336
1337 return (NSArray *)children;
1338 }
1339
1340 // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children.
1341 // See similar optimization in JavaComponentAccessibility. We have to extend the base implementation here, since
1342 // children of tabs are AXTabs + AXContents
1343 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount {
1344 NSArray *result = nil;
1345 if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
1346 // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child
1347 JNIEnv *env = [ThreadUtilities getJNIEnv];
1348 jobject axContext = [self axContextWithEnv:env];
1349
1350 //children = AXTabs + AXContents
1351 NSArray *children = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:index allowIgnored:NO]; // first look at the tabs
1352 if ([children count] > 0) {
1353 result = children;
1354 } else {
1355 children= [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:(index-[self numTabs]) allowIgnored:NO];
1356 if ([children count] > 0) {
1357 result = children;
1358 }
1359 }
1360 } else {
1361 result = [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
1362 }
1363 return result;
1364 }
1365
1366 @end
1367
1368
1369 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component);
1370
1371 @implementation TabGroupControlAccessibility
1372
1373 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole
1374 {
1375 self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole];
1376 if (self) {
1377 if (tabGroup != NULL) {
1378 fTabGroupAxContext = JNFNewGlobalRef(env, tabGroup);
1379 } else {
1384 }
1385
1386 - (void)dealloc
1387 {
1388 JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
1389
1390 if (fTabGroupAxContext != NULL) {
1391 JNFDeleteGlobalRef(env, fTabGroupAxContext);
1392 fTabGroupAxContext = NULL;
1393 }
1394
1395 [super dealloc];
1396 }
1397
1398 - (id)accessibilityValueAttribute
1399 {
1400 JNIEnv *env = [ThreadUtilities getJNIEnv];
1401 jobject axContext = [self axContextWithEnv:env];
1402
1403 // Returns the current selection of the page tab list
1404 return [NSNumber numberWithBool:ObjectEquals(env, axContext, getAxContextSelection(env, [self tabGroup], fIndex, fComponent), fComponent)];
1405 }
1406
1407 - (void)getActionsWithEnv:(JNIEnv *)env
1408 {
1409 TabGroupAction *action = [[TabGroupAction alloc] initWithEnv:env withTabGroup:[self tabGroup] withIndex:fIndex withComponent:fComponent];
1410 [fActions setObject:action forKey:NSAccessibilityPressAction];
1411 [action release];
1412 }
1413
1414 - (jobject)tabGroup
1415 {
1416 if (fTabGroupAxContext == NULL) {
1417 JNIEnv* env = [ThreadUtilities getJNIEnv];
1418 jobject tabGroupAxContext = [(JavaComponentAccessibility *)[self parent] axContextWithEnv:env];
1419 fTabGroupAxContext = JNFNewGlobalRef(env, tabGroupAxContext);
1420 }
1421 return fTabGroupAxContext;
1422 }
1423
1424 @end
1425
1426
1427 @implementation ScrollAreaAccessibility
1428
1429 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
1430 {
1431 NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env];
1432
1433 [names addObject:NSAccessibilityHorizontalScrollBarAttribute];
1434 [names addObject:NSAccessibilityVerticalScrollBarAttribute];
1435 [names addObject:NSAccessibilityContentsAttribute];
1436
1437 return names;
1438 }
1439
1440 - (id)accessibilityHorizontalScrollBarAttribute
1441 {
1442 JNIEnv *env = [ThreadUtilities getJNIEnv];
1443
1444 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1445 if ([children count] <= 0) return nil;
1446
1447 // The scroll bars are in the children.
1448 JavaComponentAccessibility *aElement;
1449 NSEnumerator *enumerator = [children objectEnumerator];
1450 while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) {
1451 if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) {
1452 jobject elementAxContext = [aElement axContextWithEnv:env];
1453 if (isHorizontal(env, elementAxContext, fComponent)) {
1454 return aElement;
1455 }
1456 }
1457 }
1458
1459 return nil;
1460 }
1461
1462 - (BOOL)accessibilityIsHorizontalScrollBarAttributeSettable
1463 {
1464 return NO;
1465 }
1466
1467 - (id)accessibilityVerticalScrollBarAttribute
1468 {
1469 JNIEnv *env = [ThreadUtilities getJNIEnv];
1470
1471 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1472 if ([children count] <= 0) return nil;
1473
1474 // The scroll bars are in the children.
1475 NSEnumerator *enumerator = [children objectEnumerator];
1476 JavaComponentAccessibility *aElement;
1477 while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) {
1478 if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) {
1479 jobject elementAxContext = [aElement axContextWithEnv:env];
1480 if (isVertical(env, elementAxContext, fComponent)) {
1481 return aElement;
1482 }
1483 }
1484 }
1485
1486 return nil;
1487 }
1488
1489 - (BOOL)accessibilityIsVerticalScrollBarAttributeSettable
1490 {
1491 return NO;
1492 }
1493
1494 - (NSArray *)accessibilityContentsAttribute
1495 {
1496 JNIEnv *env = [ThreadUtilities getJNIEnv];
1497 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1498
1499 if ([children count] <= 0) return nil;
1500 NSArray *contents = [NSMutableArray arrayWithCapacity:[children count]];
1501
1502 // The scroll bars are in the children. children less the scroll bars is the contents
|
121
122
123 @implementation JavaComponentAccessibility
124
125 - (NSString *)description
126 {
127 return [NSString stringWithFormat:@"%@(title:'%@', desc:'%@', value:'%@')", [self accessibilityRoleAttribute],
128 [self accessibilityTitleAttribute], [self accessibilityRoleDescriptionAttribute], [self accessibilityValueAttribute]];
129 }
130
131 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole
132 {
133 self = [super init];
134 if (self)
135 {
136 fParent = [parent retain];
137 fView = [view retain];
138 fJavaRole = [javaRole retain];
139
140 fAccessible = JNFNewGlobalRef(env, accessible);
141
142 jobject jcomponent = [(AWTView *)fView awtComponent:env];
143 fComponent = JNFNewGlobalRef(env, jcomponent);
144 (*env)->DeleteLocalRef(env, jcomponent);
145
146 fIndex = index;
147
148 fActions = nil;
149 fActionsLOCK = [[NSObject alloc] init];
150 }
151 return self;
152 }
153
154 - (void)unregisterFromCocoaAXSystem
155 {
156 AWT_ASSERT_APPKIT_THREAD;
157 static dispatch_once_t initialize_unregisterUniqueId_once;
158 static void (*unregisterUniqueId)(id);
159 dispatch_once(&initialize_unregisterUniqueId_once, ^{
160 void *jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL);
161 unregisterUniqueId = dlsym(jrsFwk, "JRSAccessibilityUnregisterUniqueIdForUIElement");
162 });
163 if (unregisterUniqueId) unregisterUniqueId(self);
164 }
273 + (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env {
274 if (JNFIsInstanceOf(env, jaccessible, &sjc_CAccessible)) {
275 return jaccessible;
276 }
277 else if (JNFIsInstanceOf(env, jaccessible, &sjc_Accessible)) {
278 return JNFCallStaticObjectMethod(env, sjm_getCAccessible, jaccessible);
279 }
280 return NULL;
281 }
282
283 + (NSArray *)childrenOfParent:(JavaComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored
284 {
285 jobjectArray jchildrenAndRoles = JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, parent->fAccessible, parent->fComponent, whichChildren, allowIgnored); // AWT_THREADING Safe (AWTRunLoop)
286 if (jchildrenAndRoles == NULL) return nil;
287
288 jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles);
289 NSMutableArray *children = [NSMutableArray arrayWithCapacity:arrayLen/2]; //childrenAndRoles array contains two elements (child, role) for each child
290
291 NSInteger i;
292 NSUInteger childIndex = (whichChildren >= 0) ? whichChildren : 0; // if we're getting one particular child, make sure to set its index correctly
293
294 for(i = 0; i < arrayLen; i+=2)
295 {
296 jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i);
297 jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1);
298
299 NSString *childJavaRole = nil;
300 if (jchildJavaRole != NULL) {
301 childJavaRole = JNFJavaToNSString(env, JNFGetObjectField(env, jchildJavaRole, sjf_key));
302 }
303
304 JavaComponentAccessibility *child = [self createWithParent:parent accessible:jchild role:childJavaRole index:childIndex withEnv:env withView:parent->fView];
305
306 (*env)->DeleteLocalRef(env, jchild);
307 (*env)->DeleteLocalRef(env, jchildJavaRole);
308
309 [children addObject:child];
310 childIndex++;
311 }
312 (*env)->DeleteLocalRef(env, jchildrenAndRoles);
313
314 return children;
315 }
316
317 + (JavaComponentAccessibility *)createWithAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env withView:(NSView *)view
318 {
319 jobject jcomponent = [(AWTView *)view awtComponent:env];
320 jint index = JNFCallStaticIntMethod(env, sjm_getAccessibleIndexInParent, jaccessible, jcomponent);
321 NSString *javaRole = getJavaRole(env, jaccessible, jcomponent);
322 (*env)->DeleteLocalRef(env, jcomponent);
323
324 return [self createWithAccessible:jaccessible role:javaRole index:index withEnv:env withView:view];
325 }
326
327 + (JavaComponentAccessibility *) createWithAccessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view
328 {
329 return [self createWithParent:nil accessible:jaccessible role:javaRole index:index withEnv:env withView:view];
330 }
331
332 + (JavaComponentAccessibility *) createWithParent:(JavaComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view
333 {
334 // try to fetch the jCAX from Java, and return autoreleased
335 jobject jCAX = [JavaComponentAccessibility getCAccessible:jaccessible withEnv:env];
336 if (jCAX == NULL) return nil;
337 JavaComponentAccessibility *value = (JavaComponentAccessibility *) jlong_to_ptr(JNFGetLongField(env, jCAX, jf_ptr));
338 if (value != nil) {
339 (*env)->DeleteLocalRef(env, jCAX);
340 return [[value retain] autorelease];
341 }
342
343 // otherwise, create a new instance
344 JavaComponentAccessibility *newChild = nil;
345 if ([javaRole isEqualToString:@"pagetablist"]) {
346 newChild = [TabGroupAccessibility alloc];
347 } else if ([javaRole isEqualToString:@"scrollpane"]) {
348 newChild = [ScrollAreaAccessibility alloc];
349 } else {
350 NSString *nsRole = [sRoles objectForKey:javaRole];
351 if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] || [nsRole isEqualToString:NSAccessibilityTextAreaRole] || [nsRole isEqualToString:NSAccessibilityTextFieldRole]) {
352 newChild = [JavaTextAccessibility alloc];
353 } else {
354 newChild = [JavaComponentAccessibility alloc];
355 }
356 }
357
358 // must init freshly -alloc'd object
359 [newChild initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // must init new instance
360
361 // must hard retain pointer poked into Java object
362 [newChild retain];
363 JNFSetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild));
364 (*env)->DeleteLocalRef(env, jCAX);
365
366 // return autoreleased instance
367 return [newChild autorelease];
368 }
369
370 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
371 {
372 static JNF_STATIC_MEMBER_CACHE(jm_getInitialAttributeStates, sjc_CAccessibility, "getInitialAttributeStates", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)[Z");
373
374 NSMutableArray *attributeNames = [NSMutableArray arrayWithCapacity:10];
375 [attributeNames retain];
376
377 // all elements respond to parent, role, role description, window, topLevelUIElement, help
378 [attributeNames addObject:NSAccessibilityParentAttribute];
379 [attributeNames addObject:NSAccessibilityRoleAttribute];
380 [attributeNames addObject:NSAccessibilityRoleDescriptionAttribute];
381 [attributeNames addObject:NSAccessibilityHelpAttribute];
382
383 // cmcnote: AXMenu usually doesn't respond to window / topLevelUIElement. But menus within a Java app's window
384 // probably should. Should we use some role other than AXMenu / AXMenuBar for Java menus?
493 }
494
495 - (jobject)axContextWithEnv:(JNIEnv *)env
496 {
497 return getAxContext(env, fAccessible, fComponent);
498 }
499
500 - (id)parent
501 {
502 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleParent, sjc_CAccessibility, "getAccessibleParent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/Accessible;");
503
504 if(fParent == nil) {
505 JNIEnv* env = [ThreadUtilities getJNIEnv];
506
507 jobject jparent = JNFCallStaticObjectMethod(env, sjm_getAccessibleParent, fAccessible, fComponent);
508
509 if (jparent == NULL) {
510 fParent = fView;
511 } else {
512 fParent = [JavaComponentAccessibility createWithAccessible:jparent withEnv:env withView:fView];
513 (*env)->DeleteLocalRef(env, jparent);
514 if (fParent == nil) {
515 fParent = fView;
516 }
517 }
518 [fParent retain];
519 }
520 return fParent;
521 }
522
523 - (NSView *)view
524 {
525 return fView;
526 }
527
528 - (NSWindow *)window
529 {
530 return [[self view] window];
531 }
532
533 - (NSString *)javaRole
544 {
545 id role = [self accessibilityRoleAttribute];
546 return [role isEqualToString:NSAccessibilityMenuBarRole] || [role isEqualToString:NSAccessibilityMenuRole] || [role isEqualToString:NSAccessibilityMenuItemRole];
547 }
548
549 - (BOOL)isSelected:(JNIEnv *)env
550 {
551 if (fIndex == -1) {
552 return NO;
553 }
554
555 return isChildSelected(env, ((JavaComponentAccessibility *)[self parent])->fAccessible, fIndex, fComponent);
556 }
557
558 - (BOOL)isVisible:(JNIEnv *)env
559 {
560 if (fIndex == -1) {
561 return NO;
562 }
563
564 jobject axContext = [self axContextWithEnv:env];
565 BOOL showing = isShowing(env, axContext, fComponent);
566 (*env)->DeleteLocalRef(env, axContext);
567 return showing;
568 }
569
570 // the array of names for each role is cached in the sAttributeNamesForRoleCache
571 - (NSArray *)accessibilityAttributeNames
572 {
573 JNIEnv* env = [ThreadUtilities getJNIEnv];
574
575 @synchronized(sAttributeNamesLOCK) {
576 NSString *javaRole = [self javaRole];
577 NSArray *names = (NSArray *)[sAttributeNamesForRoleCache objectForKey:javaRole];
578 if (names != nil) return names;
579
580 names = [self initializeAttributeNamesWithEnv:env];
581 if (names != nil) {
582 #ifdef JAVA_AX_DEBUG
583 NSLog(@"Initializing: %s for %@: %@", __FUNCTION__, javaRole, names);
584 #endif
585 [sAttributeNamesForRoleCache setObject:names forKey:javaRole];
586 return names;
587 }
771 static JNF_STATIC_MEMBER_CACHE(jm_getMinimumAccessibleValue, sjc_CAccessibility, "getMinimumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;");
772
773 JNIEnv* env = [ThreadUtilities getJNIEnv];
774
775 jobject axValue = JNFCallStaticObjectMethod(env, jm_getMinimumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
776 return JNFJavaToNSNumber(env, axValue);
777 }
778
779 - (BOOL)accessibilityIsMinValueAttributeSettable
780 {
781 return NO;
782 }
783
784 - (id)accessibilityOrientationAttribute
785 {
786 JNIEnv* env = [ThreadUtilities getJNIEnv];
787 jobject axContext = [self axContextWithEnv:env];
788
789 // cmcnote - should batch these two calls into one that returns an array of two bools, one for vertical and one for horiz
790 if (isVertical(env, axContext, fComponent)) {
791 (*env)->DeleteLocalRef(env, axContext);
792 return NSAccessibilityVerticalOrientationValue;
793 }
794
795 if (isHorizontal(env, axContext, fComponent)) {
796 (*env)->DeleteLocalRef(env, axContext);
797 return NSAccessibilityHorizontalOrientationValue;
798 }
799
800 (*env)->DeleteLocalRef(env, axContext);
801 return nil;
802 }
803
804 - (BOOL)accessibilityIsOrientationAttributeSettable
805 {
806 return NO;
807 }
808
809 // Element containing current element (id)
810 - (id)accessibilityParentAttribute
811 {
812 return NSAccessibilityUnignoredAncestor([self parent]);
813 }
814
815 - (BOOL)accessibilityIsParentAttributeSettable
816 {
817 return NO;
818 }
819
820 // Screen position of element's lower-left corner in lower-left relative screen coordinates (NSValue)
821 - (NSValue *)accessibilityPositionAttribute
822 {
823 JNIEnv* env = [ThreadUtilities getJNIEnv];
824 jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
825
826 // NSAccessibility wants the bottom left point of the object in
827 // bottom left based screen coords
828
829 // Get the java screen coords, and make a NSPoint of the bottom left of the AxComponent.
830 NSSize size = getAxComponentSize(env, axComponent, fComponent);
831 NSPoint point = getAxComponentLocationOnScreen(env, axComponent, fComponent);
832 (*env)->DeleteLocalRef(env, axComponent);
833
834 point.y += size.height;
835
836 // Now make it into Cocoa screen coords.
837 point.y = [[[[self view] window] screen] frame].size.height - point.y;
838
839 return [NSValue valueWithPoint:point];
840 }
841
842 - (BOOL)accessibilityIsPositionAttributeSettable
843 {
844 // 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
845 // We *could* make use of Java's AccessibleComponent.setLocation() method. Investigate. radr://3953869
846 return NO;
847 }
848
849 // Element type, such as NSAccessibilityRadioButtonRole (NSString). See the role table
850 // at http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html
851 - (NSString *)accessibilityRoleAttribute
852 {
898 - (NSArray *)accessibilitySelectedChildrenAttribute
899 {
900 JNIEnv* env = [ThreadUtilities getJNIEnv];
901 NSArray *selectedChildren = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_SELECTED_CHILDREN allowIgnored:NO];
902 if ([selectedChildren count] > 0) {
903 return selectedChildren;
904 }
905
906 return nil;
907 }
908
909 - (BOOL)accessibilityIsSelectedChildrenAttributeSettable
910 {
911 return NO; // cmcnote: actually it should be. so need to write accessibilitySetSelectedChildrenAttribute also
912 }
913
914 // Element size (NSValue)
915 - (NSValue *)accessibilitySizeAttribute {
916 JNIEnv* env = [ThreadUtilities getJNIEnv];
917 jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
918 NSValue* size = [NSValue valueWithSize:getAxComponentSize(env, axComponent, fComponent)];
919 (*env)->DeleteLocalRef(env, axComponent);
920 return size;
921 }
922
923 - (BOOL)accessibilityIsSizeAttributeSettable
924 {
925 // SIZE is settable in windows if [self styleMask] & NSResizableWindowMask - but windows are heavyweight so we're ok here
926 // SIZE is settable in columns if [[self tableValue] allowsColumnResizing - haven't dealt with columns yet
927 return NO;
928 }
929
930 // Element subrole type, such as NSAccessibilityTableRowSubrole (NSString). See the subrole attribute table at
931 // http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html
932 - (NSString *)accessibilitySubroleAttribute
933 {
934 NSString *value = nil;
935 if ([[self javaRole] isEqualToString:@"passwordtext"])
936 {
937 value = NSAccessibilitySecureTextFieldSubrole;
938 }
939 /*
940 // other subroles. TableRow and OutlineRow may be relevant to us
1092 return NO;
1093 #else
1094 return [[self accessibilityRoleAttribute] isEqualToString:JavaAccessibilityIgnore];
1095 #endif /* JAVA_AX_NO_IGNORES */
1096 }
1097
1098 - (id)accessibilityHitTest:(NSPoint)point withEnv:(JNIEnv *)env
1099 {
1100 static JNF_CLASS_CACHE(jc_Container, "java/awt/Container");
1101 static JNF_STATIC_MEMBER_CACHE(jm_accessibilityHitTest, sjc_CAccessibility, "accessibilityHitTest", "(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;");
1102
1103 // Make it into java screen coords
1104 point.y = [[[[self view] window] screen] frame].size.height - point.y;
1105
1106 jobject jparent = fComponent;
1107
1108 id value = nil;
1109 if (JNFIsInstanceOf(env, jparent, &jc_Container)) {
1110 jobject jaccessible = JNFCallStaticObjectMethod(env, jm_accessibilityHitTest, jparent, (jfloat)point.x, (jfloat)point.y); // AWT_THREADING Safe (AWTRunLoop)
1111 value = [JavaComponentAccessibility createWithAccessible:jaccessible withEnv:env withView:fView];
1112 (*env)->DeleteLocalRef(env, jaccessible);
1113 }
1114
1115 if (value == nil) {
1116 value = self;
1117 }
1118
1119 if ([value accessibilityIsIgnored]) {
1120 value = NSAccessibilityUnignoredAncestor(value);
1121 }
1122
1123 #ifdef JAVA_AX_DEBUG
1124 NSLog(@"%s: %@", __FUNCTION__, value);
1125 #endif
1126 return value;
1127 }
1128
1129 - (id)accessibilityFocusedUIElement
1130 {
1131 static JNF_STATIC_MEMBER_CACHE(jm_getFocusOwner, sjc_CAccessibility, "getFocusOwner", "(Ljava/awt/Component;)Ljavax/accessibility/Accessible;");
1132
1133 JNIEnv *env = [ThreadUtilities getJNIEnv];
1134 id value = nil;
1135
1136 NSWindow* hostWindow = [[self->fView window] retain];
1137 jobject focused = JNFCallStaticObjectMethod(env, jm_getFocusOwner, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1138 [hostWindow release];
1139
1140 if (focused != NULL) {
1141 if (JNFIsInstanceOf(env, focused, &sjc_Accessible)) {
1142 value = [JavaComponentAccessibility createWithAccessible:focused withEnv:env withView:fView];
1143 }
1144 (*env)->DeleteLocalRef(env, focused);
1145 }
1146
1147 if (value == nil) {
1148 value = self;
1149 }
1150 #ifdef JAVA_AX_DEBUG
1151 NSLog(@"%s: %@", __FUNCTION__, value);
1152 #endif
1153 return value;
1154 }
1155
1156 @end
1157
1158 /*
1159 * Class: sun_lwawt_macosx_CAccessibility
1160 * Method: focusChanged
1161 * Signature: ()V
1162 */
1163 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessibility_focusChanged
1164 (JNIEnv *env, jobject jthis)
1244 // Go through the tabs and find selAccessible
1245 _numTabs = [tabs count];
1246 JavaComponentAccessibility *aTab;
1247 NSInteger i;
1248 for (i = 0; i < _numTabs; i++) {
1249 aTab = (JavaComponentAccessibility *)[tabs objectAtIndex:i];
1250 if ([aTab isAccessibleWithEnv:env forAccessible:selAccessible]) {
1251 return aTab;
1252 }
1253 }
1254
1255 return nil;
1256 }
1257
1258 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored
1259 {
1260 jobjectArray jtabsAndRoles = JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, fAccessible, fComponent, whichTabs, allowIgnored); // AWT_THREADING Safe (AWTRunLoop)
1261 if(jtabsAndRoles == NULL) return nil;
1262
1263 jsize arrayLen = (*env)->GetArrayLength(env, jtabsAndRoles);
1264 if (arrayLen == 0) {
1265 (*env)->DeleteLocalRef(env, jtabsAndRoles);
1266 return nil;
1267 }
1268 NSMutableArray *tabs = [NSMutableArray arrayWithCapacity:(arrayLen/2)];
1269
1270 // 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
1271 jobject jtabJavaRole = (*env)->GetObjectArrayElement(env, jtabsAndRoles, 1); // the array entries alternate between tab/role, starting with tab. so the first role is entry 1.
1272 if (jtabJavaRole == NULL) {
1273 (*env)->DeleteLocalRef(env, jtabsAndRoles);
1274 return nil;
1275 }
1276 NSString *tabJavaRole = JNFJavaToNSString(env, JNFGetObjectField(env, jtabJavaRole, sjf_key));
1277
1278 NSInteger i;
1279 NSUInteger tabIndex = (whichTabs >= 0) ? whichTabs : 0; // if we're getting one particular child, make sure to set its index correctly
1280 for(i = 0; i < arrayLen; i+=2) {
1281 jobject jtab = (*env)->GetObjectArrayElement(env, jtabsAndRoles, i);
1282 JavaComponentAccessibility *tab = [[[TabGroupControlAccessibility alloc] initWithParent:self withEnv:env withAccessible:jtab withIndex:tabIndex withTabGroup:axContext withView:[self view] withJavaRole:tabJavaRole] autorelease];
1283 [tabs addObject:tab];
1284 tabIndex++;
1285 }
1286 (*env)->DeleteLocalRef(env, jtabsAndRoles);
1287 return tabs;
1288 }
1289
1290 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored
1291 {
1292 // Contents are the children of the selected tab.
1293 id currentTab = [self currentTabWithEnv:env withAxContext:axContext];
1294 if (currentTab == nil) return nil;
1295
1296 NSArray *contents = [JavaComponentAccessibility childrenOfParent:currentTab withEnv:env withChildrenCode:whichTabs allowIgnored:allowIgnored];
1297 if ([contents count] <= 0) return nil;
1298 return contents;
1299 }
1300
1301 - (id) accessibilityTabsAttribute
1302 {
1303 JNIEnv *env = [ThreadUtilities getJNIEnv];
1304 jobject axContext = [self axContextWithEnv:env];
1305 id tabs = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO];
1306 (*env)->DeleteLocalRef(env, axContext);
1307 return tabs;
1308 }
1309
1310 - (BOOL)accessibilityIsTabsAttributeSettable
1311 {
1312 return NO; //cmcnote: not sure.
1313 }
1314
1315 - (NSInteger)numTabs
1316 {
1317 if (_numTabs == -1) {
1318 _numTabs = [[self accessibilityTabsAttribute] count];
1319 }
1320 return _numTabs;
1321 }
1322
1323 - (NSArray *) accessibilityContentsAttribute
1324 {
1325 JNIEnv *env = [ThreadUtilities getJNIEnv];
1326 jobject axContext = [self axContextWithEnv:env];
1327 NSArray* cont = [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO];
1328 (*env)->DeleteLocalRef(env, axContext);
1329 return cont;
1330 }
1331
1332 - (BOOL)accessibilityIsContentsAttributeSettable
1333 {
1334 return NO;
1335 }
1336
1337 // axValue is the currently selected tab
1338 -(id) accessibilityValueAttribute
1339 {
1340 JNIEnv *env = [ThreadUtilities getJNIEnv];
1341 jobject axContext = [self axContextWithEnv:env];
1342 id val = [self currentTabWithEnv:env withAxContext:axContext];
1343 (*env)->DeleteLocalRef(env, axContext);
1344 return val;
1345 }
1346
1347 - (BOOL)accessibilityIsValueAttributeSettable
1348 {
1349 return YES;
1350 }
1351
1352 - (void)accessibilitySetValueAttribute:(id)value //cmcnote: not certain this is ever actually called. investigate.
1353 {
1354 // set the current tab
1355 NSNumber *number = (NSNumber *)value;
1356 if (![number boolValue]) return;
1357
1358 JNIEnv *env = [ThreadUtilities getJNIEnv];
1359 jobject axContext = [self axContextWithEnv:env];
1360 setAxContextSelection(env, axContext, fIndex, fComponent);
1361 (*env)->DeleteLocalRef(env, axContext);
1362 }
1363
1364 - (NSArray *)accessibilityChildrenAttribute
1365 {
1366 //children = AXTabs + AXContents
1367 NSArray *tabs = [self accessibilityTabsAttribute];
1368 NSArray *contents = [self accessibilityContentsAttribute];
1369
1370 NSMutableArray *children = [NSMutableArray arrayWithCapacity:[tabs count] + [contents count]];
1371 [children addObjectsFromArray:tabs];
1372 [children addObjectsFromArray:contents];
1373
1374 return (NSArray *)children;
1375 }
1376
1377 // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children.
1378 // See similar optimization in JavaComponentAccessibility. We have to extend the base implementation here, since
1379 // children of tabs are AXTabs + AXContents
1380 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount {
1381 NSArray *result = nil;
1382 if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
1383 // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child
1384 JNIEnv *env = [ThreadUtilities getJNIEnv];
1385 jobject axContext = [self axContextWithEnv:env];
1386
1387 //children = AXTabs + AXContents
1388 NSArray *children = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:index allowIgnored:NO]; // first look at the tabs
1389 if ([children count] > 0) {
1390 result = children;
1391 } else {
1392 children= [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:(index-[self numTabs]) allowIgnored:NO];
1393 if ([children count] > 0) {
1394 result = children;
1395 }
1396 }
1397 (*env)->DeleteLocalRef(env, axContext);
1398 } else {
1399 result = [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
1400 }
1401 return result;
1402 }
1403
1404 @end
1405
1406
1407 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component);
1408
1409 @implementation TabGroupControlAccessibility
1410
1411 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole
1412 {
1413 self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole];
1414 if (self) {
1415 if (tabGroup != NULL) {
1416 fTabGroupAxContext = JNFNewGlobalRef(env, tabGroup);
1417 } else {
1422 }
1423
1424 - (void)dealloc
1425 {
1426 JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
1427
1428 if (fTabGroupAxContext != NULL) {
1429 JNFDeleteGlobalRef(env, fTabGroupAxContext);
1430 fTabGroupAxContext = NULL;
1431 }
1432
1433 [super dealloc];
1434 }
1435
1436 - (id)accessibilityValueAttribute
1437 {
1438 JNIEnv *env = [ThreadUtilities getJNIEnv];
1439 jobject axContext = [self axContextWithEnv:env];
1440
1441 // Returns the current selection of the page tab list
1442 id val = [NSNumber numberWithBool:ObjectEquals(env, axContext, getAxContextSelection(env, [self tabGroup], fIndex, fComponent), fComponent)];
1443 (*env)->DeleteLocalRef(env, axContext);
1444 return val;
1445 }
1446
1447 - (void)getActionsWithEnv:(JNIEnv *)env
1448 {
1449 TabGroupAction *action = [[TabGroupAction alloc] initWithEnv:env withTabGroup:[self tabGroup] withIndex:fIndex withComponent:fComponent];
1450 [fActions setObject:action forKey:NSAccessibilityPressAction];
1451 [action release];
1452 }
1453
1454 - (jobject)tabGroup
1455 {
1456 if (fTabGroupAxContext == NULL) {
1457 JNIEnv* env = [ThreadUtilities getJNIEnv];
1458 jobject tabGroupAxContext = [(JavaComponentAccessibility *)[self parent] axContextWithEnv:env];
1459 fTabGroupAxContext = JNFNewGlobalRef(env, tabGroupAxContext);
1460 (*env)->DeleteLocalRef(env, tabGroupAxContext);
1461 }
1462 return fTabGroupAxContext;
1463 }
1464
1465 @end
1466
1467
1468 @implementation ScrollAreaAccessibility
1469
1470 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
1471 {
1472 NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env];
1473
1474 [names addObject:NSAccessibilityHorizontalScrollBarAttribute];
1475 [names addObject:NSAccessibilityVerticalScrollBarAttribute];
1476 [names addObject:NSAccessibilityContentsAttribute];
1477
1478 return names;
1479 }
1480
1481 - (id)accessibilityHorizontalScrollBarAttribute
1482 {
1483 JNIEnv *env = [ThreadUtilities getJNIEnv];
1484
1485 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1486 if ([children count] <= 0) return nil;
1487
1488 // The scroll bars are in the children.
1489 JavaComponentAccessibility *aElement;
1490 NSEnumerator *enumerator = [children objectEnumerator];
1491 while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) {
1492 if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) {
1493 jobject elementAxContext = [aElement axContextWithEnv:env];
1494 if (isHorizontal(env, elementAxContext, fComponent)) {
1495 (*env)->DeleteLocalRef(env, elementAxContext);
1496 return aElement;
1497 }
1498 (*env)->DeleteLocalRef(env, elementAxContext);
1499 }
1500 }
1501
1502 return nil;
1503 }
1504
1505 - (BOOL)accessibilityIsHorizontalScrollBarAttributeSettable
1506 {
1507 return NO;
1508 }
1509
1510 - (id)accessibilityVerticalScrollBarAttribute
1511 {
1512 JNIEnv *env = [ThreadUtilities getJNIEnv];
1513
1514 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1515 if ([children count] <= 0) return nil;
1516
1517 // The scroll bars are in the children.
1518 NSEnumerator *enumerator = [children objectEnumerator];
1519 JavaComponentAccessibility *aElement;
1520 while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) {
1521 if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) {
1522 jobject elementAxContext = [aElement axContextWithEnv:env];
1523 if (isVertical(env, elementAxContext, fComponent)) {
1524 (*env)->DeleteLocalRef(env, elementAxContext);
1525 return aElement;
1526 }
1527 (*env)->DeleteLocalRef(env, elementAxContext);
1528 }
1529 }
1530
1531 return nil;
1532 }
1533
1534 - (BOOL)accessibilityIsVerticalScrollBarAttributeSettable
1535 {
1536 return NO;
1537 }
1538
1539 - (NSArray *)accessibilityContentsAttribute
1540 {
1541 JNIEnv *env = [ThreadUtilities getJNIEnv];
1542 NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1543
1544 if ([children count] <= 0) return nil;
1545 NSArray *contents = [NSMutableArray arrayWithCapacity:[children count]];
1546
1547 // The scroll bars are in the children. children less the scroll bars is the contents
|