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