1 /* 2 * Copyright (c) 2002, 2015, 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 @jdk.Exported 47 public class AccessibilityEventMonitor { 48 49 // listeners 50 /** 51 * The current list of registered {@link java.beans.PropertyChangeListener 52 * PropertyChangeListener} classes. 53 * 54 * @see #addPropertyChangeListener 55 * @see #removePropertyChangeListener 56 */ 57 static protected final AccessibilityListenerList listenerList = 58 new AccessibilityListenerList(); 59 60 61 /** 62 * The actual listener that is installed on the component instances. 63 * This listener calls the other registered listeners when an event 64 * occurs. By doing things this way, the actual number of listeners 65 * installed on a component instance is drastically reduced. 66 */ 67 static protected final AccessibilityEventListener accessibilityListener = 68 new AccessibilityEventListener(); 69 70 /** 71 * Adds the specified listener to receive all PropertyChange events on 72 * each UI object instance in the Java Virtual Machine as they occur. 73 * <P>Note: This listener is automatically added to all component 74 * instances created after this method is called. In addition, it 75 * is only added to UI object instances that support this listener type. 76 * 77 * @param l the listener to add 78 * 79 * @see #removePropertyChangeListener 80 */ 81 static public void addPropertyChangeListener(PropertyChangeListener l) { 82 if (listenerList.getListenerCount(PropertyChangeListener.class) == 0) { 83 accessibilityListener.installListeners(); 84 } 85 listenerList.add(PropertyChangeListener.class, l); 86 } 87 88 /** 89 * Removes the specified listener so it no longer receives PropertyChange 90 * events when they occur. 91 * @see #addPropertyChangeListener 92 * @param l the listener to remove 93 */ 94 static public void removePropertyChangeListener(PropertyChangeListener l) { 95 listenerList.remove(PropertyChangeListener.class, l); 96 if (listenerList.getListenerCount(PropertyChangeListener.class) == 0) { 97 accessibilityListener.removeListeners(); 98 } 99 } 100 101 102 /** 103 * AccessibilityEventListener is the class that does all the work for 104 * AccessibilityEventMonitor. It is not intended for use by any other 105 * class except AccessibilityEventMonitor. 106 * 107 */ 108 109 static class AccessibilityEventListener implements TopLevelWindowListener, 110 PropertyChangeListener { 111 112 /** 113 * Create a new instance of this class and install it on each component 114 * instance in the virtual machine that supports any of the currently 115 * registered listeners in AccessibilityEventMonitor. Also registers 116 * itself as a TopLevelWindowListener with EventQueueMonitor so it can 117 * automatically add new listeners to new components. 118 * @see EventQueueMonitor 119 * @see AccessibilityEventMonitor 120 */ 121 public AccessibilityEventListener() { 122 EventQueueMonitor.addTopLevelWindowListener(this); 123 } 124 125 /** 126 * Installs PropertyChange listeners on all Accessible objects based 127 * upon the current topLevelWindows cached by EventQueueMonitor. 128 * @see EventQueueMonitor 129 * @see AWTEventMonitor 130 */ 131 protected void installListeners() { 132 Window topLevelWindows[] = EventQueueMonitor.getTopLevelWindows(); 133 if (topLevelWindows != null) { 134 for (int i = 0; i < topLevelWindows.length; i++) { 135 if (topLevelWindows[i] instanceof Accessible) { 136 installListeners((Accessible) topLevelWindows[i]); 137 } 138 } 139 } 140 } 141 142 /** 143 * Installs PropertyChange listeners to the Accessible object, and it's 144 * children (so long as the object isn't of TRANSIENT state). 145 * @param a the Accessible object to add listeners to 146 */ 147 protected void installListeners(Accessible a) { 148 installListeners(a.getAccessibleContext()); 149 } 150 151 /** 152 * Installs PropertyChange listeners to the AccessibleContext object, 153 * and it's * children (so long as the object isn't of TRANSIENT state). 154 * @param a the Accessible object to add listeners to 155 */ 156 private void installListeners(AccessibleContext ac) { 157 158 if (ac != null) { 159 AccessibleStateSet states = ac.getAccessibleStateSet(); 160 if (!states.contains(AccessibleState.TRANSIENT)) { 161 ac.addPropertyChangeListener(this); 162 /* 163 * Don't add listeners to transient children. Components 164 * with transient children should return an AccessibleStateSet 165 * containing AccessibleState.MANAGES_DESCENDANTS. Components 166 * may not explicitly return the MANAGES_DESCENDANTS state. 167 * In this case, don't add listeners to the children of 168 * lists, tables and trees. 169 */ 170 AccessibleStateSet set = ac.getAccessibleStateSet(); 171 if (set.contains(_AccessibleState.MANAGES_DESCENDANTS)) { 172 return; 173 } 174 AccessibleRole role = ac.getAccessibleRole(); 175 if (role == AccessibleRole.LIST || 176 role == AccessibleRole.TREE) { 177 return; 178 } 179 if (role == AccessibleRole.TABLE) { 180 // handle Oracle tables containing tables 181 Accessible child = ac.getAccessibleChild(0); 182 if (child != null) { 183 AccessibleContext ac2 = child.getAccessibleContext(); 184 if (ac2 != null) { 185 role = ac2.getAccessibleRole(); 186 if (role != null && role != AccessibleRole.TABLE) { 187 return; 188 } 189 } 190 } 191 } 192 int count = ac.getAccessibleChildrenCount(); 193 for (int i = 0; i < count; i++) { 194 Accessible child = ac.getAccessibleChild(i); 195 if (child != null) { 196 installListeners(child); 197 } 198 } 199 } 200 } 201 } 202 203 /** 204 * Removes PropertyChange listeners on all Accessible objects based 205 * upon the topLevelWindows cached by EventQueueMonitor. 206 * @param eventID the event ID 207 * @see EventID 208 */ 209 protected void removeListeners() { 210 Window topLevelWindows[] = EventQueueMonitor.getTopLevelWindows(); 211 if (topLevelWindows != null) { 212 for (int i = 0; i < topLevelWindows.length; i++) { 213 if (topLevelWindows[i] instanceof Accessible) { 214 removeListeners((Accessible) topLevelWindows[i]); 215 } 216 } 217 } 218 } 219 220 /** 221 * Removes PropertyChange listeners for the given Accessible object, 222 * it's children (so long as the object isn't of TRANSIENT state). 223 * @param a the Accessible object to remove listeners from 224 */ 225 protected void removeListeners(Accessible a) { 226 removeListeners(a.getAccessibleContext()); 227 } 228 229 /** 230 * Removes PropertyChange listeners for the given AccessibleContext 231 * object, it's children (so long as the object isn't of TRANSIENT 232 * state). 233 * @param a the Accessible object to remove listeners from 234 */ 235 private void removeListeners(AccessibleContext ac) { 236 237 238 if (ac != null) { 239 // Listeners are not added to transient components. 240 AccessibleStateSet states = ac.getAccessibleStateSet(); 241 if (!states.contains(AccessibleState.TRANSIENT)) { 242 ac.removePropertyChangeListener(this); 243 /* 244 * Listeners are not added to transient children. Components 245 * with transient children should return an AccessibleStateSet 246 * containing AccessibleState.MANAGES_DESCENDANTS. Components 247 * may not explicitly return the MANAGES_DESCENDANTS state. 248 * In this case, don't remove listeners from the children of 249 * lists, tables and trees. 250 */ 251 if (states.contains(_AccessibleState.MANAGES_DESCENDANTS)) { 252 return; 253 } 254 AccessibleRole role = ac.getAccessibleRole(); 255 if (role == AccessibleRole.LIST || 256 role == AccessibleRole.TABLE || 257 role == AccessibleRole.TREE) { 258 return; 259 } 260 int count = ac.getAccessibleChildrenCount(); 261 for (int i = 0; i < count; i++) { 262 Accessible child = ac.getAccessibleChild(i); 263 if (child != null) { 264 removeListeners(child); 265 } 266 } 267 } 268 } 269 } 270 271 /********************************************************************/ 272 /* */ 273 /* Listener Interface Methods */ 274 /* */ 275 /********************************************************************/ 276 277 /* TopLevelWindow Methods ***************************************/ 278 279 /** 280 * Called when top level window is created. 281 * @see EventQueueMonitor 282 * @see EventQueueMonitor#addTopLevelWindowListener 283 */ 284 public void topLevelWindowCreated(Window w) { 285 if (w instanceof Accessible) { 286 installListeners((Accessible) w); 287 } 288 } 289 290 /** 291 * Called when top level window is destroyed. 292 * @see EventQueueMonitor 293 * @see EventQueueMonitor#addTopLevelWindowListener 294 */ 295 public void topLevelWindowDestroyed(Window w) { 296 if (w instanceof Accessible) { 297 removeListeners((Accessible) w); 298 } 299 } 300 301 302 /* PropertyChangeListener Methods **************************************/ 303 304 public void propertyChange(PropertyChangeEvent e) { 305 // propogate the event 306 Object[] listeners = 307 AccessibilityEventMonitor.listenerList.getListenerList(); 308 for (int i = listeners.length-2; i>=0; i-=2) { 309 if (listeners[i]==PropertyChangeListener.class) { 310 ((PropertyChangeListener)listeners[i+1]).propertyChange(e); 311 } 312 } 313 314 // handle childbirth/death 315 String name = e.getPropertyName(); 316 if (name.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) { 317 Object oldValue = e.getOldValue(); 318 Object newValue = e.getNewValue(); 319 320 if ((oldValue == null) ^ (newValue == null)) { // one null, not both 321 if (oldValue != null) { 322 // this Accessible is a child that's going away 323 if (oldValue instanceof Accessible) { 324 Accessible a = (Accessible) oldValue; 325 removeListeners(a.getAccessibleContext()); 326 } else if (oldValue instanceof AccessibleContext) { 327 removeListeners((AccessibleContext) oldValue); 328 } 329 } else if (newValue != null) { 330 // this Accessible is a child was just born 331 if (newValue instanceof Accessible) { 332 Accessible a = (Accessible) newValue; 333 installListeners(a.getAccessibleContext()); 334 } else if (newValue instanceof AccessibleContext) { 335 installListeners((AccessibleContext) newValue); 336 } 337 } 338 } else { 339 System.out.println("ERROR in usage of PropertyChangeEvents for: " + e.toString()); 340 } 341 } 342 } 343 } 344 } 345 346 /* 347 * workaround for no public AccessibleState constructor 348 */ 349 class _AccessibleState extends AccessibleState { 350 /** 351 * Indicates this object is responsible for managing its 352 * subcomponents. This is typically used for trees and tables 353 * that have a large number of subcomponents and where the 354 * objects are created only when needed and otherwise remain virtual. 355 * The application should not manage the subcomponents directly. 356 */ 357 public static final _AccessibleState MANAGES_DESCENDANTS 358 = new _AccessibleState ("managesDescendants"); 359 360 /** 361 * Creates a new AccessibleState using the given locale independent key. 362 * This should not be a public method. Instead, it is used to create 363 * the constants in this file to make it a strongly typed enumeration. 364 * Subclasses of this class should enforce similar policy. 365 * <p> 366 * The key String should be a locale independent key for the state. 367 * It is not intended to be used as the actual String to display 368 * to the user. To get the localized string, use toDisplayString. 369 * 370 * @param key the locale independent name of the state. 371 * @see AccessibleBundle#toDisplayString 372 */ 373 protected _AccessibleState(String key) { 374 super(key); 375 } 376 }