1 /* 2 * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.java.accessibility.util; 27 28 import java.util.*; 29 import java.beans.*; 30 import java.awt.*; 31 import java.awt.event.*; 32 import javax.accessibility.*; 33 34 /** 35 * <P>{@code AccessibilityEventMonitor} implements a PropertyChange listener 36 * on every UI object that implements interface {@code Accessible} in the Java 37 * Virtual Machine. The events captured by these listeners are made available 38 * through listeners supported by {@code AccessibilityEventMonitor}. 39 * With this, all the individual events on each of the UI object 40 * instances are funneled into one set of PropertyChange listeners. 41 * <p>This class depends upon {@link EventQueueMonitor}, which provides the base 42 * level support for capturing the top-level containers as they are created. 43 * 44 */ 45 46 public class AccessibilityEventMonitor { 47 48 // listeners 49 /** 50 * The current list of registered {@link java.beans.PropertyChangeListener 51 * PropertyChangeListener} classes. 52 * 53 * @see #addPropertyChangeListener 54 * @see #removePropertyChangeListener 55 */ 56 static protected final AccessibilityListenerList listenerList = 57 new AccessibilityListenerList(); 58 59 60 /** 61 * The actual listener that is installed on the component instances. 62 * This listener calls the other registered listeners when an event 63 * occurs. By doing things this way, the actual number of listeners 64 * installed on a component instance is drastically reduced. 65 */ 66 static private final AccessibilityEventListener accessibilityListener = 67 new AccessibilityEventListener(); 68 69 /** 70 * Adds the specified listener to receive all PropertyChange events on 71 * each UI object instance in the Java Virtual Machine as they occur. 72 * <P>Note: This listener is automatically added to all component 73 * instances created after this method is called. In addition, it 74 * is only added to UI object instances that support this listener type. 75 * 76 * @param l the listener to add 77 * 78 * @see #removePropertyChangeListener 79 */ 80 static public void addPropertyChangeListener(PropertyChangeListener l) { 81 if (listenerList.getListenerCount(PropertyChangeListener.class) == 0) { 82 accessibilityListener.installListeners(); 83 } 84 listenerList.add(PropertyChangeListener.class, l); 85 } 86 87 /** 88 * Removes the specified listener so it no longer receives PropertyChange 89 * events when they occur. 90 * @see #addPropertyChangeListener 91 * @param l the listener to remove 92 */ 93 static public void removePropertyChangeListener(PropertyChangeListener l) { 94 listenerList.remove(PropertyChangeListener.class, l); 95 if (listenerList.getListenerCount(PropertyChangeListener.class) == 0) { 96 accessibilityListener.removeListeners(); 97 } 98 } 99 100 101 /** 102 * AccessibilityEventListener is the class that does all the work for 103 * AccessibilityEventMonitor. It is not intended for use by any other 104 * class except AccessibilityEventMonitor. 105 * 106 */ 107 108 static class AccessibilityEventListener implements TopLevelWindowListener, 109 PropertyChangeListener { 110 111 /** 112 * Create a new instance of this class and install it on each component 113 * instance in the virtual machine that supports any of the currently 114 * registered listeners in AccessibilityEventMonitor. Also registers 115 * itself as a TopLevelWindowListener with EventQueueMonitor so it can 116 * automatically add new listeners to new components. 117 * @see EventQueueMonitor 118 * @see AccessibilityEventMonitor 119 */ 120 public AccessibilityEventListener() { 121 EventQueueMonitor.addTopLevelWindowListener(this); 122 } 123 124 /** 125 * Installs PropertyChange listeners on all Accessible objects based 126 * upon the current topLevelWindows cached by EventQueueMonitor. 127 * @see EventQueueMonitor 128 * @see AWTEventMonitor 129 */ 130 protected void installListeners() { 131 Window topLevelWindows[] = EventQueueMonitor.getTopLevelWindows(); 132 if (topLevelWindows != null) { 133 for (int i = 0; i < topLevelWindows.length; i++) { 134 if (topLevelWindows[i] instanceof Accessible) { 135 installListeners((Accessible) topLevelWindows[i]); 136 } 137 } 138 } 139 } 140 141 /** 142 * Installs PropertyChange listeners to the Accessible object, and it's 143 * children (so long as the object isn't of TRANSIENT state). 144 * @param a the Accessible object to add listeners to 145 */ 146 protected void installListeners(Accessible a) { 147 installListeners(a.getAccessibleContext()); 148 } 149 150 /** 151 * Installs PropertyChange listeners to the AccessibleContext object, 152 * and it's * children (so long as the object isn't of TRANSIENT state). 153 * @param a the Accessible object to add listeners to 154 */ 155 private void installListeners(AccessibleContext ac) { 156 157 if (ac != null) { 158 AccessibleStateSet states = ac.getAccessibleStateSet(); 159 if (!states.contains(AccessibleState.TRANSIENT)) { 160 ac.addPropertyChangeListener(this); 161 /* 162 * Don't add listeners to transient children. Components 163 * with transient children should return an AccessibleStateSet 164 * containing AccessibleState.MANAGES_DESCENDANTS. Components 165 * may not explicitly return the MANAGES_DESCENDANTS state. 166 * In this case, don't add listeners to the children of 167 * lists, tables and trees. 168 */ 169 AccessibleStateSet set = ac.getAccessibleStateSet(); 170 if (set.contains(_AccessibleState.MANAGES_DESCENDANTS)) { 171 return; 172 } 173 AccessibleRole role = ac.getAccessibleRole(); 174 if (role == AccessibleRole.LIST || 175 role == AccessibleRole.TREE) { 176 return; 177 } 178 if (role == AccessibleRole.TABLE) { 179 // handle Oracle tables containing tables 180 Accessible child = ac.getAccessibleChild(0); 181 if (child != null) { 182 AccessibleContext ac2 = child.getAccessibleContext(); 183 if (ac2 != null) { 184 role = ac2.getAccessibleRole(); 185 if (role != null && role != AccessibleRole.TABLE) { 186 return; 187 } 188 } 189 } 190 } 191 int count = ac.getAccessibleChildrenCount(); 192 for (int i = 0; i < count; i++) { 193 Accessible child = ac.getAccessibleChild(i); 194 if (child != null) { 195 installListeners(child); 196 } 197 } 198 } 199 } 200 } 201 202 /** 203 * Removes PropertyChange listeners on all Accessible objects based 204 * upon the topLevelWindows cached by EventQueueMonitor. 205 * @param eventID the event ID 206 * @see EventID 207 */ 208 protected void removeListeners() { 209 Window topLevelWindows[] = EventQueueMonitor.getTopLevelWindows(); 210 if (topLevelWindows != null) { 211 for (int i = 0; i < topLevelWindows.length; i++) { 212 if (topLevelWindows[i] instanceof Accessible) { 213 removeListeners((Accessible) topLevelWindows[i]); 214 } 215 } 216 } 217 } 218 219 /** 220 * Removes PropertyChange listeners for the given Accessible object, 221 * it's children (so long as the object isn't of TRANSIENT state). 222 * @param a the Accessible object to remove listeners from 223 */ 224 protected void removeListeners(Accessible a) { 225 removeListeners(a.getAccessibleContext()); 226 } 227 228 /** 229 * Removes PropertyChange listeners for the given AccessibleContext 230 * object, it's children (so long as the object isn't of TRANSIENT 231 * state). 232 * @param a the Accessible object to remove listeners from 233 */ 234 private void removeListeners(AccessibleContext ac) { 235 236 237 if (ac != null) { 238 // Listeners are not added to transient components. 239 AccessibleStateSet states = ac.getAccessibleStateSet(); 240 if (!states.contains(AccessibleState.TRANSIENT)) { 241 ac.removePropertyChangeListener(this); 242 /* 243 * Listeners are not added to transient children. Components 244 * with transient children should return an AccessibleStateSet 245 * containing AccessibleState.MANAGES_DESCENDANTS. Components 246 * may not explicitly return the MANAGES_DESCENDANTS state. 247 * In this case, don't remove listeners from the children of 248 * lists, tables and trees. 249 */ 250 if (states.contains(_AccessibleState.MANAGES_DESCENDANTS)) { 251 return; 252 } 253 AccessibleRole role = ac.getAccessibleRole(); 254 if (role == AccessibleRole.LIST || 255 role == AccessibleRole.TABLE || 256 role == AccessibleRole.TREE) { 257 return; 258 } 259 int count = ac.getAccessibleChildrenCount(); 260 for (int i = 0; i < count; i++) { 261 Accessible child = ac.getAccessibleChild(i); 262 if (child != null) { 263 removeListeners(child); 264 } 265 } 266 } 267 } 268 } 269 270 /********************************************************************/ 271 /* */ 272 /* Listener Interface Methods */ 273 /* */ 274 /********************************************************************/ 275 276 /* TopLevelWindow Methods ***************************************/ 277 278 /** 279 * Called when top level window is created. 280 * @see EventQueueMonitor 281 * @see EventQueueMonitor#addTopLevelWindowListener 282 */ 283 public void topLevelWindowCreated(Window w) { 284 if (w instanceof Accessible) { 285 installListeners((Accessible) w); 286 } 287 } 288 289 /** 290 * Called when top level window is destroyed. 291 * @see EventQueueMonitor 292 * @see EventQueueMonitor#addTopLevelWindowListener 293 */ 294 public void topLevelWindowDestroyed(Window w) { 295 if (w instanceof Accessible) { 296 removeListeners((Accessible) w); 297 } 298 } 299 300 301 /* PropertyChangeListener Methods **************************************/ 302 303 public void propertyChange(PropertyChangeEvent e) { 304 // propogate the event 305 Object[] listeners = 306 AccessibilityEventMonitor.listenerList.getListenerList(); 307 for (int i = listeners.length-2; i>=0; i-=2) { 308 if (listeners[i]==PropertyChangeListener.class) { 309 ((PropertyChangeListener)listeners[i+1]).propertyChange(e); 310 } 311 } 312 313 // handle childbirth/death 314 String name = e.getPropertyName(); 315 if (name.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) { 316 Object oldValue = e.getOldValue(); 317 Object newValue = e.getNewValue(); 318 319 if ((oldValue == null) ^ (newValue == null)) { // one null, not both 320 if (oldValue != null) { 321 // this Accessible is a child that's going away 322 if (oldValue instanceof Accessible) { 323 Accessible a = (Accessible) oldValue; 324 removeListeners(a.getAccessibleContext()); 325 } else if (oldValue instanceof AccessibleContext) { 326 removeListeners((AccessibleContext) oldValue); 327 } 328 } else if (newValue != null) { 329 // this Accessible is a child was just born 330 if (newValue instanceof Accessible) { 331 Accessible a = (Accessible) newValue; 332 installListeners(a.getAccessibleContext()); 333 } else if (newValue instanceof AccessibleContext) { 334 installListeners((AccessibleContext) newValue); 335 } 336 } 337 } else { 338 System.out.println("ERROR in usage of PropertyChangeEvents for: " + e.toString()); 339 } 340 } 341 } 342 } 343 } 344 345 /* 346 * workaround for no public AccessibleState constructor 347 */ 348 class _AccessibleState extends AccessibleState { 349 /** 350 * Indicates this object is responsible for managing its 351 * subcomponents. This is typically used for trees and tables 352 * that have a large number of subcomponents and where the 353 * objects are created only when needed and otherwise remain virtual. 354 * The application should not manage the subcomponents directly. 355 */ 356 public static final _AccessibleState MANAGES_DESCENDANTS 357 = new _AccessibleState ("managesDescendants"); 358 359 /** 360 * Creates a new AccessibleState using the given locale independent key. 361 * This should not be a public method. Instead, it is used to create 362 * the constants in this file to make it a strongly typed enumeration. 363 * Subclasses of this class should enforce similar policy. 364 * <p> 365 * The key String should be a locale independent key for the state. 366 * It is not intended to be used as the actual String to display 367 * to the user. To get the localized string, use toDisplayString. 368 * 369 * @param key the locale independent name of the state. 370 * @see AccessibleBundle#toDisplayString 371 */ 372 protected _AccessibleState(String key) { 373 super(key); 374 } 375 }