3434import java .awt .event .KeyEvent ;
3535import java .beans .PropertyChangeEvent ;
3636import java .beans .PropertyChangeListener ;
37+ import java .lang .annotation .Native ;
3738import java .lang .reflect .InvocationTargetException ;
3839import java .util .ArrayList ;
3940import java .util .HashSet ;
4041import java .util .Set ;
4142import java .util .concurrent .Callable ;
43+ import java .util .Arrays ;
4244
4345import javax .accessibility .Accessible ;
4446import javax .accessibility .AccessibleAction ;
5759import javax .swing .JLabel ;
5860import javax .swing .JMenuItem ;
5961import javax .swing .JTextArea ;
62+ import javax .swing .JList ;
63+ import javax .swing .JTree ;
6064import javax .swing .KeyStroke ;
6165
6266import sun .awt .AWTAccessor ;
@@ -116,12 +120,7 @@ public void propertyChange(final PropertyChangeEvent evt) {
116120private native void focusChanged ();
117121
118122static <T >T invokeAndWait (final Callable <T >callable ,final Component c ) {
119- if (c !=null ) {
120- try {
121- return LWCToolkit .invokeAndWait (callable ,c );
122- }catch (final Exception e ) {e .printStackTrace (); }
123- }
124- return null ;
123+ return invokeAndWait (callable ,c ,null );
125124 }
126125
127126static <T >T invokeAndWait (final Callable <T >callable ,final Component c ,final T defValue ) {
@@ -551,6 +550,10 @@ public void run() {
551550if (pac ==null )return ;
552551AccessibleSelection as =pac .getAccessibleSelection ();
553552if (as ==null )return ;
553+ if (parent instanceof JList ) {
554+ ((JList )parent ).setSelectedIndex (i );
555+ return ;
556+ }
554557as .addAccessibleSelection (i );
555558 }
556559 },c );
@@ -651,77 +654,148 @@ public boolean[] call() throws Exception {
651654
652655// Duplicated from JavaComponentAccessibility
653656// Note that values >=0 are indexes into the child array
654- static final int JAVA_AX_ALL_CHILDREN = -1 ;
655- static final int JAVA_AX_SELECTED_CHILDREN = -2 ;
656- static final int JAVA_AX_VISIBLE_CHILDREN = -3 ;
657+ @ Native static final int JAVA_AX_ALL_CHILDREN = -1 ;
658+ @ Native static final int JAVA_AX_SELECTED_CHILDREN = -2 ;
659+ @ Native static final int JAVA_AX_VISIBLE_CHILDREN = -3 ;
657660
658661// Each child takes up two entries in the array: one for itself and one for its role
659662public static Object []getChildrenAndRoles (final Accessible a ,final Component c ,final int whichChildren ,final boolean allowIgnored ) {
660663if (a ==null )return null ;
661664return invokeAndWait (new Callable <Object []>() {
662665public Object []call ()throws Exception {
663- ArrayList <Object >childrenAndRoles =new ArrayList <Object >();
664- _addChildren (a ,whichChildren ,allowIgnored ,childrenAndRoles );
665-
666- /* In the case of fetching a selection, need to check to see if
667- * the active descendant is at the beginning of the list. If it
668- * is not it needs to be moved to the beginning of the list so
669- * VoiceOver will annouce it correctly. The list returned
670- * from Java is always in order from top to bottom, but when shift
671- * selecting downward (extending the list) or multi-selecting using
672- * the VO keys control+option+command+return the active descendant
673- * is not at the top of the list in the shift select down case and
674- * may not be in the multi select case.
675- */
676- if (whichChildren ==JAVA_AX_SELECTED_CHILDREN ) {
677- if (!childrenAndRoles .isEmpty ()) {
678- AccessibleContext activeDescendantAC =
679- CAccessible .getActiveDescendant (a );
680- if (activeDescendantAC !=null ) {
681- String activeDescendantName =
682- activeDescendantAC .getAccessibleName ();
683- AccessibleRole activeDescendantRole =
684- activeDescendantAC .getAccessibleRole ();
685- // Move active descendant to front of list.
686- // List contains pairs of each selected item's
687- // Accessible and AccessibleRole.
688- ArrayList <Object >newArray =new ArrayList <Object >();
689- int count =childrenAndRoles .size ();
690- Accessible currentAccessible =null ;
691- AccessibleContext currentAC =null ;
692- String currentName =null ;
693- AccessibleRole currentRole =null ;
694- for (int i =0 ;i <count ;i +=2 ) {
695- // Is this the active descendant?
696- currentAccessible = (Accessible )childrenAndRoles .get (i );
697- currentAC =currentAccessible .getAccessibleContext ();
698- currentName =currentAC .getAccessibleName ();
699- currentRole = (AccessibleRole )childrenAndRoles .get (i +1 );
700- if (currentName !=null &¤tName .equals (activeDescendantName ) &&
701- currentRole .equals (activeDescendantRole ) ) {
702- newArray .add (0 ,currentAccessible );
703- newArray .add (1 ,currentRole );
704- }else {
705- newArray .add (currentAccessible );
706- newArray .add (currentRole );
707- }
708- }
709- childrenAndRoles =newArray ;
666+ return getChildrenAndRolesImpl (a ,c ,whichChildren ,allowIgnored );
667+ }
668+ },c );
669+ }
670+
671+ private static Object []getChildrenAndRolesImpl (final Accessible a ,final Component c ,final int whichChildren ,final boolean allowIgnored ) {
672+ if (a ==null )return null ;
673+
674+ ArrayList <Object >childrenAndRoles =new ArrayList <Object >();
675+ _addChildren (a ,whichChildren ,allowIgnored ,childrenAndRoles );
676+
677+ /* In case of fetching a selection, we need to check if
678+ * the active descendant is at the beginning of the list, or
679+ * otherwise move it, so that VoiceOver announces it correctly.
680+ * The java list is always in order from top to bottom, but when
681+ * (1) shift-selecting downward (extending the list) or (2) multi-selecting with
682+ * the VO keys (CTRL+ALT+CMD+RETURN) the active descendant
683+ * is not at the top of the list in the 1st case and may not be in the 2nd.
684+ */
685+ if (whichChildren ==JAVA_AX_SELECTED_CHILDREN ) {
686+ if (!childrenAndRoles .isEmpty ()) {
687+ AccessibleContext activeDescendantAC =
688+ CAccessible .getActiveDescendant (a );
689+ if (activeDescendantAC !=null ) {
690+ String activeDescendantName =
691+ activeDescendantAC .getAccessibleName ();
692+ AccessibleRole activeDescendantRole =
693+ activeDescendantAC .getAccessibleRole ();
694+ // Move active descendant to front of list.
695+ // List contains pairs of each selected item's
696+ // Accessible and AccessibleRole.
697+ ArrayList <Object >newArray =new ArrayList <Object >();
698+ int count =childrenAndRoles .size ();
699+ Accessible currentAccessible =null ;
700+ AccessibleContext currentAC =null ;
701+ String currentName =null ;
702+ AccessibleRole currentRole =null ;
703+ for (int i =0 ;i <count ;i +=2 ) {
704+ // Is this the active descendant?
705+ currentAccessible = (Accessible )childrenAndRoles .get (i );
706+ currentAC =currentAccessible .getAccessibleContext ();
707+ currentName =currentAC .getAccessibleName ();
708+ currentRole = (AccessibleRole )childrenAndRoles .get (i +1 );
709+ if (currentName !=null &¤tName .equals (activeDescendantName ) &&
710+ currentRole .equals (activeDescendantRole )) {
711+ newArray .add (0 ,currentAccessible );
712+ newArray .add (1 ,currentRole );
713+ }else {
714+ newArray .add (currentAccessible );
715+ newArray .add (currentRole );
710716 }
711717 }
718+ childrenAndRoles =newArray ;
712719 }
720+ }
721+ }
722+
723+ if ((whichChildren <0 ) || (whichChildren *2 >=childrenAndRoles .size ())) {
724+ return childrenAndRoles .toArray ();
725+ }
726+
727+ return new Object []{childrenAndRoles .get (whichChildren *2 ),childrenAndRoles .get ((whichChildren *2 ) +1 )};
728+ }
729+
730+ // This method is called from the native
731+ // Each child takes up three entries in the array: one for itself, one for its role, and one for the recursion level
732+ private static Object []getChildrenAndRolesRecursive (final Accessible a ,final Component c ,final int whichChildren ,final boolean allowIgnored ,final int level ) {
733+ if (a ==null )return null ;
734+ return invokeAndWait (new Callable <Object []>() {
735+ public Object []call ()throws Exception {
736+ ArrayList <Object >currentLevelChildren =new ArrayList <Object >();
737+ ArrayList <Object >allChildren =new ArrayList <Object >();
738+ ArrayList <Accessible >parentStack =new ArrayList <Accessible >();
739+ parentStack .add (a );
740+ ArrayList <Integer >indexses =new ArrayList <Integer >();
741+ Integer index =0 ;
742+ int currentLevel =level ;
743+ while (!parentStack .isEmpty ()) {
744+ Accessible p =parentStack .get (parentStack .size () -1 );
745+
746+ currentLevelChildren .addAll (Arrays .asList (getChildrenAndRolesImpl (p ,c ,JAVA_AX_ALL_CHILDREN ,allowIgnored )));
747+ if ((currentLevelChildren .size () ==0 ) || (index >=currentLevelChildren .size ())) {
748+ if (!parentStack .isEmpty ())parentStack .remove (parentStack .size () -1 );
749+ if (!indexses .isEmpty ())index =indexses .remove (indexses .size () -1 );
750+ currentLevel -=1 ;
751+ currentLevelChildren .clear ();
752+ continue ;
753+ }
754+
755+ Accessible ca =null ;
756+ Object obj =currentLevelChildren .get (index );
757+ if (!(obj instanceof Accessible )) {
758+ index +=2 ;
759+ currentLevelChildren .clear ();
760+ continue ;
761+ }
762+ ca = (Accessible )obj ;
763+ Object role =currentLevelChildren .get (index +1 );
764+ currentLevelChildren .clear ();
765+
766+ AccessibleContext cac =ca .getAccessibleContext ();
767+ if (cac ==null ) {
768+ index +=2 ;
769+ continue ;
770+ }
771+
772+ if ((cac .getAccessibleStateSet ().contains (AccessibleState .SELECTED ) && (whichChildren ==JAVA_AX_SELECTED_CHILDREN )) ||
773+ (cac .getAccessibleStateSet ().contains (AccessibleState .VISIBLE ) && (whichChildren ==JAVA_AX_VISIBLE_CHILDREN )) ||
774+ (whichChildren ==JAVA_AX_ALL_CHILDREN )) {
775+ allChildren .add (ca );
776+ allChildren .add (role );
777+ allChildren .add (String .valueOf (currentLevel ));
778+ }
779+
780+ index +=2 ;
781+
782+ if (cac .getAccessibleStateSet ().contains (AccessibleState .EXPANDED )) {
783+ parentStack .add (ca );
784+ indexses .add (index );
785+ index =0 ;
786+ currentLevel +=1 ;
787+ continue ;
788+ }
713789
714- if ((whichChildren <0 ) || (whichChildren *2 >=childrenAndRoles .size ())) {
715- return childrenAndRoles .toArray ();
716790 }
717791
718- return new Object [] { childrenAndRoles . get ( whichChildren * 2 ), childrenAndRoles . get (( whichChildren * 2 ) + 1 ) } ;
792+ return allChildren . toArray () ;
719793 }
720794 },c );
721795 }
722796
723- private static final int JAVA_AX_ROWS =1 ;
724- private static final int JAVA_AX_COLS =2 ;
797+ @ Native private static final int JAVA_AX_ROWS =1 ;
798+ @ Native private static final int JAVA_AX_COLS =2 ;
725799
726800public static int getTableInfo (final Accessible a ,final Component c ,
727801final int info ) {
@@ -740,6 +814,23 @@ public static int getTableInfo(final Accessible a, final Component c,
740814 },c );
741815 }
742816
817+ private static int []getTableSelectedInfo (final Accessible a ,final Component c ,
818+ final int info ) {
819+ if (a ==null )return null ;
820+ return invokeAndWait (() -> {
821+ AccessibleContext ac =a .getAccessibleContext ();
822+ AccessibleTable table =ac .getAccessibleTable ();
823+ if (table !=null ) {
824+ if (info ==JAVA_AX_COLS ) {
825+ return table .getSelectedAccessibleColumns ();
826+ }else if (info ==JAVA_AX_ROWS ) {
827+ return table .getSelectedAccessibleRows ();
828+ }
829+ }
830+ return null ;
831+ },c );
832+ }
833+
743834private static AccessibleRole getAccessibleRoleForLabel (JLabel l ,AccessibleRole fallback ) {
744835String text =l .getText ();
745836if (text !=null &&text .length () >0 ) {
@@ -858,4 +949,18 @@ public Long call() throws Exception {
858949 }
859950 }, (Component )ax );
860951 }
952+
953+ private static boolean isTreeRootVisible (Accessible a ,Component c ) {
954+ if (a ==null )return false ;
955+
956+ return invokeAndWait (new Callable <Boolean >() {
957+ public Boolean call ()throws Exception {
958+ Accessible sa =CAccessible .getSwingAccessible (a );
959+ if (sa instanceof JTree ) {
960+ return ((JTree )sa ).isRootVisible ();
961+ }
962+ return false ;
963+ }
964+ },c );
965+ }
861966}