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