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