src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java
changeset 47216 71c04702a3d5
parent 41780 9deca564da25
child 52260 1cfc72a40bb8
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     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.InvocationTargetException;
       
    33 import java.util.*;
       
    34 import java.util.concurrent.Callable;
       
    35 
       
    36 import javax.accessibility.*;
       
    37 import javax.swing.*;
       
    38 import sun.awt.AWTAccessor;
       
    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 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 String getAccessibleRoleFor(final Accessible a) {
       
   229         final AccessibleContext ac = a.getAccessibleContext();
       
   230         if (ac == null) return null;
       
   231 
       
   232         final AccessibleRole role = ac.getAccessibleRole();
       
   233         return AWTAccessor.getAccessibleBundleAccessor().getKey(role);
       
   234     }
       
   235 
       
   236     public static String getAccessibleRole(final Accessible a, final Component c) {
       
   237         if (a == null) return null;
       
   238 
       
   239         return invokeAndWait(new Callable<String>() {
       
   240             public String call() throws Exception {
       
   241                 final Accessible sa = CAccessible.getSwingAccessible(a);
       
   242                 final String role = getAccessibleRoleFor(a);
       
   243 
       
   244                 if (!"text".equals(role)) return role;
       
   245                 if (sa instanceof JTextArea || sa instanceof JEditorPane) {
       
   246                     return "textarea";
       
   247                 }
       
   248                 return role;
       
   249             }
       
   250         }, c);
       
   251     }
       
   252 
       
   253     public static Point getLocationOnScreen(final AccessibleComponent ac, final Component c) {
       
   254         if (ac == null) return null;
       
   255 
       
   256         return invokeAndWait(new Callable<Point>() {
       
   257             public Point call() throws Exception {
       
   258                 return ac.getLocationOnScreen();
       
   259             }
       
   260         }, c);
       
   261     }
       
   262 
       
   263     public static int getCharCount(final AccessibleText at, final Component c) {
       
   264         if (at == null) return 0;
       
   265 
       
   266         return invokeAndWait(new Callable<Integer>() {
       
   267             public Integer call() throws Exception {
       
   268                 return at.getCharCount();
       
   269             }
       
   270         }, c, 0);
       
   271     }
       
   272 
       
   273     // Accessibility Threadsafety for JavaComponentAccessibility.m
       
   274     public static Accessible getAccessibleParent(final Accessible a, final Component c) {
       
   275         if (a == null) return null;
       
   276 
       
   277         return invokeAndWait(new Callable<Accessible>() {
       
   278             public Accessible call() throws Exception {
       
   279                 final AccessibleContext ac = a.getAccessibleContext();
       
   280                 if (ac == null) return null;
       
   281                 return ac.getAccessibleParent();
       
   282             }
       
   283         }, c);
       
   284     }
       
   285 
       
   286     public static int getAccessibleIndexInParent(final Accessible a, final Component c) {
       
   287         if (a == null) return -1;
       
   288 
       
   289         return invokeAndWait(new Callable<Integer>() {
       
   290             public Integer call() throws Exception {
       
   291                 final AccessibleContext ac = a.getAccessibleContext();
       
   292                 if (ac == null) return null;
       
   293                 return ac.getAccessibleIndexInParent();
       
   294             }
       
   295         }, c, -1);
       
   296     }
       
   297 
       
   298     public static AccessibleComponent getAccessibleComponent(final Accessible a, final Component c) {
       
   299         if (a == null) return null;
       
   300 
       
   301         return invokeAndWait(new Callable<AccessibleComponent>() {
       
   302             public AccessibleComponent call() throws Exception {
       
   303                 final AccessibleContext ac = a.getAccessibleContext();
       
   304                 if (ac == null) return null;
       
   305                 return ac.getAccessibleComponent();
       
   306             }
       
   307         }, c);
       
   308     }
       
   309 
       
   310     public static AccessibleValue getAccessibleValue(final Accessible a, final Component c) {
       
   311         if (a == null) return null;
       
   312 
       
   313         return invokeAndWait(new Callable<AccessibleValue>() {
       
   314             public AccessibleValue call() throws Exception {
       
   315                 final AccessibleContext ac = a.getAccessibleContext();
       
   316                 if (ac == null) return null;
       
   317 
       
   318                 AccessibleValue accessibleValue = ac.getAccessibleValue();
       
   319                 return accessibleValue;
       
   320             }
       
   321         }, c);
       
   322     }
       
   323 
       
   324     public static String getAccessibleName(final Accessible a, final Component c) {
       
   325         if (a == null) return null;
       
   326 
       
   327         return invokeAndWait(new Callable<String>() {
       
   328             public String call() throws Exception {
       
   329                 final AccessibleContext ac = a.getAccessibleContext();
       
   330                 if (ac == null) return null;
       
   331 
       
   332                 final String accessibleName = ac.getAccessibleName();
       
   333                 if (accessibleName == null) {
       
   334                     return ac.getAccessibleDescription();
       
   335                 }
       
   336                 return accessibleName;
       
   337             }
       
   338         }, c);
       
   339     }
       
   340 
       
   341     public static AccessibleText getAccessibleText(final Accessible a, final Component c) {
       
   342         if (a == null) return null;
       
   343 
       
   344         return invokeAndWait(new Callable<AccessibleText>() {
       
   345             public AccessibleText call() throws Exception {
       
   346                 final AccessibleContext ac = a.getAccessibleContext();
       
   347                 if (ac == null) return null;
       
   348 
       
   349                 AccessibleText accessibleText = ac.getAccessibleText();
       
   350                 return accessibleText;
       
   351             }
       
   352         }, c);
       
   353     }
       
   354 
       
   355     public static String getAccessibleDescription(final Accessible a, final Component c) {
       
   356         if (a == null) return null;
       
   357 
       
   358         return invokeAndWait(new Callable<String>() {
       
   359             public String call() throws Exception {
       
   360                 final AccessibleContext ac = a.getAccessibleContext();
       
   361                 if (ac == null) return null;
       
   362 
       
   363                 final String accessibleDescription = ac.getAccessibleDescription();
       
   364                 if (accessibleDescription == null) {
       
   365                     if (c instanceof JComponent) {
       
   366                         String toolTipText = ((JComponent)c).getToolTipText();
       
   367                         if (toolTipText != null) {
       
   368                             return toolTipText;
       
   369                         }
       
   370                     }
       
   371                 }
       
   372 
       
   373                 return accessibleDescription;
       
   374             }
       
   375         }, c);
       
   376     }
       
   377 
       
   378     public static boolean isFocusTraversable(final Accessible a, final Component c) {
       
   379         if (a == null) return false;
       
   380 
       
   381         return invokeAndWait(new Callable<Boolean>() {
       
   382             public Boolean call() throws Exception {
       
   383                 final AccessibleContext ac = a.getAccessibleContext();
       
   384                 if (ac == null) return null;
       
   385 
       
   386                 final AccessibleComponent aComp = ac.getAccessibleComponent();
       
   387                 if (aComp == null) return null;
       
   388 
       
   389                 return aComp.isFocusTraversable();
       
   390             }
       
   391         }, c, false);
       
   392     }
       
   393 
       
   394     public static Accessible accessibilityHitTest(final Container parent, final float hitPointX, final float hitPointY) {
       
   395         return invokeAndWait(new Callable<Accessible>() {
       
   396             public Accessible call() throws Exception {
       
   397                 final Point p = parent.getLocationOnScreen();
       
   398 
       
   399                 // Make it into local coords
       
   400                 final Point localPoint = new Point((int)(hitPointX - p.getX()), (int)(hitPointY - p.getY()));
       
   401 
       
   402                 final Component component = parent.findComponentAt(localPoint);
       
   403                 if (component == null) return null;
       
   404 
       
   405                 final AccessibleContext axContext = component.getAccessibleContext();
       
   406                 if (axContext == null) return null;
       
   407 
       
   408                 final AccessibleComponent axComponent = axContext.getAccessibleComponent();
       
   409                 if (axComponent == null) return null;
       
   410 
       
   411                 final int numChildren = axContext.getAccessibleChildrenCount();
       
   412                 if (numChildren > 0) {
       
   413                     // It has children, check to see which one is hit.
       
   414                     final Point p2 = axComponent.getLocationOnScreen();
       
   415                     final Point localP2 = new Point((int)(hitPointX - p2.getX()), (int)(hitPointY - p2.getY()));
       
   416                     return CAccessible.getCAccessible(axComponent.getAccessibleAt(localP2));
       
   417                 }
       
   418 
       
   419                 if (!(component instanceof Accessible)) return null;
       
   420                 return CAccessible.getCAccessible((Accessible)component);
       
   421             }
       
   422         }, parent);
       
   423     }
       
   424 
       
   425     public static AccessibleAction getAccessibleAction(final Accessible a, final Component c) {
       
   426         if (a == null) return null;
       
   427 
       
   428         return invokeAndWait(new Callable<AccessibleAction>() {
       
   429             public AccessibleAction call() throws Exception {
       
   430                 final AccessibleContext ac = a.getAccessibleContext();
       
   431                 if (ac == null) return null;
       
   432                 return ac.getAccessibleAction();
       
   433             }
       
   434         }, c);
       
   435     }
       
   436 
       
   437     public static boolean isEnabled(final Accessible a, final Component c) {
       
   438         if (a == null) return false;
       
   439 
       
   440         return invokeAndWait(new Callable<Boolean>() {
       
   441             public Boolean call() throws Exception {
       
   442                 final AccessibleContext ac = a.getAccessibleContext();
       
   443                 if (ac == null) return null;
       
   444 
       
   445                 final AccessibleComponent aComp = ac.getAccessibleComponent();
       
   446                 if (aComp == null) return null;
       
   447 
       
   448                 return aComp.isEnabled();
       
   449             }
       
   450         }, c, false);
       
   451     }
       
   452 
       
   453     // KCH - can we make this a postEvent instead?
       
   454     public static void requestFocus(final Accessible a, final Component c) {
       
   455         if (a == null) return;
       
   456 
       
   457         invokeLater(new Runnable() {
       
   458             public void run() {
       
   459                 final AccessibleContext ac = a.getAccessibleContext();
       
   460                 if (ac == null) return;
       
   461 
       
   462                 final AccessibleComponent aComp = ac.getAccessibleComponent();
       
   463                 if (aComp == null) return;
       
   464 
       
   465                 aComp.requestFocus();
       
   466             }
       
   467         }, c);
       
   468     }
       
   469 
       
   470     public static void requestSelection(final Accessible a, final Component c) {
       
   471         if (a == null) return;
       
   472         invokeLater(new Runnable() {
       
   473             public void run() {
       
   474                 AccessibleContext ac = a.getAccessibleContext();
       
   475                 if (ac == null) return;
       
   476                 int i = ac.getAccessibleIndexInParent();
       
   477                 if (i == -1) return;
       
   478                 Accessible parent = ac.getAccessibleParent();
       
   479                 AccessibleContext pac = parent.getAccessibleContext();
       
   480                 if (pac == null) return;
       
   481                 AccessibleSelection as = pac.getAccessibleSelection();
       
   482                 if (as == null) return;
       
   483                 as.addAccessibleSelection(i);
       
   484             }
       
   485         }, c);
       
   486     }
       
   487 
       
   488     public static Number getMaximumAccessibleValue(final Accessible a, final Component c) {
       
   489         if (a == null) return null;
       
   490 
       
   491         return invokeAndWait(new Callable<Number>() {
       
   492             public Number call() throws Exception {
       
   493                 final AccessibleContext ac = a.getAccessibleContext();
       
   494                 if (ac == null) return null;
       
   495 
       
   496                 final AccessibleValue av = ac.getAccessibleValue();
       
   497                 if (av == null) return null;
       
   498 
       
   499                 return av.getMaximumAccessibleValue();
       
   500             }
       
   501         }, c);
       
   502     }
       
   503 
       
   504     public static Number getMinimumAccessibleValue(final Accessible a, final Component c) {
       
   505         if (a == null) return null;
       
   506 
       
   507         return invokeAndWait(new Callable<Number>() {
       
   508             public Number call() throws Exception {
       
   509                 final AccessibleContext ac = a.getAccessibleContext();
       
   510                 if (ac == null) return null;
       
   511 
       
   512                 final AccessibleValue av = ac.getAccessibleValue();
       
   513                 if (av == null) return null;
       
   514 
       
   515                 return av.getMinimumAccessibleValue();
       
   516             }
       
   517         }, c);
       
   518     }
       
   519 
       
   520     public static String getAccessibleRoleDisplayString(final Accessible a, final Component c) {
       
   521         if (a == null) return null;
       
   522 
       
   523         return invokeAndWait(new Callable<String>() {
       
   524             public String call() throws Exception {
       
   525                 final AccessibleContext ac = a.getAccessibleContext();
       
   526                 if (ac == null) return null;
       
   527 
       
   528                 final AccessibleRole ar = ac.getAccessibleRole();
       
   529                 if (ar == null) return null;
       
   530 
       
   531                 return ar.toDisplayString();
       
   532             }
       
   533         }, c);
       
   534     }
       
   535 
       
   536     public static Number getCurrentAccessibleValue(final AccessibleValue av, final Component c) {
       
   537         if (av == null) return null;
       
   538 
       
   539         return invokeAndWait(new Callable<Number>() {
       
   540             public Number call() throws Exception {
       
   541                 Number currentAccessibleValue = av.getCurrentAccessibleValue();
       
   542                 return currentAccessibleValue;
       
   543             }
       
   544         }, c);
       
   545     }
       
   546 
       
   547     public static Accessible getFocusOwner(final Component c) {
       
   548         return invokeAndWait(new Callable<Accessible>() {
       
   549             public Accessible call() throws Exception {
       
   550                 Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
       
   551                 if (c == null || !(c instanceof Accessible)) return null;
       
   552                 return CAccessible.getCAccessible((Accessible)c);
       
   553             }
       
   554         }, c);
       
   555     }
       
   556 
       
   557     public static boolean[] getInitialAttributeStates(final Accessible a, final Component c) {
       
   558         final boolean[] ret = new boolean[7];
       
   559         if (a == null) return ret;
       
   560 
       
   561         return invokeAndWait(new Callable<boolean[]>() {
       
   562             public boolean[] call() throws Exception {
       
   563                 final AccessibleContext aContext = a.getAccessibleContext();
       
   564                 if (aContext == null) return ret;
       
   565 
       
   566                 final AccessibleComponent aComponent = aContext.getAccessibleComponent();
       
   567                 ret[0] = (aComponent != null);
       
   568                 ret[1] = ((aComponent != null) && (aComponent.isFocusTraversable()));
       
   569                 ret[2] = (aContext.getAccessibleValue() != null);
       
   570                 ret[3] = (aContext.getAccessibleText() != null);
       
   571 
       
   572                 final AccessibleStateSet aStateSet = aContext.getAccessibleStateSet();
       
   573                 ret[4] = (aStateSet.contains(AccessibleState.HORIZONTAL) || aStateSet.contains(AccessibleState.VERTICAL));
       
   574                 ret[5] = (aContext.getAccessibleName() != null);
       
   575                 ret[6] = (aContext.getAccessibleChildrenCount() > 0);
       
   576                 return ret;
       
   577             }
       
   578         }, c);
       
   579     }
       
   580 
       
   581     // Duplicated from JavaComponentAccessibility
       
   582     // Note that values >=0 are indexes into the child array
       
   583     static final int JAVA_AX_ALL_CHILDREN = -1;
       
   584     static final int JAVA_AX_SELECTED_CHILDREN = -2;
       
   585     static final int JAVA_AX_VISIBLE_CHILDREN = -3;
       
   586 
       
   587     // Each child takes up two entries in the array: one for itself and one for its role
       
   588     public static Object[] getChildrenAndRoles(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) {
       
   589         if (a == null) return null;
       
   590         return invokeAndWait(new Callable<Object[]>() {
       
   591             public Object[] call() throws Exception {
       
   592                 ArrayList<Object> childrenAndRoles = new ArrayList<Object>();
       
   593                 _addChildren(a, whichChildren, allowIgnored, childrenAndRoles);
       
   594 
       
   595                 /* In the case of fetching a selection, need to check to see if
       
   596                  * the active descendant is at the beginning of the list.  If it
       
   597                  * is not it needs to be moved to the beginning of the list so
       
   598                  * VoiceOver will annouce it correctly.  The list returned
       
   599                  * from Java is always in order from top to bottom, but when shift
       
   600                  * selecting downward (extending the list) or multi-selecting using
       
   601                  * the VO keys control+option+command+return the active descendant
       
   602                  * is not at the top of the list in the shift select down case and
       
   603                  * may not be in the multi select case.
       
   604                  */
       
   605                 if (whichChildren == JAVA_AX_SELECTED_CHILDREN) {
       
   606                     if (!childrenAndRoles.isEmpty()) {
       
   607                         AccessibleContext activeDescendantAC =
       
   608                             CAccessible.getActiveDescendant(a);
       
   609                         if (activeDescendantAC != null) {
       
   610                             String activeDescendantName =
       
   611                                 activeDescendantAC.getAccessibleName();
       
   612                             AccessibleRole activeDescendantRole =
       
   613                                 activeDescendantAC.getAccessibleRole();
       
   614                             // Move active descendant to front of list.
       
   615                             // List contains pairs of each selected item's
       
   616                             // Accessible and AccessibleRole.
       
   617                             ArrayList<Object> newArray  = new ArrayList<Object>();
       
   618                             int count = childrenAndRoles.size();
       
   619                             Accessible currentAccessible = null;
       
   620                             AccessibleContext currentAC = null;
       
   621                             String currentName = null;
       
   622                             AccessibleRole currentRole = null;
       
   623                             for (int i = 0; i < count; i+=2) {
       
   624                                 // Is this the active descendant?
       
   625                                 currentAccessible = (Accessible)childrenAndRoles.get(i);
       
   626                                 currentAC = currentAccessible.getAccessibleContext();
       
   627                                 currentName = currentAC.getAccessibleName();
       
   628                                 currentRole = (AccessibleRole)childrenAndRoles.get(i+1);
       
   629                                 if ( currentName.equals(activeDescendantName) &&
       
   630                                      currentRole.equals(activeDescendantRole) ) {
       
   631                                     newArray.add(0, currentAccessible);
       
   632                                     newArray.add(1, currentRole);
       
   633                                 } else {
       
   634                                     newArray.add(currentAccessible);
       
   635                                     newArray.add(currentRole);
       
   636                                 }
       
   637                             }
       
   638                             childrenAndRoles = newArray;
       
   639                         }
       
   640                     }
       
   641                 }
       
   642 
       
   643                 if ((whichChildren < 0) || (whichChildren * 2 >= childrenAndRoles.size())) {
       
   644                     return childrenAndRoles.toArray();
       
   645                 }
       
   646 
       
   647                 return new Object[] { childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1) };
       
   648             }
       
   649         }, c);
       
   650     }
       
   651 
       
   652     private static AccessibleRole getAccessibleRoleForLabel(JLabel l, AccessibleRole fallback) {
       
   653         String text = l.getText();
       
   654         if (text != null && text.length() > 0) {
       
   655             return fallback;
       
   656         }
       
   657         Icon icon = l.getIcon();
       
   658         if (icon != null) {
       
   659             return AccessibleRole.ICON;
       
   660         }
       
   661         return fallback;
       
   662     }
       
   663 
       
   664     private static AccessibleRole getAccessibleRole(Accessible a) {
       
   665         AccessibleContext ac = a.getAccessibleContext();
       
   666         AccessibleRole role = ac.getAccessibleRole();
       
   667         Object component = CAccessible.getSwingAccessible(a);
       
   668         if (role == null) return null;
       
   669         String roleString = role.toString();
       
   670         if ("label".equals(roleString) && component instanceof JLabel) {
       
   671             return getAccessibleRoleForLabel((JLabel) component, role);
       
   672         }
       
   673         return role;
       
   674     }
       
   675 
       
   676 
       
   677     // Either gets the immediate children of a, or recursively gets all unignored children of a
       
   678     private static void _addChildren(final Accessible a, final int whichChildren, final boolean allowIgnored, final ArrayList<Object> childrenAndRoles) {
       
   679         if (a == null) return;
       
   680 
       
   681         final AccessibleContext ac = a.getAccessibleContext();
       
   682         if (ac == null) return;
       
   683 
       
   684         final int numChildren = ac.getAccessibleChildrenCount();
       
   685 
       
   686         // each child takes up two entries in the array: itself, and its role
       
   687         // so the array holds alternating Accessible and AccessibleRole objects
       
   688         for (int i = 0; i < numChildren; i++) {
       
   689             final Accessible child = ac.getAccessibleChild(i);
       
   690             if (child == null) continue;
       
   691 
       
   692             final AccessibleContext context = child.getAccessibleContext();
       
   693             if (context == null) continue;
       
   694 
       
   695             if (whichChildren == JAVA_AX_VISIBLE_CHILDREN) {
       
   696                 AccessibleComponent acomp = context.getAccessibleComponent();
       
   697                 if (acomp == null || !acomp.isVisible()) {
       
   698                     continue;
       
   699                 }
       
   700             } else if (whichChildren == JAVA_AX_SELECTED_CHILDREN) {
       
   701                 AccessibleSelection sel = ac.getAccessibleSelection();
       
   702                 if (sel == null || !sel.isAccessibleChildSelected(i)) {
       
   703                     continue;
       
   704                 }
       
   705             }
       
   706 
       
   707             if (!allowIgnored) {
       
   708                 final AccessibleRole role = context.getAccessibleRole();
       
   709                 if (role != null && ignoredRoles != null && ignoredRoles.contains(roleKey(role))) {
       
   710                     // Get the child's unignored children.
       
   711                     _addChildren(child, whichChildren, false, childrenAndRoles);
       
   712                 } else {
       
   713                     childrenAndRoles.add(child);
       
   714                     childrenAndRoles.add(getAccessibleRole(child));
       
   715                 }
       
   716             } else {
       
   717                 childrenAndRoles.add(child);
       
   718                 childrenAndRoles.add(getAccessibleRole(child));
       
   719             }
       
   720 
       
   721             // If there is an index, and we are beyond it, time to finish up
       
   722             if ((whichChildren >= 0) && (childrenAndRoles.size() / 2) >= (whichChildren + 1)) {
       
   723                 return;
       
   724             }
       
   725         }
       
   726     }
       
   727 
       
   728     private static native String roleKey(AccessibleRole aRole);
       
   729 
       
   730     public static Object[] getChildren(final Accessible a, final Component c) {
       
   731         if (a == null) return null;
       
   732         return invokeAndWait(new Callable<Object[]>() {
       
   733             public Object[] call() throws Exception {
       
   734                 final AccessibleContext ac = a.getAccessibleContext();
       
   735                 if (ac == null) return null;
       
   736 
       
   737                 final int numChildren = ac.getAccessibleChildrenCount();
       
   738                 final Object[] children = new Object[numChildren];
       
   739                 for (int i = 0; i < numChildren; i++) {
       
   740                     children[i] = ac.getAccessibleChild(i);
       
   741                 }
       
   742                 return children;
       
   743             }
       
   744         }, c);
       
   745     }
       
   746 
       
   747     /**
       
   748      * @return AWTView ptr, a peer of the CPlatformView associated with the toplevel container of the Accessible, if any
       
   749      */
       
   750     private static long getAWTView(Accessible a) {
       
   751         Accessible ax = CAccessible.getSwingAccessible(a);
       
   752         if (!(ax instanceof Component)) return 0;
       
   753 
       
   754         return invokeAndWait(new Callable<Long>() {
       
   755             public Long call() throws Exception {
       
   756                 Component cont = (Component) ax;
       
   757                 while (cont != null && !(cont instanceof Window)) {
       
   758                     cont = cont.getParent();
       
   759                 }
       
   760                 if (cont != null) {
       
   761                     LWWindowPeer peer = (LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(cont);
       
   762                     if (peer != null) {
       
   763                         return ((CPlatformWindow) peer.getPlatformWindow()).getContentView().getAWTView();
       
   764                     }
       
   765                 }
       
   766                 return 0L;
       
   767             }
       
   768         }, (Component)ax);
       
   769     }
       
   770 }