1 /* 2 * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.java.accessibility.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 != null ? role.toDisplayString(Locale.US) : "null") ); 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 if(ac instanceof AccessibleExtendedTable) { 4531 _getVisibleChildrenCount((AccessibleExtendedTable)ac); 4532 return; 4533 } 4534 int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4535 @Override 4536 public Integer call() throws Exception { 4537 return ac.getAccessibleChildrenCount(); 4538 } 4539 }, ac); 4540 for (int i = 0; i < numChildren; i++) { 4541 final int idx = i; 4542 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4543 @Override 4544 public AccessibleContext call() throws Exception { 4545 Accessible a = ac.getAccessibleChild(idx); 4546 if (a != null) 4547 return a.getAccessibleContext(); 4548 else 4549 return null; 4550 } 4551 }, ac); 4552 if ( ac2 == null || 4553 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4554 @Override 4555 public Boolean call() throws Exception { 4556 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4557 } 4558 }, ac)) 4559 ) { 4560 continue; 4561 } 4562 _visibleChildrenCount++; 4563 4564 if (InvocationUtils.invokeAndWait(new Callable<Integer>() { 4565 @Override 4566 public Integer call() throws Exception { 4567 return ac2.getAccessibleChildrenCount(); 4568 } 4569 }, ac) > 0 ) { 4570 _getVisibleChildrenCount(ac2); 4571 } 4572 } 4573 } 4574 4575 /* 4576 * Recursively descends AccessibleContext and gets the number 4577 * of visible children. Stops search if get to invisible part of table. 4578 */ 4579 private void _getVisibleChildrenCount(final AccessibleExtendedTable acTable) { 4580 if (acTable == null) 4581 return; 4582 int lastVisibleRow = -1; 4583 int lastVisibleColumn = -1; 4584 boolean foundVisible = false; 4585 int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4586 @Override 4587 public Integer call() throws Exception { 4588 return acTable.getAccessibleRowCount(); 4589 } 4590 }, acTable); 4591 int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4592 @Override 4593 public Integer call() throws Exception { 4594 return acTable.getAccessibleColumnCount(); 4595 } 4596 }, acTable); 4597 for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) { 4598 for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) { 4599 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) { 4600 continue; 4601 } 4602 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) { 4603 continue; 4604 } 4605 int finalRowIdx = rowIdx; 4606 int finalColumnIdx = columnIdx; 4607 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4608 @Override 4609 public AccessibleContext call() throws Exception { 4610 Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx); 4611 if (a == null) 4612 return null; 4613 else 4614 return a.getAccessibleContext(); 4615 } 4616 }, acTable); 4617 if (ac2 == null || 4618 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4619 @Override 4620 public Boolean call() throws Exception { 4621 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4622 } 4623 }, acTable)) 4624 ) { 4625 if (foundVisible) { 4626 if (columnIdx != 0 && lastVisibleColumn == -1) { 4627 //the same row, so we found the last visible column 4628 lastVisibleColumn = columnIdx - 1; 4629 } else if (columnIdx == 0 && lastVisibleRow == -1) { 4630 lastVisibleRow = rowIdx - 1; 4631 } 4632 } 4633 continue; 4634 } 4635 4636 foundVisible = true; 4637 4638 _visibleChildrenCount++; 4639 4640 if (InvocationUtils.invokeAndWait(new Callable<Integer>() { 4641 @Override 4642 public Integer call() throws Exception { 4643 return ac2.getAccessibleChildrenCount(); 4644 } 4645 }, acTable) > 0) { 4646 _getVisibleChildrenCount(ac2); 4647 } 4648 } 4649 } 4650 } 4651 4652 /** 4653 * Gets the visible child of an AccessibleContext at the 4654 * specified index 4655 * 4656 * Bug ID 4944762- getVisibleChildren for list-like components needed 4657 */ 4658 private AccessibleContext getVisibleChild(AccessibleContext ac, int index) { 4659 debugString("getVisibleChild: index = "+index); 4660 if (ac == null) { 4661 return null; 4662 } 4663 _visibleChild = null; 4664 _currentVisibleIndex = 0; 4665 _foundVisibleChild = false; 4666 _getVisibleChild(ac, index); 4667 4668 if (_visibleChild != null) { 4669 debugString( " getVisibleChild: found child = " + 4670 InvocationUtils.invokeAndWait(new Callable<String>() { 4671 @Override 4672 public String call() throws Exception { 4673 return AccessBridge.this._visibleChild.getAccessibleName(); 4674 } 4675 }, ac) ); 4676 } 4677 return _visibleChild; 4678 } 4679 4680 /* 4681 * Recursively searchs AccessibleContext and finds the visible component 4682 * at the specified index 4683 */ 4684 private void _getVisibleChild(final AccessibleContext ac, final int index) { 4685 if (_visibleChild != null) { 4686 return; 4687 } 4688 if(ac instanceof AccessibleExtendedTable) { 4689 _getVisibleChild((AccessibleExtendedTable)ac, index); 4690 return; 4691 } 4692 int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4693 @Override 4694 public Integer call() throws Exception { 4695 return ac.getAccessibleChildrenCount(); 4696 } 4697 }, ac); 4698 for (int i = 0; i < numChildren; i++) { 4699 final int idx=i; 4700 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4701 @Override 4702 public AccessibleContext call() throws Exception { 4703 Accessible a = ac.getAccessibleChild(idx); 4704 if (a == null) 4705 return null; 4706 else 4707 return a.getAccessibleContext(); 4708 } 4709 }, ac); 4710 if (ac2 == null || 4711 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4712 @Override 4713 public Boolean call() throws Exception { 4714 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4715 } 4716 }, ac))) { 4717 continue; 4718 } 4719 if (!_foundVisibleChild && _currentVisibleIndex == index) { 4720 _visibleChild = ac2; 4721 _foundVisibleChild = true; 4722 return; 4723 } 4724 _currentVisibleIndex++; 4725 4726 if ( InvocationUtils.invokeAndWait(new Callable<Integer>() { 4727 @Override 4728 public Integer call() throws Exception { 4729 return ac2.getAccessibleChildrenCount(); 4730 } 4731 }, ac) > 0 ) { 4732 _getVisibleChild(ac2, index); 4733 } 4734 } 4735 } 4736 4737 private void _getVisibleChild(final AccessibleExtendedTable acTable, final int index) { 4738 if (_visibleChild != null) { 4739 return; 4740 } 4741 int lastVisibleRow = -1; 4742 int lastVisibleColumn = -1; 4743 boolean foundVisible = false; 4744 int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4745 @Override 4746 public Integer call() throws Exception { 4747 return acTable.getAccessibleRowCount(); 4748 } 4749 }, acTable); 4750 int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4751 @Override 4752 public Integer call() throws Exception { 4753 return acTable.getAccessibleColumnCount(); 4754 } 4755 }, acTable); 4756 for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) { 4757 for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) { 4758 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) { 4759 continue; 4760 } 4761 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) { 4762 continue; 4763 } 4764 int finalRowIdx = rowIdx; 4765 int finalColumnIdx = columnIdx; 4766 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4767 @Override 4768 public AccessibleContext call() throws Exception { 4769 Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx); 4770 if (a == null) 4771 return null; 4772 else 4773 return a.getAccessibleContext(); 4774 } 4775 }, acTable); 4776 if (ac2 == null || 4777 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4778 @Override 4779 public Boolean call() throws Exception { 4780 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4781 } 4782 }, acTable))) { 4783 if (foundVisible) { 4784 if (columnIdx != 0 && lastVisibleColumn == -1) { 4785 //the same row, so we found the last visible column 4786 lastVisibleColumn = columnIdx - 1; 4787 } else if (columnIdx == 0 && lastVisibleRow == -1) { 4788 lastVisibleRow = rowIdx - 1; 4789 } 4790 } 4791 continue; 4792 } 4793 foundVisible = true; 4794 4795 if (!_foundVisibleChild && _currentVisibleIndex == index) { 4796 _visibleChild = ac2; 4797 _foundVisibleChild = true; 4798 return; 4799 } 4800 _currentVisibleIndex++; 4801 4802 if (InvocationUtils.invokeAndWait(new Callable<Integer>() { 4803 @Override 4804 public Integer call() throws Exception { 4805 return ac2.getAccessibleChildrenCount(); 4806 } 4807 }, acTable) > 0) { 4808 _getVisibleChild(ac2, index); 4809 } 4810 } 4811 } 4812 } 4813 4814 /* ===== Java object memory management code ===== */ 4815 4816 /** 4817 * Class to track object references to ensure the 4818 * Java VM doesn't garbage collect them 4819 */ 4820 private class ObjectReferences { 4821 4822 private class Reference { 4823 private int value; 4824 4825 Reference(int i) { 4826 value = i; 4827 } 4828 4829 public String toString() { 4830 return ("refCount: " + value); 4831 } 4832 } 4833 4834 /** 4835 * table object references, to keep 'em from being garbage collected 4836 */ 4837 private ConcurrentHashMap<Object,Reference> refs; 4838 4839 /** 4840 * Constructor 4841 */ 4842 ObjectReferences() { 4843 refs = new ConcurrentHashMap<>(4); 4844 } 4845 4846 /** 4847 * Debugging: dump the contents of ObjectReferences' refs Hashtable 4848 */ 4849 String dump() { 4850 return refs.toString(); 4851 } 4852 4853 /** 4854 * Increment ref count; set to 1 if we have no references for it 4855 */ 4856 void increment(Object o) { 4857 if (o == null){ 4858 debugString("ObjectReferences::increment - Passed in object is null"); 4859 return; 4860 } 4861 4862 if (refs.containsKey(o)) { 4863 (refs.get(o)).value++; 4864 } else { 4865 refs.put(o, new Reference(1)); 4866 } 4867 } 4868 4869 /** 4870 * Decrement ref count; remove if count drops to 0 4871 */ 4872 void decrement(Object o) { 4873 Reference aRef = refs.get(o); 4874 if (aRef != null) { 4875 aRef.value--; 4876 if (aRef.value == 0) { 4877 refs.remove(o); 4878 } else if (aRef.value < 0) { 4879 debugString("ERROR: decrementing reference count below 0"); 4880 } 4881 } else { 4882 debugString("ERROR: object to decrement not in ObjectReferences table"); 4883 } 4884 } 4885 4886 } 4887 4888 /* ===== event handling code ===== */ 4889 4890 /** 4891 * native method for handling property change events 4892 */ 4893 private native void propertyCaretChange(PropertyChangeEvent e, 4894 AccessibleContext src, 4895 int oldValue, int newValue); 4896 private native void propertyDescriptionChange(PropertyChangeEvent e, 4897 AccessibleContext src, 4898 String oldValue, String newValue); 4899 private native void propertyNameChange(PropertyChangeEvent e, 4900 AccessibleContext src, 4901 String oldValue, String newValue); 4902 private native void propertySelectionChange(PropertyChangeEvent e, 4903 AccessibleContext src); 4904 private native void propertyStateChange(PropertyChangeEvent e, 4905 AccessibleContext src, 4906 String oldValue, String newValue); 4907 private native void propertyTextChange(PropertyChangeEvent e, 4908 AccessibleContext src); 4909 private native void propertyValueChange(PropertyChangeEvent e, 4910 AccessibleContext src, 4911 String oldValue, String newValue); 4912 private native void propertyVisibleDataChange(PropertyChangeEvent e, 4913 AccessibleContext src); 4914 private native void propertyChildChange(PropertyChangeEvent e, 4915 AccessibleContext src, 4916 AccessibleContext oldValue, 4917 AccessibleContext newValue); 4918 private native void propertyActiveDescendentChange(PropertyChangeEvent e, 4919 AccessibleContext src, 4920 AccessibleContext oldValue, 4921 AccessibleContext newValue); 4922 4923 private native void javaShutdown(); 4924 4925 /** 4926 * native methods for handling focus events 4927 */ 4928 private native void focusGained(FocusEvent e, AccessibleContext src); 4929 private native void focusLost(FocusEvent e, AccessibleContext src); 4930 4931 /** 4932 * native method for handling caret events 4933 */ 4934 private native void caretUpdate(CaretEvent e, AccessibleContext src); 4935 4936 /** 4937 * native methods for handling mouse events 4938 */ 4939 private native void mouseClicked(MouseEvent e, AccessibleContext src); 4940 private native void mouseEntered(MouseEvent e, AccessibleContext src); 4941 private native void mouseExited(MouseEvent e, AccessibleContext src); 4942 private native void mousePressed(MouseEvent e, AccessibleContext src); 4943 private native void mouseReleased(MouseEvent e, AccessibleContext src); 4944 4945 /** 4946 * native methods for handling menu & popupMenu events 4947 */ 4948 private native void menuCanceled(MenuEvent e, AccessibleContext src); 4949 private native void menuDeselected(MenuEvent e, AccessibleContext src); 4950 private native void menuSelected(MenuEvent e, AccessibleContext src); 4951 private native void popupMenuCanceled(PopupMenuEvent e, AccessibleContext src); 4952 private native void popupMenuWillBecomeInvisible(PopupMenuEvent e, 4953 AccessibleContext src); 4954 private native void popupMenuWillBecomeVisible(PopupMenuEvent e, 4955 AccessibleContext src); 4956 4957 /* ===== event definitions ===== */ 4958 4959 private static final long PROPERTY_CHANGE_EVENTS = 1; 4960 private static final long FOCUS_GAINED_EVENTS = 2; 4961 private static final long FOCUS_LOST_EVENTS = 4; 4962 private static final long FOCUS_EVENTS = (FOCUS_GAINED_EVENTS | FOCUS_LOST_EVENTS); 4963 4964 private static final long CARET_UPATE_EVENTS = 8; 4965 private static final long CARET_EVENTS = CARET_UPATE_EVENTS; 4966 4967 private static final long MOUSE_CLICKED_EVENTS = 16; 4968 private static final long MOUSE_ENTERED_EVENTS = 32; 4969 private static final long MOUSE_EXITED_EVENTS = 64; 4970 private static final long MOUSE_PRESSED_EVENTS = 128; 4971 private static final long MOUSE_RELEASED_EVENTS = 256; 4972 private static final long MOUSE_EVENTS = (MOUSE_CLICKED_EVENTS | MOUSE_ENTERED_EVENTS | 4973 MOUSE_EXITED_EVENTS | MOUSE_PRESSED_EVENTS | 4974 MOUSE_RELEASED_EVENTS); 4975 4976 private static final long MENU_CANCELED_EVENTS = 512; 4977 private static final long MENU_DESELECTED_EVENTS = 1024; 4978 private static final long MENU_SELECTED_EVENTS = 2048; 4979 private static final long MENU_EVENTS = (MENU_CANCELED_EVENTS | MENU_DESELECTED_EVENTS | 4980 MENU_SELECTED_EVENTS); 4981 4982 private static final long POPUPMENU_CANCELED_EVENTS = 4096; 4983 private static final long POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS = 8192; 4984 private static final long POPUPMENU_WILL_BECOME_VISIBLE_EVENTS = 16384; 4985 private static final long POPUPMENU_EVENTS = (POPUPMENU_CANCELED_EVENTS | 4986 POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS | 4987 POPUPMENU_WILL_BECOME_VISIBLE_EVENTS); 4988 4989 /* These use their own numbering scheme, to ensure sufficient expansion room */ 4990 private static final long PROPERTY_NAME_CHANGE_EVENTS = 1; 4991 private static final long PROPERTY_DESCRIPTION_CHANGE_EVENTS = 2; 4992 private static final long PROPERTY_STATE_CHANGE_EVENTS = 4; 4993 private static final long PROPERTY_VALUE_CHANGE_EVENTS = 8; 4994 private static final long PROPERTY_SELECTION_CHANGE_EVENTS = 16; 4995 private static final long PROPERTY_TEXT_CHANGE_EVENTS = 32; 4996 private static final long PROPERTY_CARET_CHANGE_EVENTS = 64; 4997 private static final long PROPERTY_VISIBLEDATA_CHANGE_EVENTS = 128; 4998 private static final long PROPERTY_CHILD_CHANGE_EVENTS = 256; 4999 private static final long PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS = 512; 5000 5001 5002 private static final long PROPERTY_EVENTS = (PROPERTY_NAME_CHANGE_EVENTS | 5003 PROPERTY_DESCRIPTION_CHANGE_EVENTS | 5004 PROPERTY_STATE_CHANGE_EVENTS | 5005 PROPERTY_VALUE_CHANGE_EVENTS | 5006 PROPERTY_SELECTION_CHANGE_EVENTS | 5007 PROPERTY_TEXT_CHANGE_EVENTS | 5008 PROPERTY_CARET_CHANGE_EVENTS | 5009 PROPERTY_VISIBLEDATA_CHANGE_EVENTS | 5010 PROPERTY_CHILD_CHANGE_EVENTS | 5011 PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS); 5012 5013 /** 5014 * The EventHandler class listens for Java events and 5015 * forwards them to the AT 5016 */ 5017 private class EventHandler implements PropertyChangeListener, 5018 FocusListener, CaretListener, 5019 MenuListener, PopupMenuListener, 5020 MouseListener, WindowListener, 5021 ChangeListener { 5022 5023 private AccessBridge accessBridge; 5024 private long javaEventMask = 0; 5025 private long accessibilityEventMask = 0; 5026 5027 EventHandler(AccessBridge bridge) { 5028 accessBridge = bridge; 5029 5030 // Register to receive WINDOW_OPENED and WINDOW_CLOSED 5031 // events. Add the event source as a native window 5032 // handler is it implements NativeWindowHandler. 5033 // SwingEventMonitor.addWindowListener(this); 5034 } 5035 5036 // --------- Event Notification Registration methods 5037 5038 /** 5039 * Invoked the first time a window is made visible. 5040 */ 5041 public void windowOpened(WindowEvent e) { 5042 // If the window is a NativeWindowHandler, add it. 5043 Object o = null; 5044 if (e != null) 5045 o = e.getSource(); 5046 if (o instanceof NativeWindowHandler) { 5047 addNativeWindowHandler((NativeWindowHandler)o); 5048 } 5049 } 5050 5051 /** 5052 * Invoked when the user attempts to close the window 5053 * from the window's system menu. If the program does not 5054 * explicitly hide or dispose the window while processing 5055 * this event, the window close operation will be canceled. 5056 */ 5057 public void windowClosing(WindowEvent e) {} 5058 5059 /** 5060 * Invoked when a window has been closed as the result 5061 * of calling dispose on the window. 5062 */ 5063 public void windowClosed(WindowEvent e) { 5064 // If the window is a NativeWindowHandler, remove it. 5065 Object o = null; 5066 if (e != null) 5067 o = e.getSource(); 5068 if (o instanceof NativeWindowHandler) { 5069 removeNativeWindowHandler((NativeWindowHandler)o); 5070 } 5071 } 5072 5073 /** 5074 * Invoked when a window is changed from a normal to a 5075 * minimized state. For many platforms, a minimized window 5076 * is displayed as the icon specified in the window's 5077 * iconImage property. 5078 * @see java.awt.Frame#setIconImage 5079 */ 5080 public void windowIconified(WindowEvent e) {} 5081 5082 /** 5083 * Invoked when a window is changed from a minimized 5084 * to a normal state. 5085 */ 5086 public void windowDeiconified(WindowEvent e) {} 5087 5088 /** 5089 * Invoked when the Window is set to be the active Window. Only a Frame or 5090 * a Dialog can be the active Window. The native windowing system may 5091 * denote the active Window or its children with special decorations, such 5092 * as a highlighted title bar. The active Window is always either the 5093 * focused Window, or the first Frame or Dialog that is an owner of the 5094 * focused Window. 5095 */ 5096 public void windowActivated(WindowEvent e) {} 5097 5098 /** 5099 * Invoked when a Window is no longer the active Window. Only a Frame or a 5100 * Dialog can be the active Window. The native windowing system may denote 5101 * the active Window or its children with special decorations, such as a 5102 * highlighted title bar. The active Window is always either the focused 5103 * Window, or the first Frame or Dialog that is an owner of the focused 5104 * Window. 5105 */ 5106 public void windowDeactivated(WindowEvent e) {} 5107 5108 /** 5109 * Turn on event monitoring for the event type passed in 5110 * If necessary, add the appropriate event listener (if 5111 * no other event of that type is being listened for) 5112 */ 5113 void addJavaEventNotification(long type) { 5114 long newEventMask = javaEventMask | type; 5115 /* 5116 if ( ((javaEventMask & PROPERTY_EVENTS) == 0) && 5117 ((newEventMask & PROPERTY_EVENTS) != 0) ) { 5118 AccessibilityEventMonitor.addPropertyChangeListener(this); 5119 } 5120 */ 5121 if ( ((javaEventMask & FOCUS_EVENTS) == 0) && 5122 ((newEventMask & FOCUS_EVENTS) != 0) ) { 5123 SwingEventMonitor.addFocusListener(this); 5124 } 5125 if ( ((javaEventMask & CARET_EVENTS) == 0) && 5126 ((newEventMask & CARET_EVENTS) != 0) ) { 5127 SwingEventMonitor.addCaretListener(this); 5128 } 5129 if ( ((javaEventMask & MOUSE_EVENTS) == 0) && 5130 ((newEventMask & MOUSE_EVENTS) != 0) ) { 5131 SwingEventMonitor.addMouseListener(this); 5132 } 5133 if ( ((javaEventMask & MENU_EVENTS) == 0) && 5134 ((newEventMask & MENU_EVENTS) != 0) ) { 5135 SwingEventMonitor.addMenuListener(this); 5136 SwingEventMonitor.addPopupMenuListener(this); 5137 } 5138 if ( ((javaEventMask & POPUPMENU_EVENTS) == 0) && 5139 ((newEventMask & POPUPMENU_EVENTS) != 0) ) { 5140 SwingEventMonitor.addPopupMenuListener(this); 5141 } 5142 5143 javaEventMask = newEventMask; 5144 } 5145 5146 /** 5147 * Turn off event monitoring for the event type passed in 5148 * If necessary, remove the appropriate event listener (if 5149 * no other event of that type is being listened for) 5150 */ 5151 void removeJavaEventNotification(long type) { 5152 long newEventMask = javaEventMask & (~type); 5153 /* 5154 if ( ((javaEventMask & PROPERTY_EVENTS) != 0) && 5155 ((newEventMask & PROPERTY_EVENTS) == 0) ) { 5156 AccessibilityEventMonitor.removePropertyChangeListener(this); 5157 } 5158 */ 5159 if (((javaEventMask & FOCUS_EVENTS) != 0) && 5160 ((newEventMask & FOCUS_EVENTS) == 0)) { 5161 SwingEventMonitor.removeFocusListener(this); 5162 } 5163 if (((javaEventMask & CARET_EVENTS) != 0) && 5164 ((newEventMask & CARET_EVENTS) == 0)) { 5165 SwingEventMonitor.removeCaretListener(this); 5166 } 5167 if (((javaEventMask & MOUSE_EVENTS) == 0) && 5168 ((newEventMask & MOUSE_EVENTS) != 0)) { 5169 SwingEventMonitor.removeMouseListener(this); 5170 } 5171 if (((javaEventMask & MENU_EVENTS) == 0) && 5172 ((newEventMask & MENU_EVENTS) != 0)) { 5173 SwingEventMonitor.removeMenuListener(this); 5174 } 5175 if (((javaEventMask & POPUPMENU_EVENTS) == 0) && 5176 ((newEventMask & POPUPMENU_EVENTS) != 0)) { 5177 SwingEventMonitor.removePopupMenuListener(this); 5178 } 5179 5180 javaEventMask = newEventMask; 5181 } 5182 5183 /** 5184 * Turn on event monitoring for the event type passed in 5185 * If necessary, add the appropriate event listener (if 5186 * no other event of that type is being listened for) 5187 */ 5188 void addAccessibilityEventNotification(long type) { 5189 long newEventMask = accessibilityEventMask | type; 5190 if ( ((accessibilityEventMask & PROPERTY_EVENTS) == 0) && 5191 ((newEventMask & PROPERTY_EVENTS) != 0) ) { 5192 AccessibilityEventMonitor.addPropertyChangeListener(this); 5193 } 5194 accessibilityEventMask = newEventMask; 5195 } 5196 5197 /** 5198 * Turn off event monitoring for the event type passed in 5199 * If necessary, remove the appropriate event listener (if 5200 * no other event of that type is being listened for) 5201 */ 5202 void removeAccessibilityEventNotification(long type) { 5203 long newEventMask = accessibilityEventMask & (~type); 5204 if ( ((accessibilityEventMask & PROPERTY_EVENTS) != 0) && 5205 ((newEventMask & PROPERTY_EVENTS) == 0) ) { 5206 AccessibilityEventMonitor.removePropertyChangeListener(this); 5207 } 5208 accessibilityEventMask = newEventMask; 5209 } 5210 5211 /** 5212 * ------- property change event glue 5213 */ 5214 // This is invoked on the EDT , as 5215 public void propertyChange(PropertyChangeEvent e) { 5216 5217 accessBridge.debugString("propertyChange(" + e.toString() + ") called"); 5218 5219 if (e != null && (accessibilityEventMask & PROPERTY_EVENTS) != 0) { 5220 Object o = e.getSource(); 5221 AccessibleContext ac; 5222 5223 if (o instanceof AccessibleContext) { 5224 ac = (AccessibleContext) o; 5225 } else { 5226 Accessible a = Translator.getAccessible(e.getSource()); 5227 if (a == null) 5228 return; 5229 else 5230 ac = a.getAccessibleContext(); 5231 } 5232 if (ac != null) { 5233 InvocationUtils.registerAccessibleContext(ac, AppContext.getAppContext()); 5234 5235 accessBridge.debugString("AccessibleContext: " + ac); 5236 String propertyName = e.getPropertyName(); 5237 5238 if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CARET_PROPERTY) == 0) { 5239 int oldValue = 0; 5240 int newValue = 0; 5241 5242 if (e.getOldValue() instanceof Integer) { 5243 oldValue = ((Integer) e.getOldValue()).intValue(); 5244 } 5245 if (e.getNewValue() instanceof Integer) { 5246 newValue = ((Integer) e.getNewValue()).intValue(); 5247 } 5248 accessBridge.debugString(" - about to call propertyCaretChange()"); 5249 accessBridge.debugString(" old value: " + oldValue + "new value: " + newValue); 5250 accessBridge.propertyCaretChange(e, ac, oldValue, newValue); 5251 5252 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY) == 0) { 5253 String oldValue = null; 5254 String newValue = null; 5255 5256 if (e.getOldValue() != null) { 5257 oldValue = e.getOldValue().toString(); 5258 } 5259 if (e.getNewValue() != null) { 5260 newValue = e.getNewValue().toString(); 5261 } 5262 accessBridge.debugString(" - about to call propertyDescriptionChange()"); 5263 accessBridge.debugString(" old value: " + oldValue + "new value: " + newValue); 5264 accessBridge.propertyDescriptionChange(e, ac, oldValue, newValue); 5265 5266 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_NAME_PROPERTY) == 0) { 5267 String oldValue = null; 5268 String newValue = null; 5269 5270 if (e.getOldValue() != null) { 5271 oldValue = e.getOldValue().toString(); 5272 } 5273 if (e.getNewValue() != null) { 5274 newValue = e.getNewValue().toString(); 5275 } 5276 accessBridge.debugString(" - about to call propertyNameChange()"); 5277 accessBridge.debugString(" old value: " + oldValue + " new value: " + newValue); 5278 accessBridge.propertyNameChange(e, ac, oldValue, newValue); 5279 5280 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY) == 0) { 5281 accessBridge.debugString(" - about to call propertySelectionChange() " + ac + " " + Thread.currentThread() + " " + e.getSource()); 5282 5283 accessBridge.propertySelectionChange(e, ac); 5284 5285 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_STATE_PROPERTY) == 0) { 5286 String oldValue = null; 5287 String newValue = null; 5288 5289 // Localization fix requested by Oliver for EA-1 5290 if (e.getOldValue() != null) { 5291 AccessibleState oldState = (AccessibleState) e.getOldValue(); 5292 oldValue = oldState.toDisplayString(Locale.US); 5293 } 5294 if (e.getNewValue() != null) { 5295 AccessibleState newState = (AccessibleState) e.getNewValue(); 5296 newValue = newState.toDisplayString(Locale.US); 5297 } 5298 5299 accessBridge.debugString(" - about to call propertyStateChange()"); 5300 accessBridge.propertyStateChange(e, ac, oldValue, newValue); 5301 5302 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_TEXT_PROPERTY) == 0) { 5303 accessBridge.debugString(" - about to call propertyTextChange()"); 5304 accessBridge.propertyTextChange(e, ac); 5305 5306 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY) == 0) { // strings 'cause of floating point, etc. 5307 String oldValue = null; 5308 String newValue = null; 5309 5310 if (e.getOldValue() != null) { 5311 oldValue = e.getOldValue().toString(); 5312 } 5313 if (e.getNewValue() != null) { 5314 newValue = e.getNewValue().toString(); 5315 } 5316 accessBridge.debugString(" - about to call propertyDescriptionChange()"); 5317 accessBridge.propertyValueChange(e, ac, oldValue, newValue); 5318 5319 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY) == 0) { 5320 accessBridge.propertyVisibleDataChange(e, ac); 5321 5322 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) { 5323 AccessibleContext oldAC = null; 5324 AccessibleContext newAC = null; 5325 Accessible a; 5326 5327 if (e.getOldValue() instanceof AccessibleContext) { 5328 oldAC = (AccessibleContext) e.getOldValue(); 5329 InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext()); 5330 } 5331 if (e.getNewValue() instanceof AccessibleContext) { 5332 newAC = (AccessibleContext) e.getNewValue(); 5333 InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext()); 5334 } 5335 accessBridge.debugString(" - about to call propertyChildChange()"); 5336 accessBridge.debugString(" old AC: " + oldAC + "new AC: " + newAC); 5337 accessBridge.propertyChildChange(e, ac, oldAC, newAC); 5338 5339 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0) { 5340 handleActiveDescendentEvent(e, ac); 5341 } 5342 } 5343 } 5344 } 5345 5346 /* 5347 * Handle an ActiveDescendent PropertyChangeEvent. This 5348 * method works around a JTree bug where ActiveDescendent 5349 * PropertyChangeEvents have the wrong parent. 5350 */ 5351 private AccessibleContext prevAC = null; // previous AccessibleContext 5352 5353 private void handleActiveDescendentEvent(PropertyChangeEvent e, 5354 AccessibleContext ac) { 5355 if (e == null || ac == null) 5356 return; 5357 AccessibleContext oldAC = null; 5358 AccessibleContext newAC = null; 5359 Accessible a; 5360 5361 // get the old active descendent 5362 if (e.getOldValue() instanceof Accessible) { 5363 oldAC = ((Accessible) e.getOldValue()).getAccessibleContext(); 5364 } else if (e.getOldValue() instanceof Component) { 5365 a = Translator.getAccessible(e.getOldValue()); 5366 if (a != null) { 5367 oldAC = a.getAccessibleContext(); 5368 } 5369 } 5370 if (oldAC != null) { 5371 Accessible parent = oldAC.getAccessibleParent(); 5372 if (parent instanceof JTree) { 5373 // use the previous AccessibleJTreeNode 5374 oldAC = prevAC; 5375 } 5376 } 5377 5378 // get the new active descendent 5379 if (e.getNewValue() instanceof Accessible) { 5380 newAC = ((Accessible) e.getNewValue()).getAccessibleContext(); 5381 } else if (e.getNewValue() instanceof Component) { 5382 a = Translator.getAccessible(e.getNewValue()); 5383 if (a != null) { 5384 newAC = a.getAccessibleContext(); 5385 } 5386 } 5387 if (newAC != null) { 5388 Accessible parent = newAC.getAccessibleParent(); 5389 if (parent instanceof JTree) { 5390 // use a new AccessibleJTreeNode with the right parent 5391 JTree tree = (JTree)parent; 5392 newAC = new AccessibleJTreeNode(tree, 5393 tree.getSelectionPath(), 5394 null); 5395 } 5396 } 5397 prevAC = newAC; 5398 5399 accessBridge.debugString(" - about to call propertyActiveDescendentChange()"); 5400 accessBridge.debugString(" AC: " + ac); 5401 accessBridge.debugString(" old AC: " + oldAC + "new AC: " + newAC); 5402 5403 InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext()); 5404 InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext()); 5405 accessBridge.propertyActiveDescendentChange(e, ac, oldAC, newAC); 5406 } 5407 5408 /** 5409 * ------- focus event glue 5410 */ 5411 private boolean stateChangeListenerAdded = false; 5412 5413 public void focusGained(FocusEvent e) { 5414 processFocusGained(); 5415 } 5416 5417 public void stateChanged(ChangeEvent e) { 5418 processFocusGained(); 5419 } 5420 5421 private void processFocusGained() { 5422 Component focusOwner = KeyboardFocusManager. 5423 getCurrentKeyboardFocusManager().getFocusOwner(); 5424 if (focusOwner == null) { 5425 return; 5426 } 5427 5428 // Only menus and popup selections are handled by the JRootPane. 5429 if (focusOwner instanceof JRootPane) { 5430 MenuElement [] path = 5431 MenuSelectionManager.defaultManager().getSelectedPath(); 5432 if (path.length > 1) { 5433 Component penult = path[path.length-2].getComponent(); 5434 Component last = path[path.length-1].getComponent(); 5435 5436 if (last instanceof JPopupMenu) { 5437 // This is a popup with nothing in the popup 5438 // selected. The menu itself is selected. 5439 FocusEvent e = new FocusEvent(penult, FocusEvent.FOCUS_GAINED); 5440 AccessibleContext context = penult.getAccessibleContext(); 5441 InvocationUtils.registerAccessibleContext(context, SunToolkit.targetToAppContext(penult)); 5442 accessBridge.focusGained(e, context); 5443 } else if (penult instanceof JPopupMenu) { 5444 // This is a popup with an item selected 5445 FocusEvent e = 5446 new FocusEvent(last, FocusEvent.FOCUS_GAINED); 5447 accessBridge.debugString(" - about to call focusGained()"); 5448 AccessibleContext focusedAC = last.getAccessibleContext(); 5449 InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(last)); 5450 accessBridge.debugString(" AC: " + focusedAC); 5451 accessBridge.focusGained(e, focusedAC); 5452 } 5453 } 5454 } else { 5455 // The focus owner has the selection. 5456 if (focusOwner instanceof Accessible) { 5457 FocusEvent e = new FocusEvent(focusOwner, 5458 FocusEvent.FOCUS_GAINED); 5459 accessBridge.debugString(" - about to call focusGained()"); 5460 AccessibleContext focusedAC = focusOwner.getAccessibleContext(); 5461 InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(focusOwner)); 5462 accessBridge.debugString(" AC: " + focusedAC); 5463 accessBridge.focusGained(e, focusedAC); 5464 } 5465 } 5466 } 5467 5468 public void focusLost(FocusEvent e) { 5469 if (e != null && (javaEventMask & FOCUS_LOST_EVENTS) != 0) { 5470 Accessible a = Translator.getAccessible(e.getSource()); 5471 if (a != null) { 5472 accessBridge.debugString(" - about to call focusLost()"); 5473 accessBridge.debugString(" AC: " + a.getAccessibleContext()); 5474 AccessibleContext context = a.getAccessibleContext(); 5475 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5476 accessBridge.focusLost(e, context); 5477 } 5478 } 5479 } 5480 5481 /** 5482 * ------- caret event glue 5483 */ 5484 public void caretUpdate(CaretEvent e) { 5485 if (e != null && (javaEventMask & CARET_UPATE_EVENTS) != 0) { 5486 Accessible a = Translator.getAccessible(e.getSource()); 5487 if (a != null) { 5488 AccessibleContext context = a.getAccessibleContext(); 5489 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5490 accessBridge.caretUpdate(e, context); 5491 } 5492 } 5493 } 5494 5495 /** 5496 * ------- mouse event glue 5497 */ 5498 5499 public void mouseClicked(MouseEvent e) { 5500 if (e != null && (javaEventMask & MOUSE_CLICKED_EVENTS) != 0) { 5501 Accessible a = Translator.getAccessible(e.getSource()); 5502 if (a != null) { 5503 AccessibleContext context = a.getAccessibleContext(); 5504 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5505 accessBridge.mouseClicked(e, context); 5506 } 5507 } 5508 } 5509 5510 public void mouseEntered(MouseEvent e) { 5511 if (e != null && (javaEventMask & MOUSE_ENTERED_EVENTS) != 0) { 5512 Accessible a = Translator.getAccessible(e.getSource()); 5513 if (a != null) { 5514 AccessibleContext context = a.getAccessibleContext(); 5515 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5516 accessBridge.mouseEntered(e, context); 5517 } 5518 } 5519 } 5520 5521 public void mouseExited(MouseEvent e) { 5522 if (e != null && (javaEventMask & MOUSE_EXITED_EVENTS) != 0) { 5523 Accessible a = Translator.getAccessible(e.getSource()); 5524 if (a != null) { 5525 AccessibleContext context = a.getAccessibleContext(); 5526 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5527 accessBridge.mouseExited(e, context); 5528 } 5529 } 5530 } 5531 5532 public void mousePressed(MouseEvent e) { 5533 if (e != null && (javaEventMask & MOUSE_PRESSED_EVENTS) != 0) { 5534 Accessible a = Translator.getAccessible(e.getSource()); 5535 if (a != null) { 5536 AccessibleContext context = a.getAccessibleContext(); 5537 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5538 accessBridge.mousePressed(e, context); 5539 } 5540 } 5541 } 5542 5543 public void mouseReleased(MouseEvent e) { 5544 if (e != null && (javaEventMask & MOUSE_RELEASED_EVENTS) != 0) { 5545 Accessible a = Translator.getAccessible(e.getSource()); 5546 if (a != null) { 5547 AccessibleContext context = a.getAccessibleContext(); 5548 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5549 accessBridge.mouseReleased(e, context); 5550 } 5551 } 5552 } 5553 5554 /** 5555 * ------- menu event glue 5556 */ 5557 public void menuCanceled(MenuEvent e) { 5558 if (e != null && (javaEventMask & MENU_CANCELED_EVENTS) != 0) { 5559 Accessible a = Translator.getAccessible(e.getSource()); 5560 if (a != null) { 5561 AccessibleContext context = a.getAccessibleContext(); 5562 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5563 accessBridge.menuCanceled(e, context); 5564 } 5565 } 5566 } 5567 5568 public void menuDeselected(MenuEvent e) { 5569 if (e != null && (javaEventMask & MENU_DESELECTED_EVENTS) != 0) { 5570 Accessible a = Translator.getAccessible(e.getSource()); 5571 if (a != null) { 5572 AccessibleContext context = a.getAccessibleContext(); 5573 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5574 accessBridge.menuDeselected(e, context); 5575 } 5576 } 5577 } 5578 5579 public void menuSelected(MenuEvent e) { 5580 if (e != null && (javaEventMask & MENU_SELECTED_EVENTS) != 0) { 5581 Accessible a = Translator.getAccessible(e.getSource()); 5582 if (a != null) { 5583 AccessibleContext context = a.getAccessibleContext(); 5584 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5585 accessBridge.menuSelected(e, context); 5586 } 5587 } 5588 } 5589 5590 public void popupMenuCanceled(PopupMenuEvent e) { 5591 if (e != null && (javaEventMask & POPUPMENU_CANCELED_EVENTS) != 0) { 5592 Accessible a = Translator.getAccessible(e.getSource()); 5593 if (a != null) { 5594 AccessibleContext context = a.getAccessibleContext(); 5595 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5596 accessBridge.popupMenuCanceled(e, context); 5597 } 5598 } 5599 } 5600 5601 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { 5602 if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS) != 0) { 5603 Accessible a = Translator.getAccessible(e.getSource()); 5604 if (a != null) { 5605 AccessibleContext context = a.getAccessibleContext(); 5606 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5607 accessBridge.popupMenuWillBecomeInvisible(e, context); 5608 } 5609 } 5610 } 5611 5612 public void popupMenuWillBecomeVisible(PopupMenuEvent e) { 5613 if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_VISIBLE_EVENTS) != 0) { 5614 Accessible a = Translator.getAccessible(e.getSource()); 5615 if (a != null) { 5616 AccessibleContext context = a.getAccessibleContext(); 5617 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5618 accessBridge.popupMenuWillBecomeVisible(e, context); 5619 } 5620 } 5621 } 5622 5623 } // End of EventHandler Class 5624 5625 // --------- Event Notification Registration methods 5626 5627 /** 5628 * Wrapper method around eventHandler.addJavaEventNotification() 5629 */ 5630 private void addJavaEventNotification(final long type) { 5631 EventQueue.invokeLater(new Runnable() { 5632 public void run(){ 5633 eventHandler.addJavaEventNotification(type); 5634 } 5635 }); 5636 } 5637 5638 /** 5639 * Wrapper method around eventHandler.removeJavaEventNotification() 5640 */ 5641 private void removeJavaEventNotification(final long type) { 5642 EventQueue.invokeLater(new Runnable() { 5643 public void run(){ 5644 eventHandler.removeJavaEventNotification(type); 5645 } 5646 }); 5647 } 5648 5649 5650 /** 5651 * Wrapper method around eventHandler.addAccessibilityEventNotification() 5652 */ 5653 private void addAccessibilityEventNotification(final long type) { 5654 EventQueue.invokeLater(new Runnable() { 5655 public void run(){ 5656 eventHandler.addAccessibilityEventNotification(type); 5657 } 5658 }); 5659 } 5660 5661 /** 5662 * Wrapper method around eventHandler.removeAccessibilityEventNotification() 5663 */ 5664 private void removeAccessibilityEventNotification(final long type) { 5665 EventQueue.invokeLater(new Runnable() { 5666 public void run(){ 5667 eventHandler.removeAccessibilityEventNotification(type); 5668 } 5669 }); 5670 } 5671 5672 /** 5673 ****************************************************** 5674 * All AccessibleRoles 5675 * 5676 * We shouldn't have to do this since it requires us 5677 * to synchronize the allAccessibleRoles array when 5678 * the AccessibleRoles class interface changes. However, 5679 * there is no Accessibility API method to get all 5680 * AccessibleRoles 5681 ****************************************************** 5682 */ 5683 private AccessibleRole [] allAccessibleRoles = { 5684 /** 5685 * Object is used to alert the user about something. 5686 */ 5687 AccessibleRole.ALERT, 5688 5689 /** 5690 * The header for a column of data. 5691 */ 5692 AccessibleRole.COLUMN_HEADER, 5693 5694 /** 5695 * Object that can be drawn into and is used to trap 5696 * events. 5697 * @see #FRAME 5698 * @see #GLASS_PANE 5699 * @see #LAYERED_PANE 5700 */ 5701 AccessibleRole.CANVAS, 5702 5703 /** 5704 * A list of choices the user can select from. Also optionally 5705 * allows the user to enter a choice of their own. 5706 */ 5707 AccessibleRole.COMBO_BOX, 5708 5709 /** 5710 * An iconified internal frame in a DESKTOP_PANE. 5711 * @see #DESKTOP_PANE 5712 * @see #INTERNAL_FRAME 5713 */ 5714 AccessibleRole.DESKTOP_ICON, 5715 5716 /** 5717 * A frame-like object that is clipped by a desktop pane. The 5718 * desktop pane, internal frame, and desktop icon objects are 5719 * often used to create multiple document interfaces within an 5720 * application. 5721 * @see #DESKTOP_ICON 5722 * @see #DESKTOP_PANE 5723 * @see #FRAME 5724 */ 5725 AccessibleRole.INTERNAL_FRAME, 5726 5727 /** 5728 * A pane that supports internal frames and 5729 * iconified versions of those internal frames. 5730 * @see #DESKTOP_ICON 5731 * @see #INTERNAL_FRAME 5732 */ 5733 AccessibleRole.DESKTOP_PANE, 5734 5735 /** 5736 * A specialized pane whose primary use is inside a DIALOG 5737 * @see #DIALOG 5738 */ 5739 AccessibleRole.OPTION_PANE, 5740 5741 /** 5742 * A top level window with no title or border. 5743 * @see #FRAME 5744 * @see #DIALOG 5745 */ 5746 AccessibleRole.WINDOW, 5747 5748 /** 5749 * A top level window with a title bar, border, menu bar, etc. It is 5750 * often used as the primary window for an application. 5751 * @see #DIALOG 5752 * @see #CANVAS 5753 * @see #WINDOW 5754 */ 5755 AccessibleRole.FRAME, 5756 5757 /** 5758 * A top level window with title bar and a border. A dialog is similar 5759 * to a frame, but it has fewer properties and is often used as a 5760 * secondary window for an application. 5761 * @see #FRAME 5762 * @see #WINDOW 5763 */ 5764 AccessibleRole.DIALOG, 5765 5766 /** 5767 * A specialized dialog that lets the user choose a color. 5768 */ 5769 AccessibleRole.COLOR_CHOOSER, 5770 5771 5772 /** 5773 * A pane that allows the user to navigate through 5774 * and select the contents of a directory. May be used 5775 * by a file chooser. 5776 * @see #FILE_CHOOSER 5777 */ 5778 AccessibleRole.DIRECTORY_PANE, 5779 5780 /** 5781 * A specialized dialog that displays the files in the directory 5782 * and lets the user select a file, browse a different directory, 5783 * or specify a filename. May use the directory pane to show the 5784 * contents of a directory. 5785 * @see #DIRECTORY_PANE 5786 */ 5787 AccessibleRole.FILE_CHOOSER, 5788 5789 /** 5790 * An object that fills up space in a user interface. It is often 5791 * used in interfaces to tweak the spacing between components, 5792 * but serves no other purpose. 5793 */ 5794 AccessibleRole.FILLER, 5795 5796 /** 5797 * A hypertext anchor 5798 */ 5799 // AccessibleRole.HYPERLINK, 5800 5801 /** 5802 * A small fixed size picture, typically used to decorate components. 5803 */ 5804 AccessibleRole.ICON, 5805 5806 /** 5807 * An object used to present an icon or short string in an interface. 5808 */ 5809 AccessibleRole.LABEL, 5810 5811 /** 5812 * A specialized pane that has a glass pane and a layered pane as its 5813 * children. 5814 * @see #GLASS_PANE 5815 * @see #LAYERED_PANE 5816 */ 5817 AccessibleRole.ROOT_PANE, 5818 5819 /** 5820 * A pane that is guaranteed to be painted on top 5821 * of all panes beneath it. 5822 * @see #ROOT_PANE 5823 * @see #CANVAS 5824 */ 5825 AccessibleRole.GLASS_PANE, 5826 5827 /** 5828 * A specialized pane that allows its children to be drawn in layers, 5829 * providing a form of stacking order. This is usually the pane that 5830 * holds the menu bar as well as the pane that contains most of the 5831 * visual components in a window. 5832 * @see #GLASS_PANE 5833 * @see #ROOT_PANE 5834 */ 5835 AccessibleRole.LAYERED_PANE, 5836 5837 /** 5838 * An object that presents a list of objects to the user and allows the 5839 * user to select one or more of them. A list is usually contained 5840 * within a scroll pane. 5841 * @see #SCROLL_PANE 5842 * @see #LIST_ITEM 5843 */ 5844 AccessibleRole.LIST, 5845 5846 /** 5847 * An object that presents an element in a list. A list is usually 5848 * contained within a scroll pane. 5849 * @see #SCROLL_PANE 5850 * @see #LIST 5851 */ 5852 AccessibleRole.LIST_ITEM, 5853 5854 /** 5855 * An object usually drawn at the top of the primary dialog box of 5856 * an application that contains a list of menus the user can choose 5857 * from. For example, a menu bar might contain menus for "File," 5858 * "Edit," and "Help." 5859 * @see #MENU 5860 * @see #POPUP_MENU 5861 * @see #LAYERED_PANE 5862 */ 5863 AccessibleRole.MENU_BAR, 5864 5865 /** 5866 * A temporary window that is usually used to offer the user a 5867 * list of choices, and then hides when the user selects one of 5868 * those choices. 5869 * @see #MENU 5870 * @see #MENU_ITEM 5871 */ 5872 AccessibleRole.POPUP_MENU, 5873 5874 /** 5875 * An object usually found inside a menu bar that contains a list 5876 * of actions the user can choose from. A menu can have any object 5877 * as its children, but most often they are menu items, other menus, 5878 * or rudimentary objects such as radio buttons, check boxes, or 5879 * separators. For example, an application may have an "Edit" menu 5880 * that contains menu items for "Cut" and "Paste." 5881 * @see #MENU_BAR 5882 * @see #MENU_ITEM 5883 * @see #SEPARATOR 5884 * @see #RADIO_BUTTON 5885 * @see #CHECK_BOX 5886 * @see #POPUP_MENU 5887 */ 5888 AccessibleRole.MENU, 5889 5890 /** 5891 * An object usually contained in a menu that presents an action 5892 * the user can choose. For example, the "Cut" menu item in an 5893 * "Edit" menu would be an action the user can select to cut the 5894 * selected area of text in a document. 5895 * @see #MENU_BAR 5896 * @see #SEPARATOR 5897 * @see #POPUP_MENU 5898 */ 5899 AccessibleRole.MENU_ITEM, 5900 5901 /** 5902 * An object usually contained in a menu to provide a visual 5903 * and logical separation of the contents in a menu. For example, 5904 * the "File" menu of an application might contain menu items for 5905 * "Open," "Close," and "Exit," and will place a separator between 5906 * "Close" and "Exit" menu items. 5907 * @see #MENU 5908 * @see #MENU_ITEM 5909 */ 5910 AccessibleRole.SEPARATOR, 5911 5912 /** 5913 * An object that presents a series of panels (or page tabs), one at a 5914 * time, through some mechanism provided by the object. The most common 5915 * mechanism is a list of tabs at the top of the panel. The children of 5916 * a page tab list are all page tabs. 5917 * @see #PAGE_TAB 5918 */ 5919 AccessibleRole.PAGE_TAB_LIST, 5920 5921 /** 5922 * An object that is a child of a page tab list. Its sole child is 5923 * the panel that is to be presented to the user when the user 5924 * selects the page tab from the list of tabs in the page tab list. 5925 * @see #PAGE_TAB_LIST 5926 */ 5927 AccessibleRole.PAGE_TAB, 5928 5929 /** 5930 * A generic container that is often used to group objects. 5931 */ 5932 AccessibleRole.PANEL, 5933 5934 /** 5935 * An object used to indicate how much of a task has been completed. 5936 */ 5937 AccessibleRole.PROGRESS_BAR, 5938 5939 /** 5940 * A text object used for passwords, or other places where the 5941 * text contents is not shown visibly to the user 5942 */ 5943 AccessibleRole.PASSWORD_TEXT, 5944 5945 /** 5946 * An object the user can manipulate to tell the application to do 5947 * something. 5948 * @see #CHECK_BOX 5949 * @see #TOGGLE_BUTTON 5950 * @see #RADIO_BUTTON 5951 */ 5952 AccessibleRole.PUSH_BUTTON, 5953 5954 /** 5955 * A specialized push button that can be checked or unchecked, but 5956 * does not provide a separate indicator for the current state. 5957 * @see #PUSH_BUTTON 5958 * @see #CHECK_BOX 5959 * @see #RADIO_BUTTON 5960 */ 5961 AccessibleRole.TOGGLE_BUTTON, 5962 5963 /** 5964 * A choice that can be checked or unchecked and provides a 5965 * separate indicator for the current state. 5966 * @see #PUSH_BUTTON 5967 * @see #TOGGLE_BUTTON 5968 * @see #RADIO_BUTTON 5969 */ 5970 AccessibleRole.CHECK_BOX, 5971 5972 /** 5973 * A specialized check box that will cause other radio buttons in the 5974 * same group to become unchecked when this one is checked. 5975 * @see #PUSH_BUTTON 5976 * @see #TOGGLE_BUTTON 5977 * @see #CHECK_BOX 5978 */ 5979 AccessibleRole.RADIO_BUTTON, 5980 5981 /** 5982 * The header for a row of data. 5983 */ 5984 AccessibleRole.ROW_HEADER, 5985 5986 /** 5987 * An object that allows a user to incrementally view a large amount 5988 * of information. Its children can include scroll bars and a viewport. 5989 * @see #SCROLL_BAR 5990 * @see #VIEWPORT 5991 */ 5992 AccessibleRole.SCROLL_PANE, 5993 5994 /** 5995 * An object usually used to allow a user to incrementally view a 5996 * large amount of data. Usually used only by a scroll pane. 5997 * @see #SCROLL_PANE 5998 */ 5999 AccessibleRole.SCROLL_BAR, 6000 6001 /** 6002 * An object usually used in a scroll pane. It represents the portion 6003 * of the entire data that the user can see. As the user manipulates 6004 * the scroll bars, the contents of the viewport can change. 6005 * @see #SCROLL_PANE 6006 */ 6007 AccessibleRole.VIEWPORT, 6008 6009 /** 6010 * An object that allows the user to select from a bounded range. For 6011 * example, a slider might be used to select a number between 0 and 100. 6012 */ 6013 AccessibleRole.SLIDER, 6014 6015 /** 6016 * A specialized panel that presents two other panels at the same time. 6017 * Between the two panels is a divider the user can manipulate to make 6018 * one panel larger and the other panel smaller. 6019 */ 6020 AccessibleRole.SPLIT_PANE, 6021 6022 /** 6023 * An object used to present information in terms of rows and columns. 6024 * An example might include a spreadsheet application. 6025 */ 6026 AccessibleRole.TABLE, 6027 6028 /** 6029 * An object that presents text to the user. The text is usually 6030 * editable by the user as opposed to a label. 6031 * @see #LABEL 6032 */ 6033 AccessibleRole.TEXT, 6034 6035 /** 6036 * An object used to present hierarchical information to the user. 6037 * The individual nodes in the tree can be collapsed and expanded 6038 * to provide selective disclosure of the tree's contents. 6039 */ 6040 AccessibleRole.TREE, 6041 6042 /** 6043 * A bar or palette usually composed of push buttons or toggle buttons. 6044 * It is often used to provide the most frequently used functions for an 6045 * application. 6046 */ 6047 AccessibleRole.TOOL_BAR, 6048 6049 /** 6050 * An object that provides information about another object. The 6051 * accessibleDescription property of the tool tip is often displayed 6052 * to the user in a small "help bubble" when the user causes the 6053 * mouse to hover over the object associated with the tool tip. 6054 */ 6055 AccessibleRole.TOOL_TIP, 6056 6057 /** 6058 * An AWT component, but nothing else is known about it. 6059 * @see #SWING_COMPONENT 6060 * @see #UNKNOWN 6061 */ 6062 AccessibleRole.AWT_COMPONENT, 6063 6064 /** 6065 * A Swing component, but nothing else is known about it. 6066 * @see #AWT_COMPONENT 6067 * @see #UNKNOWN 6068 */ 6069 AccessibleRole.SWING_COMPONENT, 6070 6071 /** 6072 * The object contains some Accessible information, but its role is 6073 * not known. 6074 * @see #AWT_COMPONENT 6075 * @see #SWING_COMPONENT 6076 */ 6077 AccessibleRole.UNKNOWN, 6078 6079 // These roles are available since JDK 1.4 6080 6081 /** 6082 * A STATUS_BAR is an simple component that can contain 6083 * multiple labels of status information to the user. 6084 AccessibleRole.STATUS_BAR, 6085 6086 /** 6087 * A DATE_EDITOR is a component that allows users to edit 6088 * java.util.Date and java.util.Time objects 6089 AccessibleRole.DATE_EDITOR, 6090 6091 /** 6092 * A SPIN_BOX is a simple spinner component and its main use 6093 * is for simple numbers. 6094 AccessibleRole.SPIN_BOX, 6095 6096 /** 6097 * A FONT_CHOOSER is a component that lets the user pick various 6098 * attributes for fonts. 6099 AccessibleRole.FONT_CHOOSER, 6100 6101 /** 6102 * A GROUP_BOX is a simple container that contains a border 6103 * around it and contains components inside it. 6104 AccessibleRole.GROUP_BOX 6105 6106 /** 6107 * Since JDK 1.5 6108 * 6109 * A text header 6110 6111 AccessibleRole.HEADER, 6112 6113 /** 6114 * A text footer 6115 6116 AccessibleRole.FOOTER, 6117 6118 /** 6119 * A text paragraph 6120 6121 AccessibleRole.PARAGRAPH, 6122 6123 /** 6124 * A ruler is an object used to measure distance 6125 6126 AccessibleRole.RULER, 6127 6128 /** 6129 * A role indicating the object acts as a formula for 6130 * calculating a value. An example is a formula in 6131 * a spreadsheet cell. 6132 AccessibleRole.EDITBAR 6133 */ 6134 }; 6135 6136 /** 6137 * This class implements accessibility support for the 6138 * <code>JTree</code> child. It provides an implementation of the 6139 * Java Accessibility API appropriate to tree nodes. 6140 * 6141 * Copied from JTree.java to work around a JTree bug where 6142 * ActiveDescendent PropertyChangeEvents contain the wrong 6143 * parent. 6144 */ 6145 /** 6146 * This class in invoked on the EDT as its part of ActiveDescendant, 6147 * hence the calls do not need to be specifically made on the EDT 6148 */ 6149 private class AccessibleJTreeNode extends AccessibleContext 6150 implements Accessible, AccessibleComponent, AccessibleSelection, 6151 AccessibleAction { 6152 6153 private JTree tree = null; 6154 private TreeModel treeModel = null; 6155 private Object obj = null; 6156 private TreePath path = null; 6157 private Accessible accessibleParent = null; 6158 private int index = 0; 6159 private boolean isLeaf = false; 6160 6161 /** 6162 * Constructs an AccessibleJTreeNode 6163 */ 6164 AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) { 6165 tree = t; 6166 path = p; 6167 accessibleParent = ap; 6168 if (t != null) 6169 treeModel = t.getModel(); 6170 if (p != null) { 6171 obj = p.getLastPathComponent(); 6172 if (treeModel != null && obj != null) { 6173 isLeaf = treeModel.isLeaf(obj); 6174 } 6175 } 6176 debugString("AccessibleJTreeNode: name = "+getAccessibleName()+"; TreePath = "+p+"; parent = "+ap); 6177 } 6178 6179 private TreePath getChildTreePath(int i) { 6180 // Tree nodes can't be so complex that they have 6181 // two sets of children -> we're ignoring that case 6182 if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) { 6183 return null; 6184 } else { 6185 Object childObj = treeModel.getChild(obj, i); 6186 Object[] objPath = path.getPath(); 6187 Object[] objChildPath = new Object[objPath.length+1]; 6188 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length); 6189 objChildPath[objChildPath.length-1] = childObj; 6190 return new TreePath(objChildPath); 6191 } 6192 } 6193 6194 /** 6195 * Get the AccessibleContext associated with this tree node. 6196 * In the implementation of the Java Accessibility API for 6197 * this class, return this object, which is its own 6198 * AccessibleContext. 6199 * 6200 * @return this object 6201 */ 6202 public AccessibleContext getAccessibleContext() { 6203 return this; 6204 } 6205 6206 private AccessibleContext getCurrentAccessibleContext() { 6207 Component c = getCurrentComponent(); 6208 if (c instanceof Accessible) { 6209 return (c.getAccessibleContext()); 6210 } else { 6211 return null; 6212 } 6213 } 6214 6215 private Component getCurrentComponent() { 6216 debugString("AccessibleJTreeNode: getCurrentComponent"); 6217 // is the object visible? 6218 // if so, get row, selected, focus & leaf state, 6219 // and then get the renderer component and return it 6220 if (tree != null && tree.isVisible(path)) { 6221 TreeCellRenderer r = tree.getCellRenderer(); 6222 if (r == null) { 6223 debugString(" returning null 1"); 6224 return null; 6225 } 6226 TreeUI ui = tree.getUI(); 6227 if (ui != null) { 6228 int row = ui.getRowForPath(tree, path); 6229 boolean selected = tree.isPathSelected(path); 6230 boolean expanded = tree.isExpanded(path); 6231 boolean hasFocus = false; // how to tell?? -PK 6232 Component retval = r.getTreeCellRendererComponent(tree, obj, 6233 selected, expanded, 6234 isLeaf, row, hasFocus); 6235 debugString(" returning = "+retval.getClass()); 6236 return retval; 6237 } 6238 } 6239 debugString(" returning null 2"); 6240 return null; 6241 } 6242 6243 // AccessibleContext methods 6244 6245 /** 6246 * Get the accessible name of this object. 6247 * 6248 * @return the localized name of the object; null if this 6249 * object does not have a name 6250 */ 6251 public String getAccessibleName() { 6252 debugString("AccessibleJTreeNode: getAccessibleName"); 6253 AccessibleContext ac = getCurrentAccessibleContext(); 6254 if (ac != null) { 6255 String name = ac.getAccessibleName(); 6256 if ((name != null) && (!name.isEmpty())) { 6257 String retval = ac.getAccessibleName(); 6258 debugString(" returning "+retval); 6259 return retval; 6260 } else { 6261 return null; 6262 } 6263 } 6264 if ((accessibleName != null) && (accessibleName.isEmpty())) { 6265 return accessibleName; 6266 } else { 6267 return null; 6268 } 6269 } 6270 6271 /** 6272 * Set the localized accessible name of this object. 6273 * 6274 * @param s the new localized name of the object. 6275 */ 6276 public void setAccessibleName(String s) { 6277 AccessibleContext ac = getCurrentAccessibleContext(); 6278 if (ac != null) { 6279 ac.setAccessibleName(s); 6280 } else { 6281 super.setAccessibleName(s); 6282 } 6283 } 6284 6285 // 6286 // *** should check tooltip text for desc. (needs MouseEvent) 6287 // 6288 /** 6289 * Get the accessible description of this object. 6290 * 6291 * @return the localized description of the object; null if 6292 * this object does not have a description 6293 */ 6294 public String getAccessibleDescription() { 6295 AccessibleContext ac = getCurrentAccessibleContext(); 6296 if (ac != null) { 6297 return ac.getAccessibleDescription(); 6298 } else { 6299 return super.getAccessibleDescription(); 6300 } 6301 } 6302 6303 /** 6304 * Set the accessible description of this object. 6305 * 6306 * @param s the new localized description of the object 6307 */ 6308 public void setAccessibleDescription(String s) { 6309 AccessibleContext ac = getCurrentAccessibleContext(); 6310 if (ac != null) { 6311 ac.setAccessibleDescription(s); 6312 } else { 6313 super.setAccessibleDescription(s); 6314 } 6315 } 6316 6317 /** 6318 * Get the role of this object. 6319 * 6320 * @return an instance of AccessibleRole describing the role of the object 6321 * @see AccessibleRole 6322 */ 6323 public AccessibleRole getAccessibleRole() { 6324 AccessibleContext ac = getCurrentAccessibleContext(); 6325 if (ac != null) { 6326 return ac.getAccessibleRole(); 6327 } else { 6328 return AccessibleRole.UNKNOWN; 6329 } 6330 } 6331 6332 /** 6333 * Get the state set of this object. 6334 * 6335 * @return an instance of AccessibleStateSet containing the 6336 * current state set of the object 6337 * @see AccessibleState 6338 */ 6339 public AccessibleStateSet getAccessibleStateSet() { 6340 if (tree == null) 6341 return null; 6342 AccessibleContext ac = getCurrentAccessibleContext(); 6343 AccessibleStateSet states; 6344 int row = tree.getUI().getRowForPath(tree,path); 6345 int lsr = tree.getLeadSelectionRow(); 6346 if (ac != null) { 6347 states = ac.getAccessibleStateSet(); 6348 } else { 6349 states = new AccessibleStateSet(); 6350 } 6351 // need to test here, 'cause the underlying component 6352 // is a cellRenderer, which is never showing... 6353 if (isShowing()) { 6354 states.add(AccessibleState.SHOWING); 6355 } else if (states.contains(AccessibleState.SHOWING)) { 6356 states.remove(AccessibleState.SHOWING); 6357 } 6358 if (isVisible()) { 6359 states.add(AccessibleState.VISIBLE); 6360 } else if (states.contains(AccessibleState.VISIBLE)) { 6361 states.remove(AccessibleState.VISIBLE); 6362 } 6363 if (tree.isPathSelected(path)){ 6364 states.add(AccessibleState.SELECTED); 6365 } 6366 if (lsr == row) { 6367 states.add(AccessibleState.ACTIVE); 6368 } 6369 if (!isLeaf) { 6370 states.add(AccessibleState.EXPANDABLE); 6371 } 6372 if (tree.isExpanded(path)) { 6373 states.add(AccessibleState.EXPANDED); 6374 } else { 6375 states.add(AccessibleState.COLLAPSED); 6376 } 6377 if (tree.isEditable()) { 6378 states.add(AccessibleState.EDITABLE); 6379 } 6380 return states; 6381 } 6382 6383 /** 6384 * Get the Accessible parent of this object. 6385 * 6386 * @return the Accessible parent of this object; null if this 6387 * object does not have an Accessible parent 6388 */ 6389 public Accessible getAccessibleParent() { 6390 // someone wants to know, so we need to create our parent 6391 // if we don't have one (hey, we're a talented kid!) 6392 if (accessibleParent == null && path != null) { 6393 Object[] objPath = path.getPath(); 6394 if (objPath.length > 1) { 6395 Object objParent = objPath[objPath.length-2]; 6396 if (treeModel != null) { 6397 index = treeModel.getIndexOfChild(objParent, obj); 6398 } 6399 Object[] objParentPath = new Object[objPath.length-1]; 6400 java.lang.System.arraycopy(objPath, 0, objParentPath, 6401 0, objPath.length-1); 6402 TreePath parentPath = new TreePath(objParentPath); 6403 accessibleParent = new AccessibleJTreeNode(tree, 6404 parentPath, 6405 null); 6406 this.setAccessibleParent(accessibleParent); 6407 } else if (treeModel != null) { 6408 accessibleParent = tree; // we're the top! 6409 index = 0; // we're an only child! 6410 this.setAccessibleParent(accessibleParent); 6411 } 6412 } 6413 return accessibleParent; 6414 } 6415 6416 /** 6417 * Get the index of this object in its accessible parent. 6418 * 6419 * @return the index of this object in its parent; -1 if this 6420 * object does not have an accessible parent. 6421 * @see #getAccessibleParent 6422 */ 6423 public int getAccessibleIndexInParent() { 6424 // index is invalid 'till we have an accessibleParent... 6425 if (accessibleParent == null) { 6426 getAccessibleParent(); 6427 } 6428 if (path != null) { 6429 Object[] objPath = path.getPath(); 6430 if (objPath.length > 1) { 6431 Object objParent = objPath[objPath.length-2]; 6432 if (treeModel != null) { 6433 index = treeModel.getIndexOfChild(objParent, obj); 6434 } 6435 } 6436 } 6437 return index; 6438 } 6439 6440 /** 6441 * Returns the number of accessible children in the object. 6442 * 6443 * @return the number of accessible children in the object. 6444 */ 6445 public int getAccessibleChildrenCount() { 6446 // Tree nodes can't be so complex that they have 6447 // two sets of children -> we're ignoring that case 6448 if (obj != null && treeModel != null) { 6449 return treeModel.getChildCount(obj); 6450 } 6451 return 0; 6452 } 6453 6454 /** 6455 * Return the specified Accessible child of the object. 6456 * 6457 * @param i zero-based index of child 6458 * @return the Accessible child of the object 6459 */ 6460 public Accessible getAccessibleChild(int i) { 6461 // Tree nodes can't be so complex that they have 6462 // two sets of children -> we're ignoring that case 6463 if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) { 6464 return null; 6465 } else { 6466 Object childObj = treeModel.getChild(obj, i); 6467 Object[] objPath = path.getPath(); 6468 Object[] objChildPath = new Object[objPath.length+1]; 6469 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length); 6470 objChildPath[objChildPath.length-1] = childObj; 6471 TreePath childPath = new TreePath(objChildPath); 6472 return new AccessibleJTreeNode(tree, childPath, this); 6473 } 6474 } 6475 6476 /** 6477 * Gets the locale of the component. If the component does not have 6478 * a locale, then the locale of its parent is returned. 6479 * 6480 * @return This component's locale. If this component does not have 6481 * a locale, the locale of its parent is returned. 6482 * @exception IllegalComponentStateException 6483 * If the Component does not have its own locale and has not yet 6484 * been added to a containment hierarchy such that the locale can be 6485 * determined from the containing parent. 6486 * @see #setLocale 6487 */ 6488 public Locale getLocale() { 6489 if (tree == null) 6490 return null; 6491 AccessibleContext ac = getCurrentAccessibleContext(); 6492 if (ac != null) { 6493 return ac.getLocale(); 6494 } else { 6495 return tree.getLocale(); 6496 } 6497 } 6498 6499 /** 6500 * Add a PropertyChangeListener to the listener list. 6501 * The listener is registered for all properties. 6502 * 6503 * @param l The PropertyChangeListener to be added 6504 */ 6505 public void addPropertyChangeListener(PropertyChangeListener l) { 6506 AccessibleContext ac = getCurrentAccessibleContext(); 6507 if (ac != null) { 6508 ac.addPropertyChangeListener(l); 6509 } else { 6510 super.addPropertyChangeListener(l); 6511 } 6512 } 6513 6514 /** 6515 * Remove a PropertyChangeListener from the listener list. 6516 * This removes a PropertyChangeListener that was registered 6517 * for all properties. 6518 * 6519 * @param l The PropertyChangeListener to be removed 6520 */ 6521 public void removePropertyChangeListener(PropertyChangeListener l) { 6522 AccessibleContext ac = getCurrentAccessibleContext(); 6523 if (ac != null) { 6524 ac.removePropertyChangeListener(l); 6525 } else { 6526 super.removePropertyChangeListener(l); 6527 } 6528 } 6529 6530 /** 6531 * Get the AccessibleAction associated with this object. In the 6532 * implementation of the Java Accessibility API for this class, 6533 * return this object, which is responsible for implementing the 6534 * AccessibleAction interface on behalf of itself. 6535 * 6536 * @return this object 6537 */ 6538 public AccessibleAction getAccessibleAction() { 6539 return this; 6540 } 6541 6542 /** 6543 * Get the AccessibleComponent associated with this object. In the 6544 * implementation of the Java Accessibility API for this class, 6545 * return this object, which is responsible for implementing the 6546 * AccessibleComponent interface on behalf of itself. 6547 * 6548 * @return this object 6549 */ 6550 public AccessibleComponent getAccessibleComponent() { 6551 return this; // to override getBounds() 6552 } 6553 6554 /** 6555 * Get the AccessibleSelection associated with this object if one 6556 * exists. Otherwise return null. 6557 * 6558 * @return the AccessibleSelection, or null 6559 */ 6560 public AccessibleSelection getAccessibleSelection() { 6561 AccessibleContext ac = getCurrentAccessibleContext(); 6562 if (ac != null && isLeaf) { 6563 return getCurrentAccessibleContext().getAccessibleSelection(); 6564 } else { 6565 return this; 6566 } 6567 } 6568 6569 /** 6570 * Get the AccessibleText associated with this object if one 6571 * exists. Otherwise return null. 6572 * 6573 * @return the AccessibleText, or null 6574 */ 6575 public AccessibleText getAccessibleText() { 6576 AccessibleContext ac = getCurrentAccessibleContext(); 6577 if (ac != null) { 6578 return getCurrentAccessibleContext().getAccessibleText(); 6579 } else { 6580 return null; 6581 } 6582 } 6583 6584 /** 6585 * Get the AccessibleValue associated with this object if one 6586 * exists. Otherwise return null. 6587 * 6588 * @return the AccessibleValue, or null 6589 */ 6590 public AccessibleValue getAccessibleValue() { 6591 AccessibleContext ac = getCurrentAccessibleContext(); 6592 if (ac != null) { 6593 return getCurrentAccessibleContext().getAccessibleValue(); 6594 } else { 6595 return null; 6596 } 6597 } 6598 6599 6600 // AccessibleComponent methods 6601 6602 /** 6603 * Get the background color of this object. 6604 * 6605 * @return the background color, if supported, of the object; 6606 * otherwise, null 6607 */ 6608 public Color getBackground() { 6609 AccessibleContext ac = getCurrentAccessibleContext(); 6610 if (ac instanceof AccessibleComponent) { 6611 return ((AccessibleComponent) ac).getBackground(); 6612 } else { 6613 Component c = getCurrentComponent(); 6614 if (c != null) { 6615 return c.getBackground(); 6616 } else { 6617 return null; 6618 } 6619 } 6620 } 6621 6622 /** 6623 * Set the background color of this object. 6624 * 6625 * @param c the new Color for the background 6626 */ 6627 public void setBackground(Color c) { 6628 AccessibleContext ac = getCurrentAccessibleContext(); 6629 if (ac instanceof AccessibleComponent) { 6630 ((AccessibleComponent) ac).setBackground(c); 6631 } else { 6632 Component cp = getCurrentComponent(); 6633 if ( cp != null) { 6634 cp.setBackground(c); 6635 } 6636 } 6637 } 6638 6639 6640 /** 6641 * Get the foreground color of this object. 6642 * 6643 * @return the foreground color, if supported, of the object; 6644 * otherwise, null 6645 */ 6646 public Color getForeground() { 6647 AccessibleContext ac = getCurrentAccessibleContext(); 6648 if (ac instanceof AccessibleComponent) { 6649 return ((AccessibleComponent) ac).getForeground(); 6650 } else { 6651 Component c = getCurrentComponent(); 6652 if (c != null) { 6653 return c.getForeground(); 6654 } else { 6655 return null; 6656 } 6657 } 6658 } 6659 6660 public void setForeground(Color c) { 6661 AccessibleContext ac = getCurrentAccessibleContext(); 6662 if (ac instanceof AccessibleComponent) { 6663 ((AccessibleComponent) ac).setForeground(c); 6664 } else { 6665 Component cp = getCurrentComponent(); 6666 if (cp != null) { 6667 cp.setForeground(c); 6668 } 6669 } 6670 } 6671 6672 public Cursor getCursor() { 6673 AccessibleContext ac = getCurrentAccessibleContext(); 6674 if (ac instanceof AccessibleComponent) { 6675 return ((AccessibleComponent) ac).getCursor(); 6676 } else { 6677 Component c = getCurrentComponent(); 6678 if (c != null) { 6679 return c.getCursor(); 6680 } else { 6681 Accessible ap = getAccessibleParent(); 6682 if (ap instanceof AccessibleComponent) { 6683 return ((AccessibleComponent) ap).getCursor(); 6684 } else { 6685 return null; 6686 } 6687 } 6688 } 6689 } 6690 6691 public void setCursor(Cursor c) { 6692 AccessibleContext ac = getCurrentAccessibleContext(); 6693 if (ac instanceof AccessibleComponent) { 6694 ((AccessibleComponent) ac).setCursor(c); 6695 } else { 6696 Component cp = getCurrentComponent(); 6697 if (cp != null) { 6698 cp.setCursor(c); 6699 } 6700 } 6701 } 6702 6703 public Font getFont() { 6704 AccessibleContext ac = getCurrentAccessibleContext(); 6705 if (ac instanceof AccessibleComponent) { 6706 return ((AccessibleComponent) ac).getFont(); 6707 } else { 6708 Component c = getCurrentComponent(); 6709 if (c != null) { 6710 return c.getFont(); 6711 } else { 6712 return null; 6713 } 6714 } 6715 } 6716 6717 public void setFont(Font f) { 6718 AccessibleContext ac = getCurrentAccessibleContext(); 6719 if (ac instanceof AccessibleComponent) { 6720 ((AccessibleComponent) ac).setFont(f); 6721 } else { 6722 Component c = getCurrentComponent(); 6723 if (c != null) { 6724 c.setFont(f); 6725 } 6726 } 6727 } 6728 6729 public FontMetrics getFontMetrics(Font f) { 6730 AccessibleContext ac = getCurrentAccessibleContext(); 6731 if (ac instanceof AccessibleComponent) { 6732 return ((AccessibleComponent) ac).getFontMetrics(f); 6733 } else { 6734 Component c = getCurrentComponent(); 6735 if (c != null) { 6736 return c.getFontMetrics(f); 6737 } else { 6738 return null; 6739 } 6740 } 6741 } 6742 6743 public boolean isEnabled() { 6744 AccessibleContext ac = getCurrentAccessibleContext(); 6745 if (ac instanceof AccessibleComponent) { 6746 return ((AccessibleComponent) ac).isEnabled(); 6747 } else { 6748 Component c = getCurrentComponent(); 6749 if (c != null) { 6750 return c.isEnabled(); 6751 } else { 6752 return false; 6753 } 6754 } 6755 } 6756 6757 public void setEnabled(boolean b) { 6758 AccessibleContext ac = getCurrentAccessibleContext(); 6759 if (ac instanceof AccessibleComponent) { 6760 ((AccessibleComponent) ac).setEnabled(b); 6761 } else { 6762 Component c = getCurrentComponent(); 6763 if (c != null) { 6764 c.setEnabled(b); 6765 } 6766 } 6767 } 6768 6769 public boolean isVisible() { 6770 if (tree == null) 6771 return false; 6772 Rectangle pathBounds = tree.getPathBounds(path); 6773 Rectangle parentBounds = tree.getVisibleRect(); 6774 if ( pathBounds != null && parentBounds != null && 6775 parentBounds.intersects(pathBounds) ) { 6776 return true; 6777 } else { 6778 return false; 6779 } 6780 } 6781 6782 public void setVisible(boolean b) { 6783 } 6784 6785 public boolean isShowing() { 6786 return (tree.isShowing() && isVisible()); 6787 } 6788 6789 public boolean contains(Point p) { 6790 AccessibleContext ac = getCurrentAccessibleContext(); 6791 if (ac instanceof AccessibleComponent) { 6792 Rectangle r = ((AccessibleComponent) ac).getBounds(); 6793 return r.contains(p); 6794 } else { 6795 Component c = getCurrentComponent(); 6796 if (c != null) { 6797 Rectangle r = c.getBounds(); 6798 return r.contains(p); 6799 } else { 6800 return getBounds().contains(p); 6801 } 6802 } 6803 } 6804 6805 public Point getLocationOnScreen() { 6806 if (tree != null) { 6807 Point treeLocation = tree.getLocationOnScreen(); 6808 Rectangle pathBounds = tree.getPathBounds(path); 6809 if (treeLocation != null && pathBounds != null) { 6810 Point nodeLocation = new Point(pathBounds.x, 6811 pathBounds.y); 6812 nodeLocation.translate(treeLocation.x, treeLocation.y); 6813 return nodeLocation; 6814 } else { 6815 return null; 6816 } 6817 } else { 6818 return null; 6819 } 6820 } 6821 6822 private Point getLocationInJTree() { 6823 Rectangle r = tree.getPathBounds(path); 6824 if (r != null) { 6825 return r.getLocation(); 6826 } else { 6827 return null; 6828 } 6829 } 6830 6831 public Point getLocation() { 6832 Rectangle r = getBounds(); 6833 if (r != null) { 6834 return r.getLocation(); 6835 } else { 6836 return null; 6837 } 6838 } 6839 6840 public void setLocation(Point p) { 6841 } 6842 6843 public Rectangle getBounds() { 6844 if (tree == null) 6845 return null; 6846 Rectangle r = tree.getPathBounds(path); 6847 Accessible parent = getAccessibleParent(); 6848 if (parent instanceof AccessibleJTreeNode) { 6849 Point parentLoc = ((AccessibleJTreeNode) parent).getLocationInJTree(); 6850 if (parentLoc != null && r != null) { 6851 r.translate(-parentLoc.x, -parentLoc.y); 6852 } else { 6853 return null; // not visible! 6854 } 6855 } 6856 return r; 6857 } 6858 6859 public void setBounds(Rectangle r) { 6860 AccessibleContext ac = getCurrentAccessibleContext(); 6861 if (ac instanceof AccessibleComponent) { 6862 ((AccessibleComponent) ac).setBounds(r); 6863 } else { 6864 Component c = getCurrentComponent(); 6865 if (c != null) { 6866 c.setBounds(r); 6867 } 6868 } 6869 } 6870 6871 public Dimension getSize() { 6872 return getBounds().getSize(); 6873 } 6874 6875 public void setSize (Dimension d) { 6876 AccessibleContext ac = getCurrentAccessibleContext(); 6877 if (ac instanceof AccessibleComponent) { 6878 ((AccessibleComponent) ac).setSize(d); 6879 } else { 6880 Component c = getCurrentComponent(); 6881 if (c != null) { 6882 c.setSize(d); 6883 } 6884 } 6885 } 6886 6887 /** 6888 * Returns the <code>Accessible</code> child, if one exists, 6889 * contained at the local coordinate <code>Point</code>. 6890 * Otherwise returns <code>null</code>. 6891 * 6892 * @param p point in local coordinates of this 6893 * <code>Accessible</code> 6894 * @return the <code>Accessible</code>, if it exists, 6895 * at the specified location; else <code>null</code> 6896 */ 6897 public Accessible getAccessibleAt(Point p) { 6898 AccessibleContext ac = getCurrentAccessibleContext(); 6899 if (ac instanceof AccessibleComponent) { 6900 return ((AccessibleComponent) ac).getAccessibleAt(p); 6901 } else { 6902 return null; 6903 } 6904 } 6905 6906 public boolean isFocusTraversable() { 6907 AccessibleContext ac = getCurrentAccessibleContext(); 6908 if (ac instanceof AccessibleComponent) { 6909 return ((AccessibleComponent) ac).isFocusTraversable(); 6910 } else { 6911 Component c = getCurrentComponent(); 6912 if (c != null) { 6913 return c.isFocusable(); 6914 } else { 6915 return false; 6916 } 6917 } 6918 } 6919 6920 public void requestFocus() { 6921 AccessibleContext ac = getCurrentAccessibleContext(); 6922 if (ac instanceof AccessibleComponent) { 6923 ((AccessibleComponent) ac).requestFocus(); 6924 } else { 6925 Component c = getCurrentComponent(); 6926 if (c != null) { 6927 c.requestFocus(); 6928 } 6929 } 6930 } 6931 6932 public void addFocusListener(FocusListener l) { 6933 AccessibleContext ac = getCurrentAccessibleContext(); 6934 if (ac instanceof AccessibleComponent) { 6935 ((AccessibleComponent) ac).addFocusListener(l); 6936 } else { 6937 Component c = getCurrentComponent(); 6938 if (c != null) { 6939 c.addFocusListener(l); 6940 } 6941 } 6942 } 6943 6944 public void removeFocusListener(FocusListener l) { 6945 AccessibleContext ac = getCurrentAccessibleContext(); 6946 if (ac instanceof AccessibleComponent) { 6947 ((AccessibleComponent) ac).removeFocusListener(l); 6948 } else { 6949 Component c = getCurrentComponent(); 6950 if (c != null) { 6951 c.removeFocusListener(l); 6952 } 6953 } 6954 } 6955 6956 // AccessibleSelection methods 6957 6958 /** 6959 * Returns the number of items currently selected. 6960 * If no items are selected, the return value will be 0. 6961 * 6962 * @return the number of items currently selected. 6963 */ 6964 public int getAccessibleSelectionCount() { 6965 int count = 0; 6966 int childCount = getAccessibleChildrenCount(); 6967 for (int i = 0; i < childCount; i++) { 6968 TreePath childPath = getChildTreePath(i); 6969 if (tree.isPathSelected(childPath)) { 6970 count++; 6971 } 6972 } 6973 return count; 6974 } 6975 6976 /** 6977 * Returns an Accessible representing the specified selected item 6978 * in the object. If there isn't a selection, or there are 6979 * fewer items selected than the integer passed in, the return 6980 * value will be null. 6981 * 6982 * @param i the zero-based index of selected items 6983 * @return an Accessible containing the selected item 6984 */ 6985 public Accessible getAccessibleSelection(int i) { 6986 int childCount = getAccessibleChildrenCount(); 6987 if (i < 0 || i >= childCount) { 6988 return null; // out of range 6989 } 6990 int count = 0; 6991 for (int j = 0; j < childCount && i >= count; j++) { 6992 TreePath childPath = getChildTreePath(j); 6993 if (tree.isPathSelected(childPath)) { 6994 if (count == i) { 6995 return new AccessibleJTreeNode(tree, childPath, this); 6996 } else { 6997 count++; 6998 } 6999 } 7000 } 7001 return null; 7002 } 7003 7004 /** 7005 * Returns true if the current child of this object is selected. 7006 * 7007 * @param i the zero-based index of the child in this Accessible 7008 * object. 7009 * @see AccessibleContext#getAccessibleChild 7010 */ 7011 public boolean isAccessibleChildSelected(int i) { 7012 int childCount = getAccessibleChildrenCount(); 7013 if (i < 0 || i >= childCount) { 7014 return false; // out of range 7015 } else { 7016 TreePath childPath = getChildTreePath(i); 7017 return tree.isPathSelected(childPath); 7018 } 7019 } 7020 7021 /** 7022 * Adds the specified selected item in the object to the object's 7023 * selection. If the object supports multiple selections, 7024 * the specified item is added to any existing selection, otherwise 7025 * it replaces any existing selection in the object. If the 7026 * specified item is already selected, this method has no effect. 7027 * 7028 * @param i the zero-based index of selectable items 7029 */ 7030 public void addAccessibleSelection(int i) { 7031 if (tree == null) 7032 return; 7033 TreeModel model = tree.getModel(); 7034 if (model != null) { 7035 if (i >= 0 && i < getAccessibleChildrenCount()) { 7036 TreePath path = getChildTreePath(i); 7037 tree.addSelectionPath(path); 7038 } 7039 } 7040 } 7041 7042 /** 7043 * Removes the specified selected item in the object from the 7044 * object's 7045 * selection. If the specified item isn't currently selected, this 7046 * method has no effect. 7047 * 7048 * @param i the zero-based index of selectable items 7049 */ 7050 public void removeAccessibleSelection(int i) { 7051 if (tree == null) 7052 return; 7053 TreeModel model = tree.getModel(); 7054 if (model != null) { 7055 if (i >= 0 && i < getAccessibleChildrenCount()) { 7056 TreePath path = getChildTreePath(i); 7057 tree.removeSelectionPath(path); 7058 } 7059 } 7060 } 7061 7062 /** 7063 * Clears the selection in the object, so that nothing in the 7064 * object is selected. 7065 */ 7066 public void clearAccessibleSelection() { 7067 int childCount = getAccessibleChildrenCount(); 7068 for (int i = 0; i < childCount; i++) { 7069 removeAccessibleSelection(i); 7070 } 7071 } 7072 7073 /** 7074 * Causes every selected item in the object to be selected 7075 * if the object supports multiple selections. 7076 */ 7077 public void selectAllAccessibleSelection() { 7078 if (tree == null) 7079 return; 7080 TreeModel model = tree.getModel(); 7081 if (model != null) { 7082 int childCount = getAccessibleChildrenCount(); 7083 TreePath path; 7084 for (int i = 0; i < childCount; i++) { 7085 path = getChildTreePath(i); 7086 tree.addSelectionPath(path); 7087 } 7088 } 7089 } 7090 7091 // AccessibleAction methods 7092 7093 /** 7094 * Returns the number of accessible actions available in this 7095 * tree node. If this node is not a leaf, there is at least 7096 * one action (toggle expand), in addition to any available 7097 * on the object behind the TreeCellRenderer. 7098 * 7099 * @return the number of Actions in this object 7100 */ 7101 public int getAccessibleActionCount() { 7102 AccessibleContext ac = getCurrentAccessibleContext(); 7103 if (ac != null) { 7104 AccessibleAction aa = ac.getAccessibleAction(); 7105 if (aa != null) { 7106 return (aa.getAccessibleActionCount() + (isLeaf ? 0 : 1)); 7107 } 7108 } 7109 return isLeaf ? 0 : 1; 7110 } 7111 7112 /** 7113 * Return a description of the specified action of the tree node. 7114 * If this node is not a leaf, there is at least one action 7115 * description (toggle expand), in addition to any available 7116 * on the object behind the TreeCellRenderer. 7117 * 7118 * @param i zero-based index of the actions 7119 * @return a description of the action 7120 */ 7121 public String getAccessibleActionDescription(int i) { 7122 if (i < 0 || i >= getAccessibleActionCount()) { 7123 return null; 7124 } 7125 AccessibleContext ac = getCurrentAccessibleContext(); 7126 if (i == 0) { 7127 // TIGER - 4766636 7128 // return AccessibleAction.TOGGLE_EXPAND; 7129 return "toggle expand"; 7130 } else if (ac != null) { 7131 AccessibleAction aa = ac.getAccessibleAction(); 7132 if (aa != null) { 7133 return aa.getAccessibleActionDescription(i - 1); 7134 } 7135 } 7136 return null; 7137 } 7138 7139 /** 7140 * Perform the specified Action on the tree node. If this node 7141 * is not a leaf, there is at least one action which can be 7142 * done (toggle expand), in addition to any available on the 7143 * object behind the TreeCellRenderer. 7144 * 7145 * @param i zero-based index of actions 7146 * @return true if the the action was performed; else false. 7147 */ 7148 public boolean doAccessibleAction(int i) { 7149 if (i < 0 || i >= getAccessibleActionCount()) { 7150 return false; 7151 } 7152 AccessibleContext ac = getCurrentAccessibleContext(); 7153 if (i == 0) { 7154 if (tree.isExpanded(path)) { 7155 tree.collapsePath(path); 7156 } else { 7157 tree.expandPath(path); 7158 } 7159 return true; 7160 } else if (ac != null) { 7161 AccessibleAction aa = ac.getAccessibleAction(); 7162 if (aa != null) { 7163 return aa.doAccessibleAction(i - 1); 7164 } 7165 } 7166 return false; 7167 } 7168 7169 } // inner class AccessibleJTreeNode 7170 7171 /** 7172 * A helper class to perform {@code Callable} objects on the event dispatch thread appropriate 7173 * for the provided {@code AccessibleContext}. 7174 */ 7175 private static class InvocationUtils { 7176 7177 /** 7178 * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible} 7179 * and waits for it to finish blocking the caller thread. 7180 * 7181 * @param callable the {@code Callable} to invoke 7182 * @param accessibleTable the {@code AccessibleExtendedTable} which would be used to find the right context 7183 * for the task execution 7184 * @param <T> type parameter for the result value 7185 * 7186 * @return the result of the {@code Callable} execution 7187 */ 7188 public static <T> T invokeAndWait(final Callable<T> callable, 7189 final AccessibleExtendedTable accessibleTable) { 7190 if (accessibleTable instanceof AccessibleContext) { 7191 return invokeAndWait(callable, (AccessibleContext)accessibleTable); 7192 } 7193 throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleTable); 7194 } 7195 7196 /** 7197 * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible} 7198 * and waits for it to finish blocking the caller thread. 7199 * 7200 * @param callable the {@code Callable} to invoke 7201 * @param accessible the {@code Accessible} which would be used to find the right context 7202 * for the task execution 7203 * @param <T> type parameter for the result value 7204 * 7205 * @return the result of the {@code Callable} execution 7206 */ 7207 public static <T> T invokeAndWait(final Callable<T> callable, 7208 final Accessible accessible) { 7209 if (accessible instanceof Component) { 7210 return invokeAndWait(callable, (Component)accessible); 7211 } 7212 if (accessible instanceof AccessibleContext) { 7213 // This case also covers the Translator 7214 return invokeAndWait(callable, (AccessibleContext)accessible); 7215 } 7216 throw new RuntimeException("Unmapped Accessible used to dispatch event: " + accessible); 7217 } 7218 7219 /** 7220 * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Component} 7221 * and waits for it to finish blocking the caller thread. 7222 * 7223 * @param callable the {@code Callable} to invoke 7224 * @param component the {@code Component} which would be used to find the right context 7225 * for the task execution 7226 * @param <T> type parameter for the result value 7227 * 7228 * @return the result of the {@code Callable} execution 7229 */ 7230 public static <T> T invokeAndWait(final Callable<T> callable, 7231 final Component component) { 7232 return invokeAndWait(callable, SunToolkit.targetToAppContext(component)); 7233 } 7234 7235 /** 7236 * Invokes a {@code Callable} in the {@code AppContext} mapped to the given {@code AccessibleContext} 7237 * and waits for it to finish blocking the caller thread. 7238 * 7239 * @param callable the {@code Callable} to invoke 7240 * @param accessibleContext the {@code AccessibleContext} which would be used to determine the right 7241 * context for the task execution. 7242 * @param <T> type parameter for the result value 7243 * 7244 * @return the result of the {@code Callable} execution 7245 */ 7246 public static <T> T invokeAndWait(final Callable<T> callable, 7247 final AccessibleContext accessibleContext) { 7248 AppContext targetContext = AWTAccessor.getAccessibleContextAccessor() 7249 .getAppContext(accessibleContext); 7250 if (targetContext != null) { 7251 return invokeAndWait(callable, targetContext); 7252 } else { 7253 // Normally this should not happen, unmapped context provided and 7254 // the target AppContext is unknown. 7255 7256 // Try to recover in case the context is a translator. 7257 if (accessibleContext instanceof Translator) { 7258 Object source = ((Translator)accessibleContext).getSource(); 7259 if (source instanceof Component) { 7260 return invokeAndWait(callable, (Component)source); 7261 } 7262 } 7263 } 7264 throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleContext); 7265 } 7266 7267 private static <T> T invokeAndWait(final Callable<T> callable, 7268 final AppContext targetAppContext) { 7269 final CallableWrapper<T> wrapper = new CallableWrapper<T>(callable); 7270 try { 7271 invokeAndWait(wrapper, targetAppContext); 7272 T result = wrapper.getResult(); 7273 updateAppContextMap(result, targetAppContext); 7274 return result; 7275 } catch (final Exception e) { 7276 throw new RuntimeException(e); 7277 } 7278 } 7279 7280 private static void invokeAndWait(final Runnable runnable, 7281 final AppContext appContext) 7282 throws InterruptedException, InvocationTargetException { 7283 7284 EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext); 7285 Object lock = new Object(); 7286 Toolkit source = Toolkit.getDefaultToolkit(); 7287 InvocationEvent event = 7288 new InvocationEvent(source, runnable, lock, true); 7289 synchronized (lock) { 7290 eq.postEvent(event); 7291 lock.wait(); 7292 } 7293 7294 Throwable eventThrowable = event.getThrowable(); 7295 if (eventThrowable != null) { 7296 throw new InvocationTargetException(eventThrowable); 7297 } 7298 } 7299 7300 /** 7301 * Maps the {@code AccessibleContext} to the {@code AppContext} which should be used 7302 * to dispatch events related to the {@code AccessibleContext} 7303 * @param accessibleContext the {@code AccessibleContext} for the mapping 7304 * @param targetContext the {@code AppContext} for the mapping 7305 */ 7306 public static void registerAccessibleContext(final AccessibleContext accessibleContext, 7307 final AppContext targetContext) { 7308 if (accessibleContext != null) { 7309 AWTAccessor.getAccessibleContextAccessor().setAppContext(accessibleContext, targetContext); 7310 } 7311 } 7312 7313 private static <T> void updateAppContextMap(final T accessibleContext, 7314 final AppContext targetContext) { 7315 if (accessibleContext instanceof AccessibleContext) { 7316 registerAccessibleContext((AccessibleContext)accessibleContext, targetContext); 7317 } 7318 } 7319 7320 private static class CallableWrapper<T> implements Runnable { 7321 private final Callable<T> callable; 7322 private volatile T object; 7323 private Exception e; 7324 7325 CallableWrapper(final Callable<T> callable) { 7326 this.callable = callable; 7327 } 7328 7329 public void run() { 7330 try { 7331 if (callable != null) { 7332 object = callable.call(); 7333 } 7334 } catch (final Exception e) { 7335 this.e = e; 7336 } 7337 } 7338 7339 T getResult() throws Exception { 7340 if (e != null) 7341 throw e; 7342 return object; 7343 } 7344 } 7345 } 7346 }