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