diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java index b52be53385784..774897221c1af 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java @@ -34,11 +34,13 @@ import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.lang.annotation.Native; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; +import java.util.Arrays; import javax.accessibility.Accessible; import javax.accessibility.AccessibleAction; @@ -57,6 +59,8 @@ import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JTextArea; +import javax.swing.JList; +import javax.swing.JTree; import javax.swing.KeyStroke; import sun.awt.AWTAccessor; @@ -116,12 +120,7 @@ public void propertyChange(final PropertyChangeEvent evt) { private native void focusChanged(); static T invokeAndWait(final Callable callable, final Component c) { - if (c != null) { - try { - return LWCToolkit.invokeAndWait(callable, c); - } catch (final Exception e) { e.printStackTrace(); } - } - return null; + return invokeAndWait(callable, c, null); } static T invokeAndWait(final Callable callable, final Component c, final T defValue) { @@ -551,6 +550,10 @@ public void run() { if (pac == null) return; AccessibleSelection as = pac.getAccessibleSelection(); if (as == null) return; + if (parent instanceof JList) { + ((JList) parent).setSelectedIndex(i); + return; + } as.addAccessibleSelection(i); } }, c); @@ -651,77 +654,148 @@ public boolean[] call() throws Exception { // Duplicated from JavaComponentAccessibility // Note that values >=0 are indexes into the child array - static final int JAVA_AX_ALL_CHILDREN = -1; - static final int JAVA_AX_SELECTED_CHILDREN = -2; - static final int JAVA_AX_VISIBLE_CHILDREN = -3; + @Native static final int JAVA_AX_ALL_CHILDREN = -1; + @Native static final int JAVA_AX_SELECTED_CHILDREN = -2; + @Native static final int JAVA_AX_VISIBLE_CHILDREN = -3; // Each child takes up two entries in the array: one for itself and one for its role public static Object[] getChildrenAndRoles(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) { if (a == null) return null; return invokeAndWait(new Callable() { public Object[] call() throws Exception { - ArrayList childrenAndRoles = new ArrayList(); - _addChildren(a, whichChildren, allowIgnored, childrenAndRoles); - - /* In the case of fetching a selection, need to check to see if - * the active descendant is at the beginning of the list. If it - * is not it needs to be moved to the beginning of the list so - * VoiceOver will annouce it correctly. The list returned - * from Java is always in order from top to bottom, but when shift - * selecting downward (extending the list) or multi-selecting using - * the VO keys control+option+command+return the active descendant - * is not at the top of the list in the shift select down case and - * may not be in the multi select case. - */ - if (whichChildren == JAVA_AX_SELECTED_CHILDREN) { - if (!childrenAndRoles.isEmpty()) { - AccessibleContext activeDescendantAC = - CAccessible.getActiveDescendant(a); - if (activeDescendantAC != null) { - String activeDescendantName = - activeDescendantAC.getAccessibleName(); - AccessibleRole activeDescendantRole = - activeDescendantAC.getAccessibleRole(); - // Move active descendant to front of list. - // List contains pairs of each selected item's - // Accessible and AccessibleRole. - ArrayList newArray = new ArrayList(); - int count = childrenAndRoles.size(); - Accessible currentAccessible = null; - AccessibleContext currentAC = null; - String currentName = null; - AccessibleRole currentRole = null; - for (int i = 0; i < count; i+=2) { - // Is this the active descendant? - currentAccessible = (Accessible)childrenAndRoles.get(i); - currentAC = currentAccessible.getAccessibleContext(); - currentName = currentAC.getAccessibleName(); - currentRole = (AccessibleRole)childrenAndRoles.get(i+1); - if (currentName != null && currentName.equals(activeDescendantName) && - currentRole.equals(activeDescendantRole) ) { - newArray.add(0, currentAccessible); - newArray.add(1, currentRole); - } else { - newArray.add(currentAccessible); - newArray.add(currentRole); - } - } - childrenAndRoles = newArray; + return getChildrenAndRolesImpl(a, c, whichChildren, allowIgnored); + } + }, c); + } + + private static Object[] getChildrenAndRolesImpl(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) { + if (a == null) return null; + + ArrayList childrenAndRoles = new ArrayList(); + _addChildren(a, whichChildren, allowIgnored, childrenAndRoles); + + /* In case of fetching a selection, we need to check if +* the active descendant is at the beginning of the list, or +* otherwise move it, so that VoiceOver announces it correctly. + * The java list is always in order from top to bottom, but when + * (1) shift-selecting downward (extending the list) or (2) multi-selecting with + * the VO keys (CTRL+ALT+CMD+RETURN) the active descendant + * is not at the top of the list in the 1st case and may not be in the 2nd. + */ + if (whichChildren == JAVA_AX_SELECTED_CHILDREN) { + if (!childrenAndRoles.isEmpty()) { + AccessibleContext activeDescendantAC = + CAccessible.getActiveDescendant(a); + if (activeDescendantAC != null) { + String activeDescendantName = + activeDescendantAC.getAccessibleName(); + AccessibleRole activeDescendantRole = + activeDescendantAC.getAccessibleRole(); + // Move active descendant to front of list. + // List contains pairs of each selected item's + // Accessible and AccessibleRole. + ArrayList newArray = new ArrayList(); + int count = childrenAndRoles.size(); + Accessible currentAccessible = null; + AccessibleContext currentAC = null; + String currentName = null; + AccessibleRole currentRole = null; + for (int i = 0; i < count; i += 2) { + // Is this the active descendant? + currentAccessible = (Accessible) childrenAndRoles.get(i); + currentAC = currentAccessible.getAccessibleContext(); + currentName = currentAC.getAccessibleName(); + currentRole = (AccessibleRole) childrenAndRoles.get(i + 1); + if (currentName != null && currentName.equals(activeDescendantName) && + currentRole.equals(activeDescendantRole)) { + newArray.add(0, currentAccessible); + newArray.add(1, currentRole); + } else { + newArray.add(currentAccessible); + newArray.add(currentRole); } } + childrenAndRoles = newArray; } + } + } + + if ((whichChildren < 0) || (whichChildren * 2 >= childrenAndRoles.size())) { + return childrenAndRoles.toArray(); + } + + return new Object[]{childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1)}; + } + + // This method is called from the native + // Each child takes up three entries in the array: one for itself, one for its role, and one for the recursion level + private static Object[] getChildrenAndRolesRecursive(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored, final int level) { + if (a == null) return null; + return invokeAndWait(new Callable() { + public Object[] call() throws Exception { + ArrayList currentLevelChildren = new ArrayList(); + ArrayList allChildren = new ArrayList(); + ArrayList parentStack = new ArrayList(); + parentStack.add(a); + ArrayList indexses = new ArrayList(); + Integer index = 0; + int currentLevel = level; + while (!parentStack.isEmpty()) { + Accessible p = parentStack.get(parentStack.size() - 1); + + currentLevelChildren.addAll(Arrays.asList(getChildrenAndRolesImpl(p, c, JAVA_AX_ALL_CHILDREN, allowIgnored))); + if ((currentLevelChildren.size() == 0) || (index >= currentLevelChildren.size())) { + if (!parentStack.isEmpty()) parentStack.remove(parentStack.size() - 1); + if (!indexses.isEmpty()) index = indexses.remove(indexses.size() - 1); + currentLevel -= 1; + currentLevelChildren.clear(); + continue; + } + + Accessible ca = null; + Object obj = currentLevelChildren.get(index); + if (!(obj instanceof Accessible)) { + index += 2; + currentLevelChildren.clear(); + continue; + } + ca = (Accessible) obj; + Object role = currentLevelChildren.get(index + 1); + currentLevelChildren.clear(); + + AccessibleContext cac = ca.getAccessibleContext(); + if (cac == null) { + index += 2; + continue; + } + + if ((cac.getAccessibleStateSet().contains(AccessibleState.SELECTED) && (whichChildren == JAVA_AX_SELECTED_CHILDREN)) || + (cac.getAccessibleStateSet().contains(AccessibleState.VISIBLE) && (whichChildren == JAVA_AX_VISIBLE_CHILDREN)) || + (whichChildren == JAVA_AX_ALL_CHILDREN)) { + allChildren.add(ca); + allChildren.add(role); + allChildren.add(String.valueOf(currentLevel)); + } + + index += 2; + + if (cac.getAccessibleStateSet().contains(AccessibleState.EXPANDED)) { + parentStack.add(ca); + indexses.add(index); + index = 0; + currentLevel += 1; + continue; + } - if ((whichChildren < 0) || (whichChildren * 2 >= childrenAndRoles.size())) { - return childrenAndRoles.toArray(); } - return new Object[] { childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1) }; + return allChildren.toArray(); } }, c); } - private static final int JAVA_AX_ROWS = 1; - private static final int JAVA_AX_COLS = 2; + @Native private static final int JAVA_AX_ROWS = 1; + @Native private static final int JAVA_AX_COLS = 2; public static int getTableInfo(final Accessible a, final Component c, final int info) { @@ -740,6 +814,23 @@ public static int getTableInfo(final Accessible a, final Component c, }, c); } + private static int[] getTableSelectedInfo(final Accessible a, final Component c, + final int info) { + if (a == null) return null; + return invokeAndWait(() -> { + AccessibleContext ac = a.getAccessibleContext(); + AccessibleTable table = ac.getAccessibleTable(); + if (table != null) { + if (info == JAVA_AX_COLS) { + return table.getSelectedAccessibleColumns(); + } else if (info == JAVA_AX_ROWS) { + return table.getSelectedAccessibleRows(); + } + } + return null; + }, c); + } + private static AccessibleRole getAccessibleRoleForLabel(JLabel l, AccessibleRole fallback) { String text = l.getText(); if (text != null && text.length() > 0) { @@ -858,4 +949,18 @@ public Long call() throws Exception { } }, (Component)ax); } + + private static boolean isTreeRootVisible(Accessible a, Component c) { + if (a == null) return false; + + return invokeAndWait(new Callable() { + public Boolean call() throws Exception { + Accessible sa = CAccessible.getSwingAccessible(a); + if (sa instanceof JTree) { + return ((JTree) sa).isRootVisible(); + } + return false; + } + }, c); + } } diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java index 212ddb6969c10..72bfa8d541e47 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java @@ -31,11 +31,7 @@ import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; -import javax.swing.JProgressBar; import javax.swing.JTabbedPane; -import javax.swing.JSlider; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import static javax.accessibility.AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY; import static javax.accessibility.AccessibleContext.ACCESSIBLE_CARET_PROPERTY; @@ -75,6 +71,8 @@ public static CAccessible getCAccessible(final Accessible a) { private static native void menuOpened(long ptr); private static native void menuClosed(long ptr); private static native void menuItemSelected(long ptr); + private static native void treeNodeExpanded(long ptr); + private static native void treeNodeCollapsed(long ptr); private Accessible accessible; @@ -137,6 +135,13 @@ public void propertyChange(PropertyChangeEvent e) { if (parentAccessible != null) { parentRole = parentAccessible.getAccessibleContext().getAccessibleRole(); } + + if (newValue == AccessibleState.EXPANDED) { + treeNodeExpanded(ptr); + } else if (newValue == AccessibleState.COLLAPSED) { + treeNodeCollapsed(ptr); + } + // At least for now don't handle combo box menu state changes. // This may change when later fixing issues which currently // exist for combo boxes, but for now the following is only diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m index 60da8dcac1d96..8e44052333c2c 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m @@ -27,8 +27,7 @@ #import "CGLGraphicsConfig.h" #import "AWTView.h" #import "AWTWindow.h" -#import "JavaComponentAccessibility.h" -#import "JavaTextAccessibility.h" +#import "a11y/CommonComponentAccessibility.h" #import "JavaAccessibilityUtilities.h" #import "GeomUtilities.h" #import "ThreadUtilities.h" @@ -611,42 +610,29 @@ + (AWTView *) awtView:(JNIEnv*)env ofAccessible:(jobject)jaccessible - (id)getAxData:(JNIEnv*)env { jobject jcomponent = [self awtComponent:env]; - id ax = [[[JavaComponentAccessibility alloc] initWithParent:self withEnv:env withAccessible:jcomponent withIndex:-1 withView:self withJavaRole:nil] autorelease]; + id ax = [[[CommonComponentAccessibility alloc] initWithParent:self withEnv:env withAccessible:jcomponent withIndex:-1 withView:self withJavaRole:nil] autorelease]; (*env)->DeleteLocalRef(env, jcomponent); return ax; } -- (NSArray *)accessibilityAttributeNames -{ - return [[super accessibilityAttributeNames] arrayByAddingObject:NSAccessibilityChildrenAttribute]; -} - // NSAccessibility messages -// attribute methods -- (id)accessibilityAttributeValue:(NSString *)attribute +- (id)accessibilityChildren { AWT_ASSERT_APPKIT_THREAD; + JNIEnv *env = [ThreadUtilities getJNIEnv]; - if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) - { - JNIEnv *env = [ThreadUtilities getJNIEnv]; - - (*env)->PushLocalFrame(env, 4); + (*env)->PushLocalFrame(env, 4); - id result = NSAccessibilityUnignoredChildrenForOnlyChild([self getAxData:env]); + id result = NSAccessibilityUnignoredChildrenForOnlyChild([self getAxData:env]); - (*env)->PopLocalFrame(env, NULL); + (*env)->PopLocalFrame(env, NULL); - return result; - } - else - { - return [super accessibilityAttributeValue:attribute]; - } + return result; } -- (BOOL)accessibilityIsIgnored + +- (BOOL)isAccessibilityElement { - return YES; + return NO; } - (id)accessibilityHitTest:(NSPoint)point @@ -656,7 +642,7 @@ - (id)accessibilityHitTest:(NSPoint)point (*env)->PushLocalFrame(env, 4); - id result = [[self getAxData:env] accessibilityHitTest:point withEnv:env]; + id result = [[self getAxData:env] accessibilityHitTest:point]; (*env)->PopLocalFrame(env, NULL); @@ -681,17 +667,24 @@ - (id)accessibilityFocusedUIElement // --- Services menu support for lightweights --- // finds the focused accessible element, and if it is a text element, obtains the text from it -- (NSString *)accessibleSelectedText +- (NSString *)accessibilitySelectedText { id focused = [self accessibilityFocusedUIElement]; - if (![focused isKindOfClass:[JavaTextAccessibility class]]) return nil; - return [(JavaTextAccessibility *)focused accessibilitySelectedTextAttribute]; + if (![focused respondsToSelector:@selector(accessibilitySelectedText)]) return nil; + return [focused accessibilitySelectedText]; +} + +- (void)setAccessibilitySelectedText:(NSString *)accessibilitySelectedText { + id focused = [self accessibilityFocusedUIElement]; + if ([focused respondsToSelector:@selector(setAccessibilitySelectedText:)]) { + [focused setAccessibilitySelectedText:accessibilitySelectedText]; +} } // same as above, but converts to RTFD - (NSData *)accessibleSelectedTextAsRTFD { - NSString *selectedText = [self accessibleSelectedText]; + NSString *selectedText = [self accessibilitySelectedText]; NSAttributedString *styledText = [[NSAttributedString alloc] initWithString:selectedText]; NSData *rtfdData = [styledText RTFDFromRange:NSMakeRange(0, [styledText length]) documentAttributes: @@ -704,8 +697,8 @@ - (NSData *)accessibleSelectedTextAsRTFD - (BOOL)replaceAccessibleTextSelection:(NSString *)text { id focused = [self accessibilityFocusedUIElement]; - if (![focused isKindOfClass:[JavaTextAccessibility class]]) return NO; - [(JavaTextAccessibility *)focused accessibilitySetSelectedTextAttribute:text]; + if (![focused respondsToSelector:@selector(setAccessibilitySelectedText)]) return NO; + [focused setAccessibilitySelectedText:text]; return YES; } @@ -715,7 +708,7 @@ - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)retu if ([[self window] firstResponder] != self) return nil; // let AWT components handle themselves if ([sendType isEqual:NSStringPboardType] || [returnType isEqual:NSStringPboardType]) { - NSString *selectedText = [self accessibleSelectedText]; + NSString *selectedText = [self accessibilitySelectedText]; if (selectedText) return self; } @@ -728,7 +721,7 @@ - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard types:(NSArray *)types if ([types containsObject:NSStringPboardType]) { [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; - return [pboard setString:[self accessibleSelectedText] forType:NSStringPboardType]; + return [pboard setString:[self accessibilitySelectedText] forType:NSStringPboardType]; } if ([types containsObject:NSRTFDPboardType]) diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.h index a628c4de5b16f..b8042db69b771 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.h @@ -26,6 +26,10 @@ #import #import +extern NSMutableDictionary *sActions; +extern NSMutableDictionary *sActionSelectors; +extern NSMutableArray *sAllActionSelectors; +void initializeActions(); @protocol JavaAccessibilityAction diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.m index 03a5630b608a2..06e998ec4098e 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.m @@ -29,6 +29,10 @@ #import "ThreadUtilities.h" #import "JNIUtilities.h" +NSMutableDictionary *sActions = nil; +NSMutableDictionary *sActionSelectors = nil; +NSMutableArray *sAllActionSelectors = nil; +void initializeActions(); @implementation JavaAxAction @@ -148,3 +152,31 @@ - (void)perform } @end + +void initializeActions() { + int actionsCount = 5; + + sActions = [[NSMutableDictionary alloc] initWithCapacity:actionsCount]; + + [sActions setObject:NSAccessibilityPressAction forKey:@"click"]; + [sActions setObject:NSAccessibilityIncrementAction forKey:@"increment"]; + [sActions setObject:NSAccessibilityDecrementAction forKey:@"decrement"]; + [sActions setObject:NSAccessibilityShowMenuAction forKey:@"togglePopup"]; + [sActions setObject:NSAccessibilityPressAction forKey:@"toggleExpand"]; + + sActionSelectors = [[NSMutableDictionary alloc] initWithCapacity:actionsCount]; + + [sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformPress)) forKey:NSAccessibilityPressAction]; + [sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformShowMenu)) forKey:NSAccessibilityShowMenuAction]; + [sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformDecrement)) forKey:NSAccessibilityDecrementAction]; + [sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformIncrement)) forKey:NSAccessibilityIncrementAction]; + [sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformPick)) forKey:NSAccessibilityPickAction]; + + sAllActionSelectors = [[NSMutableArray alloc] initWithCapacity:actionsCount]; + + [sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformPick))]; + [sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformIncrement))]; + [sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformDecrement))]; + [sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformShowMenu))]; + [sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformPress))]; +} diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h index f3dfe247ede90..b829335fc2992 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h @@ -48,6 +48,7 @@ BOOL isVertical(JNIEnv *env, jobject axContext, jobject component); BOOL isHorizontal(JNIEnv *env, jobject axContext, jobject component); BOOL isShowing(JNIEnv *env, jobject axContext, jobject component); BOOL isSelectable(JNIEnv *env, jobject axContext, jobject component); +BOOL isExpanded(JNIEnv *env, jobject axContext, jobject component); NSPoint getAxComponentLocationOnScreen(JNIEnv *env, jobject axComponent, jobject component); jint getAxTextCharCount(JNIEnv *env, jobject axText, jobject component); @@ -60,3 +61,6 @@ void JavaAccessibilitySetAttributeValue(id element, NSString *attribute, id valu void JavaAccessibilityRaiseSetAttributeToIllegalTypeException(const char *functionName, id element, NSString *attribute, id value); void JavaAccessibilityRaiseUnimplementedAttributeException(const char *functionName, id element, NSString *attribute); void JavaAccessibilityRaiseIllegalParameterTypeException(const char *functionName, id element, NSString *attribute, id parameter); +BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component); +NSNumber* JavaNumberToNSNumber(JNIEnv *env, jobject jnumber); +NSValue *javaIntArrayToNSRangeValue(JNIEnv* env, jintArray array); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m index 12817e63ecd31..9520c3d552b49 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m @@ -27,6 +27,7 @@ #import "JNIUtilities.h" #import +#import "ThreadUtilities.h" static BOOL JavaAccessibilityIsSupportedAttribute(id element, NSString *attribute); static void JavaAccessibilityLogError(NSString *message); @@ -199,6 +200,20 @@ BOOL isSelectable(JNIEnv *env, jobject axContext, jobject component) return selectable; } +BOOL isExpanded(JNIEnv *env, jobject axContext, jobject component) +{ + GET_ACCESSIBLESTATE_CLASS_RETURN(NO); + DECLARE_STATIC_FIELD_RETURN(jm_EXPANDED, + sjc_AccessibleState, + "EXPANDED", + "Ljavax/accessibility/AccessibleState;", NO ); + jobject axExpandedState = (*env)->GetStaticObjectField(env, sjc_AccessibleState, jm_EXPANDED); + CHECK_EXCEPTION_NULL_RETURN(axExpandedState, NO); + BOOL expanded = containsAxState(env, axContext, axExpandedState, component); + (*env)->DeleteLocalRef(env, axExpandedState); + return expanded; +} + NSPoint getAxComponentLocationOnScreen(JNIEnv *env, jobject axComponent, jobject component) { GET_CACCESSIBILITY_CLASS_RETURN(NSZeroPoint); @@ -348,6 +363,75 @@ static void JavaAccessibilityLogError(NSString *message) NSLog(@"!!! %@", message); } +/* + * Returns Object.equals for the two items + * This may use LWCToolkit.invokeAndWait(); don't call while holding fLock + * and try to pass a component so the event happens on the correct thread. + */ +BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component) +{ + DECLARE_CLASS_RETURN(sjc_Object, "java/lang/Object", NO); + DECLARE_METHOD_RETURN(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z", NO); + + if ((a == NULL) && (b == NULL)) return YES; + if ((a == NULL) || (b == NULL)) return NO; + + if (pthread_main_np() != 0) { + // If we are on the AppKit thread + DECLARE_CLASS_RETURN(sjc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit", NO); + DECLARE_STATIC_METHOD_RETURN(jm_doEquals, sjc_LWCToolkit, "doEquals", + "(Ljava/lang/Object;Ljava/lang/Object;Ljava/awt/Component;)Z", NO); + return (*env)->CallStaticBooleanMethod(env, sjc_LWCToolkit, jm_doEquals, a, b, component); + CHECK_EXCEPTION(); + } + + jboolean jb = (*env)->CallBooleanMethod(env, a, jm_equals, b); + CHECK_EXCEPTION(); + return jb; +} + +/* + * The java/lang/Number concrete class could be for any of the Java primitive + * numerical types or some other subclass. + * All existing A11Y code uses Integer so that is what we look for first + * But all must be able to return a double and NSNumber accepts a double, + * so that's the fall back. + */ +NSNumber* JavaNumberToNSNumber(JNIEnv *env, jobject jnumber) { + if (jnumber == NULL) { + return nil; + } + DECLARE_CLASS_RETURN(jnumber_Class, "java/lang/Number", nil); + DECLARE_CLASS_RETURN(jinteger_Class, "java/lang/Integer", nil); + DECLARE_METHOD_RETURN(jm_intValue, jnumber_Class, "intValue", "()I", nil); + DECLARE_METHOD_RETURN(jm_doubleValue, jnumber_Class, "doubleValue", "()D", nil); + if ((*env)->IsInstanceOf(env, jnumber, jinteger_Class)) { + jint i = (*env)->CallIntMethod(env, jnumber, jm_intValue); + CHECK_EXCEPTION(); + return [NSNumber numberWithInteger:i]; + } else { + jdouble d = (*env)->CallDoubleMethod(env, jnumber, jm_doubleValue); + CHECK_EXCEPTION(); + return [NSNumber numberWithDouble:d]; + } +} + +/* + * Converts an int array to an NSRange wrapped inside an NSValue + * takes [start, end] values and returns [start, end - start] + */ +NSValue *javaIntArrayToNSRangeValue(JNIEnv* env, jintArray array) { + jint *values = (*env)->GetIntArrayElements(env, array, 0); + if (values == NULL) { + // Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck. + NSLog(@"%s failed calling GetIntArrayElements", __FUNCTION__); + return nil; + }; + NSValue *value = [NSValue valueWithRange:NSMakeRange(values[0], values[1] - values[0])]; + (*env)->ReleaseIntArrayElements(env, array, values, 0); + return value; +} + // end appKit copies /* @@ -417,13 +501,13 @@ void initializeRoles() [sRoles setObject:NSAccessibilitySplitGroupRole forKey:@"splitpane"]; [sRoles setObject:NSAccessibilityValueIndicatorRole forKey:@"statusbar"]; [sRoles setObject:NSAccessibilityGroupRole forKey:@"swingcomponent"]; - [sRoles setObject:NSAccessibilityGridRole forKey:@"table"]; + [sRoles setObject:NSAccessibilityTableRole forKey:@"table"]; [sRoles setObject:NSAccessibilityTextFieldRole forKey:@"text"]; [sRoles setObject:NSAccessibilityTextAreaRole forKey:@"textarea"]; // supports top/bottom of document notifications: CAccessability.getAccessibleRole() [sRoles setObject:NSAccessibilityCheckBoxRole forKey:@"togglebutton"]; [sRoles setObject:NSAccessibilityToolbarRole forKey:@"toolbar"]; [sRoles setObject:JavaAccessibilityIgnore forKey:@"tooltip"]; - [sRoles setObject:NSAccessibilityBrowserRole forKey:@"tree"]; + [sRoles setObject:NSAccessibilityOutlineRole forKey:@"tree"]; [sRoles setObject:NSAccessibilityUnknownRole forKey:@"unknown"]; [sRoles setObject:JavaAccessibilityIgnore forKey:@"viewport"]; [sRoles setObject:JavaAccessibilityIgnore forKey:@"window"]; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m index 33e838b070a1f..9db8342f8444a 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m @@ -29,7 +29,6 @@ // #import "JavaComponentAccessibility.h" -#import "a11y/CommonComponentAccessibility.h" #import "sun_lwawt_macosx_CAccessibility.h" #import @@ -45,6 +44,12 @@ #import "JNIUtilities.h" #import "AWTView.h" +// these constants are duplicated in CAccessibility.java +#define JAVA_AX_ALL_CHILDREN (-1) +#define JAVA_AX_SELECTED_CHILDREN (-2) +#define JAVA_AX_VISIBLE_CHILDREN (-3) +// If the value is >=0, it's an index + // GET* macros defined in JavaAccessibilityUtilities.h, so they can be shared. static jclass sjc_CAccessibility = NULL; @@ -82,7 +87,7 @@ static NSMutableDictionary *sAttributeNamesForRoleCache = nil; static NSObject *sAttributeNamesLOCK = nil; -@interface TabGroupAccessibility : JavaComponentAccessibility { +@interface TabGroupLegacyAccessibility : JavaComponentAccessibility { NSInteger _numTabs; } @@ -112,7 +117,7 @@ - (void)getActionsWithEnv:(JNIEnv *)env; - (id)accessibilityValueAttribute; @end -@interface TableAccessibility : JavaComponentAccessibility { +@interface TableLegacyAccessibility : JavaComponentAccessibility { } - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env; @@ -362,21 +367,18 @@ + (JavaComponentAccessibility *) createWithParent:(JavaComponentAccessibility *) // otherwise, create a new instance JavaComponentAccessibility *newChild = nil; - newChild = [CommonComponentAccessibility getComponentAccessibility:javaRole]; - if (newChild == nil) { - if ([javaRole isEqualToString:@"pagetablist"]) { - newChild = [TabGroupAccessibility alloc]; - } else if ([javaRole isEqualToString:@"table"]) { - newChild = [TableAccessibility alloc]; + if ([javaRole isEqualToString:@"pagetablist"]) { + newChild = [TabGroupLegacyAccessibility alloc]; + } else if ([javaRole isEqualToString:@"table"]) { + newChild = [TableLegacyAccessibility alloc]; + } else { + NSString *nsRole = [sRoles objectForKey:javaRole]; + if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] || + [nsRole isEqualToString:NSAccessibilityTextAreaRole] || + [nsRole isEqualToString:NSAccessibilityTextFieldRole]) { + newChild = [JavaTextAccessibility alloc]; } else { - NSString *nsRole = [sRoles objectForKey:javaRole]; - if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] || - [nsRole isEqualToString:NSAccessibilityTextAreaRole] || - [nsRole isEqualToString:NSAccessibilityTextFieldRole]) { - newChild = [JavaTextAccessibility alloc]; - } else { - newChild = [JavaComponentAccessibility alloc]; - } + newChild = [JavaComponentAccessibility alloc]; } } @@ -387,7 +389,7 @@ + (JavaComponentAccessibility *) createWithParent:(JavaComponentAccessibility *) // This is the only way to know if the menu is opening; visible state change // can't be caught because the listeners are not set up in time. if ( [javaRole isEqualToString:@"popupmenu"] && - ![[parent javaRole] isEqualToString:@"combobox"] ) { + ![[parent javaRole] isEqualToString:@"combobox"] ) { [newChild postMenuOpened]; } @@ -904,32 +906,6 @@ - (NSInteger)accessibilityIndex { return index; } -/* - * The java/lang/Number concrete class could be for any of the Java primitive - * numerical types or some other subclass. - * All existing A11Y code uses Integer so that is what we look for first - * But all must be able to return a double and NSNumber accepts a double, - * so that's the fall back. - */ -static NSNumber* JavaNumberToNSNumber(JNIEnv *env, jobject jnumber) { - if (jnumber == NULL) { - return nil; - } - DECLARE_CLASS_RETURN(jnumber_Class, "java/lang/Number", nil); - DECLARE_CLASS_RETURN(jinteger_Class, "java/lang/Integer", nil); - DECLARE_METHOD_RETURN(jm_intValue, jnumber_Class, "intValue", "()I", nil); - DECLARE_METHOD_RETURN(jm_doubleValue, jnumber_Class, "doubleValue", "()D", nil); - if ((*env)->IsInstanceOf(env, jnumber, jinteger_Class)) { - jint i = (*env)->CallIntMethod(env, jnumber, jm_intValue); - CHECK_EXCEPTION(); - return [NSNumber numberWithInteger:i]; - } else { - jdouble d = (*env)->CallDoubleMethod(env, jnumber, jm_doubleValue); - CHECK_EXCEPTION(); - return [NSNumber numberWithDouble:d]; - } -} - // Element's maximum value (id) - (id)accessibilityMaxValueAttribute { @@ -1600,7 +1576,7 @@ - (id)accessibilityFocusedUIElement JNI_COCOA_EXIT(env); } -@implementation TabGroupAccessibility +@implementation TabGroupLegacyAccessibility - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole { @@ -1802,9 +1778,6 @@ - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUI @end - -static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component); - @implementation TabGroupControlAccessibility - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole @@ -1873,7 +1846,7 @@ - (jobject)tabGroup #define JAVA_AX_ROWS (1) #define JAVA_AX_COLS (2) -@implementation TableAccessibility +@implementation TableLegacyAccessibility - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env { @@ -1907,30 +1880,3 @@ - (id)accessibilityColumnCountAttribute { return [self getTableInfo:JAVA_AX_COLS]; } @end - -/* - * Returns Object.equals for the two items - * This may use LWCToolkit.invokeAndWait(); don't call while holding fLock - * and try to pass a component so the event happens on the correct thread. - */ -static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component) -{ - DECLARE_CLASS_RETURN(sjc_Object, "java/lang/Object", NO); - DECLARE_METHOD_RETURN(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z", NO); - - if ((a == NULL) && (b == NULL)) return YES; - if ((a == NULL) || (b == NULL)) return NO; - - if (pthread_main_np() != 0) { - // If we are on the AppKit thread - DECLARE_CLASS_RETURN(sjc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit", NO); - DECLARE_STATIC_METHOD_RETURN(jm_doEquals, sjc_LWCToolkit, "doEquals", - "(Ljava/lang/Object;Ljava/lang/Object;Ljava/awt/Component;)Z", NO); - return (*env)->CallStaticBooleanMethod(env, sjc_LWCToolkit, jm_doEquals, a, b, component); - CHECK_EXCEPTION(); - } - - jboolean jb = (*env)->CallBooleanMethod(env, a, jm_equals, b); - CHECK_EXCEPTION(); - return jb; -} diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.m index 730e98e414703..4036775d27508 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.m @@ -53,23 +53,6 @@ GET_STATIC_METHOD_RETURN(sjm_getAccessibleEditableText, sjc_CAccessibleText, "getAccessibleEditableText", \ "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleEditableText;", ret); - -/* - * Converts an int array to an NSRange wrapped inside an NSValue - * takes [start, end] values and returns [start, end - start] - */ -NSValue *javaIntArrayToNSRangeValue(JNIEnv* env, jintArray array) { - jint *values = (*env)->GetIntArrayElements(env, array, 0); - if (values == NULL) { - // Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck. - NSLog(@"%s failed calling GetIntArrayElements", __FUNCTION__); - return nil; - }; - NSValue *value = [NSValue valueWithRange:NSMakeRange(values[0], values[1] - values[0])]; - (*env)->ReleaseIntArrayElements(env, array, values, 0); - return value; -} - @implementation JavaTextAccessibility // based strongly upon NSTextViewAccessibility:accessibilityAttributeNames diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ButtonAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ButtonAccessibility.m index 912cef1fa67ac..8748d42bfa7fb 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ButtonAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ButtonAccessibility.m @@ -36,7 +36,7 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (NSString * _Nullable)accessibilityLabel { - return [self accessibilityTitleAttribute]; + return [super accessibilityLabel]; } - (BOOL)accessibilityPerformPress @@ -44,4 +44,14 @@ - (BOOL)accessibilityPerformPress return [self performAccessibleAction:0]; } +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.h new file mode 100644 index 0000000000000..bcaa0269576ac --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface CellAccessibility : CommonComponentAccessibility +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m new file mode 100644 index 0000000000000..688d8eb3176b2 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CellAccessibility.h" +#import "ThreadUtilities.h" +#import "TableAccessibility.h" + +@implementation CellAccessibility + +// NSAccessibilityElement protocol methods + +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityCellRole;; +} + +- (NSArray *)accessibilityChildren +{ + NSArray *children = [super accessibilityChildren]; + if (children == NULL) { + NSString *javaRole = [self javaRole]; + CommonComponentAccessibility *newChild = [CommonComponentAccessibility createWithParent:self + accessible:self->fAccessible + role:javaRole + index:self->fIndex + withEnv:[ThreadUtilities getJNIEnv] + withView:self->fView + isWrapped:NO]; + return [NSArray arrayWithObject:newChild]; + } else { + return children; + } +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +- (NSRange)accessibilityRowIndexRange { + NSInteger location = -1; + if ([[(CommonComponentAccessibility *)fParent accessibilityParent] isKindOfClass:[TableAccessibility class]]) { + TableAccessibility *table = [(CommonComponentAccessibility *)fParent accessibilityParent]; + location = [table accessibleRowAtIndex:fIndex]; + } + + return NSMakeRange(location, 1); +} + +- (NSRange)accessibilityColumnIndexRange { + NSInteger location = -1; + if ([[(CommonComponentAccessibility *)fParent accessibilityParent] isKindOfClass:[TableAccessibility class]]) { + TableAccessibility *table = [(CommonComponentAccessibility *)fParent accessibilityParent]; + location = [table accessibleColumnAtIndex:fIndex]; + } + + return NSMakeRange(location, 1); +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m index bbfaf39fe66bb..f2dbf60d92d4b 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m @@ -39,7 +39,7 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (id _Nonnull) accessibilityValue { AWT_ASSERT_APPKIT_THREAD; - return [self accessibilityValueAttribute]; + return [super accessibilityValue]; } @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.h new file mode 100644 index 0000000000000..a78afaa924b01 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface ColumnAccessibility : CommonComponentAccessibility + +@property(readonly) NSUInteger columnNumberInTable; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m new file mode 100644 index 0000000000000..9d4582445a471 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" +#import "CellAccessibility.h" +#import "ColumnAccessibility.h" +#import "TableAccessibility.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" + +static jclass sjc_CAccessibility = NULL; + +static jmethodID jm_getChildrenAndRoles = NULL; +#define GET_CHILDRENANDROLES_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles",\ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;", ret); + +@implementation ColumnAccessibility + +// NSAccessibilityElement protocol methods + +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityColumnRole; +} + +- (NSArray *)accessibilityChildren +{ + NSArray *children = [super accessibilityChildren]; + if (children == NULL) { + JNIEnv *env = [ThreadUtilities getJNIEnv]; + CommonComponentAccessibility *parent = [self accessibilityParent]; + if (parent->fAccessible == NULL) return nil; + GET_CHILDRENANDROLES_METHOD_RETURN(nil); + jobjectArray jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, + parent->fAccessible, parent->fComponent, sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN, NO); + CHECK_EXCEPTION(); + if (jchildrenAndRoles == NULL) return nil; + + jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); + NSMutableArray *childrenCells = [NSMutableArray arrayWithCapacity:arrayLen/2]; + + NSUInteger childIndex = fIndex; + + int inc = [(TableAccessibility *)[self accessibilityParent] accessibilityRowCount] * 2; + NSInteger i = childIndex * 2; + for(i; i < arrayLen; i += inc) + { + jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); + jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1); + + NSString *childJavaRole = nil; + if (jchildJavaRole != NULL) { + DECLARE_CLASS_RETURN(sjc_AccessibleRole, "javax/accessibility/AccessibleRole", nil); + DECLARE_FIELD_RETURN(sjf_key, sjc_AccessibleRole, "key", "Ljava/lang/String;", nil); + jobject jkey = (*env)->GetObjectField(env, jchildJavaRole, sjf_key); + CHECK_EXCEPTION(); + childJavaRole = JavaStringToNSString(env, jkey); + (*env)->DeleteLocalRef(env, jkey); + } + + CellAccessibility *child = [[CellAccessibility alloc] initWithParent:self + withEnv:env + withAccessible:jchild + withIndex:childIndex + withView:self->fView + withJavaRole:childJavaRole]; + [childrenCells addObject:[[child retain] autorelease]]; + + (*env)->DeleteLocalRef(env, jchild); + (*env)->DeleteLocalRef(env, jchildJavaRole); + + childIndex += (inc / 2); + } + (*env)->DeleteLocalRef(env, jchildrenAndRoles); + return childrenCells; + } else { + return children; + } +} + +- (NSInteger)accessibilityIndex +{ + return fIndex; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.h new file mode 100644 index 0000000000000..4a39938a826fa --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface ComboBoxAccessibility : CommonComponentAccessibility +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m new file mode 100644 index 0000000000000..63c00414d66fc --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "ComboBoxAccessibility.h" +#import "../JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +static jclass sjc_CAccessibility = NULL; + +static jmethodID sjm_getAccessibleName = NULL; +#define GET_ACCESSIBLENAME_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", ret); + +@implementation ComboBoxAccessibility + +// NSAccessibilityElement protocol methods + +- (id)accessibilityValue { + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return nil; + jclass axContextClass = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_getAccessibleSelection, axContextClass, "getAccessibleSelection", "(I)Ljavax/accessibility/Accessible;", nil); + jobject axSelectedChild = (*env)->CallObjectMethod(env, axContext, jm_getAccessibleSelection, 0); + CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, axContext); + if (axSelectedChild == NULL) { + return nil; + } + GET_CACCESSIBILITY_CLASS_RETURN(nil); + GET_ACCESSIBLENAME_METHOD_RETURN(nil); + jobject childName = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleName, axSelectedChild, fComponent); + CHECK_EXCEPTION(); + if (childName == NULL) { + (*env)->DeleteLocalRef(env, axSelectedChild); + return nil; + } + NSString *selectedText = JavaStringToNSString(env, childName); + (*env)->DeleteLocalRef(env, axSelectedChild); + (*env)->DeleteLocalRef(env, childName); + return selectedText; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h index 4140e0ea5f75e..0e946b4a67fc3 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h @@ -26,23 +26,83 @@ #ifndef JAVA_COMPONENT_ACCESSIBILITY #define JAVA_COMPONENT_ACCESSIBILITY -#import "JavaComponentAccessibility.h" +#include "jni.h" + +#import #import "JavaAccessibilityUtilities.h" -// these constants are duplicated in CAccessibility.java -#define JAVA_AX_ALL_CHILDREN (-1) -#define JAVA_AX_SELECTED_CHILDREN (-2) -#define JAVA_AX_VISIBLE_CHILDREN (-3) -// If the value is >=0, it's an index +@interface CommonComponentAccessibility : NSAccessibilityElement { + NSView *fView; + NSObject *fParent; + + NSString *fNSRole; + NSString *fJavaRole; -@interface CommonComponentAccessibility : JavaComponentAccessibility { + jint fIndex; + jobject fAccessible; + jobject fComponent; + NSMutableDictionary *fActions; + NSMutableArray *fActionSelectors; + NSObject *fActionsLOCK; } + +@property(nonnull, readonly) NSArray *actionSelectors; + +- (id _Nonnull)initWithParent:(NSObject* _Nonnull)parent withEnv:(JNIEnv _Nonnull * _Nonnull)env withAccessible:(jobject _Nullable)accessible withIndex:(jint)index withView:(NSView* _Nonnull)view withJavaRole:(NSString* _Nullable)javaRole; +- (void)unregisterFromCocoaAXSystem; +- (void)postValueChanged; +- (void)postSelectedTextChanged; +- (void)postSelectionChanged; +- (void)postTitleChanged; +- (void)postTreeNodeExpanded; +- (void)postTreeNodeCollapsed; +- (BOOL)isEqual:(nonnull id)anObject; +- (BOOL)isAccessibleWithEnv:(JNIEnv _Nonnull * _Nonnull)env forAccessible:(nonnull jobject)accessible; + ++ (void)postFocusChanged:(nullable id)message; + + (void) initializeRolesMap; -+ (JavaComponentAccessibility * _Nullable) getComponentAccessibility:(NSString * _Nonnull)role; + ++ (CommonComponentAccessibility* _Nullable) getComponentAccessibility:(NSString* _Nonnull)role; ++ (CommonComponentAccessibility * _Nullable) getComponentAccessibility:(NSString * _Nonnull)role andParent:(CommonComponentAccessibility * _Nonnull)parent; + ++ (NSArray* _Nullable)childrenOfParent:(CommonComponentAccessibility* _Nonnull)parent withEnv:(JNIEnv _Nonnull * _Nonnull)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored; ++ (NSArray* _Nullable)childrenOfParent:(CommonComponentAccessibility* _Nonnull)parent withEnv:(JNIEnv _Nonnull * _Nonnull)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored recursive:(BOOL)recursive; ++ (CommonComponentAccessibility* _Nullable) createWithParent:(CommonComponentAccessibility* _Nullable)parent accessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)javaRole index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; ++ (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)role index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; ++ (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; + +// If the isWraped parameter is true, then the object passed as a parent was created based on the same java component, +// but performs a different NSAccessibilityRole of a table cell, or a list row, or tree row, +// and we need to create an element whose role corresponds to the role in Java. ++ (CommonComponentAccessibility* _Nullable) createWithParent:(CommonComponentAccessibility* _Nullable)parent accessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)javaRole index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view isWrapped:(BOOL)wrapped; + +// The current parameter is used to bypass the check for an item's index on the parent so that the item is created. This is necessary, +// for example, for AccessibleJTreeNode, whose currentComponent has index -1 ++ (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view isCurrent:(BOOL)current; + +- (jobject _Nullable)axContextWithEnv:(JNIEnv _Nonnull * _Nonnull)env; +- (NSView* _Nonnull)view; +- (NSWindow* _Nonnull)window; +- (id _Nonnull)parent; +- (NSString* _Nonnull)javaRole; + +- (BOOL)isMenu; +- (BOOL)isSelected:(JNIEnv _Nonnull * _Nonnull)env; +- (BOOL)isSelectable:(JNIEnv _Nonnull * _Nonnull)env; +- (BOOL)isVisible:(JNIEnv _Nonnull * _Nonnull)env; + +- (NSArray* _Nullable)accessibleChildrenWithChildCode:(NSInteger)childCode; + +- (NSDictionary* _Nullable)getActions:(JNIEnv _Nonnull * _Nonnull)env; +- (void)getActionsWithEnv:(JNIEnv _Nonnull * _Nonnull)env; +- (BOOL)accessiblePerformAction:(NSAccessibilityActionName _Nonnull)actionName; + +- (BOOL)performAccessibleAction:(int)index; + - (NSRect)accessibilityFrame; - (id _Nullable)accessibilityParent; -- (BOOL)performAccessibleAction:(int)index; - (BOOL)isAccessibilityElement; @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 25317afa3b3ce..9fe7ce4e52040 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -24,18 +24,57 @@ */ #import "CommonComponentAccessibility.h" -#import "JNIUtilities.h" +#import +#import +#import +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" #import "ThreadUtilities.h" +#import "JNIUtilities.h" +#import "AWTView.h" +#import "sun_lwawt_macosx_CAccessible.h" +#import "sun_lwawt_macosx_CAccessibility.h" + +// GET* macros defined in JavaAccessibilityUtilities.h, so they can be shared. static jclass sjc_CAccessibility = NULL; -static jmethodID sjm_getAccessibleComponent = NULL; +static jmethodID sjm_getAccessibleName = NULL; +#define GET_ACCESSIBLENAME_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", ret); + +static jmethodID jm_getChildrenAndRoles = NULL; +#define GET_CHILDRENANDROLES_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles",\ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;", ret); + +static jmethodID jm_getChildrenAndRolesRecursive = NULL; +#define GET_CHILDRENANDROLESRECURSIVE_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(jm_getChildrenAndRolesRecursive, sjc_CAccessibility, "getChildrenAndRolesRecursive",\ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZI)[Ljava/lang/Object;", ret); + +static jmethodID sjm_getAccessibleComponent = NULL; #define GET_ACCESSIBLECOMPONENT_STATIC_METHOD_RETURN(ret) \ GET_CACCESSIBILITY_CLASS_RETURN(ret); \ GET_STATIC_METHOD_RETURN(sjm_getAccessibleComponent, sjc_CAccessibility, "getAccessibleComponent", \ "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleComponent;", ret); +static jmethodID sjm_getAccessibleIndexInParent = NULL; +#define GET_ACCESSIBLEINDEXINPARENT_STATIC_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleIndexInParent, sjc_CAccessibility, "getAccessibleIndexInParent", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)I", ret); + +static jclass sjc_CAccessible = NULL; +#define GET_CACCESSIBLE_CLASS_RETURN(ret) \ + GET_CLASS_RETURN(sjc_CAccessible, "sun/lwawt/macosx/CAccessible", ret); + static NSMutableDictionary * _Nullable rolesMap; +static NSMutableDictionary * _Nullable rowRolesMapForParent; NSString *const IgnoreClassName = @"IgnoreAccessibility"; static jobject sAccessibilityClass = NULL; @@ -44,11 +83,46 @@ */ @implementation CommonComponentAccessibility +- (BOOL)isMenu +{ + id role = [self accessibilityRole]; + return [role isEqualToString:NSAccessibilityMenuBarRole] || [role isEqualToString:NSAccessibilityMenuRole] || [role isEqualToString:NSAccessibilityMenuItemRole]; +} + +- (BOOL)isSelected:(JNIEnv *)env +{ + if (fIndex == -1) { + return NO; + } + + return isChildSelected(env, ((CommonComponentAccessibility *)[self parent])->fAccessible, fIndex, fComponent); +} + +- (BOOL)isSelectable:(JNIEnv *)env +{ + jobject axContext = [self axContextWithEnv:env]; + BOOL selectable = isSelectable(env, axContext, fComponent); + (*env)->DeleteLocalRef(env, axContext); + return selectable; +} + +- (BOOL)isVisible:(JNIEnv *)env +{ + if (fIndex == -1) { + return NO; + } + + jobject axContext = [self axContextWithEnv:env]; + BOOL showing = isShowing(env, axContext, fComponent); + (*env)->DeleteLocalRef(env, axContext); + return showing; +} + + (void) initializeRolesMap { /* * Here we should keep all the mapping between the accessibility roles and implementing classes */ - rolesMap = [[NSMutableDictionary alloc] initWithCapacity:37]; + rolesMap = [[NSMutableDictionary alloc] initWithCapacity:46]; [rolesMap setObject:@"ButtonAccessibility" forKey:@"pushbutton"]; [rolesMap setObject:@"ImageAccessibility" forKey:@"icon"]; @@ -69,6 +143,15 @@ + (void) initializeRolesMap { [rolesMap setObject:@"ToolbarAccessibility" forKey:@"toolbar"]; [rolesMap setObject:@"SplitpaneAccessibility" forKey:@"splitpane"]; [rolesMap setObject:@"StatusbarAccessibility" forKey:@"statusbar"]; + [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"textarea"]; + [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"text"]; + [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"passwordtext"]; + [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"dateeditor"]; + [rolesMap setObject:@"ComboBoxAccessibility" forKey:@"combobox"]; + [rolesMap setObject:@"TabGroupAccessibility" forKey:@"pagetablist"]; + [rolesMap setObject:@"ListAccessibility" forKey:@"list"]; + [rolesMap setObject:@"OutlineAccessibility" forKey:@"tree"]; + [rolesMap setObject:@"TableAccessibility" forKey:@"table"]; /* * All the components below should be ignored by the accessibility subsystem, @@ -94,6 +177,11 @@ + (void) initializeRolesMap { [rolesMap setObject:IgnoreClassName forKey:@"viewport"]; [rolesMap setObject:IgnoreClassName forKey:@"window"]; + rowRolesMapForParent = [[NSMutableDictionary alloc] initWithCapacity:2]; + + [rowRolesMapForParent setObject:@"ListRowAccessibility" forKey:@"ListAccessibility"]; + [rowRolesMapForParent setObject:@"OutlineRowAccessibility" forKey:@"OutlineAccessibility"]; + /* * Initialize CAccessibility instance */ @@ -132,7 +220,7 @@ + (void) initializeRolesMap { * If new implementation of the accessible component peer for the given role exists * return the allocated class otherwise return nil to let old implementation being initialized */ -+ (JavaComponentAccessibility *) getComponentAccessibility:(NSString *)role ++ (CommonComponentAccessibility *) getComponentAccessibility:(NSString *)role { AWT_ASSERT_APPKIT_THREAD; if (rolesMap == nil) { @@ -143,10 +231,542 @@ + (JavaComponentAccessibility *) getComponentAccessibility:(NSString *)role if (className != nil) { return [NSClassFromString(className) alloc]; } - return nil; + return [CommonComponentAccessibility alloc]; +} + ++ (CommonComponentAccessibility *) getComponentAccessibility:(NSString *)role andParent:(CommonComponentAccessibility *)parent +{ + AWT_ASSERT_APPKIT_THREAD; + if (rolesMap == nil) { + [self initializeRolesMap]; + } + NSString *className = [rowRolesMapForParent objectForKey:[[parent class] className]]; + if (className == nil) { + return [CommonComponentAccessibility getComponentAccessibility:role]; + } + return [NSClassFromString(className) alloc]; +} + +- (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole +{ + self = [super init]; + if (self) + { + fParent = [parent retain]; + fView = [view retain]; + fJavaRole = [javaRole retain]; + + if (accessible != NULL) { + fAccessible = (*env)->NewWeakGlobalRef(env, accessible); + CHECK_EXCEPTION(); + } + + jobject jcomponent = [(AWTView *)fView awtComponent:env]; + fComponent = (*env)->NewWeakGlobalRef(env, jcomponent); + CHECK_EXCEPTION(); + + (*env)->DeleteLocalRef(env, jcomponent); + + fIndex = index; + + fActions = nil; + fActionSelectors = nil; + fActionsLOCK = [[NSObject alloc] init]; + } + return self; +} + +- (void)unregisterFromCocoaAXSystem +{ + AWT_ASSERT_APPKIT_THREAD; + static dispatch_once_t initialize_unregisterUniqueId_once; + static void (*unregisterUniqueId)(id); + dispatch_once(&initialize_unregisterUniqueId_once, ^{ + void *jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL); + unregisterUniqueId = dlsym(jrsFwk, "JRSAccessibilityUnregisterUniqueIdForUIElement"); + }); + if (unregisterUniqueId) unregisterUniqueId(self); +} + +- (void)dealloc +{ + [self unregisterFromCocoaAXSystem]; + + JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; + + (*env)->DeleteWeakGlobalRef(env, fAccessible); + fAccessible = NULL; + + (*env)->DeleteWeakGlobalRef(env, fComponent); + fComponent = NULL; + + [fParent release]; + fParent = nil; + + [fNSRole release]; + fNSRole = nil; + + [fJavaRole release]; + fJavaRole = nil; + + [fView release]; + fView = nil; + + [fActions release]; + fActions = nil; + + [fActionsLOCK release]; + fActionsLOCK = nil; + + [fActionSelectors release]; + fActionSelectors = nil; + + [super dealloc]; +} + +- (void)postValueChanged +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, NSAccessibilityValueChangedNotification); +} + +- (void)postSelectedTextChanged +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, NSAccessibilitySelectedTextChangedNotification); +} + +- (void)postSelectionChanged +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, NSAccessibilitySelectedChildrenChangedNotification); +} + +-(void)postTitleChanged +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, NSAccessibilityTitleChangedNotification); +} + +- (void)postMenuOpened +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, (NSString *)kAXMenuOpenedNotification); +} + +- (void)postMenuClosed +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, (NSString *)kAXMenuClosedNotification); +} + +- (void)postMenuItemSelected +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, (NSString *)kAXMenuItemSelectedNotification); +} + +- (void)postTreeNodeExpanded +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification([[self accessibilitySelectedRows] firstObject], NSAccessibilityRowExpandedNotification); +} + +- (void)postTreeNodeCollapsed +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification([[self accessibilitySelectedRows] firstObject], NSAccessibilityRowCollapsedNotification); +} + +- (BOOL)isEqual:(id)anObject +{ + if (![anObject isKindOfClass:[self class]]) return NO; + CommonComponentAccessibility *accessibility = (CommonComponentAccessibility *)anObject; + + JNIEnv* env = [ThreadUtilities getJNIEnv]; + return (*env)->IsSameObject(env, accessibility->fAccessible, fAccessible); +} + +- (BOOL)isAccessibleWithEnv:(JNIEnv *)env forAccessible:(jobject)accessible +{ + return (*env)->IsSameObject(env, fAccessible, accessible); +} + ++ (void)initialize +{ + if (sRoles == nil) { + initializeRoles(); + } + if (sActions == nil) { + initializeActions(); + } +} + ++ (void)postFocusChanged:(id)message +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification([NSApp accessibilityFocusedUIElement], NSAccessibilityFocusedUIElementChangedNotification); +} + ++ (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env { + DECLARE_CLASS_RETURN(sjc_Accessible, "javax/accessibility/Accessible", NULL); + GET_CACCESSIBLE_CLASS_RETURN(NULL); + DECLARE_STATIC_METHOD_RETURN(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", + "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;", NULL); + if ((*env)->IsInstanceOf(env, jaccessible, sjc_CAccessible)) { + return jaccessible; + } else if ((*env)->IsInstanceOf(env, jaccessible, sjc_Accessible)) { + jobject o = (*env)->CallStaticObjectMethod(env, sjc_CAccessible, sjm_getCAccessible, jaccessible); + CHECK_EXCEPTION(); + return o; + } + return NULL; +} + ++ (NSArray *)childrenOfParent:(CommonComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored +{ + return [CommonComponentAccessibility childrenOfParent:parent withEnv:env withChildrenCode:whichChildren allowIgnored:allowIgnored recursive:NO]; +} + ++ (NSArray *)childrenOfParent:(CommonComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored recursive:(BOOL)recursive +{ + if (parent->fAccessible == NULL) return nil; + jobjectArray jchildrenAndRoles = NULL; + if (recursive) { + GET_CHILDRENANDROLESRECURSIVE_METHOD_RETURN(nil); + jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRolesRecursive, + parent->fAccessible, parent->fComponent, whichChildren, allowIgnored, 0); + CHECK_EXCEPTION(); + } else { + GET_CHILDRENANDROLES_METHOD_RETURN(nil); + jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, + parent->fAccessible, parent->fComponent, whichChildren, allowIgnored); + CHECK_EXCEPTION(); + } + if (jchildrenAndRoles == NULL) return nil; + + jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); + NSMutableArray *children = [NSMutableArray arrayWithCapacity:(recursive ? arrayLen/3 : arrayLen/2)]; //childrenAndRoles array contains two elements (child, role) for each child + + NSInteger i; + NSUInteger childIndex = (whichChildren >= 0) ? whichChildren : 0; // if we're getting one particular child, make sure to set its index correctly + int inc = recursive ? 3 : 2; + for(i = 0; i < arrayLen; i+=inc) + { + jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); + jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1); + + NSString *childJavaRole = nil; + if (jchildJavaRole != NULL) { + DECLARE_CLASS_RETURN(sjc_AccessibleRole, "javax/accessibility/AccessibleRole", nil); + DECLARE_FIELD_RETURN(sjf_key, sjc_AccessibleRole, "key", "Ljava/lang/String;", nil); + jobject jkey = (*env)->GetObjectField(env, jchildJavaRole, sjf_key); + CHECK_EXCEPTION(); + childJavaRole = JavaStringToNSString(env, jkey); + (*env)->DeleteLocalRef(env, jkey); + } + + CommonComponentAccessibility *child = [self createWithParent:parent accessible:jchild role:childJavaRole index:childIndex withEnv:env withView:parent->fView]; + + if (recursive && [child respondsToSelector:@selector(accessibleLevel)]) { + jobject jLevel = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+2); + NSString *sLevel = nil; + if (jLevel != NULL) { + sLevel = JavaStringToNSString(env, jLevel); + if (sLevel != nil) { + int level = sLevel.intValue; + [child setAccessibleLevel:level]; + } + (*env)->DeleteLocalRef(env, jLevel); + } + } + + (*env)->DeleteLocalRef(env, jchild); + (*env)->DeleteLocalRef(env, jchildJavaRole); + + [children addObject:child]; + childIndex++; + } + (*env)->DeleteLocalRef(env, jchildrenAndRoles); + + return children; +} + ++ (CommonComponentAccessibility *) createWithAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env withView:(NSView *)view +{ + return [CommonComponentAccessibility createWithAccessible:jaccessible withEnv:env withView:view isCurrent:NO]; +} + ++ (CommonComponentAccessibility *) createWithAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env withView:(NSView *)view isCurrent:(BOOL)current +{ + GET_ACCESSIBLEINDEXINPARENT_STATIC_METHOD_RETURN(nil); + CommonComponentAccessibility *ret = nil; + jobject jcomponent = [(AWTView *)view awtComponent:env]; + jint index = (*env)->CallStaticIntMethod(env, sjc_CAccessibility, sjm_getAccessibleIndexInParent, jaccessible, jcomponent); + CHECK_EXCEPTION(); + if (index >= 0 || current) { + NSString *javaRole = getJavaRole(env, jaccessible, jcomponent); + ret = [self createWithAccessible:jaccessible role:javaRole index:index withEnv:env withView:view]; + } + (*env)->DeleteLocalRef(env, jcomponent); + return ret; +} + ++ (CommonComponentAccessibility *) createWithAccessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view +{ + return [self createWithParent:nil accessible:jaccessible role:javaRole index:index withEnv:env withView:view]; +} + ++ (CommonComponentAccessibility *) createWithParent:(CommonComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view +{ + return [CommonComponentAccessibility createWithParent:parent accessible:jaccessible role:javaRole index:index withEnv:env withView:view isWrapped:NO]; +} + ++ (CommonComponentAccessibility *) createWithParent:(CommonComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view isWrapped:(BOOL)wrapped +{ + GET_CACCESSIBLE_CLASS_RETURN(NULL); + DECLARE_FIELD_RETURN(jf_ptr, sjc_CAccessible, "ptr", "J", NULL); + // try to fetch the jCAX from Java, and return autoreleased + jobject jCAX = [CommonComponentAccessibility getCAccessible:jaccessible withEnv:env]; + if (jCAX == NULL) return nil; + if (!wrapped) { // If wrapped is true, then you don't need to get an existing instance, you need to create a new one + CommonComponentAccessibility *value = (CommonComponentAccessibility *) jlong_to_ptr((*env)->GetLongField(env, jCAX, jf_ptr)); + if (value != nil) { + (*env)->DeleteLocalRef(env, jCAX); + return [[value retain] autorelease]; + } + } + + // otherwise, create a new instance + CommonComponentAccessibility *newChild = [CommonComponentAccessibility getComponentAccessibility:javaRole andParent:parent]; + + // must init freshly -alloc'd object + [newChild initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // must init new instance + + // If creating a JPopupMenu (not a combobox popup list) need to fire menuOpened. + // This is the only way to know if the menu is opening; visible state change + // can't be caught because the listeners are not set up in time. + if ( [javaRole isEqualToString:@"popupmenu"] && + ![[parent javaRole] isEqualToString:@"combobox"] ) { + [newChild postMenuOpened]; + } + + // must hard retain pointer poked into Java object + [newChild retain]; + (*env)->SetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild)); + + // the link is removed in the wrapper + if (!wrapped) { + (*env)->DeleteLocalRef(env, jCAX); + } + + // return autoreleased instance + return [newChild autorelease]; +} + +- (NSDictionary *)getActions:(JNIEnv *)env +{ + @synchronized(fActionsLOCK) { + if (fActions == nil) { + [self getActionsWithEnv:env]; + } + } + + return fActions; +} + +- (void)getActionsWithEnv:(JNIEnv *)env +{ + GET_CACCESSIBILITY_CLASS(); + DECLARE_STATIC_METHOD(jm_getAccessibleAction, sjc_CAccessibility, "getAccessibleAction", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleAction;"); + + jobject axAction = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getAccessibleAction, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axAction != NULL) { + jclass jc_AccessibleAction = NULL; + GET_CLASS(jc_AccessibleAction, "javax/accessibility/AccessibleAction"); + jmethodID jm_getAccessibleActionCount = NULL; + GET_METHOD(jm_getAccessibleActionCount, jc_AccessibleAction, "getAccessibleActionCount", "()I"); + jint count = (*env)->CallIntMethod(env, axAction, jm_getAccessibleActionCount); + fActions = [[NSMutableDictionary alloc] initWithCapacity:count]; + fActionSelectors = [[NSMutableArray alloc] initWithCapacity:count]; + for (int i =0; i < count; i++) { + JavaAxAction *action = [[JavaAxAction alloc] initWithEnv:env withAccessibleAction:axAction withIndex:i withComponent:fComponent]; + if ([fParent isKindOfClass:[CommonComponentAccessibility class]] && + [(CommonComponentAccessibility *)fParent isMenu] && + [[sActions objectForKey:[action getDescription]] isEqualToString:NSAccessibilityPressAction]) { + [fActions setObject:action forKey:NSAccessibilityPickAction]; + [fActionSelectors addObject:[sActionSelectors objectForKey:NSAccessibilityPickAction]]; + } else { + [fActions setObject:action forKey:[sActions objectForKey:[action getDescription]]]; + [fActionSelectors addObject:[sActionSelectors objectForKey:[sActions objectForKey:[action getDescription]]]]; + } + [action release]; + } + (*env)->DeleteLocalRef(env, axAction); + } +} + +- (BOOL)accessiblePerformAction:(NSAccessibilityActionName)actionName { + NSMutableDictionary *currentAction = [self getActions:[ThreadUtilities getJNIEnv]]; + if (currentAction == nil) { + return NO; + } + if ([[currentAction allKeys] containsObject:actionName]) { + [(JavaAxAction *)[currentAction objectForKey:actionName] perform]; + return YES;; + } + return NO; +} + +- (NSArray *)actionSelectors { + @synchronized(fActionsLOCK) { + if (fActionSelectors == nil) { + [self getActionsWithEnv:[ThreadUtilities getJNIEnv]]; + } + } + + return [NSArray arrayWithArray:fActionSelectors]; +} + +- (NSArray *)accessibleChildrenWithChildCode:(NSInteger)childCode +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + NSArray *children = [CommonComponentAccessibility childrenOfParent:self + withEnv:env + withChildrenCode:childCode + allowIgnored:([[self accessibilityRole] isEqualToString:NSAccessibilityListRole] || [[self accessibilityRole] isEqualToString:NSAccessibilityOutlineRole] || [[self accessibilityRole] isEqualToString:NSAccessibilityTableRole]) + recursive:[[self accessibilityRole] isEqualToString:NSAccessibilityOutlineRole]]; + + NSArray *value = nil; + if ([children count] > 0) { + value = children; + } + + return value; +} + +- (NSView *)view +{ + return fView; +} + +- (NSWindow *)window +{ + return [[self view] window]; +} + +- (id)parent +{ + if(fParent == nil) { + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getAccessibleParent, sjc_CAccessibility, "getAccessibleParent", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/Accessible;", nil); + GET_CACCESSIBLE_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getSwingAccessible, sjc_CAccessible, "getSwingAccessible", + "(Ljavax/accessibility/Accessible;)Ljavax/accessibility/Accessible;", nil); + DECLARE_CLASS_RETURN(sjc_Window, "java/awt/Window", nil); + + jobject jparent = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleParent, fAccessible, fComponent); + CHECK_EXCEPTION(); + + if (jparent == NULL) { + fParent = fView; + } else { + AWTView *view = fView; + jobject jax = (*env)->CallStaticObjectMethod(env, sjc_CAccessible, sjm_getSwingAccessible, fAccessible); + CHECK_EXCEPTION(); + + if ((*env)->IsInstanceOf(env, jax, sjc_Window)) { + // In this case jparent is an owner toplevel and we should retrieve its own view + view = [AWTView awtView:env ofAccessible:jparent]; + } + if (view != nil) { + fParent = [CommonComponentAccessibility createWithAccessible:jparent withEnv:env withView:view]; + } + if (fParent == nil) { + fParent = fView; + } + (*env)->DeleteLocalRef(env, jparent); + (*env)->DeleteLocalRef(env, jax ); + } + [fParent retain]; + } + return fParent; +} + +- (NSString *)javaRole +{ + if(fJavaRole == nil) { + JNIEnv* env = [ThreadUtilities getJNIEnv]; + fJavaRole = getJavaRole(env, fAccessible, fComponent); + [fJavaRole retain]; + } + return fJavaRole; +} + +- (jobject)axContextWithEnv:(JNIEnv *)env +{ + return getAxContext(env, fAccessible, fComponent); } // NSAccessibilityElement protocol implementation + +- (BOOL)isAccessibilityElement +{ + return ![[self accessibilityRole] isEqualToString:JavaAccessibilityIgnore]; +} + +- (NSString *)accessibilityLabel +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + + GET_ACCESSIBLENAME_METHOD_RETURN(nil); + jobject val = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleName, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (val == NULL) { + return nil; + } + NSString* str = JavaStringToNSString(env, val); + (*env)->DeleteLocalRef(env, val); + return str; +} + +- (NSString *)accessibilityHelp +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getAccessibleDescription, sjc_CAccessibility, "getAccessibleDescription", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", nil); + jobject val = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, + sjm_getAccessibleDescription, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (val == NULL) { + return nil; + } + NSString* str = JavaStringToNSString(env, val); + (*env)->DeleteLocalRef(env, val); + return str; +} + +- (NSArray *)accessibilityChildren +{ + return [self accessibleChildrenWithChildCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN]; +} + +- (NSArray *)accessibilitySelectedChildren +{ + return [self accessibleChildrenWithChildCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_SELECTED_CHILDREN]; +} + +- (NSArray *)accessibilityVisibleChildren +{ + return [self accessibleChildrenWithChildCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_VISIBLE_CHILDREN]; +} + - (NSRect)accessibilityFrame { JNIEnv* env = [ThreadUtilities getJNIEnv]; @@ -168,10 +788,370 @@ - (NSRect)accessibilityFrame - (id _Nullable)accessibilityParent { - return [self accessibilityParentAttribute]; + return NSAccessibilityUnignoredAncestor([self parent]); +} + +- (BOOL)isAccessibilityEnabled +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(NO); + DECLARE_STATIC_METHOD_RETURN(jm_isEnabled, sjc_CAccessibility, "isEnabled", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z", NO); + + BOOL value = (*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, jm_isEnabled, fAccessible, fComponent); + CHECK_EXCEPTION(); + + return value; +} + +- (id)accessibilityApplicationFocusedUIElement +{ + return [self accessibilityFocusedUIElement]; +} + +- (NSAccessibilityRole)accessibilityRole +{ + if (fNSRole == nil) { + NSString *javaRole = [self javaRole]; + fNSRole = [sRoles objectForKey:javaRole]; + // The sRoles NSMutableDictionary maps popupmenu to Mac's popup button. + // JComboBox behavior currently relies on this. However this is not the + // proper mapping for a JPopupMenu so fix that. + if ( [javaRole isEqualToString:@"popupmenu"] && + ![[[self parent] javaRole] isEqualToString:@"combobox"] ) { + fNSRole = NSAccessibilityMenuRole; + } + if (fNSRole == nil) { + // this component has assigned itself a custom AccessibleRole not in the sRoles array + fNSRole = javaRole; + } + [fNSRole retain]; + } + return fNSRole; +} + +- (NSString *)accessibilityRoleDescription +{ + // first ask AppKit for its accessible role description for a given AXRole + NSString *value = NSAccessibilityRoleDescription([self accessibilityRole], nil); + + if (value == nil) { + // query java if necessary + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getAccessibleRoleDisplayString, sjc_CAccessibility, "getAccessibleRoleDisplayString", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", nil); + + jobject axRole = (*env)->CallStaticObjectMethod(env, jm_getAccessibleRoleDisplayString, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axRole != NULL) { + value = JavaStringToNSString(env, axRole); + (*env)->DeleteLocalRef(env, axRole); + } else { + value = @"unknown"; + } + } + + return value; +} + +- (BOOL)isAccessibilityFocused +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(NO); + DECLARE_STATIC_METHOD_RETURN(sjm_isFocusTraversable, sjc_CAccessibility, "isFocusTraversable", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z", NO); + // According to javadoc, a component that is focusable will return true from isFocusTraversable, + // as well as having AccessibleState.FOCUSABLE in its AccessibleStateSet. + // We use the former heuristic; if the component focus-traversable, add a focused attribute + // See also initializeAttributeNamesWithEnv: + if ((*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, sjm_isFocusTraversable, fAccessible, fComponent)) { + return [self isEqual:[NSApp accessibilityFocusedUIElement]]; + } + CHECK_EXCEPTION(); + + return NO; +} + +- (void)setAccessibilityFocused:(BOOL)accessibilityFocused +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + + GET_CACCESSIBILITY_CLASS(); + DECLARE_STATIC_METHOD(jm_requestFocus, sjc_CAccessibility, "requestFocus", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V"); + + if (accessibilityFocused) + { + (*env)->CallStaticVoidMethod(env, sjc_CAccessibility, jm_requestFocus, fAccessible, fComponent); + CHECK_EXCEPTION(); + } +} + +- (NSUInteger)accessibilityIndexOfChild:(id)child +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_ACCESSIBLEINDEXINPARENT_STATIC_METHOD_RETURN(0); + jint returnValue = + (*env)->CallStaticIntMethod( env, + sjc_CAccessibility, + sjm_getAccessibleIndexInParent, + ((CommonComponentAccessibility *)child)->fAccessible, + ((CommonComponentAccessibility *)child)->fComponent ); + CHECK_EXCEPTION(); + return (returnValue == -1) ? NSNotFound : returnValue; +} + +- (NSInteger)accessibilityIndex +{ + int index = 0; + if (fParent != NULL) { + index = [fParent accessibilityIndexOfChild:self]; + } + return index; +} + +- (id)accessibilityMaxValue +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getMaximumAccessibleValue, sjc_CAccessibility, "getMaximumAccessibleValue", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;", nil); + + jobject axValue = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getMaximumAccessibleValue, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axValue == NULL) { + return [NSNumber numberWithInt:0]; + } + NSNumber* num = JavaNumberToNSNumber(env, axValue); + (*env)->DeleteLocalRef(env, axValue); + return num; +} + +- (id)accessibilityMinValue +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getMinimumAccessibleValue, sjc_CAccessibility, "getMinimumAccessibleValue", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;", nil); + + jobject axValue = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getMinimumAccessibleValue, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axValue == NULL) { + return [NSNumber numberWithInt:0]; + } + NSNumber* num = JavaNumberToNSNumber(env, axValue); + (*env)->DeleteLocalRef(env, axValue); + return num; +} + +- (NSAccessibilityOrientation)accessibilityOrientation +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + + // cmcnote - should batch these two calls into one that returns an array of two bools, one for vertical and one for horiz + if (isVertical(env, axContext, fComponent)) { + (*env)->DeleteLocalRef(env, axContext); + return NSAccessibilityOrientationVertical; + } + if (isHorizontal(env, axContext, fComponent)) { + (*env)->DeleteLocalRef(env, axContext); + return NSAccessibilityOrientationHorizontal; + } + return NSAccessibilityOrientationUnknown; +} + +- (NSPoint)accessibilityActivationPoint +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_ACCESSIBLECOMPONENT_STATIC_METHOD_RETURN(NSPointFromString(@"")); + jobject axComponent = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleComponent, + fAccessible, fComponent); + CHECK_EXCEPTION(); + + // NSAccessibility wants the bottom left point of the object in + // bottom left based screen coords + + // Get the java screen coords, and make a NSPoint of the bottom left of the AxComponent. + NSSize size = getAxComponentSize(env, axComponent, fComponent); + NSPoint point = getAxComponentLocationOnScreen(env, axComponent, fComponent); + (*env)->DeleteLocalRef(env, axComponent); + + point.y += size.height; + + // Now make it into Cocoa screen coords. + point.y = [[[[self view] window] screen] frame].size.height - point.y; + + return point; +} + +- (BOOL)isAccessibilitySelected +{ + return [self isSelected:[ThreadUtilities getJNIEnv]]; +} + +- (void)setAccessibilitySelected:(BOOL)accessibilitySelected +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS(); + DECLARE_STATIC_METHOD(jm_requestSelection, + sjc_CAccessibility, + "requestSelection", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V"); + + if (accessibilitySelected) { + (*env)->CallStaticVoidMethod(env, sjc_CAccessibility, jm_requestSelection, fAccessible, fComponent); + CHECK_EXCEPTION(); + } +} + +- (id)accessibilityValue +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + + // Need to handle popupmenus differently. + // + // At least for now don't handle combo box menus. + // This may change when later fixing issues which currently + // exist for combo boxes, but for now the following is only + // for JPopupMenus, not for combobox menus. + id parent = [self parent]; + if ( [[self javaRole] isEqualToString:@"popupmenu"] && + ![[parent javaRole] isEqualToString:@"combobox"] ) { + NSArray *children = + [CommonComponentAccessibility childrenOfParent:self + withEnv:env + withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN + allowIgnored:YES]; + if ([children count] > 0) { + // handle case of AXMenuItem + // need to ask menu what is selected + NSArray *selectedChildrenOfMenu = + [self accessibilitySelectedChildren]; + CommonComponentAccessibility *selectedMenuItem = + [selectedChildrenOfMenu objectAtIndex:0]; + if (selectedMenuItem != nil) { + GET_CACCESSIBILITY_CLASS_RETURN(nil); + GET_ACCESSIBLENAME_METHOD_RETURN(nil); + jobject itemValue = + (*env)->CallStaticObjectMethod( env, + sjm_getAccessibleName, + selectedMenuItem->fAccessible, + selectedMenuItem->fComponent ); + CHECK_EXCEPTION(); + if (itemValue == NULL) { + return nil; + } + NSString* itemString = JavaStringToNSString(env, itemValue); + (*env)->DeleteLocalRef(env, itemValue); + return itemString; + } else { + return nil; + } + } + } + + // ask Java for the component's accessibleValue. In java, the "accessibleValue" just means a numerical value + // a text value is taken care of in JavaTextAccessibility + + // cmcnote should coalesce these calls into one java call + NSNumber *num = nil; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getAccessibleValue, sjc_CAccessibility, "getAccessibleValue", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleValue;", nil); + jobject axValue = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleValue, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axValue != NULL) { + DECLARE_STATIC_METHOD_RETURN(jm_getCurrentAccessibleValue, sjc_CAccessibility, "getCurrentAccessibleValue", + "(Ljavax/accessibility/AccessibleValue;Ljava/awt/Component;)Ljava/lang/Number;", nil); + jobject str = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getCurrentAccessibleValue, axValue, fComponent); + CHECK_EXCEPTION(); + if (str != NULL) { + num = JavaNumberToNSNumber(env, str); + (*env)->DeleteLocalRef(env, str); + } + (*env)->DeleteLocalRef(env, axValue); + } + if (num == nil) { + num = [NSNumber numberWithInt:0]; + } + return num; +} + +- (id)accessibilityHitTest:(NSPoint)point +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + + DECLARE_CLASS_RETURN(jc_Container, "java/awt/Container", nil); + DECLARE_STATIC_METHOD_RETURN(jm_accessibilityHitTest, sjc_CAccessibility, "accessibilityHitTest", + "(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;", nil); + + // Make it into java screen coords + point.y = [[[[self view] window] screen] frame].size.height - point.y; + + jobject jparent = fComponent; + + id value = nil; + if ((*env)->IsInstanceOf(env, jparent, jc_Container)) { + jobject jaccessible = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_accessibilityHitTest, + jparent, (jfloat)point.x, (jfloat)point.y); + CHECK_EXCEPTION(); + if (jaccessible != NULL) { + value = [CommonComponentAccessibility createWithAccessible:jaccessible withEnv:env withView:fView]; + (*env)->DeleteLocalRef(env, jaccessible); + } + } + + if (value == nil) { + value = self; + } + + if (![value isAccessibilityElement]) { + value = NSAccessibilityUnignoredAncestor(value); + } + +#ifdef JAVA_AX_DEBUG + NSLog(@"%s: %@", __FUNCTION__, value); +#endif + return value; +} + +- (id)accessibilityFocusedUIElement +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getFocusOwner, sjc_CAccessibility, "getFocusOwner", + "(Ljava/awt/Component;)Ljavax/accessibility/Accessible;", nil); + id value = nil; + + NSWindow* hostWindow = [[self->fView window] retain]; + jobject focused = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getFocusOwner, fComponent); + [hostWindow release]; + CHECK_EXCEPTION(); + + if (focused != NULL) { + DECLARE_CLASS_RETURN(sjc_Accessible, "javax/accessibility/Accessible", nil); + if ((*env)->IsInstanceOf(env, focused, sjc_Accessible)) { + value = [CommonComponentAccessibility createWithAccessible:focused withEnv:env withView:fView]; + } + CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, focused); + } + + if (value == nil) { + value = self; + } +#ifdef JAVA_AX_DEBUG + NSLog(@"%s: %@", __FUNCTION__, value); +#endif + return value; +} + +- (id)accessibilityWindow { + return [self window]; } // AccessibleAction support + - (BOOL)performAccessibleAction:(int)index { AWT_ASSERT_APPKIT_THREAD; @@ -187,8 +1167,66 @@ - (BOOL)performAccessibleAction:(int)index return TRUE; } -- (BOOL)isAccessibilityElement { - return YES; +// NSAccessibilityActions methods + +- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector { + if ([sAllActionSelectors containsObject:NSStringFromSelector(selector)] && + ![[self actionSelectors] containsObject:NSStringFromSelector(selector)]) { + return NO; + } + return [super isAccessibilitySelectorAllowed:selector]; +} + +- (BOOL)accessibilityPerformPick { + return [self accessiblePerformAction:NSAccessibilityPickAction]; +} + +- (BOOL)accessibilityPerformPress { + return [self accessiblePerformAction:NSAccessibilityPressAction]; +} + +- (BOOL)accessibilityPerformShowMenu { + return [self accessiblePerformAction:NSAccessibilityShowMenuAction]; +} + +- (BOOL)accessibilityPerformDecrement { + return [self accessiblePerformAction:NSAccessibilityDecrementAction]; +} + +- (BOOL)accessibilityPerformIncrement { + return [self accessiblePerformAction:NSAccessibilityIncrementAction]; } @end + +/* + * Class: sun_lwawt_macosx_CAccessible + * Method: treeNodeExpanded + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_treeNodeExpanded + (JNIEnv *env, jclass jklass, jlong element) +{ + JNI_COCOA_ENTER(env); + [ThreadUtilities performOnMainThread:@selector(postTreeNodeExpanded) + on:(CommonComponentAccessibility *)jlong_to_ptr(element) + withObject:nil + waitUntilDone:NO]; + JNI_COCOA_EXIT(env); +} + +/* + * Class: sun_lwawt_macosx_CAccessible + * Method: treeNodeCollapsed + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_treeNodeCollapsed + (JNIEnv *env, jclass jklass, jlong element) +{ + JNI_COCOA_ENTER(env); + [ThreadUtilities performOnMainThread:@selector(postTreeNodeCollapsed) + on:(CommonComponentAccessibility *)jlong_to_ptr(element) + withObject:nil + waitUntilDone:NO]; + JNI_COCOA_EXIT(env); +} diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonTextAccessibility.m index 007cbe9de8c54..18e3f47e1ebba 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonTextAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonTextAccessibility.m @@ -70,7 +70,7 @@ - (nullable NSString *)accessibilityValueAttribute GET_CACCESSIBILITY_CLASS_RETURN(nil); DECLARE_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", nil); - if ([[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityStaticTextRole]) { + if ([[self accessibilityRole] isEqualToString:NSAccessibilityStaticTextRole]) { jobject axName = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleName, fAccessible, fComponent); CHECK_EXCEPTION(); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m index 1a33b6a003a77..f3e0ee1f092c9 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m @@ -26,6 +26,7 @@ #import "GroupAccessibility.h" #import "JNIUtilities.h" #import "ThreadUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" /* * This is the protocol for the components that contain children. * Basic logic of accessibilityChildren might be overridden in the specific implementing @@ -43,9 +44,9 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (NSArray *)accessibilityChildren { JNIEnv *env = [ThreadUtilities getJNIEnv]; - NSArray *children = [JavaComponentAccessibility childrenOfParent:self + NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env - withChildrenCode:JAVA_AX_ALL_CHILDREN + withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:NO]; if ([children count] == 0) { @@ -55,4 +56,14 @@ - (NSArray *)accessibilityChildren { } } +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ImageAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ImageAccessibility.m index 4882d709a0169..63701a180c90f 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ImageAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ImageAccessibility.m @@ -36,7 +36,17 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (NSString * _Nullable)accessibilityLabel { - return [self accessibilityTitleAttribute]; + return [super accessibilityLabel]; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; } @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.h new file mode 100644 index 0000000000000..ddfcd2217f8cb --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface ListAccessibility : CommonComponentAccessibility +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.m new file mode 100644 index 0000000000000..4dc958886bb21 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.m @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "ListAccessibility.h" +#import "JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" + +@implementation ListAccessibility + +// NSAccessibilityElement protocol methods + +- (nullable NSArray> *)accessibilityRows +{ + return [self accessibilityChildren]; +} + +- (nullable NSArray> *)accessibilitySelectedRows +{ + return [self accessibilitySelectedChildren]; +} + +- (NSString *)accessibilityLabel +{ + return [super accessibilityLabel] == NULL ? @"list" : [super accessibilityLabel]; +} + +// to avoid warning (why?): method in protocol 'NSAccessibilityElement' not implemented + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +// to avoid warning (why?): method in protocol 'NSAccessibilityElement' not implemented + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +@end + diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.h new file mode 100644 index 0000000000000..2453f92e58fe0 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface ListRowAccessibility : CommonComponentAccessibility +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m new file mode 100644 index 0000000000000..2c4d51e10ffab --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#import "ListRowAccessibility.h" +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" +#import "ListAccessibility.h" +#import "ThreadUtilities.h" + +@implementation ListRowAccessibility + +// NSAccessibilityElement protocol methods + +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityRowRole; +} + +- (NSArray *)accessibilityChildren +{ + NSArray *children = [super accessibilityChildren]; + if (children == NULL) { + + // Since the row element has already been created, we should no create it again, but just retrieve it by a pointer, that's why isWrapped is set to YES. + CommonComponentAccessibility *newChild = [CommonComponentAccessibility createWithParent:self + accessible:self->fAccessible + role:self->fJavaRole + index:self->fIndex + withEnv:[ThreadUtilities getJNIEnv] + withView:self->fView + isWrapped:YES]; + return [NSArray arrayWithObject:newChild]; + } else { + return children; + } +} + +- (NSInteger)accessibilityIndex +{ + return [[self accessibilityParent] accessibilityIndexOfChild:self]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.h new file mode 100644 index 0000000000000..33a05318b5a4a --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface NavigableTextAccessibility : CommonComponentAccessibility + +@property(readonly) BOOL accessibleIsPasswordText; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m new file mode 100644 index 0000000000000..84d8785e20541 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "NavigableTextAccessibility.h" +#import "JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +static jclass sjc_CAccessibility = NULL; +#define GET_CACCESSIBLITY_CLASS() \ + GET_CLASS(sjc_CAccessibility, "sun/lwawt/macosx/CAccessibility"); +#define GET_CACCESSIBLITY_CLASS_RETURN(ret) \ + GET_CLASS_RETURN(sjc_CAccessibility, "sun/lwawt/macosx/CAccessibility", ret); + +static jmethodID sjm_getAccessibleText = NULL; +#define GET_ACCESSIBLETEXT_METHOD_RETURN(ret) \ + GET_CACCESSIBLITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleText, sjc_CAccessibility, "getAccessibleText", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleText;", ret); + +static jclass sjc_CAccessibleText = NULL; +#define GET_CACCESSIBLETEXT_CLASS() \ + GET_CLASS(sjc_CAccessibleText, "sun/lwawt/macosx/CAccessibleText"); +#define GET_CACCESSIBLETEXT_CLASS_RETURN(ret) \ + GET_CLASS_RETURN(sjc_CAccessibleText, "sun/lwawt/macosx/CAccessibleText", ret); + +static jmethodID sjm_getAccessibleEditableText = NULL; +#define GET_ACCESSIBLEEDITABLETEXT_METHOD_RETURN(ret) \ + GET_CACCESSIBLETEXT_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleEditableText, sjc_CAccessibleText, "getAccessibleEditableText", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleEditableText;", ret); + + +@implementation NavigableTextAccessibility + +- (BOOL)accessibleIsPasswordText { + return [fJavaRole isEqualToString:@"passwordtext"]; +} + +// NSAccessibilityElement protocol methods + +- (NSRect)accessibilityFrameForRange:(NSRange)range +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(NSMakeRect(0, 0, 0, 0)); + DECLARE_STATIC_METHOD_RETURN(jm_getBoundsForRange, sjc_CAccessibleText, "getBoundsForRange", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;II)[D", NSMakeRect(0, 0, 0, 0)); + jdoubleArray axBounds = (jdoubleArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, jm_getBoundsForRange, + fAccessible, fComponent, range.location, range.length); + CHECK_EXCEPTION(); + if (axBounds == NULL) return NSMakeRect(0, 0, 0, 0); + + // We cheat because we know that the array is 4 elements long (x, y, width, height) + jdouble *values = (*env)->GetDoubleArrayElements(env, axBounds, 0); + CHECK_EXCEPTION(); + + NSRect bounds; + bounds.origin.x = values[0]; + bounds.origin.y = [[[[self view] window] screen] frame].size.height - values[1] - values[3]; //values[1] is y-coord from top-left of screen. Flip. Account for the height (values[3]) when flipping + bounds.size.width = values[2]; + bounds.size.height = values[3]; + (*env)->ReleaseDoubleArrayElements(env, axBounds, values, 0); + return bounds; +} + +- (NSInteger)accessibilityLineForIndex:(NSInteger)index +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(-1); + DECLARE_STATIC_METHOD_RETURN(jm_getLineNumberForIndex, sjc_CAccessibleText, "getLineNumberForIndex", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)I", -1); + jint row = (*env)->CallStaticIntMethod(env, sjc_CAccessibleText, jm_getLineNumberForIndex, + fAccessible, fComponent, index); + CHECK_EXCEPTION(); + if (row < 0) return -1; + return row; +} + +- (NSRange)accessibilityRangeForLine:(NSInteger)line +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(NSRangeFromString(@"")); + DECLARE_STATIC_METHOD_RETURN(jm_getRangeForLine, sjc_CAccessibleText, "getRangeForLine", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)[I", NSRangeFromString(@"")); + jintArray axTextRange = (jintArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, + jm_getRangeForLine, fAccessible, fComponent, line); + CHECK_EXCEPTION(); + if (axTextRange == NULL) return NSRangeFromString(@""); + + NSRange range = [javaIntArrayToNSRangeValue(env,axTextRange) rangeValue]; + (*env)->DeleteLocalRef(env, axTextRange); + return range; +} + +- (NSString *)accessibilityStringForRange:(NSRange)range +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getStringForRange, sjc_CAccessibleText, "getStringForRange", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;II)Ljava/lang/String;", nil); + jstring jstringForRange = (jstring)(*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, jm_getStringForRange, + fAccessible, fComponent, range.location, range.length); + CHECK_EXCEPTION(); + if (jstringForRange == NULL) return @""; + NSString* str = JavaStringToNSString(env, jstringForRange); + (*env)->DeleteLocalRef(env, jstringForRange); + return str; +} + +- (id)accessibilityValue +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", nil); + // cmcnote: inefficient to make three distinct JNI calls. Coalesce. radr://3951923 + GET_ACCESSIBLETEXT_METHOD_RETURN(@""); + jobject axText = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, + sjm_getAccessibleText, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axText == NULL) return nil; + (*env)->DeleteLocalRef(env, axText); + + GET_ACCESSIBLEEDITABLETEXT_METHOD_RETURN(nil); + jobject axEditableText = (*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, + sjm_getAccessibleEditableText, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axEditableText == NULL) return nil; + + DECLARE_STATIC_METHOD_RETURN(jm_getTextRange, sjc_CAccessibleText, "getTextRange", + "(Ljavax/accessibility/AccessibleEditableText;IILjava/awt/Component;)Ljava/lang/String;", nil); + jobject jrange = (*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, jm_getTextRange, + axEditableText, 0, getAxTextCharCount(env, axEditableText, fComponent), fComponent); + CHECK_EXCEPTION(); + NSString *string = JavaStringToNSString(env, jrange); + + (*env)->DeleteLocalRef(env, jrange); + (*env)->DeleteLocalRef(env, axEditableText); + + if (string == nil) string = @""; + return string; +} + +- (NSAccessibilitySubrole)accessibilitySubrole { + if ([self accessibleIsPasswordText]) { + return NSAccessibilitySecureTextFieldSubrole; + } + return nil; +} + +- (NSRange)accessibilityRangeForIndex:(NSInteger)index +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(NSRangeFromString(@"")); + DECLARE_STATIC_METHOD_RETURN(jm_getRangeForIndex, sjc_CAccessibleText, "getRangeForIndex", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)[I", NSRangeFromString(@"")); + jintArray axTextRange = (jintArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, jm_getRangeForIndex, + fAccessible, fComponent, index); + CHECK_EXCEPTION(); + if (axTextRange == NULL) return NSRangeFromString(@""); + + return [javaIntArrayToNSRangeValue(env, axTextRange) rangeValue]; +} + +- (NSAccessibilityRole)accessibilityRole { + return [sRoles objectForKey:self.javaRole]; +} + +- (NSRange)accessibilityRangeForPosition:(NSPoint)point +{ + point.y = [[[[self view] window] screen] frame].size.height - point.y; // flip into java screen coords (0 is at upper-left corner of screen) + + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(NSRangeFromString(@"")); + DECLARE_STATIC_METHOD_RETURN(jm_getCharacterIndexAtPosition, sjc_CAccessibleText, "getCharacterIndexAtPosition", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;II)I", NSRangeFromString(@"")); + jint charIndex = (*env)->CallStaticIntMethod(env, sjc_CAccessibleText, jm_getCharacterIndexAtPosition, + fAccessible, fComponent, point.x, point.y); + CHECK_EXCEPTION(); + if (charIndex == -1) return NSRangeFromString(@""); + + // AccessibleText.getIndexAtPoint returns -1 for an invalid point + NSRange range = NSMakeRange(charIndex, 1); //range's length is 1 - one-character range + return range; +} + +- (NSString *)accessibilitySelectedText +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getSelectedText, sjc_CAccessibleText, "getSelectedText", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", nil); + jobject axText = (*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, jm_getSelectedText, + fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axText == NULL) return @""; + NSString* str = JavaStringToNSString(env, axText); + (*env)->DeleteLocalRef(env, axText); + return str; +} + +- (NSRange)accessibilitySelectedTextRange +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(NSRangeFromString(@"")); + DECLARE_STATIC_METHOD_RETURN(jm_getSelectedTextRange, sjc_CAccessibleText, "getSelectedTextRange", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)[I", NSRangeFromString(@"")); + jintArray axTextRange = (*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, + jm_getSelectedTextRange, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axTextRange == NULL) return NSRangeFromString(@""); + + return [javaIntArrayToNSRangeValue(env, axTextRange) rangeValue]; +} + +- (NSInteger)accessibilityNumberOfCharacters +{ + // cmcnote: should coalesce these two calls - radr://3951923 + // also, static text doesn't always have accessibleText. if axText is null, should get the charcount of the accessibleName instead + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_ACCESSIBLETEXT_METHOD_RETURN(0); + jobject axText = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, + sjm_getAccessibleText, fAccessible, fComponent); + CHECK_EXCEPTION(); + NSInteger num = getAxTextCharCount(env, axText, fComponent); + (*env)->DeleteLocalRef(env, axText); + return num; +} + +- (NSInteger)accessibilityInsertionPointLineNumber +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(0); + DECLARE_STATIC_METHOD_RETURN(jm_getLineNumberForInsertionPoint, sjc_CAccessibleText, + "getLineNumberForInsertionPoint", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)I", 0); + jint row = (*env)->CallStaticIntMethod(env, sjc_CAccessibleText, + jm_getLineNumberForInsertionPoint, fAccessible, fComponent); + CHECK_EXCEPTION(); + return row >= 0 ? row : 0; +} + +- (void)setAccessibilitySelectedText:(NSString *)accessibilitySelectedText +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jstring jstringValue = NSStringToJavaString(env, accessibilitySelectedText); + GET_CACCESSIBLETEXT_CLASS(); + DECLARE_STATIC_METHOD(jm_setSelectedText, sjc_CAccessibleText, "setSelectedText", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;Ljava/lang/String;)V"); + (*env)->CallStaticVoidMethod(env, sjc_CAccessibleText, jm_setSelectedText, + fAccessible, fComponent, jstringValue); + CHECK_EXCEPTION(); +} + +- (void)setAccessibilitySelectedTextRange:(NSRange)accessibilitySelectedTextRange +{ + jint startIndex = accessibilitySelectedTextRange.location; + jint endIndex = startIndex + accessibilitySelectedTextRange.length; + + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS(); + DECLARE_STATIC_METHOD(jm_setSelectedTextRange, sjc_CAccessibleText, "setSelectedTextRange", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;II)V"); + (*env)->CallStaticVoidMethod(env, sjc_CAccessibleText, jm_setSelectedTextRange, + fAccessible, fComponent, startIndex, endIndex); + CHECK_EXCEPTION(); +} + +- (BOOL)isAccessibilityEdited { + return YES; +} + +- (BOOL)isAccessibilityEnabled { + return YES; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +/* +* Other text methods +- (NSRange)accessibilitySharedCharacterRange; +- (NSArray *)accessibilitySharedTextUIElements; +- (NSData *)accessibilityRTFForRange:(NSRange)range; +- (NSRange)accessibilityStyleRangeForIndex:(NSInteger)index; +*/ + +@end + diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h new file mode 100644 index 0000000000000..48074b209e0be --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "ListAccessibility.h" + +// This is a tree representation. See: https://developer.apple.com/documentation/appkit/nsoutlineview + +@interface OutlineAccessibility : ListAccessibility + +@property(readonly) BOOL isTreeRootVisible; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m new file mode 100644 index 0000000000000..acc5727cd6b31 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "OutlineAccessibility.h" +#import "JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +static jclass sjc_CAccessibility = NULL; + +static jmethodID sjm_isTreeRootVisible = NULL; +#define GET_ISTREEROOTVISIBLE_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_isTreeRootVisible, sjc_CAccessibility, "isTreeRootVisible", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z", ret); + +@implementation OutlineAccessibility + +- (BOOL)isTreeRootVisible +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_ISTREEROOTVISIBLE_METHOD_RETURN(NO); + bool isTreeRootVisible = (*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, sjm_isTreeRootVisible, fAccessible, fComponent); + CHECK_EXCEPTION(); + return isTreeRootVisible; +} + +// NSAccessibilityElement protocol methods + +- (NSString *)accessibilityLabel +{ + return [[super accessibilityLabel] isEqualToString:@"list"] ? @"tree" : [super accessibilityLabel]; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.h new file mode 100644 index 0000000000000..8cc8e1760c607 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "ListRowAccessibility.h" + +@interface OutlineRowAccessibility : ListRowAccessibility + +@property(readwrite) int accessibleLevel; + +- (jobject)currentAccessibleWithENV:(JNIEnv *)env; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m new file mode 100644 index 0000000000000..a2bce0bf70152 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#import "OutlineRowAccessibility.h" +#import "JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" +#import "OutlineAccessibility.h" +#import "sun_lwawt_macosx_CAccessibility.h" + +static jclass sjc_CAccessible = NULL; +#define GET_CACCESSIBLE_CLASS_RETURN(ret) \ + GET_CLASS_RETURN(sjc_CAccessible, "sun/lwawt/macosx/CAccessible", ret); + +@implementation OutlineRowAccessibility + +@synthesize accessibleLevel; + +- (jobject)currentAccessibleWithENV:(JNIEnv *)env +{ + jobject jAxContext = getAxContext(env, fAccessible, fComponent); + if (jAxContext == NULL) return NULL; + jclass axContextClass = (*env)->GetObjectClass(env, jAxContext); + DECLARE_METHOD_RETURN(jm_getCurrentComponent, axContextClass, "getCurrentComponent", "()Ljava/awt/Component;", NULL); + jobject newComponent = (*env)->CallObjectMethod(env, jAxContext, jm_getCurrentComponent); + CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, jAxContext); + if (newComponent != NULL) { + GET_CACCESSIBLE_CLASS_RETURN(NULL); + DECLARE_STATIC_METHOD_RETURN(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;", NULL); + jobject currentAccessible = (*env)->CallStaticObjectMethod(env, sjc_CAccessible, sjm_getCAccessible, newComponent); + CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, newComponent); + return currentAccessible; + } else { + return NULL; + } +} + +// NSAccessibilityElement protocol methods + +- (NSArray *)accessibilityChildren +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject currentAccessible = [self currentAccessibleWithENV:env]; + if (currentAccessible != NULL) { + CommonComponentAccessibility *currentElement = [CommonComponentAccessibility createWithAccessible:currentAccessible withEnv:env withView:self->fView isCurrent:YES]; + NSArray *children = [CommonComponentAccessibility childrenOfParent:currentElement withEnv:env withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:YES]; + if ([children count] != 0) { + return children; + } + } + + return [NSArray arrayWithObject:[CommonComponentAccessibility createWithParent:self + accessible:self->fAccessible + role:self->fJavaRole + index:self->fIndex + withEnv:env + withView:self->fView + isWrapped:YES]]; +} + +- (NSInteger)accessibilityDisclosureLevel +{ + int level = [self accessibleLevel]; + return [(OutlineAccessibility *)[self accessibilityParent] isTreeRootVisible] ? level - 1 : level; +} + +- (BOOL)isAccessibilityDisclosed +{ + return isExpanded([ThreadUtilities getJNIEnv], [self axContextWithEnv:[ThreadUtilities getJNIEnv]], self->fComponent); +} + +- (NSAccessibilitySubrole)accessibilitySubrole +{ + return NSAccessibilityOutlineRowSubrole;; +} + +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityRowRole;; +} + +- (BOOL)isAccessibilitySelected +{ + return YES; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/RadiobuttonAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/RadiobuttonAccessibility.m index 8254b63df3d5a..7c2ce4a473561 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/RadiobuttonAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/RadiobuttonAccessibility.m @@ -39,7 +39,7 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (id _Nonnull) accessibilityValue { AWT_ASSERT_APPKIT_THREAD; - return [self accessibilityValueAttribute]; + return [super accessibilityValue]; } @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m index 71c8de3de6ac7..d3e2d352db693 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m @@ -26,6 +26,7 @@ #import "ScrollAreaAccessibility.h" #import "ThreadUtilities.h" #import "JNIUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" /* * Implementation of the accessibility peer for the ScrollArea role @@ -35,16 +36,16 @@ @implementation ScrollAreaAccessibility - (NSArray * _Nullable)accessibilityContentsAttribute { JNIEnv *env = [ThreadUtilities getJNIEnv]; - NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; + NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:YES]; if ([children count] <= 0) return nil; NSArray *contents = [NSMutableArray arrayWithCapacity:[children count]]; // The scroll bars are in the children. children less the scroll bars is the contents NSEnumerator *enumerator = [children objectEnumerator]; - JavaComponentAccessibility *aElement; - while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) { - if (![[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) { + CommonComponentAccessibility *aElement; + while ((aElement = (CommonComponentAccessibility *)[enumerator nextObject])) { + if (![[aElement accessibilityRole] isEqualToString:NSAccessibilityScrollBarRole]) { // no scroll bars in contents [(NSMutableArray *)contents addObject:aElement]; } @@ -56,14 +57,14 @@ - (id _Nullable)getScrollBarwithOrientation:(enum NSAccessibilityOrientation)ori { JNIEnv *env = [ThreadUtilities getJNIEnv]; - NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; + NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:YES]; if ([children count] <= 0) return nil; // The scroll bars are in the children. - JavaComponentAccessibility *aElement; + CommonComponentAccessibility *aElement; NSEnumerator *enumerator = [children objectEnumerator]; - while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) { - if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) { + while ((aElement = (CommonComponentAccessibility *)[enumerator nextObject])) { + if ([[aElement accessibilityRole] isEqualToString:NSAccessibilityScrollBarRole]) { jobject elementAxContext = [aElement axContextWithEnv:env]; if (orientation == NSAccessibilityOrientationHorizontal) { if (isHorizontal(env, elementAxContext, fComponent)) { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SliderAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SliderAccessibility.m index 5324b7e1afd4f..39cb21f3149b1 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SliderAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SliderAccessibility.m @@ -39,12 +39,12 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (NSString * _Nullable)accessibilityLabel { - return [self accessibilityTitleAttribute]; + return [super accessibilityLabel]; } - (id _Nullable)accessibilityValue { - return [self accessibilityValueAttribute]; + return [super accessibilityValue]; } - (BOOL)accessibilityPerformIncrement @@ -57,4 +57,14 @@ - (BOOL)accessibilityPerformDecrement return [self performAccessibleAction:DECREMENT]; } +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m index 7290a2ee8484d..4dac6bd93f953 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m @@ -39,12 +39,12 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (NSString * _Nullable)accessibilityLabel { - return [self accessibilityTitleAttribute]; + return [super accessibilityLabel]; } - (id _Nullable)accessibilityValue { - return [self accessibilityValueAttribute]; + return [super accessibilityValue]; } - (BOOL)accessibilityPerformIncrement @@ -58,4 +58,14 @@ - (BOOL)accessibilityPerformDecrement return [self performAccessibleAction:DECREMENT]; } +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m index 8e905c45c6438..b54a32be5eba9 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m @@ -47,4 +47,14 @@ - (NSRange)accessibilityVisibleCharacterRange return [self accessibilityVisibleCharacterRangeAttribute]; } +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.h new file mode 100644 index 0000000000000..0b497d4afc264 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface TabButtonAccessibility : CommonComponentAccessibility { + jobject fTabGroupAxContext; +} + +@property(readonly) jobject tabGroup; + +// from TabGroup controller +- (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole; +- (void)performPressAction; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.m new file mode 100644 index 0000000000000..4caf97c21cb67 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.m @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "TabButtonAccessibility.h" +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +@implementation TabButtonAccessibility + +- (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole +{ + self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole]; + if (self) { + if (tabGroup != NULL) { + fTabGroupAxContext = (*env)->NewWeakGlobalRef(env, tabGroup); + CHECK_EXCEPTION(); + } else { + fTabGroupAxContext = NULL; + } + } + return self; +} + +- (void)dealloc +{ + JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; + + if (fTabGroupAxContext != NULL) { + (*env)->DeleteWeakGlobalRef(env, fTabGroupAxContext); + fTabGroupAxContext = NULL; + } + + [super dealloc]; +} + +- (jobject)tabGroup +{ + if (fTabGroupAxContext == NULL) { + JNIEnv* env = [ThreadUtilities getJNIEnv]; + jobject tabGroupAxContext = [(CommonComponentAccessibility *)[self parent] axContextWithEnv:env]; + fTabGroupAxContext = (*env)->NewWeakGlobalRef(env, tabGroupAxContext); + CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, tabGroupAxContext); + } + return fTabGroupAxContext; +} + +- (void)performPressAction { + JNIEnv *env = [ThreadUtilities getJNIEnv]; + TabGroupAction *action = [[TabGroupAction alloc] initWithEnv:env withTabGroup:[self tabGroup] withIndex:fIndex withComponent:fComponent]; + [action perform]; + [action release]; +} + +// NSAccessibilityElement protocol methods + +- (NSAccessibilitySubrole)accessibilitySubrole +{ + if (@available(macOS 10.13, *)) { + return NSAccessibilityTabButtonSubrole; + } + return NSAccessibilityUnknownSubrole; +} + +- (id)accessibilityValue +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + jobject selAccessible = getAxContextSelection(env, [self tabGroup], fIndex, fComponent); + + // Returns the current selection of the page tab list + id val = [NSNumber numberWithBool:ObjectEquals(env, axContext, selAccessible, fComponent)]; + + (*env)->DeleteLocalRef(env, selAccessible); + (*env)->DeleteLocalRef(env, axContext); + return val; +} + +- (BOOL)accessibilityPerformPress { + [self performPressAction]; + return YES; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.h new file mode 100644 index 0000000000000..c99b18c904e95 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface TabGroupAccessibility : CommonComponentAccessibility { + NSInteger _numTabs; +} + +@property(readonly) NSInteger numTabs; + +- (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext; +- (NSArray *)tabButtonsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored; +- (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.m new file mode 100644 index 0000000000000..76fafc0faafda --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.m @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "TabGroupAccessibility.h" +#import "TabButtonAccessibility.h" +#import "../JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" + +static jclass sjc_CAccessibility = NULL; + +static jmethodID jm_getChildrenAndRoles = NULL; +#define GET_CHILDRENANDROLES_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles",\ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;", ret); + +@implementation TabGroupAccessibility + +- (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext +{ + NSArray *tabs = [self tabButtonsWithEnv:env withTabGroupAxContext:axContext withTabCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:NO]; + + // Looking at the JTabbedPane sources, there is always one AccessibleSelection. + jobject selAccessible = getAxContextSelection(env, axContext, 0, fComponent); + if (selAccessible == NULL) return nil; + + // Go through the tabs and find selAccessible + _numTabs = [tabs count]; + CommonComponentAccessibility *aTab; + NSInteger i; + for (i = 0; i < _numTabs; i++) { + aTab = (CommonComponentAccessibility *)[tabs objectAtIndex:i]; + if ([aTab isAccessibleWithEnv:env forAccessible:selAccessible]) { + (*env)->DeleteLocalRef(env, selAccessible); + return aTab; + } + } + (*env)->DeleteLocalRef(env, selAccessible); + return nil; +} + +- (NSArray *)tabButtonsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored +{ + GET_CHILDRENANDROLES_METHOD_RETURN(nil); + jobjectArray jtabsAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, + fAccessible, fComponent, whichTabs, allowIgnored); + CHECK_EXCEPTION(); + if(jtabsAndRoles == NULL) return nil; + + jsize arrayLen = (*env)->GetArrayLength(env, jtabsAndRoles); + if (arrayLen == 0) { + (*env)->DeleteLocalRef(env, jtabsAndRoles); + return nil; + } + NSMutableArray *tabs = [NSMutableArray arrayWithCapacity:(arrayLen/2)]; + + // 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 + jobject jtabJavaRole = (*env)->GetObjectArrayElement(env, jtabsAndRoles, 1); // the array entries alternate between tab/role, starting with tab. so the first role is entry 1. + if (jtabJavaRole == NULL) { + (*env)->DeleteLocalRef(env, jtabsAndRoles); + return nil; + } + DECLARE_CLASS_RETURN(sjc_AccessibleRole, "javax/accessibility/AccessibleRole", nil); + DECLARE_FIELD_RETURN(sjf_key, sjc_AccessibleRole, "key", "Ljava/lang/String;", nil); + jobject jkey = (*env)->GetObjectField(env, jtabJavaRole, sjf_key); + CHECK_EXCEPTION(); + NSString *tabJavaRole = JavaStringToNSString(env, jkey); + (*env)->DeleteLocalRef(env, jkey); + + NSInteger i; + NSUInteger tabIndex = (whichTabs >= 0) ? whichTabs : 0; // if we're getting one particular child, make sure to set its index correctly + for(i = 0; i < arrayLen; i+=2) { + jobject jtab = (*env)->GetObjectArrayElement(env, jtabsAndRoles, i); + CommonComponentAccessibility *tab = [[[TabButtonAccessibility alloc] initWithParent:self withEnv:env withAccessible:jtab withIndex:tabIndex withTabGroup:axContext withView:[self view] withJavaRole:tabJavaRole] autorelease]; + (*env)->DeleteLocalRef(env, jtab); + [tabs addObject:tab]; + tabIndex++; + } + (*env)->DeleteLocalRef(env, jtabsAndRoles); + return tabs; +} + +- (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored +{ + // Contents are the children of the selected tab. + id currentTab = [self currentTabWithEnv:env withAxContext:axContext]; + if (currentTab == nil) return nil; + + NSArray *contents = [CommonComponentAccessibility childrenOfParent:currentTab withEnv:env withChildrenCode:whichTabs allowIgnored:allowIgnored]; + if ([contents count] <= 0) return nil; + return contents; +} + +- (NSInteger)numTabs +{ + if (_numTabs == -1) { + _numTabs = [[self accessibilityTabsAttribute] count]; + } + return _numTabs; +} + +// NSAccessibilityElement protocol methods + +- (NSArray *)accessibilityTabs +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + id tabs = [self tabButtonsWithEnv:env withTabGroupAxContext:axContext withTabCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:NO]; + (*env)->DeleteLocalRef(env, axContext); + return tabs; +} + +- (NSArray *)accessibilityContents +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + NSArray* cont = [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:NO]; + (*env)->DeleteLocalRef(env, axContext); + return cont; +} + +- (id)accessibilityValue +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + id val = [self currentTabWithEnv:env withAxContext:axContext]; + (*env)->DeleteLocalRef(env, axContext); + return val; +} + +- (NSArray *)accessibilityChildren +{ + //children = AXTabs + AXContents + NSArray *tabs = [self accessibilityTabs]; + NSArray *contents = [self accessibilityContents]; + + NSMutableArray *children = [NSMutableArray arrayWithCapacity:[tabs count] + [contents count]]; + [children addObjectsFromArray:tabs]; + [children addObjectsFromArray:contents]; + + return (NSArray *)children; +} + +- (NSArray *)accessibilityArrayAttributeValues:(NSAccessibilityAttributeName)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount +{ + NSArray *result = nil; + if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) { + // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + + //children = AXTabs + AXContents + NSArray *children = [self tabButtonsWithEnv:env withTabGroupAxContext:axContext withTabCode:index allowIgnored:NO]; // first look at the tabs + if ([children count] > 0) { + result = children; + } else { + children= [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:(index-[self numTabs]) allowIgnored:NO]; + if ([children count] > 0) { + result = children; + } + } + (*env)->DeleteLocalRef(env, axContext); + } else { + result = [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount]; + } + return result; +} + +- (void)setAccessibilityValue:(id)accessibilityValue +{ + // set the current tab + NSNumber *number = (NSNumber *)accessibilityValue; + if (![number boolValue]) return; + + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + setAxContextSelection(env, axContext, fIndex, fComponent); + (*env)->DeleteLocalRef(env, axContext); +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h new file mode 100644 index 0000000000000..4f746706f5c53 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface TableAccessibility : CommonComponentAccessibility + +- (BOOL)isAccessibleChildSelectedFromIndex:(int)index; +- (int) accessibleRowAtIndex:(int)index; +- (int) accessibleColumnAtIndex:(int)index; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m new file mode 100644 index 0000000000000..cd534c1ad3ccb --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#import "TableRowAccessibility.h" +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" +#import "TableAccessibility.h" +#import "CellAccessibility.h" +#import "ColumnAccessibility.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" +#import "CellAccessibility.h" +#import "sun_lwawt_macosx_CAccessibility.h" + +static jclass sjc_CAccessibility = NULL; + +static jmethodID sjm_getAccessibleName = NULL; +#define GET_ACCESSIBLENAME_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", ret); + +@implementation TableAccessibility + +- (id)getTableInfo:(jint)info +{ + if (fAccessible == NULL) return 0; + + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getTableInfo, sjc_CAccessibility, "getTableInfo", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)I", nil); + jint count = (*env)->CallStaticIntMethod(env, sjc_CAccessibility, jm_getTableInfo, fAccessible, + fComponent, info); + CHECK_EXCEPTION(); + NSNumber *index = [NSNumber numberWithInt:count]; + return index; +} + +- (NSArray *)getTableSelectedInfo:(jint)info +{ + if (fAccessible == NULL) return 0; + + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getTableSelectedInfo, sjc_CAccessibility, "getTableSelectedInfo", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)[I", nil); + jintArray selected = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getTableSelectedInfo, fAccessible, + fComponent, info); + CHECK_EXCEPTION(); + if (selected == NULL) { + return nil; + } + jsize arrayLen = (*env)->GetArrayLength(env, selected); + jint *indexsis = (*env)->GetIntArrayElements(env, selected, 0); + NSMutableArray *nsArraySelected = [NSMutableArray arrayWithCapacity:arrayLen]; + for (int i = 0; i < arrayLen; i++) { + [nsArraySelected addObject:[NSNumber numberWithInt:indexsis[i]]]; + } + (*env)->DeleteLocalRef(env, selected); + return [NSArray arrayWithArray:nsArraySelected]; +} + +- (int)accessibleRowAtIndex:(int)index +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return 0; + jclass clsInfo = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_getAccessibleRowAtIndex, clsInfo, "getAccessibleRowAtIndex", "(I)I", -1); + jint rowAtIndex = (*env)->CallIntMethod(env, axContext, jm_getAccessibleRowAtIndex, (jint)index); + CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, axContext); + return (int)rowAtIndex; +} + +- (int)accessibleColumnAtIndex:(int)index +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return 0; + jclass clsInfo = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_getAccessibleColumnAtIndex, clsInfo, "getAccessibleColumnAtIndex", "(I)I", -1); + jint columnAtIndex = (*env)->CallIntMethod(env, axContext, jm_getAccessibleColumnAtIndex, (jint)index); + CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, axContext); + return (int)columnAtIndex; +} + +- (BOOL) isAccessibleChildSelectedFromIndex:(int)index +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return NO; + jclass clsInfo = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_isAccessibleChildSelected, clsInfo, "isAccessibleChildSelected", "(I)Z", NO); + jboolean isAccessibleChildSelected = (*env)->CallIntMethod(env, axContext, jm_isAccessibleChildSelected, (jint)index); + CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, axContext); + return isAccessibleChildSelected; +} + +// NSAccessibilityElement protocol methods + +- (NSArray *)accessibilityChildren +{ + return [self accessibilityRows]; +} + +- (NSArray *)accessibilitySelectedChildren +{ + return [self accessibilitySelectedRows]; +} + +- (NSArray *)accessibilityRows +{ + int rowCount = [self accessibilityRowCount]; + NSMutableArray *children = [NSMutableArray arrayWithCapacity:rowCount]; + for (int i = 0; i < rowCount; i++) { + [children addObject:[[TableRowAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:NULL + withIndex:i + withView:[self view] + withJavaRole:JavaAccessibilityIgnore]]; + } + return [NSArray arrayWithArray:children]; +} + +- (nullable NSArray> *)accessibilitySelectedRows +{ + NSArray *selectedRowIndexses = [self getTableSelectedInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_ROWS]; + NSMutableArray *children = [NSMutableArray arrayWithCapacity:[selectedRowIndexses count]]; + for (NSNumber *index in selectedRowIndexses) { + [children addObject:[[TableRowAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:NULL + withIndex:index.unsignedIntValue + withView:[self view] + withJavaRole:JavaAccessibilityIgnore]]; + } + return [NSArray arrayWithArray:children]; +} + +- (NSString *)accessibilityLabel +{ + return [super accessibilityLabel] == NULL ? @"table" : [super accessibilityLabel]; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +- (nullable NSArray *)accessibilityColumns +{ + int colCount = [self accessibilityColumnCount]; + NSMutableArray *columns = [NSMutableArray arrayWithCapacity:colCount]; + for (int i = 0; i < colCount; i++) { + [columns addObject:[[ColumnAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:NULL + withIndex:i + withView:self->fView + withJavaRole:JavaAccessibilityIgnore]]; + } + return [NSArray arrayWithArray:columns]; +} + +- (nullable NSArray *)accessibilitySelectedColumns +{ + NSArray *indexes = [self getTableSelectedInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_COLS]; + NSMutableArray *columns = [NSMutableArray arrayWithCapacity:[indexes count]]; + for (NSNumber *i in indexes) { + [columns addObject:[[ColumnAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:NULL + withIndex:i.unsignedIntValue + withView:self->fView + withJavaRole:JavaAccessibilityIgnore]]; + } + return [NSArray arrayWithArray:columns]; +} + +- (NSInteger)accessibilityRowCount +{ + return [[self getTableInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_ROWS] integerValue]; +} + +- (NSInteger)accessibilityColumnCount +{ + return [[self getTableInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_COLS] integerValue]; +} + +- (nullable NSArray *)accessibilitySelectedCells +{ + NSArray *children = [super accessibilitySelectedChildren]; + NSMutableArray *cells = [NSMutableArray arrayWithCapacity:[children count]]; + for (CommonComponentAccessibility *child in children) { + [cells addObject:[[CellAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:child->fAccessible + withIndex:child->fIndex + withView:fView + withJavaRole:child->fJavaRole]]; + } + return [NSArray arrayWithArray:cells]; +} + +- (id)accessibilityCellForColumn:(NSInteger)column row:(NSInteger)row { + return [[(TableRowAccessibility *)[[self accessibilityRows] objectAtIndex:row] accessibilityChildren] objectAtIndex:column]; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.h new file mode 100644 index 0000000000000..afa9eb44e3f22 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface TableRowAccessibility : CommonComponentAccessibility + +@property(readonly) NSUInteger rowNumberInTable; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m new file mode 100644 index 0000000000000..60985e203f9a6 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "TableRowAccessibility.h" +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" +#import "TableAccessibility.h" +#import "CellAccessibility.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" + +static jclass sjc_CAccessibility = NULL; + +static jmethodID jm_getChildrenAndRoles = NULL; +#define GET_CHILDRENANDROLES_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles",\ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;", ret); + +@implementation TableRowAccessibility + +// NSAccessibilityElement protocol methods + +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityRowRole; +} + +- (NSAccessibilitySubrole)accessibilitySubrole +{ + return NSAccessibilityTableRowSubrole; +} + +- (NSArray *)accessibilityChildren +{ + NSArray *children = [super accessibilityChildren]; + if (children == nil) { + JNIEnv *env = [ThreadUtilities getJNIEnv]; + CommonComponentAccessibility *parent = [self accessibilityParent]; + if (parent->fAccessible == NULL) return nil; + GET_CHILDRENANDROLES_METHOD_RETURN(nil); + jobjectArray jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, + parent->fAccessible, parent->fComponent, sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN, NO); + CHECK_EXCEPTION(); + if (jchildrenAndRoles == NULL) return nil; + + jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); + NSMutableArray *childrenCells = [NSMutableArray arrayWithCapacity:arrayLen/2]; + + NSUInteger childIndex = fIndex * [(TableAccessibility *)parent accessibilityColumnCount]; + NSInteger i = childIndex * 2; + NSInteger n = (fIndex + 1) * [(TableAccessibility *)parent accessibilityColumnCount] * 2; + for(i; i < n; i+=2) + { + jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); + jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1); + + NSString *childJavaRole = nil; + if (jchildJavaRole != NULL) { + DECLARE_CLASS_RETURN(sjc_AccessibleRole, "javax/accessibility/AccessibleRole", nil); + DECLARE_FIELD_RETURN(sjf_key, sjc_AccessibleRole, "key", "Ljava/lang/String;", nil); + jobject jkey = (*env)->GetObjectField(env, jchildJavaRole, sjf_key); + CHECK_EXCEPTION(); + childJavaRole = JavaStringToNSString(env, jkey); + (*env)->DeleteLocalRef(env, jkey); + } + + CellAccessibility *child = [[CellAccessibility alloc] initWithParent:self + withEnv:env + withAccessible:jchild + withIndex:childIndex + withView:self->fView + withJavaRole:childJavaRole]; + [childrenCells addObject:[[child retain] autorelease]]; + + (*env)->DeleteLocalRef(env, jchild); + (*env)->DeleteLocalRef(env, jchildJavaRole); + + childIndex++; + } + (*env)->DeleteLocalRef(env, jchildrenAndRoles); + return childrenCells; + } else { + return children; + } +} + +- (NSInteger)accessibilityIndex +{ + return self->fIndex; +} + +- (NSString *)accessibilityLabel +{ + NSString *accessibilityName = @""; + NSArray *children = [self accessibilityChildren]; + for (id cell in children) { + if ([accessibilityName isEqualToString:@""]) { + accessibilityName = [cell accessibilityLabel]; + } else { + accessibilityName = [accessibilityName stringByAppendingFormat:@", %@", [cell accessibilityLabel]]; + } + } + return accessibilityName; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +- (NSRect)accessibilityFrame +{ + int height = [[[self accessibilityChildren] objectAtIndex:0] accessibilityFrame].size.height; + int width = 0; + NSPoint point = [[[self accessibilityChildren] objectAtIndex:0] accessibilityFrame].origin; + for (id cell in [self accessibilityChildren]) { + width += [cell accessibilityFrame].size.width; + } + return NSMakeRect(point.x, point.y, width, height); +} + +@end diff --git a/test/jdk/java/awt/a11y/AccessibleComponentTest.java b/test/jdk/java/awt/a11y/AccessibleComponentTest.java new file mode 100644 index 0000000000000..86d41574d6e34 --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleComponentTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.*; +import java.security.PublicKey; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.CountDownLatch; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.GridBagLayout; + +import javax.swing.JPanel; +import javax.swing.JFrame; +import javax.swing.JTextArea; +import javax.swing.JButton; + +public abstract class AccessibleComponentTest { + + protected static volatile boolean testResult = true; + protected static volatile CountDownLatch countDownLatch; + protected static String INSTRUCTIONS; + protected static String exceptionString; + protected JFrame mainFrame; + protected static AccessibleComponentTest a11yTest; + + public abstract CountDownLatch createCountDownLatch(); + + public void createUI(JPanel component, String testName) { + mainFrame = new JFrame(testName); + GridBagLayout layout = new GridBagLayout(); + JPanel mainControlPanel = new JPanel(layout); + JPanel resultButtonPanel = new JPanel(layout); + + GridBagConstraints gbc = new GridBagConstraints(); + + JTextArea instructionTextArea = new JTextArea(); + instructionTextArea.setText(INSTRUCTIONS); + instructionTextArea.setEditable(false); + instructionTextArea.setBackground(Color.white); + + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.HORIZONTAL; + mainControlPanel.add(instructionTextArea, gbc); + gbc.gridx = 0; + gbc.gridy = 1; + mainControlPanel.add(component); + + JButton passButton = new JButton("Pass"); + passButton.setActionCommand("Pass"); + passButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + mainFrame.dispose(); + countDownLatch.countDown(); + } + }); + + JButton failButton = new JButton("Fail"); + failButton.setActionCommand("Fail"); + failButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + testResult = false; + mainFrame.dispose(); + countDownLatch.countDown(); + } + }); + + gbc.gridx = 0; + gbc.gridy = 0; + resultButtonPanel.add(passButton, gbc); + + gbc.gridx = 1; + gbc.gridy = 0; + resultButtonPanel.add(failButton, gbc); + + gbc.gridx = 0; + gbc.gridy = 2; + mainControlPanel.add(resultButtonPanel, gbc); + + mainFrame.add(mainControlPanel); + mainFrame.pack(); + + mainFrame.addWindowListener(new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent e) { + mainFrame.dispose(); + countDownLatch.countDown(); + } + }); + mainFrame.setVisible(true); + } +} diff --git a/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java b/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java new file mode 100644 index 0000000000000..a9b29d5310ceb --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8264287 + * @summary Test implementation of NSAccessibilityComboBox protocol peer + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleJComboboxTest + * @requires (os.family == "windows" | os.family == "mac") + */ + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; +import java.awt.FlowLayout; +import java.util.concurrent.CountDownLatch; + +public class AccessibleJComboboxTest extends AccessibleComponentTest { + + @java.lang.Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + void createCombobox() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JCombobox.\n\n" + + "Turn screen reader on, and Tab to the combobox.\n\n" + + "If you can hear combobox selected item tab further and press PASS, otherwise press FAIL."; + + JPanel frame = new JPanel(); + + String[] NAMES = {"One", "Two", "Three", "Four", "Five"}; + JComboBox combo = new JComboBox<>(NAMES); + + JLabel label = new JLabel("This is combobox:"); + label.setLabelFor(combo); + + frame.setLayout(new FlowLayout()); + frame.add(label); + frame.add(combo); + exceptionString = "AccessibleJCombobox test failed!"; + super.createUI(frame, "AccessibleJComboboxTest"); + } + + public static void main(String[] args) throws Exception { + AccessibleJComboboxTest test = new AccessibleJComboboxTest(); + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeLater(test::createCombobox); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + } +} diff --git a/test/jdk/java/awt/a11y/AccessibleJListTest.java b/test/jdk/java/awt/a11y/AccessibleJListTest.java new file mode 100644 index 0000000000000..ae1dbce9b71b0 --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleJListTest.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8264292 + * @summary Test implementation of NSAccessibilityList protocol peer + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleJListTest + * @requires (os.family == "windows" | os.family == "mac") + */ + +import javax.swing.JList; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JWindow; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.ListCellRenderer; +import javax.swing.SwingUtilities; +import javax.swing.Popup; +import javax.swing.PopupFactory; + +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.Rectangle; +import java.awt.Dimension; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Window; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class AccessibleJListTest extends AccessibleComponentTest { + + private static final String[] NAMES = {"One", "Two", "Three", "Four", "Five"}; + static JWindow window; + + public static void main(String[] args) throws Exception { + a11yTest = new AccessibleJListTest(); + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createSimpleList); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createSimpleListRenderer); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createSimpleListNamed); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createCombobox); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createPushButton); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createPushButtonHeavyWeight); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + } + + @java.lang.Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + public void createSimpleList() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JList.\n\n" + + "Turn screen reader on, and Tab to the list.\n" + + "Press the up and down arrow buttons to move through the list.\n\n" + + "If you can hear menu items tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JList list = new JList<>(NAMES); + + frame.setLayout(new FlowLayout()); + frame.add(list); + exceptionString = "Accessible JList simple list test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + public void createSimpleListRenderer() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JList with renderer.\n\n" + + "Turn screen reader on, and Tab to the list.\n" + + "Press the up and down arrow buttons to move through the list.\n\n" + + "If you can hear menu items tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JList list = new JList<>(NAMES); + list.setCellRenderer(new AccessibleJListTestRenderer()); + + frame.setLayout(new FlowLayout()); + frame.add(list); + exceptionString = "Accessible JList with renderer simple list test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + public void createSimpleListNamed() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of named JList.\n\n" + + "Turn screen reader on, and Tab to the list.\n" + + "Press the tab button to move to second list.\n\n" + + "If you can hear second list name: \"second list\" - tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JList list = new JList<>(NAMES); + JList secondList = new JList<>(NAMES); + secondList.getAccessibleContext().setAccessibleName("Second list"); + frame.setLayout(new FlowLayout()); + frame.add(list); + frame.add(secondList); + exceptionString = "Accessible JList simple list named test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + + public void createCombobox() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JList in a combobox.\n\n" + + "Turn screen reader on, and Tab to the combobox.\n" + + "Press the up and down arrow buttons to move through the list.\n\n" + + "If you can hear combobox items tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JComboBox combo = new JComboBox<>(NAMES); + + frame.setLayout(new FlowLayout()); + frame.add(combo); + exceptionString = "Accessible JList combobox test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + public void createPushButton() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JList in a popup in a simple window.\n\n" + + "Turn screen reader on, and Tab to the show button and press space.\n" + + "Press the up and down arrow buttons to move through the list.\n\n" + + "If you can hear popup menu items tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JButton button = new JButton("show"); + button.setPreferredSize(new Dimension(100, 35)); + + button.addActionListener(new ActionListener() { + + final Runnable dispose = () -> { + window.dispose(); + window = null; + button.setText("show"); + }; + + @Override + public void actionPerformed(ActionEvent e) { + if (window == null) { + Rectangle bounds = frame.getBounds(); + window = new JWindow(mainFrame); + JList winList = new JList<>(NAMES); + winList.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + dispose.run(); + } + } + }); + window.add(winList); + window.setLocation(bounds.x + bounds.width + 20, bounds.y); + window.pack(); + window.setVisible(true); + button.setText("hide (ESC)"); + } else { + dispose.run(); + } + } + }); + + frame.setLayout(new FlowLayout()); + frame.add(button); + exceptionString = "Accessible JList push button with simple window test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + public void createPushButtonHeavyWeight() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JList in a popup in a heavy weight window.\n\n" + + "Turn screen reader on, and Tab to the show button and press space.\n" + + "Press the up and down arrow buttons to move through the list.\n\n" + + "If you can hear popup menu items tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JButton button = new JButton("show"); + button.setPreferredSize(new Dimension(100, 35)); + + button.addActionListener(new ActionListener() { + private Popup popup = null; + + final Runnable dispose = () -> { + popup.hide(); + popup = null; + button.requestFocus(); + button.setText("show"); + }; + + @Override + public void actionPerformed(ActionEvent e) { + if (popup == null) { + Rectangle bounds = frame.getBounds(); + PopupFactory factory = PopupFactory.getSharedInstance(); + JList winList = new JList<>(NAMES); + winList.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + dispose.run(); + } + } + }); + popup = factory.getPopup(frame, winList, bounds.x + bounds.width + 20, bounds.y); + Window c = SwingUtilities.getWindowAncestor(winList); + if (c != null) { + c.setAutoRequestFocus(true); + c.setFocusableWindowState(true); + } + popup.show(); + button.setText("hide (ESC)"); + } else { + dispose.run(); + } + } + }); + + frame.setLayout(new FlowLayout()); + frame.add(button); + exceptionString = "Accessible JList push button with heavy weight window test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + public static class AccessibleJListTestRenderer extends JPanel implements ListCellRenderer { + private JLabel labelAJT = new JLabel("AJL"); + private JLabel itemName = new JLabel(); + + AccessibleJListTestRenderer() { + super(new FlowLayout()); + setFocusable(false); + layoutComponents(); + } + + private void layoutComponents() { + add(labelAJT); + add(itemName); + } + + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(Math.min(size.width, 245), size.height); + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + itemName.setText(((String) value)); + + getAccessibleContext().setAccessibleName(labelAJT.getText() + ", " + itemName.getText()); + return this; + } + } +} diff --git a/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java b/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java new file mode 100644 index 0000000000000..f03af5d3a0975 --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8264303 + * @summary Test implementation of NSAccessibilityTabPanel protocol peer + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleJTabbedPaneTest + * @requires (os.family == "windows" | os.family == "mac") + */ + +import javax.swing.JTabbedPane; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JTextField; +import javax.swing.JCheckBox; +import javax.swing.SwingUtilities; + +import java.util.concurrent.CountDownLatch; + +public class AccessibleJTabbedPaneTest extends AccessibleComponentTest { + + @Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + void createTabPane() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTabbedPane.\n\n" + + "Turn screen reader on, and tab to the JTabbedPane.\n" + + "Use page up and page down arrow buttons to move through the tabs.\n\n" + + "If you can hear selected tab names tab further and press PASS, otherwise press FAIL.\n"; + + JTabbedPane tabbedPane = new JTabbedPane(); + + JPanel panel1 = new JPanel(); + String[] names = {"One", "Two", "Three", "Four", "Five"}; + JList list = new JList(names); + JLabel fieldName = new JLabel("Text field:"); + JTextField textField = new JTextField("some text"); + fieldName.setLabelFor(textField); + panel1.add(fieldName); + panel1.add(textField); + panel1.add(list); + tabbedPane.addTab("Tab 1", panel1); + JPanel panel2 = new JPanel(); + for (int i = 0; i < 5; i++) { + panel2.add(new JCheckBox("CheckBox " + String.valueOf(i + 1))); + } + tabbedPane.addTab("tab 2", panel2); + JPanel panel = new JPanel(); + panel.add(tabbedPane); + + exceptionString = "AccessibleJTabbedPane test failed!"; + createUI(panel, "AccessibleJTabbedPaneTest"); + } + + public static void main(String[] args) throws Exception { + AccessibleJTabbedPaneTest test = new AccessibleJTabbedPaneTest(); + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeLater(test::createTabPane); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + } +} diff --git a/test/jdk/java/awt/a11y/AccessibleJTableTest.java b/test/jdk/java/awt/a11y/AccessibleJTableTest.java new file mode 100644 index 0000000000000..209b81a58861f --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleJTableTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8267388 + * @summary Test implementation of NSAccessibilityTable protocol peer + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleJTableTest + * @requires (os.family == "windows" | os.family == "mac") + */ + +import javax.swing.JTable; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + +import java.awt.FlowLayout; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class AccessibleJTableTest extends AccessibleComponentTest { + private static final String[] columnNames = {"One", "Two", "Three"}; + private static final String[][] data = { + {"One1", "Two1", "Three1"}, + {"One2", "Two2", "Three2"}, + {"One3", "Two3", "Three3"}, + {"One4", "Two4", "Three4"}, + {"One5", "Two5", "Three5"} + }; + + @Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + public void createUI() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTable.\n\n" + + "Turn screen reader on, and Tab to the table.\n" + + "On Windows press the arrow buttons to move through the table.\n\n" + + "On MacOS, use the up and down arrow buttons to move through rows, and VoiceOver fast navigation to move through columns.\n\n" + + "If you can hear table cells ctrl+tab further and press PASS, otherwise press FAIL.\n"; + + JTable table = new JTable(data, columnNames); + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(table); + panel.add(scrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTable test failed!"; + super.createUI(panel, "AccessibleJTableTest"); + } + + public void createUINamed() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of named JTable.\n\n" + + "Turn screen reader on, and Tab to the table.\n" + + "Press the ctrl+tab button to move to second table.\n\n" + + "If you can hear second table name: \"second table\" - ctrl+tab further and press PASS, otherwise press FAIL.\n"; + + JTable table = new JTable(data, columnNames); + JTable secondTable = new JTable(data, columnNames); + secondTable.getAccessibleContext().setAccessibleName("Second table"); + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(table); + JScrollPane secondScrollPane = new JScrollPane(secondTable); + panel.add(scrollPane); + panel.add(secondScrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTable test failed!"; + super.createUI(panel, "AccessibleJTableTest"); + } + + public static void main(String[] args) throws Exception { + AccessibleJTableTest test = new AccessibleJTableTest(); + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createUI); + countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createUINamed); + countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + } +} diff --git a/test/jdk/java/awt/a11y/AccessibleJTreeTest.java b/test/jdk/java/awt/a11y/AccessibleJTreeTest.java new file mode 100644 index 0000000000000..ab4c131b0ac0a --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleJTreeTest.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8267387 + * @summary Test implementation of NSAccessibilityOutLine protocol peer + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleJTreeTest + * @requires (os.family == "windows" | os.family == "mac") + */ + +import javax.swing.JTree; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.SwingUtilities; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.util.Hashtable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class AccessibleJTreeTest extends AccessibleComponentTest { + + @Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + public void createSampleTree() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTree.\n\n" + + "Turn screen reader on, and Tab to the tree.\n" + + "Press the arrow buttons to move through the tree.\n\n" + + "If you can hear tree components tab further and press PASS, otherwise press FAIL.\n"; + + String root = "Root"; + String[] nodes = new String[] {"One node", "Two node"}; + String[][] leafs = new String[][]{{"leaf 1.1", "leaf 1.2", "leaf 1.3", "leaf 1.4"}, + {"leaf 2.1", "leaf 2.2", "leaf 2.3", "leaf 2.4"}}; + + Hashtable data = new Hashtable(); + for (int i = 0; i < nodes.length; i++) { + data.put(nodes[i], leafs[i]); + } + + JTree tree = new JTree(data); + tree.setRootVisible(true); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(tree); + panel.add(scrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTree sample item test failed!"; + super.createUI(panel, "AccessibleJTreeTest"); + } + + public void createSampleTreeUnvisableRoot() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTree with invisible root.\n\n" + + "Turn screen reader on, and Tab to the tree.\n" + + "Press the arrow buttons to move through the tree.\n\n" + + "If you can hear tree components tab further and press PASS, otherwise press FAIL.\n"; + + String root = "Root"; + String[] nodes = new String[] {"One node", "Two node"}; + String[][] leafs = new String[][]{{"leaf 1.1", "leaf 1.2", "leaf 1.3", "leaf 1.4"}, + {"leaf 2.1", "leaf 2.2", "leaf 2.3", "leaf 2.4"}}; + + Hashtable data = new Hashtable(); + for (int i = 0; i < nodes.length; i++) { + data.put(nodes[i], leafs[i]); + } + + JTree tree = new JTree(data); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(tree); + panel.add(scrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTree sample item invisible root test failed!"; + super.createUI(panel, "AccessibleJTreeTest"); + } + + public void createSampleTreeNamed() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of named JTree.\n\n" + + "Turn screen reader on, and Tab to the tree.\n" + + "Press the tab button to move to second tree.\\n\n" + + "If you can hear second tree name: \"second tree\" - tab further and press PASS, otherwise press FAIL.\n"; + + String root = "Root"; + String[] nodes = new String[] {"One node", "Two node"}; + String[][] leafs = new String[][]{{"leaf 1.1", "leaf 1.2", "leaf 1.3", "leaf 1.4"}, + {"leaf 2.1", "leaf 2.2", "leaf 2.3", "leaf 2.4"}}; + + Hashtable data = new Hashtable(); + for (int i = 0; i < nodes.length; i++) { + data.put(nodes[i], leafs[i]); + } + + JTree tree = new JTree(data); + JTree secondTree = new JTree(data); + secondTree.getAccessibleContext().setAccessibleName("Second tree"); + tree.setRootVisible(true); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(tree); + JScrollPane secondScrollPane = new JScrollPane(secondTree); + panel.add(scrollPane); + panel.add(secondScrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTree named test failed!"; + super.createUI(panel, "AccessibleJTreeTest"); + } + + + public void createRendererTree() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTree using renderer.\n\n" + + "Turn screen reader on, and Tab to the tree.\n" + + "Press the arrow buttons to move through the tree.\n\n" + + "If you can hear tree components tab further and press PASS, otherwise press FAIL.\n"; + + String root = "Root"; + String[] nodes = new String[] {"One node", "Two node"}; + String[][] leafs = new String[][]{{"leaf 1.1", "leaf 1.2", "leaf 1.3", "leaf 1.4"}, + {"leaf 2.1", "leaf 2.2", "leaf 2.3", "leaf 2.4"}}; + + Hashtable data = new Hashtable(); + for (int i = 0; i < nodes.length; i++) { + data.put(nodes[i], leafs[i]); + } + + JTree tree = new JTree(data); + tree.setRootVisible(true); + tree.setCellRenderer(new AccessibleJTreeTestRenderer()); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(tree); + panel.add(scrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTree renderer item test failed!"; + super.createUI(panel, "AccessibleJTreeTest"); + } + + public static void main(String[] args) throws Exception { + AccessibleJTreeTest test = new AccessibleJTreeTest(); + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createSampleTree); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createSampleTreeNamed); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createSampleTreeUnvisableRoot); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createRendererTree); + countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(AccessibleComponentTest.exceptionString); + } + } + + public static class AccessibleJTreeTestRenderer extends JPanel implements TreeCellRenderer { + private JLabel labelAJT = new JLabel("AJT"); + private JLabel itemName = new JLabel(); + + AccessibleJTreeTestRenderer() { + super(new FlowLayout()); + setFocusable(false); + layoutComponents(); + } + + private void layoutComponents() { + add(labelAJT); + add(itemName); + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + itemName.setText((String) (((DefaultMutableTreeNode) value).getUserObject())); + + getAccessibleContext().setAccessibleName(labelAJT.getText() + ", " + itemName.getText()); + return this; + } + + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(Math.min(size.width, 245), size.height); + } + } +} diff --git a/test/jdk/java/awt/a11y/AccessibleTextTest.java b/test/jdk/java/awt/a11y/AccessibleTextTest.java new file mode 100644 index 0000000000000..542d7bde0825b --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleTextTest.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8262031 + * @summary Test implementation of NSAccessibilityNavigableStaticTest and NSAccessibilityStaticText protocols peer + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleTextTest + * @requires (os.family == "windows" | os.family == "mac") + */ + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.JTextPane; +import javax.swing.SwingUtilities; + +import java.awt.FlowLayout; +import java.awt.event.KeyEvent; +import java.awt.event.KeyAdapter; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class AccessibleTextTest extends AccessibleComponentTest { + @Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + private void createSimpleLabel() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JLabel.\n\n" + + "Turn screen reader on.\n" + + "On MacOS, use the VO navigation keys to read the label text;\n" + + "ON Windows with JAWS, use window virtualization (insert+alt+w and arrows) to read the label text;\n" + + "ON Windows with NVDA, use the browse cursor (insert+num4 or insert+num6) to read the label text;\n\n" + + "If you can hear text from label tab further and press PASS, otherwise press FAIL."; + + JLabel label = new JLabel("this is a label"); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(label); + exceptionString = "Simple label test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createOneLineTexField() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTextField.\n\n" + + "Turn screen reader on and press Tab to move to the text field and type some characters.\n\n" + + "If you can hear input results according to your screen reader settings, tab further and press PASS, otherwise press FAIL."; + + JTextField textField = new JTextField("some text to edit"); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textField); + exceptionString = "Simple text field test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createPassField() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JPasswordField.\n\n" + + "Turn screen reader on and press Tab to move to the password field and type some characters.\n\n" + + "If you can hear sounds specific to your screen reader when interacting with password fields, tab further and press PASS, otherwise press FAIL."; + + JPasswordField passwordField = new JPasswordField("12345678"); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(passwordField); + exceptionString = "Simple passfield field test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createNamedTextField() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of named JTextField.\n\n" + + "Turn screen reader on and press Tab to move to the text field.\n\n" + + "If you can hear in addition to the fact that this text field is also the names of these fields, tab further and press PASS, otherwise press FAIL."; + + JTextField textField = new JTextField("some text 1"); + textField.getAccessibleContext().setAccessibleName("This is the first text field:"); + + JLabel label = new JLabel("This is the second text field:"); + JTextField secondTextField = new JTextField("some text 2"); + label.setLabelFor(secondTextField); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textField); + panel.add(label); + panel.add(secondTextField); + exceptionString = "Named text field test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createTextArea() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTextArea.\n\n" + + "Turn screen reader on and press the arrow keys.\n\n" + + "If you can hear this instructions, tab further and press PASS, otherwise press FAIL."; + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + exceptionString = "Simple text area test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createEditableTextArea() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of editable JTextArea.\n\n" + + "Turn screen reader on and press Tab to move to the text area and type some characters.\n\n" + + "If you can hear input results according to your screen reader settings, tab further and press PASS, otherwise press FAIL."; + + JTextArea textArea = new JTextArea("some text to edit"); + JLabel label = new JLabel(textArea.getText().length() + " chars"); + label.setLabelFor(textArea); + textArea.setEditable(true); + textArea.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + label.setText(String.valueOf(textArea.getText().length()) + " chars"); + } + }); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textArea); + panel.add(label); + exceptionString = "Editable text area test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createTextPane() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of text in JTextPane.\n\n" + + "Turn screen reader on and press Tab to move to the text pane and press the arrow keys.\n\n" + + "If you can hear text, tab further and press PASS, otherwise press FAIL."; + + String str = "Line 1\nLine 2\nLine 3"; + JTextPane textPane = new JTextPane(); + textPane.setEditable(false); + textPane.setText(str); + JTextArea textArea = new JTextArea(); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textPane); + exceptionString = "Simple text pane test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createHTMLTextPane() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of html text in JTextPane.\n\n" + + "Turn screen reader on and press Tab to move to the text pane and press the arrow keys.\n\n" + + "If you can hear text, tab further and press PASS, otherwise press FAIL."; + + String str = "

Header

  • Item 1
  • Item 2
  • Item 3
"; + JTextPane textPane = new JTextPane(); + textPane.setEditable(false); + textPane.setContentType("text/html"); + textPane.setText(str); + JTextArea textArea = new JTextArea(); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textPane); + exceptionString = "HTML text pane test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createEditableTextPane() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of editable JTextPane.\n\n" + + "Turn screen reader on and press Tab to move to the text pane and type some characters.\n\n" + + "If you can hear input results according to your screen reader settings, tab further and press PASS, otherwise press FAIL."; + + JTextPane textPane = new JTextPane(); + textPane.setText("some text to edit"); + JLabel label = new JLabel(textPane.getText().length() + " chars"); + label.setLabelFor(textPane); + textPane.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + label.setText(String.valueOf(textPane.getText().length()) + " chars"); + } + }); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textPane); + panel.add(label); + exceptionString = "Editable text pane test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + public static void main(String[] args) throws Exception { + AccessibleTextTest test = new AccessibleTextTest(); + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createSimpleLabel); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createOneLineTexField); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createPassField); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createNamedTextField); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createTextArea); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createEditableTextArea); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createTextPane); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createHTMLTextPane); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createEditableTextPane); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + } +}