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