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 3783 */ 3784 private KeyStroke getAccelerator(final AccessibleContext ac) { 3785 // workaround for getAccessibleKeyBinding not returning the 3786 // JMenuItem accelerator 3787 if (ac == null) 3788 return null; 3789 return InvocationUtils.invokeAndWait(new Callable<KeyStroke>() { 3790 @Override 3791 public KeyStroke call() throws Exception { 3792 Accessible parent = ac.getAccessibleParent(); 3793 if (parent instanceof Accessible) { 3794 int indexInParent = ac.getAccessibleIndexInParent(); 3795 Accessible child = 3796 parent.getAccessibleContext().getAccessibleChild(indexInParent); 3797 if (child instanceof JMenuItem) { 3798 JMenuItem menuItem = (JMenuItem) child; 3799 if (menuItem == null) 3800 return null; 3801 KeyStroke keyStroke = menuItem.getAccelerator(); 3802 return keyStroke; 3803 } 3804 } 3805 return null; 3806 } 3807 }, ac); 3808 } 3809 3810 /* 3811 * returns 1-24 to indicate which F key is being used for a shortcut or 0 otherwise 3812 */ 3813 private int fKeyNumber(KeyStroke keyStroke) { 3814 if (keyStroke == null) 3815 return 0; 3816 int fKey = 0; 3817 String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode()); 3818 if (keyText != null && (keyText.length() == 2 || keyText.length() == 3)) { 3819 String prefix = keyText.substring(0, 1); 3820 if (prefix.equals("F")) { 3821 try { 3822 int suffix = Integer.parseInt(keyText.substring(1)); 3823 if (suffix >= 1 && suffix <= 24) { 3824 fKey = suffix; 3825 } 3826 } catch (Exception e) { // ignore NumberFormatException 3827 } 3828 } 3829 } 3830 return fKey; 3831 } 3832 3833 /* 3834 * returns one of several important control characters or 0 otherwise 3835 */ 3836 private int controlCode(KeyStroke keyStroke) { 3837 if (keyStroke == null) 3838 return 0; 3839 int code = keyStroke.getKeyCode(); 3840 switch (code) { 3841 case KeyEvent.VK_BACK_SPACE: 3842 case KeyEvent.VK_DELETE: 3843 case KeyEvent.VK_DOWN: 3844 case KeyEvent.VK_END: 3845 case KeyEvent.VK_HOME: 3846 case KeyEvent.VK_INSERT: 3847 case KeyEvent.VK_KP_DOWN: 3848 case KeyEvent.VK_KP_LEFT: 3849 case KeyEvent.VK_KP_RIGHT: 3850 case KeyEvent.VK_KP_UP: 3851 case KeyEvent.VK_LEFT: 3852 case KeyEvent.VK_PAGE_DOWN: 3853 case KeyEvent.VK_PAGE_UP: 3854 case KeyEvent.VK_RIGHT: 3855 case KeyEvent.VK_UP: 3856 break; 3857 default: 3858 code = 0; 3859 break; 3860 } 3861 return code; 3862 } 3863 3864 /* 3865 * returns the KeyStoke character 3866 */ 3867 private char getKeyChar(KeyStroke keyStroke) { 3868 // If the shortcut is an FKey return 1-24 3869 if (keyStroke == null) 3870 return 0; 3871 int fKey = fKeyNumber(keyStroke); 3872 if (fKey != 0) { 3873 // return 0x00000001 through 0x00000018 3874 debugString("[INFO]: Shortcut is: F" + fKey); 3875 return (char)fKey; 3876 } 3877 // If the accelerator is a control character, return it 3878 int keyCode = controlCode(keyStroke); 3879 if (keyCode != 0) { 3880 debugString("[INFO]: Shortcut is control character: " + Integer.toHexString(keyCode)); 3881 return (char)keyCode; 3882 } 3883 String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode()); 3884 debugString("[INFO]: Shortcut is: " + keyText); 3885 if (keyText != null || keyText.length() > 0) { 3886 CharSequence seq = keyText.subSequence(0, 1); 3887 if (seq != null || seq.length() > 0) { 3888 return seq.charAt(0); 3889 } 3890 } 3891 return 0; 3892 } 3893 3894 /* 3895 * returns the KeyStroke modifiers as an int 3896 */ 3897 private int getModifiers(KeyStroke keyStroke) { 3898 if (keyStroke == null) 3899 return 0; 3900 debugString("[INFO]: In AccessBridge.getModifiers"); 3901 // modifiers is a bit strip where bits 0-7 indicate a traditional modifier 3902 // such as Ctrl/Alt/Shift, bit 8 indicates an F key shortcut, and bit 9 indicates 3903 // a control code shortcut such as the delete key. 3904 3905 int modifiers = 0; 3906 // Is the shortcut an FKey? 3907 if (fKeyNumber(keyStroke) != 0) { 3908 modifiers |= 1 << 8; 3909 } 3910 // Is the shortcut a control code? 3911 if (controlCode(keyStroke) != 0) { 3912 modifiers |= 1 << 9; 3913 } 3914 // The following is needed in order to handle translated modifiers. 3915 // getKeyModifiersText doesn't work because for example in German Strg is 3916 // returned for Ctrl. 3917 3918 // There can be more than one modifier, e.g. if the modifier is ctrl + shift + B 3919 // the toString text is "shift ctrl pressed B". Need to parse through that. 3920 StringTokenizer st = new StringTokenizer(keyStroke.toString()); 3921 while (st.hasMoreTokens()) { 3922 String text = st.nextToken(); 3923 // Meta+Ctrl+Alt+Shift 3924 // 0-3 are shift, ctrl, meta, alt 3925 // 4-7 are for Solaris workstations (though not being used) 3926 if (text.startsWith("met")) { 3927 debugString("[INFO]: found meta"); 3928 modifiers |= ActionEvent.META_MASK; 3929 } 3930 if (text.startsWith("ctr")) { 3931 debugString("[INFO]: found ctrl"); 3932 modifiers |= ActionEvent.CTRL_MASK; 3933 } 3934 if (text.startsWith("alt")) { 3935 debugString("[INFO]: found alt"); 3936 modifiers |= ActionEvent.ALT_MASK; 3937 } 3938 if (text.startsWith("shi")) { 3939 debugString(" found shift"); 3940 modifiers |= ActionEvent.SHIFT_MASK; 3941 } 3942 } 3943 debugString("[INFO]: returning modifiers: 0x" + Integer.toHexString(modifiers)); 3944 return modifiers; 3945 } 3946 3947 /* 3948 * returns the number of key bindings associated with this context 3949 */ 3950 private int getAccessibleKeyBindingsCount(AccessibleContext ac) { 3951 if (ac == null) 3952 return 0; 3953 int count = 0; 3954 3955 if (getMnemonic(ac) != null) { 3956 count++; 3957 } 3958 if (getAccelerator(ac) != null) { 3959 count++; 3960 } 3961 return count; 3962 } 3963 3964 /* 3965 * returns the key binding character at the specified index 3966 */ 3967 private char getAccessibleKeyBindingChar(AccessibleContext ac, int index) { 3968 if (ac == null) 3969 return 0; 3970 if((index == 0) && getMnemonic(ac)==null) {// special case when there is no mnemonic 3971 KeyStroke keyStroke = getAccelerator(ac); 3972 if (keyStroke != null) { 3973 return getKeyChar(keyStroke); 3974 } 3975 } 3976 if (index == 0) { // mnemonic 3977 KeyStroke keyStroke = getMnemonic(ac); 3978 if (keyStroke != null) { 3979 return getKeyChar(keyStroke); 3980 } 3981 } else if (index == 1) { // accelerator 3982 KeyStroke keyStroke = getAccelerator(ac); 3983 if (keyStroke != null) { 3984 return getKeyChar(keyStroke); 3985 } 3986 } 3987 return 0; 3988 } 3989 3990 /* 3991 * returns the key binding modifiers at the specified index 3992 */ 3993 private int getAccessibleKeyBindingModifiers(AccessibleContext ac, int index) { 3994 if (ac == null) 3995 return 0; 3996 if((index == 0) && getMnemonic(ac)==null) {// special case when there is no mnemonic 3997 KeyStroke keyStroke = getAccelerator(ac); 3998 if (keyStroke != null) { 3999 return getModifiers(keyStroke); 4000 } 4001 } 4002 if (index == 0) { // mnemonic 4003 KeyStroke keyStroke = getMnemonic(ac); 4004 if (keyStroke != null) { 4005 return getModifiers(keyStroke); 4006 } 4007 } else if (index == 1) { // accelerator 4008 KeyStroke keyStroke = getAccelerator(ac); 4009 if (keyStroke != null) { 4010 return getModifiers(keyStroke); 4011 } 4012 } 4013 return 0; 4014 } 4015 4016 // ========== AccessibleIcon ============ 4017 4018 /* 4019 * return the number of icons associated with this context 4020 */ 4021 private int getAccessibleIconsCount(final AccessibleContext ac) { 4022 debugString("[INFO]: getAccessibleIconsCount"); 4023 if (ac == null) { 4024 return 0; 4025 } 4026 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 4027 @Override 4028 public Integer call() throws Exception { 4029 AccessibleIcon[] ai = ac.getAccessibleIcon(); 4030 if (ai == null) { 4031 return 0; 4032 } 4033 return ai.length; 4034 } 4035 }, ac); 4036 } 4037 4038 /* 4039 * return icon description at the specified index 4040 */ 4041 private String getAccessibleIconDescription(final AccessibleContext ac, final int index) { 4042 debugString("[INFO]: getAccessibleIconDescription: index = "+index); 4043 if (ac == null) { 4044 return null; 4045 } 4046 return InvocationUtils.invokeAndWait(new Callable<String>() { 4047 @Override 4048 public String call() throws Exception { 4049 AccessibleIcon[] ai = ac.getAccessibleIcon(); 4050 if (ai == null || index < 0 || index >= ai.length) { 4051 return null; 4052 } 4053 return ai[index].getAccessibleIconDescription(); 4054 } 4055 }, ac); 4056 } 4057 4058 /* 4059 * return icon height at the specified index 4060 */ 4061 private int getAccessibleIconHeight(final AccessibleContext ac, final int index) { 4062 debugString("[INFO]: getAccessibleIconHeight: index = "+index); 4063 if (ac == null) { 4064 return 0; 4065 } 4066 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 4067 @Override 4068 public Integer call() throws Exception { 4069 AccessibleIcon[] ai = ac.getAccessibleIcon(); 4070 if (ai == null || index < 0 || index >= ai.length) { 4071 return 0; 4072 } 4073 return ai[index].getAccessibleIconHeight(); 4074 } 4075 }, ac); 4076 } 4077 4078 /* 4079 * return icon width at the specified index 4080 */ 4081 private int getAccessibleIconWidth(final AccessibleContext ac, final int index) { 4082 debugString("[INFO]: getAccessibleIconWidth: index = "+index); 4083 if (ac == null) { 4084 return 0; 4085 } 4086 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 4087 @Override 4088 public Integer call() throws Exception { 4089 AccessibleIcon[] ai = ac.getAccessibleIcon(); 4090 if (ai == null || index < 0 || index >= ai.length) { 4091 return 0; 4092 } 4093 return ai[index].getAccessibleIconWidth(); 4094 } 4095 }, ac); 4096 } 4097 4098 // ========= AccessibleAction =========== 4099 4100 /* 4101 * return the number of icons associated with this context 4102 */ 4103 private int getAccessibleActionsCount(final AccessibleContext ac) { 4104 debugString("[INFO]: getAccessibleActionsCount"); 4105 if (ac == null) { 4106 return 0; 4107 } 4108 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 4109 @Override 4110 public Integer call() throws Exception { 4111 AccessibleAction aa = ac.getAccessibleAction(); 4112 if (aa == null) 4113 return 0; 4114 return aa.getAccessibleActionCount(); 4115 } 4116 }, ac); 4117 } 4118 4119 /* 4120 * return icon description at the specified index 4121 */ 4122 private String getAccessibleActionName(final AccessibleContext ac, final int index) { 4123 debugString("[INFO]: getAccessibleActionName: index = "+index); 4124 if (ac == null) { 4125 return null; 4126 } 4127 return InvocationUtils.invokeAndWait(new Callable<String>() { 4128 @Override 4129 public String call() throws Exception { 4130 AccessibleAction aa = ac.getAccessibleAction(); 4131 if (aa == null) { 4132 return null; 4133 } 4134 return aa.getAccessibleActionDescription(index); 4135 } 4136 }, ac); 4137 } 4138 /* 4139 * return icon description at the specified index 4140 */ 4141 private boolean doAccessibleActions(final AccessibleContext ac, final String name) { 4142 debugString("[INFO]: doAccessibleActions: action name = "+name); 4143 if (ac == null || name == null) { 4144 return false; 4145 } 4146 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4147 @Override 4148 public Boolean call() throws Exception { 4149 AccessibleAction aa = ac.getAccessibleAction(); 4150 if (aa == null) { 4151 return false; 4152 } 4153 int index = -1; 4154 int numActions = aa.getAccessibleActionCount(); 4155 for (int i = 0; i < numActions; i++) { 4156 String actionName = aa.getAccessibleActionDescription(i); 4157 if (name.equals(actionName)) { 4158 index = i; 4159 break; 4160 } 4161 } 4162 if (index == -1) { 4163 return false; 4164 } 4165 boolean retval = aa.doAccessibleAction(index); 4166 return retval; 4167 } 4168 }, ac); 4169 } 4170 4171 /* ===== AT utility methods ===== */ 4172 4173 /** 4174 * Sets the contents of an AccessibleContext that 4175 * implements AccessibleEditableText with the 4176 * specified text string. 4177 * Returns whether successful. 4178 */ 4179 private boolean setTextContents(final AccessibleContext ac, final String text) { 4180 debugString("[INFO]: setTextContents: ac = "+ac+"; text = "+text); 4181 4182 if (! (ac instanceof AccessibleEditableText)) { 4183 debugString("[WARN]: ac not instanceof AccessibleEditableText: "+ac); 4184 return false; 4185 } 4186 if (text == null) { 4187 debugString("[WARN]: text is null"); 4188 return false; 4189 } 4190 4191 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4192 @Override 4193 public Boolean call() throws Exception { 4194 // check whether the text field is editable 4195 AccessibleStateSet ass = ac.getAccessibleStateSet(); 4196 if (!ass.contains(AccessibleState.ENABLED)) { 4197 return false; 4198 } 4199 ((AccessibleEditableText) ac).setTextContents(text); 4200 return true; 4201 } 4202 }, ac); 4203 } 4204 4205 /** 4206 * Returns the Accessible Context of an Internal Frame object that is 4207 * the ancestor of a given object. If the object is an Internal Frame 4208 * object or an Internal Frame ancestor object was found, returns the 4209 * object's AccessibleContext. 4210 * If there is no ancestor object that has an Accessible Role of 4211 * Internal Frame, returns (AccessibleContext)0. 4212 */ 4213 private AccessibleContext getInternalFrame (AccessibleContext ac) { 4214 return getParentWithRole(ac, AccessibleRole.INTERNAL_FRAME.toString()); 4215 } 4216 4217 /** 4218 * Returns the Accessible Context for the top level object in 4219 * a Java Window. This is same Accessible Context that is obtained 4220 * from GetAccessibleContextFromHWND for that window. Returns 4221 * (AccessibleContext)0 on error. 4222 */ 4223 private AccessibleContext getTopLevelObject (final AccessibleContext ac) { 4224 debugString("[INFO]: getTopLevelObject; ac = "+ac); 4225 if (ac == null) { 4226 return null; 4227 } 4228 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4229 @Override 4230 public AccessibleContext call() throws Exception { 4231 if (ac.getAccessibleRole() == AccessibleRole.DIALOG) { 4232 // return the dialog, not the parent window 4233 return ac; 4234 } 4235 4236 Accessible parent = ac.getAccessibleParent(); 4237 if (parent == null) { 4238 return ac; 4239 } 4240 Accessible tmp = parent; 4241 while (tmp != null && tmp.getAccessibleContext() != null) { 4242 AccessibleContext ac2 = tmp.getAccessibleContext(); 4243 if (ac2 != null && ac2.getAccessibleRole() == AccessibleRole.DIALOG) { 4244 // return the dialog, not the parent window 4245 return ac2; 4246 } 4247 parent = tmp; 4248 tmp = parent.getAccessibleContext().getAccessibleParent(); 4249 } 4250 return parent.getAccessibleContext(); 4251 } 4252 }, ac); 4253 } 4254 4255 /** 4256 * Returns the parent AccessibleContext that has the specified AccessibleRole. 4257 * Returns null on error or if the AccessibleContext does not exist. 4258 */ 4259 private AccessibleContext getParentWithRole (final AccessibleContext ac, 4260 final String roleName) { 4261 debugString("[INFO]: getParentWithRole; ac = "+ac + "\n role = "+roleName); 4262 if (ac == null || roleName == null) { 4263 return null; 4264 } 4265 4266 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4267 @Override 4268 public AccessibleContext call() throws Exception { 4269 AccessibleRole role = AccessBridge.this.accessibleRoleMap.get(roleName); 4270 if (role == null) { 4271 return ac; 4272 } 4273 4274 Accessible parent = ac.getAccessibleParent(); 4275 if (parent == null && ac.getAccessibleRole() == role) { 4276 return ac; 4277 } 4278 4279 Accessible tmp = parent; 4280 AccessibleContext tmp_ac = null; 4281 4282 while (tmp != null && (tmp_ac = tmp.getAccessibleContext()) != null) { 4283 AccessibleRole ar = tmp_ac.getAccessibleRole(); 4284 if (ar == role) { 4285 // found 4286 return tmp_ac; 4287 } 4288 parent = tmp; 4289 tmp = parent.getAccessibleContext().getAccessibleParent(); 4290 } 4291 // not found 4292 return null; 4293 } 4294 }, ac); 4295 } 4296 4297 /** 4298 * Returns the parent AccessibleContext that has the specified AccessibleRole. 4299 * Otherwise, returns the top level object for the Java Window. 4300 * Returns (AccessibleContext)0 on error. 4301 */ 4302 private AccessibleContext getParentWithRoleElseRoot (AccessibleContext ac, 4303 String roleName) { 4304 AccessibleContext retval = getParentWithRole(ac, roleName); 4305 if (retval == null) { 4306 retval = getTopLevelObject(ac); 4307 } 4308 return retval; 4309 } 4310 4311 /** 4312 * Returns how deep in the object hierarchy a given object is. 4313 * The top most object in the object hierarchy has an object depth of 0. 4314 * Returns -1 on error. 4315 */ 4316 private int getObjectDepth(final AccessibleContext ac) { 4317 debugString("[INFO]: getObjectDepth: ac = "+ac); 4318 4319 if (ac == null) { 4320 return -1; 4321 } 4322 return InvocationUtils.invokeAndWait(new Callable<Integer>() { 4323 @Override 4324 public Integer call() throws Exception { 4325 int count = 0; 4326 Accessible parent = ac.getAccessibleParent(); 4327 if (parent == null) { 4328 return count; 4329 } 4330 Accessible tmp = parent; 4331 while (tmp != null && tmp.getAccessibleContext() != null) { 4332 parent = tmp; 4333 tmp = parent.getAccessibleContext().getAccessibleParent(); 4334 count++; 4335 } 4336 return count; 4337 } 4338 }, ac); 4339 } 4340 4341 /** 4342 * Returns the Accessible Context of the current ActiveDescendent of an object. 4343 * Returns (AccessibleContext)0 on error. 4344 */ 4345 private AccessibleContext getActiveDescendent (final AccessibleContext ac) { 4346 debugString("[INFO]: getActiveDescendent: ac = "+ac); 4347 if (ac == null) { 4348 return null; 4349 } 4350 // workaround for JTree bug where the only possible active 4351 // descendent is the JTree root 4352 final Accessible parent = InvocationUtils.invokeAndWait(new Callable<Accessible>() { 4353 @Override 4354 public Accessible call() throws Exception { 4355 return ac.getAccessibleParent(); 4356 } 4357 }, ac); 4358 4359 if (parent != null) { 4360 Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() { 4361 @Override 4362 public Accessible call() throws Exception { 4363 int indexInParent = ac.getAccessibleIndexInParent(); 4364 return parent.getAccessibleContext().getAccessibleChild(indexInParent); 4365 } 4366 }, ac); 4367 4368 if (child instanceof JTree) { 4369 // return the selected node 4370 final JTree tree = (JTree)child; 4371 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4372 @Override 4373 public AccessibleContext call() throws Exception { 4374 return new AccessibleJTreeNode(tree, 4375 tree.getSelectionPath(), 4376 null); 4377 } 4378 }, child); 4379 } 4380 } 4381 4382 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4383 @Override 4384 public AccessibleContext call() throws Exception { 4385 AccessibleSelection as = ac.getAccessibleSelection(); 4386 if (as == null) { 4387 return null; 4388 } 4389 // assume single selection 4390 if (as.getAccessibleSelectionCount() != 1) { 4391 return null; 4392 } 4393 Accessible a = as.getAccessibleSelection(0); 4394 if (a == null) { 4395 return null; 4396 } 4397 return a.getAccessibleContext(); 4398 } 4399 }, ac); 4400 } 4401 4402 4403 /** 4404 * Additional methods for Teton 4405 */ 4406 4407 /** 4408 * Gets the AccessibleName for a component based upon the JAWS algorithm. 4409 * Returns whether successful. 4410 * 4411 * Bug ID 4916682 - Implement JAWS AccessibleName policy 4412 */ 4413 private String getJAWSAccessibleName(final AccessibleContext ac) { 4414 debugString("[INFO]: getJAWSAccessibleName"); 4415 if (ac == null) { 4416 return null; 4417 } 4418 // placeholder 4419 return InvocationUtils.invokeAndWait(new Callable<String>() { 4420 @Override 4421 public String call() throws Exception { 4422 return ac.getAccessibleName(); 4423 } 4424 }, ac); 4425 } 4426 4427 /** 4428 * Request focus for a component. Returns whether successful; 4429 * 4430 * Bug ID 4944757 - requestFocus method needed 4431 */ 4432 private boolean requestFocus(final AccessibleContext ac) { 4433 debugString("[INFO]: requestFocus"); 4434 if (ac == null) { 4435 return false; 4436 } 4437 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4438 @Override 4439 public Boolean call() throws Exception { 4440 AccessibleComponent acomp = ac.getAccessibleComponent(); 4441 if (acomp == null) { 4442 return false; 4443 } 4444 acomp.requestFocus(); 4445 return ac.getAccessibleStateSet().contains(AccessibleState.FOCUSED); 4446 } 4447 }, ac); 4448 } 4449 4450 /** 4451 * Selects text between two indices. Selection includes the 4452 * text at the start index and the text at the end index. Returns 4453 * whether successful; 4454 * 4455 * Bug ID 4944758 - selectTextRange method needed 4456 */ 4457 private boolean selectTextRange(final AccessibleContext ac, final int startIndex, final int endIndex) { 4458 debugString("[INFO]: selectTextRange: start = "+startIndex+"; end = "+endIndex); 4459 if (ac == null) { 4460 return false; 4461 } 4462 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4463 @Override 4464 public Boolean call() throws Exception { 4465 AccessibleText at = ac.getAccessibleText(); 4466 if (!(at instanceof AccessibleEditableText)) { 4467 return false; 4468 } 4469 ((AccessibleEditableText) at).selectText(startIndex, endIndex); 4470 4471 boolean result = at.getSelectionStart() == startIndex && 4472 at.getSelectionEnd() == endIndex; 4473 return result; 4474 } 4475 }, ac); 4476 } 4477 4478 /** 4479 * Set the caret to a text position. Returns whether successful; 4480 * 4481 * Bug ID 4944770 - setCaretPosition method needed 4482 */ 4483 private boolean setCaretPosition(final AccessibleContext ac, final int position) { 4484 debugString("[INFO]: setCaretPosition: position = "+position); 4485 if (ac == null) { 4486 return false; 4487 } 4488 return InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4489 @Override 4490 public Boolean call() throws Exception { 4491 AccessibleText at = ac.getAccessibleText(); 4492 if (!(at instanceof AccessibleEditableText)) { 4493 return false; 4494 } 4495 ((AccessibleEditableText) at).selectText(position, position); 4496 return at.getCaretPosition() == position; 4497 } 4498 }, ac); 4499 } 4500 4501 /** 4502 * Gets the number of visible children of an AccessibleContext. 4503 * 4504 * Bug ID 4944762- getVisibleChildren for list-like components needed 4505 */ 4506 private int _visibleChildrenCount; 4507 private AccessibleContext _visibleChild; 4508 private int _currentVisibleIndex; 4509 private boolean _foundVisibleChild; 4510 4511 private int getVisibleChildrenCount(AccessibleContext ac) { 4512 debugString("[INFO]: getVisibleChildrenCount"); 4513 if (ac == null) { 4514 return -1; 4515 } 4516 _visibleChildrenCount = 0; 4517 _getVisibleChildrenCount(ac); 4518 debugString("[INFO]: _visibleChildrenCount = "+_visibleChildrenCount); 4519 return _visibleChildrenCount; 4520 } 4521 4522 /* 4523 * Recursively descends AccessibleContext and gets the number 4524 * of visible children 4525 */ 4526 private void _getVisibleChildrenCount(final AccessibleContext ac) { 4527 if (ac == null) 4528 return; 4529 if(ac instanceof AccessibleExtendedTable) { 4530 _getVisibleChildrenCount((AccessibleExtendedTable)ac); 4531 return; 4532 } 4533 int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4534 @Override 4535 public Integer call() throws Exception { 4536 return ac.getAccessibleChildrenCount(); 4537 } 4538 }, ac); 4539 for (int i = 0; i < numChildren; i++) { 4540 final int idx = i; 4541 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4542 @Override 4543 public AccessibleContext call() throws Exception { 4544 Accessible a = ac.getAccessibleChild(idx); 4545 if (a != null) 4546 return a.getAccessibleContext(); 4547 else 4548 return null; 4549 } 4550 }, ac); 4551 if ( ac2 == null || 4552 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4553 @Override 4554 public Boolean call() throws Exception { 4555 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4556 } 4557 }, ac)) 4558 ) { 4559 continue; 4560 } 4561 _visibleChildrenCount++; 4562 4563 if (InvocationUtils.invokeAndWait(new Callable<Integer>() { 4564 @Override 4565 public Integer call() throws Exception { 4566 return ac2.getAccessibleChildrenCount(); 4567 } 4568 }, ac) > 0 ) { 4569 _getVisibleChildrenCount(ac2); 4570 } 4571 } 4572 } 4573 4574 /* 4575 * Recursively descends AccessibleContext and gets the number 4576 * of visible children. Stops search if get to invisible part of table. 4577 */ 4578 private void _getVisibleChildrenCount(final AccessibleExtendedTable acTable) { 4579 if (acTable == null) 4580 return; 4581 int lastVisibleRow = -1; 4582 int lastVisibleColumn = -1; 4583 boolean foundVisible = false; 4584 int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4585 @Override 4586 public Integer call() throws Exception { 4587 return acTable.getAccessibleRowCount(); 4588 } 4589 }, acTable); 4590 int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4591 @Override 4592 public Integer call() throws Exception { 4593 return acTable.getAccessibleColumnCount(); 4594 } 4595 }, acTable); 4596 for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) { 4597 for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) { 4598 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) { 4599 continue; 4600 } 4601 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) { 4602 continue; 4603 } 4604 int finalRowIdx = rowIdx; 4605 int finalColumnIdx = columnIdx; 4606 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4607 @Override 4608 public AccessibleContext call() throws Exception { 4609 Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx); 4610 if (a == null) 4611 return null; 4612 else 4613 return a.getAccessibleContext(); 4614 } 4615 }, acTable); 4616 if (ac2 == null || 4617 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4618 @Override 4619 public Boolean call() throws Exception { 4620 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4621 } 4622 }, acTable)) 4623 ) { 4624 if (foundVisible) { 4625 if (columnIdx != 0 && lastVisibleColumn == -1) { 4626 //the same row, so we found the last visible column 4627 lastVisibleColumn = columnIdx - 1; 4628 } else if (columnIdx == 0 && lastVisibleRow == -1) { 4629 lastVisibleRow = rowIdx - 1; 4630 } 4631 } 4632 continue; 4633 } 4634 4635 foundVisible = true; 4636 4637 _visibleChildrenCount++; 4638 4639 if (InvocationUtils.invokeAndWait(new Callable<Integer>() { 4640 @Override 4641 public Integer call() throws Exception { 4642 return ac2.getAccessibleChildrenCount(); 4643 } 4644 }, acTable) > 0) { 4645 _getVisibleChildrenCount(ac2); 4646 } 4647 } 4648 } 4649 } 4650 4651 /** 4652 * Gets the visible child of an AccessibleContext at the 4653 * specified index 4654 * 4655 * Bug ID 4944762- getVisibleChildren for list-like components needed 4656 */ 4657 private AccessibleContext getVisibleChild(AccessibleContext ac, int index) { 4658 debugString("[INFO]: getVisibleChild: index = "+index); 4659 if (ac == null) { 4660 return null; 4661 } 4662 _visibleChild = null; 4663 _currentVisibleIndex = 0; 4664 _foundVisibleChild = false; 4665 _getVisibleChild(ac, index); 4666 4667 if (_visibleChild != null) { 4668 debugString( "[INFO]: getVisibleChild: found child = " + 4669 InvocationUtils.invokeAndWait(new Callable<String>() { 4670 @Override 4671 public String call() throws Exception { 4672 return AccessBridge.this._visibleChild.getAccessibleName(); 4673 } 4674 }, ac) ); 4675 } 4676 return _visibleChild; 4677 } 4678 4679 /* 4680 * Recursively searchs AccessibleContext and finds the visible component 4681 * at the specified index 4682 */ 4683 private void _getVisibleChild(final AccessibleContext ac, final int index) { 4684 if (_visibleChild != null) { 4685 return; 4686 } 4687 if(ac instanceof AccessibleExtendedTable) { 4688 _getVisibleChild((AccessibleExtendedTable)ac, index); 4689 return; 4690 } 4691 int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4692 @Override 4693 public Integer call() throws Exception { 4694 return ac.getAccessibleChildrenCount(); 4695 } 4696 }, ac); 4697 for (int i = 0; i < numChildren; i++) { 4698 final int idx=i; 4699 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4700 @Override 4701 public AccessibleContext call() throws Exception { 4702 Accessible a = ac.getAccessibleChild(idx); 4703 if (a == null) 4704 return null; 4705 else 4706 return a.getAccessibleContext(); 4707 } 4708 }, ac); 4709 if (ac2 == null || 4710 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4711 @Override 4712 public Boolean call() throws Exception { 4713 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4714 } 4715 }, ac))) { 4716 continue; 4717 } 4718 if (!_foundVisibleChild && _currentVisibleIndex == index) { 4719 _visibleChild = ac2; 4720 _foundVisibleChild = true; 4721 return; 4722 } 4723 _currentVisibleIndex++; 4724 4725 if ( InvocationUtils.invokeAndWait(new Callable<Integer>() { 4726 @Override 4727 public Integer call() throws Exception { 4728 return ac2.getAccessibleChildrenCount(); 4729 } 4730 }, ac) > 0 ) { 4731 _getVisibleChild(ac2, index); 4732 } 4733 } 4734 } 4735 4736 private void _getVisibleChild(final AccessibleExtendedTable acTable, final int index) { 4737 if (_visibleChild != null) { 4738 return; 4739 } 4740 int lastVisibleRow = -1; 4741 int lastVisibleColumn = -1; 4742 boolean foundVisible = false; 4743 int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4744 @Override 4745 public Integer call() throws Exception { 4746 return acTable.getAccessibleRowCount(); 4747 } 4748 }, acTable); 4749 int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() { 4750 @Override 4751 public Integer call() throws Exception { 4752 return acTable.getAccessibleColumnCount(); 4753 } 4754 }, acTable); 4755 for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) { 4756 for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) { 4757 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) { 4758 continue; 4759 } 4760 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) { 4761 continue; 4762 } 4763 int finalRowIdx = rowIdx; 4764 int finalColumnIdx = columnIdx; 4765 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() { 4766 @Override 4767 public AccessibleContext call() throws Exception { 4768 Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx); 4769 if (a == null) 4770 return null; 4771 else 4772 return a.getAccessibleContext(); 4773 } 4774 }, acTable); 4775 if (ac2 == null || 4776 (!InvocationUtils.invokeAndWait(new Callable<Boolean>() { 4777 @Override 4778 public Boolean call() throws Exception { 4779 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING); 4780 } 4781 }, acTable))) { 4782 if (foundVisible) { 4783 if (columnIdx != 0 && lastVisibleColumn == -1) { 4784 //the same row, so we found the last visible column 4785 lastVisibleColumn = columnIdx - 1; 4786 } else if (columnIdx == 0 && lastVisibleRow == -1) { 4787 lastVisibleRow = rowIdx - 1; 4788 } 4789 } 4790 continue; 4791 } 4792 foundVisible = true; 4793 4794 if (!_foundVisibleChild && _currentVisibleIndex == index) { 4795 _visibleChild = ac2; 4796 _foundVisibleChild = true; 4797 return; 4798 } 4799 _currentVisibleIndex++; 4800 4801 if (InvocationUtils.invokeAndWait(new Callable<Integer>() { 4802 @Override 4803 public Integer call() throws Exception { 4804 return ac2.getAccessibleChildrenCount(); 4805 } 4806 }, acTable) > 0) { 4807 _getVisibleChild(ac2, index); 4808 } 4809 } 4810 } 4811 } 4812 4813 /* ===== Java object memory management code ===== */ 4814 4815 /** 4816 * Class to track object references to ensure the 4817 * Java VM doesn't garbage collect them 4818 */ 4819 private class ObjectReferences { 4820 4821 private class Reference { 4822 private int value; 4823 4824 Reference(int i) { 4825 value = i; 4826 } 4827 4828 public String toString() { 4829 return ("refCount: " + value); 4830 } 4831 } 4832 4833 /** 4834 * table object references, to keep 'em from being garbage collected 4835 */ 4836 private ConcurrentHashMap<Object,Reference> refs; 4837 4838 /** 4839 * Constructor 4840 */ 4841 ObjectReferences() { 4842 refs = new ConcurrentHashMap<>(4); 4843 } 4844 4845 /** 4846 * Debugging: dump the contents of ObjectReferences' refs Hashtable 4847 */ 4848 String dump() { 4849 return refs.toString(); 4850 } 4851 4852 /** 4853 * Increment ref count; set to 1 if we have no references for it 4854 */ 4855 void increment(Object o) { 4856 if (o == null){ 4857 debugString("[WARN]: ObjectReferences::increment - Passed in object is null"); 4858 return; 4859 } 4860 4861 if (refs.containsKey(o)) { 4862 (refs.get(o)).value++; 4863 } else { 4864 refs.put(o, new Reference(1)); 4865 } 4866 } 4867 4868 /** 4869 * Decrement ref count; remove if count drops to 0 4870 */ 4871 void decrement(Object o) { 4872 Reference aRef = refs.get(o); 4873 if (aRef != null) { 4874 aRef.value--; 4875 if (aRef.value == 0) { 4876 refs.remove(o); 4877 } else if (aRef.value < 0) { 4878 debugString("[ERROR]: decrementing reference count below 0"); 4879 } 4880 } else { 4881 debugString("[ERROR]: object to decrement not in ObjectReferences table"); 4882 } 4883 } 4884 4885 } 4886 4887 /* ===== event handling code ===== */ 4888 4889 /** 4890 * native method for handling property change events 4891 */ 4892 private native void propertyCaretChange(PropertyChangeEvent e, 4893 AccessibleContext src, 4894 int oldValue, int newValue); 4895 private native void propertyDescriptionChange(PropertyChangeEvent e, 4896 AccessibleContext src, 4897 String oldValue, String newValue); 4898 private native void propertyNameChange(PropertyChangeEvent e, 4899 AccessibleContext src, 4900 String oldValue, String newValue); 4901 private native void propertySelectionChange(PropertyChangeEvent e, 4902 AccessibleContext src); 4903 private native void propertyStateChange(PropertyChangeEvent e, 4904 AccessibleContext src, 4905 String oldValue, String newValue); 4906 private native void propertyTextChange(PropertyChangeEvent e, 4907 AccessibleContext src); 4908 private native void propertyValueChange(PropertyChangeEvent e, 4909 AccessibleContext src, 4910 String oldValue, String newValue); 4911 private native void propertyVisibleDataChange(PropertyChangeEvent e, 4912 AccessibleContext src); 4913 private native void propertyChildChange(PropertyChangeEvent e, 4914 AccessibleContext src, 4915 AccessibleContext oldValue, 4916 AccessibleContext newValue); 4917 private native void propertyActiveDescendentChange(PropertyChangeEvent e, 4918 AccessibleContext src, 4919 AccessibleContext oldValue, 4920 AccessibleContext newValue); 4921 4922 private native void javaShutdown(); 4923 4924 /** 4925 * native methods for handling focus events 4926 */ 4927 private native void focusGained(FocusEvent e, AccessibleContext src); 4928 private native void focusLost(FocusEvent e, AccessibleContext src); 4929 4930 /** 4931 * native method for handling caret events 4932 */ 4933 private native void caretUpdate(CaretEvent e, AccessibleContext src); 4934 4935 /** 4936 * native methods for handling mouse events 4937 */ 4938 private native void mouseClicked(MouseEvent e, AccessibleContext src); 4939 private native void mouseEntered(MouseEvent e, AccessibleContext src); 4940 private native void mouseExited(MouseEvent e, AccessibleContext src); 4941 private native void mousePressed(MouseEvent e, AccessibleContext src); 4942 private native void mouseReleased(MouseEvent e, AccessibleContext src); 4943 4944 /** 4945 * native methods for handling menu & popupMenu events 4946 */ 4947 private native void menuCanceled(MenuEvent e, AccessibleContext src); 4948 private native void menuDeselected(MenuEvent e, AccessibleContext src); 4949 private native void menuSelected(MenuEvent e, AccessibleContext src); 4950 private native void popupMenuCanceled(PopupMenuEvent e, AccessibleContext src); 4951 private native void popupMenuWillBecomeInvisible(PopupMenuEvent e, 4952 AccessibleContext src); 4953 private native void popupMenuWillBecomeVisible(PopupMenuEvent e, 4954 AccessibleContext src); 4955 4956 /* ===== event definitions ===== */ 4957 4958 private static final long PROPERTY_CHANGE_EVENTS = 1; 4959 private static final long FOCUS_GAINED_EVENTS = 2; 4960 private static final long FOCUS_LOST_EVENTS = 4; 4961 private static final long FOCUS_EVENTS = (FOCUS_GAINED_EVENTS | FOCUS_LOST_EVENTS); 4962 4963 private static final long CARET_UPATE_EVENTS = 8; 4964 private static final long CARET_EVENTS = CARET_UPATE_EVENTS; 4965 4966 private static final long MOUSE_CLICKED_EVENTS = 16; 4967 private static final long MOUSE_ENTERED_EVENTS = 32; 4968 private static final long MOUSE_EXITED_EVENTS = 64; 4969 private static final long MOUSE_PRESSED_EVENTS = 128; 4970 private static final long MOUSE_RELEASED_EVENTS = 256; 4971 private static final long MOUSE_EVENTS = (MOUSE_CLICKED_EVENTS | MOUSE_ENTERED_EVENTS | 4972 MOUSE_EXITED_EVENTS | MOUSE_PRESSED_EVENTS | 4973 MOUSE_RELEASED_EVENTS); 4974 4975 private static final long MENU_CANCELED_EVENTS = 512; 4976 private static final long MENU_DESELECTED_EVENTS = 1024; 4977 private static final long MENU_SELECTED_EVENTS = 2048; 4978 private static final long MENU_EVENTS = (MENU_CANCELED_EVENTS | MENU_DESELECTED_EVENTS | 4979 MENU_SELECTED_EVENTS); 4980 4981 private static final long POPUPMENU_CANCELED_EVENTS = 4096; 4982 private static final long POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS = 8192; 4983 private static final long POPUPMENU_WILL_BECOME_VISIBLE_EVENTS = 16384; 4984 private static final long POPUPMENU_EVENTS = (POPUPMENU_CANCELED_EVENTS | 4985 POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS | 4986 POPUPMENU_WILL_BECOME_VISIBLE_EVENTS); 4987 4988 /* These use their own numbering scheme, to ensure sufficient expansion room */ 4989 private static final long PROPERTY_NAME_CHANGE_EVENTS = 1; 4990 private static final long PROPERTY_DESCRIPTION_CHANGE_EVENTS = 2; 4991 private static final long PROPERTY_STATE_CHANGE_EVENTS = 4; 4992 private static final long PROPERTY_VALUE_CHANGE_EVENTS = 8; 4993 private static final long PROPERTY_SELECTION_CHANGE_EVENTS = 16; 4994 private static final long PROPERTY_TEXT_CHANGE_EVENTS = 32; 4995 private static final long PROPERTY_CARET_CHANGE_EVENTS = 64; 4996 private static final long PROPERTY_VISIBLEDATA_CHANGE_EVENTS = 128; 4997 private static final long PROPERTY_CHILD_CHANGE_EVENTS = 256; 4998 private static final long PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS = 512; 4999 5000 5001 private static final long PROPERTY_EVENTS = (PROPERTY_NAME_CHANGE_EVENTS | 5002 PROPERTY_DESCRIPTION_CHANGE_EVENTS | 5003 PROPERTY_STATE_CHANGE_EVENTS | 5004 PROPERTY_VALUE_CHANGE_EVENTS | 5005 PROPERTY_SELECTION_CHANGE_EVENTS | 5006 PROPERTY_TEXT_CHANGE_EVENTS | 5007 PROPERTY_CARET_CHANGE_EVENTS | 5008 PROPERTY_VISIBLEDATA_CHANGE_EVENTS | 5009 PROPERTY_CHILD_CHANGE_EVENTS | 5010 PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS); 5011 5012 /** 5013 * The EventHandler class listens for Java events and 5014 * forwards them to the AT 5015 */ 5016 private class EventHandler implements PropertyChangeListener, 5017 FocusListener, CaretListener, 5018 MenuListener, PopupMenuListener, 5019 MouseListener, WindowListener, 5020 ChangeListener { 5021 5022 private AccessBridge accessBridge; 5023 private long javaEventMask = 0; 5024 private long accessibilityEventMask = 0; 5025 5026 EventHandler(AccessBridge bridge) { 5027 accessBridge = bridge; 5028 5029 // Register to receive WINDOW_OPENED and WINDOW_CLOSED 5030 // events. Add the event source as a native window 5031 // handler is it implements NativeWindowHandler. 5032 // SwingEventMonitor.addWindowListener(this); 5033 } 5034 5035 // --------- Event Notification Registration methods 5036 5037 /** 5038 * Invoked the first time a window is made visible. 5039 */ 5040 public void windowOpened(WindowEvent e) { 5041 // If the window is a NativeWindowHandler, add it. 5042 Object o = null; 5043 if (e != null) 5044 o = e.getSource(); 5045 if (o instanceof NativeWindowHandler) { 5046 addNativeWindowHandler((NativeWindowHandler)o); 5047 } 5048 } 5049 5050 /** 5051 * Invoked when the user attempts to close the window 5052 * from the window's system menu. If the program does not 5053 * explicitly hide or dispose the window while processing 5054 * this event, the window close operation will be canceled. 5055 */ 5056 public void windowClosing(WindowEvent e) {} 5057 5058 /** 5059 * Invoked when a window has been closed as the result 5060 * of calling dispose on the window. 5061 */ 5062 public void windowClosed(WindowEvent e) { 5063 // If the window is a NativeWindowHandler, remove it. 5064 Object o = null; 5065 if (e != null) 5066 o = e.getSource(); 5067 if (o instanceof NativeWindowHandler) { 5068 removeNativeWindowHandler((NativeWindowHandler)o); 5069 } 5070 } 5071 5072 /** 5073 * Invoked when a window is changed from a normal to a 5074 * minimized state. For many platforms, a minimized window 5075 * is displayed as the icon specified in the window's 5076 * iconImage property. 5077 * @see java.awt.Frame#setIconImage 5078 */ 5079 public void windowIconified(WindowEvent e) {} 5080 5081 /** 5082 * Invoked when a window is changed from a minimized 5083 * to a normal state. 5084 */ 5085 public void windowDeiconified(WindowEvent e) {} 5086 5087 /** 5088 * Invoked when the Window is set to be the active Window. Only a Frame or 5089 * a Dialog can be the active Window. The native windowing system may 5090 * denote the active Window or its children with special decorations, such 5091 * as a highlighted title bar. The active Window is always either the 5092 * focused Window, or the first Frame or Dialog that is an owner of the 5093 * focused Window. 5094 */ 5095 public void windowActivated(WindowEvent e) {} 5096 5097 /** 5098 * Invoked when a Window is no longer the active Window. Only a Frame or a 5099 * Dialog can be the active Window. The native windowing system may denote 5100 * the active Window or its children with special decorations, such as a 5101 * highlighted title bar. The active Window is always either the focused 5102 * Window, or the first Frame or Dialog that is an owner of the focused 5103 * Window. 5104 */ 5105 public void windowDeactivated(WindowEvent e) {} 5106 5107 /** 5108 * Turn on event monitoring for the event type passed in 5109 * If necessary, add the appropriate event listener (if 5110 * no other event of that type is being listened for) 5111 */ 5112 void addJavaEventNotification(long type) { 5113 long newEventMask = javaEventMask | type; 5114 /* 5115 if ( ((javaEventMask & PROPERTY_EVENTS) == 0) && 5116 ((newEventMask & PROPERTY_EVENTS) != 0) ) { 5117 AccessibilityEventMonitor.addPropertyChangeListener(this); 5118 } 5119 */ 5120 if ( ((javaEventMask & FOCUS_EVENTS) == 0) && 5121 ((newEventMask & FOCUS_EVENTS) != 0) ) { 5122 SwingEventMonitor.addFocusListener(this); 5123 } 5124 if ( ((javaEventMask & CARET_EVENTS) == 0) && 5125 ((newEventMask & CARET_EVENTS) != 0) ) { 5126 SwingEventMonitor.addCaretListener(this); 5127 } 5128 if ( ((javaEventMask & MOUSE_EVENTS) == 0) && 5129 ((newEventMask & MOUSE_EVENTS) != 0) ) { 5130 SwingEventMonitor.addMouseListener(this); 5131 } 5132 if ( ((javaEventMask & MENU_EVENTS) == 0) && 5133 ((newEventMask & MENU_EVENTS) != 0) ) { 5134 SwingEventMonitor.addMenuListener(this); 5135 SwingEventMonitor.addPopupMenuListener(this); 5136 } 5137 if ( ((javaEventMask & POPUPMENU_EVENTS) == 0) && 5138 ((newEventMask & POPUPMENU_EVENTS) != 0) ) { 5139 SwingEventMonitor.addPopupMenuListener(this); 5140 } 5141 5142 javaEventMask = newEventMask; 5143 } 5144 5145 /** 5146 * Turn off event monitoring for the event type passed in 5147 * If necessary, remove the appropriate event listener (if 5148 * no other event of that type is being listened for) 5149 */ 5150 void removeJavaEventNotification(long type) { 5151 long newEventMask = javaEventMask & (~type); 5152 /* 5153 if ( ((javaEventMask & PROPERTY_EVENTS) != 0) && 5154 ((newEventMask & PROPERTY_EVENTS) == 0) ) { 5155 AccessibilityEventMonitor.removePropertyChangeListener(this); 5156 } 5157 */ 5158 if (((javaEventMask & FOCUS_EVENTS) != 0) && 5159 ((newEventMask & FOCUS_EVENTS) == 0)) { 5160 SwingEventMonitor.removeFocusListener(this); 5161 } 5162 if (((javaEventMask & CARET_EVENTS) != 0) && 5163 ((newEventMask & CARET_EVENTS) == 0)) { 5164 SwingEventMonitor.removeCaretListener(this); 5165 } 5166 if (((javaEventMask & MOUSE_EVENTS) == 0) && 5167 ((newEventMask & MOUSE_EVENTS) != 0)) { 5168 SwingEventMonitor.removeMouseListener(this); 5169 } 5170 if (((javaEventMask & MENU_EVENTS) == 0) && 5171 ((newEventMask & MENU_EVENTS) != 0)) { 5172 SwingEventMonitor.removeMenuListener(this); 5173 } 5174 if (((javaEventMask & POPUPMENU_EVENTS) == 0) && 5175 ((newEventMask & POPUPMENU_EVENTS) != 0)) { 5176 SwingEventMonitor.removePopupMenuListener(this); 5177 } 5178 5179 javaEventMask = newEventMask; 5180 } 5181 5182 /** 5183 * Turn on event monitoring for the event type passed in 5184 * If necessary, add the appropriate event listener (if 5185 * no other event of that type is being listened for) 5186 */ 5187 void addAccessibilityEventNotification(long type) { 5188 long newEventMask = accessibilityEventMask | type; 5189 if ( ((accessibilityEventMask & PROPERTY_EVENTS) == 0) && 5190 ((newEventMask & PROPERTY_EVENTS) != 0) ) { 5191 AccessibilityEventMonitor.addPropertyChangeListener(this); 5192 } 5193 accessibilityEventMask = newEventMask; 5194 } 5195 5196 /** 5197 * Turn off event monitoring for the event type passed in 5198 * If necessary, remove the appropriate event listener (if 5199 * no other event of that type is being listened for) 5200 */ 5201 void removeAccessibilityEventNotification(long type) { 5202 long newEventMask = accessibilityEventMask & (~type); 5203 if ( ((accessibilityEventMask & PROPERTY_EVENTS) != 0) && 5204 ((newEventMask & PROPERTY_EVENTS) == 0) ) { 5205 AccessibilityEventMonitor.removePropertyChangeListener(this); 5206 } 5207 accessibilityEventMask = newEventMask; 5208 } 5209 5210 /** 5211 * ------- property change event glue 5212 */ 5213 // This is invoked on the EDT , as 5214 public void propertyChange(PropertyChangeEvent e) { 5215 5216 accessBridge.debugString("[INFO]: propertyChange(" + e.toString() + ") called"); 5217 5218 if (e != null && (accessibilityEventMask & PROPERTY_EVENTS) != 0) { 5219 Object o = e.getSource(); 5220 AccessibleContext ac; 5221 5222 if (o instanceof AccessibleContext) { 5223 ac = (AccessibleContext) o; 5224 } else { 5225 Accessible a = Translator.getAccessible(e.getSource()); 5226 if (a == null) 5227 return; 5228 else 5229 ac = a.getAccessibleContext(); 5230 } 5231 if (ac != null) { 5232 InvocationUtils.registerAccessibleContext(ac, AppContext.getAppContext()); 5233 5234 accessBridge.debugString("[INFO]: AccessibleContext: " + ac); 5235 String propertyName = e.getPropertyName(); 5236 5237 if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CARET_PROPERTY) == 0) { 5238 int oldValue = 0; 5239 int newValue = 0; 5240 5241 if (e.getOldValue() instanceof Integer) { 5242 oldValue = ((Integer) e.getOldValue()).intValue(); 5243 } 5244 if (e.getNewValue() instanceof Integer) { 5245 newValue = ((Integer) e.getNewValue()).intValue(); 5246 } 5247 accessBridge.debugString("[INFO]: - about to call propertyCaretChange() old value: " + oldValue + "new value: " + newValue); 5248 accessBridge.propertyCaretChange(e, ac, oldValue, newValue); 5249 5250 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY) == 0) { 5251 String oldValue = null; 5252 String newValue = null; 5253 5254 if (e.getOldValue() != null) { 5255 oldValue = e.getOldValue().toString(); 5256 } 5257 if (e.getNewValue() != null) { 5258 newValue = e.getNewValue().toString(); 5259 } 5260 accessBridge.debugString("[INFO]: - about to call propertyDescriptionChange() old value: " + oldValue + "new value: " + newValue); 5261 accessBridge.propertyDescriptionChange(e, ac, oldValue, newValue); 5262 5263 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_NAME_PROPERTY) == 0) { 5264 String oldValue = null; 5265 String newValue = null; 5266 5267 if (e.getOldValue() != null) { 5268 oldValue = e.getOldValue().toString(); 5269 } 5270 if (e.getNewValue() != null) { 5271 newValue = e.getNewValue().toString(); 5272 } 5273 accessBridge.debugString("[INFO]: - about to call propertyNameChange() old value: " + oldValue + " new value: " + newValue); 5274 accessBridge.propertyNameChange(e, ac, oldValue, newValue); 5275 5276 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY) == 0) { 5277 accessBridge.debugString("[INFO]: - about to call propertySelectionChange() " + ac + " " + Thread.currentThread() + " " + e.getSource()); 5278 5279 accessBridge.propertySelectionChange(e, ac); 5280 5281 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_STATE_PROPERTY) == 0) { 5282 String oldValue = null; 5283 String newValue = null; 5284 5285 // Localization fix requested by Oliver for EA-1 5286 if (e.getOldValue() != null) { 5287 AccessibleState oldState = (AccessibleState) e.getOldValue(); 5288 oldValue = oldState.toDisplayString(Locale.US); 5289 } 5290 if (e.getNewValue() != null) { 5291 AccessibleState newState = (AccessibleState) e.getNewValue(); 5292 newValue = newState.toDisplayString(Locale.US); 5293 } 5294 5295 accessBridge.debugString("[INFO]: - about to call propertyStateChange()"); 5296 accessBridge.propertyStateChange(e, ac, oldValue, newValue); 5297 5298 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_TEXT_PROPERTY) == 0) { 5299 accessBridge.debugString("[INFO]: - about to call propertyTextChange()"); 5300 accessBridge.propertyTextChange(e, ac); 5301 5302 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY) == 0) { // strings 'cause of floating point, etc. 5303 String oldValue = null; 5304 String newValue = null; 5305 5306 if (e.getOldValue() != null) { 5307 oldValue = e.getOldValue().toString(); 5308 } 5309 if (e.getNewValue() != null) { 5310 newValue = e.getNewValue().toString(); 5311 } 5312 accessBridge.debugString("[INFO]: - about to call propertyDescriptionChange()"); 5313 accessBridge.propertyValueChange(e, ac, oldValue, newValue); 5314 5315 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY) == 0) { 5316 accessBridge.propertyVisibleDataChange(e, ac); 5317 5318 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) { 5319 AccessibleContext oldAC = null; 5320 AccessibleContext newAC = null; 5321 Accessible a; 5322 5323 if (e.getOldValue() instanceof AccessibleContext) { 5324 oldAC = (AccessibleContext) e.getOldValue(); 5325 InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext()); 5326 } 5327 if (e.getNewValue() instanceof AccessibleContext) { 5328 newAC = (AccessibleContext) e.getNewValue(); 5329 InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext()); 5330 } 5331 accessBridge.debugString("[INFO]: - about to call propertyChildChange() old AC: " + oldAC + "new AC: " + newAC); 5332 accessBridge.propertyChildChange(e, ac, oldAC, newAC); 5333 5334 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0) { 5335 handleActiveDescendentEvent(e, ac); 5336 } 5337 } 5338 } 5339 } 5340 5341 /* 5342 * Handle an ActiveDescendent PropertyChangeEvent. This 5343 * method works around a JTree bug where ActiveDescendent 5344 * PropertyChangeEvents have the wrong parent. 5345 */ 5346 private AccessibleContext prevAC = null; // previous AccessibleContext 5347 5348 private void handleActiveDescendentEvent(PropertyChangeEvent e, 5349 AccessibleContext ac) { 5350 if (e == null || ac == null) 5351 return; 5352 AccessibleContext oldAC = null; 5353 AccessibleContext newAC = null; 5354 Accessible a; 5355 5356 // get the old active descendent 5357 if (e.getOldValue() instanceof Accessible) { 5358 oldAC = ((Accessible) e.getOldValue()).getAccessibleContext(); 5359 } else if (e.getOldValue() instanceof Component) { 5360 a = Translator.getAccessible(e.getOldValue()); 5361 if (a != null) { 5362 oldAC = a.getAccessibleContext(); 5363 } 5364 } 5365 if (oldAC != null) { 5366 Accessible parent = oldAC.getAccessibleParent(); 5367 if (parent instanceof JTree) { 5368 // use the previous AccessibleJTreeNode 5369 oldAC = prevAC; 5370 } 5371 } 5372 5373 // get the new active descendent 5374 if (e.getNewValue() instanceof Accessible) { 5375 newAC = ((Accessible) e.getNewValue()).getAccessibleContext(); 5376 } else if (e.getNewValue() instanceof Component) { 5377 a = Translator.getAccessible(e.getNewValue()); 5378 if (a != null) { 5379 newAC = a.getAccessibleContext(); 5380 } 5381 } 5382 if (newAC != null) { 5383 Accessible parent = newAC.getAccessibleParent(); 5384 if (parent instanceof JTree) { 5385 // use a new AccessibleJTreeNode with the right parent 5386 JTree tree = (JTree)parent; 5387 newAC = new AccessibleJTreeNode(tree, 5388 tree.getSelectionPath(), 5389 null); 5390 } 5391 } 5392 prevAC = newAC; 5393 5394 accessBridge.debugString("[INFO]: - about to call propertyActiveDescendentChange() AC: " + ac + " old AC: " + oldAC + "new AC: " + newAC); 5395 InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext()); 5396 InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext()); 5397 accessBridge.propertyActiveDescendentChange(e, ac, oldAC, newAC); 5398 } 5399 5400 /** 5401 * ------- focus event glue 5402 */ 5403 private boolean stateChangeListenerAdded = false; 5404 5405 public void focusGained(FocusEvent e) { 5406 processFocusGained(); 5407 } 5408 5409 public void stateChanged(ChangeEvent e) { 5410 processFocusGained(); 5411 } 5412 5413 private void processFocusGained() { 5414 Component focusOwner = KeyboardFocusManager. 5415 getCurrentKeyboardFocusManager().getFocusOwner(); 5416 if (focusOwner == null) { 5417 return; 5418 } 5419 5420 // Only menus and popup selections are handled by the JRootPane. 5421 if (focusOwner instanceof JRootPane) { 5422 MenuElement [] path = 5423 MenuSelectionManager.defaultManager().getSelectedPath(); 5424 if (path.length > 1) { 5425 Component penult = path[path.length-2].getComponent(); 5426 Component last = path[path.length-1].getComponent(); 5427 5428 if (last instanceof JPopupMenu) { 5429 // This is a popup with nothing in the popup 5430 // selected. The menu itself is selected. 5431 FocusEvent e = new FocusEvent(penult, FocusEvent.FOCUS_GAINED); 5432 AccessibleContext context = penult.getAccessibleContext(); 5433 InvocationUtils.registerAccessibleContext(context, SunToolkit.targetToAppContext(penult)); 5434 accessBridge.focusGained(e, context); 5435 } else if (penult instanceof JPopupMenu) { 5436 // This is a popup with an item selected 5437 FocusEvent e = 5438 new FocusEvent(last, FocusEvent.FOCUS_GAINED); 5439 AccessibleContext focusedAC = last.getAccessibleContext(); 5440 InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(last)); 5441 accessBridge.debugString("[INFO]: - about to call focusGained() AC: " + focusedAC); 5442 accessBridge.focusGained(e, focusedAC); 5443 } 5444 } 5445 } else { 5446 // The focus owner has the selection. 5447 if (focusOwner instanceof Accessible) { 5448 FocusEvent e = new FocusEvent(focusOwner, 5449 FocusEvent.FOCUS_GAINED); 5450 AccessibleContext focusedAC = focusOwner.getAccessibleContext(); 5451 InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(focusOwner)); 5452 accessBridge.debugString("[INFO]: - about to call focusGained() AC: " + focusedAC); 5453 accessBridge.focusGained(e, focusedAC); 5454 } 5455 } 5456 } 5457 5458 public void focusLost(FocusEvent e) { 5459 if (e != null && (javaEventMask & FOCUS_LOST_EVENTS) != 0) { 5460 Accessible a = Translator.getAccessible(e.getSource()); 5461 if (a != null) { 5462 accessBridge.debugString("[INFO]: - about to call focusLost() AC: " + a.getAccessibleContext()); 5463 AccessibleContext context = a.getAccessibleContext(); 5464 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5465 accessBridge.focusLost(e, context); 5466 } 5467 } 5468 } 5469 5470 /** 5471 * ------- caret event glue 5472 */ 5473 public void caretUpdate(CaretEvent e) { 5474 if (e != null && (javaEventMask & CARET_UPATE_EVENTS) != 0) { 5475 Accessible a = Translator.getAccessible(e.getSource()); 5476 if (a != null) { 5477 AccessibleContext context = a.getAccessibleContext(); 5478 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5479 accessBridge.caretUpdate(e, context); 5480 } 5481 } 5482 } 5483 5484 /** 5485 * ------- mouse event glue 5486 */ 5487 5488 public void mouseClicked(MouseEvent e) { 5489 if (e != null && (javaEventMask & MOUSE_CLICKED_EVENTS) != 0) { 5490 Accessible a = Translator.getAccessible(e.getSource()); 5491 if (a != null) { 5492 AccessibleContext context = a.getAccessibleContext(); 5493 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5494 accessBridge.mouseClicked(e, context); 5495 } 5496 } 5497 } 5498 5499 public void mouseEntered(MouseEvent e) { 5500 if (e != null && (javaEventMask & MOUSE_ENTERED_EVENTS) != 0) { 5501 Accessible a = Translator.getAccessible(e.getSource()); 5502 if (a != null) { 5503 AccessibleContext context = a.getAccessibleContext(); 5504 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5505 accessBridge.mouseEntered(e, context); 5506 } 5507 } 5508 } 5509 5510 public void mouseExited(MouseEvent e) { 5511 if (e != null && (javaEventMask & MOUSE_EXITED_EVENTS) != 0) { 5512 Accessible a = Translator.getAccessible(e.getSource()); 5513 if (a != null) { 5514 AccessibleContext context = a.getAccessibleContext(); 5515 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5516 accessBridge.mouseExited(e, context); 5517 } 5518 } 5519 } 5520 5521 public void mousePressed(MouseEvent e) { 5522 if (e != null && (javaEventMask & MOUSE_PRESSED_EVENTS) != 0) { 5523 Accessible a = Translator.getAccessible(e.getSource()); 5524 if (a != null) { 5525 AccessibleContext context = a.getAccessibleContext(); 5526 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5527 accessBridge.mousePressed(e, context); 5528 } 5529 } 5530 } 5531 5532 public void mouseReleased(MouseEvent e) { 5533 if (e != null && (javaEventMask & MOUSE_RELEASED_EVENTS) != 0) { 5534 Accessible a = Translator.getAccessible(e.getSource()); 5535 if (a != null) { 5536 AccessibleContext context = a.getAccessibleContext(); 5537 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5538 accessBridge.mouseReleased(e, context); 5539 } 5540 } 5541 } 5542 5543 /** 5544 * ------- menu event glue 5545 */ 5546 public void menuCanceled(MenuEvent e) { 5547 if (e != null && (javaEventMask & MENU_CANCELED_EVENTS) != 0) { 5548 Accessible a = Translator.getAccessible(e.getSource()); 5549 if (a != null) { 5550 AccessibleContext context = a.getAccessibleContext(); 5551 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5552 accessBridge.menuCanceled(e, context); 5553 } 5554 } 5555 } 5556 5557 public void menuDeselected(MenuEvent e) { 5558 if (e != null && (javaEventMask & MENU_DESELECTED_EVENTS) != 0) { 5559 Accessible a = Translator.getAccessible(e.getSource()); 5560 if (a != null) { 5561 AccessibleContext context = a.getAccessibleContext(); 5562 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5563 accessBridge.menuDeselected(e, context); 5564 } 5565 } 5566 } 5567 5568 public void menuSelected(MenuEvent e) { 5569 if (e != null && (javaEventMask & MENU_SELECTED_EVENTS) != 0) { 5570 Accessible a = Translator.getAccessible(e.getSource()); 5571 if (a != null) { 5572 AccessibleContext context = a.getAccessibleContext(); 5573 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5574 accessBridge.menuSelected(e, context); 5575 } 5576 } 5577 } 5578 5579 public void popupMenuCanceled(PopupMenuEvent e) { 5580 if (e != null && (javaEventMask & POPUPMENU_CANCELED_EVENTS) != 0) { 5581 Accessible a = Translator.getAccessible(e.getSource()); 5582 if (a != null) { 5583 AccessibleContext context = a.getAccessibleContext(); 5584 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5585 accessBridge.popupMenuCanceled(e, context); 5586 } 5587 } 5588 } 5589 5590 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { 5591 if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS) != 0) { 5592 Accessible a = Translator.getAccessible(e.getSource()); 5593 if (a != null) { 5594 AccessibleContext context = a.getAccessibleContext(); 5595 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5596 accessBridge.popupMenuWillBecomeInvisible(e, context); 5597 } 5598 } 5599 } 5600 5601 public void popupMenuWillBecomeVisible(PopupMenuEvent e) { 5602 if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_VISIBLE_EVENTS) != 0) { 5603 Accessible a = Translator.getAccessible(e.getSource()); 5604 if (a != null) { 5605 AccessibleContext context = a.getAccessibleContext(); 5606 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); 5607 accessBridge.popupMenuWillBecomeVisible(e, context); 5608 } 5609 } 5610 } 5611 5612 } // End of EventHandler Class 5613 5614 // --------- Event Notification Registration methods 5615 5616 /** 5617 * Wrapper method around eventHandler.addJavaEventNotification() 5618 */ 5619 private void addJavaEventNotification(final long type) { 5620 EventQueue.invokeLater(new Runnable() { 5621 public void run(){ 5622 eventHandler.addJavaEventNotification(type); 5623 } 5624 }); 5625 } 5626 5627 /** 5628 * Wrapper method around eventHandler.removeJavaEventNotification() 5629 */ 5630 private void removeJavaEventNotification(final long type) { 5631 EventQueue.invokeLater(new Runnable() { 5632 public void run(){ 5633 eventHandler.removeJavaEventNotification(type); 5634 } 5635 }); 5636 } 5637 5638 5639 /** 5640 * Wrapper method around eventHandler.addAccessibilityEventNotification() 5641 */ 5642 private void addAccessibilityEventNotification(final long type) { 5643 EventQueue.invokeLater(new Runnable() { 5644 public void run(){ 5645 eventHandler.addAccessibilityEventNotification(type); 5646 } 5647 }); 5648 } 5649 5650 /** 5651 * Wrapper method around eventHandler.removeAccessibilityEventNotification() 5652 */ 5653 private void removeAccessibilityEventNotification(final long type) { 5654 EventQueue.invokeLater(new Runnable() { 5655 public void run(){ 5656 eventHandler.removeAccessibilityEventNotification(type); 5657 } 5658 }); 5659 } 5660 5661 /** 5662 ****************************************************** 5663 * All AccessibleRoles 5664 * 5665 * We shouldn't have to do this since it requires us 5666 * to synchronize the allAccessibleRoles array when 5667 * the AccessibleRoles class interface changes. However, 5668 * there is no Accessibility API method to get all 5669 * AccessibleRoles 5670 ****************************************************** 5671 */ 5672 private AccessibleRole [] allAccessibleRoles = { 5673 /** 5674 * Object is used to alert the user about something. 5675 */ 5676 AccessibleRole.ALERT, 5677 5678 /** 5679 * The header for a column of data. 5680 */ 5681 AccessibleRole.COLUMN_HEADER, 5682 5683 /** 5684 * Object that can be drawn into and is used to trap 5685 * events. 5686 * @see #FRAME 5687 * @see #GLASS_PANE 5688 * @see #LAYERED_PANE 5689 */ 5690 AccessibleRole.CANVAS, 5691 5692 /** 5693 * A list of choices the user can select from. Also optionally 5694 * allows the user to enter a choice of their own. 5695 */ 5696 AccessibleRole.COMBO_BOX, 5697 5698 /** 5699 * An iconified internal frame in a DESKTOP_PANE. 5700 * @see #DESKTOP_PANE 5701 * @see #INTERNAL_FRAME 5702 */ 5703 AccessibleRole.DESKTOP_ICON, 5704 5705 /** 5706 * A frame-like object that is clipped by a desktop pane. The 5707 * desktop pane, internal frame, and desktop icon objects are 5708 * often used to create multiple document interfaces within an 5709 * application. 5710 * @see #DESKTOP_ICON 5711 * @see #DESKTOP_PANE 5712 * @see #FRAME 5713 */ 5714 AccessibleRole.INTERNAL_FRAME, 5715 5716 /** 5717 * A pane that supports internal frames and 5718 * iconified versions of those internal frames. 5719 * @see #DESKTOP_ICON 5720 * @see #INTERNAL_FRAME 5721 */ 5722 AccessibleRole.DESKTOP_PANE, 5723 5724 /** 5725 * A specialized pane whose primary use is inside a DIALOG 5726 * @see #DIALOG 5727 */ 5728 AccessibleRole.OPTION_PANE, 5729 5730 /** 5731 * A top level window with no title or border. 5732 * @see #FRAME 5733 * @see #DIALOG 5734 */ 5735 AccessibleRole.WINDOW, 5736 5737 /** 5738 * A top level window with a title bar, border, menu bar, etc. It is 5739 * often used as the primary window for an application. 5740 * @see #DIALOG 5741 * @see #CANVAS 5742 * @see #WINDOW 5743 */ 5744 AccessibleRole.FRAME, 5745 5746 /** 5747 * A top level window with title bar and a border. A dialog is similar 5748 * to a frame, but it has fewer properties and is often used as a 5749 * secondary window for an application. 5750 * @see #FRAME 5751 * @see #WINDOW 5752 */ 5753 AccessibleRole.DIALOG, 5754 5755 /** 5756 * A specialized dialog that lets the user choose a color. 5757 */ 5758 AccessibleRole.COLOR_CHOOSER, 5759 5760 5761 /** 5762 * A pane that allows the user to navigate through 5763 * and select the contents of a directory. May be used 5764 * by a file chooser. 5765 * @see #FILE_CHOOSER 5766 */ 5767 AccessibleRole.DIRECTORY_PANE, 5768 5769 /** 5770 * A specialized dialog that displays the files in the directory 5771 * and lets the user select a file, browse a different directory, 5772 * or specify a filename. May use the directory pane to show the 5773 * contents of a directory. 5774 * @see #DIRECTORY_PANE 5775 */ 5776 AccessibleRole.FILE_CHOOSER, 5777 5778 /** 5779 * An object that fills up space in a user interface. It is often 5780 * used in interfaces to tweak the spacing between components, 5781 * but serves no other purpose. 5782 */ 5783 AccessibleRole.FILLER, 5784 5785 /** 5786 * A hypertext anchor 5787 */ 5788 // AccessibleRole.HYPERLINK, 5789 5790 /** 5791 * A small fixed size picture, typically used to decorate components. 5792 */ 5793 AccessibleRole.ICON, 5794 5795 /** 5796 * An object used to present an icon or short string in an interface. 5797 */ 5798 AccessibleRole.LABEL, 5799 5800 /** 5801 * A specialized pane that has a glass pane and a layered pane as its 5802 * children. 5803 * @see #GLASS_PANE 5804 * @see #LAYERED_PANE 5805 */ 5806 AccessibleRole.ROOT_PANE, 5807 5808 /** 5809 * A pane that is guaranteed to be painted on top 5810 * of all panes beneath it. 5811 * @see #ROOT_PANE 5812 * @see #CANVAS 5813 */ 5814 AccessibleRole.GLASS_PANE, 5815 5816 /** 5817 * A specialized pane that allows its children to be drawn in layers, 5818 * providing a form of stacking order. This is usually the pane that 5819 * holds the menu bar as well as the pane that contains most of the 5820 * visual components in a window. 5821 * @see #GLASS_PANE 5822 * @see #ROOT_PANE 5823 */ 5824 AccessibleRole.LAYERED_PANE, 5825 5826 /** 5827 * An object that presents a list of objects to the user and allows the 5828 * user to select one or more of them. A list is usually contained 5829 * within a scroll pane. 5830 * @see #SCROLL_PANE 5831 * @see #LIST_ITEM 5832 */ 5833 AccessibleRole.LIST, 5834 5835 /** 5836 * An object that presents an element in a list. A list is usually 5837 * contained within a scroll pane. 5838 * @see #SCROLL_PANE 5839 * @see #LIST 5840 */ 5841 AccessibleRole.LIST_ITEM, 5842 5843 /** 5844 * An object usually drawn at the top of the primary dialog box of 5845 * an application that contains a list of menus the user can choose 5846 * from. For example, a menu bar might contain menus for "File," 5847 * "Edit," and "Help." 5848 * @see #MENU 5849 * @see #POPUP_MENU 5850 * @see #LAYERED_PANE 5851 */ 5852 AccessibleRole.MENU_BAR, 5853 5854 /** 5855 * A temporary window that is usually used to offer the user a 5856 * list of choices, and then hides when the user selects one of 5857 * those choices. 5858 * @see #MENU 5859 * @see #MENU_ITEM 5860 */ 5861 AccessibleRole.POPUP_MENU, 5862 5863 /** 5864 * An object usually found inside a menu bar that contains a list 5865 * of actions the user can choose from. A menu can have any object 5866 * as its children, but most often they are menu items, other menus, 5867 * or rudimentary objects such as radio buttons, check boxes, or 5868 * separators. For example, an application may have an "Edit" menu 5869 * that contains menu items for "Cut" and "Paste." 5870 * @see #MENU_BAR 5871 * @see #MENU_ITEM 5872 * @see #SEPARATOR 5873 * @see #RADIO_BUTTON 5874 * @see #CHECK_BOX 5875 * @see #POPUP_MENU 5876 */ 5877 AccessibleRole.MENU, 5878 5879 /** 5880 * An object usually contained in a menu that presents an action 5881 * the user can choose. For example, the "Cut" menu item in an 5882 * "Edit" menu would be an action the user can select to cut the 5883 * selected area of text in a document. 5884 * @see #MENU_BAR 5885 * @see #SEPARATOR 5886 * @see #POPUP_MENU 5887 */ 5888 AccessibleRole.MENU_ITEM, 5889 5890 /** 5891 * An object usually contained in a menu to provide a visual 5892 * and logical separation of the contents in a menu. For example, 5893 * the "File" menu of an application might contain menu items for 5894 * "Open," "Close," and "Exit," and will place a separator between 5895 * "Close" and "Exit" menu items. 5896 * @see #MENU 5897 * @see #MENU_ITEM 5898 */ 5899 AccessibleRole.SEPARATOR, 5900 5901 /** 5902 * An object that presents a series of panels (or page tabs), one at a 5903 * time, through some mechanism provided by the object. The most common 5904 * mechanism is a list of tabs at the top of the panel. The children of 5905 * a page tab list are all page tabs. 5906 * @see #PAGE_TAB 5907 */ 5908 AccessibleRole.PAGE_TAB_LIST, 5909 5910 /** 5911 * An object that is a child of a page tab list. Its sole child is 5912 * the panel that is to be presented to the user when the user 5913 * selects the page tab from the list of tabs in the page tab list. 5914 * @see #PAGE_TAB_LIST 5915 */ 5916 AccessibleRole.PAGE_TAB, 5917 5918 /** 5919 * A generic container that is often used to group objects. 5920 */ 5921 AccessibleRole.PANEL, 5922 5923 /** 5924 * An object used to indicate how much of a task has been completed. 5925 */ 5926 AccessibleRole.PROGRESS_BAR, 5927 5928 /** 5929 * A text object used for passwords, or other places where the 5930 * text contents is not shown visibly to the user 5931 */ 5932 AccessibleRole.PASSWORD_TEXT, 5933 5934 /** 5935 * An object the user can manipulate to tell the application to do 5936 * something. 5937 * @see #CHECK_BOX 5938 * @see #TOGGLE_BUTTON 5939 * @see #RADIO_BUTTON 5940 */ 5941 AccessibleRole.PUSH_BUTTON, 5942 5943 /** 5944 * A specialized push button that can be checked or unchecked, but 5945 * does not provide a separate indicator for the current state. 5946 * @see #PUSH_BUTTON 5947 * @see #CHECK_BOX 5948 * @see #RADIO_BUTTON 5949 */ 5950 AccessibleRole.TOGGLE_BUTTON, 5951 5952 /** 5953 * A choice that can be checked or unchecked and provides a 5954 * separate indicator for the current state. 5955 * @see #PUSH_BUTTON 5956 * @see #TOGGLE_BUTTON 5957 * @see #RADIO_BUTTON 5958 */ 5959 AccessibleRole.CHECK_BOX, 5960 5961 /** 5962 * A specialized check box that will cause other radio buttons in the 5963 * same group to become unchecked when this one is checked. 5964 * @see #PUSH_BUTTON 5965 * @see #TOGGLE_BUTTON 5966 * @see #CHECK_BOX 5967 */ 5968 AccessibleRole.RADIO_BUTTON, 5969 5970 /** 5971 * The header for a row of data. 5972 */ 5973 AccessibleRole.ROW_HEADER, 5974 5975 /** 5976 * An object that allows a user to incrementally view a large amount 5977 * of information. Its children can include scroll bars and a viewport. 5978 * @see #SCROLL_BAR 5979 * @see #VIEWPORT 5980 */ 5981 AccessibleRole.SCROLL_PANE, 5982 5983 /** 5984 * An object usually used to allow a user to incrementally view a 5985 * large amount of data. Usually used only by a scroll pane. 5986 * @see #SCROLL_PANE 5987 */ 5988 AccessibleRole.SCROLL_BAR, 5989 5990 /** 5991 * An object usually used in a scroll pane. It represents the portion 5992 * of the entire data that the user can see. As the user manipulates 5993 * the scroll bars, the contents of the viewport can change. 5994 * @see #SCROLL_PANE 5995 */ 5996 AccessibleRole.VIEWPORT, 5997 5998 /** 5999 * An object that allows the user to select from a bounded range. For 6000 * example, a slider might be used to select a number between 0 and 100. 6001 */ 6002 AccessibleRole.SLIDER, 6003 6004 /** 6005 * A specialized panel that presents two other panels at the same time. 6006 * Between the two panels is a divider the user can manipulate to make 6007 * one panel larger and the other panel smaller. 6008 */ 6009 AccessibleRole.SPLIT_PANE, 6010 6011 /** 6012 * An object used to present information in terms of rows and columns. 6013 * An example might include a spreadsheet application. 6014 */ 6015 AccessibleRole.TABLE, 6016 6017 /** 6018 * An object that presents text to the user. The text is usually 6019 * editable by the user as opposed to a label. 6020 * @see #LABEL 6021 */ 6022 AccessibleRole.TEXT, 6023 6024 /** 6025 * An object used to present hierarchical information to the user. 6026 * The individual nodes in the tree can be collapsed and expanded 6027 * to provide selective disclosure of the tree's contents. 6028 */ 6029 AccessibleRole.TREE, 6030 6031 /** 6032 * A bar or palette usually composed of push buttons or toggle buttons. 6033 * It is often used to provide the most frequently used functions for an 6034 * application. 6035 */ 6036 AccessibleRole.TOOL_BAR, 6037 6038 /** 6039 * An object that provides information about another object. The 6040 * accessibleDescription property of the tool tip is often displayed 6041 * to the user in a small "help bubble" when the user causes the 6042 * mouse to hover over the object associated with the tool tip. 6043 */ 6044 AccessibleRole.TOOL_TIP, 6045 6046 /** 6047 * An AWT component, but nothing else is known about it. 6048 * @see #SWING_COMPONENT 6049 * @see #UNKNOWN 6050 */ 6051 AccessibleRole.AWT_COMPONENT, 6052 6053 /** 6054 * A Swing component, but nothing else is known about it. 6055 * @see #AWT_COMPONENT 6056 * @see #UNKNOWN 6057 */ 6058 AccessibleRole.SWING_COMPONENT, 6059 6060 /** 6061 * The object contains some Accessible information, but its role is 6062 * not known. 6063 * @see #AWT_COMPONENT 6064 * @see #SWING_COMPONENT 6065 */ 6066 AccessibleRole.UNKNOWN, 6067 6068 // These roles are available since JDK 1.4 6069 6070 /** 6071 * A STATUS_BAR is an simple component that can contain 6072 * multiple labels of status information to the user. 6073 AccessibleRole.STATUS_BAR, 6074 6075 /** 6076 * A DATE_EDITOR is a component that allows users to edit 6077 * java.util.Date and java.util.Time objects 6078 AccessibleRole.DATE_EDITOR, 6079 6080 /** 6081 * A SPIN_BOX is a simple spinner component and its main use 6082 * is for simple numbers. 6083 AccessibleRole.SPIN_BOX, 6084 6085 /** 6086 * A FONT_CHOOSER is a component that lets the user pick various 6087 * attributes for fonts. 6088 AccessibleRole.FONT_CHOOSER, 6089 6090 /** 6091 * A GROUP_BOX is a simple container that contains a border 6092 * around it and contains components inside it. 6093 AccessibleRole.GROUP_BOX 6094 6095 /** 6096 * Since JDK 1.5 6097 * 6098 * A text header 6099 6100 AccessibleRole.HEADER, 6101 6102 /** 6103 * A text footer 6104 6105 AccessibleRole.FOOTER, 6106 6107 /** 6108 * A text paragraph 6109 6110 AccessibleRole.PARAGRAPH, 6111 6112 /** 6113 * A ruler is an object used to measure distance 6114 6115 AccessibleRole.RULER, 6116 6117 /** 6118 * A role indicating the object acts as a formula for 6119 * calculating a value. An example is a formula in 6120 * a spreadsheet cell. 6121 AccessibleRole.EDITBAR 6122 */ 6123 }; 6124 6125 /** 6126 * This class implements accessibility support for the 6127 * <code>JTree</code> child. It provides an implementation of the 6128 * Java Accessibility API appropriate to tree nodes. 6129 * 6130 * Copied from JTree.java to work around a JTree bug where 6131 * ActiveDescendent PropertyChangeEvents contain the wrong 6132 * parent. 6133 */ 6134 /** 6135 * This class in invoked on the EDT as its part of ActiveDescendant, 6136 * hence the calls do not need to be specifically made on the EDT 6137 */ 6138 private class AccessibleJTreeNode extends AccessibleContext 6139 implements Accessible, AccessibleComponent, AccessibleSelection, 6140 AccessibleAction { 6141 6142 private JTree tree = null; 6143 private TreeModel treeModel = null; 6144 private Object obj = null; 6145 private TreePath path = null; 6146 private Accessible accessibleParent = null; 6147 private int index = 0; 6148 private boolean isLeaf = false; 6149 6150 /** 6151 * Constructs an AccessibleJTreeNode 6152 */ 6153 AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) { 6154 tree = t; 6155 path = p; 6156 accessibleParent = ap; 6157 if (t != null) 6158 treeModel = t.getModel(); 6159 if (p != null) { 6160 obj = p.getLastPathComponent(); 6161 if (treeModel != null && obj != null) { 6162 isLeaf = treeModel.isLeaf(obj); 6163 } 6164 } 6165 debugString("[INFO]: AccessibleJTreeNode: name = "+getAccessibleName()+"; TreePath = "+p+"; parent = "+ap); 6166 } 6167 6168 private TreePath getChildTreePath(int i) { 6169 // Tree nodes can't be so complex that they have 6170 // two sets of children -> we're ignoring that case 6171 if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) { 6172 return null; 6173 } else { 6174 Object childObj = treeModel.getChild(obj, i); 6175 Object[] objPath = path.getPath(); 6176 Object[] objChildPath = new Object[objPath.length+1]; 6177 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length); 6178 objChildPath[objChildPath.length-1] = childObj; 6179 return new TreePath(objChildPath); 6180 } 6181 } 6182 6183 /** 6184 * Get the AccessibleContext associated with this tree node. 6185 * In the implementation of the Java Accessibility API for 6186 * this class, return this object, which is its own 6187 * AccessibleContext. 6188 * 6189 * @return this object 6190 */ 6191 public AccessibleContext getAccessibleContext() { 6192 return this; 6193 } 6194 6195 private AccessibleContext getCurrentAccessibleContext() { 6196 Component c = getCurrentComponent(); 6197 if (c instanceof Accessible) { 6198 return (c.getAccessibleContext()); 6199 } else { 6200 return null; 6201 } 6202 } 6203 6204 private Component getCurrentComponent() { 6205 debugString("[INFO]: AccessibleJTreeNode: getCurrentComponent"); 6206 // is the object visible? 6207 // if so, get row, selected, focus & leaf state, 6208 // and then get the renderer component and return it 6209 if (tree != null && tree.isVisible(path)) { 6210 TreeCellRenderer r = tree.getCellRenderer(); 6211 if (r == null) { 6212 debugString("[WARN]: returning null 1"); 6213 return null; 6214 } 6215 TreeUI ui = tree.getUI(); 6216 if (ui != null) { 6217 int row = ui.getRowForPath(tree, path); 6218 boolean selected = tree.isPathSelected(path); 6219 boolean expanded = tree.isExpanded(path); 6220 boolean hasFocus = false; // how to tell?? -PK 6221 Component retval = r.getTreeCellRendererComponent(tree, obj, 6222 selected, expanded, 6223 isLeaf, row, hasFocus); 6224 debugString("[INFO]: returning = "+retval.getClass()); 6225 return retval; 6226 } 6227 } 6228 debugString("[WARN]: returning null 2"); 6229 return null; 6230 } 6231 6232 // AccessibleContext methods 6233 6234 /** 6235 * Get the accessible name of this object. 6236 * 6237 * @return the localized name of the object; null if this 6238 * object does not have a name 6239 */ 6240 public String getAccessibleName() { 6241 debugString("[INFO]: AccessibleJTreeNode: getAccessibleName"); 6242 AccessibleContext ac = getCurrentAccessibleContext(); 6243 if (ac != null) { 6244 String name = ac.getAccessibleName(); 6245 if ((name != null) && (!name.isEmpty())) { 6246 String retval = ac.getAccessibleName(); 6247 debugString("[INFO]: returning "+retval); 6248 return retval; 6249 } else { 6250 return null; 6251 } 6252 } 6253 if ((accessibleName != null) && (accessibleName.isEmpty())) { 6254 return accessibleName; 6255 } else { 6256 return null; 6257 } 6258 } 6259 6260 /** 6261 * Set the localized accessible name of this object. 6262 * 6263 * @param s the new localized name of the object. 6264 */ 6265 public void setAccessibleName(String s) { 6266 AccessibleContext ac = getCurrentAccessibleContext(); 6267 if (ac != null) { 6268 ac.setAccessibleName(s); 6269 } else { 6270 super.setAccessibleName(s); 6271 } 6272 } 6273 6274 // 6275 // *** should check tooltip text for desc. (needs MouseEvent) 6276 // 6277 /** 6278 * Get the accessible description of this object. 6279 * 6280 * @return the localized description of the object; null if 6281 * this object does not have a description 6282 */ 6283 public String getAccessibleDescription() { 6284 AccessibleContext ac = getCurrentAccessibleContext(); 6285 if (ac != null) { 6286 return ac.getAccessibleDescription(); 6287 } else { 6288 return super.getAccessibleDescription(); 6289 } 6290 } 6291 6292 /** 6293 * Set the accessible description of this object. 6294 * 6295 * @param s the new localized description of the object 6296 */ 6297 public void setAccessibleDescription(String s) { 6298 AccessibleContext ac = getCurrentAccessibleContext(); 6299 if (ac != null) { 6300 ac.setAccessibleDescription(s); 6301 } else { 6302 super.setAccessibleDescription(s); 6303 } 6304 } 6305 6306 /** 6307 * Get the role of this object. 6308 * 6309 * @return an instance of AccessibleRole describing the role of the object 6310 * @see AccessibleRole 6311 */ 6312 public AccessibleRole getAccessibleRole() { 6313 AccessibleContext ac = getCurrentAccessibleContext(); 6314 if (ac != null) { 6315 return ac.getAccessibleRole(); 6316 } else { 6317 return AccessibleRole.UNKNOWN; 6318 } 6319 } 6320 6321 /** 6322 * Get the state set of this object. 6323 * 6324 * @return an instance of AccessibleStateSet containing the 6325 * current state set of the object 6326 * @see AccessibleState 6327 */ 6328 public AccessibleStateSet getAccessibleStateSet() { 6329 if (tree == null) 6330 return null; 6331 AccessibleContext ac = getCurrentAccessibleContext(); 6332 AccessibleStateSet states; 6333 int row = tree.getUI().getRowForPath(tree,path); 6334 int lsr = tree.getLeadSelectionRow(); 6335 if (ac != null) { 6336 states = ac.getAccessibleStateSet(); 6337 } else { 6338 states = new AccessibleStateSet(); 6339 } 6340 // need to test here, 'cause the underlying component 6341 // is a cellRenderer, which is never showing... 6342 if (isShowing()) { 6343 states.add(AccessibleState.SHOWING); 6344 } else if (states.contains(AccessibleState.SHOWING)) { 6345 states.remove(AccessibleState.SHOWING); 6346 } 6347 if (isVisible()) { 6348 states.add(AccessibleState.VISIBLE); 6349 } else if (states.contains(AccessibleState.VISIBLE)) { 6350 states.remove(AccessibleState.VISIBLE); 6351 } 6352 if (tree.isPathSelected(path)){ 6353 states.add(AccessibleState.SELECTED); 6354 } 6355 if (lsr == row) { 6356 states.add(AccessibleState.ACTIVE); 6357 } 6358 if (!isLeaf) { 6359 states.add(AccessibleState.EXPANDABLE); 6360 } 6361 if (tree.isExpanded(path)) { 6362 states.add(AccessibleState.EXPANDED); 6363 } else { 6364 states.add(AccessibleState.COLLAPSED); 6365 } 6366 if (tree.isEditable()) { 6367 states.add(AccessibleState.EDITABLE); 6368 } 6369 return states; 6370 } 6371 6372 /** 6373 * Get the Accessible parent of this object. 6374 * 6375 * @return the Accessible parent of this object; null if this 6376 * object does not have an Accessible parent 6377 */ 6378 public Accessible getAccessibleParent() { 6379 // someone wants to know, so we need to create our parent 6380 // if we don't have one (hey, we're a talented kid!) 6381 if (accessibleParent == null && path != null) { 6382 Object[] objPath = path.getPath(); 6383 if (objPath.length > 1) { 6384 Object objParent = objPath[objPath.length-2]; 6385 if (treeModel != null) { 6386 index = treeModel.getIndexOfChild(objParent, obj); 6387 } 6388 Object[] objParentPath = new Object[objPath.length-1]; 6389 java.lang.System.arraycopy(objPath, 0, objParentPath, 6390 0, objPath.length-1); 6391 TreePath parentPath = new TreePath(objParentPath); 6392 accessibleParent = new AccessibleJTreeNode(tree, 6393 parentPath, 6394 null); 6395 this.setAccessibleParent(accessibleParent); 6396 } else if (treeModel != null) { 6397 accessibleParent = tree; // we're the top! 6398 index = 0; // we're an only child! 6399 this.setAccessibleParent(accessibleParent); 6400 } 6401 } 6402 return accessibleParent; 6403 } 6404 6405 /** 6406 * Get the index of this object in its accessible parent. 6407 * 6408 * @return the index of this object in its parent; -1 if this 6409 * object does not have an accessible parent. 6410 * @see #getAccessibleParent 6411 */ 6412 public int getAccessibleIndexInParent() { 6413 // index is invalid 'till we have an accessibleParent... 6414 if (accessibleParent == null) { 6415 getAccessibleParent(); 6416 } 6417 if (path != null) { 6418 Object[] objPath = path.getPath(); 6419 if (objPath.length > 1) { 6420 Object objParent = objPath[objPath.length-2]; 6421 if (treeModel != null) { 6422 index = treeModel.getIndexOfChild(objParent, obj); 6423 } 6424 } 6425 } 6426 return index; 6427 } 6428 6429 /** 6430 * Returns the number of accessible children in the object. 6431 * 6432 * @return the number of accessible children in the object. 6433 */ 6434 public int getAccessibleChildrenCount() { 6435 // Tree nodes can't be so complex that they have 6436 // two sets of children -> we're ignoring that case 6437 if (obj != null && treeModel != null) { 6438 return treeModel.getChildCount(obj); 6439 } 6440 return 0; 6441 } 6442 6443 /** 6444 * Return the specified Accessible child of the object. 6445 * 6446 * @param i zero-based index of child 6447 * @return the Accessible child of the object 6448 */ 6449 public Accessible getAccessibleChild(int i) { 6450 // Tree nodes can't be so complex that they have 6451 // two sets of children -> we're ignoring that case 6452 if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) { 6453 return null; 6454 } else { 6455 Object childObj = treeModel.getChild(obj, i); 6456 Object[] objPath = path.getPath(); 6457 Object[] objChildPath = new Object[objPath.length+1]; 6458 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length); 6459 objChildPath[objChildPath.length-1] = childObj; 6460 TreePath childPath = new TreePath(objChildPath); 6461 return new AccessibleJTreeNode(tree, childPath, this); 6462 } 6463 } 6464 6465 /** 6466 * Gets the locale of the component. If the component does not have 6467 * a locale, then the locale of its parent is returned. 6468 * 6469 * @return This component's locale. If this component does not have 6470 * a locale, the locale of its parent is returned. 6471 * @exception IllegalComponentStateException 6472 * If the Component does not have its own locale and has not yet 6473 * been added to a containment hierarchy such that the locale can be 6474 * determined from the containing parent. 6475 * @see #setLocale 6476 */ 6477 public Locale getLocale() { 6478 if (tree == null) 6479 return null; 6480 AccessibleContext ac = getCurrentAccessibleContext(); 6481 if (ac != null) { 6482 return ac.getLocale(); 6483 } else { 6484 return tree.getLocale(); 6485 } 6486 } 6487 6488 /** 6489 * Add a PropertyChangeListener to the listener list. 6490 * The listener is registered for all properties. 6491 * 6492 * @param l The PropertyChangeListener to be added 6493 */ 6494 public void addPropertyChangeListener(PropertyChangeListener l) { 6495 AccessibleContext ac = getCurrentAccessibleContext(); 6496 if (ac != null) { 6497 ac.addPropertyChangeListener(l); 6498 } else { 6499 super.addPropertyChangeListener(l); 6500 } 6501 } 6502 6503 /** 6504 * Remove a PropertyChangeListener from the listener list. 6505 * This removes a PropertyChangeListener that was registered 6506 * for all properties. 6507 * 6508 * @param l The PropertyChangeListener to be removed 6509 */ 6510 public void removePropertyChangeListener(PropertyChangeListener l) { 6511 AccessibleContext ac = getCurrentAccessibleContext(); 6512 if (ac != null) { 6513 ac.removePropertyChangeListener(l); 6514 } else { 6515 super.removePropertyChangeListener(l); 6516 } 6517 } 6518 6519 /** 6520 * Get the AccessibleAction associated with this object. In the 6521 * implementation of the Java Accessibility API for this class, 6522 * return this object, which is responsible for implementing the 6523 * AccessibleAction interface on behalf of itself. 6524 * 6525 * @return this object 6526 */ 6527 public AccessibleAction getAccessibleAction() { 6528 return this; 6529 } 6530 6531 /** 6532 * Get the AccessibleComponent associated with this object. In the 6533 * implementation of the Java Accessibility API for this class, 6534 * return this object, which is responsible for implementing the 6535 * AccessibleComponent interface on behalf of itself. 6536 * 6537 * @return this object 6538 */ 6539 public AccessibleComponent getAccessibleComponent() { 6540 return this; // to override getBounds() 6541 } 6542 6543 /** 6544 * Get the AccessibleSelection associated with this object if one 6545 * exists. Otherwise return null. 6546 * 6547 * @return the AccessibleSelection, or null 6548 */ 6549 public AccessibleSelection getAccessibleSelection() { 6550 AccessibleContext ac = getCurrentAccessibleContext(); 6551 if (ac != null && isLeaf) { 6552 return getCurrentAccessibleContext().getAccessibleSelection(); 6553 } else { 6554 return this; 6555 } 6556 } 6557 6558 /** 6559 * Get the AccessibleText associated with this object if one 6560 * exists. Otherwise return null. 6561 * 6562 * @return the AccessibleText, or null 6563 */ 6564 public AccessibleText getAccessibleText() { 6565 AccessibleContext ac = getCurrentAccessibleContext(); 6566 if (ac != null) { 6567 return getCurrentAccessibleContext().getAccessibleText(); 6568 } else { 6569 return null; 6570 } 6571 } 6572 6573 /** 6574 * Get the AccessibleValue associated with this object if one 6575 * exists. Otherwise return null. 6576 * 6577 * @return the AccessibleValue, or null 6578 */ 6579 public AccessibleValue getAccessibleValue() { 6580 AccessibleContext ac = getCurrentAccessibleContext(); 6581 if (ac != null) { 6582 return getCurrentAccessibleContext().getAccessibleValue(); 6583 } else { 6584 return null; 6585 } 6586 } 6587 6588 6589 // AccessibleComponent methods 6590 6591 /** 6592 * Get the background color of this object. 6593 * 6594 * @return the background color, if supported, of the object; 6595 * otherwise, null 6596 */ 6597 public Color getBackground() { 6598 AccessibleContext ac = getCurrentAccessibleContext(); 6599 if (ac instanceof AccessibleComponent) { 6600 return ((AccessibleComponent) ac).getBackground(); 6601 } else { 6602 Component c = getCurrentComponent(); 6603 if (c != null) { 6604 return c.getBackground(); 6605 } else { 6606 return null; 6607 } 6608 } 6609 } 6610 6611 /** 6612 * Set the background color of this object. 6613 * 6614 * @param c the new Color for the background 6615 */ 6616 public void setBackground(Color c) { 6617 AccessibleContext ac = getCurrentAccessibleContext(); 6618 if (ac instanceof AccessibleComponent) { 6619 ((AccessibleComponent) ac).setBackground(c); 6620 } else { 6621 Component cp = getCurrentComponent(); 6622 if ( cp != null) { 6623 cp.setBackground(c); 6624 } 6625 } 6626 } 6627 6628 6629 /** 6630 * Get the foreground color of this object. 6631 * 6632 * @return the foreground color, if supported, of the object; 6633 * otherwise, null 6634 */ 6635 public Color getForeground() { 6636 AccessibleContext ac = getCurrentAccessibleContext(); 6637 if (ac instanceof AccessibleComponent) { 6638 return ((AccessibleComponent) ac).getForeground(); 6639 } else { 6640 Component c = getCurrentComponent(); 6641 if (c != null) { 6642 return c.getForeground(); 6643 } else { 6644 return null; 6645 } 6646 } 6647 } 6648 6649 public void setForeground(Color c) { 6650 AccessibleContext ac = getCurrentAccessibleContext(); 6651 if (ac instanceof AccessibleComponent) { 6652 ((AccessibleComponent) ac).setForeground(c); 6653 } else { 6654 Component cp = getCurrentComponent(); 6655 if (cp != null) { 6656 cp.setForeground(c); 6657 } 6658 } 6659 } 6660 6661 public Cursor getCursor() { 6662 AccessibleContext ac = getCurrentAccessibleContext(); 6663 if (ac instanceof AccessibleComponent) { 6664 return ((AccessibleComponent) ac).getCursor(); 6665 } else { 6666 Component c = getCurrentComponent(); 6667 if (c != null) { 6668 return c.getCursor(); 6669 } else { 6670 Accessible ap = getAccessibleParent(); 6671 if (ap instanceof AccessibleComponent) { 6672 return ((AccessibleComponent) ap).getCursor(); 6673 } else { 6674 return null; 6675 } 6676 } 6677 } 6678 } 6679 6680 public void setCursor(Cursor c) { 6681 AccessibleContext ac = getCurrentAccessibleContext(); 6682 if (ac instanceof AccessibleComponent) { 6683 ((AccessibleComponent) ac).setCursor(c); 6684 } else { 6685 Component cp = getCurrentComponent(); 6686 if (cp != null) { 6687 cp.setCursor(c); 6688 } 6689 } 6690 } 6691 6692 public Font getFont() { 6693 AccessibleContext ac = getCurrentAccessibleContext(); 6694 if (ac instanceof AccessibleComponent) { 6695 return ((AccessibleComponent) ac).getFont(); 6696 } else { 6697 Component c = getCurrentComponent(); 6698 if (c != null) { 6699 return c.getFont(); 6700 } else { 6701 return null; 6702 } 6703 } 6704 } 6705 6706 public void setFont(Font f) { 6707 AccessibleContext ac = getCurrentAccessibleContext(); 6708 if (ac instanceof AccessibleComponent) { 6709 ((AccessibleComponent) ac).setFont(f); 6710 } else { 6711 Component c = getCurrentComponent(); 6712 if (c != null) { 6713 c.setFont(f); 6714 } 6715 } 6716 } 6717 6718 public FontMetrics getFontMetrics(Font f) { 6719 AccessibleContext ac = getCurrentAccessibleContext(); 6720 if (ac instanceof AccessibleComponent) { 6721 return ((AccessibleComponent) ac).getFontMetrics(f); 6722 } else { 6723 Component c = getCurrentComponent(); 6724 if (c != null) { 6725 return c.getFontMetrics(f); 6726 } else { 6727 return null; 6728 } 6729 } 6730 } 6731 6732 public boolean isEnabled() { 6733 AccessibleContext ac = getCurrentAccessibleContext(); 6734 if (ac instanceof AccessibleComponent) { 6735 return ((AccessibleComponent) ac).isEnabled(); 6736 } else { 6737 Component c = getCurrentComponent(); 6738 if (c != null) { 6739 return c.isEnabled(); 6740 } else { 6741 return false; 6742 } 6743 } 6744 } 6745 6746 public void setEnabled(boolean b) { 6747 AccessibleContext ac = getCurrentAccessibleContext(); 6748 if (ac instanceof AccessibleComponent) { 6749 ((AccessibleComponent) ac).setEnabled(b); 6750 } else { 6751 Component c = getCurrentComponent(); 6752 if (c != null) { 6753 c.setEnabled(b); 6754 } 6755 } 6756 } 6757 6758 public boolean isVisible() { 6759 if (tree == null) 6760 return false; 6761 Rectangle pathBounds = tree.getPathBounds(path); 6762 Rectangle parentBounds = tree.getVisibleRect(); 6763 if ( pathBounds != null && parentBounds != null && 6764 parentBounds.intersects(pathBounds) ) { 6765 return true; 6766 } else { 6767 return false; 6768 } 6769 } 6770 6771 public void setVisible(boolean b) { 6772 } 6773 6774 public boolean isShowing() { 6775 return (tree.isShowing() && isVisible()); 6776 } 6777 6778 public boolean contains(Point p) { 6779 AccessibleContext ac = getCurrentAccessibleContext(); 6780 if (ac instanceof AccessibleComponent) { 6781 Rectangle r = ((AccessibleComponent) ac).getBounds(); 6782 return r.contains(p); 6783 } else { 6784 Component c = getCurrentComponent(); 6785 if (c != null) { 6786 Rectangle r = c.getBounds(); 6787 return r.contains(p); 6788 } else { 6789 return getBounds().contains(p); 6790 } 6791 } 6792 } 6793 6794 public Point getLocationOnScreen() { 6795 if (tree != null) { 6796 Point treeLocation = tree.getLocationOnScreen(); 6797 Rectangle pathBounds = tree.getPathBounds(path); 6798 if (treeLocation != null && pathBounds != null) { 6799 Point nodeLocation = new Point(pathBounds.x, 6800 pathBounds.y); 6801 nodeLocation.translate(treeLocation.x, treeLocation.y); 6802 return nodeLocation; 6803 } else { 6804 return null; 6805 } 6806 } else { 6807 return null; 6808 } 6809 } 6810 6811 private Point getLocationInJTree() { 6812 Rectangle r = tree.getPathBounds(path); 6813 if (r != null) { 6814 return r.getLocation(); 6815 } else { 6816 return null; 6817 } 6818 } 6819 6820 public Point getLocation() { 6821 Rectangle r = getBounds(); 6822 if (r != null) { 6823 return r.getLocation(); 6824 } else { 6825 return null; 6826 } 6827 } 6828 6829 public void setLocation(Point p) { 6830 } 6831 6832 public Rectangle getBounds() { 6833 if (tree == null) 6834 return null; 6835 Rectangle r = tree.getPathBounds(path); 6836 Accessible parent = getAccessibleParent(); 6837 if (parent instanceof AccessibleJTreeNode) { 6838 Point parentLoc = ((AccessibleJTreeNode) parent).getLocationInJTree(); 6839 if (parentLoc != null && r != null) { 6840 r.translate(-parentLoc.x, -parentLoc.y); 6841 } else { 6842 return null; // not visible! 6843 } 6844 } 6845 return r; 6846 } 6847 6848 public void setBounds(Rectangle r) { 6849 AccessibleContext ac = getCurrentAccessibleContext(); 6850 if (ac instanceof AccessibleComponent) { 6851 ((AccessibleComponent) ac).setBounds(r); 6852 } else { 6853 Component c = getCurrentComponent(); 6854 if (c != null) { 6855 c.setBounds(r); 6856 } 6857 } 6858 } 6859 6860 public Dimension getSize() { 6861 return getBounds().getSize(); 6862 } 6863 6864 public void setSize (Dimension d) { 6865 AccessibleContext ac = getCurrentAccessibleContext(); 6866 if (ac instanceof AccessibleComponent) { 6867 ((AccessibleComponent) ac).setSize(d); 6868 } else { 6869 Component c = getCurrentComponent(); 6870 if (c != null) { 6871 c.setSize(d); 6872 } 6873 } 6874 } 6875 6876 /** 6877 * Returns the <code>Accessible</code> child, if one exists, 6878 * contained at the local coordinate <code>Point</code>. 6879 * Otherwise returns <code>null</code>. 6880 * 6881 * @param p point in local coordinates of this 6882 * <code>Accessible</code> 6883 * @return the <code>Accessible</code>, if it exists, 6884 * at the specified location; else <code>null</code> 6885 */ 6886 public Accessible getAccessibleAt(Point p) { 6887 AccessibleContext ac = getCurrentAccessibleContext(); 6888 if (ac instanceof AccessibleComponent) { 6889 return ((AccessibleComponent) ac).getAccessibleAt(p); 6890 } else { 6891 return null; 6892 } 6893 } 6894 6895 public boolean isFocusTraversable() { 6896 AccessibleContext ac = getCurrentAccessibleContext(); 6897 if (ac instanceof AccessibleComponent) { 6898 return ((AccessibleComponent) ac).isFocusTraversable(); 6899 } else { 6900 Component c = getCurrentComponent(); 6901 if (c != null) { 6902 return c.isFocusable(); 6903 } else { 6904 return false; 6905 } 6906 } 6907 } 6908 6909 public void requestFocus() { 6910 AccessibleContext ac = getCurrentAccessibleContext(); 6911 if (ac instanceof AccessibleComponent) { 6912 ((AccessibleComponent) ac).requestFocus(); 6913 } else { 6914 Component c = getCurrentComponent(); 6915 if (c != null) { 6916 c.requestFocus(); 6917 } 6918 } 6919 } 6920 6921 public void addFocusListener(FocusListener l) { 6922 AccessibleContext ac = getCurrentAccessibleContext(); 6923 if (ac instanceof AccessibleComponent) { 6924 ((AccessibleComponent) ac).addFocusListener(l); 6925 } else { 6926 Component c = getCurrentComponent(); 6927 if (c != null) { 6928 c.addFocusListener(l); 6929 } 6930 } 6931 } 6932 6933 public void removeFocusListener(FocusListener l) { 6934 AccessibleContext ac = getCurrentAccessibleContext(); 6935 if (ac instanceof AccessibleComponent) { 6936 ((AccessibleComponent) ac).removeFocusListener(l); 6937 } else { 6938 Component c = getCurrentComponent(); 6939 if (c != null) { 6940 c.removeFocusListener(l); 6941 } 6942 } 6943 } 6944 6945 // AccessibleSelection methods 6946 6947 /** 6948 * Returns the number of items currently selected. 6949 * If no items are selected, the return value will be 0. 6950 * 6951 * @return the number of items currently selected. 6952 */ 6953 public int getAccessibleSelectionCount() { 6954 int count = 0; 6955 int childCount = getAccessibleChildrenCount(); 6956 for (int i = 0; i < childCount; i++) { 6957 TreePath childPath = getChildTreePath(i); 6958 if (tree.isPathSelected(childPath)) { 6959 count++; 6960 } 6961 } 6962 return count; 6963 } 6964 6965 /** 6966 * Returns an Accessible representing the specified selected item 6967 * in the object. If there isn't a selection, or there are 6968 * fewer items selected than the integer passed in, the return 6969 * value will be null. 6970 * 6971 * @param i the zero-based index of selected items 6972 * @return an Accessible containing the selected item 6973 */ 6974 public Accessible getAccessibleSelection(int i) { 6975 int childCount = getAccessibleChildrenCount(); 6976 if (i < 0 || i >= childCount) { 6977 return null; // out of range 6978 } 6979 int count = 0; 6980 for (int j = 0; j < childCount && i >= count; j++) { 6981 TreePath childPath = getChildTreePath(j); 6982 if (tree.isPathSelected(childPath)) { 6983 if (count == i) { 6984 return new AccessibleJTreeNode(tree, childPath, this); 6985 } else { 6986 count++; 6987 } 6988 } 6989 } 6990 return null; 6991 } 6992 6993 /** 6994 * Returns true if the current child of this object is selected. 6995 * 6996 * @param i the zero-based index of the child in this Accessible 6997 * object. 6998 * @see AccessibleContext#getAccessibleChild 6999 */ 7000 public boolean isAccessibleChildSelected(int i) { 7001 int childCount = getAccessibleChildrenCount(); 7002 if (i < 0 || i >= childCount) { 7003 return false; // out of range 7004 } else { 7005 TreePath childPath = getChildTreePath(i); 7006 return tree.isPathSelected(childPath); 7007 } 7008 } 7009 7010 /** 7011 * Adds the specified selected item in the object to the object's 7012 * selection. If the object supports multiple selections, 7013 * the specified item is added to any existing selection, otherwise 7014 * it replaces any existing selection in the object. If the 7015 * specified item is already selected, this method has no effect. 7016 * 7017 * @param i the zero-based index of selectable items 7018 */ 7019 public void addAccessibleSelection(int i) { 7020 if (tree == null) 7021 return; 7022 TreeModel model = tree.getModel(); 7023 if (model != null) { 7024 if (i >= 0 && i < getAccessibleChildrenCount()) { 7025 TreePath path = getChildTreePath(i); 7026 tree.addSelectionPath(path); 7027 } 7028 } 7029 } 7030 7031 /** 7032 * Removes the specified selected item in the object from the 7033 * object's 7034 * selection. If the specified item isn't currently selected, this 7035 * method has no effect. 7036 * 7037 * @param i the zero-based index of selectable items 7038 */ 7039 public void removeAccessibleSelection(int i) { 7040 if (tree == null) 7041 return; 7042 TreeModel model = tree.getModel(); 7043 if (model != null) { 7044 if (i >= 0 && i < getAccessibleChildrenCount()) { 7045 TreePath path = getChildTreePath(i); 7046 tree.removeSelectionPath(path); 7047 } 7048 } 7049 } 7050 7051 /** 7052 * Clears the selection in the object, so that nothing in the 7053 * object is selected. 7054 */ 7055 public void clearAccessibleSelection() { 7056 int childCount = getAccessibleChildrenCount(); 7057 for (int i = 0; i < childCount; i++) { 7058 removeAccessibleSelection(i); 7059 } 7060 } 7061 7062 /** 7063 * Causes every selected item in the object to be selected 7064 * if the object supports multiple selections. 7065 */ 7066 public void selectAllAccessibleSelection() { 7067 if (tree == null) 7068 return; 7069 TreeModel model = tree.getModel(); 7070 if (model != null) { 7071 int childCount = getAccessibleChildrenCount(); 7072 TreePath path; 7073 for (int i = 0; i < childCount; i++) { 7074 path = getChildTreePath(i); 7075 tree.addSelectionPath(path); 7076 } 7077 } 7078 } 7079 7080 // AccessibleAction methods 7081 7082 /** 7083 * Returns the number of accessible actions available in this 7084 * tree node. If this node is not a leaf, there is at least 7085 * one action (toggle expand), in addition to any available 7086 * on the object behind the TreeCellRenderer. 7087 * 7088 * @return the number of Actions in this object 7089 */ 7090 public int getAccessibleActionCount() { 7091 AccessibleContext ac = getCurrentAccessibleContext(); 7092 if (ac != null) { 7093 AccessibleAction aa = ac.getAccessibleAction(); 7094 if (aa != null) { 7095 return (aa.getAccessibleActionCount() + (isLeaf ? 0 : 1)); 7096 } 7097 } 7098 return isLeaf ? 0 : 1; 7099 } 7100 7101 /** 7102 * Return a description of the specified action of the tree node. 7103 * If this node is not a leaf, there is at least one action 7104 * description (toggle expand), in addition to any available 7105 * on the object behind the TreeCellRenderer. 7106 * 7107 * @param i zero-based index of the actions 7108 * @return a description of the action 7109 */ 7110 public String getAccessibleActionDescription(int i) { 7111 if (i < 0 || i >= getAccessibleActionCount()) { 7112 return null; 7113 } 7114 AccessibleContext ac = getCurrentAccessibleContext(); 7115 if (i == 0) { 7116 // TIGER - 4766636 7117 // return AccessibleAction.TOGGLE_EXPAND; 7118 return "toggle expand"; 7119 } else if (ac != null) { 7120 AccessibleAction aa = ac.getAccessibleAction(); 7121 if (aa != null) { 7122 return aa.getAccessibleActionDescription(i - 1); 7123 } 7124 } 7125 return null; 7126 } 7127 7128 /** 7129 * Perform the specified Action on the tree node. If this node 7130 * is not a leaf, there is at least one action which can be 7131 * done (toggle expand), in addition to any available on the 7132 * object behind the TreeCellRenderer. 7133 * 7134 * @param i zero-based index of actions 7135 * @return true if the the action was performed; else false. 7136 */ 7137 public boolean doAccessibleAction(int i) { 7138 if (i < 0 || i >= getAccessibleActionCount()) { 7139 return false; 7140 } 7141 AccessibleContext ac = getCurrentAccessibleContext(); 7142 if (i == 0) { 7143 if (tree.isExpanded(path)) { 7144 tree.collapsePath(path); 7145 } else { 7146 tree.expandPath(path); 7147 } 7148 return true; 7149 } else if (ac != null) { 7150 AccessibleAction aa = ac.getAccessibleAction(); 7151 if (aa != null) { 7152 return aa.doAccessibleAction(i - 1); 7153 } 7154 } 7155 return false; 7156 } 7157 7158 } // inner class AccessibleJTreeNode 7159 7160 /** 7161 * A helper class to perform {@code Callable} objects on the event dispatch thread appropriate 7162 * for the provided {@code AccessibleContext}. 7163 */ 7164 private static class InvocationUtils { 7165 7166 /** 7167 * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible} 7168 * and waits for it to finish blocking the caller thread. 7169 * 7170 * @param callable the {@code Callable} to invoke 7171 * @param accessibleTable the {@code AccessibleExtendedTable} which would be used to find the right context 7172 * for the task execution 7173 * @param <T> type parameter for the result value 7174 * 7175 * @return the result of the {@code Callable} execution 7176 */ 7177 public static <T> T invokeAndWait(final Callable<T> callable, 7178 final AccessibleExtendedTable accessibleTable) { 7179 if (accessibleTable instanceof AccessibleContext) { 7180 return invokeAndWait(callable, (AccessibleContext)accessibleTable); 7181 } 7182 throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleTable); 7183 } 7184 7185 /** 7186 * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible} 7187 * and waits for it to finish blocking the caller thread. 7188 * 7189 * @param callable the {@code Callable} to invoke 7190 * @param accessible the {@code Accessible} which would be used to find the right context 7191 * for the task execution 7192 * @param <T> type parameter for the result value 7193 * 7194 * @return the result of the {@code Callable} execution 7195 */ 7196 public static <T> T invokeAndWait(final Callable<T> callable, 7197 final Accessible accessible) { 7198 if (accessible instanceof Component) { 7199 return invokeAndWait(callable, (Component)accessible); 7200 } 7201 if (accessible instanceof AccessibleContext) { 7202 // This case also covers the Translator 7203 return invokeAndWait(callable, (AccessibleContext)accessible); 7204 } 7205 throw new RuntimeException("Unmapped Accessible used to dispatch event: " + accessible); 7206 } 7207 7208 /** 7209 * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Component} 7210 * and waits for it to finish blocking the caller thread. 7211 * 7212 * @param callable the {@code Callable} to invoke 7213 * @param component the {@code Component} which would be used to find the right context 7214 * for the task execution 7215 * @param <T> type parameter for the result value 7216 * 7217 * @return the result of the {@code Callable} execution 7218 */ 7219 public static <T> T invokeAndWait(final Callable<T> callable, 7220 final Component component) { 7221 return invokeAndWait(callable, SunToolkit.targetToAppContext(component)); 7222 } 7223 7224 /** 7225 * Invokes a {@code Callable} in the {@code AppContext} mapped to the given {@code AccessibleContext} 7226 * and waits for it to finish blocking the caller thread. 7227 * 7228 * @param callable the {@code Callable} to invoke 7229 * @param accessibleContext the {@code AccessibleContext} which would be used to determine the right 7230 * context for the task execution. 7231 * @param <T> type parameter for the result value 7232 * 7233 * @return the result of the {@code Callable} execution 7234 */ 7235 public static <T> T invokeAndWait(final Callable<T> callable, 7236 final AccessibleContext accessibleContext) { 7237 AppContext targetContext = AWTAccessor.getAccessibleContextAccessor() 7238 .getAppContext(accessibleContext); 7239 if (targetContext != null) { 7240 return invokeAndWait(callable, targetContext); 7241 } else { 7242 // Normally this should not happen, unmapped context provided and 7243 // the target AppContext is unknown. 7244 7245 // Try to recover in case the context is a translator. 7246 if (accessibleContext instanceof Translator) { 7247 Object source = ((Translator)accessibleContext).getSource(); 7248 if (source instanceof Component) { 7249 return invokeAndWait(callable, (Component)source); 7250 } 7251 } 7252 } 7253 throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleContext); 7254 } 7255 7256 private static <T> T invokeAndWait(final Callable<T> callable, 7257 final AppContext targetAppContext) { 7258 final CallableWrapper<T> wrapper = new CallableWrapper<T>(callable); 7259 try { 7260 invokeAndWait(wrapper, targetAppContext); 7261 T result = wrapper.getResult(); 7262 updateAppContextMap(result, targetAppContext); 7263 return result; 7264 } catch (final Exception e) { 7265 throw new RuntimeException(e); 7266 } 7267 } 7268 7269 private static void invokeAndWait(final Runnable runnable, 7270 final AppContext appContext) 7271 throws InterruptedException, InvocationTargetException { 7272 7273 EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext); 7274 Object lock = new Object(); 7275 Toolkit source = Toolkit.getDefaultToolkit(); 7276 InvocationEvent event = 7277 new InvocationEvent(source, runnable, lock, true); 7278 synchronized (lock) { 7279 eq.postEvent(event); 7280 lock.wait(); 7281 } 7282 7283 Throwable eventThrowable = event.getThrowable(); 7284 if (eventThrowable != null) { 7285 throw new InvocationTargetException(eventThrowable); 7286 } 7287 } 7288 7289 /** 7290 * Maps the {@code AccessibleContext} to the {@code AppContext} which should be used 7291 * to dispatch events related to the {@code AccessibleContext} 7292 * @param accessibleContext the {@code AccessibleContext} for the mapping 7293 * @param targetContext the {@code AppContext} for the mapping 7294 */ 7295 public static void registerAccessibleContext(final AccessibleContext accessibleContext, 7296 final AppContext targetContext) { 7297 if (accessibleContext != null) { 7298 AWTAccessor.getAccessibleContextAccessor().setAppContext(accessibleContext, targetContext); 7299 } 7300 } 7301 7302 private static <T> void updateAppContextMap(final T accessibleContext, 7303 final AppContext targetContext) { 7304 if (accessibleContext instanceof AccessibleContext) { 7305 registerAccessibleContext((AccessibleContext)accessibleContext, targetContext); 7306 } 7307 } 7308 7309 private static class CallableWrapper<T> implements Runnable { 7310 private final Callable<T> callable; 7311 private volatile T object; 7312 private Exception e; 7313 7314 CallableWrapper(final Callable<T> callable) { 7315 this.callable = callable; 7316 } 7317 7318 public void run() { 7319 try { 7320 if (callable != null) { 7321 object = callable.call(); 7322 } 7323 } catch (final Exception e) { 7324 this.e = e; 7325 } 7326 } 7327 7328 T getResult() throws Exception { 7329 if (e != null) 7330 throw e; 7331 return object; 7332 } 7333 } 7334 } 7335 }