1 /* 2 * Copyright (c) 2005, 2018, 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; 27 28 import java.awt.*; 29 import java.awt.event.*; 30 import java.util.*; 31 import java.lang.*; 32 import java.lang.reflect.*; 33 34 import java.beans.*; 35 import javax.swing.*; 36 import javax.swing.event.*; 37 import javax.swing.text.*; 38 import javax.swing.tree.*; 39 import javax.swing.table.*; 40 import javax.swing.plaf.TreeUI; 41 42 import javax.accessibility.*; 43 import com.sun.java.accessibility.util.*; 44 import sun.awt.AWTAccessor; 45 import sun.awt.AppContext; 46 import sun.awt.SunToolkit; 47 48 import java.util.concurrent.Callable; 49 import java.util.concurrent.ConcurrentHashMap; 50 import java.util.concurrent.CountDownLatch; 51 52 /* 53 * Note: This class has to be public. It's loaded from the VM like this: 54 * Class.forName(atName).newInstance(); 55 */ 56 @jdk.Exported(false) 57 final public class AccessBridge extends AccessBridgeLoader { 58 59 private final String AccessBridgeVersion = 60 "AccessBridge 2.0.4"; 61 62 private static AccessBridge theAccessBridge; 63 private ObjectReferences references; 64 private EventHandler eventHandler; 65 private boolean runningOnJDK1_4 = false; 66 private boolean runningOnJDK1_5 = false; 67 68 // Maps AccessibleRoles strings to AccessibleRoles. 69 private ConcurrentHashMap<String,AccessibleRole> accessibleRoleMap = new ConcurrentHashMap<>(); 70 71 /** 72 If the object's role is in the following array getVirtualAccessibleName 73 will use the extended search algorithm. 74 */ 75 private ArrayList<AccessibleRole> extendedVirtualNameSearchRoles = new ArrayList<>(); 76 /** 77 If the role of the object's parent is in the following array 78 getVirtualAccessibleName will NOT use the extended search 79 algorithm even if the object's role is in the 80 extendedVirtualNameSearchRoles array. 81 */ 82 private ArrayList<AccessibleRole> noExtendedVirtualNameSearchParentRoles = new ArrayList<>(); 83 84 /** 85 * AccessBridge constructor 86 * 87 * Note: This constructor has to be public. It's called from the VM like this: 88 * Class.forName(atName).newInstance(); 89 */ AccessBridge()90 public AccessBridge() { 91 super(); 92 theAccessBridge = this; 93 references = new ObjectReferences(); 94 95 // initialize shutdown hook 96 Runtime runTime = Runtime.getRuntime(); 97 shutdownHook hook = new shutdownHook(); 98 runTime.addShutdownHook(new Thread(hook)); 99 100 // initialize AccessibleRole map 101 initAccessibleRoleMap(); 102 103 // determine which version of the JDK is running 104 String version = getJavaVersionProperty(); 105 debugString("[INFO]:JDK version = "+version); 106 runningOnJDK1_4 = (version.compareTo("1.4") >= 0); 107 runningOnJDK1_5 = (version.compareTo("1.5") >= 0); 108 109 // initialize the methods that map HWNDs and Java top-level 110 // windows 111 if (initHWNDcalls() == true) { 112 113 // is this a JVM we can use? 114 // install JDK 1.2 and later Swing ToolKit listener 115 EventQueueMonitor.isGUIInitialized(); 116 117 // start the Java event handler 118 eventHandler = new EventHandler(this); 119 120 // register for menu selection events 121 if (runningOnJDK1_4) { 122 MenuSelectionManager.defaultManager().addChangeListener(eventHandler); 123 } 124 125 // register as a NativeWindowHandler 126 addNativeWindowHandler(new DefaultNativeWindowHandler()); 127 128 // start in a new thread 129 Thread abthread = new Thread(new dllRunner()); 130 abthread.setDaemon(true); 131 abthread.start(); 132 debugString("[INFO]:AccessBridge started"); 133 } 134 } 135 136 /* 137 * adaptor to run the AccessBridge DLL 138 */ 139 private class dllRunner implements Runnable { run()140 public void run() { 141 runDLL(); 142 } 143 } 144 145 /* 146 * shutdown hook 147 */ 148 private class shutdownHook implements Runnable { 149 run()150 public void run() { 151 debugString("[INFO]:***** shutdownHook: shutting down..."); 152 javaShutdown(); 153 } 154 } 155 156 157 /* 158 * Initialize the hashtable that maps Strings to AccessibleRoles. 159 */ initAccessibleRoleMap()160 private void initAccessibleRoleMap() { 161 /* 162 * Initialize the AccessibleRoles map. This code uses methods in 163 * java.lang.reflect.* to build the map. 164 */ 165 try { 166 Class<?> clAccessibleRole = Class.forName ("javax.accessibility.AccessibleRole"); 167 if (null != clAccessibleRole) { 168 AccessibleRole roleUnknown = AccessibleRole.UNKNOWN; 169 Field [] fields = clAccessibleRole.getFields (); 170 int i = 0; 171 for (i = 0; i < fields.length; i ++) { 172 Field f = fields [i]; 173 if (javax.accessibility.AccessibleRole.class == f.getType ()) { 174 AccessibleRole nextRole = (AccessibleRole) (f.get (roleUnknown)); 175 String nextRoleString = nextRole.toDisplayString (Locale.US); 176 accessibleRoleMap.put (nextRoleString, nextRole); 177 } 178 } 179 } 180 } catch (Exception e) {} 181 182 /* 183 Build the extendedVirtualNameSearchRoles array list. I chose this method 184 because some of the Accessible Roles that need to be added to it are not 185 available in all versions of the J2SE that we want to support. 186 */ 187 extendedVirtualNameSearchRoles.add (AccessibleRole.COMBO_BOX); 188 try { 189 /* 190 Added in J2SE 1.4 191 */ 192 extendedVirtualNameSearchRoles.add (AccessibleRole.DATE_EDITOR); 193 } catch (NoSuchFieldError e) {} 194 extendedVirtualNameSearchRoles.add (AccessibleRole.LIST); 195 extendedVirtualNameSearchRoles.add (AccessibleRole.PASSWORD_TEXT); 196 extendedVirtualNameSearchRoles.add (AccessibleRole.SLIDER); 197 try { 198 /* 199 Added in J2SE 1.3 200 */ 201 extendedVirtualNameSearchRoles.add (AccessibleRole.SPIN_BOX); 202 } catch (NoSuchFieldError e) {} 203 extendedVirtualNameSearchRoles.add (AccessibleRole.TABLE); 204 extendedVirtualNameSearchRoles.add (AccessibleRole.TEXT); 205 extendedVirtualNameSearchRoles.add (AccessibleRole.UNKNOWN); 206 207 noExtendedVirtualNameSearchParentRoles.add (AccessibleRole.TABLE); 208 noExtendedVirtualNameSearchParentRoles.add (AccessibleRole.TOOL_BAR); 209 } 210 211 /** 212 * start the AccessBridge DLL running in its own thread 213 */ runDLL()214 private native void runDLL(); 215 216 /** 217 * debugging output (goes to OutputDebugStr()) 218 */ sendDebugString(String debugStr)219 private native void sendDebugString(String debugStr); 220 221 /** 222 * debugging output (goes to OutputDebugStr()) 223 */ debugString(String debugStr)224 private void debugString(String debugStr) { 225 sendDebugString(debugStr); 226 } 227 228 /* ===== utility methods ===== */ 229 230 /** 231 * decrement the reference to the object (called by native code) 232 */ decrementReference(Object o)233 private void decrementReference(Object o) { 234 references.decrement(o); 235 } 236 237 /** 238 * get the java.version property from the JVM 239 */ getJavaVersionProperty()240 private String getJavaVersionProperty() { 241 String s = System.getProperty("java.version"); 242 if (s != null) { 243 references.increment(s); 244 return s; 245 } 246 return null; 247 } 248 249 /** 250 * get the java.version property from the JVM 251 */ getAccessBridgeVersion()252 private String getAccessBridgeVersion() { 253 String s = new String(AccessBridgeVersion); 254 references.increment(s); 255 return s; 256 } 257 258 /* ===== HWND/Java window mapping methods ===== */ 259 260 // Java toolkit methods for mapping HWNDs to Java components 261 private Method javaGetComponentFromNativeWindowHandleMethod; 262 private Method javaGetNativeWindowHandleFromComponentMethod; 263 264 // native jawt methods for mapping HWNDs to Java components isJAWTInstalled()265 private native int isJAWTInstalled(); 266 jawtGetNativeWindowHandleFromComponent(Component comp)267 private native int jawtGetNativeWindowHandleFromComponent(Component comp); 268 jawtGetComponentFromNativeWindowHandle(int handle)269 private native Component jawtGetComponentFromNativeWindowHandle(int handle); 270 271 Toolkit toolkit; 272 273 /** 274 * map an HWND to an AWT Component 275 */ initHWNDcalls()276 private boolean initHWNDcalls() { 277 Class<?> integerParemter[] = new Class<?>[1]; 278 integerParemter[0] = Integer.TYPE; 279 Class<?> componentParemter[] = new Class<?>[1]; 280 try { 281 componentParemter[0] = Class.forName("java.awt.Component"); 282 } catch (ClassNotFoundException e) { 283 debugString("[ERROR]:Exception: " + e.toString()); 284 } 285 Object[] args = new Object[1]; 286 Component c; 287 boolean returnVal = false; 288 289 toolkit = Toolkit.getDefaultToolkit(); 290 291 if (useJAWT_DLL) { 292 returnVal = true; 293 } else { 294 // verify javaGetComponentFromNativeWindowHandle() method 295 // is present if JAWT.DLL is not installed 296 try { 297 javaGetComponentFromNativeWindowHandleMethod = 298 toolkit.getClass().getMethod( 299 "getComponentFromNativeWindowHandle", integerParemter); 300 if (javaGetComponentFromNativeWindowHandleMethod != null) { 301 try { 302 args[0] = new Integer(1); 303 c = (Component) javaGetComponentFromNativeWindowHandleMethod.invoke(toolkit, args); 304 returnVal = true; 305 } catch (InvocationTargetException e) { 306 debugString("[ERROR]:Exception: " + e.toString()); 307 } catch (IllegalAccessException e) { 308 debugString("[ERROR]:Exception: " + e.toString()); 309 } 310 } 311 } catch (NoSuchMethodException e) { 312 debugString("[ERROR]:Exception: " + e.toString()); 313 } catch (SecurityException e) { 314 debugString("[ERROR]:Exception: " + e.toString()); 315 } 316 317 // verify getComponentFromNativeWindowHandle() method 318 // is present if JAWT.DLL is not installed 319 try { 320 javaGetNativeWindowHandleFromComponentMethod = 321 toolkit.getClass().getMethod( 322 "getNativeWindowHandleFromComponent", componentParemter); 323 if (javaGetNativeWindowHandleFromComponentMethod != null) { 324 try { 325 args[0] = new Button("OK"); // need some Component... 326 Integer i = (Integer) javaGetNativeWindowHandleFromComponentMethod.invoke(toolkit, args); 327 returnVal = true; 328 } catch (InvocationTargetException e) { 329 debugString("[ERROR]:Exception: " + e.toString()); 330 } catch (IllegalAccessException e) { 331 debugString("[ERROR]:Exception: " + e.toString()); 332 } catch (Exception e) { 333 debugString("[ERROR]:Exception: " + e.toString()); 334 } 335 } 336 } catch (NoSuchMethodException e) { 337 debugString("[ERROR]:Exception: " + e.toString()); 338 } catch (SecurityException e) { 339 debugString("[ERROR]:Exception: " + e.toString()); 340 } 341 } 342 return returnVal; 343 } 344 345 // native window handler interface 346 private interface NativeWindowHandler { getAccessibleFromNativeWindowHandle(int nativeHandle)347 public Accessible getAccessibleFromNativeWindowHandle(int nativeHandle); 348 } 349 350 // hash table of native window handle to AccessibleContext mappings 351 static private ConcurrentHashMap<Integer,AccessibleContext> windowHandleToContextMap = new ConcurrentHashMap<>(); 352 353 // hash table of AccessibleContext to native window handle mappings 354 static private ConcurrentHashMap<AccessibleContext,Integer> contextToWindowHandleMap = new ConcurrentHashMap<>(); 355 356 /* 357 * adds a virtual window handler to our hash tables 358 */ registerVirtualFrame(final Accessible a, Integer nativeWindowHandle )359 static private void registerVirtualFrame(final Accessible a, 360 Integer nativeWindowHandle ) { 361 if (a != null) { 362 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 363 @Override 364 public AccessibleContext call() throws Exception { 365 return a.getAccessibleContext(); 366 } 367 }, a); 368 windowHandleToContextMap.put(nativeWindowHandle, ac); 369 contextToWindowHandleMap.put(ac, nativeWindowHandle); 370 } 371 } 372 373 /* 374 * removes a virtual window handler to our hash tables 375 */ revokeVirtualFrame(final Accessible a, Integer nativeWindowHandle )376 static private void revokeVirtualFrame(final Accessible a, 377 Integer nativeWindowHandle ) { 378 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 379 @Override 380 public AccessibleContext call() throws Exception { 381 return a.getAccessibleContext(); 382 } 383 }, a); 384 windowHandleToContextMap.remove(nativeWindowHandle); 385 contextToWindowHandleMap.remove(ac); 386 } 387 388 // vector of native window handlers 389 private static Vector<NativeWindowHandler> nativeWindowHandlers = new Vector<>(); 390 391 /* 392 * adds a native window handler to our list 393 */ addNativeWindowHandler(NativeWindowHandler handler)394 private static void addNativeWindowHandler(NativeWindowHandler handler) { 395 if (handler == null) { 396 throw new IllegalArgumentException(); 397 } 398 nativeWindowHandlers.addElement(handler); 399 } 400 401 /* 402 * removes a native window handler to our list 403 */ removeNativeWindowHandler(NativeWindowHandler handler)404 private static boolean removeNativeWindowHandler(NativeWindowHandler handler) { 405 if (handler == null) { 406 throw new IllegalArgumentException(); 407 } 408 return nativeWindowHandlers.removeElement(handler); 409 } 410 411 /** 412 * verifies that a native window handle is a Java window 413 */ isJavaWindow(int nativeHandle)414 private boolean isJavaWindow(int nativeHandle) { 415 AccessibleContext ac = getContextFromNativeWindowHandle(nativeHandle); 416 if (ac != null) { 417 saveContextToWindowHandleMapping(ac, nativeHandle); 418 return true; 419 } 420 return false; 421 } 422 423 /* 424 * saves the mapping between an AccessibleContext and a window handle 425 */ saveContextToWindowHandleMapping(AccessibleContext ac, int nativeHandle)426 private void saveContextToWindowHandleMapping(AccessibleContext ac, 427 int nativeHandle) { 428 debugString("[INFO]:saveContextToWindowHandleMapping..."); 429 if (ac == null) { 430 return; 431 } 432 if (! contextToWindowHandleMap.containsKey(ac)) { 433 debugString("[INFO]: saveContextToWindowHandleMapping: ac = "+ac+"; handle = "+nativeHandle); 434 contextToWindowHandleMap.put(ac, nativeHandle); 435 } 436 } 437 438 /** 439 * maps a native window handle to an Accessible Context 440 */ getContextFromNativeWindowHandle(int nativeHandle)441 private AccessibleContext getContextFromNativeWindowHandle(int nativeHandle) { 442 // First, look for the Accessible in our hash table of 443 // virtual window handles. 444 AccessibleContext ac = windowHandleToContextMap.get(nativeHandle); 445 if(ac!=null) { 446 saveContextToWindowHandleMapping(ac, nativeHandle); 447 return ac; 448 } 449 450 // Next, look for the native window handle in our vector 451 // of native window handles. 452 int numHandlers = nativeWindowHandlers.size(); 453 for (int i = 0; i < numHandlers; i++) { 454 NativeWindowHandler nextHandler = nativeWindowHandlers.elementAt(i); 455 final Accessible a = nextHandler.getAccessibleFromNativeWindowHandle(nativeHandle); 456 if (a != null) { 457 ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 458 @Override 459 public AccessibleContext call() throws Exception { 460 return a.getAccessibleContext(); 461 } 462 }, a); 463 saveContextToWindowHandleMapping(ac, nativeHandle); 464 return ac; 465 } 466 } 467 // Not found. 468 return null; 469 } 470 471 /** 472 * maps an AccessibleContext to a native window handle 473 * returns 0 on error 474 */ getNativeWindowHandleFromContext(AccessibleContext ac)475 private int getNativeWindowHandleFromContext(AccessibleContext ac) { 476 debugString("[INFO]: getNativeWindowHandleFromContext: ac = "+ac); 477 try { 478 return contextToWindowHandleMap.get(ac); 479 } catch (Exception ex) { 480 return 0; 481 } 482 } 483 484 private class DefaultNativeWindowHandler implements NativeWindowHandler { 485 /* 486 * returns the Accessible associated with a native window 487 */ getAccessibleFromNativeWindowHandle(int nativeHandle)488 public Accessible getAccessibleFromNativeWindowHandle(int nativeHandle) { 489 final Component c = getComponentFromNativeWindowHandle(nativeHandle); 490 if (c instanceof Accessible) { 491 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 492 @Override 493 public AccessibleContext call() throws Exception { 494 return c.getAccessibleContext(); 495 } 496 }, c); 497 saveContextToWindowHandleMapping(ac, nativeHandle); 498 return (Accessible)c; 499 } else { 500 return null; 501 } 502 } 503 504 /** 505 * map an HWND to an AWT Component 506 */ getComponentFromNativeWindowHandle(int nativeHandle)507 private Component getComponentFromNativeWindowHandle(int nativeHandle) { 508 if (useJAWT_DLL) { 509 debugString("[INFO]:*** calling jawtGetComponentFromNativeWindowHandle"); 510 return jawtGetComponentFromNativeWindowHandle(nativeHandle); 511 } else { 512 debugString("[INFO]:*** calling javaGetComponentFromNativeWindowHandle"); 513 Object[] args = new Object[1]; 514 if (javaGetComponentFromNativeWindowHandleMethod != null) { 515 try { 516 args[0] = nativeHandle; 517 Object o = javaGetComponentFromNativeWindowHandleMethod.invoke(toolkit, args); 518 if (o instanceof Accessible) { 519 final Accessible acc=(Accessible)o; 520 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 521 @Override 522 public AccessibleContext call() throws Exception { 523 return acc.getAccessibleContext(); 524 } 525 }, (Component)o); 526 saveContextToWindowHandleMapping(ac,nativeHandle); 527 } 528 return (Component)o; 529 } catch (InvocationTargetException | IllegalAccessException e) { 530 debugString("[ERROR]:Exception: " + e.toString()); 531 } 532 } 533 } 534 return null; 535 } 536 } 537 538 /** 539 * map an AWT Component to an HWND 540 */ getNativeWindowHandleFromComponent(final Component target)541 private int getNativeWindowHandleFromComponent(final Component target) { 542 if (useJAWT_DLL) { 543 debugString("[INFO]:*** calling jawtGetNativeWindowHandleFromComponent"); 544 return jawtGetNativeWindowHandleFromComponent(target); 545 } else { 546 Object[] args = new Object[1]; 547 debugString("[INFO]:*** calling javaGetNativeWindowHandleFromComponent"); 548 if (javaGetNativeWindowHandleFromComponentMethod != null) { 549 try { 550 args[0] = target; 551 Integer i = (Integer) javaGetNativeWindowHandleFromComponentMethod.invoke(toolkit, args); 552 // cache the mapping 553 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 554 @Override 555 public AccessibleContext call() throws Exception { 556 return target.getAccessibleContext(); 557 } 558 }, target); 559 contextToWindowHandleMap.put(ac, i); 560 return i.intValue(); 561 } catch (InvocationTargetException e) { 562 debugString("[ERROR]:Exception: " + e.toString()); 563 } catch (IllegalAccessException e) { 564 debugString("[ERROR]:Exception: " + e.toString()); 565 } 566 } 567 } 568 return -1; 569 } 570 571 /* ===== AccessibleContext methods =====*/ 572 573 /* 574 * returns the inner-most AccessibleContext in parent at Point(x, y) 575 */ getAccessibleContextAt(int x, int y, AccessibleContext parent)576 private AccessibleContext getAccessibleContextAt(int x, int y, 577 AccessibleContext parent) { 578 if (parent == null) { 579 return null; 580 } 581 if (windowHandleToContextMap != null && 582 windowHandleToContextMap.containsValue(getRootAccessibleContext(parent))) { 583 // Path for applications that register their top-level 584 // windows with the AccessBridge (e.g., StarOffice 6.1) 585 return getAccessibleContextAt_1(x, y, parent); 586 } else { 587 // Path for applications that do not register 588 // their top-level windows with the AccessBridge 589 // (e.g., Swing/AWT applications) 590 return getAccessibleContextAt_2(x, y, parent); 591 } 592 } 593 594 /* 595 * returns the root accessible context 596 */ getRootAccessibleContext(final AccessibleContext ac)597 private AccessibleContext getRootAccessibleContext(final AccessibleContext ac) { 598 if (ac == null) { 599 return null; 600 } 601 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 602 @Override 603 public AccessibleContext call() throws Exception { 604 Accessible parent = ac.getAccessibleParent(); 605 if (parent == null) { 606 return ac; 607 } 608 Accessible tmp = parent.getAccessibleContext().getAccessibleParent(); 609 while (tmp != null) { 610 parent = tmp; 611 tmp = parent.getAccessibleContext().getAccessibleParent(); 612 } 613 return parent.getAccessibleContext(); 614 } 615 }, ac); 616 } 617 618 /* 619 * StarOffice version that does not use the EventQueueMonitor 620 */ 621 private AccessibleContext getAccessibleContextAt_1(final int x, final int y, 622 final AccessibleContext parent) { 623 debugString("[INFO]: getAccessibleContextAt_1 called"); 624 debugString("[INFO]: -> x = " + x + " y = " + y + " parent = " + parent); 625 626 if (parent == null) return null; 627 final AccessibleComponent acmp = InvocationUtils.invokeAndWait(new Callable<AccessibleComponent>() { 628 @Override 629 public AccessibleComponent call() throws Exception { 630 return parent.getAccessibleComponent(); 631 } 632 }, parent); 633 if (acmp!=null) { 634 final Point loc = InvocationUtils.invokeAndWait(new Callable<Point>() { 635 @Override 636 public Point call() throws Exception { 637 return acmp.getLocation(); 638 } 639 }, parent); 640 final Accessible a = InvocationUtils.invokeAndWait(new Callable<Accessible>() { 641 @Override 642 public Accessible call() throws Exception { 643 return acmp.getAccessibleAt(new Point(x - loc.x, y - loc.y)); 644 } 645 }, parent); 646 if (a != null) { 647 AccessibleContext foundAC = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 648 @Override 649 public AccessibleContext call() throws Exception { 650 return a.getAccessibleContext(); 651 } 652 }, parent); 653 if (foundAC != null) { 654 if (foundAC != parent) { 655 // recurse down into the child 656 return getAccessibleContextAt_1(x - loc.x, y - loc.y, 657 foundAC); 658 } else 659 return foundAC; 660 } 661 } 662 } 663 return parent; 664 } 665 666 /* 667 * AWT/Swing version 668 */ 669 private AccessibleContext getAccessibleContextAt_2(final int x, final int y, 670 AccessibleContext parent) { 671 debugString("[INFO]: getAccessibleContextAt_2 called"); 672 debugString("[INFO]: -> x = " + x + " y = " + y + " parent = " + parent); 673 674 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 675 @Override 676 public AccessibleContext call() throws Exception { 677 Accessible a = EventQueueMonitor.getAccessibleAt(new Point(x, y)); 678 if (a != null) { 679 AccessibleContext childAC = a.getAccessibleContext(); 680 if (childAC != null) { 681 debugString("[INFO]: returning childAC = " + childAC); 682 return childAC; 683 } 684 } 685 return null; 686 } 687 }, parent); 688 } 689 690 /** 691 * returns the Accessible that has focus 692 */ 693 private AccessibleContext getAccessibleContextWithFocus() { 694 Component c = AWTEventMonitor.getComponentWithFocus(); 695 if (c != null) { 696 final Accessible a = Translator.getAccessible(c); 697 if (a != null) { 698 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 699 @Override 700 public AccessibleContext call() throws Exception { 701 return a.getAccessibleContext(); 702 } 703 }, c); 704 if (ac != null) { 705 return ac; 706 } 707 } 708 } 709 return null; 710 } 711 712 /** 713 * returns the AccessibleName from an AccessibleContext 714 */ 715 private String getAccessibleNameFromContext(final AccessibleContext ac) { 716 debugString("[INFO]: ***** ac = "+ac.getClass()); 717 if (ac != null) { 718 String s = InvocationUtils.invokeAndWait(new Callable<String>() { 719 @Override 720 public String call() throws Exception { 721 return ac.getAccessibleName(); 722 } 723 }, ac); 724 if (s != null) { 725 references.increment(s); 726 debugString("[INFO]: Returning AccessibleName from Context: " + s); 727 return s; 728 } else { 729 return null; 730 } 731 } else { 732 debugString("[INFO]: getAccessibleNameFromContext; ac = null!"); 733 return null; 734 } 735 } 736 737 /** 738 * Returns an AccessibleName for a component using an algorithm optimized 739 * for the JAWS screen reader. This method is only intended for JAWS. All 740 * other uses are entirely optional. 741 */ 742 private String getVirtualAccessibleNameFromContext(final AccessibleContext ac) { 743 if (null != ac) { 744 /* 745 Step 1: 746 ======= 747 Determine if we can obtain the Virtual Accessible Name from the 748 Accessible Name or Accessible Description of the object. 749 */ 750 String nameString = InvocationUtils.invokeAndWait(new Callable<String>() { 751 @Override 752 public String call() throws Exception { 753 return ac.getAccessibleName(); 754 } 755 }, ac); 756 if ( ( null != nameString ) && ( 0 != nameString.length () ) ) { 757 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from AccessibleContext::getAccessibleName."); 758 references.increment (nameString); 759 return nameString; 760 } 761 String descriptionString = InvocationUtils.invokeAndWait(new Callable<String>() { 762 @Override 763 public String call() throws Exception { 764 return ac.getAccessibleDescription(); 765 } 766 }, ac); 767 if ( ( null != descriptionString ) && ( 0 != descriptionString.length () ) ) { 768 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from AccessibleContext::getAccessibleDescription."); 769 references.increment (descriptionString); 770 return descriptionString; 771 } 772 773 debugString ("[WARN]: The Virtual Accessible Name was not found using AccessibleContext::getAccessibleDescription. or getAccessibleName"); 774 /* 775 Step 2: 776 ======= 777 Decide whether the extended name search algorithm should be 778 used for this object. 779 */ 780 boolean bExtendedSearch = false; 781 AccessibleRole role = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() { 782 @Override 783 public AccessibleRole call() throws Exception { 784 return ac.getAccessibleRole(); 785 } 786 }, ac); 787 AccessibleContext parentContext = null; 788 AccessibleRole parentRole = AccessibleRole.UNKNOWN; 789 790 if ( extendedVirtualNameSearchRoles.contains (role) ) { 791 parentContext = getAccessibleParentFromContext (ac); 792 if ( null != parentContext ) { 793 final AccessibleContext parentContextInnerTemp = parentContext; 794 parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() { 795 @Override 796 public AccessibleRole call() throws Exception { 797 return parentContextInnerTemp.getAccessibleRole(); 798 } 799 }, ac); 800 if ( AccessibleRole.UNKNOWN != parentRole ) { 801 bExtendedSearch = true; 802 if ( noExtendedVirtualNameSearchParentRoles.contains (parentRole) ) { 803 bExtendedSearch = false; 804 } 805 } 806 } 807 } 808 809 if (false == bExtendedSearch) { 810 debugString ("[INFO]: bk -- getVirtualAccessibleNameFromContext will not use the extended name search algorithm. role = " + ( role != null ? role.toDisplayString(Locale.US) : "null") ); 811 /* 812 Step 3: 813 ======= 814 We have determined that we should not use the extended name 815 search algorithm for this object (we must obtain the name of 816 the object from the object itself and not from neighboring 817 objects). However the object name cannot be obtained from 818 the Accessible Name or Accessible Description of the object. 819 820 Handle several special cases here that might yield a value for 821 the Virtual Accessible Name. Return null if the object does 822 not match the criteria for any of these special cases. 823 */ 824 if (AccessibleRole.LABEL == role) { 825 /* 826 Does the label support the Accessible Text Interface? 827 */ 828 final AccessibleText at = InvocationUtils.invokeAndWait(new Callable<AccessibleText>() { 829 @Override 830 public AccessibleText call() throws Exception { 831 return ac.getAccessibleText(); 832 } 833 }, ac); 834 if (null != at) { 835 int charCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 836 @Override 837 public Integer call() throws Exception { 838 return at.getCharCount(); 839 } 840 }, ac); 841 String text = getAccessibleTextRangeFromContext (ac, 0, charCount); 842 if (null != text) { 843 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the Accessible Text of the LABEL object."); 844 references.increment (text); 845 return text; 846 } 847 } 848 /* 849 Does the label support the Accessible Icon Interface? 850 */ 851 debugString ("[INFO]: bk -- Attempting to obtain the Virtual Accessible Name from the Accessible Icon information."); 852 final AccessibleIcon [] ai = InvocationUtils.invokeAndWait(new Callable<AccessibleIcon[]>() { 853 @Override 854 public AccessibleIcon[] call() throws Exception { 855 return ac.getAccessibleIcon(); 856 } 857 }, ac); 858 if ( (null != ai) && (ai.length > 0) ) { 859 String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() { 860 @Override 861 public String call() throws Exception { 862 return ai[0].getAccessibleIconDescription(); 863 } 864 }, ac); 865 if (iconDescription != null){ 866 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the LABEL object."); 867 references.increment (iconDescription); 868 return iconDescription; 869 } 870 } else { 871 parentContext = getAccessibleParentFromContext (ac); 872 if ( null != parentContext ) { 873 final AccessibleContext parentContextInnerTemp = parentContext; 874 parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() { 875 @Override 876 public AccessibleRole call() throws Exception { 877 return parentContextInnerTemp.getAccessibleRole(); 878 } 879 }, ac); 880 if ( AccessibleRole.TABLE == parentRole ) { 881 int indexInParent = InvocationUtils.invokeAndWait(new Callable<Integer>() { 882 @Override 883 public Integer call() throws Exception { 884 return ac.getAccessibleIndexInParent(); 885 } 886 }, ac); 887 final AccessibleContext acTableCell = getAccessibleChildFromContext (parentContext, indexInParent); 888 debugString ("[INFO]: bk -- Making a second attempt to obtain the Virtual Accessible Name from the Accessible Icon information for the Table Cell."); 889 if (acTableCell != null) { 890 final AccessibleIcon [] aiRet =InvocationUtils.invokeAndWait(new Callable<AccessibleIcon[]>() { 891 @Override 892 public AccessibleIcon[] call() throws Exception { 893 return acTableCell.getAccessibleIcon(); 894 } 895 }, ac); 896 if ( (null != aiRet) && (aiRet.length > 0) ) { 897 String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() { 898 public String call() { 899 return aiRet[0].getAccessibleIconDescription (); 900 } 901 }, ac); 902 if (iconDescription != null){ 903 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the Table Cell object."); 904 references.increment (iconDescription); 905 return iconDescription; 906 } 907 } 908 } 909 } 910 } 911 } 912 } else if ( (AccessibleRole.TOGGLE_BUTTON == role) || 913 (AccessibleRole.PUSH_BUTTON == role) ) { 914 /* 915 Does the button support the Accessible Icon Interface? 916 */ 917 debugString ("[INFO]: bk -- Attempting to obtain the Virtual Accessible Name from the Accessible Icon information."); 918 final AccessibleIcon [] ai = InvocationUtils.invokeAndWait(new Callable<AccessibleIcon []>() { 919 public AccessibleIcon [] call() { 920 return ac.getAccessibleIcon (); 921 } 922 }, ac); 923 if ( (null != ai) && (ai.length > 0) ) { 924 String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() { 925 public String call() { 926 return ai[0].getAccessibleIconDescription (); 927 } 928 }, ac); 929 if (iconDescription != null){ 930 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the TOGGLE_BUTTON or PUSH_BUTTON object."); 931 references.increment (iconDescription); 932 return iconDescription; 933 } 934 } 935 } else if ( AccessibleRole.CHECK_BOX == role ) { 936 /* 937 NOTE: The only case I know of in which a check box does not 938 have a name is when that check box is contained in a table. 939 940 In this case it would be appropriate to use the display string 941 of the check box object as the name (in US English the display 942 string is typically either "true" or "false"). 943 944 I am using the AccessibleValue interface to obtain the display 945 string of the check box. If the Accessible Value is 1, I am 946 returning Boolean.TRUE.toString (), If the Accessible Value is 947 0, I am returning Boolean.FALSE.toString (). If the Accessible 948 Value is some other number, I will return the display string of 949 the current numerical value of the check box. 950 */ 951 final AccessibleValue av = InvocationUtils.invokeAndWait(new Callable<AccessibleValue>() { 952 @Override 953 public AccessibleValue call() throws Exception { 954 return ac.getAccessibleValue(); 955 } 956 }, ac); 957 if ( null != av ) { 958 nameString = null; 959 Number value = InvocationUtils.invokeAndWait(new Callable<Number>() { 960 @Override 961 public Number call() throws Exception { 962 return av.getCurrentAccessibleValue(); 963 } 964 }, ac); 965 if ( null != value ) { 966 if ( 1 == value.intValue () ) { 967 nameString = Boolean.TRUE.toString (); 968 } else if ( 0 == value.intValue () ) { 969 nameString = Boolean.FALSE.toString (); 970 } else { 971 nameString = value.toString (); 972 } 973 if ( null != nameString ) { 974 references.increment (nameString); 975 return nameString; 976 } 977 } 978 } 979 } 980 return null; 981 } 982 983 /* 984 + 985 Beginning of the extended name search 986 + 987 */ 988 final AccessibleContext parentContextOuterTemp = parentContext; 989 String parentName = InvocationUtils.invokeAndWait(new Callable<String>() { 990 @Override 991 public String call() throws Exception { 992 return parentContextOuterTemp.getAccessibleName(); 993 } 994 }, ac); 995 String parentDescription = InvocationUtils.invokeAndWait(new Callable<String>() { 996 @Override 997 public String call() throws Exception { 998 return parentContextOuterTemp.getAccessibleDescription(); 999 } 1000 }, ac); 1001 1002 /* 1003 Step 4: 1004 ======= 1005 Special case for Slider Bar objects. 1006 */ 1007 if ( (AccessibleRole.SLIDER == role) && 1008 (AccessibleRole.PANEL == parentRole) && 1009 (null != parentName) ) { 1010 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the Accessible Name of the SLIDER object's parent object."); 1011 references.increment (parentName); 1012 return parentName; 1013 } 1014 1015 boolean bIsEditCombo = false; 1016 1017 AccessibleContext testContext = ac; 1018 /* 1019 Step 5: 1020 ======= 1021 Special case for Edit Combo Boxes 1022 */ 1023 if ( (AccessibleRole.TEXT == role) && 1024 (AccessibleRole.COMBO_BOX == parentRole) ) { 1025 bIsEditCombo = true; 1026 if (null != parentName) { 1027 debugString ("[INFO]: bk -- The Virtual Accessible Name for this Edit Combo box was obtained from the Accessible Name of the object's parent object."); 1028 references.increment (parentName); 1029 return parentName; 1030 } else if (null != parentDescription) { 1031 debugString ("[INFO]: bk -- The Virtual Accessible Name for this Edit Combo box was obtained from the Accessible Description of the object's parent object."); 1032 references.increment (parentDescription); 1033 return parentDescription; 1034 } 1035 testContext = parentContext; 1036 parentRole = AccessibleRole.UNKNOWN; 1037 parentContext = getAccessibleParentFromContext (testContext); 1038 if ( null != parentContext ) { 1039 final AccessibleContext parentContextInnerTemp = parentContext; 1040 parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() { 1041 @Override 1042 public AccessibleRole call() throws Exception { 1043 return parentContextInnerTemp.getAccessibleRole(); 1044 } 1045 }, ac); 1046 } 1047 } 1048 1049 /* 1050 Step 6: 1051 ======= 1052 Attempt to get the Virtual Accessible Name of the object using the 1053 Accessible Relation Set Info (the LABELED_BY Accessible Relation). 1054 */ 1055 String version = getJavaVersionProperty (); 1056 if ( (null != version) && (version.compareTo ("1.3") >= 0) ) { 1057 final AccessibleContext parentContextTempInner = parentContext; 1058 AccessibleRelationSet ars = InvocationUtils.invokeAndWait(new Callable<AccessibleRelationSet>() { 1059 @Override 1060 public AccessibleRelationSet call() throws Exception { 1061 return parentContextTempInner.getAccessibleRelationSet(); 1062 } 1063 }, ac); 1064 if ( ars != null && (ars.size () > 0) && (ars.contains (AccessibleRelation.LABELED_BY)) ) { 1065 AccessibleRelation labeledByRelation = ars.get (AccessibleRelation.LABELED_BY); 1066 if (labeledByRelation != null) { 1067 Object [] targets = labeledByRelation.getTarget (); 1068 Object o = targets [0]; 1069 if (o instanceof Accessible) { 1070 AccessibleContext labelContext = ((Accessible)o).getAccessibleContext (); 1071 if (labelContext != null) { 1072 String labelName = labelContext.getAccessibleName (); 1073 String labelDescription = labelContext.getAccessibleDescription (); 1074 if (null != labelName) { 1075 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained using the LABELED_BY AccessibleRelation -- Name Case."); 1076 references.increment (labelName); 1077 return labelName; 1078 } else if (null != labelDescription) { 1079 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained using the LABELED_BY AccessibleRelation -- Description Case."); 1080 references.increment (labelDescription); 1081 return labelDescription; 1082 } 1083 } 1084 } 1085 } 1086 } 1087 } else { 1088 debugString ("[ERROR]:bk -- This version of Java does not support AccessibleContext::getAccessibleRelationSet."); 1089 } 1090 1091 //Note: add AccessibleContext to use InvocationUtils.invokeAndWait 1092 /* 1093 Step 7: 1094 ======= 1095 Search for a label object that is positioned either just to the left 1096 or just above the object and get the Accessible Name of the Label 1097 object. 1098 */ 1099 int testIndexMax = 0; 1100 int testX = 0; 1101 int testY = 0; 1102 int testWidth = 0; 1103 int testHeight = 0; 1104 int targetX = 0; 1105 int targetY = 0; 1106 final AccessibleContext tempContext = testContext; 1107 int testIndex = InvocationUtils.invokeAndWait(new Callable<Integer>() { 1108 @Override 1109 public Integer call() throws Exception { 1110 return tempContext.getAccessibleIndexInParent(); 1111 } 1112 }, ac); 1113 if ( null != parentContext ) { 1114 final AccessibleContext parentContextInnerTemp = parentContext; 1115 testIndexMax = InvocationUtils.invokeAndWait(new Callable<Integer>() { 1116 @Override 1117 public Integer call() throws Exception { 1118 return parentContextInnerTemp.getAccessibleChildrenCount() - 1; 1119 } 1120 }, ac); 1121 } 1122 testX = getAccessibleXcoordFromContext (testContext); 1123 testY = getAccessibleYcoordFromContext (testContext); 1124 testWidth = getAccessibleWidthFromContext (testContext); 1125 testHeight = getAccessibleHeightFromContext (testContext); 1126 targetX = testX + 2; 1127 targetY = testY + 2; 1128 1129 int childIndex = testIndex - 1; 1130 /*Accessible child = null; 1131 AccessibleContext childContext = null; 1132 AccessibleRole childRole = AccessibleRole.UNKNOWN;*/ 1133 int childX = 0; 1134 int childY = 0; 1135 int childWidth = 0; 1136 int childHeight = 0; 1137 String childName = null; 1138 String childDescription = null; 1139 while (childIndex >= 0) { 1140 final int childIndexTemp = childIndex; 1141 final AccessibleContext parentContextInnerTemp = parentContext; 1142 final Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() { 1143 @Override 1144 public Accessible call() throws Exception { 1145 return parentContextInnerTemp.getAccessibleChild(childIndexTemp); 1146 } 1147 }, ac); 1148 if ( null != child ) { 1149 final AccessibleContext childContext = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 1150 @Override 1151 public AccessibleContext call() throws Exception { 1152 return child.getAccessibleContext(); 1153 } 1154 }, ac); 1155 if ( null != childContext ) { 1156 AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() { 1157 @Override 1158 public AccessibleRole call() throws Exception { 1159 return childContext.getAccessibleRole(); 1160 } 1161 }, ac); 1162 if ( AccessibleRole.LABEL == childRole ) { 1163 childX = getAccessibleXcoordFromContext (childContext); 1164 childY = getAccessibleYcoordFromContext (childContext); 1165 childWidth = getAccessibleWidthFromContext (childContext); 1166 childHeight = getAccessibleHeightFromContext (childContext); 1167 if ( (childX < testX) && 1168 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) { 1169 childName = InvocationUtils.invokeAndWait(new Callable<String>() { 1170 public String call() { 1171 return childContext.getAccessibleName (); 1172 } 1173 }, ac); 1174 if ( null != childName ) { 1175 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned to the left of the object."); 1176 references.increment (childName); 1177 return childName; 1178 } 1179 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() { 1180 public String call() { 1181 return childContext.getAccessibleDescription (); 1182 } 1183 }, ac); 1184 if ( null != childDescription ) { 1185 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned to the left of the object."); 1186 references.increment (childDescription); 1187 return childDescription; 1188 } 1189 } else if ( (childY < targetY) && 1190 ((childX <= targetX) && (targetX <= (childX + childWidth))) ) { 1191 childName = InvocationUtils.invokeAndWait(new Callable<String>() { 1192 public String call() { 1193 return childContext.getAccessibleName (); 1194 } 1195 }, ac); 1196 if ( null != childName ) { 1197 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned above the object."); 1198 references.increment (childName); 1199 return childName; 1200 } 1201 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() { 1202 public String call() { 1203 return childContext.getAccessibleDescription (); 1204 } 1205 }, ac); 1206 if ( null != childDescription ) { 1207 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned above the object."); 1208 references.increment (childDescription); 1209 return childDescription; 1210 } 1211 } 1212 } 1213 } 1214 } 1215 childIndex --; 1216 } 1217 childIndex = testIndex + 1; 1218 while (childIndex <= testIndexMax) { 1219 final int childIndexTemp = childIndex; 1220 final AccessibleContext parentContextInnerTemp = parentContext; 1221 final Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() { 1222 @Override 1223 public Accessible call() throws Exception { 1224 return parentContextInnerTemp.getAccessibleChild(childIndexTemp); 1225 } 1226 }, ac); 1227 if ( null != child ) { 1228 final AccessibleContext childContext = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 1229 @Override 1230 public AccessibleContext call() throws Exception { 1231 return child.getAccessibleContext(); 1232 } 1233 }, ac); 1234 if ( null != childContext ) { 1235 AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() { 1236 @Override 1237 public AccessibleRole call() throws Exception { 1238 return childContext.getAccessibleRole(); 1239 } 1240 }, ac); 1241 if ( AccessibleRole.LABEL == childRole ) { 1242 childX = getAccessibleXcoordFromContext (childContext); 1243 childY = getAccessibleYcoordFromContext (childContext); 1244 childWidth = getAccessibleWidthFromContext (childContext); 1245 childHeight = getAccessibleHeightFromContext (childContext); 1246 if ( (childX < testX) && 1247 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) { 1248 childName = InvocationUtils.invokeAndWait(new Callable<String>() { 1249 public String call() { 1250 return childContext.getAccessibleName (); 1251 } 1252 }, ac); 1253 if ( null != childName ) { 1254 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned to the left of the object."); 1255 references.increment (childName); 1256 return childName; 1257 } 1258 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() { 1259 public String call() { 1260 return childContext.getAccessibleDescription (); 1261 } 1262 }, ac); 1263 if ( null != childDescription ) { 1264 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned to the left of the object."); 1265 references.increment (childDescription); 1266 return childDescription; 1267 } 1268 } else if ( (childY < targetY) && 1269 ((childX <= targetX) && (targetX <= (childX + childWidth))) ) { 1270 childName = InvocationUtils.invokeAndWait(new Callable<String>() { 1271 public String call() { 1272 return childContext.getAccessibleName (); 1273 } 1274 }, ac); 1275 if ( null != childName ) { 1276 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned above the object."); 1277 references.increment (childName); 1278 return childName; 1279 } 1280 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() { 1281 public String call() { 1282 return childContext.getAccessibleDescription (); 1283 } 1284 }, ac); 1285 if ( null != childDescription ) { 1286 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned above the object."); 1287 references.increment (childDescription); 1288 return childDescription; 1289 } 1290 } 1291 } 1292 } 1293 } 1294 childIndex ++; 1295 } 1296 /* 1297 Step 8: 1298 ======= 1299 Special case for combo boxes and text objects, based on a 1300 similar special case I found in some of our internal JAWS code. 1301 1302 Search for a button object that is positioned either just to the left 1303 or just above the object and get the Accessible Name of the button 1304 object. 1305 */ 1306 if ( (AccessibleRole.TEXT == role) || 1307 (AccessibleRole.COMBO_BOX == role) || 1308 (bIsEditCombo) ) { 1309 childIndex = testIndex - 1; 1310 while (childIndex >= 0) { 1311 final int childIndexTemp = childIndex; 1312 final AccessibleContext parentContextInnerTemp = parentContext; 1313 final Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() { 1314 @Override 1315 public Accessible call() throws Exception { 1316 return parentContextInnerTemp.getAccessibleChild(childIndexTemp); 1317 } 1318 }, ac); 1319 if ( null != child ) { 1320 final AccessibleContext childContext = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 1321 @Override 1322 public AccessibleContext call() throws Exception { 1323 return child.getAccessibleContext(); 1324 } 1325 }, ac); 1326 if ( null != childContext ) { 1327 AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() { 1328 @Override 1329 public AccessibleRole call() throws Exception { 1330 return childContext.getAccessibleRole(); 1331 } 1332 }, ac); 1333 if ( ( AccessibleRole.PUSH_BUTTON == childRole ) || 1334 ( AccessibleRole.TOGGLE_BUTTON == childRole )) { 1335 childX = getAccessibleXcoordFromContext (childContext); 1336 childY = getAccessibleYcoordFromContext (childContext); 1337 childWidth = getAccessibleWidthFromContext (childContext); 1338 childHeight = getAccessibleHeightFromContext (childContext); 1339 if ( (childX < testX) && 1340 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) { 1341 childName = InvocationUtils.invokeAndWait(new Callable<String>() { 1342 public String call() { 1343 return childContext.getAccessibleName (); 1344 } 1345 }, ac); 1346 if ( null != childName ) { 1347 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object."); 1348 references.increment (childName); 1349 return childName; 1350 } 1351 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() { 1352 public String call() { 1353 return childContext.getAccessibleDescription (); 1354 } 1355 }, ac); 1356 if ( null != childDescription ) { 1357 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object."); 1358 references.increment (childDescription); 1359 return childDescription; 1360 } 1361 } 1362 } 1363 } 1364 } 1365 childIndex --; 1366 } 1367 childIndex = testIndex + 1; 1368 while (childIndex <= testIndexMax) { 1369 final int childIndexTemp = childIndex; 1370 final AccessibleContext parentContextInnerTemp = parentContext; 1371 final Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() { 1372 @Override 1373 public Accessible call() throws Exception { 1374 return parentContextInnerTemp.getAccessibleChild(childIndexTemp); 1375 } 1376 }, ac); 1377 if ( null != child ) { 1378 final AccessibleContext childContext = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 1379 @Override 1380 public AccessibleContext call() throws Exception { 1381 return child.getAccessibleContext(); 1382 } 1383 }, ac); 1384 if ( null != childContext ) { 1385 AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() { 1386 @Override 1387 public AccessibleRole call() throws Exception { 1388 return childContext.getAccessibleRole(); 1389 } 1390 }, ac); 1391 if ( ( AccessibleRole.PUSH_BUTTON == childRole ) || 1392 ( AccessibleRole.TOGGLE_BUTTON == childRole ) ) { 1393 childX = getAccessibleXcoordFromContext (childContext); 1394 childY = getAccessibleYcoordFromContext (childContext); 1395 childWidth = getAccessibleWidthFromContext (childContext); 1396 childHeight = getAccessibleHeightFromContext (childContext); 1397 if ( (childX < testX) && 1398 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) { 1399 childName = InvocationUtils.invokeAndWait(new Callable<String>() { 1400 public String call() { 1401 return childContext.getAccessibleName(); 1402 } 1403 }, ac); 1404 if ( null != childName ) { 1405 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object."); 1406 references.increment (childName); 1407 return childName; 1408 } 1409 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() { 1410 public String call() { 1411 return childContext.getAccessibleDescription (); 1412 } 1413 }, ac); 1414 if ( null != childDescription ) { 1415 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object."); 1416 references.increment (childDescription); 1417 return childDescription; 1418 } 1419 } 1420 } 1421 } 1422 } 1423 childIndex ++; 1424 } 1425 } 1426 return null; 1427 } else { 1428 debugString ("[ERROR]: AccessBridge::getVirtualAccessibleNameFromContext error - ac == null."); 1429 return null; 1430 } 1431 } 1432 1433 /** 1434 * returns the AccessibleDescription from an AccessibleContext 1435 */ 1436 private String getAccessibleDescriptionFromContext(final AccessibleContext ac) { 1437 if (ac != null) { 1438 String s = InvocationUtils.invokeAndWait(new Callable<String>() { 1439 @Override 1440 public String call() throws Exception { 1441 return ac.getAccessibleDescription(); 1442 } 1443 }, ac); 1444 if (s != null) { 1445 references.increment(s); 1446 debugString("[INFO]: Returning AccessibleDescription from Context: " + s); 1447 return s; 1448 } 1449 } else { 1450 debugString("[ERROR]: getAccessibleDescriptionFromContext; ac = null"); 1451 } 1452 return null; 1453 } 1454 1455 /** 1456 * returns the AccessibleRole from an AccessibleContext 1457 */ 1458 private String getAccessibleRoleStringFromContext(final AccessibleContext ac) { 1459 if (ac != null) { 1460 AccessibleRole role = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() { 1461 @Override 1462 public AccessibleRole call() throws Exception { 1463 return ac.getAccessibleRole(); 1464 } 1465 }, ac); 1466 if (role != null) { 1467 String s = role.toDisplayString(Locale.US); 1468 if (s != null) { 1469 references.increment(s); 1470 debugString("[INFO]: Returning AccessibleRole from Context: " + s); 1471 return s; 1472 } 1473 } 1474 } else { 1475 debugString("[ERROR]: getAccessibleRoleStringFromContext; ac = null"); 1476 } 1477 return null; 1478 } 1479 1480 /** 1481 * return the AccessibleRole from an AccessibleContext in the en_US locale 1482 */ 1483 private String getAccessibleRoleStringFromContext_en_US(final AccessibleContext ac) { 1484 return getAccessibleRoleStringFromContext(ac); 1485 } 1486 1487 /** 1488 * return the AccessibleStates from an AccessibleContext 1489 */ 1490 private String getAccessibleStatesStringFromContext(final AccessibleContext ac) { 1491 if (ac != null) { 1492 AccessibleStateSet stateSet = InvocationUtils.invokeAndWait(new Callable<AccessibleStateSet>() { 1493 @Override 1494 public AccessibleStateSet call() throws Exception { 1495 return ac.getAccessibleStateSet(); 1496 } 1497 }, ac); 1498 if (stateSet != null) { 1499 String s = stateSet.toString(); 1500 if (s != null && 1501 s.indexOf(AccessibleState.MANAGES_DESCENDANTS.toDisplayString(Locale.US)) == -1) { 1502 // Indicate whether this component manages its own 1503 // children 1504 AccessibleRole role = InvocationUtils.invokeAndWait(() -> { 1505 return ac.getAccessibleRole(); 1506 }, ac); 1507 if (role == AccessibleRole.LIST || 1508 role == AccessibleRole.TABLE || 1509 role == AccessibleRole.TREE) { 1510 s += ","; 1511 s += AccessibleState.MANAGES_DESCENDANTS.toDisplayString(Locale.US); 1512 } 1513 references.increment(s); 1514 debugString("[INFO]: Returning AccessibleStateSet from Context: " + s); 1515 return s; 1516 } 1517 } 1518 } else { 1519 debugString("[ERROR]: getAccessibleStatesStringFromContext; ac = null"); 1520 } 1521 return null; 1522 } 1523 1524 /** 1525 * returns the AccessibleStates from an AccessibleContext in the en_US locale 1526 */ 1527 private String getAccessibleStatesStringFromContext_en_US(final AccessibleContext ac) { 1528 if (ac != null) { 1529 AccessibleStateSet stateSet = InvocationUtils.invokeAndWait(new Callable<AccessibleStateSet>() { 1530 @Override 1531 public AccessibleStateSet call() throws Exception { 1532 return ac.getAccessibleStateSet(); 1533 } 1534 }, ac); 1535 if (stateSet != null) { 1536 String s = ""; 1537 AccessibleState[] states = stateSet.toArray(); 1538 if (states != null && states.length > 0) { 1539 s = states[0].toDisplayString(Locale.US); 1540 for (int i = 1; i < states.length; i++) { 1541 s = s + "," + states[i].toDisplayString(Locale.US); 1542 } 1543 } 1544 references.increment(s); 1545 debugString("[INFO]: Returning AccessibleStateSet en_US from Context: " + s); 1546 return s; 1547 } 1548 } 1549 debugString("[ERROR]: getAccessibleStatesStringFromContext; ac = null"); 1550 return null; 1551 } 1552 1553 /** 1554 * returns the AccessibleParent from an AccessibleContext 1555 */ 1556 private AccessibleContext getAccessibleParentFromContext(final AccessibleContext ac) { 1557 if (ac==null) 1558 return null; 1559 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 1560 @Override 1561 public AccessibleContext call() throws Exception { 1562 Accessible a = ac.getAccessibleParent(); 1563 if (a != null) { 1564 AccessibleContext apc = a.getAccessibleContext(); 1565 if (apc != null) { 1566 return apc; 1567 } 1568 } 1569 return null; 1570 } 1571 }, ac); 1572 } 1573 1574 /** 1575 * returns the AccessibleIndexInParent from an AccessibleContext 1576 */ 1577 private int getAccessibleIndexInParentFromContext(final AccessibleContext ac) { 1578 if (ac==null) 1579 return -1; 1580 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 1581 @Override 1582 public Integer call() throws Exception { 1583 return ac.getAccessibleIndexInParent(); 1584 } 1585 }, ac); 1586 } 1587 1588 /** 1589 * returns the AccessibleChild count from an AccessibleContext 1590 */ 1591 private int getAccessibleChildrenCountFromContext(final AccessibleContext ac) { 1592 if (ac==null) 1593 return -1; 1594 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 1595 @Override 1596 public Integer call() throws Exception { 1597 return ac.getAccessibleChildrenCount(); 1598 } 1599 }, ac); 1600 } 1601 1602 /** 1603 * returns the AccessibleChild Context from an AccessibleContext 1604 */ 1605 private AccessibleContext getAccessibleChildFromContext(final AccessibleContext ac, final int index) { 1606 1607 if (ac == null) { 1608 return null; 1609 } 1610 1611 final JTable table = InvocationUtils.invokeAndWait(new Callable<JTable>() { 1612 @Override 1613 public JTable call() throws Exception { 1614 // work-around for AccessibleJTable.getCurrentAccessibleContext returning 1615 // wrong renderer component when cell contains more than one component 1616 Accessible parent = ac.getAccessibleParent(); 1617 if (parent != null) { 1618 int indexInParent = ac.getAccessibleIndexInParent(); 1619 Accessible child = 1620 parent.getAccessibleContext().getAccessibleChild(indexInParent); 1621 if (child instanceof JTable) { 1622 return (JTable) child; 1623 } 1624 } 1625 return null; 1626 } 1627 }, ac); 1628 1629 if (table == null) { 1630 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 1631 @Override 1632 public AccessibleContext call() throws Exception { 1633 Accessible a = ac.getAccessibleChild(index); 1634 if (a != null) { 1635 return a.getAccessibleContext(); 1636 } 1637 return null; 1638 } 1639 }, ac); 1640 } 1641 1642 final AccessibleTable at = getAccessibleTableFromContext(ac); 1643 1644 final int row = getAccessibleTableRow(at, index); 1645 final int column = getAccessibleTableColumn(at, index); 1646 1647 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 1648 @Override 1649 public AccessibleContext call() throws Exception { 1650 TableCellRenderer renderer = table.getCellRenderer(row, column); 1651 if (renderer == null) { 1652 Class<?> columnClass = table.getColumnClass(column); 1653 renderer = table.getDefaultRenderer(columnClass); 1654 } 1655 Component component = 1656 renderer.getTableCellRendererComponent(table, table.getValueAt(row, column), 1657 false, false, row, column); 1658 if (component instanceof Accessible) { 1659 return component.getAccessibleContext(); 1660 } 1661 return null; 1662 } 1663 }, ac); 1664 } 1665 1666 /** 1667 * returns the AccessibleComponent bounds on screen from an AccessibleContext 1668 */ 1669 private Rectangle getAccessibleBoundsOnScreenFromContext(final AccessibleContext ac) { 1670 if(ac==null) 1671 return null; 1672 return InvocationUtils.invokeAndWait(new Callable<Rectangle>() { 1673 @Override 1674 public Rectangle call() throws Exception { 1675 AccessibleComponent acmp = ac.getAccessibleComponent(); 1676 if (acmp != null) { 1677 Rectangle r = acmp.getBounds(); 1678 if (r != null) { 1679 try { 1680 Point p = acmp.getLocationOnScreen(); 1681 if (p != null) { 1682 r.x = p.x; 1683 r.y = p.y; 1684 return r; 1685 } 1686 } catch (Exception e) { 1687 return null; 1688 } 1689 } 1690 } 1691 return null; 1692 } 1693 }, ac); 1694 } 1695 1696 /** 1697 * returns the AccessibleComponent x-coord from an AccessibleContext 1698 */ 1699 private int getAccessibleXcoordFromContext(AccessibleContext ac) { 1700 if (ac != null) { 1701 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac); 1702 if (r != null) { 1703 debugString("[INFO]: Returning Accessible x coord from Context: " + r.x); 1704 return r.x; 1705 } 1706 } else { 1707 debugString("[ERROR]: getAccessibleXcoordFromContext ac = null"); 1708 } 1709 return -1; 1710 } 1711 1712 /** 1713 * returns the AccessibleComponent y-coord from an AccessibleContext 1714 */ 1715 private int getAccessibleYcoordFromContext(AccessibleContext ac) { 1716 debugString("[INFO]: getAccessibleYcoordFromContext() called"); 1717 if (ac != null) { 1718 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac); 1719 if (r != null) { 1720 return r.y; 1721 } 1722 } else { 1723 debugString("[ERROR]: getAccessibleYcoordFromContext; ac = null"); 1724 } 1725 return -1; 1726 } 1727 1728 /** 1729 * returns the AccessibleComponent height from an AccessibleContext 1730 */ 1731 private int getAccessibleHeightFromContext(AccessibleContext ac) { 1732 if (ac != null) { 1733 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac); 1734 if (r != null) { 1735 return r.height; 1736 } 1737 } else { 1738 debugString("[ERROR]: getAccessibleHeightFromContext; ac = null"); 1739 } 1740 return -1; 1741 } 1742 1743 /** 1744 * returns the AccessibleComponent width from an AccessibleContext 1745 */ 1746 private int getAccessibleWidthFromContext(AccessibleContext ac) { 1747 if (ac != null) { 1748 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac); 1749 if (r != null) { 1750 return r.width; 1751 } 1752 } else { 1753 debugString("[ERROR]: getAccessibleWidthFromContext; ac = null"); 1754 } 1755 return -1; 1756 } 1757 1758 1759 /** 1760 * returns the AccessibleComponent from an AccessibleContext 1761 */ 1762 private AccessibleComponent getAccessibleComponentFromContext(AccessibleContext ac) { 1763 if (ac != null) { 1764 AccessibleComponent acmp = InvocationUtils.invokeAndWait(() -> { 1765 return ac.getAccessibleComponent(); 1766 }, ac); 1767 if (acmp != null) { 1768 debugString("[INFO]: Returning AccessibleComponent Context"); 1769 return acmp; 1770 } 1771 } else { 1772 debugString("[ERROR]: getAccessibleComponentFromContext; ac = null"); 1773 } 1774 return null; 1775 } 1776 1777 /** 1778 * returns the AccessibleAction from an AccessibleContext 1779 */ 1780 private AccessibleAction getAccessibleActionFromContext(final AccessibleContext ac) { 1781 debugString("[INFO]: Returning AccessibleAction Context"); 1782 return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleAction>() { 1783 @Override 1784 public AccessibleAction call() throws Exception { 1785 return ac.getAccessibleAction(); 1786 } 1787 }, ac); 1788 } 1789 1790 /** 1791 * returns the AccessibleSelection from an AccessibleContext 1792 */ 1793 private AccessibleSelection getAccessibleSelectionFromContext(final AccessibleContext ac) { 1794 return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleSelection>() { 1795 @Override 1796 public AccessibleSelection call() throws Exception { 1797 return ac.getAccessibleSelection(); 1798 } 1799 }, ac); 1800 } 1801 1802 /** 1803 * return the AccessibleText from an AccessibleContext 1804 */ 1805 private AccessibleText getAccessibleTextFromContext(final AccessibleContext ac) { 1806 return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleText>() { 1807 @Override 1808 public AccessibleText call() throws Exception { 1809 return ac.getAccessibleText(); 1810 } 1811 }, ac); 1812 } 1813 1814 /** 1815 * return the AccessibleComponent from an AccessibleContext 1816 */ 1817 private AccessibleValue getAccessibleValueFromContext(final AccessibleContext ac) { 1818 return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleValue>() { 1819 @Override 1820 public AccessibleValue call() throws Exception { 1821 return ac.getAccessibleValue(); 1822 } 1823 }, ac); 1824 } 1825 1826 /* ===== AccessibleText methods ===== */ 1827 1828 /** 1829 * returns the bounding rectangle for the text cursor 1830 * XXX 1831 */ 1832 private Rectangle getCaretLocation(final AccessibleContext ac) { 1833 debugString("[INFO]: getCaretLocation"); 1834 if (ac==null) 1835 return null; 1836 return InvocationUtils.invokeAndWait(new Callable<Rectangle>() { 1837 @Override 1838 public Rectangle call() throws Exception { 1839 // workaround for JAAPI not returning cursor bounding rectangle 1840 Rectangle r = null; 1841 Accessible parent = ac.getAccessibleParent(); 1842 if (parent instanceof Accessible) { 1843 int indexInParent = ac.getAccessibleIndexInParent(); 1844 Accessible child = 1845 parent.getAccessibleContext().getAccessibleChild(indexInParent); 1846 1847 if (child instanceof JTextComponent) { 1848 JTextComponent text = (JTextComponent) child; 1849 try { 1850 r = text.modelToView(text.getCaretPosition()); 1851 if (r != null) { 1852 Point p = text.getLocationOnScreen(); 1853 r.translate(p.x, p.y); 1854 } 1855 } catch (BadLocationException ble) { 1856 } 1857 } 1858 } 1859 return r; 1860 } 1861 }, ac); 1862 } 1863 1864 /** 1865 * returns the x-coordinate for the text cursor rectangle 1866 */ 1867 private int getCaretLocationX(AccessibleContext ac) { 1868 Rectangle r = getCaretLocation(ac); 1869 if (r != null) { 1870 return r.x; 1871 } else { 1872 return -1; 1873 } 1874 } 1875 1876 /** 1877 * returns the y-coordinate for the text cursor rectangle 1878 */ 1879 private int getCaretLocationY(AccessibleContext ac) { 1880 Rectangle r = getCaretLocation(ac); 1881 if (r != null) { 1882 return r.y; 1883 } else { 1884 return -1; 1885 } 1886 } 1887 1888 /** 1889 * returns the height for the text cursor rectangle 1890 */ 1891 private int getCaretLocationHeight(AccessibleContext ac) { 1892 Rectangle r = getCaretLocation(ac); 1893 if (r != null) { 1894 return r.height; 1895 } else { 1896 return -1; 1897 } 1898 } 1899 1900 /** 1901 * returns the width for the text cursor rectangle 1902 */ 1903 private int getCaretLocationWidth(AccessibleContext ac) { 1904 Rectangle r = getCaretLocation(ac); 1905 if (r != null) { 1906 return r.width; 1907 } else { 1908 return -1; 1909 } 1910 } 1911 1912 /** 1913 * returns the character count from an AccessibleContext 1914 */ 1915 private int getAccessibleCharCountFromContext(final AccessibleContext ac) { 1916 if (ac==null) 1917 return -1; 1918 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 1919 @Override 1920 public Integer call() throws Exception { 1921 AccessibleText at = ac.getAccessibleText(); 1922 if (at != null) { 1923 return at.getCharCount(); 1924 } 1925 return -1; 1926 } 1927 }, ac); 1928 } 1929 1930 /** 1931 * returns the caret position from an AccessibleContext 1932 */ 1933 private int getAccessibleCaretPositionFromContext(final AccessibleContext ac) { 1934 if (ac==null) 1935 return -1; 1936 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 1937 @Override 1938 public Integer call() throws Exception { 1939 AccessibleText at = ac.getAccessibleText(); 1940 if (at != null) { 1941 return at.getCaretPosition(); 1942 } 1943 return -1; 1944 } 1945 }, ac); 1946 } 1947 1948 /** 1949 * Return the index at a specific point from an AccessibleContext 1950 * Point(x, y) is in screen coordinates. 1951 */ 1952 private int getAccessibleIndexAtPointFromContext(final AccessibleContext ac, 1953 final int x, final int y) { 1954 debugString("[INFO]: getAccessibleIndexAtPointFromContext: x = "+x+"; y = "+y); 1955 if (ac==null) 1956 return -1; 1957 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 1958 @Override 1959 public Integer call() throws Exception { 1960 AccessibleText at = ac.getAccessibleText(); 1961 AccessibleComponent acomp = ac.getAccessibleComponent(); 1962 if (at != null && acomp != null) { 1963 // Convert x and y from screen coordinates to 1964 // local coordinates. 1965 try { 1966 Point p = acomp.getLocationOnScreen(); 1967 int x1, y1; 1968 if (p != null) { 1969 x1 = x - p.x; 1970 if (x1 < 0) { 1971 x1 = 0; 1972 } 1973 y1 = y - p.y; 1974 if (y1 < 0) { 1975 y1 = 0; 1976 } 1977 1978 Point newPoint = new Point(x1, y1); 1979 int indexAtPoint = at.getIndexAtPoint(new Point(x1, y1)); 1980 return indexAtPoint; 1981 } 1982 } catch (Exception e) { 1983 } 1984 } 1985 return -1; 1986 } 1987 }, ac); 1988 } 1989 1990 /** 1991 * return the letter at a specific point from an AccessibleContext 1992 */ 1993 private String getAccessibleLetterAtIndexFromContext(final AccessibleContext ac, final int index) { 1994 if (ac != null) { 1995 String s = InvocationUtils.invokeAndWait(new Callable<String>() { 1996 @Override 1997 public String call() throws Exception { 1998 AccessibleText at = ac.getAccessibleText(); 1999 if (at == null) return null; 2000 return at.getAtIndex(AccessibleText.CHARACTER, index); 2001 } 2002 }, ac); 2003 if (s != null) { 2004 references.increment(s); 2005 return s; 2006 } 2007 } else { 2008 debugString("[ERROR]: getAccessibleLetterAtIndexFromContext; ac = null"); 2009 } 2010 return null; 2011 } 2012 2013 /** 2014 * return the word at a specific point from an AccessibleContext 2015 */ 2016 private String getAccessibleWordAtIndexFromContext(final AccessibleContext ac, final int index) { 2017 if (ac != null) { 2018 String s = InvocationUtils.invokeAndWait(new Callable<String>() { 2019 @Override 2020 public String call() throws Exception { 2021 AccessibleText at = ac.getAccessibleText(); 2022 if (at == null) return null; 2023 return at.getAtIndex(AccessibleText.WORD, index); 2024 } 2025 }, ac); 2026 if (s != null) { 2027 references.increment(s); 2028 return s; 2029 } 2030 } else { 2031 debugString("[ERROR]: getAccessibleWordAtIndexFromContext; ac = null"); 2032 } 2033 return null; 2034 } 2035 2036 /** 2037 * return the sentence at a specific point from an AccessibleContext 2038 */ 2039 private String getAccessibleSentenceAtIndexFromContext(final AccessibleContext ac, final int index) { 2040 if (ac != null) { 2041 String s = InvocationUtils.invokeAndWait(new Callable<String>() { 2042 @Override 2043 public String call() throws Exception { 2044 AccessibleText at = ac.getAccessibleText(); 2045 if (at == null) return null; 2046 return at.getAtIndex(AccessibleText.SENTENCE, index); 2047 } 2048 }, ac); 2049 if (s != null) { 2050 references.increment(s); 2051 return s; 2052 } 2053 } else { 2054 debugString("[ERROR]: getAccessibleSentenceAtIndexFromContext; ac = null"); 2055 } 2056 return null; 2057 } 2058 2059 /** 2060 * return the text selection start from an AccessibleContext 2061 */ 2062 private int getAccessibleTextSelectionStartFromContext(final AccessibleContext ac) { 2063 if (ac == null) return -1; 2064 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 2065 @Override 2066 public Integer call() throws Exception { 2067 AccessibleText at = ac.getAccessibleText(); 2068 if (at != null) { 2069 return at.getSelectionStart(); 2070 } 2071 return -1; 2072 } 2073 }, ac); 2074 } 2075 2076 /** 2077 * return the text selection end from an AccessibleContext 2078 */ 2079 private int getAccessibleTextSelectionEndFromContext(final AccessibleContext ac) { 2080 if (ac == null) 2081 return -1; 2082 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 2083 @Override 2084 public Integer call() throws Exception { 2085 AccessibleText at = ac.getAccessibleText(); 2086 if (at != null) { 2087 return at.getSelectionEnd(); 2088 } 2089 return -1; 2090 } 2091 }, ac); 2092 } 2093 2094 /** 2095 * return the selected text from an AccessibleContext 2096 */ 2097 private String getAccessibleTextSelectedTextFromContext(final AccessibleContext ac) { 2098 if (ac != null) { 2099 String s = InvocationUtils.invokeAndWait(new Callable<String>() { 2100 @Override 2101 public String call() throws Exception { 2102 AccessibleText at = ac.getAccessibleText(); 2103 if (at == null) return null; 2104 return at.getSelectedText(); 2105 } 2106 }, ac); 2107 if (s != null) { 2108 references.increment(s); 2109 return s; 2110 } 2111 } else { 2112 debugString("[ERROR]: getAccessibleTextSelectedTextFromContext; ac = null"); 2113 } 2114 return null; 2115 } 2116 2117 /** 2118 * return the attribute string at a given index from an AccessibleContext 2119 */ 2120 private String getAccessibleAttributesAtIndexFromContext(final AccessibleContext ac, 2121 final int index) { 2122 if (ac == null) 2123 return null; 2124 AttributeSet as = InvocationUtils.invokeAndWait(new Callable<AttributeSet>() { 2125 @Override 2126 public AttributeSet call() throws Exception { 2127 AccessibleText at = ac.getAccessibleText(); 2128 if (at != null) { 2129 return at.getCharacterAttribute(index); 2130 } 2131 return null; 2132 } 2133 }, ac); 2134 String s = expandStyleConstants(as); 2135 if (s != null) { 2136 references.increment(s); 2137 return s; 2138 } 2139 return null; 2140 } 2141 2142 /** 2143 * Get line info: left index of line 2144 * 2145 * algorithm: cast back, doubling each time, 2146 * 'till find line boundaries 2147 * 2148 * return -1 if we can't get the info (e.g. index or at passed in 2149 * is bogus; etc.) 2150 */ 2151 private int getAccessibleTextLineLeftBoundsFromContext(final AccessibleContext ac, 2152 final int index) { 2153 if (ac == null) 2154 return -1; 2155 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 2156 @Override 2157 public Integer call() throws Exception { 2158 AccessibleText at = ac.getAccessibleText(); 2159 if (at != null) { 2160 int lineStart; 2161 int offset; 2162 Rectangle charRect; 2163 Rectangle indexRect = at.getCharacterBounds(index); 2164 int textLen = at.getCharCount(); 2165 if (indexRect == null) { 2166 return -1; 2167 } 2168 // find the start of the line 2169 // 2170 offset = 1; 2171 lineStart = index - offset < 0 ? 0 : index - offset; 2172 charRect = at.getCharacterBounds(lineStart); 2173 // slouch behind beginning of line 2174 while (charRect != null 2175 && charRect.y >= indexRect.y 2176 && lineStart > 0) { 2177 offset = offset << 1; 2178 lineStart = index - offset < 0 ? 0 : index - offset; 2179 charRect = at.getCharacterBounds(lineStart); 2180 } 2181 if (lineStart == 0) { // special case: we're on the first line! 2182 // we found it! 2183 } else { 2184 offset = offset >> 1; // know boundary within last expansion 2185 // ground forward to beginning of line 2186 while (offset > 0) { 2187 charRect = at.getCharacterBounds(lineStart + offset); 2188 if (charRect.y < indexRect.y) { // still before line 2189 lineStart += offset; 2190 } else { 2191 // leave lineStart alone, it's close! 2192 } 2193 offset = offset >> 1; 2194 } 2195 // subtract one 'cause we're already too far... 2196 lineStart += 1; 2197 } 2198 return lineStart; 2199 } 2200 return -1; 2201 } 2202 }, ac); 2203 } 2204 2205 /** 2206 * Get line info: right index of line 2207 * 2208 * algorithm: cast back, doubling each time, 2209 * 'till find line boundaries 2210 * 2211 * return -1 if we can't get the info (e.g. index or at passed in 2212 * is bogus; etc.) 2213 */ 2214 private int getAccessibleTextLineRightBoundsFromContext(final AccessibleContext ac, final int index) { 2215 if(ac == null) 2216 return -1; 2217 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 2218 @Override 2219 public Integer call() throws Exception { 2220 AccessibleText at = ac.getAccessibleText(); 2221 if (at != null) { 2222 int lineEnd; 2223 int offset; 2224 Rectangle charRect; 2225 Rectangle indexRect = at.getCharacterBounds(index); 2226 int textLen = at.getCharCount(); 2227 if (indexRect == null) { 2228 return -1; 2229 } 2230 // find the end of the line 2231 // 2232 offset = 1; 2233 lineEnd = index + offset > textLen - 1 2234 ? textLen - 1 : index + offset; 2235 charRect = at.getCharacterBounds(lineEnd); 2236 // push past end of line 2237 while (charRect != null && 2238 charRect.y <= indexRect.y && 2239 lineEnd < textLen - 1) { 2240 offset = offset << 1; 2241 lineEnd = index + offset > textLen - 1 2242 ? textLen - 1 : index + offset; 2243 charRect = at.getCharacterBounds(lineEnd); 2244 } 2245 if (lineEnd == textLen - 1) { // special case: on the last line! 2246 // we found it! 2247 } else { 2248 offset = offset >> 1; // know boundary within last expansion 2249 // pull back to end of line 2250 while (offset > 0) { 2251 charRect = at.getCharacterBounds(lineEnd - offset); 2252 if (charRect.y > indexRect.y) { // still beyond line 2253 lineEnd -= offset; 2254 } else { 2255 // leave lineEnd alone, it's close! 2256 } 2257 offset = offset >> 1; 2258 } 2259 // subtract one 'cause we're already too far... 2260 lineEnd -= 1; 2261 } 2262 return lineEnd; 2263 } 2264 return -1; 2265 } 2266 }, ac); 2267 } 2268 2269 /** 2270 * Get a range of text; null if indicies are bogus 2271 */ 2272 private String getAccessibleTextRangeFromContext(final AccessibleContext ac, 2273 final int start, final int end) { 2274 String s = InvocationUtils.invokeAndWait(new Callable<String>() { 2275 @Override 2276 public String call() throws Exception { 2277 if (ac != null) { 2278 AccessibleText at = ac.getAccessibleText(); 2279 if (at != null) { 2280 // start - end is inclusive 2281 if (start > end) { 2282 return null; 2283 } 2284 if (end >= at.getCharCount()) { 2285 return null; 2286 } 2287 StringBuffer buf = new StringBuffer(end - start + 1); 2288 for (int i = start; i <= end; i++) { 2289 buf.append(at.getAtIndex(AccessibleText.CHARACTER, i)); 2290 } 2291 return buf.toString(); 2292 } 2293 } 2294 return null; 2295 } 2296 }, ac); 2297 if (s != null) { 2298 references.increment(s); 2299 return s; 2300 } else { 2301 return null; 2302 } 2303 } 2304 2305 /** 2306 * return the AttributeSet object at a given index from an AccessibleContext 2307 */ 2308 private AttributeSet getAccessibleAttributeSetAtIndexFromContext(final AccessibleContext ac, 2309 final int index) { 2310 return InvocationUtils.invokeAndWait(new Callable<AttributeSet>() { 2311 @Override 2312 public AttributeSet call() throws Exception { 2313 if (ac != null) { 2314 AccessibleText at = ac.getAccessibleText(); 2315 if (at != null) { 2316 AttributeSet as = at.getCharacterAttribute(index); 2317 if (as != null) { 2318 AccessBridge.this.references.increment(as); 2319 return as; 2320 } 2321 } 2322 } 2323 return null; 2324 } 2325 }, ac); 2326 } 2327 2328 2329 /** 2330 * return the bounding rectangle at index from an AccessibleContext 2331 */ 2332 private Rectangle getAccessibleTextRectAtIndexFromContext(final AccessibleContext ac, 2333 final int index) { 2334 // want to do this in global coords, so need to combine w/ac global coords 2335 Rectangle r = InvocationUtils.invokeAndWait(new Callable<Rectangle>() { 2336 @Override 2337 public Rectangle call() throws Exception { 2338 // want to do this in global coords, so need to combine w/ac global coords 2339 if (ac != null) { 2340 AccessibleText at = ac.getAccessibleText(); 2341 if (at != null) { 2342 Rectangle rect = at.getCharacterBounds(index); 2343 if (rect != null) { 2344 String s = at.getAtIndex(AccessibleText.CHARACTER, index); 2345 if (s != null && s.equals("\n")) { 2346 rect.width = 0; 2347 } 2348 return rect; 2349 } 2350 } 2351 } 2352 return null; 2353 } 2354 }, ac); 2355 Rectangle acRect = getAccessibleBoundsOnScreenFromContext(ac); 2356 if (r != null && acRect != null) { 2357 r.translate(acRect.x, acRect.y); 2358 return r; 2359 } 2360 return null; 2361 } 2362 2363 /** 2364 * return the AccessibleText character x-coord at index from an AccessibleContext 2365 */ 2366 private int getAccessibleXcoordTextRectAtIndexFromContext(AccessibleContext ac, int index) { 2367 if (ac != null) { 2368 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index); 2369 if (r != null) { 2370 return r.x; 2371 } 2372 } else { 2373 debugString("[ERROR]: getAccessibleXcoordTextRectAtIndexFromContext; ac = null"); 2374 } 2375 return -1; 2376 } 2377 2378 /** 2379 * return the AccessibleText character y-coord at index from an AccessibleContext 2380 */ 2381 private int getAccessibleYcoordTextRectAtIndexFromContext(AccessibleContext ac, int index) { 2382 if (ac != null) { 2383 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index); 2384 if (r != null) { 2385 return r.y; 2386 } 2387 } else { 2388 debugString("[ERROR]: getAccessibleYcoordTextRectAtIndexFromContext; ac = null"); 2389 } 2390 return -1; 2391 } 2392 2393 /** 2394 * return the AccessibleText character height at index from an AccessibleContext 2395 */ 2396 private int getAccessibleHeightTextRectAtIndexFromContext(AccessibleContext ac, int index) { 2397 if (ac != null) { 2398 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index); 2399 if (r != null) { 2400 return r.height; 2401 } 2402 } else { 2403 debugString("[ERROR]: getAccessibleHeightTextRectAtIndexFromContext; ac = null"); 2404 } 2405 return -1; 2406 } 2407 2408 /** 2409 * return the AccessibleText character width at index from an AccessibleContext 2410 */ 2411 private int getAccessibleWidthTextRectAtIndexFromContext(AccessibleContext ac, int index) { 2412 if (ac != null) { 2413 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index); 2414 if (r != null) { 2415 return r.width; 2416 } 2417 } else { 2418 debugString("[ERROR]: getAccessibleWidthTextRectAtIndexFromContext; ac = null"); 2419 } 2420 return -1; 2421 } 2422 2423 /* ===== AttributeSet methods for AccessibleText ===== */ 2424 2425 /** 2426 * return the bold setting from an AttributeSet 2427 */ 2428 private boolean getBoldFromAttributeSet(AttributeSet as) { 2429 if (as != null) { 2430 return StyleConstants.isBold(as); 2431 } else { 2432 debugString("[ERROR]: getBoldFromAttributeSet; as = null"); 2433 } 2434 return false; 2435 } 2436 2437 /** 2438 * return the italic setting from an AttributeSet 2439 */ 2440 private boolean getItalicFromAttributeSet(AttributeSet as) { 2441 if (as != null) { 2442 return StyleConstants.isItalic(as); 2443 } else { 2444 debugString("[ERROR]: getItalicFromAttributeSet; as = null"); 2445 } 2446 return false; 2447 } 2448 2449 /** 2450 * return the underline setting from an AttributeSet 2451 */ 2452 private boolean getUnderlineFromAttributeSet(AttributeSet as) { 2453 if (as != null) { 2454 return StyleConstants.isUnderline(as); 2455 } else { 2456 debugString("[ERROR]: getUnderlineFromAttributeSet; as = null"); 2457 } 2458 return false; 2459 } 2460 2461 /** 2462 * return the strikethrough setting from an AttributeSet 2463 */ 2464 private boolean getStrikethroughFromAttributeSet(AttributeSet as) { 2465 if (as != null) { 2466 return StyleConstants.isStrikeThrough(as); 2467 } else { 2468 debugString("[ERROR]: getStrikethroughFromAttributeSet; as = null"); 2469 } 2470 return false; 2471 } 2472 2473 /** 2474 * return the superscript setting from an AttributeSet 2475 */ 2476 private boolean getSuperscriptFromAttributeSet(AttributeSet as) { 2477 if (as != null) { 2478 return StyleConstants.isSuperscript(as); 2479 } else { 2480 debugString("[ERROR]: getSuperscriptFromAttributeSet; as = null"); 2481 } 2482 return false; 2483 } 2484 2485 /** 2486 * return the subscript setting from an AttributeSet 2487 */ 2488 private boolean getSubscriptFromAttributeSet(AttributeSet as) { 2489 if (as != null) { 2490 return StyleConstants.isSubscript(as); 2491 } else { 2492 debugString("[ERROR]: getSubscriptFromAttributeSet; as = null"); 2493 } 2494 return false; 2495 } 2496 2497 /** 2498 * return the background color from an AttributeSet 2499 */ 2500 private String getBackgroundColorFromAttributeSet(AttributeSet as) { 2501 if (as != null) { 2502 String s = StyleConstants.getBackground(as).toString(); 2503 if (s != null) { 2504 references.increment(s); 2505 return s; 2506 } 2507 } else { 2508 debugString("[ERROR]: getBackgroundColorFromAttributeSet; as = null"); 2509 } 2510 return null; 2511 } 2512 2513 /** 2514 * return the foreground color from an AttributeSet 2515 */ 2516 private String getForegroundColorFromAttributeSet(AttributeSet as) { 2517 if (as != null) { 2518 String s = StyleConstants.getForeground(as).toString(); 2519 if (s != null) { 2520 references.increment(s); 2521 return s; 2522 } 2523 } else { 2524 debugString("[ERROR]: getForegroundColorFromAttributeSet; as = null"); 2525 } 2526 return null; 2527 } 2528 2529 /** 2530 * return the font family from an AttributeSet 2531 */ 2532 private String getFontFamilyFromAttributeSet(AttributeSet as) { 2533 if (as != null) { 2534 String s = StyleConstants.getFontFamily(as).toString(); 2535 if (s != null) { 2536 references.increment(s); 2537 return s; 2538 } 2539 } else { 2540 debugString("[ERROR]: getFontFamilyFromAttributeSet; as = null"); 2541 } 2542 return null; 2543 } 2544 2545 /** 2546 * return the font size from an AttributeSet 2547 */ 2548 private int getFontSizeFromAttributeSet(AttributeSet as) { 2549 if (as != null) { 2550 return StyleConstants.getFontSize(as); 2551 } else { 2552 debugString("[ERROR]: getFontSizeFromAttributeSet; as = null"); 2553 } 2554 return -1; 2555 } 2556 2557 /** 2558 * return the alignment from an AttributeSet 2559 */ 2560 private int getAlignmentFromAttributeSet(AttributeSet as) { 2561 if (as != null) { 2562 return StyleConstants.getAlignment(as); 2563 } else { 2564 debugString("[ERROR]: getAlignmentFromAttributeSet; as = null"); 2565 } 2566 return -1; 2567 } 2568 2569 /** 2570 * return the BiDi level from an AttributeSet 2571 */ 2572 private int getBidiLevelFromAttributeSet(AttributeSet as) { 2573 if (as != null) { 2574 return StyleConstants.getBidiLevel(as); 2575 } else { 2576 debugString("[ERROR]: getBidiLevelFromAttributeSet; as = null"); 2577 } 2578 return -1; 2579 } 2580 2581 2582 /** 2583 * return the first line indent from an AttributeSet 2584 */ 2585 private float getFirstLineIndentFromAttributeSet(AttributeSet as) { 2586 if (as != null) { 2587 return StyleConstants.getFirstLineIndent(as); 2588 } else { 2589 debugString("[ERROR]: getFirstLineIndentFromAttributeSet; as = null"); 2590 } 2591 return -1; 2592 } 2593 2594 /** 2595 * return the left indent from an AttributeSet 2596 */ 2597 private float getLeftIndentFromAttributeSet(AttributeSet as) { 2598 if (as != null) { 2599 return StyleConstants.getLeftIndent(as); 2600 } else { 2601 debugString("[ERROR]: getLeftIndentFromAttributeSet; as = null"); 2602 } 2603 return -1; 2604 } 2605 2606 /** 2607 * return the right indent from an AttributeSet 2608 */ 2609 private float getRightIndentFromAttributeSet(AttributeSet as) { 2610 if (as != null) { 2611 return StyleConstants.getRightIndent(as); 2612 } else { 2613 debugString("[ERROR]: getRightIndentFromAttributeSet; as = null"); 2614 } 2615 return -1; 2616 } 2617 2618 /** 2619 * return the line spacing from an AttributeSet 2620 */ 2621 private float getLineSpacingFromAttributeSet(AttributeSet as) { 2622 if (as != null) { 2623 return StyleConstants.getLineSpacing(as); 2624 } else { 2625 debugString("[ERROR]: getLineSpacingFromAttributeSet; as = null"); 2626 } 2627 return -1; 2628 } 2629 2630 /** 2631 * return the space above from an AttributeSet 2632 */ 2633 private float getSpaceAboveFromAttributeSet(AttributeSet as) { 2634 if (as != null) { 2635 return StyleConstants.getSpaceAbove(as); 2636 } else { 2637 debugString("[ERROR]: getSpaceAboveFromAttributeSet; as = null"); 2638 } 2639 return -1; 2640 } 2641 2642 /** 2643 * return the space below from an AttributeSet 2644 */ 2645 private float getSpaceBelowFromAttributeSet(AttributeSet as) { 2646 if (as != null) { 2647 return StyleConstants.getSpaceBelow(as); 2648 } else { 2649 debugString("[ERROR]: getSpaceBelowFromAttributeSet; as = null"); 2650 } 2651 return -1; 2652 } 2653 2654 /** 2655 * Enumerate all StyleConstants in the AttributeSet 2656 * 2657 * We need to check explicitly, 'cause of the HTML package conversion 2658 * mechanism (they may not be stored as StyleConstants, just translated 2659 * to them when asked). 2660 * 2661 * (Use convenience methods where they are defined...) 2662 * 2663 * Not checking the following (which the IBM SNS guidelines says 2664 * should be defined): 2665 * - ComponentElementName 2666 * - IconElementName 2667 * - NameAttribute 2668 * - ResolveAttribute 2669 */ 2670 private String expandStyleConstants(AttributeSet as) { 2671 Color c; 2672 Object o; 2673 String attrString = ""; 2674 2675 // ---------- check for various Character Constants 2676 2677 attrString += "BidiLevel = " + StyleConstants.getBidiLevel(as); 2678 2679 final Component comp = StyleConstants.getComponent(as); 2680 if (comp != null) { 2681 if (comp instanceof Accessible) { 2682 final AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 2683 @Override 2684 public AccessibleContext call() throws Exception { 2685 return comp.getAccessibleContext(); 2686 } 2687 }, comp); 2688 if (ac != null) { 2689 attrString += "; Accessible Component = " + InvocationUtils.invokeAndWait(new Callable<String>() { 2690 @Override 2691 public String call() throws Exception { 2692 return ac.getAccessibleName(); 2693 } 2694 }, ac); 2695 } else { 2696 attrString += "; Innaccessible Component = " + comp; 2697 } 2698 } else { 2699 attrString += "; Innaccessible Component = " + comp; 2700 } 2701 } 2702 2703 Icon i = StyleConstants.getIcon(as); 2704 if (i != null) { 2705 if (i instanceof ImageIcon) { 2706 attrString += "; ImageIcon = " + ((ImageIcon) i).getDescription(); 2707 } else { 2708 attrString += "; Icon = " + i; 2709 } 2710 } 2711 2712 attrString += "; FontFamily = " + StyleConstants.getFontFamily(as); 2713 2714 attrString += "; FontSize = " + StyleConstants.getFontSize(as); 2715 2716 if (StyleConstants.isBold(as)) { 2717 attrString += "; bold"; 2718 } 2719 2720 if (StyleConstants.isItalic(as)) { 2721 attrString += "; italic"; 2722 } 2723 2724 if (StyleConstants.isUnderline(as)) { 2725 attrString += "; underline"; 2726 } 2727 2728 if (StyleConstants.isStrikeThrough(as)) { 2729 attrString += "; strikethrough"; 2730 } 2731 2732 if (StyleConstants.isSuperscript(as)) { 2733 attrString += "; superscript"; 2734 } 2735 2736 if (StyleConstants.isSubscript(as)) { 2737 attrString += "; subscript"; 2738 } 2739 2740 c = StyleConstants.getForeground(as); 2741 if (c != null) { 2742 attrString += "; Foreground = " + c; 2743 } 2744 2745 c = StyleConstants.getBackground(as); 2746 if (c != null) { 2747 attrString += "; Background = " + c; 2748 } 2749 2750 attrString += "; FirstLineIndent = " + StyleConstants.getFirstLineIndent(as); 2751 2752 attrString += "; RightIndent = " + StyleConstants.getRightIndent(as); 2753 2754 attrString += "; LeftIndent = " + StyleConstants.getLeftIndent(as); 2755 2756 attrString += "; LineSpacing = " + StyleConstants.getLineSpacing(as); 2757 2758 attrString += "; SpaceAbove = " + StyleConstants.getSpaceAbove(as); 2759 2760 attrString += "; SpaceBelow = " + StyleConstants.getSpaceBelow(as); 2761 2762 attrString += "; Alignment = " + StyleConstants.getAlignment(as); 2763 2764 TabSet ts = StyleConstants.getTabSet(as); 2765 if (ts != null) { 2766 attrString += "; TabSet = " + ts; 2767 } 2768 2769 return attrString; 2770 } 2771 2772 2773 /* ===== AccessibleValue methods ===== */ 2774 2775 /** 2776 * return the AccessibleValue current value from an AccessibleContext 2777 * returned using a String 'cause the value is a java Number 2778 * 2779 */ 2780 private String getCurrentAccessibleValueFromContext(final AccessibleContext ac) { 2781 if (ac != null) { 2782 final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() { 2783 @Override 2784 public Number call() throws Exception { 2785 AccessibleValue av = ac.getAccessibleValue(); 2786 if (av == null) return null; 2787 return av.getCurrentAccessibleValue(); 2788 } 2789 }, ac); 2790 if (value != null) { 2791 String s = value.toString(); 2792 if (s != null) { 2793 references.increment(s); 2794 return s; 2795 } 2796 } 2797 } else { 2798 debugString("[ERROR]: getCurrentAccessibleValueFromContext; ac = null"); 2799 } 2800 return null; 2801 } 2802 2803 /** 2804 * return the AccessibleValue maximum value from an AccessibleContext 2805 * returned using a String 'cause the value is a java Number 2806 * 2807 */ 2808 private String getMaximumAccessibleValueFromContext(final AccessibleContext ac) { 2809 if (ac != null) { 2810 final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() { 2811 @Override 2812 public Number call() throws Exception { 2813 AccessibleValue av = ac.getAccessibleValue(); 2814 if (av == null) return null; 2815 return av.getMaximumAccessibleValue(); 2816 } 2817 }, ac); 2818 if (value != null) { 2819 String s = value.toString(); 2820 if (s != null) { 2821 references.increment(s); 2822 return s; 2823 } 2824 } 2825 } else { 2826 debugString("[ERROR]: getMaximumAccessibleValueFromContext; ac = null"); 2827 } 2828 return null; 2829 } 2830 2831 /** 2832 * return the AccessibleValue minimum value from an AccessibleContext 2833 * returned using a String 'cause the value is a java Number 2834 * 2835 */ 2836 private String getMinimumAccessibleValueFromContext(final AccessibleContext ac) { 2837 if (ac != null) { 2838 final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() { 2839 @Override 2840 public Number call() throws Exception { 2841 AccessibleValue av = ac.getAccessibleValue(); 2842 if (av == null) return null; 2843 return av.getMinimumAccessibleValue(); 2844 } 2845 }, ac); 2846 if (value != null) { 2847 String s = value.toString(); 2848 if (s != null) { 2849 references.increment(s); 2850 return s; 2851 } 2852 } 2853 } else { 2854 debugString("[ERROR]: getMinimumAccessibleValueFromContext; ac = null"); 2855 } 2856 return null; 2857 } 2858 2859 2860 /* ===== AccessibleSelection methods ===== */ 2861 2862 /** 2863 * add to the AccessibleSelection of an AccessibleContext child i 2864 * 2865 */ 2866 private void addAccessibleSelectionFromContext(final AccessibleContext ac, final int i) { 2867 try { 2868 InvocationUtils.invokeAndWait(new Callable<Object>() { 2869 @Override 2870 public Object call() throws Exception { 2871 if (ac != null) { 2872 AccessibleSelection as = ac.getAccessibleSelection(); 2873 if (as != null) { 2874 as.addAccessibleSelection(i); 2875 } 2876 } 2877 return null; 2878 } 2879 }, ac); 2880 } catch(Exception e){} 2881 } 2882 2883 /** 2884 * clear all of the AccessibleSelection of an AccessibleContex 2885 * 2886 */ 2887 private void clearAccessibleSelectionFromContext(final AccessibleContext ac) { 2888 try { 2889 InvocationUtils.invokeAndWait(new Callable<Object>() { 2890 @Override 2891 public Object call() throws Exception { 2892 AccessibleSelection as = ac.getAccessibleSelection(); 2893 if (as != null) { 2894 as.clearAccessibleSelection(); 2895 } 2896 return null; 2897 } 2898 }, ac); 2899 } catch(Exception e){} 2900 2901 } 2902 2903 /** 2904 * get the AccessibleContext of the i-th AccessibleSelection of an AccessibleContext 2905 * 2906 */ 2907 private AccessibleContext getAccessibleSelectionFromContext(final AccessibleContext ac, final int i) { 2908 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 2909 @Override 2910 public AccessibleContext call() throws Exception { 2911 if (ac != null) { 2912 AccessibleSelection as = ac.getAccessibleSelection(); 2913 if (as != null) { 2914 Accessible a = as.getAccessibleSelection(i); 2915 if (a == null) 2916 return null; 2917 else 2918 return a.getAccessibleContext(); 2919 } 2920 } 2921 return null; 2922 } 2923 }, ac); 2924 } 2925 2926 /** 2927 * get number of things selected in the AccessibleSelection of an AccessibleContext 2928 * 2929 */ 2930 private int getAccessibleSelectionCountFromContext(final AccessibleContext ac) { 2931 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 2932 @Override 2933 public Integer call() throws Exception { 2934 if (ac != null) { 2935 AccessibleSelection as = ac.getAccessibleSelection(); 2936 if (as != null) { 2937 return as.getAccessibleSelectionCount(); 2938 } 2939 } 2940 return -1; 2941 } 2942 }, ac); 2943 } 2944 2945 /** 2946 * return true if the i-th child of the AccessibleSelection of an AccessibleContext is selected 2947 * 2948 */ 2949 private boolean isAccessibleChildSelectedFromContext(final AccessibleContext ac, final int i) { 2950 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 2951 @Override 2952 public Boolean call() throws Exception { 2953 if (ac != null) { 2954 AccessibleSelection as = ac.getAccessibleSelection(); 2955 if (as != null) { 2956 return as.isAccessibleChildSelected(i); 2957 } 2958 } 2959 return false; 2960 } 2961 }, ac); 2962 } 2963 2964 /** 2965 * remove the i-th child from the AccessibleSelection of an AccessibleContext 2966 * 2967 */ 2968 private void removeAccessibleSelectionFromContext(final AccessibleContext ac, final int i) { 2969 InvocationUtils.invokeAndWait(new Callable<Object>() { 2970 @Override 2971 public Object call() throws Exception { 2972 if (ac != null) { 2973 AccessibleSelection as = ac.getAccessibleSelection(); 2974 if (as != null) { 2975 as.removeAccessibleSelection(i); 2976 } 2977 } 2978 return null; 2979 } 2980 }, ac); 2981 } 2982 2983 /** 2984 * select all (if possible) of the children of the AccessibleSelection of an AccessibleContext 2985 * 2986 */ 2987 private void selectAllAccessibleSelectionFromContext(final AccessibleContext ac) { 2988 InvocationUtils.invokeAndWait(new Callable<Object>() { 2989 @Override 2990 public Object call() throws Exception { 2991 if (ac != null) { 2992 AccessibleSelection as = ac.getAccessibleSelection(); 2993 if (as != null) { 2994 as.selectAllAccessibleSelection(); 2995 } 2996 } 2997 return null; 2998 } 2999 }, ac); 3000 } 3001 3002 // ======== AccessibleTable ======== 3003 3004 ConcurrentHashMap<AccessibleTable,AccessibleContext> hashtab = new ConcurrentHashMap<>(); 3005 3006 /** 3007 * returns the AccessibleTable for an AccessibleContext 3008 */ 3009 private AccessibleTable getAccessibleTableFromContext(final AccessibleContext ac) { 3010 String version = getJavaVersionProperty(); 3011 if ((version != null && version.compareTo("1.3") >= 0)) { 3012 return InvocationUtils.invokeAndWait(new Callable<AccessibleTable>() { 3013 @Override 3014 public AccessibleTable call() throws Exception { 3015 if (ac != null) { 3016 AccessibleTable at = ac.getAccessibleTable(); 3017 if (at != null) { 3018 AccessBridge.this.hashtab.put(at, ac); 3019 return at; 3020 } 3021 } 3022 return null; 3023 } 3024 }, ac); 3025 } 3026 return null; 3027 } 3028 3029 3030 /* 3031 * returns the AccessibleContext that contains an AccessibleTable 3032 */ 3033 private AccessibleContext getContextFromAccessibleTable(AccessibleTable at) { 3034 return hashtab.get(at); 3035 } 3036 3037 /* 3038 * returns the row count for an AccessibleTable 3039 */ 3040 private int getAccessibleTableRowCount(final AccessibleContext ac) { 3041 debugString("[INFO]: ##### getAccessibleTableRowCount"); 3042 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3043 @Override 3044 public Integer call() throws Exception { 3045 if (ac != null) { 3046 AccessibleTable at = ac.getAccessibleTable(); 3047 if (at != null) { 3048 return at.getAccessibleRowCount(); 3049 } 3050 } 3051 return -1; 3052 } 3053 }, ac); 3054 } 3055 3056 /* 3057 * returns the column count for an AccessibleTable 3058 */ 3059 private int getAccessibleTableColumnCount(final AccessibleContext ac) { 3060 debugString("[INFO]: ##### getAccessibleTableColumnCount"); 3061 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3062 @Override 3063 public Integer call() throws Exception { 3064 if (ac != null) { 3065 AccessibleTable at = ac.getAccessibleTable(); 3066 if (at != null) { 3067 return at.getAccessibleColumnCount(); 3068 } 3069 } 3070 return -1; 3071 } 3072 }, ac); 3073 } 3074 3075 /* 3076 * returns the AccessibleContext for an AccessibleTable cell 3077 */ 3078 private AccessibleContext getAccessibleTableCellAccessibleContext(final AccessibleTable at, 3079 final int row, final int column) { 3080 debugString("[INFO]: getAccessibleTableCellAccessibleContext: at = "+at.getClass()); 3081 if (at == null) return null; 3082 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 3083 @Override 3084 public AccessibleContext call() throws Exception { 3085 if (!(at instanceof AccessibleContext)) { 3086 Accessible a = at.getAccessibleAt(row, column); 3087 if (a != null) { 3088 return a.getAccessibleContext(); 3089 } 3090 } else { 3091 // work-around for AccessibleJTable.getCurrentAccessibleContext returning 3092 // wrong renderer component when cell contains more than one component 3093 AccessibleContext ac = (AccessibleContext) at; 3094 Accessible parent = ac.getAccessibleParent(); 3095 if (parent != null) { 3096 int indexInParent = ac.getAccessibleIndexInParent(); 3097 Accessible child = 3098 parent.getAccessibleContext().getAccessibleChild(indexInParent); 3099 if (child instanceof JTable) { 3100 JTable table = (JTable) child; 3101 3102 TableCellRenderer renderer = table.getCellRenderer(row, column); 3103 if (renderer == null) { 3104 Class<?> columnClass = table.getColumnClass(column); 3105 renderer = table.getDefaultRenderer(columnClass); 3106 } 3107 Component component = 3108 renderer.getTableCellRendererComponent(table, table.getValueAt(row, column), 3109 false, false, row, column); 3110 if (component instanceof Accessible) { 3111 return component.getAccessibleContext(); 3112 } 3113 } 3114 } 3115 } 3116 return null; 3117 } 3118 }, getContextFromAccessibleTable(at)); 3119 } 3120 3121 /* 3122 * returns the index of a cell at a given row and column in an AccessibleTable 3123 */ 3124 private int getAccessibleTableCellIndex(final AccessibleTable at, int row, int column) { 3125 debugString("[INFO]: ##### getAccessibleTableCellIndex: at="+at); 3126 if (at != null) { 3127 int cellIndex = row * 3128 InvocationUtils.invokeAndWait(new Callable<Integer>() { 3129 @Override 3130 public Integer call() throws Exception { 3131 return at.getAccessibleColumnCount(); 3132 } 3133 }, getContextFromAccessibleTable(at)) + 3134 column; 3135 debugString("[INFO]: ##### getAccessibleTableCellIndex="+cellIndex); 3136 return cellIndex; 3137 } 3138 debugString("[ERROR]: ##### getAccessibleTableCellIndex FAILED"); 3139 return -1; 3140 } 3141 3142 /* 3143 * returns the row extent of a cell at a given row and column in an AccessibleTable 3144 */ 3145 private int getAccessibleTableCellRowExtent(final AccessibleTable at, final int row, final int column) { 3146 debugString("[INFO]: ##### getAccessibleTableCellRowExtent"); 3147 if (at != null) { 3148 int rowExtent = InvocationUtils.invokeAndWait(new Callable<Integer>() { 3149 @Override 3150 public Integer call() throws Exception { 3151 return at.getAccessibleRowExtentAt(row, column); 3152 } 3153 }, 3154 getContextFromAccessibleTable(at)); 3155 debugString("[INFO]: ##### getAccessibleTableCellRowExtent="+rowExtent); 3156 return rowExtent; 3157 } 3158 debugString("[ERROR]: ##### getAccessibleTableCellRowExtent FAILED"); 3159 return -1; 3160 } 3161 3162 /* 3163 * returns the column extent of a cell at a given row and column in an AccessibleTable 3164 */ 3165 private int getAccessibleTableCellColumnExtent(final AccessibleTable at, final int row, final int column) { 3166 debugString("[INFO]: ##### getAccessibleTableCellColumnExtent"); 3167 if (at != null) { 3168 int columnExtent = InvocationUtils.invokeAndWait(new Callable<Integer>() { 3169 @Override 3170 public Integer call() throws Exception { 3171 return at.getAccessibleColumnExtentAt(row, column); 3172 } 3173 }, 3174 getContextFromAccessibleTable(at)); 3175 debugString("[INFO]: ##### getAccessibleTableCellColumnExtent="+columnExtent); 3176 return columnExtent; 3177 } 3178 debugString("[ERROR]: ##### getAccessibleTableCellColumnExtent FAILED"); 3179 return -1; 3180 } 3181 3182 /* 3183 * returns whether a cell is selected at a given row and column in an AccessibleTable 3184 */ 3185 private boolean isAccessibleTableCellSelected(final AccessibleTable at, final int row, 3186 final int column) { 3187 debugString("[INFO]: ##### isAccessibleTableCellSelected: ["+row+"]["+column+"]"); 3188 if (at == null) 3189 return false; 3190 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 3191 @Override 3192 public Boolean call() throws Exception { 3193 boolean isSelected = false; 3194 Accessible a = at.getAccessibleAt(row, column); 3195 if (a != null) { 3196 AccessibleContext ac = a.getAccessibleContext(); 3197 if (ac == null) 3198 return false; 3199 AccessibleStateSet as = ac.getAccessibleStateSet(); 3200 if (as != null) { 3201 isSelected = as.contains(AccessibleState.SELECTED); 3202 } 3203 } 3204 return isSelected; 3205 } 3206 }, getContextFromAccessibleTable(at)); 3207 } 3208 3209 /* 3210 * returns an AccessibleTable that represents the row header in an 3211 * AccessibleTable 3212 */ 3213 private AccessibleTable getAccessibleTableRowHeader(final AccessibleContext ac) { 3214 debugString("[INFO]: ##### getAccessibleTableRowHeader called"); 3215 AccessibleTable at = InvocationUtils.invokeAndWait(new Callable<AccessibleTable>() { 3216 @Override 3217 public AccessibleTable call() throws Exception { 3218 if (ac != null) { 3219 AccessibleTable at = ac.getAccessibleTable(); 3220 if (at != null) { 3221 return at.getAccessibleRowHeader(); 3222 } 3223 } 3224 return null; 3225 } 3226 }, ac); 3227 if (at != null) { 3228 hashtab.put(at, ac); 3229 } 3230 return at; 3231 } 3232 3233 /* 3234 * returns an AccessibleTable that represents the column header in an 3235 * AccessibleTable 3236 */ 3237 private AccessibleTable getAccessibleTableColumnHeader(final AccessibleContext ac) { 3238 debugString("[INFO]: ##### getAccessibleTableColumnHeader"); 3239 if (ac == null) 3240 return null; 3241 AccessibleTable at = InvocationUtils.invokeAndWait(new Callable<AccessibleTable>() { 3242 @Override 3243 public AccessibleTable call() throws Exception { 3244 // workaround for getAccessibleColumnHeader NPE 3245 // when the table header is null 3246 Accessible parent = ac.getAccessibleParent(); 3247 if (parent != null) { 3248 int indexInParent = ac.getAccessibleIndexInParent(); 3249 Accessible child = 3250 parent.getAccessibleContext().getAccessibleChild(indexInParent); 3251 if (child instanceof JTable) { 3252 JTable table = (JTable) child; 3253 if (table.getTableHeader() == null) { 3254 return null; 3255 } 3256 } 3257 } 3258 AccessibleTable at = ac.getAccessibleTable(); 3259 if (at != null) { 3260 return at.getAccessibleColumnHeader(); 3261 } 3262 return null; 3263 } 3264 }, ac); 3265 if (at != null) { 3266 hashtab.put(at, ac); 3267 } 3268 return at; 3269 } 3270 3271 /* 3272 * returns the number of row headers in an AccessibleTable that represents 3273 * the row header in an AccessibleTable 3274 */ 3275 private int getAccessibleTableRowHeaderRowCount(AccessibleContext ac) { 3276 3277 debugString("[INFO]: ##### getAccessibleTableRowHeaderRowCount called"); 3278 if (ac != null) { 3279 final AccessibleTable atRowHeader = getAccessibleTableRowHeader(ac); 3280 if (atRowHeader != null) { 3281 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3282 @Override 3283 public Integer call() throws Exception { 3284 if (atRowHeader != null) { 3285 return atRowHeader.getAccessibleRowCount(); 3286 } 3287 return -1; 3288 } 3289 }, ac); 3290 } 3291 } 3292 return -1; 3293 } 3294 3295 /* 3296 * returns the number of column headers in an AccessibleTable that represents 3297 * the row header in an AccessibleTable 3298 */ 3299 private int getAccessibleTableRowHeaderColumnCount(AccessibleContext ac) { 3300 debugString("[INFO]: ##### getAccessibleTableRowHeaderColumnCount called"); 3301 if (ac != null) { 3302 final AccessibleTable atRowHeader = getAccessibleTableRowHeader(ac); 3303 if (atRowHeader != null) { 3304 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3305 @Override 3306 public Integer call() throws Exception { 3307 if (atRowHeader != null) { 3308 return atRowHeader.getAccessibleColumnCount(); 3309 } 3310 return -1; 3311 } 3312 }, ac); 3313 } 3314 } 3315 debugString("[ERROR]: ##### getAccessibleTableRowHeaderColumnCount FAILED"); 3316 return -1; 3317 } 3318 3319 /* 3320 * returns the number of row headers in an AccessibleTable that represents 3321 * the column header in an AccessibleTable 3322 */ 3323 private int getAccessibleTableColumnHeaderRowCount(AccessibleContext ac) { 3324 3325 debugString("[INFO]: ##### getAccessibleTableColumnHeaderRowCount"); 3326 if (ac != null) { 3327 final AccessibleTable atColumnHeader = getAccessibleTableColumnHeader(ac); 3328 if (atColumnHeader != null) { 3329 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3330 @Override 3331 public Integer call() throws Exception { 3332 if (atColumnHeader != null) { 3333 return atColumnHeader.getAccessibleRowCount(); 3334 } 3335 return -1; 3336 } 3337 }, ac); 3338 } 3339 } 3340 debugString("[ERROR]: ##### getAccessibleTableColumnHeaderRowCount FAILED"); 3341 return -1; 3342 } 3343 3344 /* 3345 * returns the number of column headers in an AccessibleTable that represents 3346 * the column header in an AccessibleTable 3347 */ 3348 private int getAccessibleTableColumnHeaderColumnCount(AccessibleContext ac) { 3349 3350 debugString("[ERROR]: ##### getAccessibleTableColumnHeaderColumnCount"); 3351 if (ac != null) { 3352 final AccessibleTable atColumnHeader = getAccessibleTableColumnHeader(ac); 3353 if (atColumnHeader != null) { 3354 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3355 @Override 3356 public Integer call() throws Exception { 3357 if (atColumnHeader != null) { 3358 return atColumnHeader.getAccessibleColumnCount(); 3359 } 3360 return -1; 3361 } 3362 }, ac); 3363 } 3364 } 3365 debugString("[ERROR]: ##### getAccessibleTableColumnHeaderColumnCount FAILED"); 3366 return -1; 3367 } 3368 3369 /* 3370 * returns the description of a row header in an AccessibleTable 3371 */ 3372 private AccessibleContext getAccessibleTableRowDescription(final AccessibleTable table, 3373 final int row) { 3374 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 3375 @Override 3376 public AccessibleContext call() throws Exception { 3377 if (table != null) { 3378 Accessible a = table.getAccessibleRowDescription(row); 3379 if (a != null) { 3380 return a.getAccessibleContext(); 3381 } 3382 } 3383 return null; 3384 } 3385 }, getContextFromAccessibleTable(table)); 3386 } 3387 3388 /* 3389 * returns the description of a column header in an AccessibleTable 3390 */ 3391 private AccessibleContext getAccessibleTableColumnDescription(final AccessibleTable at, 3392 final int column) { 3393 if (at == null) 3394 return null; 3395 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 3396 @Override 3397 public AccessibleContext call() throws Exception { 3398 Accessible a = at.getAccessibleColumnDescription(column); 3399 if (a != null) { 3400 return a.getAccessibleContext(); 3401 } 3402 return null; 3403 } 3404 }, getContextFromAccessibleTable(at)); 3405 } 3406 3407 /* 3408 * returns the number of rows selected in an AccessibleTable 3409 */ 3410 private int getAccessibleTableRowSelectionCount(final AccessibleTable at) { 3411 if (at != null) { 3412 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3413 @Override 3414 public Integer call() throws Exception { 3415 int[] selections = at.getSelectedAccessibleRows(); 3416 if (selections != null) 3417 return selections.length; 3418 else 3419 return -1; 3420 } 3421 }, getContextFromAccessibleTable(at)); 3422 } 3423 return -1; 3424 } 3425 3426 /* 3427 * returns the row number of the i-th selected row in an AccessibleTable 3428 */ 3429 private int getAccessibleTableRowSelections(final AccessibleTable at, final int i) { 3430 if (at != null) { 3431 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3432 @Override 3433 public Integer call() throws Exception { 3434 int[] selections = at.getSelectedAccessibleRows(); 3435 if (selections.length > i) { 3436 return selections[i]; 3437 } 3438 return -1; 3439 } 3440 }, getContextFromAccessibleTable(at)); 3441 } 3442 return -1; 3443 } 3444 3445 /* 3446 * returns whether a row is selected in an AccessibleTable 3447 */ 3448 private boolean isAccessibleTableRowSelected(final AccessibleTable at, 3449 final int row) { 3450 if (at == null) 3451 return false; 3452 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 3453 @Override 3454 public Boolean call() throws Exception { 3455 return at.isAccessibleRowSelected(row); 3456 } 3457 }, getContextFromAccessibleTable(at)); 3458 } 3459 3460 /* 3461 * returns whether a column is selected in an AccessibleTable 3462 */ 3463 private boolean isAccessibleTableColumnSelected(final AccessibleTable at, 3464 final int column) { 3465 if (at == null) 3466 return false; 3467 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 3468 @Override 3469 public Boolean call() throws Exception { 3470 return at.isAccessibleColumnSelected(column); 3471 } 3472 }, getContextFromAccessibleTable(at)); 3473 } 3474 3475 /* 3476 * returns the number of columns selected in an AccessibleTable 3477 */ 3478 private int getAccessibleTableColumnSelectionCount(final AccessibleTable at) { 3479 if (at == null) 3480 return -1; 3481 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3482 @Override 3483 public Integer call() throws Exception { 3484 int[] selections = at.getSelectedAccessibleColumns(); 3485 if (selections != null) 3486 return selections.length; 3487 else 3488 return -1; 3489 } 3490 }, getContextFromAccessibleTable(at)); 3491 } 3492 3493 /* 3494 * returns the row number of the i-th selected row in an AccessibleTable 3495 */ 3496 private int getAccessibleTableColumnSelections(final AccessibleTable at, final int i) { 3497 if (at == null) 3498 return -1; 3499 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3500 @Override 3501 public Integer call() throws Exception { 3502 int[] selections = at.getSelectedAccessibleColumns(); 3503 if (selections != null && selections.length > i) { 3504 return selections[i]; 3505 } 3506 return -1; 3507 } 3508 }, getContextFromAccessibleTable(at)); 3509 } 3510 3511 /* ===== AccessibleExtendedTable (since 1.4) ===== */ 3512 3513 /* 3514 * returns the row number for a cell at a given index in an AccessibleTable 3515 */ 3516 private int getAccessibleTableRow(final AccessibleTable at, int index) { 3517 if (at == null) 3518 return -1; 3519 int colCount=InvocationUtils.invokeAndWait(new Callable<Integer>() { 3520 @Override 3521 public Integer call() throws Exception { 3522 return at.getAccessibleColumnCount(); 3523 } 3524 }, getContextFromAccessibleTable(at)); 3525 return index / colCount; 3526 } 3527 3528 /* 3529 * returns the column number for a cell at a given index in an AccessibleTable 3530 */ 3531 private int getAccessibleTableColumn(final AccessibleTable at, int index) { 3532 if (at == null) 3533 return -1; 3534 int colCount=InvocationUtils.invokeAndWait(new Callable<Integer>() { 3535 @Override 3536 public Integer call() throws Exception { 3537 return at.getAccessibleColumnCount(); 3538 } 3539 }, getContextFromAccessibleTable(at)); 3540 return index % colCount; 3541 } 3542 3543 /* 3544 * returns the index for a cell at a given row and column in an 3545 * AccessibleTable 3546 */ 3547 private int getAccessibleTableIndex(final AccessibleTable at, int row, int column) { 3548 if (at == null) 3549 return -1; 3550 int colCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 3551 @Override 3552 public Integer call() throws Exception { 3553 return at.getAccessibleColumnCount(); 3554 } 3555 }, getContextFromAccessibleTable(at)); 3556 return row * colCount + column; 3557 } 3558 3559 // ===== AccessibleRelationSet ===== 3560 3561 /* 3562 * returns the number of relations in the AccessibleContext's 3563 * AccessibleRelationSet 3564 */ 3565 private int getAccessibleRelationCount(final AccessibleContext ac) { 3566 String version = getJavaVersionProperty(); 3567 if ((version != null && version.compareTo("1.3") >= 0)) { 3568 if (ac != null) { 3569 AccessibleRelationSet ars = InvocationUtils.invokeAndWait(new Callable<AccessibleRelationSet>() { 3570 @Override 3571 public AccessibleRelationSet call() throws Exception { 3572 return ac.getAccessibleRelationSet(); 3573 } 3574 }, ac); 3575 if (ars != null) 3576 return ars.size(); 3577 } 3578 } 3579 return 0; 3580 } 3581 3582 /* 3583 * returns the ith relation key in the AccessibleContext's 3584 * AccessibleRelationSet 3585 */ 3586 private String getAccessibleRelationKey(final AccessibleContext ac, final int i) { 3587 return InvocationUtils.invokeAndWait(new Callable<String>() { 3588 @Override 3589 public String call() throws Exception { 3590 if (ac != null) { 3591 AccessibleRelationSet ars = ac.getAccessibleRelationSet(); 3592 if (ars != null) { 3593 AccessibleRelation[] relations = ars.toArray(); 3594 if (relations != null && i >= 0 && i < relations.length) { 3595 return relations[i].getKey(); 3596 } 3597 } 3598 } 3599 return null; 3600 } 3601 }, ac); 3602 } 3603 3604 /* 3605 * returns the number of targets in a relation in the AccessibleContext's 3606 * AccessibleRelationSet 3607 */ 3608 private int getAccessibleRelationTargetCount(final AccessibleContext ac, final int i) { 3609 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3610 @Override 3611 public Integer call() throws Exception { 3612 if (ac != null) { 3613 AccessibleRelationSet ars = ac.getAccessibleRelationSet(); 3614 if (ars != null) { 3615 AccessibleRelation[] relations = ars.toArray(); 3616 if (relations != null && i >= 0 && i < relations.length) { 3617 Object[] targets = relations[i].getTarget(); 3618 return targets.length; 3619 } 3620 } 3621 } 3622 return -1; 3623 } 3624 }, ac); 3625 } 3626 3627 /* 3628 * returns the jth target in the ith relation in the AccessibleContext's 3629 * AccessibleRelationSet 3630 */ 3631 private AccessibleContext getAccessibleRelationTarget(final AccessibleContext ac, 3632 final int i, final int j) { 3633 debugString("[INFO]: ***** getAccessibleRelationTarget"); 3634 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 3635 @Override 3636 public AccessibleContext call() throws Exception { 3637 if (ac != null) { 3638 AccessibleRelationSet ars = ac.getAccessibleRelationSet(); 3639 if (ars != null) { 3640 AccessibleRelation[] relations = ars.toArray(); 3641 if (relations != null && i >= 0 && i < relations.length) { 3642 Object[] targets = relations[i].getTarget(); 3643 if (targets != null && j >= 0 & j < targets.length) { 3644 Object o = targets[j]; 3645 if (o instanceof Accessible) { 3646 return ((Accessible) o).getAccessibleContext(); 3647 } 3648 } 3649 } 3650 } 3651 } 3652 return null; 3653 } 3654 }, ac); 3655 } 3656 3657 // ========= AccessibleHypertext ========= 3658 3659 private Map<AccessibleHypertext, AccessibleContext> hyperTextContextMap = new WeakHashMap<>(); 3660 private Map<AccessibleHyperlink, AccessibleContext> hyperLinkContextMap = new WeakHashMap<>(); 3661 3662 /* 3663 * Returns the AccessibleHypertext 3664 */ 3665 private AccessibleHypertext getAccessibleHypertext(final AccessibleContext ac) { 3666 debugString("[INFO]: getAccessibleHyperlink"); 3667 if (ac==null) 3668 return null; 3669 AccessibleHypertext hypertext = InvocationUtils.invokeAndWait(new Callable<AccessibleHypertext>() { 3670 @Override 3671 public AccessibleHypertext call() throws Exception { 3672 AccessibleText at = ac.getAccessibleText(); 3673 if (!(at instanceof AccessibleHypertext)) { 3674 return null; 3675 } 3676 return ((AccessibleHypertext) at); 3677 } 3678 }, ac); 3679 hyperTextContextMap.put(hypertext, ac); 3680 return hypertext; 3681 } 3682 3683 /* 3684 * Returns the number of AccessibleHyperlinks 3685 */ 3686 private int getAccessibleHyperlinkCount(AccessibleContext ac) { 3687 debugString("[INFO]: getAccessibleHyperlinkCount"); 3688 if (ac == null) { 3689 return 0; 3690 } 3691 final AccessibleHypertext hypertext = getAccessibleHypertext(ac); 3692 if (hypertext == null) { 3693 return 0; 3694 } 3695 //return hypertext.getLinkCount(); 3696 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3697 @Override 3698 public Integer call() throws Exception { 3699 return hypertext.getLinkCount(); 3700 } 3701 }, ac); 3702 } 3703 3704 /* 3705 * Returns the hyperlink at the specified index 3706 */ 3707 private AccessibleHyperlink getAccessibleHyperlink(final AccessibleHypertext hypertext, final int i) { 3708 debugString("[INFO]: getAccessibleHyperlink"); 3709 if (hypertext == null) { 3710 return null; 3711 } 3712 AccessibleContext ac = hyperTextContextMap.get(hypertext); 3713 if ( i < 0 || i >= 3714 InvocationUtils.invokeAndWait(new Callable<Integer>() { 3715 @Override 3716 public Integer call() throws Exception { 3717 return hypertext.getLinkCount(); 3718 } 3719 }, ac) ) { 3720 return null; 3721 } 3722 AccessibleHyperlink acLink = InvocationUtils.invokeAndWait(new Callable<AccessibleHyperlink>() { 3723 @Override 3724 public AccessibleHyperlink call() throws Exception { 3725 AccessibleHyperlink link = hypertext.getLink(i); 3726 if (link == null || (!link.isValid())) { 3727 return null; 3728 } 3729 return link; 3730 } 3731 }, ac); 3732 hyperLinkContextMap.put(acLink, ac); 3733 return acLink; 3734 } 3735 3736 /* 3737 * Returns the hyperlink object description 3738 */ 3739 private String getAccessibleHyperlinkText(final AccessibleHyperlink link) { 3740 debugString("[INFO]: getAccessibleHyperlinkText"); 3741 if (link == null) { 3742 return null; 3743 } 3744 return InvocationUtils.invokeAndWait(new Callable<String>() { 3745 @Override 3746 public String call() throws Exception { 3747 Object o = link.getAccessibleActionDescription(0); 3748 if (o != null) { 3749 return o.toString(); 3750 } 3751 return null; 3752 } 3753 }, hyperLinkContextMap.get(link)); 3754 } 3755 3756 /* 3757 * Returns the hyperlink URL 3758 */ 3759 private String getAccessibleHyperlinkURL(final AccessibleHyperlink link) { 3760 debugString("[INFO]: getAccessibleHyperlinkURL"); 3761 if (link == null) { 3762 return null; 3763 } 3764 return InvocationUtils.invokeAndWait(new Callable<String>() { 3765 @Override 3766 public String call() throws Exception { 3767 Object o = link.getAccessibleActionObject(0); 3768 if (o != null) { 3769 return o.toString(); 3770 } else { 3771 return null; 3772 } 3773 } 3774 }, hyperLinkContextMap.get(link)); 3775 } 3776 3777 /* 3778 * Returns the start index of the hyperlink text 3779 */ 3780 private int getAccessibleHyperlinkStartIndex(final AccessibleHyperlink link) { 3781 debugString("[INFO]: getAccessibleHyperlinkStartIndex"); 3782 if (link == null) { 3783 return -1; 3784 } 3785 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3786 @Override 3787 public Integer call() throws Exception { 3788 return link.getStartIndex(); 3789 } 3790 }, hyperLinkContextMap.get(link)); 3791 } 3792 3793 /* 3794 * Returns the end index of the hyperlink text 3795 */ 3796 private int getAccessibleHyperlinkEndIndex(final AccessibleHyperlink link) { 3797 debugString("[INFO]: getAccessibleHyperlinkEndIndex"); 3798 if (link == null) { 3799 return -1; 3800 } 3801 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 3802 @Override 3803 public Integer call() throws Exception { 3804 return link.getEndIndex(); 3805 } 3806 }, hyperLinkContextMap.get(link)); 3807 } 3808 3809 /* 3810 * Returns the index into an array of hyperlinks that 3811 * is associated with this character index, or -1 if there 3812 * is no hyperlink associated with this index. 3813 */ 3814 private int getAccessibleHypertextLinkIndex(final AccessibleHypertext hypertext, final int charIndex) { 3815 debugString("[INFO]: getAccessibleHypertextLinkIndex: charIndex = "+charIndex); 3816 if (hypertext == null) { 3817 return -1; 3818 } 3819 int linkIndex = InvocationUtils.invokeAndWait(new Callable<Integer>() { 3820 @Override 3821 public Integer call() throws Exception { 3822 return hypertext.getLinkIndex(charIndex); 3823 } 3824 }, hyperTextContextMap.get(hypertext)); 3825 debugString("[INFO]: getAccessibleHypertextLinkIndex returning "+linkIndex); 3826 return linkIndex; 3827 } 3828 3829 /* 3830 * Actives the hyperlink 3831 */ 3832 private boolean activateAccessibleHyperlink(final AccessibleContext ac, 3833 final AccessibleHyperlink link) { 3834 //debugString("activateAccessibleHyperlink: link = "+link.getClass()); 3835 if (link == null) { 3836 return false; 3837 } 3838 boolean retval = InvocationUtils.invokeAndWait(new Callable<Boolean>() { 3839 @Override 3840 public Boolean call() throws Exception { 3841 return link.doAccessibleAction(0); 3842 } 3843 }, ac); 3844 debugString("[INFO]: activateAccessibleHyperlink: returning = "+retval); 3845 return retval; 3846 } 3847 3848 3849 // ============ AccessibleKeyBinding ============= 3850 3851 /* 3852 * returns the component mnemonic 3853 */ 3854 private KeyStroke getMnemonic(final AccessibleContext ac) { 3855 if (ac == null) 3856 return null; 3857 return InvocationUtils.invokeAndWait(new Callable<KeyStroke>() { 3858 @Override 3859 public KeyStroke call() throws Exception { 3860 AccessibleComponent comp = ac.getAccessibleComponent(); 3861 if (!(comp instanceof AccessibleExtendedComponent)) { 3862 return null; 3863 } 3864 AccessibleExtendedComponent aec = (AccessibleExtendedComponent) comp; 3865 if (aec != null) { 3866 AccessibleKeyBinding akb = aec.getAccessibleKeyBinding(); 3867 if (akb != null) { 3868 Object o = akb.getAccessibleKeyBinding(0); 3869 if (o instanceof KeyStroke) { 3870 return (KeyStroke) o; 3871 } 3872 } 3873 } 3874 return null; 3875 } 3876 }, ac); 3877 } 3878 3879 /* 3880 * returns the JMenuItem accelerator 3881 */ 3882 private KeyStroke getAccelerator(final AccessibleContext ac) { 3883 // workaround for getAccessibleKeyBinding not returning the 3884 // JMenuItem accelerator 3885 if (ac == null) 3886 return null; 3887 return InvocationUtils.invokeAndWait(new Callable<KeyStroke>() { 3888 @Override 3889 public KeyStroke call() throws Exception { 3890 Accessible parent = ac.getAccessibleParent(); 3891 if (parent instanceof Accessible) { 3892 int indexInParent = ac.getAccessibleIndexInParent(); 3893 Accessible child = 3894 parent.getAccessibleContext().getAccessibleChild(indexInParent); 3895 if (child instanceof JMenuItem) { 3896 JMenuItem menuItem = (JMenuItem) child; 3897 if (menuItem == null) 3898 return null; 3899 KeyStroke keyStroke = menuItem.getAccelerator(); 3900 return keyStroke; 3901 } 3902 } 3903 return null; 3904 } 3905 }, ac); 3906 } 3907 3908 /* 3909 * returns 1-24 to indicate which F key is being used for a shortcut or 0 otherwise 3910 */ 3911 private int fKeyNumber(KeyStroke keyStroke) { 3912 if (keyStroke == null) 3913 return 0; 3914 int fKey = 0; 3915 String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode()); 3916 if (keyText != null && (keyText.length() == 2 || keyText.length() == 3)) { 3917 String prefix = keyText.substring(0, 1); 3918 if (prefix.equals("F")) { 3919 try { 3920 int suffix = Integer.parseInt(keyText.substring(1)); 3921 if (suffix >= 1 && suffix <= 24) { 3922 fKey = suffix; 3923 } 3924 } catch (Exception e) { // ignore NumberFormatException 3925 } 3926 } 3927 } 3928 return fKey; 3929 } 3930 3931 /* 3932 * returns one of several important control characters or 0 otherwise 3933 */ 3934 private int controlCode(KeyStroke keyStroke) { 3935 if (keyStroke == null) 3936 return 0; 3937 int code = keyStroke.getKeyCode(); 3938 switch (code) { 3939 case KeyEvent.VK_BACK_SPACE: 3940 case KeyEvent.VK_DELETE: 3941 case KeyEvent.VK_DOWN: 3942 case KeyEvent.VK_END: 3943 case KeyEvent.VK_HOME: 3944 case KeyEvent.VK_INSERT: 3945 case KeyEvent.VK_KP_DOWN: 3946 case KeyEvent.VK_KP_LEFT: 3947 case KeyEvent.VK_KP_RIGHT: 3948 case KeyEvent.VK_KP_UP: 3949 case KeyEvent.VK_LEFT: 3950 case KeyEvent.VK_PAGE_DOWN: 3951 case KeyEvent.VK_PAGE_UP: 3952 case KeyEvent.VK_RIGHT: 3953 case KeyEvent.VK_UP: 3954 break; 3955 default: 3956 code = 0; 3957 break; 3958 } 3959 return code; 3960 } 3961 3962 /* 3963 * returns the KeyStoke character 3964 */ 3965 private char getKeyChar(KeyStroke keyStroke) { 3966 // If the shortcut is an FKey return 1-24 3967 if (keyStroke == null) 3968 return 0; 3969 int fKey = fKeyNumber(keyStroke); 3970 if (fKey != 0) { 3971 // return 0x00000001 through 0x00000018 3972 debugString("[INFO]: Shortcut is: F" + fKey); 3973 return (char)fKey; 3974 } 3975 // If the accelerator is a control character, return it 3976 int keyCode = controlCode(keyStroke); 3977 if (keyCode != 0) { 3978 debugString("[INFO]: Shortcut is control character: " + Integer.toHexString(keyCode)); 3979 return (char)keyCode; 3980 } 3981 String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode()); 3982 debugString("[INFO]: Shortcut is: " + keyText); 3983 if (keyText != null || keyText.length() > 0) { 3984 CharSequence seq = keyText.subSequence(0, 1); 3985 if (seq != null || seq.length() > 0) { 3986 return seq.charAt(0); 3987 } 3988 } 3989 return 0; 3990 } 3991 3992 /* 3993 * returns the KeyStroke modifiers as an int 3994 */ 3995 private int getModifiers(KeyStroke keyStroke) { 3996 if (keyStroke == null) 3997 return 0; 3998 debugString("[INFO]: In AccessBridge.getModifiers"); 3999 // modifiers is a bit strip where bits 0-7 indicate a traditional modifier 4000 // such as Ctrl/Alt/Shift, bit 8 indicates an F key shortcut, and bit 9 indicates 4001 // a control code shortcut such as the delete key. 4002 4003 int modifiers = 0; 4004 // Is the shortcut an FKey? 4005 if (fKeyNumber(keyStroke) != 0) { 4006 modifiers |= 1 << 8; 4007 } 4008 // Is the shortcut a control code? 4009 if (controlCode(keyStroke) != 0) { 4010 modifiers |= 1 << 9; 4011 } 4012 // The following is needed in order to handle translated modifiers. 4013 // getKeyModifiersText doesn't work because for example in German Strg is 4014 // returned for Ctrl. 4015 4016 // There can be more than one modifier, e.g. if the modifier is ctrl + shift + B 4017 // the toString text is "shift ctrl pressed B". Need to parse through that. 4018 StringTokenizer st = new StringTokenizer(keyStroke.toString()); 4019 while (st.hasMoreTokens()) { 4020 String text = st.nextToken(); 4021 // Meta+Ctrl+Alt+Shift 4022 // 0-3 are shift, ctrl, meta, alt 4023 // 4-7 are for Solaris workstations (though not being used) 4024 if (text.startsWith("met")) { 4025 debugString("[INFO]: found meta"); 4026 modifiers |= ActionEvent.META_MASK; 4027 } 4028 if (text.startsWith("ctr")) { 4029 debugString("[INFO]: found ctrl"); 4030 modifiers |= ActionEvent.CTRL_MASK; 4031 } 4032 if (text.startsWith("alt")) { 4033 debugString("[INFO]: found alt"); 4034 modifiers |= ActionEvent.ALT_MASK; 4035 } 4036 if (text.startsWith("shi")) { 4037 debugString("[INFO]: found shift"); 4038 modifiers |= ActionEvent.SHIFT_MASK; 4039 } 4040 } 4041 debugString("[INFO]: returning modifiers: 0x" + Integer.toHexString(modifiers)); 4042 return modifiers; 4043 } 4044 4045 /* 4046 * returns the number of key bindings associated with this context 4047 */ 4048 private int getAccessibleKeyBindingsCount(AccessibleContext ac) { 4049 if (ac == null || (! runningOnJDK1_4) ) 4050 return 0; 4051 int count = 0; 4052 4053 if (getMnemonic(ac) != null) { 4054 count++; 4055 } 4056 if (getAccelerator(ac) != null) { 4057 count++; 4058 } 4059 return count; 4060 } 4061 4062 /* 4063 * returns the key binding character at the specified index 4064 */ 4065 private char getAccessibleKeyBindingChar(AccessibleContext ac, int index) { 4066 if (ac == null || (! runningOnJDK1_4) ) 4067 return 0; 4068 if((index == 0) && getMnemonic(ac)==null) {// special case when there is no mnemonic 4069 KeyStroke keyStroke = getAccelerator(ac); 4070 if (keyStroke != null) { 4071 return getKeyChar(keyStroke); 4072 } 4073 } 4074 if (index == 0) { // mnemonic 4075 KeyStroke keyStroke = getMnemonic(ac); 4076 if (keyStroke != null) { 4077 return getKeyChar(keyStroke); 4078 } 4079 } else if (index == 1) { // accelerator 4080 KeyStroke keyStroke = getAccelerator(ac); 4081 if (keyStroke != null) { 4082 return getKeyChar(keyStroke); 4083 } 4084 } 4085 return 0; 4086 } 4087 4088 /* 4089 * returns the key binding modifiers at the specified index 4090 */ 4091 private int getAccessibleKeyBindingModifiers(AccessibleContext ac, int index) { 4092 if (ac == null || (! runningOnJDK1_4) ) 4093 return 0; 4094 if((index == 0) && getMnemonic(ac)==null) {// special case when there is no mnemonic 4095 KeyStroke keyStroke = getAccelerator(ac); 4096 if (keyStroke != null) { 4097 return getModifiers(keyStroke); 4098 } 4099 } 4100 if (index == 0) { // mnemonic 4101 KeyStroke keyStroke = getMnemonic(ac); 4102 if (keyStroke != null) { 4103 return getModifiers(keyStroke); 4104 } 4105 } else if (index == 1) { // accelerator 4106 KeyStroke keyStroke = getAccelerator(ac); 4107 if (keyStroke != null) { 4108 return getModifiers(keyStroke); 4109 } 4110 } 4111 return 0; 4112 } 4113 4114 // ========== AccessibleIcon ============ 4115 4116 /* 4117 * return the number of icons associated with this context 4118 */ 4119 private int getAccessibleIconsCount(final AccessibleContext ac) { 4120 debugString("[INFO]: getAccessibleIconsCount"); 4121 if (ac == null) { 4122 return 0; 4123 } 4124 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 4125 @Override 4126 public Integer call() throws Exception { 4127 AccessibleIcon[] ai = ac.getAccessibleIcon(); 4128 if (ai == null) { 4129 return 0; 4130 } 4131 return ai.length; 4132 } 4133 }, ac); 4134 } 4135 4136 /* 4137 * return icon description at the specified index 4138 */ 4139 private String getAccessibleIconDescription(final AccessibleContext ac, final int index) { 4140 debugString("[INFO]: getAccessibleIconDescription: index = "+index); 4141 if (ac == null) { 4142 return null; 4143 } 4144 return InvocationUtils.invokeAndWait(new Callable<String>() { 4145 @Override 4146 public String call() throws Exception { 4147 AccessibleIcon[] ai = ac.getAccessibleIcon(); 4148 if (ai == null || index < 0 || index >= ai.length) { 4149 return null; 4150 } 4151 return ai[index].getAccessibleIconDescription(); 4152 } 4153 }, ac); 4154 } 4155 4156 /* 4157 * return icon height at the specified index 4158 */ 4159 private int getAccessibleIconHeight(final AccessibleContext ac, final int index) { 4160 debugString("[INFO]: getAccessibleIconHeight: index = "+index); 4161 if (ac == null) { 4162 return 0; 4163 } 4164 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 4165 @Override 4166 public Integer call() throws Exception { 4167 AccessibleIcon[] ai = ac.getAccessibleIcon(); 4168 if (ai == null || index < 0 || index >= ai.length) { 4169 return 0; 4170 } 4171 return ai[index].getAccessibleIconHeight(); 4172 } 4173 }, ac); 4174 } 4175 4176 /* 4177 * return icon width at the specified index 4178 */ 4179 private int getAccessibleIconWidth(final AccessibleContext ac, final int index) { 4180 debugString("[INFO]: getAccessibleIconWidth: index = "+index); 4181 if (ac == null) { 4182 return 0; 4183 } 4184 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 4185 @Override 4186 public Integer call() throws Exception { 4187 AccessibleIcon[] ai = ac.getAccessibleIcon(); 4188 if (ai == null || index < 0 || index >= ai.length) { 4189 return 0; 4190 } 4191 return ai[index].getAccessibleIconWidth(); 4192 } 4193 }, ac); 4194 } 4195 4196 // ========= AccessibleAction =========== 4197 4198 /* 4199 * return the number of icons associated with this context 4200 */ 4201 private int getAccessibleActionsCount(final AccessibleContext ac) { 4202 debugString("[INFO]: getAccessibleActionsCount"); 4203 if (ac == null) { 4204 return 0; 4205 } 4206 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 4207 @Override 4208 public Integer call() throws Exception { 4209 AccessibleAction aa = ac.getAccessibleAction(); 4210 if (aa == null) 4211 return 0; 4212 return aa.getAccessibleActionCount(); 4213 } 4214 }, ac); 4215 } 4216 4217 /* 4218 * return icon description at the specified index 4219 */ 4220 private String getAccessibleActionName(final AccessibleContext ac, final int index) { 4221 debugString("[INFO]: getAccessibleActionName: index = "+index); 4222 if (ac == null) { 4223 return null; 4224 } 4225 return InvocationUtils.invokeAndWait(new Callable<String>() { 4226 @Override 4227 public String call() throws Exception { 4228 AccessibleAction aa = ac.getAccessibleAction(); 4229 if (aa == null) { 4230 return null; 4231 } 4232 return aa.getAccessibleActionDescription(index); 4233 } 4234 }, ac); 4235 } 4236 /* 4237 * return icon description at the specified index 4238 */ 4239 private boolean doAccessibleActions(final AccessibleContext ac, final String name) { 4240 debugString("[INFO]: doAccessibleActions: action name = "+name); 4241 if (ac == null || name == null) { 4242 return false; 4243 } 4244 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4245 @Override 4246 public Boolean call() throws Exception { 4247 AccessibleAction aa = ac.getAccessibleAction(); 4248 if (aa == null) { 4249 return false; 4250 } 4251 int index = -1; 4252 int numActions = aa.getAccessibleActionCount(); 4253 for (int i = 0; i < numActions; i++) { 4254 String actionName = aa.getAccessibleActionDescription(i); 4255 if (name.equals(actionName)) { 4256 index = i; 4257 break; 4258 } 4259 } 4260 if (index == -1) { 4261 return false; 4262 } 4263 boolean retval = aa.doAccessibleAction(index); 4264 return retval; 4265 } 4266 }, ac); 4267 } 4268 4269 /* ===== AT utility methods ===== */ 4270 4271 /** 4272 * Sets the contents of an AccessibleContext that 4273 * implements AccessibleEditableText with the 4274 * specified text string. 4275 * Returns whether successful. 4276 */ 4277 private boolean setTextContents(final AccessibleContext ac, final String text) { 4278 debugString("[INFO]: setTextContents: ac = "+ac+"; text = "+text); 4279 4280 if (! (ac instanceof AccessibleEditableText)) { 4281 debugString("[WARN]: ac not instanceof AccessibleEditableText: "+ac); 4282 return false; 4283 } 4284 if (text == null) { 4285 debugString("[WARN]: text is null"); 4286 return false; 4287 } 4288 4289 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4290 @Override 4291 public Boolean call() throws Exception { 4292 // check whether the text field is editable 4293 AccessibleStateSet ass = ac.getAccessibleStateSet(); 4294 if (!ass.contains(AccessibleState.ENABLED)) { 4295 return false; 4296 } 4297 ((AccessibleEditableText) ac).setTextContents(text); 4298 return true; 4299 } 4300 }, ac); 4301 } 4302 4303 /** 4304 * Returns the Accessible Context of an Internal Frame object that is 4305 * the ancestor of a given object. If the object is an Internal Frame 4306 * object or an Internal Frame ancestor object was found, returns the 4307 * object's AccessibleContext. 4308 * If there is no ancestor object that has an Accessible Role of 4309 * Internal Frame, returns (AccessibleContext)0. 4310 */ 4311 private AccessibleContext getInternalFrame (AccessibleContext ac) { 4312 return getParentWithRole(ac, AccessibleRole.INTERNAL_FRAME.toString()); 4313 } 4314 4315 /** 4316 * Returns the Accessible Context for the top level object in 4317 * a Java Window. This is same Accessible Context that is obtained 4318 * from GetAccessibleContextFromHWND for that window. Returns 4319 * (AccessibleContext)0 on error. 4320 */ 4321 private AccessibleContext getTopLevelObject (final AccessibleContext ac) { 4322 debugString("[INFO]: getTopLevelObject; ac = "+ac); 4323 if (ac == null) { 4324 return null; 4325 } 4326 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4327 @Override 4328 public AccessibleContext call() throws Exception { 4329 if (ac.getAccessibleRole() == AccessibleRole.DIALOG) { 4330 // return the dialog, not the parent window 4331 return ac; 4332 } 4333 4334 Accessible parent = ac.getAccessibleParent(); 4335 if (parent == null) { 4336 return ac; 4337 } 4338 Accessible tmp = parent; 4339 while (tmp != null && tmp.getAccessibleContext() != null) { 4340 AccessibleContext ac2 = tmp.getAccessibleContext(); 4341 if (ac2 != null && ac2.getAccessibleRole() == AccessibleRole.DIALOG) { 4342 // return the dialog, not the parent window 4343 return ac2; 4344 } 4345 parent = tmp; 4346 tmp = parent.getAccessibleContext().getAccessibleParent(); 4347 } 4348 return parent.getAccessibleContext(); 4349 } 4350 }, ac); 4351 } 4352 4353 /** 4354 * Returns the parent AccessibleContext that has the specified AccessibleRole. 4355 * Returns null on error or if the AccessibleContext does not exist. 4356 */ 4357 private AccessibleContext getParentWithRole (final AccessibleContext ac, 4358 final String roleName) { 4359 debugString("[INFO]: getParentWithRole; ac = "+ac + "\n role = "+roleName); 4360 if (ac == null || roleName == null) { 4361 return null; 4362 } 4363 4364 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4365 @Override 4366 public AccessibleContext call() throws Exception { 4367 AccessibleRole role = AccessBridge.this.accessibleRoleMap.get(roleName); 4368 if (role == null) { 4369 return ac; 4370 } 4371 4372 Accessible parent = ac.getAccessibleParent(); 4373 if (parent == null && ac.getAccessibleRole() == role) { 4374 return ac; 4375 } 4376 4377 Accessible tmp = parent; 4378 AccessibleContext tmp_ac = null; 4379 4380 while (tmp != null && (tmp_ac = tmp.getAccessibleContext()) != null) { 4381 AccessibleRole ar = tmp_ac.getAccessibleRole(); 4382 if (ar == role) { 4383 // found 4384 return tmp_ac; 4385 } 4386 parent = tmp; 4387 tmp = parent.getAccessibleContext().getAccessibleParent(); 4388 } 4389 // not found 4390 return null; 4391 } 4392 }, ac); 4393 } 4394 4395 /** 4396 * Returns the parent AccessibleContext that has the specified AccessibleRole. 4397 * Otherwise, returns the top level object for the Java Window. 4398 * Returns (AccessibleContext)0 on error. 4399 */ 4400 private AccessibleContext getParentWithRoleElseRoot (AccessibleContext ac, 4401 String roleName) { 4402 AccessibleContext retval = getParentWithRole(ac, roleName); 4403 if (retval == null) { 4404 retval = getTopLevelObject(ac); 4405 } 4406 return retval; 4407 } 4408 4409 /** 4410 * Returns how deep in the object hierarchy a given object is. 4411 * The top most object in the object hierarchy has an object depth of 0. 4412 * Returns -1 on error. 4413 */ 4414 private int getObjectDepth(final AccessibleContext ac) { 4415 debugString("[INFO]: getObjectDepth: ac = "+ac); 4416 4417 if (ac == null) { 4418 return -1; 4419 } 4420 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 4421 @Override 4422 public Integer call() throws Exception { 4423 int count = 0; 4424 Accessible parent = ac.getAccessibleParent(); 4425 if (parent == null) { 4426 return count; 4427 } 4428 Accessible tmp = parent; 4429 while (tmp != null && tmp.getAccessibleContext() != null) { 4430 parent = tmp; 4431 tmp = parent.getAccessibleContext().getAccessibleParent(); 4432 count++; 4433 } 4434 return count; 4435 } 4436 }, ac); 4437 } 4438 4439 /** 4440 * Returns the Accessible Context of the current ActiveDescendent of an object. 4441 * Returns (AccessibleContext)0 on error. 4442 */ 4443 private AccessibleContext getActiveDescendent (final AccessibleContext ac) { 4444 debugString("[INFO]: getActiveDescendent: ac = "+ac); 4445 if (ac == null) { 4446 return null; 4447 } 4448 // workaround for JTree bug where the only possible active 4449 // descendent is the JTree root 4450 final Accessible parent = InvocationUtils.invokeAndWait(new Callable<Accessible>() { 4451 @Override 4452 public Accessible call() throws Exception { 4453 return ac.getAccessibleParent(); 4454 } 4455 }, ac); 4456 4457 if (parent != null) { 4458 Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() { 4459 @Override 4460 public Accessible call() throws Exception { 4461 int indexInParent = ac.getAccessibleIndexInParent(); 4462 return parent.getAccessibleContext().getAccessibleChild(indexInParent); 4463 } 4464 }, ac); 4465 4466 if (child instanceof JTree) { 4467 // return the selected node 4468 final JTree tree = (JTree)child; 4469 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4470 @Override 4471 public AccessibleContext call() throws Exception { 4472 return new AccessibleJTreeNode(tree, 4473 tree.getSelectionPath(), 4474 null); 4475 } 4476 }, child); 4477 } 4478 } 4479 4480 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4481 @Override 4482 public AccessibleContext call() throws Exception { 4483 AccessibleSelection as = ac.getAccessibleSelection(); 4484 if (as == null) { 4485 return null; 4486 } 4487 // assume single selection 4488 if (as.getAccessibleSelectionCount() != 1) { 4489 return null; 4490 } 4491 Accessible a = as.getAccessibleSelection(0); 4492 if (a == null) { 4493 return null; 4494 } 4495 return a.getAccessibleContext(); 4496 } 4497 }, ac); 4498 } 4499 4500 4501 /** 4502 * Additional methods for Teton 4503 */ 4504 4505 /** 4506 * Gets the AccessibleName for a component based upon the JAWS algorithm. 4507 * Returns whether successful. 4508 * 4509 * Bug ID 4916682 - Implement JAWS AccessibleName policy 4510 */ 4511 private String getJAWSAccessibleName(final AccessibleContext ac) { 4512 debugString("[INFO]: getJAWSAccessibleName"); 4513 if (ac == null) { 4514 return null; 4515 } 4516 // placeholder 4517 return InvocationUtils.invokeAndWait(new Callable<String>() { 4518 @Override 4519 public String call() throws Exception { 4520 return ac.getAccessibleName(); 4521 } 4522 }, ac); 4523 } 4524 4525 /** 4526 * Request focus for a component. Returns whether successful; 4527 * 4528 * Bug ID 4944757 - requestFocus method needed 4529 */ 4530 private boolean requestFocus(final AccessibleContext ac) { 4531 debugString("[INFO]: requestFocus"); 4532 if (ac == null) { 4533 return false; 4534 } 4535 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4536 @Override 4537 public Boolean call() throws Exception { 4538 AccessibleComponent acomp = ac.getAccessibleComponent(); 4539 if (acomp == null) { 4540 return false; 4541 } 4542 acomp.requestFocus(); 4543 return ac.getAccessibleStateSet().contains(AccessibleState.FOCUSED); 4544 } 4545 }, ac); 4546 } 4547 4548 /** 4549 * Selects text between two indices. Selection includes the 4550 * text at the start index and the text at the end index. Returns 4551 * whether successful; 4552 * 4553 * Bug ID 4944758 - selectTextRange method needed 4554 */ 4555 private boolean selectTextRange(final AccessibleContext ac, final int startIndex, final int endIndex) { 4556 debugString("[INFO]: selectTextRange: start = "+startIndex+"; end = "+endIndex); 4557 if (ac == null) { 4558 return false; 4559 } 4560 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4561 @Override 4562 public Boolean call() throws Exception { 4563 AccessibleText at = ac.getAccessibleText(); 4564 if (!(at instanceof AccessibleEditableText)) { 4565 return false; 4566 } 4567 ((AccessibleEditableText) at).selectText(startIndex, endIndex); 4568 4569 boolean result = at.getSelectionStart() == startIndex && 4570 at.getSelectionEnd() == endIndex; 4571 return result; 4572 } 4573 }, ac); 4574 } 4575 4576 /** 4577 * Set the caret to a text position. Returns whether successful; 4578 * 4579 * Bug ID 4944770 - setCaretPosition method needed 4580 */ 4581 private boolean setCaretPosition(final AccessibleContext ac, final int position) { 4582 debugString("[INFO]: setCaretPosition: position = "+position); 4583 if (ac == null) { 4584 return false; 4585 } 4586 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4587 @Override 4588 public Boolean call() throws Exception { 4589 AccessibleText at = ac.getAccessibleText(); 4590 if (!(at instanceof AccessibleEditableText)) { 4591 return false; 4592 } 4593 ((AccessibleEditableText) at).selectText(position, position); 4594 return at.getCaretPosition() == position; 4595 } 4596 }, ac); 4597 } 4598 4599 /** 4600 * Gets the number of visible children of an AccessibleContext. 4601 * 4602 * Bug ID 4944762- getVisibleChildren for list-like components needed 4603 */ 4604 private int _visibleChildrenCount; 4605 private AccessibleContext _visibleChild; 4606 private int _currentVisibleIndex; 4607 private boolean _foundVisibleChild; 4608 4609 private int getVisibleChildrenCount(AccessibleContext ac) { 4610 debugString("[INFO]: getVisibleChildrenCount"); 4611 if (ac == null) { 4612 return -1; 4613 } 4614 _visibleChildrenCount = 0; 4615 _getVisibleChildrenCount(ac); 4616 debugString("[INFO]: _visibleChildrenCount = "+_visibleChildrenCount); 4617 return _visibleChildrenCount; 4618 } 4619 4620 /* 4621 * Recursively descends AccessibleContext and gets the number 4622 * of visible children 4623 */ 4624 private void _getVisibleChildrenCount(final AccessibleContext ac) { 4625 if (ac == null) 4626 return; 4627 if(ac instanceof AccessibleExtendedTable) { 4628 _getVisibleChildrenCount((AccessibleExtendedTable)ac); 4629 return; 4630 } 4631 int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4632 @Override 4633 public Integer call() throws Exception { 4634 return ac.getAccessibleChildrenCount(); 4635 } 4636 }, ac); 4637 for (int i = 0; i < numChildren; i++) { 4638 final int idx = i; 4639 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4640 @Override 4641 public AccessibleContext call() throws Exception { 4642 Accessible a = ac.getAccessibleChild(idx); 4643 if (a != null) 4644 return a.getAccessibleContext(); 4645 else 4646 return null; 4647 } 4648 }, ac); 4649 if ( ac2 == null || 4650 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4651 @Override 4652 public Boolean call() throws Exception { 4653 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4654 } 4655 }, ac)) 4656 ) { 4657 continue; 4658 } 4659 _visibleChildrenCount++; 4660 4661 if (InvocationUtils.invokeAndWait(new Callable<Integer>() { 4662 @Override 4663 public Integer call() throws Exception { 4664 return ac2.getAccessibleChildrenCount(); 4665 } 4666 }, ac) > 0 ) { 4667 _getVisibleChildrenCount(ac2); 4668 } 4669 } 4670 } 4671 4672 /* 4673 * Recursively descends AccessibleContext and gets the number 4674 * of visible children. Stops search if get to invisible part of table. 4675 */ 4676 private void _getVisibleChildrenCount(final AccessibleExtendedTable acTable) { 4677 if (acTable == null) 4678 return; 4679 int lastVisibleRow = -1; 4680 int lastVisibleColumn = -1; 4681 boolean foundVisible = false; 4682 int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4683 @Override 4684 public Integer call() throws Exception { 4685 return acTable.getAccessibleRowCount(); 4686 } 4687 }, acTable); 4688 int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4689 @Override 4690 public Integer call() throws Exception { 4691 return acTable.getAccessibleColumnCount(); 4692 } 4693 }, acTable); 4694 for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) { 4695 for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) { 4696 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) { 4697 continue; 4698 } 4699 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) { 4700 continue; 4701 } 4702 int finalRowIdx = rowIdx; 4703 int finalColumnIdx = columnIdx; 4704 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4705 @Override 4706 public AccessibleContext call() throws Exception { 4707 Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx); 4708 if (a == null) 4709 return null; 4710 else 4711 return a.getAccessibleContext(); 4712 } 4713 }, acTable); 4714 if (ac2 == null || 4715 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4716 @Override 4717 public Boolean call() throws Exception { 4718 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4719 } 4720 }, acTable)) 4721 ) { 4722 if (foundVisible) { 4723 if (columnIdx != 0 && lastVisibleColumn == -1) { 4724 //the same row, so we found the last visible column 4725 lastVisibleColumn = columnIdx - 1; 4726 } else if (columnIdx == 0 && lastVisibleRow == -1) { 4727 lastVisibleRow = rowIdx - 1; 4728 } 4729 } 4730 continue; 4731 } 4732 4733 foundVisible = true; 4734 4735 _visibleChildrenCount++; 4736 4737 if (InvocationUtils.invokeAndWait(new Callable<Integer>() { 4738 @Override 4739 public Integer call() throws Exception { 4740 return ac2.getAccessibleChildrenCount(); 4741 } 4742 }, acTable) > 0) { 4743 _getVisibleChildrenCount(ac2); 4744 } 4745 } 4746 } 4747 } 4748 4749 /** 4750 * Gets the visible child of an AccessibleContext at the 4751 * specified index 4752 * 4753 * Bug ID 4944762- getVisibleChildren for list-like components needed 4754 */ 4755 private AccessibleContext getVisibleChild(AccessibleContext ac, int index) { 4756 debugString("[INFO]: getVisibleChild: index = "+index); 4757 if (ac == null) { 4758 return null; 4759 } 4760 _visibleChild = null; 4761 _currentVisibleIndex = 0; 4762 _foundVisibleChild = false; 4763 _getVisibleChild(ac, index); 4764 4765 if (_visibleChild != null) { 4766 debugString( "[INFO]: getVisibleChild: found child = " + 4767 InvocationUtils.invokeAndWait(new Callable<String>() { 4768 @Override 4769 public String call() throws Exception { 4770 return AccessBridge.this._visibleChild.getAccessibleName(); 4771 } 4772 }, ac) ); 4773 } 4774 return _visibleChild; 4775 } 4776 4777 /* 4778 * Recursively searchs AccessibleContext and finds the visible component 4779 * at the specified index 4780 */ 4781 private void _getVisibleChild(final AccessibleContext ac, final int index) { 4782 if (_visibleChild != null) { 4783 return; 4784 } 4785 if(ac instanceof AccessibleExtendedTable) { 4786 _getVisibleChild((AccessibleExtendedTable)ac, index); 4787 return; 4788 } 4789 int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4790 @Override 4791 public Integer call() throws Exception { 4792 return ac.getAccessibleChildrenCount(); 4793 } 4794 }, ac); 4795 for (int i = 0; i < numChildren; i++) { 4796 final int idx=i; 4797 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4798 @Override 4799 public AccessibleContext call() throws Exception { 4800 Accessible a = ac.getAccessibleChild(idx); 4801 if (a == null) 4802 return null; 4803 else 4804 return a.getAccessibleContext(); 4805 } 4806 }, ac); 4807 if (ac2 == null || 4808 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4809 @Override 4810 public Boolean call() throws Exception { 4811 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4812 } 4813 }, ac))) { 4814 continue; 4815 } 4816 if (!_foundVisibleChild && _currentVisibleIndex == index) { 4817 _visibleChild = ac2; 4818 _foundVisibleChild = true; 4819 return; 4820 } 4821 _currentVisibleIndex++; 4822 4823 if ( InvocationUtils.invokeAndWait(new Callable<Integer>() { 4824 @Override 4825 public Integer call() throws Exception { 4826 return ac2.getAccessibleChildrenCount(); 4827 } 4828 }, ac) > 0 ) { 4829 _getVisibleChild(ac2, index); 4830 } 4831 } 4832 } 4833 4834 private void _getVisibleChild(final AccessibleExtendedTable acTable, final int index) { 4835 if (_visibleChild != null) { 4836 return; 4837 } 4838 int lastVisibleRow = -1; 4839 int lastVisibleColumn = -1; 4840 boolean foundVisible = false; 4841 int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4842 @Override 4843 public Integer call() throws Exception { 4844 return acTable.getAccessibleRowCount(); 4845 } 4846 }, acTable); 4847 int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4848 @Override 4849 public Integer call() throws Exception { 4850 return acTable.getAccessibleColumnCount(); 4851 } 4852 }, acTable); 4853 for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) { 4854 for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) { 4855 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) { 4856 continue; 4857 } 4858 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) { 4859 continue; 4860 } 4861 int finalRowIdx = rowIdx; 4862 int finalColumnIdx = columnIdx; 4863 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4864 @Override 4865 public AccessibleContext call() throws Exception { 4866 Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx); 4867 if (a == null) 4868 return null; 4869 else 4870 return a.getAccessibleContext(); 4871 } 4872 }, acTable); 4873 if (ac2 == null || 4874 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4875 @Override 4876 public Boolean call() throws Exception { 4877 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4878 } 4879 }, acTable))) { 4880 if (foundVisible) { 4881 if (columnIdx != 0 && lastVisibleColumn == -1) { 4882 //the same row, so we found the last visible column 4883 lastVisibleColumn = columnIdx - 1; 4884 } else if (columnIdx == 0 && lastVisibleRow == -1) { 4885 lastVisibleRow = rowIdx - 1; 4886 } 4887 } 4888 continue; 4889 } 4890 foundVisible = true; 4891 4892 if (!_foundVisibleChild && _currentVisibleIndex == index) { 4893 _visibleChild = ac2; 4894 _foundVisibleChild = true; 4895 return; 4896 } 4897 _currentVisibleIndex++; 4898 4899 if (InvocationUtils.invokeAndWait(new Callable<Integer>() { 4900 @Override 4901 public Integer call() throws Exception { 4902 return ac2.getAccessibleChildrenCount(); 4903 } 4904 }, acTable) > 0) { 4905 _getVisibleChild(ac2, index); 4906 } 4907 } 4908 } 4909 } 4910 4911 /* ===== Java object memory management code ===== */ 4912 4913 /** 4914 * Class to track object references to ensure the 4915 * Java VM doesn't garbage collect them 4916 */ 4917 private class ObjectReferences { 4918 4919 private class Reference { 4920 private int value; 4921 4922 Reference(int i) { 4923 value = i; 4924 } 4925 4926 public String toString() { 4927 return ("refCount: " + value); 4928 } 4929 } 4930 4931 /** 4932 * table object references, to keep 'em from being garbage collected 4933 */ 4934 private ConcurrentHashMap<Object,Reference> refs; 4935 4936 /** 4937 * Constructor 4938 */ 4939 ObjectReferences() { 4940 refs = new ConcurrentHashMap<>(4); 4941 } 4942 4943 /** 4944 * Debugging: dump the contents of ObjectReferences' refs Hashtable 4945 */ 4946 String dump() { 4947 return refs.toString(); 4948 } 4949 4950 /** 4951 * Increment ref count; set to 1 if we have no references for it 4952 */ 4953 void increment(Object o) { 4954 if (o == null){ 4955 debugString("[WARN]: ObjectReferences::increment - Passed in object is null"); 4956 return; 4957 } 4958 4959 if (refs.containsKey(o)) { 4960 (refs.get(o)).value++; 4961 } else { 4962 refs.put(o, new Reference(1)); 4963 } 4964 } 4965 4966 /** 4967 * Decrement ref count; remove if count drops to 0 4968 */ 4969 void decrement(Object o) { 4970 Reference aRef = refs.get(o); 4971 if (aRef != null) { 4972 aRef.value--; 4973 if (aRef.value == 0) { 4974 refs.remove(o); 4975 } else if (aRef.value < 0) { 4976 debugString("[ERROR]: decrementing reference count below 0"); 4977 } 4978 } else { 4979 debugString("[ERROR]: object to decrement not in ObjectReferences table"); 4980 } 4981 } 4982 4983 } 4984 4985 /* ===== event handling code ===== */ 4986 4987 /** 4988 * native method for handling property change events 4989 */ 4990 private native void propertyCaretChange(PropertyChangeEvent e, 4991 AccessibleContext src, 4992 int oldValue, int newValue); 4993 private native void propertyDescriptionChange(PropertyChangeEvent e, 4994 AccessibleContext src, 4995 String oldValue, String newValue); 4996 private native void propertyNameChange(PropertyChangeEvent e, 4997 AccessibleContext src, 4998 String oldValue, String newValue); 4999 private native void propertySelectionChange(PropertyChangeEvent e, 5000 AccessibleContext src); 5001 private native void propertyStateChange(PropertyChangeEvent e, 5002 AccessibleContext src, 5003 String oldValue, String newValue); 5004 private native void propertyTextChange(PropertyChangeEvent e, 5005 AccessibleContext src); 5006 private native void propertyValueChange(PropertyChangeEvent e, 5007 AccessibleContext src, 5008 String oldValue, String newValue); 5009 private native void propertyVisibleDataChange(PropertyChangeEvent e, 5010 AccessibleContext src); 5011 private native void propertyChildChange(PropertyChangeEvent e, 5012 AccessibleContext src, 5013 AccessibleContext oldValue, 5014 AccessibleContext newValue); 5015 private native void propertyActiveDescendentChange(PropertyChangeEvent e, 5016 AccessibleContext src, 5017 AccessibleContext oldValue, 5018 AccessibleContext newValue); 5019 5020 private native void javaShutdown(); 5021 5022 /** 5023 * native methods for handling focus events 5024 */ 5025 private native void focusGained(FocusEvent e, AccessibleContext src); 5026 private native void focusLost(FocusEvent e, AccessibleContext src); 5027 5028 /** 5029 * native method for handling caret events 5030 */ 5031 private native void caretUpdate(CaretEvent e, AccessibleContext src); 5032 5033 /** 5034 * native methods for handling mouse events 5035 */ 5036 private native void mouseClicked(MouseEvent e, AccessibleContext src); 5037 private native void mouseEntered(MouseEvent e, AccessibleContext src); 5038 private native void mouseExited(MouseEvent e, AccessibleContext src); 5039 private native void mousePressed(MouseEvent e, AccessibleContext src); 5040 private native void mouseReleased(MouseEvent e, AccessibleContext src); 5041 5042 /** 5043 * native methods for handling menu & popupMenu events 5044 */ 5045 private native void menuCanceled(MenuEvent e, AccessibleContext src); 5046 private native void menuDeselected(MenuEvent e, AccessibleContext src); 5047 private native void menuSelected(MenuEvent e, AccessibleContext src); 5048 private native void popupMenuCanceled(PopupMenuEvent e, AccessibleContext src); 5049 private native void popupMenuWillBecomeInvisible(PopupMenuEvent e, 5050 AccessibleContext src); 5051 private native void popupMenuWillBecomeVisible(PopupMenuEvent e, 5052 AccessibleContext src); 5053 5054 /* ===== event definitions ===== */ 5055 5056 private static final long PROPERTY_CHANGE_EVENTS = 1; 5057 private static final long FOCUS_GAINED_EVENTS = 2; 5058 private static final long FOCUS_LOST_EVENTS = 4; 5059 private static final long FOCUS_EVENTS = (FOCUS_GAINED_EVENTS | FOCUS_LOST_EVENTS); 5060 5061 private static final long CARET_UPATE_EVENTS = 8; 5062 private static final long CARET_EVENTS = CARET_UPATE_EVENTS; 5063 5064 private static final long MOUSE_CLICKED_EVENTS = 16; 5065 private static final long MOUSE_ENTERED_EVENTS = 32; 5066 private static final long MOUSE_EXITED_EVENTS = 64; 5067 private static final long MOUSE_PRESSED_EVENTS = 128; 5068 private static final long MOUSE_RELEASED_EVENTS = 256; 5069 private static final long MOUSE_EVENTS = (MOUSE_CLICKED_EVENTS | MOUSE_ENTERED_EVENTS | 5070 MOUSE_EXITED_EVENTS | MOUSE_PRESSED_EVENTS | 5071 MOUSE_RELEASED_EVENTS); 5072 5073 private static final long MENU_CANCELED_EVENTS = 512; 5074 private static final long MENU_DESELECTED_EVENTS = 1024; 5075 private static final long MENU_SELECTED_EVENTS = 2048; 5076 private static final long MENU_EVENTS = (MENU_CANCELED_EVENTS | MENU_DESELECTED_EVENTS | 5077 MENU_SELECTED_EVENTS); 5078 5079 private static final long POPUPMENU_CANCELED_EVENTS = 4096; 5080 private static final long POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS = 8192; 5081 private static final long POPUPMENU_WILL_BECOME_VISIBLE_EVENTS = 16384; 5082 private static final long POPUPMENU_EVENTS = (POPUPMENU_CANCELED_EVENTS | 5083 POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS | 5084 POPUPMENU_WILL_BECOME_VISIBLE_EVENTS); 5085 5086 /* These use their own numbering scheme, to ensure sufficient expansion room */ 5087 private static final long PROPERTY_NAME_CHANGE_EVENTS = 1; 5088 private static final long PROPERTY_DESCRIPTION_CHANGE_EVENTS = 2; 5089 private static final long PROPERTY_STATE_CHANGE_EVENTS = 4; 5090 private static final long PROPERTY_VALUE_CHANGE_EVENTS = 8; 5091 private static final long PROPERTY_SELECTION_CHANGE_EVENTS = 16; 5092 private static final long PROPERTY_TEXT_CHANGE_EVENTS = 32; 5093 private static final long PROPERTY_CARET_CHANGE_EVENTS = 64; 5094 private static final long PROPERTY_VISIBLEDATA_CHANGE_EVENTS = 128; 5095 private static final long PROPERTY_CHILD_CHANGE_EVENTS = 256; 5096 private static final long PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS = 512; 5097 5098 5099 private static final long PROPERTY_EVENTS = (PROPERTY_NAME_CHANGE_EVENTS | 5100 PROPERTY_DESCRIPTION_CHANGE_EVENTS | 5101 PROPERTY_STATE_CHANGE_EVENTS | 5102 PROPERTY_VALUE_CHANGE_EVENTS | 5103 PROPERTY_SELECTION_CHANGE_EVENTS | 5104 PROPERTY_TEXT_CHANGE_EVENTS | 5105 PROPERTY_CARET_CHANGE_EVENTS | 5106 PROPERTY_VISIBLEDATA_CHANGE_EVENTS | 5107 PROPERTY_CHILD_CHANGE_EVENTS | 5108 PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS); 5109 5110 /** 5111 * The EventHandler class listens for Java events and 5112 * forwards them to the AT 5113 */ 5114 private class EventHandler implements PropertyChangeListener, 5115 FocusListener, CaretListener, 5116 MenuListener, PopupMenuListener, 5117 MouseListener, WindowListener, 5118 ChangeListener { 5119 5120 private AccessBridge accessBridge; 5121 private long javaEventMask = 0; 5122 private long accessibilityEventMask = 0; 5123 5124 EventHandler(AccessBridge bridge) { 5125 accessBridge = bridge; 5126 5127 // Register to receive WINDOW_OPENED and WINDOW_CLOSED 5128 // events. Add the event source as a native window 5129 // handler is it implements NativeWindowHandler. 5130 // SwingEventMonitor.addWindowListener(this); 5131 } 5132 5133 // --------- Event Notification Registration methods 5134 5135 /** 5136 * Invoked the first time a window is made visible. 5137 */ 5138 public void windowOpened(WindowEvent e) { 5139 // If the window is a NativeWindowHandler, add it. 5140 Object o = null; 5141 if (e != null) 5142 o = e.getSource(); 5143 if (o instanceof NativeWindowHandler) { 5144 addNativeWindowHandler((NativeWindowHandler)o); 5145 } 5146 } 5147 5148 /** 5149 * Invoked when the user attempts to close the window 5150 * from the window's system menu. If the program does not 5151 * explicitly hide or dispose the window while processing 5152 * this event, the window close operation will be canceled. 5153 */ 5154 public void windowClosing(WindowEvent e) {} 5155 5156 /** 5157 * Invoked when a window has been closed as the result 5158 * of calling dispose on the window. 5159 */ 5160 public void windowClosed(WindowEvent e) { 5161 // If the window is a NativeWindowHandler, remove it. 5162 Object o = null; 5163 if (e != null) 5164 o = e.getSource(); 5165 if (o instanceof NativeWindowHandler) { 5166 removeNativeWindowHandler((NativeWindowHandler)o); 5167 } 5168 } 5169 5170 /** 5171 * Invoked when a window is changed from a normal to a 5172 * minimized state. For many platforms, a minimized window 5173 * is displayed as the icon specified in the window's 5174 * iconImage property. 5175 * @see java.awt.Frame#setIconImage 5176 */ 5177 public void windowIconified(WindowEvent e) {} 5178 5179 /** 5180 * Invoked when a window is changed from a minimized 5181 * to a normal state. 5182 */ 5183 public void windowDeiconified(WindowEvent e) {} 5184 5185 /** 5186 * Invoked when the Window is set to be the active Window. Only a Frame or 5187 * a Dialog can be the active Window. The native windowing system may 5188 * denote the active Window or its children with special decorations, such 5189 * as a highlighted title bar. The active Window is always either the 5190 * focused Window, or the first Frame or Dialog that is an owner of the 5191 * focused Window. 5192 */ 5193 public void windowActivated(WindowEvent e) {} 5194 5195 /** 5196 * Invoked when a Window is no longer the active Window. Only a Frame or a 5197 * Dialog can be the active Window. The native windowing system may denote 5198 * the active Window or its children with special decorations, such as a 5199 * highlighted title bar. The active Window is always either the focused 5200 * Window, or the first Frame or Dialog that is an owner of the focused 5201 * Window. 5202 */ 5203 public void windowDeactivated(WindowEvent e) {} 5204 5205 /** 5206 * Turn on event monitoring for the event type passed in 5207 * If necessary, add the appropriate event listener (if 5208 * no other event of that type is being listened for) 5209 */ 5210 void addJavaEventNotification(long type) { 5211 long newEventMask = javaEventMask | type; 5212 /* 5213 if ( ((javaEventMask & PROPERTY_EVENTS) == 0) && 5214 ((newEventMask & PROPERTY_EVENTS) != 0) ) { 5215 AccessibilityEventMonitor.addPropertyChangeListener(this); 5216 } 5217 */ 5218 if ( ((javaEventMask & FOCUS_EVENTS) == 0) && 5219 ((newEventMask & FOCUS_EVENTS) != 0) ) { 5220 SwingEventMonitor.addFocusListener(this); 5221 } 5222 if ( ((javaEventMask & CARET_EVENTS) == 0) && 5223 ((newEventMask & CARET_EVENTS) != 0) ) { 5224 SwingEventMonitor.addCaretListener(this); 5225 } 5226 if ( ((javaEventMask & MOUSE_EVENTS) == 0) && 5227 ((newEventMask & MOUSE_EVENTS) != 0) ) { 5228 SwingEventMonitor.addMouseListener(this); 5229 } 5230 if ( ((javaEventMask & MENU_EVENTS) == 0) && 5231 ((newEventMask & MENU_EVENTS) != 0) ) { 5232 SwingEventMonitor.addMenuListener(this); 5233 SwingEventMonitor.addPopupMenuListener(this); 5234 } 5235 if ( ((javaEventMask & POPUPMENU_EVENTS) == 0) && 5236 ((newEventMask & POPUPMENU_EVENTS) != 0) ) { 5237 SwingEventMonitor.addPopupMenuListener(this); 5238 } 5239 5240 javaEventMask = newEventMask; 5241 } 5242 5243 /** 5244 * Turn off event monitoring for the event type passed in 5245 * If necessary, remove the appropriate event listener (if 5246 * no other event of that type is being listened for) 5247 */ 5248 void removeJavaEventNotification(long type) { 5249 long newEventMask = javaEventMask & (~type); 5250 /* 5251 if ( ((javaEventMask & PROPERTY_EVENTS) != 0) && 5252 ((newEventMask & PROPERTY_EVENTS) == 0) ) { 5253 AccessibilityEventMonitor.removePropertyChangeListener(this); 5254 } 5255 */ 5256 if (((javaEventMask & FOCUS_EVENTS) != 0) && 5257 ((newEventMask & FOCUS_EVENTS) == 0)) { 5258 SwingEventMonitor.removeFocusListener(this); 5259 } 5260 if (((javaEventMask & CARET_EVENTS) != 0) && 5261 ((newEventMask & CARET_EVENTS) == 0)) { 5262 SwingEventMonitor.removeCaretListener(this); 5263 } 5264 if (((javaEventMask & MOUSE_EVENTS) == 0) && 5265 ((newEventMask & MOUSE_EVENTS) != 0)) { 5266 SwingEventMonitor.removeMouseListener(this); 5267 } 5268 if (((javaEventMask & MENU_EVENTS) == 0) && 5269 ((newEventMask & MENU_EVENTS) != 0)) { 5270 SwingEventMonitor.removeMenuListener(this); 5271 } 5272 if (((javaEventMask & POPUPMENU_EVENTS) == 0) && 5273 ((newEventMask & POPUPMENU_EVENTS) != 0)) { 5274 SwingEventMonitor.removePopupMenuListener(this); 5275 } 5276 5277 javaEventMask = newEventMask; 5278 } 5279 5280 /** 5281 * Turn on event monitoring for the event type passed in 5282 * If necessary, add the appropriate event listener (if 5283 * no other event of that type is being listened for) 5284 */ 5285 void addAccessibilityEventNotification(long type) { 5286 long newEventMask = accessibilityEventMask | type; 5287 if ( ((accessibilityEventMask & PROPERTY_EVENTS) == 0) && 5288 ((newEventMask & PROPERTY_EVENTS) != 0) ) { 5289 AccessibilityEventMonitor.addPropertyChangeListener(this); 5290 } 5291 accessibilityEventMask = newEventMask; 5292 } 5293 5294 /** 5295 * Turn off event monitoring for the event type passed in 5296 * If necessary, remove the appropriate event listener (if 5297 * no other event of that type is being listened for) 5298 */ 5299 void removeAccessibilityEventNotification(long type) { 5300 long newEventMask = accessibilityEventMask & (~type); 5301 if ( ((accessibilityEventMask & PROPERTY_EVENTS) != 0) && 5302 ((newEventMask & PROPERTY_EVENTS) == 0) ) { 5303 AccessibilityEventMonitor.removePropertyChangeListener(this); 5304 } 5305 accessibilityEventMask = newEventMask; 5306 } 5307 5308 /** 5309 * ------- property change event glue 5310 */ 5311 // This is invoked on the EDT , as 5312 public void propertyChange(PropertyChangeEvent e) { 5313 5314 accessBridge.debugString("[INFO]: propertyChange(" + e.toString() + ") called"); 5315 5316 if (e != null && (accessibilityEventMask & PROPERTY_EVENTS) != 0) { 5317 Object o = e.getSource(); 5318 AccessibleContext ac; 5319 5320 if (o instanceof AccessibleContext) { 5321 ac = (AccessibleContext) o; 5322 } else { 5323 Accessible a = Translator.getAccessible(e.getSource()); 5324 if (a == null) 5325 return; 5326 else 5327 ac = a.getAccessibleContext(); 5328 } 5329 if (ac != null) { 5330 InvocationUtils.registerAccessibleContext(ac, AppContext.getAppContext()); 5331 5332 accessBridge.debugString("[INFO]: AccessibleContext: " + ac); 5333 String propertyName = e.getPropertyName(); 5334 5335 if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CARET_PROPERTY) == 0) { 5336 int oldValue = 0; 5337 int newValue = 0; 5338 5339 if (e.getOldValue() instanceof Integer) { 5340 oldValue = ((Integer) e.getOldValue()).intValue(); 5341 } 5342 if (e.getNewValue() instanceof Integer) { 5343 newValue = ((Integer) e.getNewValue()).intValue(); 5344 } 5345 accessBridge.debugString("[INFO]: - about to call propertyCaretChange() old value: " + oldValue + "new value: " + newValue); 5346 accessBridge.propertyCaretChange(e, ac, oldValue, newValue); 5347 5348 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY) == 0) { 5349 String oldValue = null; 5350 String newValue = null; 5351 5352 if (e.getOldValue() != null) { 5353 oldValue = e.getOldValue().toString(); 5354 } 5355 if (e.getNewValue() != null) { 5356 newValue = e.getNewValue().toString(); 5357 } 5358 accessBridge.debugString("[INFO]: - about to call propertyDescriptionChange() old value: " + oldValue + "new value: " + newValue); 5359 accessBridge.propertyDescriptionChange(e, ac, oldValue, newValue); 5360 5361 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_NAME_PROPERTY) == 0) { 5362 String oldValue = null; 5363 String newValue = null; 5364 5365 if (e.getOldValue() != null) { 5366 oldValue = e.getOldValue().toString(); 5367 } 5368 if (e.getNewValue() != null) { 5369 newValue = e.getNewValue().toString(); 5370 } 5371 accessBridge.debugString("[INFO]: - about to call propertyNameChange() old value: " + oldValue + " new value: " + newValue); 5372 accessBridge.propertyNameChange(e, ac, oldValue, newValue); 5373 5374 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY) == 0) { 5375 accessBridge.debugString("[INFO]: - about to call propertySelectionChange() " + ac + " " + Thread.currentThread() + " " + e.getSource()); 5376 5377 accessBridge.propertySelectionChange(e, ac); 5378 5379 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_STATE_PROPERTY) == 0) { 5380 String oldValue = null; 5381 String newValue = null; 5382 5383 // Localization fix requested by Oliver for EA-1 5384 if (e.getOldValue() != null) { 5385 AccessibleState oldState = (AccessibleState) e.getOldValue(); 5386 oldValue = oldState.toDisplayString(Locale.US); 5387 } 5388 if (e.getNewValue() != null) { 5389 AccessibleState newState = (AccessibleState) e.getNewValue(); 5390 newValue = newState.toDisplayString(Locale.US); 5391 } 5392 5393 accessBridge.debugString("[INFO]: - about to call propertyStateChange()"); 5394 accessBridge.propertyStateChange(e, ac, oldValue, newValue); 5395 5396 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_TEXT_PROPERTY) == 0) { 5397 accessBridge.debugString("[INFO]: - about to call propertyTextChange()"); 5398 accessBridge.propertyTextChange(e, ac); 5399 5400 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY) == 0) { // strings 'cause of floating point, etc. 5401 String oldValue = null; 5402 String newValue = null; 5403 5404 if (e.getOldValue() != null) { 5405 oldValue = e.getOldValue().toString(); 5406 } 5407 if (e.getNewValue() != null) { 5408 newValue = e.getNewValue().toString(); 5409 } 5410 accessBridge.debugString("[INFO]: - about to call propertyDescriptionChange()"); 5411 accessBridge.propertyValueChange(e, ac, oldValue, newValue); 5412 5413 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY) == 0) { 5414 accessBridge.propertyVisibleDataChange(e, ac); 5415 5416 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) { 5417 AccessibleContext oldAC = null; 5418 AccessibleContext newAC = null; 5419 Accessible a; 5420 5421 if (e.getOldValue() instanceof AccessibleContext) { 5422 oldAC = (AccessibleContext) e.getOldValue(); 5423 InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext()); 5424 } 5425 if (e.getNewValue() instanceof AccessibleContext) { 5426 newAC = (AccessibleContext) e.getNewValue(); 5427 InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext()); 5428 } 5429 accessBridge.debugString("[INFO]: - about to call propertyChildChange() old AC: " + oldAC + "new AC: " + newAC); 5430 accessBridge.propertyChildChange(e, ac, oldAC, newAC); 5431 5432 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0) { 5433 handleActiveDescendentEvent(e, ac); 5434 } 5435 } 5436 } 5437 } 5438 5439 /* 5440 * Handle an ActiveDescendent PropertyChangeEvent. This 5441 * method works around a JTree bug where ActiveDescendent 5442 * PropertyChangeEvents have the wrong parent. 5443 */ 5444 private AccessibleContext prevAC = null; // previous AccessibleContext 5445 5446 private void handleActiveDescendentEvent(PropertyChangeEvent e, 5447 AccessibleContext ac) { 5448 if (e == null || ac == null) 5449 return; 5450 AccessibleContext oldAC = null; 5451 AccessibleContext newAC = null; 5452 Accessible a; 5453 5454 // get the old active descendent 5455 if (e.getOldValue() instanceof Accessible) { 5456 oldAC = ((Accessible) e.getOldValue()).getAccessibleContext(); 5457 } else if (e.getOldValue() instanceof Component) { 5458 a = Translator.getAccessible(e.getOldValue()); 5459 if (a != null) { 5460 oldAC = a.getAccessibleContext(); 5461 } 5462 } 5463 if (oldAC != null) { 5464 Accessible parent = oldAC.getAccessibleParent(); 5465 if (parent instanceof JTree) { 5466 // use the previous AccessibleJTreeNode 5467 oldAC = prevAC; 5468 } 5469 } 5470 5471 // get the new active descendent 5472 if (e.getNewValue() instanceof Accessible) { 5473 newAC = ((Accessible) e.getNewValue()).getAccessibleContext(); 5474 } else if (e.getNewValue() instanceof Component) { 5475 a = Translator.getAccessible(e.getNewValue()); 5476 if (a != null) { 5477 newAC = a.getAccessibleContext(); 5478 } 5479 } 5480 if (newAC != null) { 5481 Accessible parent = newAC.getAccessibleParent(); 5482 if (parent instanceof JTree) { 5483 // use a new AccessibleJTreeNode with the right parent 5484 JTree tree = (JTree)parent; 5485 newAC = new AccessibleJTreeNode(tree, 5486 tree.getSelectionPath(), 5487 null); 5488 } 5489 } 5490 prevAC = newAC; 5491 5492 accessBridge.debugString("[INFO]: - about to call propertyActiveDescendentChange() AC: " + ac + " old AC: " + oldAC + "new AC: " + newAC); 5493 InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext()); 5494 InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext()); 5495 accessBridge.propertyActiveDescendentChange(e, ac, oldAC, newAC); 5496 } 5497 5498 /** 5499 * ------- focus event glue 5500 */ 5501 private boolean stateChangeListenerAdded = false; 5502 5503 public void focusGained(FocusEvent e) { 5504 if (runningOnJDK1_4) { 5505 processFocusGained(); 5506 } else { 5507 if ((javaEventMask & FOCUS_GAINED_EVENTS) != 0) { 5508 Accessible a = Translator.getAccessible(e.getSource()); 5509 if (a != null) { 5510 AccessibleContext context = a.getAccessibleContext(); 5511 InvocationUtils.registerAccessibleContext(context, SunToolkit.targetToAppContext(e.getSource())); 5512 accessBridge.focusGained(e, context); 5513 } 5514 } 5515 } 5516 } 5517 5518 public void stateChanged(ChangeEvent e) { 5519 processFocusGained(); 5520 } 5521 5522 private void processFocusGained() { 5523 Component focusOwner = KeyboardFocusManager. 5524 getCurrentKeyboardFocusManager().getFocusOwner(); 5525 if (focusOwner == null) { 5526 return; 5527 } 5528 5529 // Only menus and popup selections are handled by the JRootPane. 5530 if (focusOwner instanceof JRootPane) { 5531 MenuElement [] path = 5532 MenuSelectionManager.defaultManager().getSelectedPath(); 5533 if (path.length > 1) { 5534 Component penult = path[path.length-2].getComponent(); 5535 Component last = path[path.length-1].getComponent(); 5536 5537 if (last instanceof JPopupMenu) { 5538 // This is a popup with nothing in the popup 5539 // selected. The menu itself is selected. 5540 FocusEvent e = new FocusEvent(penult, FocusEvent.FOCUS_GAINED); 5541 AccessibleContext context = penult.getAccessibleContext(); 5542 InvocationUtils.registerAccessibleContext(context, SunToolkit.targetToAppContext(penult)); 5543 accessBridge.focusGained(e, context); 5544 } else if (penult instanceof JPopupMenu) { 5545 // This is a popup with an item selected 5546 FocusEvent e = 5547 new FocusEvent(last, FocusEvent.FOCUS_GAINED); 5548 AccessibleContext focusedAC = last.getAccessibleContext(); 5549 InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(last)); 5550 accessBridge.debugString("[INFO]: - about to call focusGained() AC: " + focusedAC); 5551 accessBridge.focusGained(e, focusedAC); 5552 } 5553 } 5554 } else { 5555 // The focus owner has the selection. 5556 if (focusOwner instanceof Accessible) { 5557 FocusEvent e = new FocusEvent(focusOwner, 5558 FocusEvent.FOCUS_GAINED); 5559 AccessibleContext focusedAC = focusOwner.getAccessibleContext(); 5560 InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(focusOwner)); 5561 accessBridge.debugString("[INFO]: - about to call focusGained() AC: " + focusedAC); 5562 accessBridge.focusGained(e, focusedAC); 5563 } 5564 } 5565 } 5566 5567 public void focusLost(FocusEvent e) { 5568 if (e != null && (javaEventMask & FOCUS_LOST_EVENTS) != 0) { 5569 Accessible a = Translator.getAccessible(e.getSource()); 5570 if (a != null) { 5571 accessBridge.debugString("[INFO]: - about to call focusLost() AC: " + a.getAccessibleContext()); 5572 AccessibleContext context = a.getAccessibleContext(); 5573 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5574 accessBridge.focusLost(e, context); 5575 } 5576 } 5577 } 5578 5579 /** 5580 * ------- caret event glue 5581 */ 5582 public void caretUpdate(CaretEvent e) { 5583 if (e != null && (javaEventMask & CARET_UPATE_EVENTS) != 0) { 5584 Accessible a = Translator.getAccessible(e.getSource()); 5585 if (a != null) { 5586 AccessibleContext context = a.getAccessibleContext(); 5587 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5588 accessBridge.caretUpdate(e, context); 5589 } 5590 } 5591 } 5592 5593 /** 5594 * ------- mouse event glue 5595 */ 5596 5597 public void mouseClicked(MouseEvent e) { 5598 if (e != null && (javaEventMask & MOUSE_CLICKED_EVENTS) != 0) { 5599 Accessible a = Translator.getAccessible(e.getSource()); 5600 if (a != null) { 5601 AccessibleContext context = a.getAccessibleContext(); 5602 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5603 accessBridge.mouseClicked(e, context); 5604 } 5605 } 5606 } 5607 5608 public void mouseEntered(MouseEvent e) { 5609 if (e != null && (javaEventMask & MOUSE_ENTERED_EVENTS) != 0) { 5610 Accessible a = Translator.getAccessible(e.getSource()); 5611 if (a != null) { 5612 AccessibleContext context = a.getAccessibleContext(); 5613 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5614 accessBridge.mouseEntered(e, context); 5615 } 5616 } 5617 } 5618 5619 public void mouseExited(MouseEvent e) { 5620 if (e != null && (javaEventMask & MOUSE_EXITED_EVENTS) != 0) { 5621 Accessible a = Translator.getAccessible(e.getSource()); 5622 if (a != null) { 5623 AccessibleContext context = a.getAccessibleContext(); 5624 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5625 accessBridge.mouseExited(e, context); 5626 } 5627 } 5628 } 5629 5630 public void mousePressed(MouseEvent e) { 5631 if (e != null && (javaEventMask & MOUSE_PRESSED_EVENTS) != 0) { 5632 Accessible a = Translator.getAccessible(e.getSource()); 5633 if (a != null) { 5634 AccessibleContext context = a.getAccessibleContext(); 5635 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5636 accessBridge.mousePressed(e, context); 5637 } 5638 } 5639 } 5640 5641 public void mouseReleased(MouseEvent e) { 5642 if (e != null && (javaEventMask & MOUSE_RELEASED_EVENTS) != 0) { 5643 Accessible a = Translator.getAccessible(e.getSource()); 5644 if (a != null) { 5645 AccessibleContext context = a.getAccessibleContext(); 5646 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5647 accessBridge.mouseReleased(e, context); 5648 } 5649 } 5650 } 5651 5652 /** 5653 * ------- menu event glue 5654 */ 5655 public void menuCanceled(MenuEvent e) { 5656 if (e != null && (javaEventMask & MENU_CANCELED_EVENTS) != 0) { 5657 Accessible a = Translator.getAccessible(e.getSource()); 5658 if (a != null) { 5659 AccessibleContext context = a.getAccessibleContext(); 5660 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5661 accessBridge.menuCanceled(e, context); 5662 } 5663 } 5664 } 5665 5666 public void menuDeselected(MenuEvent e) { 5667 if (e != null && (javaEventMask & MENU_DESELECTED_EVENTS) != 0) { 5668 Accessible a = Translator.getAccessible(e.getSource()); 5669 if (a != null) { 5670 AccessibleContext context = a.getAccessibleContext(); 5671 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5672 accessBridge.menuDeselected(e, context); 5673 } 5674 } 5675 } 5676 5677 public void menuSelected(MenuEvent e) { 5678 if (e != null && (javaEventMask & MENU_SELECTED_EVENTS) != 0) { 5679 Accessible a = Translator.getAccessible(e.getSource()); 5680 if (a != null) { 5681 AccessibleContext context = a.getAccessibleContext(); 5682 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5683 accessBridge.menuSelected(e, context); 5684 } 5685 } 5686 } 5687 5688 public void popupMenuCanceled(PopupMenuEvent e) { 5689 if (e != null && (javaEventMask & POPUPMENU_CANCELED_EVENTS) != 0) { 5690 Accessible a = Translator.getAccessible(e.getSource()); 5691 if (a != null) { 5692 AccessibleContext context = a.getAccessibleContext(); 5693 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5694 accessBridge.popupMenuCanceled(e, context); 5695 } 5696 } 5697 } 5698 5699 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { 5700 if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS) != 0) { 5701 Accessible a = Translator.getAccessible(e.getSource()); 5702 if (a != null) { 5703 AccessibleContext context = a.getAccessibleContext(); 5704 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5705 accessBridge.popupMenuWillBecomeInvisible(e, context); 5706 } 5707 } 5708 } 5709 5710 public void popupMenuWillBecomeVisible(PopupMenuEvent e) { 5711 if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_VISIBLE_EVENTS) != 0) { 5712 Accessible a = Translator.getAccessible(e.getSource()); 5713 if (a != null) { 5714 AccessibleContext context = a.getAccessibleContext(); 5715 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5716 accessBridge.popupMenuWillBecomeVisible(e, context); 5717 } 5718 } 5719 } 5720 5721 } // End of EventHandler Class 5722 5723 // --------- Event Notification Registration methods 5724 5725 /** 5726 * Wrapper method around eventHandler.addJavaEventNotification() 5727 */ 5728 private void addJavaEventNotification(final long type) { 5729 EventQueue.invokeLater(new Runnable() { 5730 public void run(){ 5731 eventHandler.addJavaEventNotification(type); 5732 } 5733 }); 5734 } 5735 5736 /** 5737 * Wrapper method around eventHandler.removeJavaEventNotification() 5738 */ 5739 private void removeJavaEventNotification(final long type) { 5740 EventQueue.invokeLater(new Runnable() { 5741 public void run(){ 5742 eventHandler.removeJavaEventNotification(type); 5743 } 5744 }); 5745 } 5746 5747 5748 /** 5749 * Wrapper method around eventHandler.addAccessibilityEventNotification() 5750 */ 5751 private void addAccessibilityEventNotification(final long type) { 5752 EventQueue.invokeLater(new Runnable() { 5753 public void run(){ 5754 eventHandler.addAccessibilityEventNotification(type); 5755 } 5756 }); 5757 } 5758 5759 /** 5760 * Wrapper method around eventHandler.removeAccessibilityEventNotification() 5761 */ 5762 private void removeAccessibilityEventNotification(final long type) { 5763 EventQueue.invokeLater(new Runnable() { 5764 public void run(){ 5765 eventHandler.removeAccessibilityEventNotification(type); 5766 } 5767 }); 5768 } 5769 5770 /** 5771 ****************************************************** 5772 * All AccessibleRoles 5773 * 5774 * We shouldn't have to do this since it requires us 5775 * to synchronize the allAccessibleRoles array when 5776 * the AccessibleRoles class interface changes. However, 5777 * there is no Accessibility API method to get all 5778 * AccessibleRoles 5779 ****************************************************** 5780 */ 5781 private AccessibleRole [] allAccessibleRoles = { 5782 /** 5783 * Object is used to alert the user about something. 5784 */ 5785 AccessibleRole.ALERT, 5786 5787 /** 5788 * The header for a column of data. 5789 */ 5790 AccessibleRole.COLUMN_HEADER, 5791 5792 /** 5793 * Object that can be drawn into and is used to trap 5794 * events. 5795 * @see #FRAME 5796 * @see #GLASS_PANE 5797 * @see #LAYERED_PANE 5798 */ 5799 AccessibleRole.CANVAS, 5800 5801 /** 5802 * A list of choices the user can select from. Also optionally 5803 * allows the user to enter a choice of their own. 5804 */ 5805 AccessibleRole.COMBO_BOX, 5806 5807 /** 5808 * An iconified internal frame in a DESKTOP_PANE. 5809 * @see #DESKTOP_PANE 5810 * @see #INTERNAL_FRAME 5811 */ 5812 AccessibleRole.DESKTOP_ICON, 5813 5814 /** 5815 * A frame-like object that is clipped by a desktop pane. The 5816 * desktop pane, internal frame, and desktop icon objects are 5817 * often used to create multiple document interfaces within an 5818 * application. 5819 * @see #DESKTOP_ICON 5820 * @see #DESKTOP_PANE 5821 * @see #FRAME 5822 */ 5823 AccessibleRole.INTERNAL_FRAME, 5824 5825 /** 5826 * A pane that supports internal frames and 5827 * iconified versions of those internal frames. 5828 * @see #DESKTOP_ICON 5829 * @see #INTERNAL_FRAME 5830 */ 5831 AccessibleRole.DESKTOP_PANE, 5832 5833 /** 5834 * A specialized pane whose primary use is inside a DIALOG 5835 * @see #DIALOG 5836 */ 5837 AccessibleRole.OPTION_PANE, 5838 5839 /** 5840 * A top level window with no title or border. 5841 * @see #FRAME 5842 * @see #DIALOG 5843 */ 5844 AccessibleRole.WINDOW, 5845 5846 /** 5847 * A top level window with a title bar, border, menu bar, etc. It is 5848 * often used as the primary window for an application. 5849 * @see #DIALOG 5850 * @see #CANVAS 5851 * @see #WINDOW 5852 */ 5853 AccessibleRole.FRAME, 5854 5855 /** 5856 * A top level window with title bar and a border. A dialog is similar 5857 * to a frame, but it has fewer properties and is often used as a 5858 * secondary window for an application. 5859 * @see #FRAME 5860 * @see #WINDOW 5861 */ 5862 AccessibleRole.DIALOG, 5863 5864 /** 5865 * A specialized dialog that lets the user choose a color. 5866 */ 5867 AccessibleRole.COLOR_CHOOSER, 5868 5869 5870 /** 5871 * A pane that allows the user to navigate through 5872 * and select the contents of a directory. May be used 5873 * by a file chooser. 5874 * @see #FILE_CHOOSER 5875 */ 5876 AccessibleRole.DIRECTORY_PANE, 5877 5878 /** 5879 * A specialized dialog that displays the files in the directory 5880 * and lets the user select a file, browse a different directory, 5881 * or specify a filename. May use the directory pane to show the 5882 * contents of a directory. 5883 * @see #DIRECTORY_PANE 5884 */ 5885 AccessibleRole.FILE_CHOOSER, 5886 5887 /** 5888 * An object that fills up space in a user interface. It is often 5889 * used in interfaces to tweak the spacing between components, 5890 * but serves no other purpose. 5891 */ 5892 AccessibleRole.FILLER, 5893 5894 /** 5895 * A hypertext anchor 5896 */ 5897 // AccessibleRole.HYPERLINK, 5898 5899 /** 5900 * A small fixed size picture, typically used to decorate components. 5901 */ 5902 AccessibleRole.ICON, 5903 5904 /** 5905 * An object used to present an icon or short string in an interface. 5906 */ 5907 AccessibleRole.LABEL, 5908 5909 /** 5910 * A specialized pane that has a glass pane and a layered pane as its 5911 * children. 5912 * @see #GLASS_PANE 5913 * @see #LAYERED_PANE 5914 */ 5915 AccessibleRole.ROOT_PANE, 5916 5917 /** 5918 * A pane that is guaranteed to be painted on top 5919 * of all panes beneath it. 5920 * @see #ROOT_PANE 5921 * @see #CANVAS 5922 */ 5923 AccessibleRole.GLASS_PANE, 5924 5925 /** 5926 * A specialized pane that allows its children to be drawn in layers, 5927 * providing a form of stacking order. This is usually the pane that 5928 * holds the menu bar as well as the pane that contains most of the 5929 * visual components in a window. 5930 * @see #GLASS_PANE 5931 * @see #ROOT_PANE 5932 */ 5933 AccessibleRole.LAYERED_PANE, 5934 5935 /** 5936 * An object that presents a list of objects to the user and allows the 5937 * user to select one or more of them. A list is usually contained 5938 * within a scroll pane. 5939 * @see #SCROLL_PANE 5940 * @see #LIST_ITEM 5941 */ 5942 AccessibleRole.LIST, 5943 5944 /** 5945 * An object that presents an element in a list. A list is usually 5946 * contained within a scroll pane. 5947 * @see #SCROLL_PANE 5948 * @see #LIST 5949 */ 5950 AccessibleRole.LIST_ITEM, 5951 5952 /** 5953 * An object usually drawn at the top of the primary dialog box of 5954 * an application that contains a list of menus the user can choose 5955 * from. For example, a menu bar might contain menus for "File," 5956 * "Edit," and "Help." 5957 * @see #MENU 5958 * @see #POPUP_MENU 5959 * @see #LAYERED_PANE 5960 */ 5961 AccessibleRole.MENU_BAR, 5962 5963 /** 5964 * A temporary window that is usually used to offer the user a 5965 * list of choices, and then hides when the user selects one of 5966 * those choices. 5967 * @see #MENU 5968 * @see #MENU_ITEM 5969 */ 5970 AccessibleRole.POPUP_MENU, 5971 5972 /** 5973 * An object usually found inside a menu bar that contains a list 5974 * of actions the user can choose from. A menu can have any object 5975 * as its children, but most often they are menu items, other menus, 5976 * or rudimentary objects such as radio buttons, check boxes, or 5977 * separators. For example, an application may have an "Edit" menu 5978 * that contains menu items for "Cut" and "Paste." 5979 * @see #MENU_BAR 5980 * @see #MENU_ITEM 5981 * @see #SEPARATOR 5982 * @see #RADIO_BUTTON 5983 * @see #CHECK_BOX 5984 * @see #POPUP_MENU 5985 */ 5986 AccessibleRole.MENU, 5987 5988 /** 5989 * An object usually contained in a menu that presents an action 5990 * the user can choose. For example, the "Cut" menu item in an 5991 * "Edit" menu would be an action the user can select to cut the 5992 * selected area of text in a document. 5993 * @see #MENU_BAR 5994 * @see #SEPARATOR 5995 * @see #POPUP_MENU 5996 */ 5997 AccessibleRole.MENU_ITEM, 5998 5999 /** 6000 * An object usually contained in a menu to provide a visual 6001 * and logical separation of the contents in a menu. For example, 6002 * the "File" menu of an application might contain menu items for 6003 * "Open," "Close," and "Exit," and will place a separator between 6004 * "Close" and "Exit" menu items. 6005 * @see #MENU 6006 * @see #MENU_ITEM 6007 */ 6008 AccessibleRole.SEPARATOR, 6009 6010 /** 6011 * An object that presents a series of panels (or page tabs), one at a 6012 * time, through some mechanism provided by the object. The most common 6013 * mechanism is a list of tabs at the top of the panel. The children of 6014 * a page tab list are all page tabs. 6015 * @see #PAGE_TAB 6016 */ 6017 AccessibleRole.PAGE_TAB_LIST, 6018 6019 /** 6020 * An object that is a child of a page tab list. Its sole child is 6021 * the panel that is to be presented to the user when the user 6022 * selects the page tab from the list of tabs in the page tab list. 6023 * @see #PAGE_TAB_LIST 6024 */ 6025 AccessibleRole.PAGE_TAB, 6026 6027 /** 6028 * A generic container that is often used to group objects. 6029 */ 6030 AccessibleRole.PANEL, 6031 6032 /** 6033 * An object used to indicate how much of a task has been completed. 6034 */ 6035 AccessibleRole.PROGRESS_BAR, 6036 6037 /** 6038 * A text object used for passwords, or other places where the 6039 * text contents is not shown visibly to the user 6040 */ 6041 AccessibleRole.PASSWORD_TEXT, 6042 6043 /** 6044 * An object the user can manipulate to tell the application to do 6045 * something. 6046 * @see #CHECK_BOX 6047 * @see #TOGGLE_BUTTON 6048 * @see #RADIO_BUTTON 6049 */ 6050 AccessibleRole.PUSH_BUTTON, 6051 6052 /** 6053 * A specialized push button that can be checked or unchecked, but 6054 * does not provide a separate indicator for the current state. 6055 * @see #PUSH_BUTTON 6056 * @see #CHECK_BOX 6057 * @see #RADIO_BUTTON 6058 */ 6059 AccessibleRole.TOGGLE_BUTTON, 6060 6061 /** 6062 * A choice that can be checked or unchecked and provides a 6063 * separate indicator for the current state. 6064 * @see #PUSH_BUTTON 6065 * @see #TOGGLE_BUTTON 6066 * @see #RADIO_BUTTON 6067 */ 6068 AccessibleRole.CHECK_BOX, 6069 6070 /** 6071 * A specialized check box that will cause other radio buttons in the 6072 * same group to become unchecked when this one is checked. 6073 * @see #PUSH_BUTTON 6074 * @see #TOGGLE_BUTTON 6075 * @see #CHECK_BOX 6076 */ 6077 AccessibleRole.RADIO_BUTTON, 6078 6079 /** 6080 * The header for a row of data. 6081 */ 6082 AccessibleRole.ROW_HEADER, 6083 6084 /** 6085 * An object that allows a user to incrementally view a large amount 6086 * of information. Its children can include scroll bars and a viewport. 6087 * @see #SCROLL_BAR 6088 * @see #VIEWPORT 6089 */ 6090 AccessibleRole.SCROLL_PANE, 6091 6092 /** 6093 * An object usually used to allow a user to incrementally view a 6094 * large amount of data. Usually used only by a scroll pane. 6095 * @see #SCROLL_PANE 6096 */ 6097 AccessibleRole.SCROLL_BAR, 6098 6099 /** 6100 * An object usually used in a scroll pane. It represents the portion 6101 * of the entire data that the user can see. As the user manipulates 6102 * the scroll bars, the contents of the viewport can change. 6103 * @see #SCROLL_PANE 6104 */ 6105 AccessibleRole.VIEWPORT, 6106 6107 /** 6108 * An object that allows the user to select from a bounded range. For 6109 * example, a slider might be used to select a number between 0 and 100. 6110 */ 6111 AccessibleRole.SLIDER, 6112 6113 /** 6114 * A specialized panel that presents two other panels at the same time. 6115 * Between the two panels is a divider the user can manipulate to make 6116 * one panel larger and the other panel smaller. 6117 */ 6118 AccessibleRole.SPLIT_PANE, 6119 6120 /** 6121 * An object used to present information in terms of rows and columns. 6122 * An example might include a spreadsheet application. 6123 */ 6124 AccessibleRole.TABLE, 6125 6126 /** 6127 * An object that presents text to the user. The text is usually 6128 * editable by the user as opposed to a label. 6129 * @see #LABEL 6130 */ 6131 AccessibleRole.TEXT, 6132 6133 /** 6134 * An object used to present hierarchical information to the user. 6135 * The individual nodes in the tree can be collapsed and expanded 6136 * to provide selective disclosure of the tree's contents. 6137 */ 6138 AccessibleRole.TREE, 6139 6140 /** 6141 * A bar or palette usually composed of push buttons or toggle buttons. 6142 * It is often used to provide the most frequently used functions for an 6143 * application. 6144 */ 6145 AccessibleRole.TOOL_BAR, 6146 6147 /** 6148 * An object that provides information about another object. The 6149 * accessibleDescription property of the tool tip is often displayed 6150 * to the user in a small "help bubble" when the user causes the 6151 * mouse to hover over the object associated with the tool tip. 6152 */ 6153 AccessibleRole.TOOL_TIP, 6154 6155 /** 6156 * An AWT component, but nothing else is known about it. 6157 * @see #SWING_COMPONENT 6158 * @see #UNKNOWN 6159 */ 6160 AccessibleRole.AWT_COMPONENT, 6161 6162 /** 6163 * A Swing component, but nothing else is known about it. 6164 * @see #AWT_COMPONENT 6165 * @see #UNKNOWN 6166 */ 6167 AccessibleRole.SWING_COMPONENT, 6168 6169 /** 6170 * The object contains some Accessible information, but its role is 6171 * not known. 6172 * @see #AWT_COMPONENT 6173 * @see #SWING_COMPONENT 6174 */ 6175 AccessibleRole.UNKNOWN, 6176 6177 // These roles are only available in JDK 1.4 6178 6179 /** 6180 * A STATUS_BAR is an simple component that can contain 6181 * multiple labels of status information to the user. 6182 AccessibleRole.STATUS_BAR, 6183 6184 /** 6185 * A DATE_EDITOR is a component that allows users to edit 6186 * java.util.Date and java.util.Time objects 6187 AccessibleRole.DATE_EDITOR, 6188 6189 /** 6190 * A SPIN_BOX is a simple spinner component and its main use 6191 * is for simple numbers. 6192 AccessibleRole.SPIN_BOX, 6193 6194 /** 6195 * A FONT_CHOOSER is a component that lets the user pick various 6196 * attributes for fonts. 6197 AccessibleRole.FONT_CHOOSER, 6198 6199 /** 6200 * A GROUP_BOX is a simple container that contains a border 6201 * around it and contains components inside it. 6202 AccessibleRole.GROUP_BOX 6203 6204 /** 6205 * Since JDK 1.5 6206 * 6207 * A text header 6208 6209 AccessibleRole.HEADER, 6210 6211 /** 6212 * A text footer 6213 6214 AccessibleRole.FOOTER, 6215 6216 /** 6217 * A text paragraph 6218 6219 AccessibleRole.PARAGRAPH, 6220 6221 /** 6222 * A ruler is an object used to measure distance 6223 6224 AccessibleRole.RULER, 6225 6226 /** 6227 * A role indicating the object acts as a formula for 6228 * calculating a value. An example is a formula in 6229 * a spreadsheet cell. 6230 AccessibleRole.EDITBAR 6231 */ 6232 }; 6233 6234 /** 6235 * This class implements accessibility support for the 6236 * <code>JTree</code> child. It provides an implementation of the 6237 * Java Accessibility API appropriate to tree nodes. 6238 * 6239 * Copied from JTree.java to work around a JTree bug where 6240 * ActiveDescendent PropertyChangeEvents contain the wrong 6241 * parent. 6242 */ 6243 /** 6244 * This class in invoked on the EDT as its part of ActiveDescendant, 6245 * hence the calls do not need to be specifically made on the EDT 6246 */ 6247 private class AccessibleJTreeNode extends AccessibleContext 6248 implements Accessible, AccessibleComponent, AccessibleSelection, 6249 AccessibleAction { 6250 6251 private JTree tree = null; 6252 private TreeModel treeModel = null; 6253 private Object obj = null; 6254 private TreePath path = null; 6255 private Accessible accessibleParent = null; 6256 private int index = 0; 6257 private boolean isLeaf = false; 6258 6259 /** 6260 * Constructs an AccessibleJTreeNode 6261 */ 6262 AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) { 6263 tree = t; 6264 path = p; 6265 accessibleParent = ap; 6266 if (t != null) 6267 treeModel = t.getModel(); 6268 if (p != null) { 6269 obj = p.getLastPathComponent(); 6270 if (treeModel != null && obj != null) { 6271 isLeaf = treeModel.isLeaf(obj); 6272 } 6273 } 6274 debugString("[INFO]: AccessibleJTreeNode: name = "+getAccessibleName()+"; TreePath = "+p+"; parent = "+ap); 6275 } 6276 6277 private TreePath getChildTreePath(int i) { 6278 // Tree nodes can't be so complex that they have 6279 // two sets of children -> we're ignoring that case 6280 if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) { 6281 return null; 6282 } else { 6283 Object childObj = treeModel.getChild(obj, i); 6284 Object[] objPath = path.getPath(); 6285 Object[] objChildPath = new Object[objPath.length+1]; 6286 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length); 6287 objChildPath[objChildPath.length-1] = childObj; 6288 return new TreePath(objChildPath); 6289 } 6290 } 6291 6292 /** 6293 * Get the AccessibleContext associated with this tree node. 6294 * In the implementation of the Java Accessibility API for 6295 * this class, return this object, which is its own 6296 * AccessibleContext. 6297 * 6298 * @return this object 6299 */ 6300 public AccessibleContext getAccessibleContext() { 6301 return this; 6302 } 6303 6304 private AccessibleContext getCurrentAccessibleContext() { 6305 Component c = getCurrentComponent(); 6306 if (c instanceof Accessible) { 6307 return (c.getAccessibleContext()); 6308 } else { 6309 return null; 6310 } 6311 } 6312 6313 private Component getCurrentComponent() { 6314 debugString("[INFO]: AccessibleJTreeNode: getCurrentComponent"); 6315 // is the object visible? 6316 // if so, get row, selected, focus & leaf state, 6317 // and then get the renderer component and return it 6318 if (tree != null && tree.isVisible(path)) { 6319 TreeCellRenderer r = tree.getCellRenderer(); 6320 if (r == null) { 6321 debugString("[WARN]: returning null 1"); 6322 return null; 6323 } 6324 TreeUI ui = tree.getUI(); 6325 if (ui != null) { 6326 int row = ui.getRowForPath(tree, path); 6327 boolean selected = tree.isPathSelected(path); 6328 boolean expanded = tree.isExpanded(path); 6329 boolean hasFocus = false; // how to tell?? -PK 6330 Component retval = r.getTreeCellRendererComponent(tree, obj, 6331 selected, expanded, 6332 isLeaf, row, hasFocus); 6333 debugString("[INFO]: returning = "+retval.getClass()); 6334 return retval; 6335 } 6336 } 6337 debugString("[WARN]: returning null 2"); 6338 return null; 6339 } 6340 6341 // AccessibleContext methods 6342 6343 /** 6344 * Get the accessible name of this object. 6345 * 6346 * @return the localized name of the object; null if this 6347 * object does not have a name 6348 */ 6349 public String getAccessibleName() { 6350 debugString("[INFO]: AccessibleJTreeNode: getAccessibleName"); 6351 AccessibleContext ac = getCurrentAccessibleContext(); 6352 if (ac != null) { 6353 String name = ac.getAccessibleName(); 6354 if ((name != null) && (!name.isEmpty())) { 6355 String retval = ac.getAccessibleName(); 6356 debugString("[INFO]: returning "+retval); 6357 return retval; 6358 } else { 6359 return null; 6360 } 6361 } 6362 if ((accessibleName != null) && (accessibleName.isEmpty())) { 6363 return accessibleName; 6364 } else { 6365 return null; 6366 } 6367 } 6368 6369 /** 6370 * Set the localized accessible name of this object. 6371 * 6372 * @param s the new localized name of the object. 6373 */ 6374 public void setAccessibleName(String s) { 6375 AccessibleContext ac = getCurrentAccessibleContext(); 6376 if (ac != null) { 6377 ac.setAccessibleName(s); 6378 } else { 6379 super.setAccessibleName(s); 6380 } 6381 } 6382 6383 // 6384 // *** should check tooltip text for desc. (needs MouseEvent) 6385 // 6386 /** 6387 * Get the accessible description of this object. 6388 * 6389 * @return the localized description of the object; null if 6390 * this object does not have a description 6391 */ 6392 public String getAccessibleDescription() { 6393 AccessibleContext ac = getCurrentAccessibleContext(); 6394 if (ac != null) { 6395 return ac.getAccessibleDescription(); 6396 } else { 6397 return super.getAccessibleDescription(); 6398 } 6399 } 6400 6401 /** 6402 * Set the accessible description of this object. 6403 * 6404 * @param s the new localized description of the object 6405 */ 6406 public void setAccessibleDescription(String s) { 6407 AccessibleContext ac = getCurrentAccessibleContext(); 6408 if (ac != null) { 6409 ac.setAccessibleDescription(s); 6410 } else { 6411 super.setAccessibleDescription(s); 6412 } 6413 } 6414 6415 /** 6416 * Get the role of this object. 6417 * 6418 * @return an instance of AccessibleRole describing the role of the object 6419 * @see AccessibleRole 6420 */ 6421 public AccessibleRole getAccessibleRole() { 6422 AccessibleContext ac = getCurrentAccessibleContext(); 6423 if (ac != null) { 6424 return ac.getAccessibleRole(); 6425 } else { 6426 return AccessibleRole.UNKNOWN; 6427 } 6428 } 6429 6430 /** 6431 * Get the state set of this object. 6432 * 6433 * @return an instance of AccessibleStateSet containing the 6434 * current state set of the object 6435 * @see AccessibleState 6436 */ 6437 public AccessibleStateSet getAccessibleStateSet() { 6438 if (tree == null) 6439 return null; 6440 AccessibleContext ac = getCurrentAccessibleContext(); 6441 AccessibleStateSet states; 6442 int row = tree.getUI().getRowForPath(tree,path); 6443 int lsr = tree.getLeadSelectionRow(); 6444 if (ac != null) { 6445 states = ac.getAccessibleStateSet(); 6446 } else { 6447 states = new AccessibleStateSet(); 6448 } 6449 // need to test here, 'cause the underlying component 6450 // is a cellRenderer, which is never showing... 6451 if (isShowing()) { 6452 states.add(AccessibleState.SHOWING); 6453 } else if (states.contains(AccessibleState.SHOWING)) { 6454 states.remove(AccessibleState.SHOWING); 6455 } 6456 if (isVisible()) { 6457 states.add(AccessibleState.VISIBLE); 6458 } else if (states.contains(AccessibleState.VISIBLE)) { 6459 states.remove(AccessibleState.VISIBLE); 6460 } 6461 if (tree.isPathSelected(path)){ 6462 states.add(AccessibleState.SELECTED); 6463 } 6464 if (lsr == row) { 6465 states.add(AccessibleState.ACTIVE); 6466 } 6467 if (!isLeaf) { 6468 states.add(AccessibleState.EXPANDABLE); 6469 } 6470 if (tree.isExpanded(path)) { 6471 states.add(AccessibleState.EXPANDED); 6472 } else { 6473 states.add(AccessibleState.COLLAPSED); 6474 } 6475 if (tree.isEditable()) { 6476 states.add(AccessibleState.EDITABLE); 6477 } 6478 return states; 6479 } 6480 6481 /** 6482 * Get the Accessible parent of this object. 6483 * 6484 * @return the Accessible parent of this object; null if this 6485 * object does not have an Accessible parent 6486 */ 6487 public Accessible getAccessibleParent() { 6488 // someone wants to know, so we need to create our parent 6489 // if we don't have one (hey, we're a talented kid!) 6490 if (accessibleParent == null && path != null) { 6491 Object[] objPath = path.getPath(); 6492 if (objPath.length > 1) { 6493 Object objParent = objPath[objPath.length-2]; 6494 if (treeModel != null) { 6495 index = treeModel.getIndexOfChild(objParent, obj); 6496 } 6497 Object[] objParentPath = new Object[objPath.length-1]; 6498 java.lang.System.arraycopy(objPath, 0, objParentPath, 6499 0, objPath.length-1); 6500 TreePath parentPath = new TreePath(objParentPath); 6501 accessibleParent = new AccessibleJTreeNode(tree, 6502 parentPath, 6503 null); 6504 this.setAccessibleParent(accessibleParent); 6505 } else if (treeModel != null) { 6506 accessibleParent = tree; // we're the top! 6507 index = 0; // we're an only child! 6508 this.setAccessibleParent(accessibleParent); 6509 } 6510 } 6511 return accessibleParent; 6512 } 6513 6514 /** 6515 * Get the index of this object in its accessible parent. 6516 * 6517 * @return the index of this object in its parent; -1 if this 6518 * object does not have an accessible parent. 6519 * @see #getAccessibleParent 6520 */ 6521 public int getAccessibleIndexInParent() { 6522 // index is invalid 'till we have an accessibleParent... 6523 if (accessibleParent == null) { 6524 getAccessibleParent(); 6525 } 6526 if (path != null) { 6527 Object[] objPath = path.getPath(); 6528 if (objPath.length > 1) { 6529 Object objParent = objPath[objPath.length-2]; 6530 if (treeModel != null) { 6531 index = treeModel.getIndexOfChild(objParent, obj); 6532 } 6533 } 6534 } 6535 return index; 6536 } 6537 6538 /** 6539 * Returns the number of accessible children in the object. 6540 * 6541 * @return the number of accessible children in the object. 6542 */ 6543 public int getAccessibleChildrenCount() { 6544 // Tree nodes can't be so complex that they have 6545 // two sets of children -> we're ignoring that case 6546 if (obj != null && treeModel != null) { 6547 return treeModel.getChildCount(obj); 6548 } 6549 return 0; 6550 } 6551 6552 /** 6553 * Return the specified Accessible child of the object. 6554 * 6555 * @param i zero-based index of child 6556 * @return the Accessible child of the object 6557 */ 6558 public Accessible getAccessibleChild(int i) { 6559 // Tree nodes can't be so complex that they have 6560 // two sets of children -> we're ignoring that case 6561 if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) { 6562 return null; 6563 } else { 6564 Object childObj = treeModel.getChild(obj, i); 6565 Object[] objPath = path.getPath(); 6566 Object[] objChildPath = new Object[objPath.length+1]; 6567 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length); 6568 objChildPath[objChildPath.length-1] = childObj; 6569 TreePath childPath = new TreePath(objChildPath); 6570 return new AccessibleJTreeNode(tree, childPath, this); 6571 } 6572 } 6573 6574 /** 6575 * Gets the locale of the component. If the component does not have 6576 * a locale, then the locale of its parent is returned. 6577 * 6578 * @return This component's locale. If this component does not have 6579 * a locale, the locale of its parent is returned. 6580 * @exception IllegalComponentStateException 6581 * If the Component does not have its own locale and has not yet 6582 * been added to a containment hierarchy such that the locale can be 6583 * determined from the containing parent. 6584 * @see #setLocale 6585 */ 6586 public Locale getLocale() { 6587 if (tree == null) 6588 return null; 6589 AccessibleContext ac = getCurrentAccessibleContext(); 6590 if (ac != null) { 6591 return ac.getLocale(); 6592 } else { 6593 return tree.getLocale(); 6594 } 6595 } 6596 6597 /** 6598 * Add a PropertyChangeListener to the listener list. 6599 * The listener is registered for all properties. 6600 * 6601 * @param l The PropertyChangeListener to be added 6602 */ 6603 public void addPropertyChangeListener(PropertyChangeListener l) { 6604 AccessibleContext ac = getCurrentAccessibleContext(); 6605 if (ac != null) { 6606 ac.addPropertyChangeListener(l); 6607 } else { 6608 super.addPropertyChangeListener(l); 6609 } 6610 } 6611 6612 /** 6613 * Remove a PropertyChangeListener from the listener list. 6614 * This removes a PropertyChangeListener that was registered 6615 * for all properties. 6616 * 6617 * @param l The PropertyChangeListener to be removed 6618 */ 6619 public void removePropertyChangeListener(PropertyChangeListener l) { 6620 AccessibleContext ac = getCurrentAccessibleContext(); 6621 if (ac != null) { 6622 ac.removePropertyChangeListener(l); 6623 } else { 6624 super.removePropertyChangeListener(l); 6625 } 6626 } 6627 6628 /** 6629 * Get the AccessibleAction associated with this object. In the 6630 * implementation of the Java Accessibility API for this class, 6631 * return this object, which is responsible for implementing the 6632 * AccessibleAction interface on behalf of itself. 6633 * 6634 * @return this object 6635 */ 6636 public AccessibleAction getAccessibleAction() { 6637 return this; 6638 } 6639 6640 /** 6641 * Get the AccessibleComponent associated with this object. In the 6642 * implementation of the Java Accessibility API for this class, 6643 * return this object, which is responsible for implementing the 6644 * AccessibleComponent interface on behalf of itself. 6645 * 6646 * @return this object 6647 */ 6648 public AccessibleComponent getAccessibleComponent() { 6649 return this; // to override getBounds() 6650 } 6651 6652 /** 6653 * Get the AccessibleSelection associated with this object if one 6654 * exists. Otherwise return null. 6655 * 6656 * @return the AccessibleSelection, or null 6657 */ 6658 public AccessibleSelection getAccessibleSelection() { 6659 AccessibleContext ac = getCurrentAccessibleContext(); 6660 if (ac != null && isLeaf) { 6661 return getCurrentAccessibleContext().getAccessibleSelection(); 6662 } else { 6663 return this; 6664 } 6665 } 6666 6667 /** 6668 * Get the AccessibleText associated with this object if one 6669 * exists. Otherwise return null. 6670 * 6671 * @return the AccessibleText, or null 6672 */ 6673 public AccessibleText getAccessibleText() { 6674 AccessibleContext ac = getCurrentAccessibleContext(); 6675 if (ac != null) { 6676 return getCurrentAccessibleContext().getAccessibleText(); 6677 } else { 6678 return null; 6679 } 6680 } 6681 6682 /** 6683 * Get the AccessibleValue associated with this object if one 6684 * exists. Otherwise return null. 6685 * 6686 * @return the AccessibleValue, or null 6687 */ 6688 public AccessibleValue getAccessibleValue() { 6689 AccessibleContext ac = getCurrentAccessibleContext(); 6690 if (ac != null) { 6691 return getCurrentAccessibleContext().getAccessibleValue(); 6692 } else { 6693 return null; 6694 } 6695 } 6696 6697 6698 // AccessibleComponent methods 6699 6700 /** 6701 * Get the background color of this object. 6702 * 6703 * @return the background color, if supported, of the object; 6704 * otherwise, null 6705 */ 6706 public Color getBackground() { 6707 AccessibleContext ac = getCurrentAccessibleContext(); 6708 if (ac instanceof AccessibleComponent) { 6709 return ((AccessibleComponent) ac).getBackground(); 6710 } else { 6711 Component c = getCurrentComponent(); 6712 if (c != null) { 6713 return c.getBackground(); 6714 } else { 6715 return null; 6716 } 6717 } 6718 } 6719 6720 /** 6721 * Set the background color of this object. 6722 * 6723 * @param c the new Color for the background 6724 */ 6725 public void setBackground(Color c) { 6726 AccessibleContext ac = getCurrentAccessibleContext(); 6727 if (ac instanceof AccessibleComponent) { 6728 ((AccessibleComponent) ac).setBackground(c); 6729 } else { 6730 Component cp = getCurrentComponent(); 6731 if ( cp != null) { 6732 cp.setBackground(c); 6733 } 6734 } 6735 } 6736 6737 6738 /** 6739 * Get the foreground color of this object. 6740 * 6741 * @return the foreground color, if supported, of the object; 6742 * otherwise, null 6743 */ 6744 public Color getForeground() { 6745 AccessibleContext ac = getCurrentAccessibleContext(); 6746 if (ac instanceof AccessibleComponent) { 6747 return ((AccessibleComponent) ac).getForeground(); 6748 } else { 6749 Component c = getCurrentComponent(); 6750 if (c != null) { 6751 return c.getForeground(); 6752 } else { 6753 return null; 6754 } 6755 } 6756 } 6757 6758 public void setForeground(Color c) { 6759 AccessibleContext ac = getCurrentAccessibleContext(); 6760 if (ac instanceof AccessibleComponent) { 6761 ((AccessibleComponent) ac).setForeground(c); 6762 } else { 6763 Component cp = getCurrentComponent(); 6764 if (cp != null) { 6765 cp.setForeground(c); 6766 } 6767 } 6768 } 6769 6770 public Cursor getCursor() { 6771 AccessibleContext ac = getCurrentAccessibleContext(); 6772 if (ac instanceof AccessibleComponent) { 6773 return ((AccessibleComponent) ac).getCursor(); 6774 } else { 6775 Component c = getCurrentComponent(); 6776 if (c != null) { 6777 return c.getCursor(); 6778 } else { 6779 Accessible ap = getAccessibleParent(); 6780 if (ap instanceof AccessibleComponent) { 6781 return ((AccessibleComponent) ap).getCursor(); 6782 } else { 6783 return null; 6784 } 6785 } 6786 } 6787 } 6788 6789 public void setCursor(Cursor c) { 6790 AccessibleContext ac = getCurrentAccessibleContext(); 6791 if (ac instanceof AccessibleComponent) { 6792 ((AccessibleComponent) ac).setCursor(c); 6793 } else { 6794 Component cp = getCurrentComponent(); 6795 if (cp != null) { 6796 cp.setCursor(c); 6797 } 6798 } 6799 } 6800 6801 public Font getFont() { 6802 AccessibleContext ac = getCurrentAccessibleContext(); 6803 if (ac instanceof AccessibleComponent) { 6804 return ((AccessibleComponent) ac).getFont(); 6805 } else { 6806 Component c = getCurrentComponent(); 6807 if (c != null) { 6808 return c.getFont(); 6809 } else { 6810 return null; 6811 } 6812 } 6813 } 6814 6815 public void setFont(Font f) { 6816 AccessibleContext ac = getCurrentAccessibleContext(); 6817 if (ac instanceof AccessibleComponent) { 6818 ((AccessibleComponent) ac).setFont(f); 6819 } else { 6820 Component c = getCurrentComponent(); 6821 if (c != null) { 6822 c.setFont(f); 6823 } 6824 } 6825 } 6826 6827 public FontMetrics getFontMetrics(Font f) { 6828 AccessibleContext ac = getCurrentAccessibleContext(); 6829 if (ac instanceof AccessibleComponent) { 6830 return ((AccessibleComponent) ac).getFontMetrics(f); 6831 } else { 6832 Component c = getCurrentComponent(); 6833 if (c != null) { 6834 return c.getFontMetrics(f); 6835 } else { 6836 return null; 6837 } 6838 } 6839 } 6840 6841 public boolean isEnabled() { 6842 AccessibleContext ac = getCurrentAccessibleContext(); 6843 if (ac instanceof AccessibleComponent) { 6844 return ((AccessibleComponent) ac).isEnabled(); 6845 } else { 6846 Component c = getCurrentComponent(); 6847 if (c != null) { 6848 return c.isEnabled(); 6849 } else { 6850 return false; 6851 } 6852 } 6853 } 6854 6855 public void setEnabled(boolean b) { 6856 AccessibleContext ac = getCurrentAccessibleContext(); 6857 if (ac instanceof AccessibleComponent) { 6858 ((AccessibleComponent) ac).setEnabled(b); 6859 } else { 6860 Component c = getCurrentComponent(); 6861 if (c != null) { 6862 c.setEnabled(b); 6863 } 6864 } 6865 } 6866 6867 public boolean isVisible() { 6868 if (tree == null) 6869 return false; 6870 Rectangle pathBounds = tree.getPathBounds(path); 6871 Rectangle parentBounds = tree.getVisibleRect(); 6872 if ( pathBounds != null && parentBounds != null && 6873 parentBounds.intersects(pathBounds) ) { 6874 return true; 6875 } else { 6876 return false; 6877 } 6878 } 6879 6880 public void setVisible(boolean b) { 6881 } 6882 6883 public boolean isShowing() { 6884 return (tree.isShowing() && isVisible()); 6885 } 6886 6887 public boolean contains(Point p) { 6888 AccessibleContext ac = getCurrentAccessibleContext(); 6889 if (ac instanceof AccessibleComponent) { 6890 Rectangle r = ((AccessibleComponent) ac).getBounds(); 6891 return r.contains(p); 6892 } else { 6893 Component c = getCurrentComponent(); 6894 if (c != null) { 6895 Rectangle r = c.getBounds(); 6896 return r.contains(p); 6897 } else { 6898 return getBounds().contains(p); 6899 } 6900 } 6901 } 6902 6903 public Point getLocationOnScreen() { 6904 if (tree != null) { 6905 Point treeLocation = tree.getLocationOnScreen(); 6906 Rectangle pathBounds = tree.getPathBounds(path); 6907 if (treeLocation != null && pathBounds != null) { 6908 Point nodeLocation = new Point(pathBounds.x, 6909 pathBounds.y); 6910 nodeLocation.translate(treeLocation.x, treeLocation.y); 6911 return nodeLocation; 6912 } else { 6913 return null; 6914 } 6915 } else { 6916 return null; 6917 } 6918 } 6919 6920 private Point getLocationInJTree() { 6921 Rectangle r = tree.getPathBounds(path); 6922 if (r != null) { 6923 return r.getLocation(); 6924 } else { 6925 return null; 6926 } 6927 } 6928 6929 public Point getLocation() { 6930 Rectangle r = getBounds(); 6931 if (r != null) { 6932 return r.getLocation(); 6933 } else { 6934 return null; 6935 } 6936 } 6937 6938 public void setLocation(Point p) { 6939 } 6940 6941 public Rectangle getBounds() { 6942 if (tree == null) 6943 return null; 6944 Rectangle r = tree.getPathBounds(path); 6945 Accessible parent = getAccessibleParent(); 6946 if (parent instanceof AccessibleJTreeNode) { 6947 Point parentLoc = ((AccessibleJTreeNode) parent).getLocationInJTree(); 6948 if (parentLoc != null && r != null) { 6949 r.translate(-parentLoc.x, -parentLoc.y); 6950 } else { 6951 return null; // not visible! 6952 } 6953 } 6954 return r; 6955 } 6956 6957 public void setBounds(Rectangle r) { 6958 AccessibleContext ac = getCurrentAccessibleContext(); 6959 if (ac instanceof AccessibleComponent) { 6960 ((AccessibleComponent) ac).setBounds(r); 6961 } else { 6962 Component c = getCurrentComponent(); 6963 if (c != null) { 6964 c.setBounds(r); 6965 } 6966 } 6967 } 6968 6969 public Dimension getSize() { 6970 return getBounds().getSize(); 6971 } 6972 6973 public void setSize (Dimension d) { 6974 AccessibleContext ac = getCurrentAccessibleContext(); 6975 if (ac instanceof AccessibleComponent) { 6976 ((AccessibleComponent) ac).setSize(d); 6977 } else { 6978 Component c = getCurrentComponent(); 6979 if (c != null) { 6980 c.setSize(d); 6981 } 6982 } 6983 } 6984 6985 /** 6986 * Returns the <code>Accessible</code> child, if one exists, 6987 * contained at the local coordinate <code>Point</code>. 6988 * Otherwise returns <code>null</code>. 6989 * 6990 * @param p point in local coordinates of this 6991 * <code>Accessible</code> 6992 * @return the <code>Accessible</code>, if it exists, 6993 * at the specified location; else <code>null</code> 6994 */ 6995 public Accessible getAccessibleAt(Point p) { 6996 AccessibleContext ac = getCurrentAccessibleContext(); 6997 if (ac instanceof AccessibleComponent) { 6998 return ((AccessibleComponent) ac).getAccessibleAt(p); 6999 } else { 7000 return null; 7001 } 7002 } 7003 7004 public boolean isFocusTraversable() { 7005 AccessibleContext ac = getCurrentAccessibleContext(); 7006 if (ac instanceof AccessibleComponent) { 7007 return ((AccessibleComponent) ac).isFocusTraversable(); 7008 } else { 7009 Component c = getCurrentComponent(); 7010 if (c != null) { 7011 return c.isFocusable(); 7012 } else { 7013 return false; 7014 } 7015 } 7016 } 7017 7018 public void requestFocus() { 7019 AccessibleContext ac = getCurrentAccessibleContext(); 7020 if (ac instanceof AccessibleComponent) { 7021 ((AccessibleComponent) ac).requestFocus(); 7022 } else { 7023 Component c = getCurrentComponent(); 7024 if (c != null) { 7025 c.requestFocus(); 7026 } 7027 } 7028 } 7029 7030 public void addFocusListener(FocusListener l) { 7031 AccessibleContext ac = getCurrentAccessibleContext(); 7032 if (ac instanceof AccessibleComponent) { 7033 ((AccessibleComponent) ac).addFocusListener(l); 7034 } else { 7035 Component c = getCurrentComponent(); 7036 if (c != null) { 7037 c.addFocusListener(l); 7038 } 7039 } 7040 } 7041 7042 public void removeFocusListener(FocusListener l) { 7043 AccessibleContext ac = getCurrentAccessibleContext(); 7044 if (ac instanceof AccessibleComponent) { 7045 ((AccessibleComponent) ac).removeFocusListener(l); 7046 } else { 7047 Component c = getCurrentComponent(); 7048 if (c != null) { 7049 c.removeFocusListener(l); 7050 } 7051 } 7052 } 7053 7054 // AccessibleSelection methods 7055 7056 /** 7057 * Returns the number of items currently selected. 7058 * If no items are selected, the return value will be 0. 7059 * 7060 * @return the number of items currently selected. 7061 */ 7062 public int getAccessibleSelectionCount() { 7063 int count = 0; 7064 int childCount = getAccessibleChildrenCount(); 7065 for (int i = 0; i < childCount; i++) { 7066 TreePath childPath = getChildTreePath(i); 7067 if (tree.isPathSelected(childPath)) { 7068 count++; 7069 } 7070 } 7071 return count; 7072 } 7073 7074 /** 7075 * Returns an Accessible representing the specified selected item 7076 * in the object. If there isn't a selection, or there are 7077 * fewer items selected than the integer passed in, the return 7078 * value will be null. 7079 * 7080 * @param i the zero-based index of selected items 7081 * @return an Accessible containing the selected item 7082 */ 7083 public Accessible getAccessibleSelection(int i) { 7084 int childCount = getAccessibleChildrenCount(); 7085 if (i < 0 || i >= childCount) { 7086 return null; // out of range 7087 } 7088 int count = 0; 7089 for (int j = 0; j < childCount && i >= count; j++) { 7090 TreePath childPath = getChildTreePath(j); 7091 if (tree.isPathSelected(childPath)) { 7092 if (count == i) { 7093 return new AccessibleJTreeNode(tree, childPath, this); 7094 } else { 7095 count++; 7096 } 7097 } 7098 } 7099 return null; 7100 } 7101 7102 /** 7103 * Returns true if the current child of this object is selected. 7104 * 7105 * @param i the zero-based index of the child in this Accessible 7106 * object. 7107 * @see AccessibleContext#getAccessibleChild 7108 */ 7109 public boolean isAccessibleChildSelected(int i) { 7110 int childCount = getAccessibleChildrenCount(); 7111 if (i < 0 || i >= childCount) { 7112 return false; // out of range 7113 } else { 7114 TreePath childPath = getChildTreePath(i); 7115 return tree.isPathSelected(childPath); 7116 } 7117 } 7118 7119 /** 7120 * Adds the specified selected item in the object to the object's 7121 * selection. If the object supports multiple selections, 7122 * the specified item is added to any existing selection, otherwise 7123 * it replaces any existing selection in the object. If the 7124 * specified item is already selected, this method has no effect. 7125 * 7126 * @param i the zero-based index of selectable items 7127 */ 7128 public void addAccessibleSelection(int i) { 7129 if (tree == null) 7130 return; 7131 TreeModel model = tree.getModel(); 7132 if (model != null) { 7133 if (i >= 0 && i < getAccessibleChildrenCount()) { 7134 TreePath path = getChildTreePath(i); 7135 tree.addSelectionPath(path); 7136 } 7137 } 7138 } 7139 7140 /** 7141 * Removes the specified selected item in the object from the 7142 * object's 7143 * selection. If the specified item isn't currently selected, this 7144 * method has no effect. 7145 * 7146 * @param i the zero-based index of selectable items 7147 */ 7148 public void removeAccessibleSelection(int i) { 7149 if (tree == null) 7150 return; 7151 TreeModel model = tree.getModel(); 7152 if (model != null) { 7153 if (i >= 0 && i < getAccessibleChildrenCount()) { 7154 TreePath path = getChildTreePath(i); 7155 tree.removeSelectionPath(path); 7156 } 7157 } 7158 } 7159 7160 /** 7161 * Clears the selection in the object, so that nothing in the 7162 * object is selected. 7163 */ 7164 public void clearAccessibleSelection() { 7165 int childCount = getAccessibleChildrenCount(); 7166 for (int i = 0; i < childCount; i++) { 7167 removeAccessibleSelection(i); 7168 } 7169 } 7170 7171 /** 7172 * Causes every selected item in the object to be selected 7173 * if the object supports multiple selections. 7174 */ 7175 public void selectAllAccessibleSelection() { 7176 if (tree == null) 7177 return; 7178 TreeModel model = tree.getModel(); 7179 if (model != null) { 7180 int childCount = getAccessibleChildrenCount(); 7181 TreePath path; 7182 for (int i = 0; i < childCount; i++) { 7183 path = getChildTreePath(i); 7184 tree.addSelectionPath(path); 7185 } 7186 } 7187 } 7188 7189 // AccessibleAction methods 7190 7191 /** 7192 * Returns the number of accessible actions available in this 7193 * tree node. If this node is not a leaf, there is at least 7194 * one action (toggle expand), in addition to any available 7195 * on the object behind the TreeCellRenderer. 7196 * 7197 * @return the number of Actions in this object 7198 */ 7199 public int getAccessibleActionCount() { 7200 AccessibleContext ac = getCurrentAccessibleContext(); 7201 if (ac != null) { 7202 AccessibleAction aa = ac.getAccessibleAction(); 7203 if (aa != null) { 7204 return (aa.getAccessibleActionCount() + (isLeaf ? 0 : 1)); 7205 } 7206 } 7207 return isLeaf ? 0 : 1; 7208 } 7209 7210 /** 7211 * Return a description of the specified action of the tree node. 7212 * If this node is not a leaf, there is at least one action 7213 * description (toggle expand), in addition to any available 7214 * on the object behind the TreeCellRenderer. 7215 * 7216 * @param i zero-based index of the actions 7217 * @return a description of the action 7218 */ 7219 public String getAccessibleActionDescription(int i) { 7220 if (i < 0 || i >= getAccessibleActionCount()) { 7221 return null; 7222 } 7223 AccessibleContext ac = getCurrentAccessibleContext(); 7224 if (i == 0) { 7225 // TIGER - 4766636 7226 // return AccessibleAction.TOGGLE_EXPAND; 7227 return "toggle expand"; 7228 } else if (ac != null) { 7229 AccessibleAction aa = ac.getAccessibleAction(); 7230 if (aa != null) { 7231 return aa.getAccessibleActionDescription(i - 1); 7232 } 7233 } 7234 return null; 7235 } 7236 7237 /** 7238 * Perform the specified Action on the tree node. If this node 7239 * is not a leaf, there is at least one action which can be 7240 * done (toggle expand), in addition to any available on the 7241 * object behind the TreeCellRenderer. 7242 * 7243 * @param i zero-based index of actions 7244 * @return true if the the action was performed; else false. 7245 */ 7246 public boolean doAccessibleAction(int i) { 7247 if (i < 0 || i >= getAccessibleActionCount()) { 7248 return false; 7249 } 7250 AccessibleContext ac = getCurrentAccessibleContext(); 7251 if (i == 0) { 7252 if (tree.isExpanded(path)) { 7253 tree.collapsePath(path); 7254 } else { 7255 tree.expandPath(path); 7256 } 7257 return true; 7258 } else if (ac != null) { 7259 AccessibleAction aa = ac.getAccessibleAction(); 7260 if (aa != null) { 7261 return aa.doAccessibleAction(i - 1); 7262 } 7263 } 7264 return false; 7265 } 7266 7267 } // inner class AccessibleJTreeNode 7268 7269 /** 7270 * A helper class to perform {@code Callable} objects on the event dispatch thread appropriate 7271 * for the provided {@code AccessibleContext}. 7272 */ 7273 private static class InvocationUtils { 7274 7275 /** 7276 * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible} 7277 * and waits for it to finish blocking the caller thread. 7278 * 7279 * @param callable the {@code Callable} to invoke 7280 * @param accessibleTable the {@code AccessibleExtendedTable} which would be used to find the right context 7281 * for the task execution 7282 * @param <T> type parameter for the result value 7283 * 7284 * @return the result of the {@code Callable} execution 7285 */ 7286 public static <T> T invokeAndWait(final Callable<T> callable, 7287 final AccessibleExtendedTable accessibleTable) { 7288 if (accessibleTable instanceof AccessibleContext) { 7289 return invokeAndWait(callable, (AccessibleContext)accessibleTable); 7290 } 7291 throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleTable); 7292 } 7293 7294 /** 7295 * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible} 7296 * and waits for it to finish blocking the caller thread. 7297 * 7298 * @param callable the {@code Callable} to invoke 7299 * @param accessible the {@code Accessible} which would be used to find the right context 7300 * for the task execution 7301 * @param <T> type parameter for the result value 7302 * 7303 * @return the result of the {@code Callable} execution 7304 */ 7305 public static <T> T invokeAndWait(final Callable<T> callable, 7306 final Accessible accessible) { 7307 if (accessible instanceof Component) { 7308 return invokeAndWait(callable, (Component)accessible); 7309 } 7310 if (accessible instanceof AccessibleContext) { 7311 // This case also covers the Translator 7312 return invokeAndWait(callable, (AccessibleContext)accessible); 7313 } 7314 throw new RuntimeException("Unmapped Accessible used to dispatch event: " + accessible); 7315 } 7316 7317 /** 7318 * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Component} 7319 * and waits for it to finish blocking the caller thread. 7320 * 7321 * @param callable the {@code Callable} to invoke 7322 * @param component the {@code Component} which would be used to find the right context 7323 * for the task execution 7324 * @param <T> type parameter for the result value 7325 * 7326 * @return the result of the {@code Callable} execution 7327 */ 7328 public static <T> T invokeAndWait(final Callable<T> callable, 7329 final Component component) { 7330 return invokeAndWait(callable, SunToolkit.targetToAppContext(component)); 7331 } 7332 7333 /** 7334 * Invokes a {@code Callable} in the {@code AppContext} mapped to the given {@code AccessibleContext} 7335 * and waits for it to finish blocking the caller thread. 7336 * 7337 * @param callable the {@code Callable} to invoke 7338 * @param accessibleContext the {@code AccessibleContext} which would be used to determine the right 7339 * context for the task execution. 7340 * @param <T> type parameter for the result value 7341 * 7342 * @return the result of the {@code Callable} execution 7343 */ 7344 public static <T> T invokeAndWait(final Callable<T> callable, 7345 final AccessibleContext accessibleContext) { 7346 AppContext targetContext = AWTAccessor.getAccessibleContextAccessor() 7347 .getAppContext(accessibleContext); 7348 if (targetContext != null) { 7349 return invokeAndWait(callable, targetContext); 7350 } else { 7351 // Normally this should not happen, unmapped context provided and 7352 // the target AppContext is unknown. 7353 7354 // Try to recover in case the context is a translator. 7355 if (accessibleContext instanceof Translator) { 7356 Object source = ((Translator)accessibleContext).getSource(); 7357 if (source instanceof Component) { 7358 return invokeAndWait(callable, (Component)source); 7359 } 7360 } 7361 } 7362 throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleContext); 7363 } 7364 7365 private static <T> T invokeAndWait(final Callable<T> callable, 7366 final AppContext targetAppContext) { 7367 final CallableWrapper<T> wrapper = new CallableWrapper<T>(callable); 7368 try { 7369 invokeAndWait(wrapper, targetAppContext); 7370 T result = wrapper.getResult(); 7371 updateAppContextMap(result, targetAppContext); 7372 return result; 7373 } catch (final Exception e) { 7374 throw new RuntimeException(e); 7375 } 7376 } 7377 7378 private static void invokeAndWait(final Runnable runnable, 7379 final AppContext appContext) 7380 throws InterruptedException, InvocationTargetException { 7381 7382 EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext); 7383 Object lock = new Object(); 7384 Toolkit source = Toolkit.getDefaultToolkit(); 7385 InvocationEvent event = 7386 new InvocationEvent(source, runnable, lock, true); 7387 synchronized (lock) { 7388 eq.postEvent(event); 7389 lock.wait(); 7390 } 7391 7392 Throwable eventThrowable = event.getThrowable(); 7393 if (eventThrowable != null) { 7394 throw new InvocationTargetException(eventThrowable); 7395 } 7396 } 7397 7398 /** 7399 * Maps the {@code AccessibleContext} to the {@code AppContext} which should be used 7400 * to dispatch events related to the {@code AccessibleContext} 7401 * @param accessibleContext the {@code AccessibleContext} for the mapping 7402 * @param targetContext the {@code AppContext} for the mapping 7403 */ 7404 public static void registerAccessibleContext(final AccessibleContext accessibleContext, 7405 final AppContext targetContext) { 7406 if (accessibleContext != null) { 7407 AWTAccessor.getAccessibleContextAccessor().setAppContext(accessibleContext, targetContext); 7408 } 7409 } 7410 7411 private static <T> void updateAppContextMap(final T accessibleContext, 7412 final AppContext targetContext) { 7413 if (accessibleContext instanceof AccessibleContext) { 7414 registerAccessibleContext((AccessibleContext)accessibleContext, targetContext); 7415 } 7416 } 7417 7418 private static class CallableWrapper<T> implements Runnable { 7419 private final Callable<T> callable; 7420 private volatile T object; 7421 private Exception e; 7422 7423 CallableWrapper(final Callable<T> callable) { 7424 this.callable = callable; 7425 } 7426 7427 public void run() { 7428 try { 7429 if (callable != null) { 7430 object = callable.call(); 7431 } 7432 } catch (final Exception e) { 7433 this.e = e; 7434 } 7435 } 7436 7437 T getResult() throws Exception { 7438 if (e != null) 7439 throw e; 7440 return object; 7441 } 7442 } 7443 } 7444 } 7445