377 lines
16 KiB
Java
377 lines
16 KiB
Java
/*
|
|
* Copyright (c) 2002, 2015, 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 com.sun.java.accessibility.util;
|
|
|
|
import java.util.*;
|
|
import java.beans.*;
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import javax.accessibility.*;
|
|
|
|
/**
|
|
* <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.
|
|
* <p>This class depends upon {@link EventQueueMonitor}, which provides the base
|
|
* level support for capturing the top-level containers as they are created.
|
|
*
|
|
*/
|
|
|
|
@jdk.Exported
|
|
public class AccessibilityEventMonitor {
|
|
|
|
// listeners
|
|
/**
|
|
* The current list of registered {@link java.beans.PropertyChangeListener
|
|
* PropertyChangeListener} classes.
|
|
*
|
|
* @see #addPropertyChangeListener
|
|
* @see #removePropertyChangeListener
|
|
*/
|
|
static protected final AccessibilityListenerList listenerList =
|
|
new AccessibilityListenerList();
|
|
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
static protected 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
|
|
*
|
|
* @see #removePropertyChangeListener
|
|
*/
|
|
static public void addPropertyChangeListener(PropertyChangeListener l) {
|
|
if (listenerList.getListenerCount(PropertyChangeListener.class) == 0) {
|
|
accessibilityListener.installListeners();
|
|
}
|
|
listenerList.add(PropertyChangeListener.class, l);
|
|
}
|
|
|
|
/**
|
|
* Removes the specified listener so it no longer receives PropertyChange
|
|
* events when they occur.
|
|
* @see #addPropertyChangeListener
|
|
* @param l the listener to remove
|
|
*/
|
|
static public void removePropertyChangeListener(PropertyChangeListener l) {
|
|
listenerList.remove(PropertyChangeListener.class, l);
|
|
if (listenerList.getListenerCount(PropertyChangeListener.class) == 0) {
|
|
accessibilityListener.removeListeners();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* AccessibilityEventListener is the class that does all the work for
|
|
* AccessibilityEventMonitor. It is not intended for use by any other
|
|
* class except AccessibilityEventMonitor.
|
|
*
|
|
*/
|
|
|
|
static class AccessibilityEventListener implements TopLevelWindowListener,
|
|
PropertyChangeListener {
|
|
|
|
/**
|
|
* Create a new instance of this class and install it on each component
|
|
* instance in the virtual machine that supports any of the currently
|
|
* registered listeners in AccessibilityEventMonitor. Also registers
|
|
* itself as a TopLevelWindowListener with EventQueueMonitor so it can
|
|
* automatically add new listeners to new components.
|
|
* @see EventQueueMonitor
|
|
* @see AccessibilityEventMonitor
|
|
*/
|
|
public AccessibilityEventListener() {
|
|
EventQueueMonitor.addTopLevelWindowListener(this);
|
|
}
|
|
|
|
/**
|
|
* Installs PropertyChange listeners on all Accessible objects based
|
|
* upon the current topLevelWindows cached by EventQueueMonitor.
|
|
* @see EventQueueMonitor
|
|
* @see AWTEventMonitor
|
|
*/
|
|
protected void installListeners() {
|
|
Window topLevelWindows[] = EventQueueMonitor.getTopLevelWindows();
|
|
if (topLevelWindows != null) {
|
|
for (int i = 0; i < topLevelWindows.length; i++) {
|
|
if (topLevelWindows[i] instanceof Accessible) {
|
|
installListeners((Accessible) topLevelWindows[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Installs PropertyChange listeners to the Accessible object, and it's
|
|
* children (so long as the object isn't of TRANSIENT state).
|
|
* @param a the Accessible object to add listeners to
|
|
*/
|
|
protected void installListeners(Accessible a) {
|
|
installListeners(a.getAccessibleContext());
|
|
}
|
|
|
|
/**
|
|
* Installs PropertyChange listeners to the AccessibleContext object,
|
|
* and it's * children (so long as the object isn't of TRANSIENT state).
|
|
* @param a the Accessible object 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 on all Accessible objects based
|
|
* upon the topLevelWindows cached by EventQueueMonitor.
|
|
* @param eventID the event ID
|
|
* @see EventID
|
|
*/
|
|
protected void removeListeners() {
|
|
Window topLevelWindows[] = EventQueueMonitor.getTopLevelWindows();
|
|
if (topLevelWindows != null) {
|
|
for (int i = 0; i < topLevelWindows.length; i++) {
|
|
if (topLevelWindows[i] instanceof Accessible) {
|
|
removeListeners((Accessible) topLevelWindows[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes PropertyChange listeners for the given Accessible object,
|
|
* it's children (so long as the object isn't of TRANSIENT state).
|
|
* @param a the Accessible object to remove listeners from
|
|
*/
|
|
protected void removeListeners(Accessible a) {
|
|
removeListeners(a.getAccessibleContext());
|
|
}
|
|
|
|
/**
|
|
* Removes PropertyChange listeners for the given AccessibleContext
|
|
* object, it's 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Listener Interface Methods */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
/* TopLevelWindow Methods ***************************************/
|
|
|
|
/**
|
|
* Called when top level window is created.
|
|
* @see EventQueueMonitor
|
|
* @see EventQueueMonitor#addTopLevelWindowListener
|
|
*/
|
|
public void topLevelWindowCreated(Window w) {
|
|
if (w instanceof Accessible) {
|
|
installListeners((Accessible) w);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when top level window is destroyed.
|
|
* @see EventQueueMonitor
|
|
* @see EventQueueMonitor#addTopLevelWindowListener
|
|
*/
|
|
public void topLevelWindowDestroyed(Window w) {
|
|
if (w instanceof Accessible) {
|
|
removeListeners((Accessible) w);
|
|
}
|
|
}
|
|
|
|
|
|
/* PropertyChangeListener Methods **************************************/
|
|
|
|
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());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* workaround for no public AccessibleState constructor
|
|
*/
|
|
class _AccessibleState extends AccessibleState {
|
|
/**
|
|
* Indicates this object is responsible for managing its
|
|
* subcomponents. This is typically used for trees and tables
|
|
* that have a large number of subcomponents and where the
|
|
* objects are created only when needed and otherwise remain virtual.
|
|
* The application should not manage the subcomponents directly.
|
|
*/
|
|
public static final _AccessibleState MANAGES_DESCENDANTS
|
|
= new _AccessibleState ("managesDescendants");
|
|
|
|
/**
|
|
* Creates a new AccessibleState using the given locale independent key.
|
|
* This should not be a public method. Instead, it is used to create
|
|
* the constants in this file to make it a strongly typed enumeration.
|
|
* Subclasses of this class should enforce similar policy.
|
|
* <p>
|
|
* The key String should be a locale independent key for the state.
|
|
* It is not intended to be used as the actual String to display
|
|
* to the user. To get the localized string, use toDisplayString.
|
|
*
|
|
* @param key the locale independent name of the state.
|
|
* @see AccessibleBundle#toDisplayString
|
|
*/
|
|
protected _AccessibleState(String key) {
|
|
super(key);
|
|
}
|
|
}
|