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