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