1 /* 2 * Copyright (c) 2011, 2016, 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 sun.lwawt.macosx; 27 28 import sun.lwawt.LWWindowPeer; 29 30 import java.awt.*; 31 import java.beans.*; 32 import java.lang.reflect.Field; 33 import java.lang.reflect.InvocationTargetException; 34 import java.util.*; 35 import java.util.concurrent.Callable; 36 37 import javax.accessibility.*; 38 import javax.swing.*; 39 40 class CAccessibility implements PropertyChangeListener { 41 private static Set<String> ignoredRoles; 42 43 static { 44 // Need to load the native library for this code. 45 java.security.AccessController.doPrivileged( 46 new java.security.PrivilegedAction<Void>() { 47 public Void run() { 48 System.loadLibrary("awt"); 49 return null; 50 } 51 }); 52 } 53 54 static CAccessibility sAccessibility; 55 static synchronized CAccessibility getAccessibility(final String[] roles) { 56 if (sAccessibility != null) return sAccessibility; 57 sAccessibility = new CAccessibility(); 58 59 if (roles != null) { 60 ignoredRoles = new HashSet<String>(roles.length); 61 for (final String role : roles) ignoredRoles.add(role); 62 } else { 63 ignoredRoles = new HashSet<String>(); 64 } 65 66 return sAccessibility; 67 } 68 69 private CAccessibility() { 70 KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("focusOwner", this); 71 } 72 73 public void propertyChange(final PropertyChangeEvent evt) { 74 Object newValue = evt.getNewValue(); 75 if (newValue == null) return; 76 // Don't post focus on things that don't matter, i.e. alert, colorchooser, 77 // desktoppane, dialog, directorypane, filechooser, filler, fontchoose, 78 // frame, glasspane, layeredpane, optionpane, panel, rootpane, separator, 79 // tooltip, viewport, window. 80 // List taken from initializeRoles() in JavaComponentUtilities.m. 81 if (newValue instanceof Accessible) { 82 AccessibleContext nvAC = ((Accessible) newValue).getAccessibleContext(); 83 AccessibleRole nvRole = nvAC.getAccessibleRole(); 84 if (!ignoredRoles.contains(roleKey(nvRole))) { 85 focusChanged(); 86 } 87 } 88 } 89 90 private native void focusChanged(); 91 92 static <T> T invokeAndWait(final Callable<T> callable, final Component c) { 93 try { 94 return LWCToolkit.invokeAndWait(callable, c); 95 } catch (final Exception e) { e.printStackTrace(); } 96 return null; 97 } 98 99 static <T> T invokeAndWait(final Callable<T> callable, final Component c, final T defValue) { 100 T value = null; 101 try { 102 value = LWCToolkit.invokeAndWait(callable, c); 103 } catch (final Exception e) { e.printStackTrace(); } 104 105 return value != null ? value : defValue; 106 } 107 108 static void invokeLater(final Runnable runnable, final Component c) { 109 try { 110 LWCToolkit.invokeLater(runnable, c); 111 } catch (InvocationTargetException e) { e.printStackTrace(); } 112 } 113 114 public static String getAccessibleActionDescription(final AccessibleAction aa, final int index, final Component c) { 115 if (aa == null) return null; 116 117 return invokeAndWait(new Callable<String>() { 118 public String call() throws Exception { 119 return aa.getAccessibleActionDescription(index); 120 } 121 }, c); 122 } 123 124 public static void doAccessibleAction(final AccessibleAction aa, final int index, final Component c) { 125 // We make this an invokeLater because we don't need a reply. 126 if (aa == null) return; 127 128 invokeLater(new Runnable() { 129 public void run() { 130 aa.doAccessibleAction(index); 131 } 132 }, c); 133 } 134 135 public static Dimension getSize(final AccessibleComponent ac, final Component c) { 136 if (ac == null) return null; 137 138 return invokeAndWait(new Callable<Dimension>() { 139 public Dimension call() throws Exception { 140 return ac.getSize(); 141 } 142 }, c); 143 } 144 145 public static AccessibleSelection getAccessibleSelection(final AccessibleContext ac, final Component c) { 146 if (ac == null) return null; 147 148 return invokeAndWait(new Callable<AccessibleSelection>() { 149 public AccessibleSelection call() throws Exception { 150 return ac.getAccessibleSelection(); 151 } 152 }, c); 153 } 154 155 public static Accessible ax_getAccessibleSelection(final AccessibleContext ac, final int index, final Component c) { 156 if (ac == null) return null; 157 158 return invokeAndWait(new Callable<Accessible>() { 159 public Accessible call() throws Exception { 160 final AccessibleSelection as = ac.getAccessibleSelection(); 161 if (as == null) return null; 162 return as.getAccessibleSelection(index); 163 } 164 }, c); 165 } 166 167 // KCH - can we make this a postEvent? 168 public static void addAccessibleSelection(final AccessibleContext ac, final int index, final Component c) { 169 if (ac == null) return; 170 171 invokeLater(new Runnable() { 172 public void run() { 173 final AccessibleSelection as = ac.getAccessibleSelection(); 174 if (as == null) return; 175 as.addAccessibleSelection(index); 176 } 177 }, c); 178 } 179 180 public static AccessibleContext getAccessibleContext(final Accessible a, final Component c) { 181 if (a == null) return null; 182 183 return invokeAndWait(new Callable<AccessibleContext>() { 184 public AccessibleContext call() throws Exception { 185 return a.getAccessibleContext(); 186 } 187 }, c); 188 } 189 190 public static boolean isAccessibleChildSelected(final Accessible a, final int index, final Component c) { 191 if (a == null) return false; 192 193 return invokeAndWait(new Callable<Boolean>() { 194 public Boolean call() throws Exception { 195 final AccessibleContext ac = a.getAccessibleContext(); 196 if (ac == null) return Boolean.FALSE; 197 198 final AccessibleSelection as = ac.getAccessibleSelection(); 199 if (as == null) return Boolean.FALSE; 200 201 return new Boolean(as.isAccessibleChildSelected(index)); 202 } 203 }, c, false); 204 } 205 206 public static AccessibleStateSet getAccessibleStateSet(final AccessibleContext ac, final Component c) { 207 if (ac == null) return null; 208 209 return invokeAndWait(new Callable<AccessibleStateSet>() { 210 public AccessibleStateSet call() throws Exception { 211 return ac.getAccessibleStateSet(); 212 } 213 }, c); 214 } 215 216 public static boolean contains(final AccessibleContext ac, final AccessibleState as, final Component c) { 217 if (ac == null || as == null) return false; 218 219 return invokeAndWait(new Callable<Boolean>() { 220 public Boolean call() throws Exception { 221 final AccessibleStateSet ass = ac.getAccessibleStateSet(); 222 if (ass == null) return null; 223 return ass.contains(as); 224 } 225 }, c, false); 226 } 227 228 static Field getAccessibleBundleKeyFieldWithReflection() { 229 try { 230 final Field fieldKey = AccessibleBundle.class.getDeclaredField("key"); 231 fieldKey.setAccessible(true); 232 return fieldKey; 233 } catch (final SecurityException e) { 234 e.printStackTrace(); 235 } catch (final NoSuchFieldException e) { 236 e.printStackTrace(); 237 } 238 return null; 239 } 240 private static final Field FIELD_KEY = getAccessibleBundleKeyFieldWithReflection(); 241 242 static String getAccessibleRoleFor(final Accessible a) { 243 final AccessibleContext ac = a.getAccessibleContext(); 244 if (ac == null) return null; 245 246 final AccessibleRole role = ac.getAccessibleRole(); 247 try { 248 return (String)FIELD_KEY.get(role); 249 } catch (final IllegalArgumentException e) { 250 e.printStackTrace(); 251 } catch (final IllegalAccessException e) { 252 e.printStackTrace(); 253 } 254 return null; 255 } 256 257 public static String getAccessibleRole(final Accessible a, final Component c) { 258 if (a == null) return null; 259 260 return invokeAndWait(new Callable<String>() { 261 public String call() throws Exception { 262 final Accessible sa = CAccessible.getSwingAccessible(a); 263 final String role = getAccessibleRoleFor(a); 264 265 if (!"text".equals(role)) return role; 266 if (sa instanceof JTextArea || sa instanceof JEditorPane) { 267 return "textarea"; 268 } 269 return role; 270 } 271 }, c); 272 } 273 274 public static Point getLocationOnScreen(final AccessibleComponent ac, final Component c) { 275 if (ac == null) return null; 276 277 return invokeAndWait(new Callable<Point>() { 278 public Point call() throws Exception { 279 return ac.getLocationOnScreen(); 280 } 281 }, c); 282 } 283 284 public static int getCharCount(final AccessibleText at, final Component c) { 285 if (at == null) return 0; 286 287 return invokeAndWait(new Callable<Integer>() { 288 public Integer call() throws Exception { 289 return at.getCharCount(); 290 } 291 }, c, 0); 292 } 293 294 // Accessibility Threadsafety for JavaComponentAccessibility.m 295 public static Accessible getAccessibleParent(final Accessible a, final Component c) { 296 if (a == null) return null; 297 298 return invokeAndWait(new Callable<Accessible>() { 299 public Accessible call() throws Exception { 300 final AccessibleContext ac = a.getAccessibleContext(); 301 if (ac == null) return null; 302 return ac.getAccessibleParent(); 303 } 304 }, c); 305 } 306 307 public static int getAccessibleIndexInParent(final Accessible a, final Component c) { 308 if (a == null) return -1; 309 310 return invokeAndWait(new Callable<Integer>() { 311 public Integer call() throws Exception { 312 final AccessibleContext ac = a.getAccessibleContext(); 313 if (ac == null) return null; 314 return ac.getAccessibleIndexInParent(); 315 } 316 }, c, -1); 317 } 318 319 public static AccessibleComponent getAccessibleComponent(final Accessible a, final Component c) { 320 if (a == null) return null; 321 322 return invokeAndWait(new Callable<AccessibleComponent>() { 323 public AccessibleComponent call() throws Exception { 324 final AccessibleContext ac = a.getAccessibleContext(); 325 if (ac == null) return null; 326 return ac.getAccessibleComponent(); 327 } 328 }, c); 329 } 330 331 public static AccessibleValue getAccessibleValue(final Accessible a, final Component c) { 332 if (a == null) return null; 333 334 return invokeAndWait(new Callable<AccessibleValue>() { 335 public AccessibleValue call() throws Exception { 336 final AccessibleContext ac = a.getAccessibleContext(); 337 if (ac == null) return null; 338 339 AccessibleValue accessibleValue = ac.getAccessibleValue(); 340 return accessibleValue; 341 } 342 }, c); 343 } 344 345 public static String getAccessibleName(final Accessible a, final Component c) { 346 if (a == null) return null; 347 348 return invokeAndWait(new Callable<String>() { 349 public String call() throws Exception { 350 final AccessibleContext ac = a.getAccessibleContext(); 351 if (ac == null) return null; 352 353 final String accessibleName = ac.getAccessibleName(); 354 if (accessibleName == null) { 355 return ac.getAccessibleDescription(); 356 } 357 return accessibleName; 358 } 359 }, c); 360 } 361 362 public static AccessibleText getAccessibleText(final Accessible a, final Component c) { 363 if (a == null) return null; 364 365 return invokeAndWait(new Callable<AccessibleText>() { 366 public AccessibleText call() throws Exception { 367 final AccessibleContext ac = a.getAccessibleContext(); 368 if (ac == null) return null; 369 370 AccessibleText accessibleText = ac.getAccessibleText(); 371 return accessibleText; 372 } 373 }, c); 374 } 375 376 public static String getAccessibleDescription(final Accessible a, final Component c) { 377 if (a == null) return null; 378 379 return invokeAndWait(new Callable<String>() { 380 public String call() throws Exception { 381 final AccessibleContext ac = a.getAccessibleContext(); 382 if (ac == null) return null; 383 384 final String accessibleDescription = ac.getAccessibleDescription(); 385 if (accessibleDescription == null) { 386 if (c instanceof JComponent) { 387 String toolTipText = ((JComponent)c).getToolTipText(); 388 if (toolTipText != null) { 389 return toolTipText; 390 } 391 } 392 } 393 394 return accessibleDescription; 395 } 396 }, c); 397 } 398 399 public static boolean isFocusTraversable(final Accessible a, final Component c) { 400 if (a == null) return false; 401 402 return invokeAndWait(new Callable<Boolean>() { 403 public Boolean call() throws Exception { 404 final AccessibleContext ac = a.getAccessibleContext(); 405 if (ac == null) return null; 406 407 final AccessibleComponent aComp = ac.getAccessibleComponent(); 408 if (aComp == null) return null; 409 410 return aComp.isFocusTraversable(); 411 } 412 }, c, false); 413 } 414 415 public static Accessible accessibilityHitTest(final Container parent, final float hitPointX, final float hitPointY) { 416 return invokeAndWait(new Callable<Accessible>() { 417 public Accessible call() throws Exception { 418 final Point p = parent.getLocationOnScreen(); 419 420 // Make it into local coords 421 final Point localPoint = new Point((int)(hitPointX - p.getX()), (int)(hitPointY - p.getY())); 422 423 final Component component = parent.findComponentAt(localPoint); 424 if (component == null) return null; 425 426 final AccessibleContext axContext = component.getAccessibleContext(); 427 if (axContext == null) return null; 428 429 final AccessibleComponent axComponent = axContext.getAccessibleComponent(); 430 if (axComponent == null) return null; 431 432 final int numChildren = axContext.getAccessibleChildrenCount(); 433 if (numChildren > 0) { 434 // It has children, check to see which one is hit. 435 final Point p2 = axComponent.getLocationOnScreen(); 436 final Point localP2 = new Point((int)(hitPointX - p2.getX()), (int)(hitPointY - p2.getY())); 437 return CAccessible.getCAccessible(axComponent.getAccessibleAt(localP2)); 438 } 439 440 if (!(component instanceof Accessible)) return null; 441 return CAccessible.getCAccessible((Accessible)component); 442 } 443 }, parent); 444 } 445 446 public static AccessibleAction getAccessibleAction(final Accessible a, final Component c) { 447 if (a == null) return null; 448 449 return invokeAndWait(new Callable<AccessibleAction>() { 450 public AccessibleAction call() throws Exception { 451 final AccessibleContext ac = a.getAccessibleContext(); 452 if (ac == null) return null; 453 return ac.getAccessibleAction(); 454 } 455 }, c); 456 } 457 458 public static boolean isEnabled(final Accessible a, final Component c) { 459 if (a == null) return false; 460 461 return invokeAndWait(new Callable<Boolean>() { 462 public Boolean call() throws Exception { 463 final AccessibleContext ac = a.getAccessibleContext(); 464 if (ac == null) return null; 465 466 final AccessibleComponent aComp = ac.getAccessibleComponent(); 467 if (aComp == null) return null; 468 469 return aComp.isEnabled(); 470 } 471 }, c, false); 472 } 473 474 // KCH - can we make this a postEvent instead? 475 public static void requestFocus(final Accessible a, final Component c) { 476 if (a == null) return; 477 478 invokeLater(new Runnable() { 479 public void run() { 480 final AccessibleContext ac = a.getAccessibleContext(); 481 if (ac == null) return; 482 483 final AccessibleComponent aComp = ac.getAccessibleComponent(); 484 if (aComp == null) return; 485 486 aComp.requestFocus(); 487 } 488 }, c); 489 } 490 491 public static void requestSelection(final Accessible a, final Component c) { 492 if (a == null) return; 493 invokeLater(new Runnable() { 494 public void run() { 495 AccessibleContext ac = a.getAccessibleContext(); 496 if (ac == null) return; 497 int i = ac.getAccessibleIndexInParent(); 498 if (i == -1) return; 499 Accessible parent = ac.getAccessibleParent(); 500 AccessibleContext pac = parent.getAccessibleContext(); 501 if (pac == null) return; 502 AccessibleSelection as = pac.getAccessibleSelection(); 503 if (as == null) return; 504 as.addAccessibleSelection(i); 505 } 506 }, c); 507 } 508 509 public static Number getMaximumAccessibleValue(final Accessible a, final Component c) { 510 if (a == null) return null; 511 512 return invokeAndWait(new Callable<Number>() { 513 public Number call() throws Exception { 514 final AccessibleContext ac = a.getAccessibleContext(); 515 if (ac == null) return null; 516 517 final AccessibleValue av = ac.getAccessibleValue(); 518 if (av == null) return null; 519 520 return av.getMaximumAccessibleValue(); 521 } 522 }, c); 523 } 524 525 public static Number getMinimumAccessibleValue(final Accessible a, final Component c) { 526 if (a == null) return null; 527 528 return invokeAndWait(new Callable<Number>() { 529 public Number call() throws Exception { 530 final AccessibleContext ac = a.getAccessibleContext(); 531 if (ac == null) return null; 532 533 final AccessibleValue av = ac.getAccessibleValue(); 534 if (av == null) return null; 535 536 return av.getMinimumAccessibleValue(); 537 } 538 }, c); 539 } 540 541 public static String getAccessibleRoleDisplayString(final Accessible a, final Component c) { 542 if (a == null) return null; 543 544 return invokeAndWait(new Callable<String>() { 545 public String call() throws Exception { 546 final AccessibleContext ac = a.getAccessibleContext(); 547 if (ac == null) return null; 548 549 final AccessibleRole ar = ac.getAccessibleRole(); 550 if (ar == null) return null; 551 552 return ar.toDisplayString(); 553 } 554 }, c); 555 } 556 557 public static Number getCurrentAccessibleValue(final AccessibleValue av, final Component c) { 558 if (av == null) return null; 559 560 return invokeAndWait(new Callable<Number>() { 561 public Number call() throws Exception { 562 Number currentAccessibleValue = av.getCurrentAccessibleValue(); 563 return currentAccessibleValue; 564 } 565 }, c); 566 } 567 568 public static Accessible getFocusOwner(final Component c) { 569 return invokeAndWait(new Callable<Accessible>() { 570 public Accessible call() throws Exception { 571 Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); 572 if (c == null || !(c instanceof Accessible)) return null; 573 return CAccessible.getCAccessible((Accessible)c); 574 } 575 }, c); 576 } 577 578 public static boolean[] getInitialAttributeStates(final Accessible a, final Component c) { 579 final boolean[] ret = new boolean[7]; 580 if (a == null) return ret; 581 582 return invokeAndWait(new Callable<boolean[]>() { 583 public boolean[] call() throws Exception { 584 final AccessibleContext aContext = a.getAccessibleContext(); 585 if (aContext == null) return ret; 586 587 final AccessibleComponent aComponent = aContext.getAccessibleComponent(); 588 ret[0] = (aComponent != null); 589 ret[1] = ((aComponent != null) && (aComponent.isFocusTraversable())); 590 ret[2] = (aContext.getAccessibleValue() != null); 591 ret[3] = (aContext.getAccessibleText() != null); 592 593 final AccessibleStateSet aStateSet = aContext.getAccessibleStateSet(); 594 ret[4] = (aStateSet.contains(AccessibleState.HORIZONTAL) || aStateSet.contains(AccessibleState.VERTICAL)); 595 ret[5] = (aContext.getAccessibleName() != null); 596 ret[6] = (aContext.getAccessibleChildrenCount() > 0); 597 return ret; 598 } 599 }, c); 600 } 601 602 // Duplicated from JavaComponentAccessibility 603 // Note that values >=0 are indexes into the child array 604 final static int JAVA_AX_ALL_CHILDREN = -1; 605 final static int JAVA_AX_SELECTED_CHILDREN = -2; 606 final static int JAVA_AX_VISIBLE_CHILDREN = -3; 607 608 // Each child takes up two entries in the array: one for itself and one for its role 609 public static Object[] getChildrenAndRoles(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) { 610 if (a == null) return null; 611 return invokeAndWait(new Callable<Object[]>() { 612 public Object[] call() throws Exception { 613 ArrayList<Object> childrenAndRoles = new ArrayList<Object>(); 614 _addChildren(a, whichChildren, allowIgnored, childrenAndRoles); 615 616 /* In the case of fetching a selection, need to check to see if 617 * the active descendant is at the beginning of the list. If it 618 * is not it needs to be moved to the beginning of the list so 619 * VoiceOver will annouce it correctly. The list returned 620 * from Java is always in order from top to bottom, but when shift 621 * selecting downward (extending the list) or multi-selecting using 622 * the VO keys control+option+command+return the active descendant 623 * is not at the top of the list in the shift select down case and 624 * may not be in the multi select case. 625 */ 626 if (whichChildren == JAVA_AX_SELECTED_CHILDREN) { 627 if (!childrenAndRoles.isEmpty()) { 628 AccessibleContext activeDescendantAC = 629 CAccessible.getActiveDescendant(a); 630 if (activeDescendantAC != null) { 631 String activeDescendantName = 632 activeDescendantAC.getAccessibleName(); 633 AccessibleRole activeDescendantRole = 634 activeDescendantAC.getAccessibleRole(); 635 // Move active descendant to front of list. 636 // List contains pairs of each selected item's 637 // Accessible and AccessibleRole. 638 ArrayList<Object> newArray = new ArrayList<Object>(); 639 int count = childrenAndRoles.size(); 640 Accessible currentAccessible = null; 641 AccessibleContext currentAC = null; 642 String currentName = null; 643 AccessibleRole currentRole = null; 644 for (int i = 0; i < count; i+=2) { 645 // Is this the active descendant? 646 currentAccessible = (Accessible)childrenAndRoles.get(i); 647 currentAC = currentAccessible.getAccessibleContext(); 648 currentName = currentAC.getAccessibleName(); 649 currentRole = (AccessibleRole)childrenAndRoles.get(i+1); 650 if ( currentName.equals(activeDescendantName) && 651 currentRole.equals(activeDescendantRole) ) { 652 newArray.add(0, currentAccessible); 653 newArray.add(1, currentRole); 654 } else { 655 newArray.add(currentAccessible); 656 newArray.add(currentRole); 657 } 658 } 659 childrenAndRoles = newArray; 660 } 661 } 662 } 663 664 if ((whichChildren < 0) || (whichChildren * 2 >= childrenAndRoles.size())) { 665 return childrenAndRoles.toArray(); 666 } 667 668 return new Object[] { childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1) }; 669 } 670 }, c); 671 } 672 673 private static AccessibleRole getAccessibleRoleForLabel(JLabel l, AccessibleRole fallback) { 674 String text = l.getText(); 675 if (text != null && text.length() > 0) { 676 return fallback; 677 } 678 Icon icon = l.getIcon(); 679 if (icon != null) { 680 return AccessibleRole.ICON; 681 } 682 return fallback; 683 } 684 685 private static AccessibleRole getAccessibleRole(Accessible a) { 686 AccessibleContext ac = a.getAccessibleContext(); 687 AccessibleRole role = ac.getAccessibleRole(); 688 Object component = CAccessible.getSwingAccessible(a); 689 if (role == null) return null; 690 String roleString = role.toString(); 691 if ("label".equals(roleString) && component instanceof JLabel) { 692 return getAccessibleRoleForLabel((JLabel) component, role); 693 } 694 return role; 695 } 696 697 698 // Either gets the immediate children of a, or recursively gets all unignored children of a 699 private static void _addChildren(final Accessible a, final int whichChildren, final boolean allowIgnored, final ArrayList<Object> childrenAndRoles) { 700 if (a == null) return; 701 702 final AccessibleContext ac = a.getAccessibleContext(); 703 if (ac == null) return; 704 705 final int numChildren = ac.getAccessibleChildrenCount(); 706 707 // each child takes up two entries in the array: itself, and its role 708 // so the array holds alternating Accessible and AccessibleRole objects 709 for (int i = 0; i < numChildren; i++) { 710 final Accessible child = ac.getAccessibleChild(i); 711 if (child == null) continue; 712 713 final AccessibleContext context = child.getAccessibleContext(); 714 if (context == null) continue; 715 716 if (whichChildren == JAVA_AX_VISIBLE_CHILDREN) { 717 AccessibleComponent acomp = context.getAccessibleComponent(); 718 if (acomp == null || !acomp.isVisible()) { 719 continue; 720 } 721 } else if (whichChildren == JAVA_AX_SELECTED_CHILDREN) { 722 AccessibleSelection sel = ac.getAccessibleSelection(); 723 if (sel == null || !sel.isAccessibleChildSelected(i)) { 724 continue; 725 } 726 } 727 728 if (!allowIgnored) { 729 final AccessibleRole role = context.getAccessibleRole(); 730 if (role != null && ignoredRoles != null && ignoredRoles.contains(roleKey(role))) { 731 // Get the child's unignored children. 732 _addChildren(child, whichChildren, false, childrenAndRoles); 733 } else { 734 childrenAndRoles.add(child); 735 childrenAndRoles.add(getAccessibleRole(child)); 736 } 737 } else { 738 childrenAndRoles.add(child); 739 childrenAndRoles.add(getAccessibleRole(child)); 740 } 741 742 // If there is an index, and we are beyond it, time to finish up 743 if ((whichChildren >= 0) && (childrenAndRoles.size() / 2) >= (whichChildren + 1)) { 744 return; 745 } 746 } 747 } 748 749 private static native String roleKey(AccessibleRole aRole); 750 751 public static Object[] getChildren(final Accessible a, final Component c) { 752 if (a == null) return null; 753 return invokeAndWait(new Callable<Object[]>() { 754 public Object[] call() throws Exception { 755 final AccessibleContext ac = a.getAccessibleContext(); 756 if (ac == null) return null; 757 758 final int numChildren = ac.getAccessibleChildrenCount(); 759 final Object[] children = new Object[numChildren]; 760 for (int i = 0; i < numChildren; i++) { 761 children[i] = ac.getAccessibleChild(i); 762 } 763 return children; 764 } 765 }, c); 766 } 767 768 /** 769 * @return AWTView ptr, a peer of the CPlatformView associated with the toplevel container of the Accessible, if any 770 */ 771 private static long getAWTView(Accessible a) { 772 Accessible ax = CAccessible.getSwingAccessible(a); 773 if (!(ax instanceof Component)) return 0; 774 775 return invokeAndWait(new Callable<Long>() { 776 public Long call() throws Exception { 777 Component cont = (Component) ax; 778 while (cont != null && !(cont instanceof Window)) { 779 cont = cont.getParent(); 780 } 781 if (cont != null) { 782 LWWindowPeer peer = (LWWindowPeer) cont.getPeer(); 783 if (peer != null) { 784 return ((CPlatformWindow) peer.getPlatformWindow()).getContentView().getAWTView(); 785 } 786 } 787 return 0L; 788 } 789 }, (Component)ax); 790 } 791 }