|
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 } |