OpenJDK / jdk9 / jdk9 / jdk
changeset 15195:233b59b7ea2f
8145207: [macosx] JList, VO can't access non-visible list items
Summary: add support for single/multi select following focus and following VO cursor
Reviewed-by: alexsch, ant
author | ptbrunet |
---|---|
date | Mon, 18 Jul 2016 15:43:30 -0500 |
parents | a54e38877443 |
children | b75e2210e090 b3a28d9722c8 |
files | src/java.desktop/macosx/classes/sun/lwawt/macosx/AccessibilityEventMonitor.java src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.m src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.h src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.h src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.m src/java.desktop/share/classes/javax/swing/JList.java |
diffstat | 11 files changed, 311 insertions(+), 306 deletions(-) [+] |
line wrap: on
line diff
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/AccessibilityEventMonitor.java Mon Jul 18 12:52:44 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,253 +0,0 @@ -/* - * Copyright (c) 2002, 2016, Oracle and/or its affiliates. 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.lwawt.macosx; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import javax.accessibility.Accessible; -import javax.accessibility.AccessibleContext; -import javax.accessibility.AccessibleRole; -import javax.accessibility.AccessibleState; -import javax.accessibility.AccessibleStateSet; -import javax.swing.event.EventListenerList; - -/** - * <P>{@code AccessibilityEventMonitor} implements a PropertyChange listener - * on every UI object that implements interface {@code Accessible} in the Java - * Virtual Machine. The events captured by these listeners are made available - * through listeners supported by {@code AccessibilityEventMonitor}. - * With this, all the individual events on each of the UI object - * instances are funneled into one set of PropertyChange listeners. - * - * This code is a subset of com.sun.java.accessibility.util.AccessibilityEventMonitor - * which resides in module jdk.accessibility. Due to modularization the code in - * this package, java.desktop, can not be dependent on code in jdk.accessibility. - */ - -class AccessibilityEventMonitor { - - /** - * The current list of registered {@link java.beans.PropertyChangeListener - * PropertyChangeListener} classes. - * - * @see #addPropertyChangeListener - */ - private static final EventListenerList listenerList = - new EventListenerList(); - - - /** - * The actual listener that is installed on the component instances. - * This listener calls the other registered listeners when an event - * occurs. By doing things this way, the actual number of listeners - * installed on a component instance is drastically reduced. - */ - private static final AccessibilityEventListener accessibilityListener = - new AccessibilityEventListener(); - - /** - * Adds the specified listener to receive all PropertyChange events on - * each UI object instance in the Java Virtual Machine as they occur. - * <P>Note: This listener is automatically added to all component - * instances created after this method is called. In addition, it - * is only added to UI object instances that support this listener type. - * - * @param l the listener to add - * @param a the Accessible object to add the PropertyChangeListener to - */ - - static void addPropertyChangeListener(PropertyChangeListener l, Accessible a) { - if (listenerList.getListenerCount(PropertyChangeListener.class) == 0) { - accessibilityListener.installListeners(a); - } - listenerList.add(PropertyChangeListener.class, l); - } - - /** - * AccessibilityEventListener is the class that does all the work for - * AccessibilityEventMonitor. It is not intended for use by any other - * class except AccessibilityEventMonitor. - */ - - private static class AccessibilityEventListener implements PropertyChangeListener { - - /** - * Installs PropertyChange listeners to the Accessible object, and its - * children (so long as the object isn't of TRANSIENT state). - * - * @param a the Accessible object to add listeners to - */ - private void installListeners(Accessible a) { - installListeners(a.getAccessibleContext()); - } - - /** - * Installs PropertyChange listeners to the AccessibleContext object, - * and its * children (so long as the object isn't of TRANSIENT state). - * - * @param ac the AccessibleContext to add listeners to - */ - private void installListeners(AccessibleContext ac) { - - if (ac != null) { - AccessibleStateSet states = ac.getAccessibleStateSet(); - if (!states.contains(AccessibleState.TRANSIENT)) { - ac.addPropertyChangeListener(this); - /* - * Don't add listeners to transient children. Components - * with transient children should return an AccessibleStateSet - * containing AccessibleState.MANAGES_DESCENDANTS. Components - * may not explicitly return the MANAGES_DESCENDANTS state. - * In this case, don't add listeners to the children of - * lists, tables and trees. - */ - AccessibleStateSet set = ac.getAccessibleStateSet(); - if (set.contains(AccessibleState.MANAGES_DESCENDANTS)) { - return; - } - AccessibleRole role = ac.getAccessibleRole(); - if ( role == AccessibleRole.LIST || - role == AccessibleRole.TREE ) { - return; - } - if (role == AccessibleRole.TABLE) { - // handle Oracle tables containing tables - Accessible child = ac.getAccessibleChild(0); - if (child != null) { - AccessibleContext ac2 = child.getAccessibleContext(); - if (ac2 != null) { - role = ac2.getAccessibleRole(); - if (role != null && role != AccessibleRole.TABLE) { - return; - } - } - } - } - int count = ac.getAccessibleChildrenCount(); - for (int i = 0; i < count; i++) { - Accessible child = ac.getAccessibleChild(i); - if (child != null) { - installListeners(child); - } - } - } - } - } - - /** - * Removes PropertyChange listeners for the given Accessible object, - * its children (so long as the object isn't of TRANSIENT state). - * - * @param a the Accessible object to remove listeners from - */ - private void removeListeners(Accessible a) { - removeListeners(a.getAccessibleContext()); - } - - /** - * Removes PropertyChange listeners for the given AccessibleContext - * object, its children (so long as the object isn't of TRANSIENT - * state). - * - * @param a the Accessible object to remove listeners from - */ - private void removeListeners(AccessibleContext ac) { - - if (ac != null) { - // Listeners are not added to transient components. - AccessibleStateSet states = ac.getAccessibleStateSet(); - if (!states.contains(AccessibleState.TRANSIENT)) { - ac.removePropertyChangeListener(this); - /* - * Listeners are not added to transient children. Components - * with transient children should return an AccessibleStateSet - * containing AccessibleState.MANAGES_DESCENDANTS. Components - * may not explicitly return the MANAGES_DESCENDANTS state. - * In this case, don't remove listeners from the children of - * lists, tables and trees. - */ - if (states.contains(AccessibleState.MANAGES_DESCENDANTS)) { - return; - } - AccessibleRole role = ac.getAccessibleRole(); - if ( role == AccessibleRole.LIST || - role == AccessibleRole.TABLE || - role == AccessibleRole.TREE ) { - return; - } - int count = ac.getAccessibleChildrenCount(); - for (int i = 0; i < count; i++) { - Accessible child = ac.getAccessibleChild(i); - if (child != null) { - removeListeners(child); - } - } - } - } - } - - @Override - public void propertyChange(PropertyChangeEvent e) { - // propogate the event - Object[] listeners = - AccessibilityEventMonitor.listenerList.getListenerList(); - for (int i = listeners.length-2; i>=0; i-=2) { - if (listeners[i]==PropertyChangeListener.class) { - ((PropertyChangeListener)listeners[i+1]).propertyChange(e); - } - } - - // handle childbirth/death - String name = e.getPropertyName(); - if (name.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) { - Object oldValue = e.getOldValue(); - Object newValue = e.getNewValue(); - - if ((oldValue == null) ^ (newValue == null)) { // one null, not both - if (oldValue != null) { - // this Accessible is a child that's going away - if (oldValue instanceof Accessible) { - Accessible a = (Accessible) oldValue; - removeListeners(a.getAccessibleContext()); - } else if (oldValue instanceof AccessibleContext) { - removeListeners((AccessibleContext) oldValue); - } - } else if (newValue != null) { - // this Accessible is a child was just born - if (newValue instanceof Accessible) { - Accessible a = (Accessible) newValue; - installListeners(a.getAccessibleContext()); - } else if (newValue instanceof AccessibleContext) { - installListeners((AccessibleContext) newValue); - } - } - } else { - System.out.println("ERROR in usage of PropertyChangeEvents for: " + e.toString()); - } - } - } - } -}
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java Mon Jul 18 12:52:44 2016 -0700 +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java Mon Jul 18 15:43:30 2016 -0500 @@ -285,7 +285,7 @@ } public static int getAccessibleIndexInParent(final Accessible a, final Component c) { - if (a == null) return 0; + if (a == null) return -1; return invokeAndWait(new Callable<Integer>() { public Integer call() throws Exception { @@ -468,6 +468,24 @@ }, c); } + public static void requestSelection(final Accessible a, final Component c) { + if (a == null) return; + invokeLater(new Runnable() { + public void run() { + AccessibleContext ac = a.getAccessibleContext(); + if (ac == null) return; + int i = ac.getAccessibleIndexInParent(); + if (i == -1) return; + Accessible parent = ac.getAccessibleParent(); + AccessibleContext pac = parent.getAccessibleContext(); + if (pac == null) return; + AccessibleSelection as = pac.getAccessibleSelection(); + if (as == null) return; + as.addAccessibleSelection(i); + } + }, c); + } + public static Number getMaximumAccessibleValue(final Accessible a, final Component c) { if (a == null) return null; @@ -572,9 +590,57 @@ if (a == null) return null; return invokeAndWait(new Callable<Object[]>() { public Object[] call() throws Exception { - final ArrayList<Object> childrenAndRoles = new ArrayList<Object>(); + ArrayList<Object> childrenAndRoles = new ArrayList<Object>(); _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<Object> newArray = new ArrayList<Object>(); + 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.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(); }
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java Mon Jul 18 12:52:44 2016 -0700 +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java Mon Jul 18 15:43:30 2016 -0500 @@ -37,7 +37,11 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import sun.lwawt.macosx.CFRetainedResource; +import static javax.accessibility.AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY; +import static javax.accessibility.AccessibleContext.ACCESSIBLE_CARET_PROPERTY; +import static javax.accessibility.AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY; +import static javax.accessibility.AccessibleContext.ACCESSIBLE_TEXT_PROPERTY; + class CAccessible extends CFRetainedResource implements Accessible { static Field getNativeAXResourceField() { @@ -71,10 +75,13 @@ private static native void unregisterFromCocoaAXSystem(long ptr); private static native void valueChanged(long ptr); + private static native void selectedTextChanged(long ptr); private static native void selectionChanged(long ptr); private Accessible accessible; + private AccessibleContext activeDescendant; + private CAccessible(final Accessible accessible) { super(0L, true); // real pointer will be poked in by native @@ -98,9 +105,9 @@ } public void addNotificationListeners(Component c) { - AXTextChangeNotifier listener = new AXTextChangeNotifier(); if (c instanceof Accessible) { - AccessibilityEventMonitor.addPropertyChangeListener(listener, (Accessible)c); + AccessibleContext ac = ((Accessible)c).getAccessibleContext(); + ac.addPropertyChangeListener(new AXChangeNotifier()); } if (c instanceof JProgressBar) { JProgressBar pb = (JProgressBar) c; @@ -112,16 +119,23 @@ } - private class AXTextChangeNotifier implements PropertyChangeListener { + private class AXChangeNotifier implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent e) { String name = e.getPropertyName(); if ( ptr != 0 ) { - if (name.compareTo(AccessibleContext.ACCESSIBLE_CARET_PROPERTY) == 0) { + if (name.compareTo(ACCESSIBLE_CARET_PROPERTY) == 0) { + selectedTextChanged(ptr); + } else if (name.compareTo(ACCESSIBLE_TEXT_PROPERTY) == 0 ) { + valueChanged(ptr); + } else if (name.compareTo(ACCESSIBLE_SELECTION_PROPERTY) == 0 ) { selectionChanged(ptr); - } else if (name.compareTo(AccessibleContext.ACCESSIBLE_TEXT_PROPERTY) == 0 ) { - valueChanged(ptr); + } else if (name.compareTo(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0 ) { + Object nv = e.getNewValue(); + if (nv instanceof AccessibleContext) { + activeDescendant = (AccessibleContext)nv; + } } } } @@ -137,4 +151,9 @@ static Accessible getSwingAccessible(final Accessible a) { return (a instanceof CAccessible) ? ((CAccessible)a).accessible : a; } + + static AccessibleContext getActiveDescendant(final Accessible a) { + return (a instanceof CAccessible) ? ((CAccessible)a).activeDescendant : null; + } + }
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.m Mon Jul 18 12:52:44 2016 -0700 +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.m Mon Jul 18 15:43:30 2016 -0500 @@ -63,16 +63,20 @@ jobject fCompLocal = (*env)->NewLocalRef(env, fComponent); if ((*env)->IsSameObject(env, fCompLocal, NULL)) { - return @"unknown"; + return nil; } NSString *str = nil; - jobject jstr = JNFCallStaticObjectMethod(env, jm_getAccessibleActionDescription, fAccessibleAction, fIndex, fCompLocal); + jstring jstr = JNFCallStaticObjectMethod( env, + jm_getAccessibleActionDescription, + fAccessibleAction, + fIndex, + fCompLocal ); if (jstr != NULL) { - NSString *str = JNFJavaToNSString(env, jstr); // AWT_THREADING Safe (AWTRunLoopMode) + str = JNFJavaToNSString(env, jstr); // AWT_THREADING Safe (AWTRunLoopMode) (*env)->DeleteLocalRef(env, jstr); } (*env)->DeleteLocalRef(env, fCompLocal); - return str == nil ? @"unknown" : str; + return str; } - (void)perform
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h Mon Jul 18 12:52:44 2016 -0700 +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h Mon Jul 18 15:43:30 2016 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. 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 @@ -55,6 +55,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); NSPoint getAxComponentLocationOnScreen(JNIEnv *env, jobject axComponent, jobject component); jint getAxTextCharCount(JNIEnv *env, jobject axText, jobject component);
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m Mon Jul 18 12:52:44 2016 -0700 +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m Mon Jul 18 15:43:30 2016 -0500 @@ -151,6 +151,18 @@ return showing; } +BOOL isSelectable(JNIEnv *env, jobject axContext, jobject component) +{ + static JNF_STATIC_MEMBER_CACHE( jm_SELECTABLE, + sjc_AccessibleState, + "SELECTABLE", + "Ljavax/accessibility/AccessibleState;" ); + jobject axSelectableState = JNFGetStaticObjectField(env, jm_SELECTABLE); + BOOL selectable = containsAxState(env, axContext, axSelectableState, component); + (*env)->DeleteLocalRef(env, axSelectableState); + return selectable; +} + NSPoint getAxComponentLocationOnScreen(JNIEnv *env, jobject axComponent, jobject component) { static JNF_STATIC_MEMBER_CACHE(jm_getLocationOnScreen, sjc_CAccessibility, "getLocationOnScreen", "(Ljavax/accessibility/AccessibleComponent;Ljava/awt/Component;)Ljava/awt/Point;");
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.h Mon Jul 18 12:52:44 2016 -0700 +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.h Mon Jul 18 15:43:30 2016 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. 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 @@ -50,6 +50,7 @@ - (id)initWithParent:(NSObject*)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole; - (void)unregisterFromCocoaAXSystem; - (void)postValueChanged; +- (void)postSelectedTextChanged; - (void)postSelectionChanged; - (BOOL)isEqual:(id)anObject; - (BOOL)isAccessibleWithEnv:(JNIEnv *)env forAccessible:(jobject)accessible; @@ -71,6 +72,7 @@ - (NSString *)javaRole; - (BOOL)isMenu; - (BOOL)isSelected:(JNIEnv *)env; +- (BOOL)isSelectable:(JNIEnv *)env; - (BOOL)isVisible:(JNIEnv *)env; // attribute names @@ -85,6 +87,8 @@ - (NSArray *)accessibilityChildrenAttribute; - (BOOL)accessibilityIsChildrenAttributeSettable; - (NSUInteger)accessibilityIndexOfChild:(id)child; +- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute + index:(NSUInteger)index maxCount:(NSUInteger)maxCount; - (NSNumber *)accessibilityEnabledAttribute; - (BOOL)accessibilityIsEnabledAttributeSettable; - (NSNumber *)accessibilityFocusedAttribute; @@ -92,6 +96,8 @@ - (void)accessibilitySetFocusedAttribute:(id)value; - (NSString *)accessibilityHelpAttribute; - (BOOL)accessibilityIsHelpAttributeSettable; +- (NSValue *)accessibilityIndexAttribute; +- (BOOL)accessibilityIsIndexAttributeSettable; - (id)accessibilityMaxValueAttribute; - (BOOL)accessibilityIsMaxValueAttributeSettable; - (id)accessibilityMinValueAttribute; @@ -108,6 +114,9 @@ - (BOOL)accessibilityIsRoleDescriptionAttributeSettable; - (NSArray *)accessibilitySelectedChildrenAttribute; - (BOOL)accessibilityIsSelectedChildrenAttributeSettable; +- (NSNumber *)accessibilitySelectedAttribute; +- (BOOL)accessibilityIsSelectedAttributeSettable; +- (void)accessibilitySetSelectedAttribute:(id)value; - (NSValue *)accessibilitySizeAttribute; - (BOOL)accessibilityIsSizeAttributeSettable; - (NSString *)accessibilitySubroleAttribute;
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m Mon Jul 18 12:52:44 2016 -0700 +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m Mon Jul 18 15:43:30 2016 -0500 @@ -201,10 +201,16 @@ NSAccessibilityPostNotification(self, NSAccessibilityValueChangedNotification); } +- (void)postSelectedTextChanged +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, NSAccessibilitySelectedTextChangedNotification); +} + - (void)postSelectionChanged { AWT_ASSERT_APPKIT_THREAD; - NSAccessibilityPostNotification(self, NSAccessibilitySelectedTextChangedNotification); + NSAccessibilityPostNotification(self, NSAccessibilitySelectedChildrenChangedNotification); } - (BOOL)isEqual:(id)anObject @@ -225,7 +231,7 @@ { if (sAttributeNamesForRoleCache == nil) { sAttributeNamesLOCK = [[NSObject alloc] init]; - sAttributeNamesForRoleCache = [[NSMutableDictionary alloc] initWithCapacity:10]; + sAttributeNamesForRoleCache = [[NSMutableDictionary alloc] initWithCapacity:60]; } if (sRoles == nil) { @@ -281,6 +287,7 @@ + (NSArray *)childrenOfParent:(JavaComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored { + if (parent->fAccessible == NULL) return nil; jobjectArray jchildrenAndRoles = (jobjectArray)JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, parent->fAccessible, parent->fComponent, whichChildren, allowIgnored); // AWT_THREADING Safe (AWTRunLoop) if (jchildrenAndRoles == NULL) return nil; @@ -370,7 +377,7 @@ { static JNF_STATIC_MEMBER_CACHE(jm_getInitialAttributeStates, sjc_CAccessibility, "getInitialAttributeStates", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)[Z"); - NSMutableArray *attributeNames = [NSMutableArray arrayWithCapacity:10]; + NSMutableArray *attributeNames = [NSMutableArray arrayWithCapacity:20]; [attributeNames retain]; // all elements respond to parent, role, role description, window, topLevelUIElement, help @@ -449,6 +456,12 @@ // children if (attributeStatesArray[6]) { [attributeNames addObject:NSAccessibilityChildrenAttribute]; + if ([javaRole isEqualToString:@"list"]) { + [attributeNames addObject:NSAccessibilitySelectedChildrenAttribute]; + [attributeNames addObject:NSAccessibilityVisibleChildrenAttribute]; + } + // Just above, the below mentioned support has been added back in for lists. + // However, the following comments may still be useful for future fixes. // [attributeNames addObject:NSAccessibilitySelectedChildrenAttribute]; // [attributeNames addObject:NSAccessibilityVisibleChildrenAttribute]; //According to AXRoles.txt: @@ -567,6 +580,14 @@ return isChildSelected(env, ((JavaComponentAccessibility *)[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) { @@ -586,18 +607,32 @@ @synchronized(sAttributeNamesLOCK) { NSString *javaRole = [self javaRole]; - NSArray *names = (NSArray *)[sAttributeNamesForRoleCache objectForKey:javaRole]; - if (names != nil) return names; - - names = [self initializeAttributeNamesWithEnv:env]; - if (names != nil) { + NSArray *names = + (NSArray *)[sAttributeNamesForRoleCache objectForKey:javaRole]; + if (names == nil) { + names = [self initializeAttributeNamesWithEnv:env]; #ifdef JAVA_AX_DEBUG NSLog(@"Initializing: %s for %@: %@", __FUNCTION__, javaRole, names); #endif [sAttributeNamesForRoleCache setObject:names forKey:javaRole]; - return names; } - } + // The above set of attributes is immutable per role, but some objects, if + // they are the child of a list, need to add the selected and index attributes. + id myParent = [self accessibilityParentAttribute]; + if ([myParent isKindOfClass:[JavaComponentAccessibility class]]) { + NSString *parentRole = [(JavaComponentAccessibility *)myParent javaRole]; + if ([parentRole isEqualToString:@"list"]) { + NSMutableArray *moreNames = + [[NSMutableArray alloc] initWithCapacity: [names count] + 2]; + [moreNames addObjectsFromArray: names]; + [moreNames addObject:NSAccessibilitySelectedAttribute]; + [moreNames addObject:NSAccessibilityIndexAttribute]; + return moreNames; + } + } + return names; + + } // end @synchronized #ifdef JAVA_AX_DEBUG NSLog(@"Warning in %s: could not find attribute names for role: %@", __FUNCTION__, [self javaRole]); @@ -656,7 +691,10 @@ - (NSArray *)accessibilityChildrenAttribute { JNIEnv* env = [ThreadUtilities getJNIEnv]; - NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_VISIBLE_CHILDREN allowIgnored:NO]; + NSArray *children = [JavaComponentAccessibility childrenOfParent:self + withEnv:env + withChildrenCode:JAVA_AX_ALL_CHILDREN + allowIgnored:NO]; NSArray *value = nil; if ([children count] > 0) { @@ -680,7 +718,12 @@ return [super accessibilityIndexOfChild:child]; } - return JNFCallStaticIntMethod([ThreadUtilities getJNIEnv], sjm_getAccessibleIndexInParent, ((JavaComponentAccessibility *)child)->fAccessible, ((JavaComponentAccessibility *)child)->fComponent); + jint returnValue = + JNFCallStaticIntMethod( [ThreadUtilities getJNIEnv], + sjm_getAccessibleIndexInParent, + ((JavaComponentAccessibility *)child)->fAccessible, + ((JavaComponentAccessibility *)child)->fComponent ); + return (returnValue == -1) ? NSNotFound : returnValue; } // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children. @@ -754,7 +797,7 @@ jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleDescription, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) if (val == NULL) { - return @"unknown"; + return nil; } NSString* str = JNFJavaToNSString(env, val); (*env)->DeleteLocalRef(env, val); @@ -766,6 +809,18 @@ return NO; } +- (NSValue *)accessibilityIndexAttribute +{ + NSInteger index = fIndex; + NSValue *returnValue = [NSValue value:&index withObjCType:@encode(NSInteger)]; + return returnValue; +} + +- (BOOL)accessibilityIsIndexAttributeSettable +{ + return NO; +} + // Element's maximum value (id) - (id)accessibilityMaxValueAttribute { @@ -939,6 +994,33 @@ return NO; // cmcnote: actually it should be. so need to write accessibilitySetSelectedChildrenAttribute also } +- (NSNumber *)accessibilitySelectedAttribute +{ + return [NSNumber numberWithBool:[self isSelected:[ThreadUtilities getJNIEnv]]]; +} + +- (BOOL)accessibilityIsSelectedAttributeSettable +{ + if ([self isSelectable:[ThreadUtilities getJNIEnv]]) { + return YES; + } else { + return NO; + } +} + +- (void)accessibilitySetSelectedAttribute:(id)value +{ + static JNF_STATIC_MEMBER_CACHE( jm_requestSelection, + sjc_CAccessibility, + "requestSelection", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V" ); + + if ([(NSNumber*)value boolValue]) { + JNIEnv* env = [ThreadUtilities getJNIEnv]; + JNFCallStaticVoidMethod(env, jm_requestSelection, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) + } +} + // Element size (NSValue) - (NSValue *)accessibilitySizeAttribute { JNIEnv* env = [ThreadUtilities getJNIEnv]; @@ -1005,7 +1087,7 @@ jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleName, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop) if (val == NULL) { - return @"unknown"; + return nil; } NSString* str = JNFJavaToNSString(env, val); (*env)->DeleteLocalRef(env, val); @@ -1210,14 +1292,11 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessibility_focusChanged (JNIEnv *env, jobject jthis) { - JNF_COCOA_ENTER(env); [ThreadUtilities performOnMainThread:@selector(postFocusChanged:) on:[JavaComponentAccessibility class] withObject:nil waitUntilDone:NO]; JNF_COCOA_EXIT(env); } - - /* * Class: sun_lwawt_macosx_CAccessible * Method: valueChanged @@ -1233,6 +1312,22 @@ /* * Class: sun_lwawt_macosx_CAccessible + * Method: selectedTextChanged + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectedTextChanged +(JNIEnv *env, jclass jklass, jlong element) +{ +JNF_COCOA_ENTER(env); + [ThreadUtilities performOnMainThread:@selector(postSelectedTextChanged) + on:(JavaComponentAccessibility *)jlong_to_ptr(element) + withObject:nil + waitUntilDone:NO]; +JNF_COCOA_EXIT(env); +} + +/* + * Class: sun_lwawt_macosx_CAccessible * Method: selectionChanged * Signature: (I)V */ @@ -1244,7 +1339,6 @@ JNF_COCOA_EXIT(env); } - /* * Class: sun_lwawt_macosx_CAccessible * Method: unregisterFromCocoaAXSystem
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.h Mon Jul 18 12:52:44 2016 -0700 +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.h Mon Jul 18 15:43:30 2016 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. 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 @@ -60,6 +60,4 @@ - (NSValue *)accessibilityRangeForPositionAttributeForParameter:(id)parameter; - (NSValue *)accessibilityRangeForIndexAttributeForParameter:(id)parameter; -// actions -- (NSDictionary *)getActions:(JNIEnv *)env; @end
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.m Mon Jul 18 12:52:44 2016 -0700 +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.m Mon Jul 18 15:43:30 2016 -0500 @@ -427,13 +427,15 @@ return javaIntArrayToNSRangeValue(env, axTextRange); } -- (NSDictionary *)getActions:(JNIEnv *)env { - // cmcnote: this isn't correct; text can have actions. Not yet implemented. radr://3941691 - // Editable text has AXShowMenu. Textfields have AXConfirm. Static text has no actions. -#ifdef JAVA_AX_DEBUG - NSLog(@"Not yet implemented: %s\n", __FUNCTION__); -#endif - return nil; -} +/* + * - (NSDictionary *)getActions:(JNIEnv *)env { ... } + * + * In the future, possibly add support: Editable text has AXShowMenu. + * Textfields have AXConfirm. + * + * Note: JLabels (static text) in JLists have a press/click selection action + * which is currently handled in superclass JavaComponentAccessibility. + * If function is added here be sure to use [super getActions:env] for JLabels. + */ @end
--- a/src/java.desktop/share/classes/javax/swing/JList.java Mon Jul 18 12:52:44 2016 -0700 +++ b/src/java.desktop/share/classes/javax/swing/JList.java Mon Jul 18 15:43:30 2016 -0500 @@ -3058,7 +3058,7 @@ public Accessible getAccessibleAt(Point p) { int i = locationToIndex(p); if (i >= 0) { - return new AccessibleJListChild(JList.this, i); + return new ActionableAccessibleJListChild(JList.this, i); } else { return null; } @@ -3085,7 +3085,7 @@ if (i >= getModel().getSize()) { return null; } else { - return new AccessibleJListChild(JList.this, i); + return new ActionableAccessibleJListChild(JList.this, i); } } @@ -3190,7 +3190,7 @@ protected class AccessibleJListChild extends AccessibleContext implements Accessible, AccessibleComponent { private JList<E> parent = null; - private int indexInParent; + int indexInParent; private Component component = null; private AccessibleContext accessibleContext = null; private ListModel<E> listModel; @@ -3215,7 +3215,7 @@ return getComponentAtIndex(indexInParent); } - private AccessibleContext getCurrentAccessibleContext() { + AccessibleContext getCurrentAccessibleContext() { Component c = getComponentAtIndex(indexInParent); if (c instanceof Accessible) { return c.getAccessibleContext(); @@ -3381,10 +3381,6 @@ } } - public AccessibleAction getAccessibleAction() { - return getCurrentAccessibleContext().getAccessibleAction(); - } - /** * Get the AccessibleComponent associated with this object. In the * implementation of the Java Accessibility API for this class, @@ -3599,7 +3595,13 @@ public Point getLocationOnScreen() { if (parent != null) { - Point listLocation = parent.getLocationOnScreen(); + Point listLocation; + try { + listLocation = parent.getLocationOnScreen(); + } catch (IllegalComponentStateException e) { + // This can happen if the component isn't visisble + return null; + } Point componentLocation = parent.indexToLocation(indexInParent); if (componentLocation != null) { componentLocation.translate(listLocation.x, listLocation.y); @@ -3740,6 +3742,57 @@ return null; } } + } // inner class AccessibleJListChild + + private class ActionableAccessibleJListChild + extends AccessibleJListChild + implements AccessibleAction { + + ActionableAccessibleJListChild(JList<E> parent, int indexInParent) { + super(parent, indexInParent); + } + + @Override + public AccessibleAction getAccessibleAction() { + AccessibleContext ac = getCurrentAccessibleContext(); + if (ac == null) { + return null; + } else { + AccessibleAction aa = ac.getAccessibleAction(); + if (aa != null) { + return aa; + } else { + return this; + } + } + } + + @Override + public boolean doAccessibleAction(int i) { + if (i == 0) { + JList.this.setSelectedIndex(indexInParent); + return true; + } else { + return false; + } + } + + @Override + public String getAccessibleActionDescription(int i) { + if (i == 0) { + return UIManager.getString("AbstractButton.clickText"); + } else { + return null; + } + } + + @Override + public int getAccessibleActionCount() { + return 1; + } + + } // inner class ActionableAccessibleJListChild + } // inner class AccessibleJList }