8160893: [macosx] JMenuItems in JPopupMenu are not accessible
Post events for MenuOpened/Closed/ItemSelected Reviewed-by: ant, alexsch
This commit is contained in:
parent
6e147fc49f
commit
27ca3765bb
@ -29,11 +29,9 @@ import sun.lwawt.LWWindowPeer;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.beans.*;
|
import java.beans.*;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import sun.awt.AWTAccessor;
|
|
||||||
|
|
||||||
import javax.accessibility.*;
|
import javax.accessibility.*;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@ -73,9 +71,21 @@ class CAccessibility implements PropertyChangeListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void propertyChange(final PropertyChangeEvent evt) {
|
public void propertyChange(final PropertyChangeEvent evt) {
|
||||||
if (evt.getNewValue() == null) return;
|
Object newValue = evt.getNewValue();
|
||||||
|
if (newValue == null) return;
|
||||||
|
// Don't post focus on things that don't matter, i.e. alert, colorchooser,
|
||||||
|
// desktoppane, dialog, directorypane, filechooser, filler, fontchoose,
|
||||||
|
// frame, glasspane, layeredpane, optionpane, panel, rootpane, separator,
|
||||||
|
// tooltip, viewport, window.
|
||||||
|
// List taken from initializeRoles() in JavaComponentUtilities.m.
|
||||||
|
if (newValue instanceof Accessible) {
|
||||||
|
AccessibleContext nvAC = ((Accessible) newValue).getAccessibleContext();
|
||||||
|
AccessibleRole nvRole = nvAC.getAccessibleRole();
|
||||||
|
if (!ignoredRoles.contains(roleKey(nvRole))) {
|
||||||
focusChanged();
|
focusChanged();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private native void focusChanged();
|
private native void focusChanged();
|
||||||
|
|
||||||
@ -683,9 +693,15 @@ class CAccessibility implements PropertyChangeListener {
|
|||||||
if (context == null) continue;
|
if (context == null) continue;
|
||||||
|
|
||||||
if (whichChildren == JAVA_AX_VISIBLE_CHILDREN) {
|
if (whichChildren == JAVA_AX_VISIBLE_CHILDREN) {
|
||||||
if (!context.getAccessibleComponent().isVisible()) continue;
|
AccessibleComponent acomp = context.getAccessibleComponent();
|
||||||
|
if (acomp == null || !acomp.isVisible()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
} else if (whichChildren == JAVA_AX_SELECTED_CHILDREN) {
|
} else if (whichChildren == JAVA_AX_SELECTED_CHILDREN) {
|
||||||
if (!ac.getAccessibleSelection().isAccessibleChildSelected(i)) continue;
|
AccessibleSelection sel = ac.getAccessibleSelection();
|
||||||
|
if (sel == null || !sel.isAccessibleChildSelected(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allowIgnored) {
|
if (!allowIgnored) {
|
||||||
|
@ -39,7 +39,10 @@ import javax.swing.event.ChangeListener;
|
|||||||
import static javax.accessibility.AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY;
|
import static javax.accessibility.AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY;
|
||||||
import static javax.accessibility.AccessibleContext.ACCESSIBLE_CARET_PROPERTY;
|
import static javax.accessibility.AccessibleContext.ACCESSIBLE_CARET_PROPERTY;
|
||||||
import static javax.accessibility.AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY;
|
import static javax.accessibility.AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY;
|
||||||
|
import static javax.accessibility.AccessibleContext.ACCESSIBLE_STATE_PROPERTY;
|
||||||
import static javax.accessibility.AccessibleContext.ACCESSIBLE_TEXT_PROPERTY;
|
import static javax.accessibility.AccessibleContext.ACCESSIBLE_TEXT_PROPERTY;
|
||||||
|
import javax.accessibility.AccessibleRole;
|
||||||
|
import javax.accessibility.AccessibleState;
|
||||||
import sun.awt.AWTAccessor;
|
import sun.awt.AWTAccessor;
|
||||||
|
|
||||||
|
|
||||||
@ -63,6 +66,9 @@ class CAccessible extends CFRetainedResource implements Accessible {
|
|||||||
private static native void valueChanged(long ptr);
|
private static native void valueChanged(long ptr);
|
||||||
private static native void selectedTextChanged(long ptr);
|
private static native void selectedTextChanged(long ptr);
|
||||||
private static native void selectionChanged(long ptr);
|
private static native void selectionChanged(long ptr);
|
||||||
|
private static native void menuOpened(long ptr);
|
||||||
|
private static native void menuClosed(long ptr);
|
||||||
|
private static native void menuItemSelected(long ptr);
|
||||||
|
|
||||||
private Accessible accessible;
|
private Accessible accessible;
|
||||||
|
|
||||||
@ -111,6 +117,8 @@ class CAccessible extends CFRetainedResource implements Accessible {
|
|||||||
public void propertyChange(PropertyChangeEvent e) {
|
public void propertyChange(PropertyChangeEvent e) {
|
||||||
String name = e.getPropertyName();
|
String name = e.getPropertyName();
|
||||||
if ( ptr != 0 ) {
|
if ( ptr != 0 ) {
|
||||||
|
Object newValue = e.getNewValue();
|
||||||
|
Object oldValue = e.getOldValue();
|
||||||
if (name.compareTo(ACCESSIBLE_CARET_PROPERTY) == 0) {
|
if (name.compareTo(ACCESSIBLE_CARET_PROPERTY) == 0) {
|
||||||
selectedTextChanged(ptr);
|
selectedTextChanged(ptr);
|
||||||
} else if (name.compareTo(ACCESSIBLE_TEXT_PROPERTY) == 0 ) {
|
} else if (name.compareTo(ACCESSIBLE_TEXT_PROPERTY) == 0 ) {
|
||||||
@ -118,9 +126,36 @@ class CAccessible extends CFRetainedResource implements Accessible {
|
|||||||
} else if (name.compareTo(ACCESSIBLE_SELECTION_PROPERTY) == 0 ) {
|
} else if (name.compareTo(ACCESSIBLE_SELECTION_PROPERTY) == 0 ) {
|
||||||
selectionChanged(ptr);
|
selectionChanged(ptr);
|
||||||
} else if (name.compareTo(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0 ) {
|
} else if (name.compareTo(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0 ) {
|
||||||
Object nv = e.getNewValue();
|
if (newValue instanceof AccessibleContext) {
|
||||||
if (nv instanceof AccessibleContext) {
|
activeDescendant = (AccessibleContext)newValue;
|
||||||
activeDescendant = (AccessibleContext)nv;
|
}
|
||||||
|
} else if (name.compareTo(ACCESSIBLE_STATE_PROPERTY) == 0) {
|
||||||
|
AccessibleContext thisAC = accessible.getAccessibleContext();
|
||||||
|
AccessibleRole thisRole = thisAC.getAccessibleRole();
|
||||||
|
Accessible parentAccessible = thisAC.getAccessibleParent();
|
||||||
|
AccessibleRole parentRole = null;
|
||||||
|
if (parentAccessible != null) {
|
||||||
|
parentRole = parentAccessible.getAccessibleContext().getAccessibleRole();
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
// for JPopupMenus, not for combobox menus.
|
||||||
|
if (parentRole != AccessibleRole.COMBO_BOX) {
|
||||||
|
if (thisRole == AccessibleRole.POPUP_MENU) {
|
||||||
|
if ( newValue != null &&
|
||||||
|
((AccessibleState)newValue) == AccessibleState.VISIBLE ) {
|
||||||
|
menuOpened(ptr);
|
||||||
|
} else if ( oldValue != null &&
|
||||||
|
((AccessibleState)oldValue) == AccessibleState.VISIBLE ) {
|
||||||
|
menuClosed(ptr);
|
||||||
|
}
|
||||||
|
} else if (thisRole == AccessibleRole.MENU_ITEM) {
|
||||||
|
if ( newValue != null &&
|
||||||
|
((AccessibleState)newValue) == AccessibleState.FOCUSED ) {
|
||||||
|
menuItemSelected(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,6 @@ static JNF_CLASS_CACHE(sjc_CAccessible, "sun/lwawt/macosx/CAccessible");
|
|||||||
static JNF_MEMBER_CACHE(jf_ptr, sjc_CAccessible, "ptr", "J");
|
static JNF_MEMBER_CACHE(jf_ptr, sjc_CAccessible, "ptr", "J");
|
||||||
static JNF_STATIC_MEMBER_CACHE(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;");
|
static JNF_STATIC_MEMBER_CACHE(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;");
|
||||||
|
|
||||||
|
|
||||||
static jobject sAccessibilityClass = NULL;
|
static jobject sAccessibilityClass = NULL;
|
||||||
|
|
||||||
// sAttributeNamesForRoleCache holds the names of the attributes to which each java
|
// sAttributeNamesForRoleCache holds the names of the attributes to which each java
|
||||||
@ -213,6 +212,24 @@ static NSObject *sAttributeNamesLOCK = nil;
|
|||||||
NSAccessibilityPostNotification(self, NSAccessibilitySelectedChildrenChangedNotification);
|
NSAccessibilityPostNotification(self, NSAccessibilitySelectedChildrenChangedNotification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (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);
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)anObject
|
- (BOOL)isEqual:(id)anObject
|
||||||
{
|
{
|
||||||
if (![anObject isKindOfClass:[self class]]) return NO;
|
if (![anObject isKindOfClass:[self class]]) return NO;
|
||||||
@ -278,8 +295,7 @@ static NSObject *sAttributeNamesLOCK = nil;
|
|||||||
+ (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env {
|
+ (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env {
|
||||||
if (JNFIsInstanceOf(env, jaccessible, &sjc_CAccessible)) {
|
if (JNFIsInstanceOf(env, jaccessible, &sjc_CAccessible)) {
|
||||||
return jaccessible;
|
return jaccessible;
|
||||||
}
|
} else if (JNFIsInstanceOf(env, jaccessible, &sjc_Accessible)) {
|
||||||
else if (JNFIsInstanceOf(env, jaccessible, &sjc_Accessible)) {
|
|
||||||
return JNFCallStaticObjectMethod(env, sjm_getCAccessible, jaccessible);
|
return JNFCallStaticObjectMethod(env, sjm_getCAccessible, jaccessible);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -368,6 +384,14 @@ static NSObject *sAttributeNamesLOCK = nil;
|
|||||||
// must init freshly -alloc'd object
|
// must init freshly -alloc'd object
|
||||||
[newChild initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // must init new instance
|
[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
|
// must hard retain pointer poked into Java object
|
||||||
[newChild retain];
|
[newChild retain];
|
||||||
JNFSetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild));
|
JNFSetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild));
|
||||||
@ -634,6 +658,15 @@ static NSObject *sAttributeNamesLOCK = nil;
|
|||||||
return moreNames;
|
return moreNames;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// popupmenu's return values not selected children
|
||||||
|
if ( [javaRole isEqualToString:@"popupmenu"] &&
|
||||||
|
![[[self parent] javaRole] isEqualToString:@"combobox"] ) {
|
||||||
|
NSMutableArray *moreNames =
|
||||||
|
[[NSMutableArray alloc] initWithCapacity: [names count] + 1];
|
||||||
|
[moreNames addObjectsFromArray: names];
|
||||||
|
[moreNames addObject:NSAccessibilityValueAttribute];
|
||||||
|
return moreNames;
|
||||||
|
}
|
||||||
return names;
|
return names;
|
||||||
|
|
||||||
} // end @synchronized
|
} // end @synchronized
|
||||||
@ -707,6 +740,7 @@ static NSObject *sAttributeNamesLOCK = nil;
|
|||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)accessibilityIsChildrenAttributeSettable
|
- (BOOL)accessibilityIsChildrenAttributeSettable
|
||||||
{
|
{
|
||||||
return NO;
|
return NO;
|
||||||
@ -939,6 +973,13 @@ static NSObject *sAttributeNamesLOCK = nil;
|
|||||||
if (fNSRole == nil) {
|
if (fNSRole == nil) {
|
||||||
NSString *javaRole = [self javaRole];
|
NSString *javaRole = [self javaRole];
|
||||||
fNSRole = [sRoles objectForKey: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) {
|
if (fNSRole == nil) {
|
||||||
// this component has assigned itself a custom AccessibleRole not in the sRoles array
|
// this component has assigned itself a custom AccessibleRole not in the sRoles array
|
||||||
fNSRole = javaRole;
|
fNSRole = javaRole;
|
||||||
@ -947,6 +988,7 @@ static NSObject *sAttributeNamesLOCK = nil;
|
|||||||
}
|
}
|
||||||
return fNSRole;
|
return fNSRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)accessibilityIsRoleAttributeSettable
|
- (BOOL)accessibilityIsRoleAttributeSettable
|
||||||
{
|
{
|
||||||
return NO;
|
return NO;
|
||||||
@ -1046,8 +1088,7 @@ static NSObject *sAttributeNamesLOCK = nil;
|
|||||||
- (NSString *)accessibilitySubroleAttribute
|
- (NSString *)accessibilitySubroleAttribute
|
||||||
{
|
{
|
||||||
NSString *value = nil;
|
NSString *value = nil;
|
||||||
if ([[self javaRole] isEqualToString:@"passwordtext"])
|
if ([[self javaRole] isEqualToString:@"passwordtext"]) {
|
||||||
{
|
|
||||||
value = NSAccessibilitySecureTextFieldSubrole;
|
value = NSAccessibilitySecureTextFieldSubrole;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@ -1123,6 +1164,45 @@ static NSObject *sAttributeNamesLOCK = nil;
|
|||||||
|
|
||||||
JNIEnv* env = [ThreadUtilities getJNIEnv];
|
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 =
|
||||||
|
[JavaComponentAccessibility childrenOfParent:self
|
||||||
|
withEnv:env
|
||||||
|
withChildrenCode:JAVA_AX_ALL_CHILDREN
|
||||||
|
allowIgnored:YES];
|
||||||
|
if ([children count] > 0) {
|
||||||
|
// handle case of AXMenuItem
|
||||||
|
// need to ask menu what is selected
|
||||||
|
NSArray *selectedChildrenOfMenu =
|
||||||
|
[self accessibilitySelectedChildrenAttribute];
|
||||||
|
JavaComponentAccessibility *selectedMenuItem =
|
||||||
|
[selectedChildrenOfMenu objectAtIndex:0];
|
||||||
|
if (selectedMenuItem != nil) {
|
||||||
|
jobject itemValue =
|
||||||
|
JNFCallStaticObjectMethod( env,
|
||||||
|
sjm_getAccessibleName,
|
||||||
|
selectedMenuItem->fAccessible,
|
||||||
|
selectedMenuItem->fComponent ); // AWT_THREADING Safe (AWTRunLoop)
|
||||||
|
if (itemValue == NULL) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
NSString* itemString = JNFJavaToNSString(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
|
// 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
|
// a text value is taken care of in JavaTextAccessibility
|
||||||
|
|
||||||
@ -1343,6 +1423,54 @@ JNF_COCOA_ENTER(env);
|
|||||||
JNF_COCOA_EXIT(env);
|
JNF_COCOA_EXIT(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: sun_lwawt_macosx_CAccessible
|
||||||
|
* Method: menuOpened
|
||||||
|
* Signature: (I)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuOpened
|
||||||
|
(JNIEnv *env, jclass jklass, jlong element)
|
||||||
|
{
|
||||||
|
JNF_COCOA_ENTER(env);
|
||||||
|
[ThreadUtilities performOnMainThread:@selector(postMenuOpened)
|
||||||
|
on:(JavaComponentAccessibility *)jlong_to_ptr(element)
|
||||||
|
withObject:nil
|
||||||
|
waitUntilDone:NO];
|
||||||
|
JNF_COCOA_EXIT(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: sun_lwawt_macosx_CAccessible
|
||||||
|
* Method: menuClosed
|
||||||
|
* Signature: (I)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuClosed
|
||||||
|
(JNIEnv *env, jclass jklass, jlong element)
|
||||||
|
{
|
||||||
|
JNF_COCOA_ENTER(env);
|
||||||
|
[ThreadUtilities performOnMainThread:@selector(postMenuClosed)
|
||||||
|
on:(JavaComponentAccessibility *)jlong_to_ptr(element)
|
||||||
|
withObject:nil
|
||||||
|
waitUntilDone:NO];
|
||||||
|
JNF_COCOA_EXIT(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: sun_lwawt_macosx_CAccessible
|
||||||
|
* Method: menuItemSelected
|
||||||
|
* Signature: (I)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuItemSelected
|
||||||
|
(JNIEnv *env, jclass jklass, jlong element)
|
||||||
|
{
|
||||||
|
JNF_COCOA_ENTER(env);
|
||||||
|
[ThreadUtilities performOnMainThread:@selector(postMenuItemSelected)
|
||||||
|
on:(JavaComponentAccessibility *)jlong_to_ptr(element)
|
||||||
|
withObject:nil
|
||||||
|
waitUntilDone:NO];
|
||||||
|
JNF_COCOA_EXIT(env);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: sun_lwawt_macosx_CAccessible
|
* Class: sun_lwawt_macosx_CAccessible
|
||||||
* Method: unregisterFromCocoaAXSystem
|
* Method: unregisterFromCocoaAXSystem
|
||||||
|
Loading…
Reference in New Issue
Block a user