143 // windows
144 initHWNDcalls();
145
146 // is this a JVM we can use?
147 // install JDK 1.2 and later Swing ToolKit listener
148 EventQueueMonitor.isGUIInitialized();
149
150 // start the Java event handler
151 eventHandler = new EventHandler(this);
152
153 // register for menu selection events
154 MenuSelectionManager.defaultManager().addChangeListener(eventHandler);
155
156 // register as a NativeWindowHandler
157 addNativeWindowHandler(new DefaultNativeWindowHandler());
158
159 // start in a new thread
160 Thread abthread = new Thread(new dllRunner());
161 abthread.setDaemon(true);
162 abthread.start();
163 debugString("AccessBridge started");
164 }
165
166 /*
167 * adaptor to run the AccessBridge DLL
168 */
169 private class dllRunner implements Runnable {
170 public void run() {
171 runDLL();
172 }
173 }
174
175 /*
176 * shutdown hook
177 */
178 private class shutdownHook implements Runnable {
179
180 public void run() {
181 debugString("***** shutdownHook: shutting down...");
182 javaShutdown();
183 }
184 }
185
186
187 /*
188 * Initialize the hashtable that maps Strings to AccessibleRoles.
189 */
190 private void initAccessibleRoleMap() {
191 /*
192 * Initialize the AccessibleRoles map. This code uses methods in
193 * java.lang.reflect.* to build the map.
194 */
195 try {
196 Class<?> clAccessibleRole = Class.forName ("javax.accessibility.AccessibleRole");
197 if (null != clAccessibleRole) {
198 AccessibleRole roleUnknown = AccessibleRole.UNKNOWN;
199 Field [] fields = clAccessibleRole.getFields ();
200 int i = 0;
201 for (i = 0; i < fields.length; i ++) {
280 private Method javaGetComponentFromNativeWindowHandleMethod;
281 private Method javaGetNativeWindowHandleFromComponentMethod;
282
283 // native jawt methods for mapping HWNDs to Java components
284 private native int jawtGetNativeWindowHandleFromComponent(Component comp);
285
286 private native Component jawtGetComponentFromNativeWindowHandle(int handle);
287
288 Toolkit toolkit;
289
290 /**
291 * map an HWND to an AWT Component
292 */
293 private void initHWNDcalls() {
294 Class<?>[] integerParemter = new Class<?>[1];
295 integerParemter[0] = Integer.TYPE;
296 Class<?>[] componentParemter = new Class<?>[1];
297 try {
298 componentParemter[0] = Class.forName("java.awt.Component");
299 } catch (ClassNotFoundException e) {
300 debugString("Exception: " + e.toString());
301 }
302 toolkit = Toolkit.getDefaultToolkit();
303 return;
304 }
305
306 // native window handler interface
307 private interface NativeWindowHandler {
308 public Accessible getAccessibleFromNativeWindowHandle(int nativeHandle);
309 }
310
311 // hash table of native window handle to AccessibleContext mappings
312 static private ConcurrentHashMap<Integer,AccessibleContext> windowHandleToContextMap = new ConcurrentHashMap<>();
313
314 // hash table of AccessibleContext to native window handle mappings
315 static private ConcurrentHashMap<AccessibleContext,Integer> contextToWindowHandleMap = new ConcurrentHashMap<>();
316
317 /*
318 * adds a virtual window handler to our hash tables
319 */
320 static private void registerVirtualFrame(final Accessible a,
369 return nativeWindowHandlers.removeElement(handler);
370 }
371
372 /**
373 * verifies that a native window handle is a Java window
374 */
375 private boolean isJavaWindow(int nativeHandle) {
376 AccessibleContext ac = getContextFromNativeWindowHandle(nativeHandle);
377 if (ac != null) {
378 saveContextToWindowHandleMapping(ac, nativeHandle);
379 return true;
380 }
381 return false;
382 }
383
384 /*
385 * saves the mapping between an AccessibleContext and a window handle
386 */
387 private void saveContextToWindowHandleMapping(AccessibleContext ac,
388 int nativeHandle) {
389 debugString("saveContextToWindowHandleMapping...");
390 if (ac == null) {
391 return;
392 }
393 if (! contextToWindowHandleMap.containsKey(ac)) {
394 debugString("saveContextToWindowHandleMapping: ac = "+ac+"; handle = "+nativeHandle);
395 contextToWindowHandleMap.put(ac, nativeHandle);
396 }
397 }
398
399 /**
400 * maps a native window handle to an Accessible Context
401 */
402 private AccessibleContext getContextFromNativeWindowHandle(int nativeHandle) {
403 // First, look for the Accessible in our hash table of
404 // virtual window handles.
405 AccessibleContext ac = windowHandleToContextMap.get(nativeHandle);
406 if(ac!=null) {
407 saveContextToWindowHandleMapping(ac, nativeHandle);
408 return ac;
409 }
410
411 // Next, look for the native window handle in our vector
412 // of native window handles.
413 int numHandlers = nativeWindowHandlers.size();
414 for (int i = 0; i < numHandlers; i++) {
417 if (a != null) {
418 ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
419 @Override
420 public AccessibleContext call() throws Exception {
421 return a.getAccessibleContext();
422 }
423 }, a);
424 saveContextToWindowHandleMapping(ac, nativeHandle);
425 return ac;
426 }
427 }
428 // Not found.
429 return null;
430 }
431
432 /**
433 * maps an AccessibleContext to a native window handle
434 * returns 0 on error
435 */
436 private int getNativeWindowHandleFromContext(AccessibleContext ac) {
437 debugString("getNativeWindowHandleFromContext: ac = "+ac);
438 try {
439 return contextToWindowHandleMap.get(ac);
440 } catch (Exception ex) {
441 return 0;
442 }
443 }
444
445 private class DefaultNativeWindowHandler implements NativeWindowHandler {
446 /*
447 * returns the Accessible associated with a native window
448 */
449 public Accessible getAccessibleFromNativeWindowHandle(int nativeHandle) {
450 final Component c = jawtGetComponentFromNativeWindowHandle(nativeHandle);
451 if (c instanceof Accessible) {
452 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
453 @Override
454 public AccessibleContext call() throws Exception {
455 return c.getAccessibleContext();
456 }
457 }, c);
498 public AccessibleContext call() throws Exception {
499 Accessible parent = ac.getAccessibleParent();
500 if (parent == null) {
501 return ac;
502 }
503 Accessible tmp = parent.getAccessibleContext().getAccessibleParent();
504 while (tmp != null) {
505 parent = tmp;
506 tmp = parent.getAccessibleContext().getAccessibleParent();
507 }
508 return parent.getAccessibleContext();
509 }
510 }, ac);
511 }
512
513 /*
514 * StarOffice version that does not use the EventQueueMonitor
515 */
516 private AccessibleContext getAccessibleContextAt_1(final int x, final int y,
517 final AccessibleContext parent) {
518 debugString(" : getAccessibleContextAt_1 called");
519 debugString(" -> x = " + x + " y = " + y + " parent = " + parent);
520
521 if (parent == null) return null;
522 final AccessibleComponent acmp = InvocationUtils.invokeAndWait(new Callable<AccessibleComponent>() {
523 @Override
524 public AccessibleComponent call() throws Exception {
525 return parent.getAccessibleComponent();
526 }
527 }, parent);
528 if (acmp!=null) {
529 final Point loc = InvocationUtils.invokeAndWait(new Callable<Point>() {
530 @Override
531 public Point call() throws Exception {
532 return acmp.getLocation();
533 }
534 }, parent);
535 final Accessible a = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
536 @Override
537 public Accessible call() throws Exception {
538 return acmp.getAccessibleAt(new Point(x - loc.x, y - loc.y));
546 }
547 }, parent);
548 if (foundAC != null) {
549 if (foundAC != parent) {
550 // recurse down into the child
551 return getAccessibleContextAt_1(x - loc.x, y - loc.y,
552 foundAC);
553 } else
554 return foundAC;
555 }
556 }
557 }
558 return parent;
559 }
560
561 /*
562 * AWT/Swing version
563 */
564 private AccessibleContext getAccessibleContextAt_2(final int x, final int y,
565 AccessibleContext parent) {
566 debugString("getAccessibleContextAt_2 called");
567 debugString(" -> x = " + x + " y = " + y + " parent = " + parent);
568
569 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
570 @Override
571 public AccessibleContext call() throws Exception {
572 Accessible a = EventQueueMonitor.getAccessibleAt(new Point(x, y));
573 if (a != null) {
574 AccessibleContext childAC = a.getAccessibleContext();
575 if (childAC != null) {
576 debugString(" returning childAC = " + childAC);
577 return childAC;
578 }
579 }
580 return null;
581 }
582 }, parent);
583 }
584
585 /**
586 * returns the Accessible that has focus
587 */
588 private AccessibleContext getAccessibleContextWithFocus() {
589 Component c = AWTEventMonitor.getComponentWithFocus();
590 if (c != null) {
591 final Accessible a = Translator.getAccessible(c);
592 if (a != null) {
593 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
594 @Override
595 public AccessibleContext call() throws Exception {
596 return a.getAccessibleContext();
597 }
598 }, c);
599 if (ac != null) {
600 return ac;
601 }
602 }
603 }
604 return null;
605 }
606
607 /**
608 * returns the AccessibleName from an AccessibleContext
609 */
610 private String getAccessibleNameFromContext(final AccessibleContext ac) {
611 debugString("***** ac = "+ac.getClass());
612 if (ac != null) {
613 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
614 @Override
615 public String call() throws Exception {
616 return ac.getAccessibleName();
617 }
618 }, ac);
619 if (s != null) {
620 references.increment(s);
621 debugString("Returning AccessibleName from Context: " + s);
622 return s;
623 } else {
624 return null;
625 }
626 } else {
627 debugString("getAccessibleNameFromContext; ac = null!");
628 return null;
629 }
630 }
631
632 /**
633 * Returns an AccessibleName for a component using an algorithm optimized
634 * for the JAWS screen reader. This method is only intended for JAWS. All
635 * other uses are entirely optional.
636 */
637 private String getVirtualAccessibleNameFromContext(final AccessibleContext ac) {
638 if (null != ac) {
639 /*
640 Step 1:
641 =======
642 Determine if we can obtain the Virtual Accessible Name from the
643 Accessible Name or Accessible Description of the object.
644 */
645 String nameString = InvocationUtils.invokeAndWait(new Callable<String>() {
646 @Override
647 public String call() throws Exception {
648 return ac.getAccessibleName();
649 }
650 }, ac);
651 if ( ( null != nameString ) && ( 0 != nameString.length () ) ) {
652 debugString ("bk -- The Virtual Accessible Name was obtained from AccessibleContext::getAccessibleName.");
653 references.increment (nameString);
654 return nameString;
655 }
656 String descriptionString = InvocationUtils.invokeAndWait(new Callable<String>() {
657 @Override
658 public String call() throws Exception {
659 return ac.getAccessibleDescription();
660 }
661 }, ac);
662 if ( ( null != descriptionString ) && ( 0 != descriptionString.length () ) ) {
663 debugString ("bk -- The Virtual Accessible Name was obtained from AccessibleContext::getAccessibleDescription.");
664 references.increment (descriptionString);
665 return descriptionString;
666 }
667
668 debugString ("The Virtual Accessible Name was not found using AccessibleContext::getAccessibleDescription. or getAccessibleName");
669 /*
670 Step 2:
671 =======
672 Decide whether the extended name search algorithm should be
673 used for this object.
674 */
675 boolean bExtendedSearch = false;
676 AccessibleRole role = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
677 @Override
678 public AccessibleRole call() throws Exception {
679 return ac.getAccessibleRole();
680 }
681 }, ac);
682 AccessibleContext parentContext = null;
683 AccessibleRole parentRole = AccessibleRole.UNKNOWN;
684
685 if ( extendedVirtualNameSearchRoles.contains (role) ) {
686 parentContext = getAccessibleParentFromContext (ac);
687 if ( null != parentContext ) {
688 final AccessibleContext parentContextInnerTemp = parentContext;
689 parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
690 @Override
691 public AccessibleRole call() throws Exception {
692 return parentContextInnerTemp.getAccessibleRole();
693 }
694 }, ac);
695 if ( AccessibleRole.UNKNOWN != parentRole ) {
696 bExtendedSearch = true;
697 if ( noExtendedVirtualNameSearchParentRoles.contains (parentRole) ) {
698 bExtendedSearch = false;
699 }
700 }
701 }
702 }
703
704 if (false == bExtendedSearch) {
705 debugString ("bk -- getVirtualAccessibleNameFromContext will not use the extended name search algorithm. role = " + ( role != null ? role.toDisplayString(Locale.US) : "null") );
706 /*
707 Step 3:
708 =======
709 We have determined that we should not use the extended name
710 search algorithm for this object (we must obtain the name of
711 the object from the object itself and not from neighboring
712 objects). However the object name cannot be obtained from
713 the Accessible Name or Accessible Description of the object.
714
715 Handle several special cases here that might yield a value for
716 the Virtual Accessible Name. Return null if the object does
717 not match the criteria for any of these special cases.
718 */
719 if (AccessibleRole.LABEL == role) {
720 /*
721 Does the label support the Accessible Text Interface?
722 */
723 final AccessibleText at = InvocationUtils.invokeAndWait(new Callable<AccessibleText>() {
724 @Override
725 public AccessibleText call() throws Exception {
726 return ac.getAccessibleText();
727 }
728 }, ac);
729 if (null != at) {
730 int charCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
731 @Override
732 public Integer call() throws Exception {
733 return at.getCharCount();
734 }
735 }, ac);
736 String text = getAccessibleTextRangeFromContext (ac, 0, charCount);
737 if (null != text) {
738 debugString ("bk -- The Virtual Accessible Name was obtained from the Accessible Text of the LABEL object.");
739 references.increment (text);
740 return text;
741 }
742 }
743 /*
744 Does the label support the Accessible Icon Interface?
745 */
746 debugString ("bk -- Attempting to obtain the Virtual Accessible Name from the Accessible Icon information.");
747 final AccessibleIcon [] ai = InvocationUtils.invokeAndWait(new Callable<AccessibleIcon[]>() {
748 @Override
749 public AccessibleIcon[] call() throws Exception {
750 return ac.getAccessibleIcon();
751 }
752 }, ac);
753 if ( (null != ai) && (ai.length > 0) ) {
754 String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
755 @Override
756 public String call() throws Exception {
757 return ai[0].getAccessibleIconDescription();
758 }
759 }, ac);
760 if (iconDescription != null){
761 debugString ("bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the LABEL object.");
762 references.increment (iconDescription);
763 return iconDescription;
764 }
765 } else {
766 parentContext = getAccessibleParentFromContext (ac);
767 if ( null != parentContext ) {
768 final AccessibleContext parentContextInnerTemp = parentContext;
769 parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
770 @Override
771 public AccessibleRole call() throws Exception {
772 return parentContextInnerTemp.getAccessibleRole();
773 }
774 }, ac);
775 if ( AccessibleRole.TABLE == parentRole ) {
776 int indexInParent = InvocationUtils.invokeAndWait(new Callable<Integer>() {
777 @Override
778 public Integer call() throws Exception {
779 return ac.getAccessibleIndexInParent();
780 }
781 }, ac);
782 final AccessibleContext acTableCell = getAccessibleChildFromContext (parentContext, indexInParent);
783 debugString ("bk -- Making a second attempt to obtain the Virtual Accessible Name from the Accessible Icon information for the Table Cell.");
784 if (acTableCell != null) {
785 final AccessibleIcon [] aiRet =InvocationUtils.invokeAndWait(new Callable<AccessibleIcon[]>() {
786 @Override
787 public AccessibleIcon[] call() throws Exception {
788 return acTableCell.getAccessibleIcon();
789 }
790 }, ac);
791 if ( (null != aiRet) && (aiRet.length > 0) ) {
792 String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
793 @Override
794 public String call() throws Exception {
795 return aiRet[0].getAccessibleIconDescription();
796 }
797 }, ac);
798 if (iconDescription != null){
799 debugString ("bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the Table Cell object.");
800 references.increment (iconDescription);
801 return iconDescription;
802 }
803 }
804 }
805 }
806 }
807 }
808 } else if ( (AccessibleRole.TOGGLE_BUTTON == role) ||
809 (AccessibleRole.PUSH_BUTTON == role) ) {
810 /*
811 Does the button support the Accessible Icon Interface?
812 */
813 debugString ("bk -- Attempting to obtain the Virtual Accessible Name from the Accessible Icon information.");
814 final AccessibleIcon [] ai = InvocationUtils.invokeAndWait(new Callable<AccessibleIcon[]>() {
815 @Override
816 public AccessibleIcon[] call() throws Exception {
817 return ac.getAccessibleIcon();
818 }
819 }, ac);
820 if ( (null != ai) && (ai.length > 0) ) {
821 String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
822 @Override
823 public String call() throws Exception {
824 return ai[0].getAccessibleIconDescription();
825 }
826 }, ac);
827 if (iconDescription != null){
828 debugString ("bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the TOGGLE_BUTTON or PUSH_BUTTON object.");
829 references.increment (iconDescription);
830 return iconDescription;
831 }
832 }
833 } else if ( AccessibleRole.CHECK_BOX == role ) {
834 /*
835 NOTE: The only case I know of in which a check box does not
836 have a name is when that check box is contained in a table.
837
838 In this case it would be appropriate to use the display string
839 of the check box object as the name (in US English the display
840 string is typically either "true" or "false").
841
842 I am using the AccessibleValue interface to obtain the display
843 string of the check box. If the Accessible Value is 1, I am
844 returning Boolean.TRUE.toString (), If the Accessible Value is
845 0, I am returning Boolean.FALSE.toString (). If the Accessible
846 Value is some other number, I will return the display string of
847 the current numerical value of the check box.
848 */
888 @Override
889 public String call() throws Exception {
890 return parentContextOuterTemp.getAccessibleName();
891 }
892 }, ac);
893 String parentDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
894 @Override
895 public String call() throws Exception {
896 return parentContextOuterTemp.getAccessibleDescription();
897 }
898 }, ac);
899
900 /*
901 Step 4:
902 =======
903 Special case for Slider Bar objects.
904 */
905 if ( (AccessibleRole.SLIDER == role) &&
906 (AccessibleRole.PANEL == parentRole) &&
907 (null != parentName) ) {
908 debugString ("bk -- The Virtual Accessible Name was obtained from the Accessible Name of the SLIDER object's parent object.");
909 references.increment (parentName);
910 return parentName;
911 }
912
913 boolean bIsEditCombo = false;
914
915 AccessibleContext testContext = ac;
916 /*
917 Step 5:
918 =======
919 Special case for Edit Combo Boxes
920 */
921 if ( (AccessibleRole.TEXT == role) &&
922 (AccessibleRole.COMBO_BOX == parentRole) ) {
923 bIsEditCombo = true;
924 if (null != parentName) {
925 debugString ("bk -- The Virtual Accessible Name for this Edit Combo box was obtained from the Accessible Name of the object's parent object.");
926 references.increment (parentName);
927 return parentName;
928 } else if (null != parentDescription) {
929 debugString ("bk -- The Virtual Accessible Name for this Edit Combo box was obtained from the Accessible Description of the object's parent object.");
930 references.increment (parentDescription);
931 return parentDescription;
932 }
933 testContext = parentContext;
934 parentRole = AccessibleRole.UNKNOWN;
935 parentContext = getAccessibleParentFromContext (testContext);
936 if ( null != parentContext ) {
937 final AccessibleContext parentContextInnerTemp = parentContext;
938 parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
939 @Override
940 public AccessibleRole call() throws Exception {
941 return parentContextInnerTemp.getAccessibleRole();
942 }
943 }, ac);
944 }
945 }
946
947 /*
948 Step 6:
949 =======
952 */
953 {
954 final AccessibleContext parentContextTempInner = parentContext;
955 AccessibleRelationSet ars = InvocationUtils.invokeAndWait(new Callable<AccessibleRelationSet>() {
956 @Override
957 public AccessibleRelationSet call() throws Exception {
958 return parentContextTempInner.getAccessibleRelationSet();
959 }
960 }, ac);
961 if ( ars != null && (ars.size () > 0) && (ars.contains (AccessibleRelation.LABELED_BY)) ) {
962 AccessibleRelation labeledByRelation = ars.get (AccessibleRelation.LABELED_BY);
963 if (labeledByRelation != null) {
964 Object [] targets = labeledByRelation.getTarget ();
965 Object o = targets [0];
966 if (o instanceof Accessible) {
967 AccessibleContext labelContext = ((Accessible)o).getAccessibleContext ();
968 if (labelContext != null) {
969 String labelName = labelContext.getAccessibleName ();
970 String labelDescription = labelContext.getAccessibleDescription ();
971 if (null != labelName) {
972 debugString ("bk -- The Virtual Accessible Name was obtained using the LABELED_BY AccessibleRelation -- Name Case.");
973 references.increment (labelName);
974 return labelName;
975 } else if (null != labelDescription) {
976 debugString ("bk -- The Virtual Accessible Name was obtained using the LABELED_BY AccessibleRelation -- Description Case.");
977 references.increment (labelDescription);
978 return labelDescription;
979 }
980 }
981 }
982 }
983 }
984 }
985
986 //Note: add AccessibleContext to use InvocationUtils.invokeAndWait
987 /*
988 Step 7:
989 =======
990 Search for a label object that is positioned either just to the left
991 or just above the object and get the Accessible Name of the Label
992 object.
993 */
994 int testIndexMax = 0;
995 int testX = 0;
996 int testY = 0;
1051 AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
1052 @Override
1053 public AccessibleRole call() throws Exception {
1054 return childContext.getAccessibleRole();
1055 }
1056 }, ac);
1057 if ( AccessibleRole.LABEL == childRole ) {
1058 childX = getAccessibleXcoordFromContext (childContext);
1059 childY = getAccessibleYcoordFromContext (childContext);
1060 childWidth = getAccessibleWidthFromContext (childContext);
1061 childHeight = getAccessibleHeightFromContext (childContext);
1062 if ( (childX < testX) &&
1063 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1064 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1065 @Override
1066 public String call() throws Exception {
1067 return childContext.getAccessibleName();
1068 }
1069 }, ac);
1070 if ( null != childName ) {
1071 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned to the left of the object.");
1072 references.increment (childName);
1073 return childName;
1074 }
1075 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1076 @Override
1077 public String call() throws Exception {
1078 return childContext.getAccessibleDescription();
1079 }
1080 }, ac);
1081 if ( null != childDescription ) {
1082 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned to the left of the object.");
1083 references.increment (childDescription);
1084 return childDescription;
1085 }
1086 } else if ( (childY < targetY) &&
1087 ((childX <= targetX) && (targetX <= (childX + childWidth))) ) {
1088 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1089 @Override
1090 public String call() throws Exception {
1091 return childContext.getAccessibleName();
1092 }
1093 }, ac);
1094 if ( null != childName ) {
1095 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned above the object.");
1096 references.increment (childName);
1097 return childName;
1098 }
1099 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1100 @Override
1101 public String call() throws Exception {
1102 return childContext.getAccessibleDescription();
1103 }
1104 }, ac);
1105 if ( null != childDescription ) {
1106 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned above the object.");
1107 references.increment (childDescription);
1108 return childDescription;
1109 }
1110 }
1111 }
1112 }
1113 }
1114 childIndex --;
1115 }
1116 childIndex = testIndex + 1;
1117 while (childIndex <= testIndexMax) {
1118 final int childIndexTemp = childIndex;
1119 final AccessibleContext parentContextInnerTemp = parentContext;
1120 final Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
1121 @Override
1122 public Accessible call() throws Exception {
1123 return parentContextInnerTemp.getAccessibleChild(childIndexTemp);
1124 }
1125 }, ac);
1126 if ( null != child ) {
1134 AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
1135 @Override
1136 public AccessibleRole call() throws Exception {
1137 return childContext.getAccessibleRole();
1138 }
1139 }, ac);
1140 if ( AccessibleRole.LABEL == childRole ) {
1141 childX = getAccessibleXcoordFromContext (childContext);
1142 childY = getAccessibleYcoordFromContext (childContext);
1143 childWidth = getAccessibleWidthFromContext (childContext);
1144 childHeight = getAccessibleHeightFromContext (childContext);
1145 if ( (childX < testX) &&
1146 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1147 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1148 @Override
1149 public String call() throws Exception {
1150 return childContext.getAccessibleName();
1151 }
1152 }, ac);
1153 if ( null != childName ) {
1154 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned to the left of the object.");
1155 references.increment (childName);
1156 return childName;
1157 }
1158 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1159 @Override
1160 public String call() throws Exception {
1161 return childContext.getAccessibleDescription();
1162 }
1163 }, ac);
1164 if ( null != childDescription ) {
1165 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned to the left of the object.");
1166 references.increment (childDescription);
1167 return childDescription;
1168 }
1169 } else if ( (childY < targetY) &&
1170 ((childX <= targetX) && (targetX <= (childX + childWidth))) ) {
1171 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1172 @Override
1173 public String call() throws Exception {
1174 return childContext.getAccessibleName();
1175 }
1176 }, ac);
1177 if ( null != childName ) {
1178 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned above the object.");
1179 references.increment (childName);
1180 return childName;
1181 }
1182 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1183 @Override
1184 public String call() throws Exception {
1185 return childContext.getAccessibleDescription();
1186 }
1187 }, ac);
1188 if ( null != childDescription ) {
1189 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned above the object.");
1190 references.increment (childDescription);
1191 return childDescription;
1192 }
1193 }
1194 }
1195 }
1196 }
1197 childIndex ++;
1198 }
1199 /*
1200 Step 8:
1201 =======
1202 Special case for combo boxes and text objects, based on a
1203 similar special case I found in some of our internal JAWS code.
1204
1205 Search for a button object that is positioned either just to the left
1206 or just above the object and get the Accessible Name of the button
1207 object.
1208 */
1209 if ( (AccessibleRole.TEXT == role) ||
1231 @Override
1232 public AccessibleRole call() throws Exception {
1233 return childContext.getAccessibleRole();
1234 }
1235 }, ac);
1236 if ( ( AccessibleRole.PUSH_BUTTON == childRole ) ||
1237 ( AccessibleRole.TOGGLE_BUTTON == childRole )) {
1238 childX = getAccessibleXcoordFromContext (childContext);
1239 childY = getAccessibleYcoordFromContext (childContext);
1240 childWidth = getAccessibleWidthFromContext (childContext);
1241 childHeight = getAccessibleHeightFromContext (childContext);
1242 if ( (childX < testX) &&
1243 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1244 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1245 @Override
1246 public String call() throws Exception {
1247 return childContext.getAccessibleName();
1248 }
1249 }, ac);
1250 if ( null != childName ) {
1251 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Name of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1252 references.increment (childName);
1253 return childName;
1254 }
1255 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1256 @Override
1257 public String call() throws Exception {
1258 return childContext.getAccessibleDescription();
1259 }
1260 }, ac);
1261 if ( null != childDescription ) {
1262 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Description of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1263 references.increment (childDescription);
1264 return childDescription;
1265 }
1266 }
1267 }
1268 }
1269 }
1270 childIndex --;
1271 }
1272 childIndex = testIndex + 1;
1273 while (childIndex <= testIndexMax) {
1274 final int childIndexTemp = childIndex;
1275 final AccessibleContext parentContextInnerTemp = parentContext;
1276 final Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
1277 @Override
1278 public Accessible call() throws Exception {
1279 return parentContextInnerTemp.getAccessibleChild(childIndexTemp);
1280 }
1281 }, ac);
1282 if ( null != child ) {
1291 @Override
1292 public AccessibleRole call() throws Exception {
1293 return childContext.getAccessibleRole();
1294 }
1295 }, ac);
1296 if ( ( AccessibleRole.PUSH_BUTTON == childRole ) ||
1297 ( AccessibleRole.TOGGLE_BUTTON == childRole ) ) {
1298 childX = getAccessibleXcoordFromContext (childContext);
1299 childY = getAccessibleYcoordFromContext (childContext);
1300 childWidth = getAccessibleWidthFromContext (childContext);
1301 childHeight = getAccessibleHeightFromContext (childContext);
1302 if ( (childX < testX) &&
1303 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1304 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1305 @Override
1306 public String call() throws Exception {
1307 return childContext.getAccessibleName();
1308 }
1309 }, ac);
1310 if ( null != childName ) {
1311 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Name of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1312 references.increment (childName);
1313 return childName;
1314 }
1315 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1316 @Override
1317 public String call() throws Exception {
1318 return childContext.getAccessibleDescription();
1319 }
1320 }, ac);
1321 if ( null != childDescription ) {
1322 debugString ("bk -- The Virtual Accessible Name was obtained from Accessible Description of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1323 references.increment (childDescription);
1324 return childDescription;
1325 }
1326 }
1327 }
1328 }
1329 }
1330 childIndex ++;
1331 }
1332 }
1333 return null;
1334 } else {
1335 debugString ("AccessBridge::getVirtualAccessibleNameFromContext error - ac == null.");
1336 return null;
1337 }
1338 }
1339
1340 /**
1341 * returns the AccessibleDescription from an AccessibleContext
1342 */
1343 private String getAccessibleDescriptionFromContext(final AccessibleContext ac) {
1344 if (ac != null) {
1345 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1346 @Override
1347 public String call() throws Exception {
1348 return ac.getAccessibleDescription();
1349 }
1350 }, ac);
1351 if (s != null) {
1352 references.increment(s);
1353 debugString("Returning AccessibleDescription from Context: " + s);
1354 return s;
1355 }
1356 } else {
1357 debugString("getAccessibleDescriptionFromContext; ac = null");
1358 }
1359 return null;
1360 }
1361
1362 /**
1363 * returns the AccessibleRole from an AccessibleContext
1364 */
1365 private String getAccessibleRoleStringFromContext(final AccessibleContext ac) {
1366 if (ac != null) {
1367 AccessibleRole role = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
1368 @Override
1369 public AccessibleRole call() throws Exception {
1370 return ac.getAccessibleRole();
1371 }
1372 }, ac);
1373 if (role != null) {
1374 String s = role.toDisplayString(Locale.US);
1375 if (s != null) {
1376 references.increment(s);
1377 debugString("Returning AccessibleRole from Context: " + s);
1378 return s;
1379 }
1380 }
1381 } else {
1382 debugString("getAccessibleRoleStringFromContext; ac = null");
1383 }
1384 return null;
1385 }
1386
1387 /**
1388 * return the AccessibleRole from an AccessibleContext in the en_US locale
1389 */
1390 private String getAccessibleRoleStringFromContext_en_US(final AccessibleContext ac) {
1391 return getAccessibleRoleStringFromContext(ac);
1392 }
1393
1394 /**
1395 * return the AccessibleStates from an AccessibleContext
1396 */
1397 private String getAccessibleStatesStringFromContext(final AccessibleContext ac) {
1398 if (ac != null) {
1399 AccessibleStateSet stateSet = InvocationUtils.invokeAndWait(new Callable<AccessibleStateSet>() {
1400 @Override
1401 public AccessibleStateSet call() throws Exception {
1402 return ac.getAccessibleStateSet();
1403 }
1404 }, ac);
1405 if (stateSet != null) {
1406 String s = stateSet.toString();
1407 if (s != null &&
1408 s.indexOf(AccessibleState.MANAGES_DESCENDANTS.toDisplayString(Locale.US)) == -1) {
1409 // Indicate whether this component manages its own
1410 // children
1411 AccessibleRole role = InvocationUtils.invokeAndWait(() -> {
1412 return ac.getAccessibleRole();
1413 }, ac);
1414 if (role == AccessibleRole.LIST ||
1415 role == AccessibleRole.TABLE ||
1416 role == AccessibleRole.TREE) {
1417 s += ",";
1418 s += AccessibleState.MANAGES_DESCENDANTS.toDisplayString(Locale.US);
1419 }
1420 references.increment(s);
1421 debugString("Returning AccessibleStateSet from Context: " + s);
1422 return s;
1423 }
1424 }
1425 } else {
1426 debugString("getAccessibleStatesStringFromContext; ac = null");
1427 }
1428 return null;
1429 }
1430
1431 /**
1432 * returns the AccessibleStates from an AccessibleContext in the en_US locale
1433 */
1434 private String getAccessibleStatesStringFromContext_en_US(final AccessibleContext ac) {
1435 if (ac != null) {
1436 AccessibleStateSet stateSet = InvocationUtils.invokeAndWait(new Callable<AccessibleStateSet>() {
1437 @Override
1438 public AccessibleStateSet call() throws Exception {
1439 return ac.getAccessibleStateSet();
1440 }
1441 }, ac);
1442 if (stateSet != null) {
1443 String s = "";
1444 AccessibleState[] states = stateSet.toArray();
1445 if (states != null && states.length > 0) {
1446 s = states[0].toDisplayString(Locale.US);
1447 for (int i = 1; i < states.length; i++) {
1448 s = s + "," + states[i].toDisplayString(Locale.US);
1449 }
1450 }
1451 references.increment(s);
1452 debugString("Returning AccessibleStateSet en_US from Context: " + s);
1453 return s;
1454 }
1455 }
1456 debugString("getAccessibleStatesStringFromContext; ac = null");
1457 return null;
1458 }
1459
1460 /**
1461 * returns the AccessibleParent from an AccessibleContext
1462 */
1463 private AccessibleContext getAccessibleParentFromContext(final AccessibleContext ac) {
1464 if (ac==null)
1465 return null;
1466 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
1467 @Override
1468 public AccessibleContext call() throws Exception {
1469 Accessible a = ac.getAccessibleParent();
1470 if (a != null) {
1471 AccessibleContext apc = a.getAccessibleContext();
1472 if (apc != null) {
1473 return apc;
1474 }
1475 }
1476 return null;
1590 r.y = p.y;
1591 return r;
1592 }
1593 } catch (Exception e) {
1594 return null;
1595 }
1596 }
1597 }
1598 return null;
1599 }
1600 }, ac);
1601 }
1602
1603 /**
1604 * returns the AccessibleComponent x-coord from an AccessibleContext
1605 */
1606 private int getAccessibleXcoordFromContext(AccessibleContext ac) {
1607 if (ac != null) {
1608 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1609 if (r != null) {
1610 debugString(" - Returning Accessible x coord from Context: " + r.x);
1611 return r.x;
1612 }
1613 } else {
1614 debugString("getAccessibleXcoordFromContext ac = null");
1615 }
1616 return -1;
1617 }
1618
1619 /**
1620 * returns the AccessibleComponent y-coord from an AccessibleContext
1621 */
1622 private int getAccessibleYcoordFromContext(AccessibleContext ac) {
1623 debugString("getAccessibleYcoordFromContext() called");
1624 if (ac != null) {
1625 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1626 if (r != null) {
1627 return r.y;
1628 }
1629 } else {
1630 debugString("getAccessibleYcoordFromContext; ac = null");
1631 }
1632 return -1;
1633 }
1634
1635 /**
1636 * returns the AccessibleComponent height from an AccessibleContext
1637 */
1638 private int getAccessibleHeightFromContext(AccessibleContext ac) {
1639 if (ac != null) {
1640 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1641 if (r != null) {
1642 return r.height;
1643 }
1644 } else {
1645 debugString("getAccessibleHeightFromContext; ac = null");
1646 }
1647 return -1;
1648 }
1649
1650 /**
1651 * returns the AccessibleComponent width from an AccessibleContext
1652 */
1653 private int getAccessibleWidthFromContext(AccessibleContext ac) {
1654 if (ac != null) {
1655 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1656 if (r != null) {
1657 return r.width;
1658 }
1659 } else {
1660 debugString("getAccessibleWidthFromContext; ac = null");
1661 }
1662 return -1;
1663 }
1664
1665
1666 /**
1667 * returns the AccessibleComponent from an AccessibleContext
1668 */
1669 private AccessibleComponent getAccessibleComponentFromContext(AccessibleContext ac) {
1670 if (ac != null) {
1671 AccessibleComponent acmp = InvocationUtils.invokeAndWait(() -> {
1672 return ac.getAccessibleComponent();
1673 }, ac);
1674 if (acmp != null) {
1675 debugString("Returning AccessibleComponent Context");
1676 return acmp;
1677 }
1678 } else {
1679 debugString("getAccessibleComponentFromContext; ac = null");
1680 }
1681 return null;
1682 }
1683
1684 /**
1685 * returns the AccessibleAction from an AccessibleContext
1686 */
1687 private AccessibleAction getAccessibleActionFromContext(final AccessibleContext ac) {
1688 debugString("Returning AccessibleAction Context");
1689 return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleAction>() {
1690 @Override
1691 public AccessibleAction call() throws Exception {
1692 return ac.getAccessibleAction();
1693 }
1694 }, ac);
1695 }
1696
1697 /**
1698 * returns the AccessibleSelection from an AccessibleContext
1699 */
1700 private AccessibleSelection getAccessibleSelectionFromContext(final AccessibleContext ac) {
1701 return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleSelection>() {
1702 @Override
1703 public AccessibleSelection call() throws Exception {
1704 return ac.getAccessibleSelection();
1705 }
1706 }, ac);
1707 }
1708
1720
1721 /**
1722 * return the AccessibleComponent from an AccessibleContext
1723 */
1724 private AccessibleValue getAccessibleValueFromContext(final AccessibleContext ac) {
1725 return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleValue>() {
1726 @Override
1727 public AccessibleValue call() throws Exception {
1728 return ac.getAccessibleValue();
1729 }
1730 }, ac);
1731 }
1732
1733 /* ===== AccessibleText methods ===== */
1734
1735 /**
1736 * returns the bounding rectangle for the text cursor
1737 * XXX
1738 */
1739 private Rectangle getCaretLocation(final AccessibleContext ac) {
1740 debugString("getCaretLocation");
1741 if (ac==null)
1742 return null;
1743 return InvocationUtils.invokeAndWait(new Callable<Rectangle>() {
1744 @Override
1745 public Rectangle call() throws Exception {
1746 // workaround for JAAPI not returning cursor bounding rectangle
1747 Rectangle r = null;
1748 Accessible parent = ac.getAccessibleParent();
1749 if (parent instanceof Accessible) {
1750 int indexInParent = ac.getAccessibleIndexInParent();
1751 Accessible child =
1752 parent.getAccessibleContext().getAccessibleChild(indexInParent);
1753
1754 if (child instanceof JTextComponent) {
1755 JTextComponent text = (JTextComponent) child;
1756 try {
1757 r = text.modelToView2D(text.getCaretPosition()).getBounds();
1758 if (r != null) {
1759 Point p = text.getLocationOnScreen();
1760 r.translate(p.x, p.y);
1841 if (ac==null)
1842 return -1;
1843 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
1844 @Override
1845 public Integer call() throws Exception {
1846 AccessibleText at = ac.getAccessibleText();
1847 if (at != null) {
1848 return at.getCaretPosition();
1849 }
1850 return -1;
1851 }
1852 }, ac);
1853 }
1854
1855 /**
1856 * Return the index at a specific point from an AccessibleContext
1857 * Point(x, y) is in screen coordinates.
1858 */
1859 private int getAccessibleIndexAtPointFromContext(final AccessibleContext ac,
1860 final int x, final int y) {
1861 debugString("getAccessibleIndexAtPointFromContext: x = "+x+"; y = "+y);
1862 if (ac==null)
1863 return -1;
1864 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
1865 @Override
1866 public Integer call() throws Exception {
1867 AccessibleText at = ac.getAccessibleText();
1868 AccessibleComponent acomp = ac.getAccessibleComponent();
1869 if (at != null && acomp != null) {
1870 // Convert x and y from screen coordinates to
1871 // local coordinates.
1872 try {
1873 Point p = acomp.getLocationOnScreen();
1874 int x1, y1;
1875 if (p != null) {
1876 x1 = x - p.x;
1877 if (x1 < 0) {
1878 x1 = 0;
1879 }
1880 y1 = y - p.y;
1881 if (y1 < 0) {
1895 }
1896
1897 /**
1898 * return the letter at a specific point from an AccessibleContext
1899 */
1900 private String getAccessibleLetterAtIndexFromContext(final AccessibleContext ac, final int index) {
1901 if (ac != null) {
1902 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1903 @Override
1904 public String call() throws Exception {
1905 AccessibleText at = ac.getAccessibleText();
1906 if (at == null) return null;
1907 return at.getAtIndex(AccessibleText.CHARACTER, index);
1908 }
1909 }, ac);
1910 if (s != null) {
1911 references.increment(s);
1912 return s;
1913 }
1914 } else {
1915 debugString("getAccessibleLetterAtIndexFromContext; ac = null");
1916 }
1917 return null;
1918 }
1919
1920 /**
1921 * return the word at a specific point from an AccessibleContext
1922 */
1923 private String getAccessibleWordAtIndexFromContext(final AccessibleContext ac, final int index) {
1924 if (ac != null) {
1925 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1926 @Override
1927 public String call() throws Exception {
1928 AccessibleText at = ac.getAccessibleText();
1929 if (at == null) return null;
1930 return at.getAtIndex(AccessibleText.WORD, index);
1931 }
1932 }, ac);
1933 if (s != null) {
1934 references.increment(s);
1935 return s;
1936 }
1937 } else {
1938 debugString("getAccessibleWordAtIndexFromContext; ac = null");
1939 }
1940 return null;
1941 }
1942
1943 /**
1944 * return the sentence at a specific point from an AccessibleContext
1945 */
1946 private String getAccessibleSentenceAtIndexFromContext(final AccessibleContext ac, final int index) {
1947 if (ac != null) {
1948 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1949 @Override
1950 public String call() throws Exception {
1951 AccessibleText at = ac.getAccessibleText();
1952 if (at == null) return null;
1953 return at.getAtIndex(AccessibleText.SENTENCE, index);
1954 }
1955 }, ac);
1956 if (s != null) {
1957 references.increment(s);
1958 return s;
1959 }
1960 } else {
1961 debugString("getAccessibleSentenceAtIndexFromContext; ac = null");
1962 }
1963 return null;
1964 }
1965
1966 /**
1967 * return the text selection start from an AccessibleContext
1968 */
1969 private int getAccessibleTextSelectionStartFromContext(final AccessibleContext ac) {
1970 if (ac == null) return -1;
1971 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
1972 @Override
1973 public Integer call() throws Exception {
1974 AccessibleText at = ac.getAccessibleText();
1975 if (at != null) {
1976 return at.getSelectionStart();
1977 }
1978 return -1;
1979 }
1980 }, ac);
1981 }
1999 }
2000
2001 /**
2002 * return the selected text from an AccessibleContext
2003 */
2004 private String getAccessibleTextSelectedTextFromContext(final AccessibleContext ac) {
2005 if (ac != null) {
2006 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
2007 @Override
2008 public String call() throws Exception {
2009 AccessibleText at = ac.getAccessibleText();
2010 if (at == null) return null;
2011 return at.getSelectedText();
2012 }
2013 }, ac);
2014 if (s != null) {
2015 references.increment(s);
2016 return s;
2017 }
2018 } else {
2019 debugString("getAccessibleTextSelectedTextFromContext; ac = null");
2020 }
2021 return null;
2022 }
2023
2024 /**
2025 * return the attribute string at a given index from an AccessibleContext
2026 */
2027 private String getAccessibleAttributesAtIndexFromContext(final AccessibleContext ac,
2028 final int index) {
2029 if (ac == null)
2030 return null;
2031 AttributeSet as = InvocationUtils.invokeAndWait(new Callable<AttributeSet>() {
2032 @Override
2033 public AttributeSet call() throws Exception {
2034 AccessibleText at = ac.getAccessibleText();
2035 if (at != null) {
2036 return at.getCharacterAttribute(index);
2037 }
2038 return null;
2039 }
2260 }
2261 }, ac);
2262 Rectangle acRect = getAccessibleBoundsOnScreenFromContext(ac);
2263 if (r != null && acRect != null) {
2264 r.translate(acRect.x, acRect.y);
2265 return r;
2266 }
2267 return null;
2268 }
2269
2270 /**
2271 * return the AccessibleText character x-coord at index from an AccessibleContext
2272 */
2273 private int getAccessibleXcoordTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2274 if (ac != null) {
2275 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2276 if (r != null) {
2277 return r.x;
2278 }
2279 } else {
2280 debugString("getAccessibleXcoordTextRectAtIndexFromContext; ac = null");
2281 }
2282 return -1;
2283 }
2284
2285 /**
2286 * return the AccessibleText character y-coord at index from an AccessibleContext
2287 */
2288 private int getAccessibleYcoordTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2289 if (ac != null) {
2290 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2291 if (r != null) {
2292 return r.y;
2293 }
2294 } else {
2295 debugString("getAccessibleYcoordTextRectAtIndexFromContext; ac = null");
2296 }
2297 return -1;
2298 }
2299
2300 /**
2301 * return the AccessibleText character height at index from an AccessibleContext
2302 */
2303 private int getAccessibleHeightTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2304 if (ac != null) {
2305 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2306 if (r != null) {
2307 return r.height;
2308 }
2309 } else {
2310 debugString("getAccessibleHeightTextRectAtIndexFromContext; ac = null");
2311 }
2312 return -1;
2313 }
2314
2315 /**
2316 * return the AccessibleText character width at index from an AccessibleContext
2317 */
2318 private int getAccessibleWidthTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2319 if (ac != null) {
2320 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2321 if (r != null) {
2322 return r.width;
2323 }
2324 } else {
2325 debugString("getAccessibleWidthTextRectAtIndexFromContext; ac = null");
2326 }
2327 return -1;
2328 }
2329
2330 /* ===== AttributeSet methods for AccessibleText ===== */
2331
2332 /**
2333 * return the bold setting from an AttributeSet
2334 */
2335 private boolean getBoldFromAttributeSet(AttributeSet as) {
2336 if (as != null) {
2337 return StyleConstants.isBold(as);
2338 } else {
2339 debugString("getBoldFromAttributeSet; as = null");
2340 }
2341 return false;
2342 }
2343
2344 /**
2345 * return the italic setting from an AttributeSet
2346 */
2347 private boolean getItalicFromAttributeSet(AttributeSet as) {
2348 if (as != null) {
2349 return StyleConstants.isItalic(as);
2350 } else {
2351 debugString("getItalicFromAttributeSet; as = null");
2352 }
2353 return false;
2354 }
2355
2356 /**
2357 * return the underline setting from an AttributeSet
2358 */
2359 private boolean getUnderlineFromAttributeSet(AttributeSet as) {
2360 if (as != null) {
2361 return StyleConstants.isUnderline(as);
2362 } else {
2363 debugString("getUnderlineFromAttributeSet; as = null");
2364 }
2365 return false;
2366 }
2367
2368 /**
2369 * return the strikethrough setting from an AttributeSet
2370 */
2371 private boolean getStrikethroughFromAttributeSet(AttributeSet as) {
2372 if (as != null) {
2373 return StyleConstants.isStrikeThrough(as);
2374 } else {
2375 debugString("getStrikethroughFromAttributeSet; as = null");
2376 }
2377 return false;
2378 }
2379
2380 /**
2381 * return the superscript setting from an AttributeSet
2382 */
2383 private boolean getSuperscriptFromAttributeSet(AttributeSet as) {
2384 if (as != null) {
2385 return StyleConstants.isSuperscript(as);
2386 } else {
2387 debugString("getSuperscriptFromAttributeSet; as = null");
2388 }
2389 return false;
2390 }
2391
2392 /**
2393 * return the subscript setting from an AttributeSet
2394 */
2395 private boolean getSubscriptFromAttributeSet(AttributeSet as) {
2396 if (as != null) {
2397 return StyleConstants.isSubscript(as);
2398 } else {
2399 debugString("getSubscriptFromAttributeSet; as = null");
2400 }
2401 return false;
2402 }
2403
2404 /**
2405 * return the background color from an AttributeSet
2406 */
2407 private String getBackgroundColorFromAttributeSet(AttributeSet as) {
2408 if (as != null) {
2409 String s = StyleConstants.getBackground(as).toString();
2410 if (s != null) {
2411 references.increment(s);
2412 return s;
2413 }
2414 } else {
2415 debugString("getBackgroundColorFromAttributeSet; as = null");
2416 }
2417 return null;
2418 }
2419
2420 /**
2421 * return the foreground color from an AttributeSet
2422 */
2423 private String getForegroundColorFromAttributeSet(AttributeSet as) {
2424 if (as != null) {
2425 String s = StyleConstants.getForeground(as).toString();
2426 if (s != null) {
2427 references.increment(s);
2428 return s;
2429 }
2430 } else {
2431 debugString("getForegroundColorFromAttributeSet; as = null");
2432 }
2433 return null;
2434 }
2435
2436 /**
2437 * return the font family from an AttributeSet
2438 */
2439 private String getFontFamilyFromAttributeSet(AttributeSet as) {
2440 if (as != null) {
2441 String s = StyleConstants.getFontFamily(as).toString();
2442 if (s != null) {
2443 references.increment(s);
2444 return s;
2445 }
2446 } else {
2447 debugString("getFontFamilyFromAttributeSet; as = null");
2448 }
2449 return null;
2450 }
2451
2452 /**
2453 * return the font size from an AttributeSet
2454 */
2455 private int getFontSizeFromAttributeSet(AttributeSet as) {
2456 if (as != null) {
2457 return StyleConstants.getFontSize(as);
2458 } else {
2459 debugString("getFontSizeFromAttributeSet; as = null");
2460 }
2461 return -1;
2462 }
2463
2464 /**
2465 * return the alignment from an AttributeSet
2466 */
2467 private int getAlignmentFromAttributeSet(AttributeSet as) {
2468 if (as != null) {
2469 return StyleConstants.getAlignment(as);
2470 } else {
2471 debugString("getAlignmentFromAttributeSet; as = null");
2472 }
2473 return -1;
2474 }
2475
2476 /**
2477 * return the BiDi level from an AttributeSet
2478 */
2479 private int getBidiLevelFromAttributeSet(AttributeSet as) {
2480 if (as != null) {
2481 return StyleConstants.getBidiLevel(as);
2482 } else {
2483 debugString("getBidiLevelFromAttributeSet; as = null");
2484 }
2485 return -1;
2486 }
2487
2488
2489 /**
2490 * return the first line indent from an AttributeSet
2491 */
2492 private float getFirstLineIndentFromAttributeSet(AttributeSet as) {
2493 if (as != null) {
2494 return StyleConstants.getFirstLineIndent(as);
2495 } else {
2496 debugString("getFirstLineIndentFromAttributeSet; as = null");
2497 }
2498 return -1;
2499 }
2500
2501 /**
2502 * return the left indent from an AttributeSet
2503 */
2504 private float getLeftIndentFromAttributeSet(AttributeSet as) {
2505 if (as != null) {
2506 return StyleConstants.getLeftIndent(as);
2507 } else {
2508 debugString("getLeftIndentFromAttributeSet; as = null");
2509 }
2510 return -1;
2511 }
2512
2513 /**
2514 * return the right indent from an AttributeSet
2515 */
2516 private float getRightIndentFromAttributeSet(AttributeSet as) {
2517 if (as != null) {
2518 return StyleConstants.getRightIndent(as);
2519 } else {
2520 debugString("getRightIndentFromAttributeSet; as = null");
2521 }
2522 return -1;
2523 }
2524
2525 /**
2526 * return the line spacing from an AttributeSet
2527 */
2528 private float getLineSpacingFromAttributeSet(AttributeSet as) {
2529 if (as != null) {
2530 return StyleConstants.getLineSpacing(as);
2531 } else {
2532 debugString("getLineSpacingFromAttributeSet; as = null");
2533 }
2534 return -1;
2535 }
2536
2537 /**
2538 * return the space above from an AttributeSet
2539 */
2540 private float getSpaceAboveFromAttributeSet(AttributeSet as) {
2541 if (as != null) {
2542 return StyleConstants.getSpaceAbove(as);
2543 } else {
2544 debugString("getSpaceAboveFromAttributeSet; as = null");
2545 }
2546 return -1;
2547 }
2548
2549 /**
2550 * return the space below from an AttributeSet
2551 */
2552 private float getSpaceBelowFromAttributeSet(AttributeSet as) {
2553 if (as != null) {
2554 return StyleConstants.getSpaceBelow(as);
2555 } else {
2556 debugString("getSpaceBelowFromAttributeSet; as = null");
2557 }
2558 return -1;
2559 }
2560
2561 /**
2562 * Enumerate all StyleConstants in the AttributeSet
2563 *
2564 * We need to check explicitly, 'cause of the HTML package conversion
2565 * mechanism (they may not be stored as StyleConstants, just translated
2566 * to them when asked).
2567 *
2568 * (Use convenience methods where they are defined...)
2569 *
2570 * Not checking the following (which the IBM SNS guidelines says
2571 * should be defined):
2572 * - ComponentElementName
2573 * - IconElementName
2574 * - NameAttribute
2575 * - ResolveAttribute
2576 */
2685 *
2686 */
2687 private String getCurrentAccessibleValueFromContext(final AccessibleContext ac) {
2688 if (ac != null) {
2689 final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() {
2690 @Override
2691 public Number call() throws Exception {
2692 AccessibleValue av = ac.getAccessibleValue();
2693 if (av == null) return null;
2694 return av.getCurrentAccessibleValue();
2695 }
2696 }, ac);
2697 if (value != null) {
2698 String s = value.toString();
2699 if (s != null) {
2700 references.increment(s);
2701 return s;
2702 }
2703 }
2704 } else {
2705 debugString("getCurrentAccessibleValueFromContext; ac = null");
2706 }
2707 return null;
2708 }
2709
2710 /**
2711 * return the AccessibleValue maximum value from an AccessibleContext
2712 * returned using a String 'cause the value is a java Number
2713 *
2714 */
2715 private String getMaximumAccessibleValueFromContext(final AccessibleContext ac) {
2716 if (ac != null) {
2717 final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() {
2718 @Override
2719 public Number call() throws Exception {
2720 AccessibleValue av = ac.getAccessibleValue();
2721 if (av == null) return null;
2722 return av.getMaximumAccessibleValue();
2723 }
2724 }, ac);
2725 if (value != null) {
2726 String s = value.toString();
2727 if (s != null) {
2728 references.increment(s);
2729 return s;
2730 }
2731 }
2732 } else {
2733 debugString("getMaximumAccessibleValueFromContext; ac = null");
2734 }
2735 return null;
2736 }
2737
2738 /**
2739 * return the AccessibleValue minimum value from an AccessibleContext
2740 * returned using a String 'cause the value is a java Number
2741 *
2742 */
2743 private String getMinimumAccessibleValueFromContext(final AccessibleContext ac) {
2744 if (ac != null) {
2745 final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() {
2746 @Override
2747 public Number call() throws Exception {
2748 AccessibleValue av = ac.getAccessibleValue();
2749 if (av == null) return null;
2750 return av.getMinimumAccessibleValue();
2751 }
2752 }, ac);
2753 if (value != null) {
2754 String s = value.toString();
2755 if (s != null) {
2756 references.increment(s);
2757 return s;
2758 }
2759 }
2760 } else {
2761 debugString("getMinimumAccessibleValueFromContext; ac = null");
2762 }
2763 return null;
2764 }
2765
2766
2767 /* ===== AccessibleSelection methods ===== */
2768
2769 /**
2770 * add to the AccessibleSelection of an AccessibleContext child i
2771 *
2772 */
2773 private void addAccessibleSelectionFromContext(final AccessibleContext ac, final int i) {
2774 try {
2775 InvocationUtils.invokeAndWait(new Callable<Object>() {
2776 @Override
2777 public Object call() throws Exception {
2778 if (ac != null) {
2779 AccessibleSelection as = ac.getAccessibleSelection();
2780 if (as != null) {
2781 as.addAccessibleSelection(i);
2924 return at;
2925 }
2926 }
2927 return null;
2928 }
2929 }, ac);
2930 }
2931
2932
2933 /*
2934 * returns the AccessibleContext that contains an AccessibleTable
2935 */
2936 private AccessibleContext getContextFromAccessibleTable(AccessibleTable at) {
2937 return hashtab.get(at);
2938 }
2939
2940 /*
2941 * returns the row count for an AccessibleTable
2942 */
2943 private int getAccessibleTableRowCount(final AccessibleContext ac) {
2944 debugString("##### getAccessibleTableRowCount");
2945 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
2946 @Override
2947 public Integer call() throws Exception {
2948 if (ac != null) {
2949 AccessibleTable at = ac.getAccessibleTable();
2950 if (at != null) {
2951 return at.getAccessibleRowCount();
2952 }
2953 }
2954 return -1;
2955 }
2956 }, ac);
2957 }
2958
2959 /*
2960 * returns the column count for an AccessibleTable
2961 */
2962 private int getAccessibleTableColumnCount(final AccessibleContext ac) {
2963 debugString("##### getAccessibleTableColumnCount");
2964 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
2965 @Override
2966 public Integer call() throws Exception {
2967 if (ac != null) {
2968 AccessibleTable at = ac.getAccessibleTable();
2969 if (at != null) {
2970 return at.getAccessibleColumnCount();
2971 }
2972 }
2973 return -1;
2974 }
2975 }, ac);
2976 }
2977
2978 /*
2979 * returns the AccessibleContext for an AccessibleTable cell
2980 */
2981 private AccessibleContext getAccessibleTableCellAccessibleContext(final AccessibleTable at,
2982 final int row, final int column) {
2983 debugString("getAccessibleTableCellAccessibleContext: at = "+at.getClass());
2984 if (at == null) return null;
2985 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
2986 @Override
2987 public AccessibleContext call() throws Exception {
2988 if (!(at instanceof AccessibleContext)) {
2989 Accessible a = at.getAccessibleAt(row, column);
2990 if (a != null) {
2991 return a.getAccessibleContext();
2992 }
2993 } else {
2994 // work-around for AccessibleJTable.getCurrentAccessibleContext returning
2995 // wrong renderer component when cell contains more than one component
2996 AccessibleContext ac = (AccessibleContext) at;
2997 Accessible parent = ac.getAccessibleParent();
2998 if (parent != null) {
2999 int indexInParent = ac.getAccessibleIndexInParent();
3000 Accessible child =
3001 parent.getAccessibleContext().getAccessibleChild(indexInParent);
3002 if (child instanceof JTable) {
3003 JTable table = (JTable) child;
3008 renderer = table.getDefaultRenderer(columnClass);
3009 }
3010 Component component =
3011 renderer.getTableCellRendererComponent(table, table.getValueAt(row, column),
3012 false, false, row, column);
3013 if (component instanceof Accessible) {
3014 return component.getAccessibleContext();
3015 }
3016 }
3017 }
3018 }
3019 return null;
3020 }
3021 }, getContextFromAccessibleTable(at));
3022 }
3023
3024 /*
3025 * returns the index of a cell at a given row and column in an AccessibleTable
3026 */
3027 private int getAccessibleTableCellIndex(final AccessibleTable at, int row, int column) {
3028 debugString("##### getAccessibleTableCellIndex: at="+at);
3029 if (at != null) {
3030 int cellIndex = row *
3031 InvocationUtils.invokeAndWait(new Callable<Integer>() {
3032 @Override
3033 public Integer call() throws Exception {
3034 return at.getAccessibleColumnCount();
3035 }
3036 }, getContextFromAccessibleTable(at)) +
3037 column;
3038 debugString(" ##### getAccessibleTableCellIndex="+cellIndex);
3039 return cellIndex;
3040 }
3041 debugString(" ##### getAccessibleTableCellIndex FAILED");
3042 return -1;
3043 }
3044
3045 /*
3046 * returns the row extent of a cell at a given row and column in an AccessibleTable
3047 */
3048 private int getAccessibleTableCellRowExtent(final AccessibleTable at, final int row, final int column) {
3049 debugString("##### getAccessibleTableCellRowExtent");
3050 if (at != null) {
3051 int rowExtent = InvocationUtils.invokeAndWait(new Callable<Integer>() {
3052 @Override
3053 public Integer call() throws Exception {
3054 return at.getAccessibleRowExtentAt(row, column);
3055 }
3056 },
3057 getContextFromAccessibleTable(at));
3058 debugString(" ##### getAccessibleTableCellRowExtent="+rowExtent);
3059 return rowExtent;
3060 }
3061 debugString(" ##### getAccessibleTableCellRowExtent FAILED");
3062 return -1;
3063 }
3064
3065 /*
3066 * returns the column extent of a cell at a given row and column in an AccessibleTable
3067 */
3068 private int getAccessibleTableCellColumnExtent(final AccessibleTable at, final int row, final int column) {
3069 debugString("##### getAccessibleTableCellColumnExtent");
3070 if (at != null) {
3071 int columnExtent = InvocationUtils.invokeAndWait(new Callable<Integer>() {
3072 @Override
3073 public Integer call() throws Exception {
3074 return at.getAccessibleColumnExtentAt(row, column);
3075 }
3076 },
3077 getContextFromAccessibleTable(at));
3078 debugString(" ##### getAccessibleTableCellColumnExtent="+columnExtent);
3079 return columnExtent;
3080 }
3081 debugString(" ##### getAccessibleTableCellColumnExtent FAILED");
3082 return -1;
3083 }
3084
3085 /*
3086 * returns whether a cell is selected at a given row and column in an AccessibleTable
3087 */
3088 private boolean isAccessibleTableCellSelected(final AccessibleTable at, final int row,
3089 final int column) {
3090 debugString("##### isAccessibleTableCellSelected: ["+row+"]["+column+"]");
3091 if (at == null)
3092 return false;
3093 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
3094 @Override
3095 public Boolean call() throws Exception {
3096 boolean isSelected = false;
3097 Accessible a = at.getAccessibleAt(row, column);
3098 if (a != null) {
3099 AccessibleContext ac = a.getAccessibleContext();
3100 if (ac == null)
3101 return false;
3102 AccessibleStateSet as = ac.getAccessibleStateSet();
3103 if (as != null) {
3104 isSelected = as.contains(AccessibleState.SELECTED);
3105 }
3106 }
3107 return isSelected;
3108 }
3109 }, getContextFromAccessibleTable(at));
3110 }
3111
3112 /*
3113 * returns an AccessibleTable that represents the row header in an
3114 * AccessibleTable
3115 */
3116 private AccessibleTable getAccessibleTableRowHeader(final AccessibleContext ac) {
3117 debugString(" ##### getAccessibleTableRowHeader called");
3118 AccessibleTable at = InvocationUtils.invokeAndWait(new Callable<AccessibleTable>() {
3119 @Override
3120 public AccessibleTable call() throws Exception {
3121 if (ac != null) {
3122 AccessibleTable at = ac.getAccessibleTable();
3123 if (at != null) {
3124 return at.getAccessibleRowHeader();
3125 }
3126 }
3127 return null;
3128 }
3129 }, ac);
3130 if (at != null) {
3131 hashtab.put(at, ac);
3132 }
3133 return at;
3134 }
3135
3136 /*
3137 * returns an AccessibleTable that represents the column header in an
3138 * AccessibleTable
3139 */
3140 private AccessibleTable getAccessibleTableColumnHeader(final AccessibleContext ac) {
3141 debugString("##### getAccessibleTableColumnHeader");
3142 if (ac == null)
3143 return null;
3144 AccessibleTable at = InvocationUtils.invokeAndWait(new Callable<AccessibleTable>() {
3145 @Override
3146 public AccessibleTable call() throws Exception {
3147 // workaround for getAccessibleColumnHeader NPE
3148 // when the table header is null
3149 Accessible parent = ac.getAccessibleParent();
3150 if (parent != null) {
3151 int indexInParent = ac.getAccessibleIndexInParent();
3152 Accessible child =
3153 parent.getAccessibleContext().getAccessibleChild(indexInParent);
3154 if (child instanceof JTable) {
3155 JTable table = (JTable) child;
3156 if (table.getTableHeader() == null) {
3157 return null;
3158 }
3159 }
3160 }
3161 AccessibleTable at = ac.getAccessibleTable();
3162 if (at != null) {
3163 return at.getAccessibleColumnHeader();
3164 }
3165 return null;
3166 }
3167 }, ac);
3168 if (at != null) {
3169 hashtab.put(at, ac);
3170 }
3171 return at;
3172 }
3173
3174 /*
3175 * returns the number of row headers in an AccessibleTable that represents
3176 * the row header in an AccessibleTable
3177 */
3178 private int getAccessibleTableRowHeaderRowCount(AccessibleContext ac) {
3179
3180 debugString(" ##### getAccessibleTableRowHeaderRowCount called");
3181 if (ac != null) {
3182 final AccessibleTable atRowHeader = getAccessibleTableRowHeader(ac);
3183 if (atRowHeader != null) {
3184 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3185 @Override
3186 public Integer call() throws Exception {
3187 if (atRowHeader != null) {
3188 return atRowHeader.getAccessibleRowCount();
3189 }
3190 return -1;
3191 }
3192 }, ac);
3193 }
3194 }
3195 return -1;
3196 }
3197
3198 /*
3199 * returns the number of column headers in an AccessibleTable that represents
3200 * the row header in an AccessibleTable
3201 */
3202 private int getAccessibleTableRowHeaderColumnCount(AccessibleContext ac) {
3203 debugString(" ##### getAccessibleTableRowHeaderColumnCount called");
3204 if (ac != null) {
3205 final AccessibleTable atRowHeader = getAccessibleTableRowHeader(ac);
3206 if (atRowHeader != null) {
3207 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3208 @Override
3209 public Integer call() throws Exception {
3210 if (atRowHeader != null) {
3211 return atRowHeader.getAccessibleColumnCount();
3212 }
3213 return -1;
3214 }
3215 }, ac);
3216 }
3217 }
3218 debugString(" ##### getAccessibleTableRowHeaderColumnCount FAILED");
3219 return -1;
3220 }
3221
3222 /*
3223 * returns the number of row headers in an AccessibleTable that represents
3224 * the column header in an AccessibleTable
3225 */
3226 private int getAccessibleTableColumnHeaderRowCount(AccessibleContext ac) {
3227
3228 debugString("##### getAccessibleTableColumnHeaderRowCount");
3229 if (ac != null) {
3230 final AccessibleTable atColumnHeader = getAccessibleTableColumnHeader(ac);
3231 if (atColumnHeader != null) {
3232 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3233 @Override
3234 public Integer call() throws Exception {
3235 if (atColumnHeader != null) {
3236 return atColumnHeader.getAccessibleRowCount();
3237 }
3238 return -1;
3239 }
3240 }, ac);
3241 }
3242 }
3243 debugString(" ##### getAccessibleTableColumnHeaderRowCount FAILED");
3244 return -1;
3245 }
3246
3247 /*
3248 * returns the number of column headers in an AccessibleTable that represents
3249 * the column header in an AccessibleTable
3250 */
3251 private int getAccessibleTableColumnHeaderColumnCount(AccessibleContext ac) {
3252
3253 debugString("##### getAccessibleTableColumnHeaderColumnCount");
3254 if (ac != null) {
3255 final AccessibleTable atColumnHeader = getAccessibleTableColumnHeader(ac);
3256 if (atColumnHeader != null) {
3257 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3258 @Override
3259 public Integer call() throws Exception {
3260 if (atColumnHeader != null) {
3261 return atColumnHeader.getAccessibleColumnCount();
3262 }
3263 return -1;
3264 }
3265 }, ac);
3266 }
3267 }
3268 debugString(" ##### getAccessibleTableColumnHeaderColumnCount FAILED");
3269 return -1;
3270 }
3271
3272 /*
3273 * returns the description of a row header in an AccessibleTable
3274 */
3275 private AccessibleContext getAccessibleTableRowDescription(final AccessibleTable table,
3276 final int row) {
3277 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
3278 @Override
3279 public AccessibleContext call() throws Exception {
3280 if (table != null) {
3281 Accessible a = table.getAccessibleRowDescription(row);
3282 if (a != null) {
3283 return a.getAccessibleContext();
3284 }
3285 }
3286 return null;
3287 }
3288 }, getContextFromAccessibleTable(table));
3515 AccessibleRelationSet ars = ac.getAccessibleRelationSet();
3516 if (ars != null) {
3517 AccessibleRelation[] relations = ars.toArray();
3518 if (relations != null && i >= 0 && i < relations.length) {
3519 Object[] targets = relations[i].getTarget();
3520 return targets.length;
3521 }
3522 }
3523 }
3524 return -1;
3525 }
3526 }, ac);
3527 }
3528
3529 /*
3530 * returns the jth target in the ith relation in the AccessibleContext's
3531 * AccessibleRelationSet
3532 */
3533 private AccessibleContext getAccessibleRelationTarget(final AccessibleContext ac,
3534 final int i, final int j) {
3535 debugString("***** getAccessibleRelationTarget");
3536 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
3537 @Override
3538 public AccessibleContext call() throws Exception {
3539 if (ac != null) {
3540 AccessibleRelationSet ars = ac.getAccessibleRelationSet();
3541 if (ars != null) {
3542 AccessibleRelation[] relations = ars.toArray();
3543 if (relations != null && i >= 0 && i < relations.length) {
3544 Object[] targets = relations[i].getTarget();
3545 if (targets != null && j >= 0 & j < targets.length) {
3546 Object o = targets[j];
3547 if (o instanceof Accessible) {
3548 return ((Accessible) o).getAccessibleContext();
3549 }
3550 }
3551 }
3552 }
3553 }
3554 return null;
3555 }
3556 }, ac);
3557 }
3558
3559 // ========= AccessibleHypertext =========
3560
3561 private Map<AccessibleHypertext, AccessibleContext> hyperTextContextMap = new WeakHashMap<>();
3562 private Map<AccessibleHyperlink, AccessibleContext> hyperLinkContextMap = new WeakHashMap<>();
3563
3564 /*
3565 * Returns the AccessibleHypertext
3566 */
3567 private AccessibleHypertext getAccessibleHypertext(final AccessibleContext ac) {
3568 debugString("getAccessibleHyperlink");
3569 if (ac==null)
3570 return null;
3571 AccessibleHypertext hypertext = InvocationUtils.invokeAndWait(new Callable<AccessibleHypertext>() {
3572 @Override
3573 public AccessibleHypertext call() throws Exception {
3574 AccessibleText at = ac.getAccessibleText();
3575 if (!(at instanceof AccessibleHypertext)) {
3576 return null;
3577 }
3578 return ((AccessibleHypertext) at);
3579 }
3580 }, ac);
3581 hyperTextContextMap.put(hypertext, ac);
3582 return hypertext;
3583 }
3584
3585 /*
3586 * Returns the number of AccessibleHyperlinks
3587 */
3588 private int getAccessibleHyperlinkCount(AccessibleContext ac) {
3589 debugString("getAccessibleHyperlinkCount");
3590 if (ac == null) {
3591 return 0;
3592 }
3593 final AccessibleHypertext hypertext = getAccessibleHypertext(ac);
3594 if (hypertext == null) {
3595 return 0;
3596 }
3597 //return hypertext.getLinkCount();
3598 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3599 @Override
3600 public Integer call() throws Exception {
3601 return hypertext.getLinkCount();
3602 }
3603 }, ac);
3604 }
3605
3606 /*
3607 * Returns the hyperlink at the specified index
3608 */
3609 private AccessibleHyperlink getAccessibleHyperlink(final AccessibleHypertext hypertext, final int i) {
3610 debugString("getAccessibleHyperlink");
3611 if (hypertext == null) {
3612 return null;
3613 }
3614 AccessibleContext ac = hyperTextContextMap.get(hypertext);
3615 if ( i < 0 || i >=
3616 InvocationUtils.invokeAndWait(new Callable<Integer>() {
3617 @Override
3618 public Integer call() throws Exception {
3619 return hypertext.getLinkCount();
3620 }
3621 }, ac) ) {
3622 return null;
3623 }
3624 AccessibleHyperlink acLink = InvocationUtils.invokeAndWait(new Callable<AccessibleHyperlink>() {
3625 @Override
3626 public AccessibleHyperlink call() throws Exception {
3627 AccessibleHyperlink link = hypertext.getLink(i);
3628 if (link == null || (!link.isValid())) {
3629 return null;
3630 }
3631 return link;
3632 }
3633 }, ac);
3634 hyperLinkContextMap.put(acLink, ac);
3635 return acLink;
3636 }
3637
3638 /*
3639 * Returns the hyperlink object description
3640 */
3641 private String getAccessibleHyperlinkText(final AccessibleHyperlink link) {
3642 debugString("getAccessibleHyperlinkText");
3643 if (link == null) {
3644 return null;
3645 }
3646 return InvocationUtils.invokeAndWait(new Callable<String>() {
3647 @Override
3648 public String call() throws Exception {
3649 Object o = link.getAccessibleActionDescription(0);
3650 if (o != null) {
3651 return o.toString();
3652 }
3653 return null;
3654 }
3655 }, hyperLinkContextMap.get(link));
3656 }
3657
3658 /*
3659 * Returns the hyperlink URL
3660 */
3661 private String getAccessibleHyperlinkURL(final AccessibleHyperlink link) {
3662 debugString("getAccessibleHyperlinkURL");
3663 if (link == null) {
3664 return null;
3665 }
3666 return InvocationUtils.invokeAndWait(new Callable<String>() {
3667 @Override
3668 public String call() throws Exception {
3669 Object o = link.getAccessibleActionObject(0);
3670 if (o != null) {
3671 return o.toString();
3672 } else {
3673 return null;
3674 }
3675 }
3676 }, hyperLinkContextMap.get(link));
3677 }
3678
3679 /*
3680 * Returns the start index of the hyperlink text
3681 */
3682 private int getAccessibleHyperlinkStartIndex(final AccessibleHyperlink link) {
3683 debugString("getAccessibleHyperlinkStartIndex");
3684 if (link == null) {
3685 return -1;
3686 }
3687 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3688 @Override
3689 public Integer call() throws Exception {
3690 return link.getStartIndex();
3691 }
3692 }, hyperLinkContextMap.get(link));
3693 }
3694
3695 /*
3696 * Returns the end index of the hyperlink text
3697 */
3698 private int getAccessibleHyperlinkEndIndex(final AccessibleHyperlink link) {
3699 debugString("getAccessibleHyperlinkEndIndex");
3700 if (link == null) {
3701 return -1;
3702 }
3703 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3704 @Override
3705 public Integer call() throws Exception {
3706 return link.getEndIndex();
3707 }
3708 }, hyperLinkContextMap.get(link));
3709 }
3710
3711 /*
3712 * Returns the index into an array of hyperlinks that
3713 * is associated with this character index, or -1 if there
3714 * is no hyperlink associated with this index.
3715 */
3716 private int getAccessibleHypertextLinkIndex(final AccessibleHypertext hypertext, final int charIndex) {
3717 debugString("getAccessibleHypertextLinkIndex: charIndex = "+charIndex);
3718 if (hypertext == null) {
3719 return -1;
3720 }
3721 int linkIndex = InvocationUtils.invokeAndWait(new Callable<Integer>() {
3722 @Override
3723 public Integer call() throws Exception {
3724 return hypertext.getLinkIndex(charIndex);
3725 }
3726 }, hyperTextContextMap.get(hypertext));
3727 debugString("getAccessibleHypertextLinkIndex returning "+linkIndex);
3728 return linkIndex;
3729 }
3730
3731 /*
3732 * Actives the hyperlink
3733 */
3734 private boolean activateAccessibleHyperlink(final AccessibleContext ac,
3735 final AccessibleHyperlink link) {
3736 //debugString("activateAccessibleHyperlink: link = "+link.getClass());
3737 if (link == null) {
3738 return false;
3739 }
3740 boolean retval = InvocationUtils.invokeAndWait(new Callable<Boolean>() {
3741 @Override
3742 public Boolean call() throws Exception {
3743 return link.doAccessibleAction(0);
3744 }
3745 }, ac);
3746 debugString("activateAccessibleHyperlink: returning = "+retval);
3747 return retval;
3748 }
3749
3750
3751 // ============ AccessibleKeyBinding =============
3752
3753 /*
3754 * returns the component mnemonic
3755 */
3756 private KeyStroke getMnemonic(final AccessibleContext ac) {
3757 if (ac == null)
3758 return null;
3759 return InvocationUtils.invokeAndWait(new Callable<KeyStroke>() {
3760 @Override
3761 public KeyStroke call() throws Exception {
3762 AccessibleComponent comp = ac.getAccessibleComponent();
3763 if (!(comp instanceof AccessibleExtendedComponent)) {
3764 return null;
3765 }
3766 AccessibleExtendedComponent aec = (AccessibleExtendedComponent) comp;
3854 case KeyEvent.VK_RIGHT:
3855 case KeyEvent.VK_UP:
3856 break;
3857 default:
3858 code = 0;
3859 break;
3860 }
3861 return code;
3862 }
3863
3864 /*
3865 * returns the KeyStoke character
3866 */
3867 private char getKeyChar(KeyStroke keyStroke) {
3868 // If the shortcut is an FKey return 1-24
3869 if (keyStroke == null)
3870 return 0;
3871 int fKey = fKeyNumber(keyStroke);
3872 if (fKey != 0) {
3873 // return 0x00000001 through 0x00000018
3874 debugString(" Shortcut is: F" + fKey);
3875 return (char)fKey;
3876 }
3877 // If the accelerator is a control character, return it
3878 int keyCode = controlCode(keyStroke);
3879 if (keyCode != 0) {
3880 debugString(" Shortcut is control character: " + Integer.toHexString(keyCode));
3881 return (char)keyCode;
3882 }
3883 String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode());
3884 debugString(" Shortcut is: " + keyText);
3885 if (keyText != null || keyText.length() > 0) {
3886 CharSequence seq = keyText.subSequence(0, 1);
3887 if (seq != null || seq.length() > 0) {
3888 return seq.charAt(0);
3889 }
3890 }
3891 return 0;
3892 }
3893
3894 /*
3895 * returns the KeyStroke modifiers as an int
3896 */
3897 private int getModifiers(KeyStroke keyStroke) {
3898 if (keyStroke == null)
3899 return 0;
3900 debugString("In AccessBridge.getModifiers");
3901 // modifiers is a bit strip where bits 0-7 indicate a traditional modifier
3902 // such as Ctrl/Alt/Shift, bit 8 indicates an F key shortcut, and bit 9 indicates
3903 // a control code shortcut such as the delete key.
3904
3905 int modifiers = 0;
3906 // Is the shortcut an FKey?
3907 if (fKeyNumber(keyStroke) != 0) {
3908 modifiers |= 1 << 8;
3909 }
3910 // Is the shortcut a control code?
3911 if (controlCode(keyStroke) != 0) {
3912 modifiers |= 1 << 9;
3913 }
3914 // The following is needed in order to handle translated modifiers.
3915 // getKeyModifiersText doesn't work because for example in German Strg is
3916 // returned for Ctrl.
3917
3918 // There can be more than one modifier, e.g. if the modifier is ctrl + shift + B
3919 // the toString text is "shift ctrl pressed B". Need to parse through that.
3920 StringTokenizer st = new StringTokenizer(keyStroke.toString());
3921 while (st.hasMoreTokens()) {
3922 String text = st.nextToken();
3923 // Meta+Ctrl+Alt+Shift
3924 // 0-3 are shift, ctrl, meta, alt
3925 // 4-7 are for Solaris workstations (though not being used)
3926 if (text.startsWith("met")) {
3927 debugString(" found meta");
3928 modifiers |= ActionEvent.META_MASK;
3929 }
3930 if (text.startsWith("ctr")) {
3931 debugString(" found ctrl");
3932 modifiers |= ActionEvent.CTRL_MASK;
3933 }
3934 if (text.startsWith("alt")) {
3935 debugString(" found alt");
3936 modifiers |= ActionEvent.ALT_MASK;
3937 }
3938 if (text.startsWith("shi")) {
3939 debugString(" found shift");
3940 modifiers |= ActionEvent.SHIFT_MASK;
3941 }
3942 }
3943 debugString(" returning modifiers: 0x" + Integer.toHexString(modifiers));
3944 return modifiers;
3945 }
3946
3947 /*
3948 * returns the number of key bindings associated with this context
3949 */
3950 private int getAccessibleKeyBindingsCount(AccessibleContext ac) {
3951 if (ac == null)
3952 return 0;
3953 int count = 0;
3954
3955 if (getMnemonic(ac) != null) {
3956 count++;
3957 }
3958 if (getAccelerator(ac) != null) {
3959 count++;
3960 }
3961 return count;
3962 }
3963
4002 if (index == 0) { // mnemonic
4003 KeyStroke keyStroke = getMnemonic(ac);
4004 if (keyStroke != null) {
4005 return getModifiers(keyStroke);
4006 }
4007 } else if (index == 1) { // accelerator
4008 KeyStroke keyStroke = getAccelerator(ac);
4009 if (keyStroke != null) {
4010 return getModifiers(keyStroke);
4011 }
4012 }
4013 return 0;
4014 }
4015
4016 // ========== AccessibleIcon ============
4017
4018 /*
4019 * return the number of icons associated with this context
4020 */
4021 private int getAccessibleIconsCount(final AccessibleContext ac) {
4022 debugString("getAccessibleIconsCount");
4023 if (ac == null) {
4024 return 0;
4025 }
4026 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4027 @Override
4028 public Integer call() throws Exception {
4029 AccessibleIcon[] ai = ac.getAccessibleIcon();
4030 if (ai == null) {
4031 return 0;
4032 }
4033 return ai.length;
4034 }
4035 }, ac);
4036 }
4037
4038 /*
4039 * return icon description at the specified index
4040 */
4041 private String getAccessibleIconDescription(final AccessibleContext ac, final int index) {
4042 debugString("getAccessibleIconDescription: index = "+index);
4043 if (ac == null) {
4044 return null;
4045 }
4046 return InvocationUtils.invokeAndWait(new Callable<String>() {
4047 @Override
4048 public String call() throws Exception {
4049 AccessibleIcon[] ai = ac.getAccessibleIcon();
4050 if (ai == null || index < 0 || index >= ai.length) {
4051 return null;
4052 }
4053 return ai[index].getAccessibleIconDescription();
4054 }
4055 }, ac);
4056 }
4057
4058 /*
4059 * return icon height at the specified index
4060 */
4061 private int getAccessibleIconHeight(final AccessibleContext ac, final int index) {
4062 debugString("getAccessibleIconHeight: index = "+index);
4063 if (ac == null) {
4064 return 0;
4065 }
4066 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4067 @Override
4068 public Integer call() throws Exception {
4069 AccessibleIcon[] ai = ac.getAccessibleIcon();
4070 if (ai == null || index < 0 || index >= ai.length) {
4071 return 0;
4072 }
4073 return ai[index].getAccessibleIconHeight();
4074 }
4075 }, ac);
4076 }
4077
4078 /*
4079 * return icon width at the specified index
4080 */
4081 private int getAccessibleIconWidth(final AccessibleContext ac, final int index) {
4082 debugString("getAccessibleIconWidth: index = "+index);
4083 if (ac == null) {
4084 return 0;
4085 }
4086 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4087 @Override
4088 public Integer call() throws Exception {
4089 AccessibleIcon[] ai = ac.getAccessibleIcon();
4090 if (ai == null || index < 0 || index >= ai.length) {
4091 return 0;
4092 }
4093 return ai[index].getAccessibleIconWidth();
4094 }
4095 }, ac);
4096 }
4097
4098 // ========= AccessibleAction ===========
4099
4100 /*
4101 * return the number of icons associated with this context
4102 */
4103 private int getAccessibleActionsCount(final AccessibleContext ac) {
4104 debugString("getAccessibleActionsCount");
4105 if (ac == null) {
4106 return 0;
4107 }
4108 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4109 @Override
4110 public Integer call() throws Exception {
4111 AccessibleAction aa = ac.getAccessibleAction();
4112 if (aa == null)
4113 return 0;
4114 return aa.getAccessibleActionCount();
4115 }
4116 }, ac);
4117 }
4118
4119 /*
4120 * return icon description at the specified index
4121 */
4122 private String getAccessibleActionName(final AccessibleContext ac, final int index) {
4123 debugString("getAccessibleActionName: index = "+index);
4124 if (ac == null) {
4125 return null;
4126 }
4127 return InvocationUtils.invokeAndWait(new Callable<String>() {
4128 @Override
4129 public String call() throws Exception {
4130 AccessibleAction aa = ac.getAccessibleAction();
4131 if (aa == null) {
4132 return null;
4133 }
4134 return aa.getAccessibleActionDescription(index);
4135 }
4136 }, ac);
4137 }
4138 /*
4139 * return icon description at the specified index
4140 */
4141 private boolean doAccessibleActions(final AccessibleContext ac, final String name) {
4142 debugString("doAccessibleActions: action name = "+name);
4143 if (ac == null || name == null) {
4144 return false;
4145 }
4146 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4147 @Override
4148 public Boolean call() throws Exception {
4149 AccessibleAction aa = ac.getAccessibleAction();
4150 if (aa == null) {
4151 return false;
4152 }
4153 int index = -1;
4154 int numActions = aa.getAccessibleActionCount();
4155 for (int i = 0; i < numActions; i++) {
4156 String actionName = aa.getAccessibleActionDescription(i);
4157 if (name.equals(actionName)) {
4158 index = i;
4159 break;
4160 }
4161 }
4162 if (index == -1) {
4163 return false;
4164 }
4165 boolean retval = aa.doAccessibleAction(index);
4166 return retval;
4167 }
4168 }, ac);
4169 }
4170
4171 /* ===== AT utility methods ===== */
4172
4173 /**
4174 * Sets the contents of an AccessibleContext that
4175 * implements AccessibleEditableText with the
4176 * specified text string.
4177 * Returns whether successful.
4178 */
4179 private boolean setTextContents(final AccessibleContext ac, final String text) {
4180 debugString("setTextContents: ac = "+ac+"; text = "+text);
4181
4182 if (! (ac instanceof AccessibleEditableText)) {
4183 debugString(" ac not instanceof AccessibleEditableText: "+ac);
4184 return false;
4185 }
4186 if (text == null) {
4187 debugString(" text is null");
4188 return false;
4189 }
4190
4191 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4192 @Override
4193 public Boolean call() throws Exception {
4194 // check whether the text field is editable
4195 AccessibleStateSet ass = ac.getAccessibleStateSet();
4196 if (!ass.contains(AccessibleState.ENABLED)) {
4197 return false;
4198 }
4199 ((AccessibleEditableText) ac).setTextContents(text);
4200 return true;
4201 }
4202 }, ac);
4203 }
4204
4205 /**
4206 * Returns the Accessible Context of an Internal Frame object that is
4207 * the ancestor of a given object. If the object is an Internal Frame
4208 * object or an Internal Frame ancestor object was found, returns the
4209 * object's AccessibleContext.
4210 * If there is no ancestor object that has an Accessible Role of
4211 * Internal Frame, returns (AccessibleContext)0.
4212 */
4213 private AccessibleContext getInternalFrame (AccessibleContext ac) {
4214 return getParentWithRole(ac, AccessibleRole.INTERNAL_FRAME.toString());
4215 }
4216
4217 /**
4218 * Returns the Accessible Context for the top level object in
4219 * a Java Window. This is same Accessible Context that is obtained
4220 * from GetAccessibleContextFromHWND for that window. Returns
4221 * (AccessibleContext)0 on error.
4222 */
4223 private AccessibleContext getTopLevelObject (final AccessibleContext ac) {
4224 debugString("getTopLevelObject; ac = "+ac);
4225 if (ac == null) {
4226 return null;
4227 }
4228 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4229 @Override
4230 public AccessibleContext call() throws Exception {
4231 if (ac.getAccessibleRole() == AccessibleRole.DIALOG) {
4232 // return the dialog, not the parent window
4233 return ac;
4234 }
4235
4236 Accessible parent = ac.getAccessibleParent();
4237 if (parent == null) {
4238 return ac;
4239 }
4240 Accessible tmp = parent;
4241 while (tmp != null && tmp.getAccessibleContext() != null) {
4242 AccessibleContext ac2 = tmp.getAccessibleContext();
4243 if (ac2 != null && ac2.getAccessibleRole() == AccessibleRole.DIALOG) {
4244 // return the dialog, not the parent window
4245 return ac2;
4246 }
4247 parent = tmp;
4248 tmp = parent.getAccessibleContext().getAccessibleParent();
4249 }
4250 return parent.getAccessibleContext();
4251 }
4252 }, ac);
4253 }
4254
4255 /**
4256 * Returns the parent AccessibleContext that has the specified AccessibleRole.
4257 * Returns null on error or if the AccessibleContext does not exist.
4258 */
4259 private AccessibleContext getParentWithRole (final AccessibleContext ac,
4260 final String roleName) {
4261 debugString("getParentWithRole; ac = "+ac);
4262 debugString("role = "+roleName);
4263 if (ac == null || roleName == null) {
4264 return null;
4265 }
4266
4267 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4268 @Override
4269 public AccessibleContext call() throws Exception {
4270 AccessibleRole role = AccessBridge.this.accessibleRoleMap.get(roleName);
4271 if (role == null) {
4272 return ac;
4273 }
4274
4275 Accessible parent = ac.getAccessibleParent();
4276 if (parent == null && ac.getAccessibleRole() == role) {
4277 return ac;
4278 }
4279
4280 Accessible tmp = parent;
4281 AccessibleContext tmp_ac = null;
4282
4298 /**
4299 * Returns the parent AccessibleContext that has the specified AccessibleRole.
4300 * Otherwise, returns the top level object for the Java Window.
4301 * Returns (AccessibleContext)0 on error.
4302 */
4303 private AccessibleContext getParentWithRoleElseRoot (AccessibleContext ac,
4304 String roleName) {
4305 AccessibleContext retval = getParentWithRole(ac, roleName);
4306 if (retval == null) {
4307 retval = getTopLevelObject(ac);
4308 }
4309 return retval;
4310 }
4311
4312 /**
4313 * Returns how deep in the object hierarchy a given object is.
4314 * The top most object in the object hierarchy has an object depth of 0.
4315 * Returns -1 on error.
4316 */
4317 private int getObjectDepth(final AccessibleContext ac) {
4318 debugString("getObjectDepth: ac = "+ac);
4319
4320 if (ac == null) {
4321 return -1;
4322 }
4323 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4324 @Override
4325 public Integer call() throws Exception {
4326 int count = 0;
4327 Accessible parent = ac.getAccessibleParent();
4328 if (parent == null) {
4329 return count;
4330 }
4331 Accessible tmp = parent;
4332 while (tmp != null && tmp.getAccessibleContext() != null) {
4333 parent = tmp;
4334 tmp = parent.getAccessibleContext().getAccessibleParent();
4335 count++;
4336 }
4337 return count;
4338 }
4339 }, ac);
4340 }
4341
4342 /**
4343 * Returns the Accessible Context of the current ActiveDescendent of an object.
4344 * Returns (AccessibleContext)0 on error.
4345 */
4346 private AccessibleContext getActiveDescendent (final AccessibleContext ac) {
4347 debugString("getActiveDescendent: ac = "+ac);
4348 if (ac == null) {
4349 return null;
4350 }
4351 // workaround for JTree bug where the only possible active
4352 // descendent is the JTree root
4353 final Accessible parent = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
4354 @Override
4355 public Accessible call() throws Exception {
4356 return ac.getAccessibleParent();
4357 }
4358 }, ac);
4359
4360 if (parent != null) {
4361 Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
4362 @Override
4363 public Accessible call() throws Exception {
4364 int indexInParent = ac.getAccessibleIndexInParent();
4365 return parent.getAccessibleContext().getAccessibleChild(indexInParent);
4366 }
4367 }, ac);
4395 if (a == null) {
4396 return null;
4397 }
4398 return a.getAccessibleContext();
4399 }
4400 }, ac);
4401 }
4402
4403
4404 /**
4405 * Additional methods for Teton
4406 */
4407
4408 /**
4409 * Gets the AccessibleName for a component based upon the JAWS algorithm.
4410 * Returns whether successful.
4411 *
4412 * Bug ID 4916682 - Implement JAWS AccessibleName policy
4413 */
4414 private String getJAWSAccessibleName(final AccessibleContext ac) {
4415 debugString("getJAWSAccessibleName");
4416 if (ac == null) {
4417 return null;
4418 }
4419 // placeholder
4420 return InvocationUtils.invokeAndWait(new Callable<String>() {
4421 @Override
4422 public String call() throws Exception {
4423 return ac.getAccessibleName();
4424 }
4425 }, ac);
4426 }
4427
4428 /**
4429 * Request focus for a component. Returns whether successful;
4430 *
4431 * Bug ID 4944757 - requestFocus method needed
4432 */
4433 private boolean requestFocus(final AccessibleContext ac) {
4434 debugString("requestFocus");
4435 if (ac == null) {
4436 return false;
4437 }
4438 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4439 @Override
4440 public Boolean call() throws Exception {
4441 AccessibleComponent acomp = ac.getAccessibleComponent();
4442 if (acomp == null) {
4443 return false;
4444 }
4445 acomp.requestFocus();
4446 return ac.getAccessibleStateSet().contains(AccessibleState.FOCUSED);
4447 }
4448 }, ac);
4449 }
4450
4451 /**
4452 * Selects text between two indices. Selection includes the
4453 * text at the start index and the text at the end index. Returns
4454 * whether successful;
4455 *
4456 * Bug ID 4944758 - selectTextRange method needed
4457 */
4458 private boolean selectTextRange(final AccessibleContext ac, final int startIndex, final int endIndex) {
4459 debugString("selectTextRange: start = "+startIndex+"; end = "+endIndex);
4460 if (ac == null) {
4461 return false;
4462 }
4463 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4464 @Override
4465 public Boolean call() throws Exception {
4466 AccessibleText at = ac.getAccessibleText();
4467 if (!(at instanceof AccessibleEditableText)) {
4468 return false;
4469 }
4470 ((AccessibleEditableText) at).selectText(startIndex, endIndex);
4471
4472 boolean result = at.getSelectionStart() == startIndex &&
4473 at.getSelectionEnd() == endIndex;
4474 return result;
4475 }
4476 }, ac);
4477 }
4478
4479 /**
4480 * Set the caret to a text position. Returns whether successful;
4481 *
4482 * Bug ID 4944770 - setCaretPosition method needed
4483 */
4484 private boolean setCaretPosition(final AccessibleContext ac, final int position) {
4485 debugString("setCaretPosition: position = "+position);
4486 if (ac == null) {
4487 return false;
4488 }
4489 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4490 @Override
4491 public Boolean call() throws Exception {
4492 AccessibleText at = ac.getAccessibleText();
4493 if (!(at instanceof AccessibleEditableText)) {
4494 return false;
4495 }
4496 ((AccessibleEditableText) at).selectText(position, position);
4497 return at.getCaretPosition() == position;
4498 }
4499 }, ac);
4500 }
4501
4502 /**
4503 * Gets the number of visible children of an AccessibleContext.
4504 *
4505 * Bug ID 4944762- getVisibleChildren for list-like components needed
4506 */
4507 private int _visibleChildrenCount;
4508 private AccessibleContext _visibleChild;
4509 private int _currentVisibleIndex;
4510 private boolean _foundVisibleChild;
4511
4512 private int getVisibleChildrenCount(AccessibleContext ac) {
4513 debugString("getVisibleChildrenCount");
4514 if (ac == null) {
4515 return -1;
4516 }
4517 _visibleChildrenCount = 0;
4518 _getVisibleChildrenCount(ac);
4519 debugString(" _visibleChildrenCount = "+_visibleChildrenCount);
4520 return _visibleChildrenCount;
4521 }
4522
4523 /*
4524 * Recursively descends AccessibleContext and gets the number
4525 * of visible children
4526 */
4527 private void _getVisibleChildrenCount(final AccessibleContext ac) {
4528 if (ac == null)
4529 return;
4530 if(ac instanceof AccessibleExtendedTable) {
4531 _getVisibleChildrenCount((AccessibleExtendedTable)ac);
4532 return;
4533 }
4534 int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4535 @Override
4536 public Integer call() throws Exception {
4537 return ac.getAccessibleChildrenCount();
4538 }
4539 }, ac);
4639
4640 if (InvocationUtils.invokeAndWait(new Callable<Integer>() {
4641 @Override
4642 public Integer call() throws Exception {
4643 return ac2.getAccessibleChildrenCount();
4644 }
4645 }, acTable) > 0) {
4646 _getVisibleChildrenCount(ac2);
4647 }
4648 }
4649 }
4650 }
4651
4652 /**
4653 * Gets the visible child of an AccessibleContext at the
4654 * specified index
4655 *
4656 * Bug ID 4944762- getVisibleChildren for list-like components needed
4657 */
4658 private AccessibleContext getVisibleChild(AccessibleContext ac, int index) {
4659 debugString("getVisibleChild: index = "+index);
4660 if (ac == null) {
4661 return null;
4662 }
4663 _visibleChild = null;
4664 _currentVisibleIndex = 0;
4665 _foundVisibleChild = false;
4666 _getVisibleChild(ac, index);
4667
4668 if (_visibleChild != null) {
4669 debugString( " getVisibleChild: found child = " +
4670 InvocationUtils.invokeAndWait(new Callable<String>() {
4671 @Override
4672 public String call() throws Exception {
4673 return AccessBridge.this._visibleChild.getAccessibleName();
4674 }
4675 }, ac) );
4676 }
4677 return _visibleChild;
4678 }
4679
4680 /*
4681 * Recursively searchs AccessibleContext and finds the visible component
4682 * at the specified index
4683 */
4684 private void _getVisibleChild(final AccessibleContext ac, final int index) {
4685 if (_visibleChild != null) {
4686 return;
4687 }
4688 if(ac instanceof AccessibleExtendedTable) {
4689 _getVisibleChild((AccessibleExtendedTable)ac, index);
4838
4839 /**
4840 * Constructor
4841 */
4842 ObjectReferences() {
4843 refs = new ConcurrentHashMap<>(4);
4844 }
4845
4846 /**
4847 * Debugging: dump the contents of ObjectReferences' refs Hashtable
4848 */
4849 String dump() {
4850 return refs.toString();
4851 }
4852
4853 /**
4854 * Increment ref count; set to 1 if we have no references for it
4855 */
4856 void increment(Object o) {
4857 if (o == null){
4858 debugString("ObjectReferences::increment - Passed in object is null");
4859 return;
4860 }
4861
4862 if (refs.containsKey(o)) {
4863 (refs.get(o)).value++;
4864 } else {
4865 refs.put(o, new Reference(1));
4866 }
4867 }
4868
4869 /**
4870 * Decrement ref count; remove if count drops to 0
4871 */
4872 void decrement(Object o) {
4873 Reference aRef = refs.get(o);
4874 if (aRef != null) {
4875 aRef.value--;
4876 if (aRef.value == 0) {
4877 refs.remove(o);
4878 } else if (aRef.value < 0) {
4879 debugString("ERROR: decrementing reference count below 0");
4880 }
4881 } else {
4882 debugString("ERROR: object to decrement not in ObjectReferences table");
4883 }
4884 }
4885
4886 }
4887
4888 /* ===== event handling code ===== */
4889
4890 /**
4891 * native method for handling property change events
4892 */
4893 private native void propertyCaretChange(PropertyChangeEvent e,
4894 AccessibleContext src,
4895 int oldValue, int newValue);
4896 private native void propertyDescriptionChange(PropertyChangeEvent e,
4897 AccessibleContext src,
4898 String oldValue, String newValue);
4899 private native void propertyNameChange(PropertyChangeEvent e,
4900 AccessibleContext src,
4901 String oldValue, String newValue);
4902 private native void propertySelectionChange(PropertyChangeEvent e,
5197 /**
5198 * Turn off event monitoring for the event type passed in
5199 * If necessary, remove the appropriate event listener (if
5200 * no other event of that type is being listened for)
5201 */
5202 void removeAccessibilityEventNotification(long type) {
5203 long newEventMask = accessibilityEventMask & (~type);
5204 if ( ((accessibilityEventMask & PROPERTY_EVENTS) != 0) &&
5205 ((newEventMask & PROPERTY_EVENTS) == 0) ) {
5206 AccessibilityEventMonitor.removePropertyChangeListener(this);
5207 }
5208 accessibilityEventMask = newEventMask;
5209 }
5210
5211 /**
5212 * ------- property change event glue
5213 */
5214 // This is invoked on the EDT , as
5215 public void propertyChange(PropertyChangeEvent e) {
5216
5217 accessBridge.debugString("propertyChange(" + e.toString() + ") called");
5218
5219 if (e != null && (accessibilityEventMask & PROPERTY_EVENTS) != 0) {
5220 Object o = e.getSource();
5221 AccessibleContext ac;
5222
5223 if (o instanceof AccessibleContext) {
5224 ac = (AccessibleContext) o;
5225 } else {
5226 Accessible a = Translator.getAccessible(e.getSource());
5227 if (a == null)
5228 return;
5229 else
5230 ac = a.getAccessibleContext();
5231 }
5232 if (ac != null) {
5233 InvocationUtils.registerAccessibleContext(ac, AppContext.getAppContext());
5234
5235 accessBridge.debugString("AccessibleContext: " + ac);
5236 String propertyName = e.getPropertyName();
5237
5238 if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CARET_PROPERTY) == 0) {
5239 int oldValue = 0;
5240 int newValue = 0;
5241
5242 if (e.getOldValue() instanceof Integer) {
5243 oldValue = ((Integer) e.getOldValue()).intValue();
5244 }
5245 if (e.getNewValue() instanceof Integer) {
5246 newValue = ((Integer) e.getNewValue()).intValue();
5247 }
5248 accessBridge.debugString(" - about to call propertyCaretChange()");
5249 accessBridge.debugString(" old value: " + oldValue + "new value: " + newValue);
5250 accessBridge.propertyCaretChange(e, ac, oldValue, newValue);
5251
5252 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY) == 0) {
5253 String oldValue = null;
5254 String newValue = null;
5255
5256 if (e.getOldValue() != null) {
5257 oldValue = e.getOldValue().toString();
5258 }
5259 if (e.getNewValue() != null) {
5260 newValue = e.getNewValue().toString();
5261 }
5262 accessBridge.debugString(" - about to call propertyDescriptionChange()");
5263 accessBridge.debugString(" old value: " + oldValue + "new value: " + newValue);
5264 accessBridge.propertyDescriptionChange(e, ac, oldValue, newValue);
5265
5266 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_NAME_PROPERTY) == 0) {
5267 String oldValue = null;
5268 String newValue = null;
5269
5270 if (e.getOldValue() != null) {
5271 oldValue = e.getOldValue().toString();
5272 }
5273 if (e.getNewValue() != null) {
5274 newValue = e.getNewValue().toString();
5275 }
5276 accessBridge.debugString(" - about to call propertyNameChange()");
5277 accessBridge.debugString(" old value: " + oldValue + " new value: " + newValue);
5278 accessBridge.propertyNameChange(e, ac, oldValue, newValue);
5279
5280 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY) == 0) {
5281 accessBridge.debugString(" - about to call propertySelectionChange() " + ac + " " + Thread.currentThread() + " " + e.getSource());
5282
5283 accessBridge.propertySelectionChange(e, ac);
5284
5285 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_STATE_PROPERTY) == 0) {
5286 String oldValue = null;
5287 String newValue = null;
5288
5289 // Localization fix requested by Oliver for EA-1
5290 if (e.getOldValue() != null) {
5291 AccessibleState oldState = (AccessibleState) e.getOldValue();
5292 oldValue = oldState.toDisplayString(Locale.US);
5293 }
5294 if (e.getNewValue() != null) {
5295 AccessibleState newState = (AccessibleState) e.getNewValue();
5296 newValue = newState.toDisplayString(Locale.US);
5297 }
5298
5299 accessBridge.debugString(" - about to call propertyStateChange()");
5300 accessBridge.propertyStateChange(e, ac, oldValue, newValue);
5301
5302 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_TEXT_PROPERTY) == 0) {
5303 accessBridge.debugString(" - about to call propertyTextChange()");
5304 accessBridge.propertyTextChange(e, ac);
5305
5306 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY) == 0) { // strings 'cause of floating point, etc.
5307 String oldValue = null;
5308 String newValue = null;
5309
5310 if (e.getOldValue() != null) {
5311 oldValue = e.getOldValue().toString();
5312 }
5313 if (e.getNewValue() != null) {
5314 newValue = e.getNewValue().toString();
5315 }
5316 accessBridge.debugString(" - about to call propertyDescriptionChange()");
5317 accessBridge.propertyValueChange(e, ac, oldValue, newValue);
5318
5319 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY) == 0) {
5320 accessBridge.propertyVisibleDataChange(e, ac);
5321
5322 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) {
5323 AccessibleContext oldAC = null;
5324 AccessibleContext newAC = null;
5325 Accessible a;
5326
5327 if (e.getOldValue() instanceof AccessibleContext) {
5328 oldAC = (AccessibleContext) e.getOldValue();
5329 InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext());
5330 }
5331 if (e.getNewValue() instanceof AccessibleContext) {
5332 newAC = (AccessibleContext) e.getNewValue();
5333 InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext());
5334 }
5335 accessBridge.debugString(" - about to call propertyChildChange()");
5336 accessBridge.debugString(" old AC: " + oldAC + "new AC: " + newAC);
5337 accessBridge.propertyChildChange(e, ac, oldAC, newAC);
5338
5339 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0) {
5340 handleActiveDescendentEvent(e, ac);
5341 }
5342 }
5343 }
5344 }
5345
5346 /*
5347 * Handle an ActiveDescendent PropertyChangeEvent. This
5348 * method works around a JTree bug where ActiveDescendent
5349 * PropertyChangeEvents have the wrong parent.
5350 */
5351 private AccessibleContext prevAC = null; // previous AccessibleContext
5352
5353 private void handleActiveDescendentEvent(PropertyChangeEvent e,
5354 AccessibleContext ac) {
5355 if (e == null || ac == null)
5356 return;
5379 if (e.getNewValue() instanceof Accessible) {
5380 newAC = ((Accessible) e.getNewValue()).getAccessibleContext();
5381 } else if (e.getNewValue() instanceof Component) {
5382 a = Translator.getAccessible(e.getNewValue());
5383 if (a != null) {
5384 newAC = a.getAccessibleContext();
5385 }
5386 }
5387 if (newAC != null) {
5388 Accessible parent = newAC.getAccessibleParent();
5389 if (parent instanceof JTree) {
5390 // use a new AccessibleJTreeNode with the right parent
5391 JTree tree = (JTree)parent;
5392 newAC = new AccessibleJTreeNode(tree,
5393 tree.getSelectionPath(),
5394 null);
5395 }
5396 }
5397 prevAC = newAC;
5398
5399 accessBridge.debugString(" - about to call propertyActiveDescendentChange()");
5400 accessBridge.debugString(" AC: " + ac);
5401 accessBridge.debugString(" old AC: " + oldAC + "new AC: " + newAC);
5402
5403 InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext());
5404 InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext());
5405 accessBridge.propertyActiveDescendentChange(e, ac, oldAC, newAC);
5406 }
5407
5408 /**
5409 * ------- focus event glue
5410 */
5411 private boolean stateChangeListenerAdded = false;
5412
5413 public void focusGained(FocusEvent e) {
5414 processFocusGained();
5415 }
5416
5417 public void stateChanged(ChangeEvent e) {
5418 processFocusGained();
5419 }
5420
5421 private void processFocusGained() {
5422 Component focusOwner = KeyboardFocusManager.
5427
5428 // Only menus and popup selections are handled by the JRootPane.
5429 if (focusOwner instanceof JRootPane) {
5430 MenuElement [] path =
5431 MenuSelectionManager.defaultManager().getSelectedPath();
5432 if (path.length > 1) {
5433 Component penult = path[path.length-2].getComponent();
5434 Component last = path[path.length-1].getComponent();
5435
5436 if (last instanceof JPopupMenu) {
5437 // This is a popup with nothing in the popup
5438 // selected. The menu itself is selected.
5439 FocusEvent e = new FocusEvent(penult, FocusEvent.FOCUS_GAINED);
5440 AccessibleContext context = penult.getAccessibleContext();
5441 InvocationUtils.registerAccessibleContext(context, SunToolkit.targetToAppContext(penult));
5442 accessBridge.focusGained(e, context);
5443 } else if (penult instanceof JPopupMenu) {
5444 // This is a popup with an item selected
5445 FocusEvent e =
5446 new FocusEvent(last, FocusEvent.FOCUS_GAINED);
5447 accessBridge.debugString(" - about to call focusGained()");
5448 AccessibleContext focusedAC = last.getAccessibleContext();
5449 InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(last));
5450 accessBridge.debugString(" AC: " + focusedAC);
5451 accessBridge.focusGained(e, focusedAC);
5452 }
5453 }
5454 } else {
5455 // The focus owner has the selection.
5456 if (focusOwner instanceof Accessible) {
5457 FocusEvent e = new FocusEvent(focusOwner,
5458 FocusEvent.FOCUS_GAINED);
5459 accessBridge.debugString(" - about to call focusGained()");
5460 AccessibleContext focusedAC = focusOwner.getAccessibleContext();
5461 InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(focusOwner));
5462 accessBridge.debugString(" AC: " + focusedAC);
5463 accessBridge.focusGained(e, focusedAC);
5464 }
5465 }
5466 }
5467
5468 public void focusLost(FocusEvent e) {
5469 if (e != null && (javaEventMask & FOCUS_LOST_EVENTS) != 0) {
5470 Accessible a = Translator.getAccessible(e.getSource());
5471 if (a != null) {
5472 accessBridge.debugString(" - about to call focusLost()");
5473 accessBridge.debugString(" AC: " + a.getAccessibleContext());
5474 AccessibleContext context = a.getAccessibleContext();
5475 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5476 accessBridge.focusLost(e, context);
5477 }
5478 }
5479 }
5480
5481 /**
5482 * ------- caret event glue
5483 */
5484 public void caretUpdate(CaretEvent e) {
5485 if (e != null && (javaEventMask & CARET_UPATE_EVENTS) != 0) {
5486 Accessible a = Translator.getAccessible(e.getSource());
5487 if (a != null) {
5488 AccessibleContext context = a.getAccessibleContext();
5489 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5490 accessBridge.caretUpdate(e, context);
5491 }
5492 }
5493 }
6156 private TreePath path = null;
6157 private Accessible accessibleParent = null;
6158 private int index = 0;
6159 private boolean isLeaf = false;
6160
6161 /**
6162 * Constructs an AccessibleJTreeNode
6163 */
6164 AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) {
6165 tree = t;
6166 path = p;
6167 accessibleParent = ap;
6168 if (t != null)
6169 treeModel = t.getModel();
6170 if (p != null) {
6171 obj = p.getLastPathComponent();
6172 if (treeModel != null && obj != null) {
6173 isLeaf = treeModel.isLeaf(obj);
6174 }
6175 }
6176 debugString("AccessibleJTreeNode: name = "+getAccessibleName()+"; TreePath = "+p+"; parent = "+ap);
6177 }
6178
6179 private TreePath getChildTreePath(int i) {
6180 // Tree nodes can't be so complex that they have
6181 // two sets of children -> we're ignoring that case
6182 if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) {
6183 return null;
6184 } else {
6185 Object childObj = treeModel.getChild(obj, i);
6186 Object[] objPath = path.getPath();
6187 Object[] objChildPath = new Object[objPath.length+1];
6188 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
6189 objChildPath[objChildPath.length-1] = childObj;
6190 return new TreePath(objChildPath);
6191 }
6192 }
6193
6194 /**
6195 * Get the AccessibleContext associated with this tree node.
6196 * In the implementation of the Java Accessibility API for
6197 * this class, return this object, which is its own
6198 * AccessibleContext.
6199 *
6200 * @return this object
6201 */
6202 public AccessibleContext getAccessibleContext() {
6203 return this;
6204 }
6205
6206 private AccessibleContext getCurrentAccessibleContext() {
6207 Component c = getCurrentComponent();
6208 if (c instanceof Accessible) {
6209 return (c.getAccessibleContext());
6210 } else {
6211 return null;
6212 }
6213 }
6214
6215 private Component getCurrentComponent() {
6216 debugString("AccessibleJTreeNode: getCurrentComponent");
6217 // is the object visible?
6218 // if so, get row, selected, focus & leaf state,
6219 // and then get the renderer component and return it
6220 if (tree != null && tree.isVisible(path)) {
6221 TreeCellRenderer r = tree.getCellRenderer();
6222 if (r == null) {
6223 debugString(" returning null 1");
6224 return null;
6225 }
6226 TreeUI ui = tree.getUI();
6227 if (ui != null) {
6228 int row = ui.getRowForPath(tree, path);
6229 boolean selected = tree.isPathSelected(path);
6230 boolean expanded = tree.isExpanded(path);
6231 boolean hasFocus = false; // how to tell?? -PK
6232 Component retval = r.getTreeCellRendererComponent(tree, obj,
6233 selected, expanded,
6234 isLeaf, row, hasFocus);
6235 debugString(" returning = "+retval.getClass());
6236 return retval;
6237 }
6238 }
6239 debugString(" returning null 2");
6240 return null;
6241 }
6242
6243 // AccessibleContext methods
6244
6245 /**
6246 * Get the accessible name of this object.
6247 *
6248 * @return the localized name of the object; null if this
6249 * object does not have a name
6250 */
6251 public String getAccessibleName() {
6252 debugString("AccessibleJTreeNode: getAccessibleName");
6253 AccessibleContext ac = getCurrentAccessibleContext();
6254 if (ac != null) {
6255 String name = ac.getAccessibleName();
6256 if ((name != null) && (!name.isEmpty())) {
6257 String retval = ac.getAccessibleName();
6258 debugString(" returning "+retval);
6259 return retval;
6260 } else {
6261 return null;
6262 }
6263 }
6264 if ((accessibleName != null) && (accessibleName.isEmpty())) {
6265 return accessibleName;
6266 } else {
6267 return null;
6268 }
6269 }
6270
6271 /**
6272 * Set the localized accessible name of this object.
6273 *
6274 * @param s the new localized name of the object.
6275 */
6276 public void setAccessibleName(String s) {
6277 AccessibleContext ac = getCurrentAccessibleContext();
6278 if (ac != null) {
|
143 // windows
144 initHWNDcalls();
145
146 // is this a JVM we can use?
147 // install JDK 1.2 and later Swing ToolKit listener
148 EventQueueMonitor.isGUIInitialized();
149
150 // start the Java event handler
151 eventHandler = new EventHandler(this);
152
153 // register for menu selection events
154 MenuSelectionManager.defaultManager().addChangeListener(eventHandler);
155
156 // register as a NativeWindowHandler
157 addNativeWindowHandler(new DefaultNativeWindowHandler());
158
159 // start in a new thread
160 Thread abthread = new Thread(new dllRunner());
161 abthread.setDaemon(true);
162 abthread.start();
163 debugString("[INFO]:AccessBridge started");
164 }
165
166 /*
167 * adaptor to run the AccessBridge DLL
168 */
169 private class dllRunner implements Runnable {
170 public void run() {
171 runDLL();
172 }
173 }
174
175 /*
176 * shutdown hook
177 */
178 private class shutdownHook implements Runnable {
179
180 public void run() {
181 debugString("[INFO]:***** shutdownHook: shutting down...");
182 javaShutdown();
183 }
184 }
185
186
187 /*
188 * Initialize the hashtable that maps Strings to AccessibleRoles.
189 */
190 private void initAccessibleRoleMap() {
191 /*
192 * Initialize the AccessibleRoles map. This code uses methods in
193 * java.lang.reflect.* to build the map.
194 */
195 try {
196 Class<?> clAccessibleRole = Class.forName ("javax.accessibility.AccessibleRole");
197 if (null != clAccessibleRole) {
198 AccessibleRole roleUnknown = AccessibleRole.UNKNOWN;
199 Field [] fields = clAccessibleRole.getFields ();
200 int i = 0;
201 for (i = 0; i < fields.length; i ++) {
280 private Method javaGetComponentFromNativeWindowHandleMethod;
281 private Method javaGetNativeWindowHandleFromComponentMethod;
282
283 // native jawt methods for mapping HWNDs to Java components
284 private native int jawtGetNativeWindowHandleFromComponent(Component comp);
285
286 private native Component jawtGetComponentFromNativeWindowHandle(int handle);
287
288 Toolkit toolkit;
289
290 /**
291 * map an HWND to an AWT Component
292 */
293 private void initHWNDcalls() {
294 Class<?>[] integerParemter = new Class<?>[1];
295 integerParemter[0] = Integer.TYPE;
296 Class<?>[] componentParemter = new Class<?>[1];
297 try {
298 componentParemter[0] = Class.forName("java.awt.Component");
299 } catch (ClassNotFoundException e) {
300 debugString("[ERROR]:Exception: " + e.toString());
301 }
302 toolkit = Toolkit.getDefaultToolkit();
303 return;
304 }
305
306 // native window handler interface
307 private interface NativeWindowHandler {
308 public Accessible getAccessibleFromNativeWindowHandle(int nativeHandle);
309 }
310
311 // hash table of native window handle to AccessibleContext mappings
312 static private ConcurrentHashMap<Integer,AccessibleContext> windowHandleToContextMap = new ConcurrentHashMap<>();
313
314 // hash table of AccessibleContext to native window handle mappings
315 static private ConcurrentHashMap<AccessibleContext,Integer> contextToWindowHandleMap = new ConcurrentHashMap<>();
316
317 /*
318 * adds a virtual window handler to our hash tables
319 */
320 static private void registerVirtualFrame(final Accessible a,
369 return nativeWindowHandlers.removeElement(handler);
370 }
371
372 /**
373 * verifies that a native window handle is a Java window
374 */
375 private boolean isJavaWindow(int nativeHandle) {
376 AccessibleContext ac = getContextFromNativeWindowHandle(nativeHandle);
377 if (ac != null) {
378 saveContextToWindowHandleMapping(ac, nativeHandle);
379 return true;
380 }
381 return false;
382 }
383
384 /*
385 * saves the mapping between an AccessibleContext and a window handle
386 */
387 private void saveContextToWindowHandleMapping(AccessibleContext ac,
388 int nativeHandle) {
389 debugString("[INFO]:saveContextToWindowHandleMapping...");
390 if (ac == null) {
391 return;
392 }
393 if (! contextToWindowHandleMap.containsKey(ac)) {
394 debugString("[INFO]: saveContextToWindowHandleMapping: ac = "+ac+"; handle = "+nativeHandle);
395 contextToWindowHandleMap.put(ac, nativeHandle);
396 }
397 }
398
399 /**
400 * maps a native window handle to an Accessible Context
401 */
402 private AccessibleContext getContextFromNativeWindowHandle(int nativeHandle) {
403 // First, look for the Accessible in our hash table of
404 // virtual window handles.
405 AccessibleContext ac = windowHandleToContextMap.get(nativeHandle);
406 if(ac!=null) {
407 saveContextToWindowHandleMapping(ac, nativeHandle);
408 return ac;
409 }
410
411 // Next, look for the native window handle in our vector
412 // of native window handles.
413 int numHandlers = nativeWindowHandlers.size();
414 for (int i = 0; i < numHandlers; i++) {
417 if (a != null) {
418 ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
419 @Override
420 public AccessibleContext call() throws Exception {
421 return a.getAccessibleContext();
422 }
423 }, a);
424 saveContextToWindowHandleMapping(ac, nativeHandle);
425 return ac;
426 }
427 }
428 // Not found.
429 return null;
430 }
431
432 /**
433 * maps an AccessibleContext to a native window handle
434 * returns 0 on error
435 */
436 private int getNativeWindowHandleFromContext(AccessibleContext ac) {
437 debugString("[INFO]: getNativeWindowHandleFromContext: ac = "+ac);
438 try {
439 return contextToWindowHandleMap.get(ac);
440 } catch (Exception ex) {
441 return 0;
442 }
443 }
444
445 private class DefaultNativeWindowHandler implements NativeWindowHandler {
446 /*
447 * returns the Accessible associated with a native window
448 */
449 public Accessible getAccessibleFromNativeWindowHandle(int nativeHandle) {
450 final Component c = jawtGetComponentFromNativeWindowHandle(nativeHandle);
451 if (c instanceof Accessible) {
452 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
453 @Override
454 public AccessibleContext call() throws Exception {
455 return c.getAccessibleContext();
456 }
457 }, c);
498 public AccessibleContext call() throws Exception {
499 Accessible parent = ac.getAccessibleParent();
500 if (parent == null) {
501 return ac;
502 }
503 Accessible tmp = parent.getAccessibleContext().getAccessibleParent();
504 while (tmp != null) {
505 parent = tmp;
506 tmp = parent.getAccessibleContext().getAccessibleParent();
507 }
508 return parent.getAccessibleContext();
509 }
510 }, ac);
511 }
512
513 /*
514 * StarOffice version that does not use the EventQueueMonitor
515 */
516 private AccessibleContext getAccessibleContextAt_1(final int x, final int y,
517 final AccessibleContext parent) {
518 debugString("[INFO]: getAccessibleContextAt_1 called");
519 debugString(" -> x = " + x + " y = " + y + " parent = " + parent);
520
521 if (parent == null) return null;
522 final AccessibleComponent acmp = InvocationUtils.invokeAndWait(new Callable<AccessibleComponent>() {
523 @Override
524 public AccessibleComponent call() throws Exception {
525 return parent.getAccessibleComponent();
526 }
527 }, parent);
528 if (acmp!=null) {
529 final Point loc = InvocationUtils.invokeAndWait(new Callable<Point>() {
530 @Override
531 public Point call() throws Exception {
532 return acmp.getLocation();
533 }
534 }, parent);
535 final Accessible a = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
536 @Override
537 public Accessible call() throws Exception {
538 return acmp.getAccessibleAt(new Point(x - loc.x, y - loc.y));
546 }
547 }, parent);
548 if (foundAC != null) {
549 if (foundAC != parent) {
550 // recurse down into the child
551 return getAccessibleContextAt_1(x - loc.x, y - loc.y,
552 foundAC);
553 } else
554 return foundAC;
555 }
556 }
557 }
558 return parent;
559 }
560
561 /*
562 * AWT/Swing version
563 */
564 private AccessibleContext getAccessibleContextAt_2(final int x, final int y,
565 AccessibleContext parent) {
566 debugString("[INFO]: getAccessibleContextAt_2 called");
567 debugString(" -> x = " + x + " y = " + y + " parent = " + parent);
568
569 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
570 @Override
571 public AccessibleContext call() throws Exception {
572 Accessible a = EventQueueMonitor.getAccessibleAt(new Point(x, y));
573 if (a != null) {
574 AccessibleContext childAC = a.getAccessibleContext();
575 if (childAC != null) {
576 debugString("[INFO]: returning childAC = " + childAC);
577 return childAC;
578 }
579 }
580 return null;
581 }
582 }, parent);
583 }
584
585 /**
586 * returns the Accessible that has focus
587 */
588 private AccessibleContext getAccessibleContextWithFocus() {
589 Component c = AWTEventMonitor.getComponentWithFocus();
590 if (c != null) {
591 final Accessible a = Translator.getAccessible(c);
592 if (a != null) {
593 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
594 @Override
595 public AccessibleContext call() throws Exception {
596 return a.getAccessibleContext();
597 }
598 }, c);
599 if (ac != null) {
600 return ac;
601 }
602 }
603 }
604 return null;
605 }
606
607 /**
608 * returns the AccessibleName from an AccessibleContext
609 */
610 private String getAccessibleNameFromContext(final AccessibleContext ac) {
611 debugString("[INFO]: ***** ac = "+ac.getClass());
612 if (ac != null) {
613 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
614 @Override
615 public String call() throws Exception {
616 return ac.getAccessibleName();
617 }
618 }, ac);
619 if (s != null) {
620 references.increment(s);
621 debugString("[INFO]: Returning AccessibleName from Context: " + s);
622 return s;
623 } else {
624 return null;
625 }
626 } else {
627 debugString("[INFO]: getAccessibleNameFromContext; ac = null!");
628 return null;
629 }
630 }
631
632 /**
633 * Returns an AccessibleName for a component using an algorithm optimized
634 * for the JAWS screen reader. This method is only intended for JAWS. All
635 * other uses are entirely optional.
636 */
637 private String getVirtualAccessibleNameFromContext(final AccessibleContext ac) {
638 if (null != ac) {
639 /*
640 Step 1:
641 =======
642 Determine if we can obtain the Virtual Accessible Name from the
643 Accessible Name or Accessible Description of the object.
644 */
645 String nameString = InvocationUtils.invokeAndWait(new Callable<String>() {
646 @Override
647 public String call() throws Exception {
648 return ac.getAccessibleName();
649 }
650 }, ac);
651 if ( ( null != nameString ) && ( 0 != nameString.length () ) ) {
652 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from AccessibleContext::getAccessibleName.");
653 references.increment (nameString);
654 return nameString;
655 }
656 String descriptionString = InvocationUtils.invokeAndWait(new Callable<String>() {
657 @Override
658 public String call() throws Exception {
659 return ac.getAccessibleDescription();
660 }
661 }, ac);
662 if ( ( null != descriptionString ) && ( 0 != descriptionString.length () ) ) {
663 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from AccessibleContext::getAccessibleDescription.");
664 references.increment (descriptionString);
665 return descriptionString;
666 }
667
668 debugString ("[WARN]: The Virtual Accessible Name was not found using AccessibleContext::getAccessibleDescription. or getAccessibleName");
669 /*
670 Step 2:
671 =======
672 Decide whether the extended name search algorithm should be
673 used for this object.
674 */
675 boolean bExtendedSearch = false;
676 AccessibleRole role = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
677 @Override
678 public AccessibleRole call() throws Exception {
679 return ac.getAccessibleRole();
680 }
681 }, ac);
682 AccessibleContext parentContext = null;
683 AccessibleRole parentRole = AccessibleRole.UNKNOWN;
684
685 if ( extendedVirtualNameSearchRoles.contains (role) ) {
686 parentContext = getAccessibleParentFromContext (ac);
687 if ( null != parentContext ) {
688 final AccessibleContext parentContextInnerTemp = parentContext;
689 parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
690 @Override
691 public AccessibleRole call() throws Exception {
692 return parentContextInnerTemp.getAccessibleRole();
693 }
694 }, ac);
695 if ( AccessibleRole.UNKNOWN != parentRole ) {
696 bExtendedSearch = true;
697 if ( noExtendedVirtualNameSearchParentRoles.contains (parentRole) ) {
698 bExtendedSearch = false;
699 }
700 }
701 }
702 }
703
704 if (false == bExtendedSearch) {
705 debugString ("[INFO]: bk -- getVirtualAccessibleNameFromContext will not use the extended name search algorithm. role = " + ( role != null ? role.toDisplayString(Locale.US) : "null") );
706 /*
707 Step 3:
708 =======
709 We have determined that we should not use the extended name
710 search algorithm for this object (we must obtain the name of
711 the object from the object itself and not from neighboring
712 objects). However the object name cannot be obtained from
713 the Accessible Name or Accessible Description of the object.
714
715 Handle several special cases here that might yield a value for
716 the Virtual Accessible Name. Return null if the object does
717 not match the criteria for any of these special cases.
718 */
719 if (AccessibleRole.LABEL == role) {
720 /*
721 Does the label support the Accessible Text Interface?
722 */
723 final AccessibleText at = InvocationUtils.invokeAndWait(new Callable<AccessibleText>() {
724 @Override
725 public AccessibleText call() throws Exception {
726 return ac.getAccessibleText();
727 }
728 }, ac);
729 if (null != at) {
730 int charCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
731 @Override
732 public Integer call() throws Exception {
733 return at.getCharCount();
734 }
735 }, ac);
736 String text = getAccessibleTextRangeFromContext (ac, 0, charCount);
737 if (null != text) {
738 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the Accessible Text of the LABEL object.");
739 references.increment (text);
740 return text;
741 }
742 }
743 /*
744 Does the label support the Accessible Icon Interface?
745 */
746 debugString ("[INFO]: bk -- Attempting to obtain the Virtual Accessible Name from the Accessible Icon information.");
747 final AccessibleIcon [] ai = InvocationUtils.invokeAndWait(new Callable<AccessibleIcon[]>() {
748 @Override
749 public AccessibleIcon[] call() throws Exception {
750 return ac.getAccessibleIcon();
751 }
752 }, ac);
753 if ( (null != ai) && (ai.length > 0) ) {
754 String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
755 @Override
756 public String call() throws Exception {
757 return ai[0].getAccessibleIconDescription();
758 }
759 }, ac);
760 if (iconDescription != null){
761 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the LABEL object.");
762 references.increment (iconDescription);
763 return iconDescription;
764 }
765 } else {
766 parentContext = getAccessibleParentFromContext (ac);
767 if ( null != parentContext ) {
768 final AccessibleContext parentContextInnerTemp = parentContext;
769 parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
770 @Override
771 public AccessibleRole call() throws Exception {
772 return parentContextInnerTemp.getAccessibleRole();
773 }
774 }, ac);
775 if ( AccessibleRole.TABLE == parentRole ) {
776 int indexInParent = InvocationUtils.invokeAndWait(new Callable<Integer>() {
777 @Override
778 public Integer call() throws Exception {
779 return ac.getAccessibleIndexInParent();
780 }
781 }, ac);
782 final AccessibleContext acTableCell = getAccessibleChildFromContext (parentContext, indexInParent);
783 debugString ("[INFO]: bk -- Making a second attempt to obtain the Virtual Accessible Name from the Accessible Icon information for the Table Cell.");
784 if (acTableCell != null) {
785 final AccessibleIcon [] aiRet =InvocationUtils.invokeAndWait(new Callable<AccessibleIcon[]>() {
786 @Override
787 public AccessibleIcon[] call() throws Exception {
788 return acTableCell.getAccessibleIcon();
789 }
790 }, ac);
791 if ( (null != aiRet) && (aiRet.length > 0) ) {
792 String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
793 @Override
794 public String call() throws Exception {
795 return aiRet[0].getAccessibleIconDescription();
796 }
797 }, ac);
798 if (iconDescription != null){
799 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the Table Cell object.");
800 references.increment (iconDescription);
801 return iconDescription;
802 }
803 }
804 }
805 }
806 }
807 }
808 } else if ( (AccessibleRole.TOGGLE_BUTTON == role) ||
809 (AccessibleRole.PUSH_BUTTON == role) ) {
810 /*
811 Does the button support the Accessible Icon Interface?
812 */
813 debugString ("[INFO]: bk -- Attempting to obtain the Virtual Accessible Name from the Accessible Icon information.");
814 final AccessibleIcon [] ai = InvocationUtils.invokeAndWait(new Callable<AccessibleIcon[]>() {
815 @Override
816 public AccessibleIcon[] call() throws Exception {
817 return ac.getAccessibleIcon();
818 }
819 }, ac);
820 if ( (null != ai) && (ai.length > 0) ) {
821 String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
822 @Override
823 public String call() throws Exception {
824 return ai[0].getAccessibleIconDescription();
825 }
826 }, ac);
827 if (iconDescription != null){
828 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the TOGGLE_BUTTON or PUSH_BUTTON object.");
829 references.increment (iconDescription);
830 return iconDescription;
831 }
832 }
833 } else if ( AccessibleRole.CHECK_BOX == role ) {
834 /*
835 NOTE: The only case I know of in which a check box does not
836 have a name is when that check box is contained in a table.
837
838 In this case it would be appropriate to use the display string
839 of the check box object as the name (in US English the display
840 string is typically either "true" or "false").
841
842 I am using the AccessibleValue interface to obtain the display
843 string of the check box. If the Accessible Value is 1, I am
844 returning Boolean.TRUE.toString (), If the Accessible Value is
845 0, I am returning Boolean.FALSE.toString (). If the Accessible
846 Value is some other number, I will return the display string of
847 the current numerical value of the check box.
848 */
888 @Override
889 public String call() throws Exception {
890 return parentContextOuterTemp.getAccessibleName();
891 }
892 }, ac);
893 String parentDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
894 @Override
895 public String call() throws Exception {
896 return parentContextOuterTemp.getAccessibleDescription();
897 }
898 }, ac);
899
900 /*
901 Step 4:
902 =======
903 Special case for Slider Bar objects.
904 */
905 if ( (AccessibleRole.SLIDER == role) &&
906 (AccessibleRole.PANEL == parentRole) &&
907 (null != parentName) ) {
908 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the Accessible Name of the SLIDER object's parent object.");
909 references.increment (parentName);
910 return parentName;
911 }
912
913 boolean bIsEditCombo = false;
914
915 AccessibleContext testContext = ac;
916 /*
917 Step 5:
918 =======
919 Special case for Edit Combo Boxes
920 */
921 if ( (AccessibleRole.TEXT == role) &&
922 (AccessibleRole.COMBO_BOX == parentRole) ) {
923 bIsEditCombo = true;
924 if (null != parentName) {
925 debugString ("[INFO]: bk -- The Virtual Accessible Name for this Edit Combo box was obtained from the Accessible Name of the object's parent object.");
926 references.increment (parentName);
927 return parentName;
928 } else if (null != parentDescription) {
929 debugString ("[INFO]: bk -- The Virtual Accessible Name for this Edit Combo box was obtained from the Accessible Description of the object's parent object.");
930 references.increment (parentDescription);
931 return parentDescription;
932 }
933 testContext = parentContext;
934 parentRole = AccessibleRole.UNKNOWN;
935 parentContext = getAccessibleParentFromContext (testContext);
936 if ( null != parentContext ) {
937 final AccessibleContext parentContextInnerTemp = parentContext;
938 parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
939 @Override
940 public AccessibleRole call() throws Exception {
941 return parentContextInnerTemp.getAccessibleRole();
942 }
943 }, ac);
944 }
945 }
946
947 /*
948 Step 6:
949 =======
952 */
953 {
954 final AccessibleContext parentContextTempInner = parentContext;
955 AccessibleRelationSet ars = InvocationUtils.invokeAndWait(new Callable<AccessibleRelationSet>() {
956 @Override
957 public AccessibleRelationSet call() throws Exception {
958 return parentContextTempInner.getAccessibleRelationSet();
959 }
960 }, ac);
961 if ( ars != null && (ars.size () > 0) && (ars.contains (AccessibleRelation.LABELED_BY)) ) {
962 AccessibleRelation labeledByRelation = ars.get (AccessibleRelation.LABELED_BY);
963 if (labeledByRelation != null) {
964 Object [] targets = labeledByRelation.getTarget ();
965 Object o = targets [0];
966 if (o instanceof Accessible) {
967 AccessibleContext labelContext = ((Accessible)o).getAccessibleContext ();
968 if (labelContext != null) {
969 String labelName = labelContext.getAccessibleName ();
970 String labelDescription = labelContext.getAccessibleDescription ();
971 if (null != labelName) {
972 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained using the LABELED_BY AccessibleRelation -- Name Case.");
973 references.increment (labelName);
974 return labelName;
975 } else if (null != labelDescription) {
976 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained using the LABELED_BY AccessibleRelation -- Description Case.");
977 references.increment (labelDescription);
978 return labelDescription;
979 }
980 }
981 }
982 }
983 }
984 }
985
986 //Note: add AccessibleContext to use InvocationUtils.invokeAndWait
987 /*
988 Step 7:
989 =======
990 Search for a label object that is positioned either just to the left
991 or just above the object and get the Accessible Name of the Label
992 object.
993 */
994 int testIndexMax = 0;
995 int testX = 0;
996 int testY = 0;
1051 AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
1052 @Override
1053 public AccessibleRole call() throws Exception {
1054 return childContext.getAccessibleRole();
1055 }
1056 }, ac);
1057 if ( AccessibleRole.LABEL == childRole ) {
1058 childX = getAccessibleXcoordFromContext (childContext);
1059 childY = getAccessibleYcoordFromContext (childContext);
1060 childWidth = getAccessibleWidthFromContext (childContext);
1061 childHeight = getAccessibleHeightFromContext (childContext);
1062 if ( (childX < testX) &&
1063 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1064 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1065 @Override
1066 public String call() throws Exception {
1067 return childContext.getAccessibleName();
1068 }
1069 }, ac);
1070 if ( null != childName ) {
1071 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned to the left of the object.");
1072 references.increment (childName);
1073 return childName;
1074 }
1075 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1076 @Override
1077 public String call() throws Exception {
1078 return childContext.getAccessibleDescription();
1079 }
1080 }, ac);
1081 if ( null != childDescription ) {
1082 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned to the left of the object.");
1083 references.increment (childDescription);
1084 return childDescription;
1085 }
1086 } else if ( (childY < targetY) &&
1087 ((childX <= targetX) && (targetX <= (childX + childWidth))) ) {
1088 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1089 @Override
1090 public String call() throws Exception {
1091 return childContext.getAccessibleName();
1092 }
1093 }, ac);
1094 if ( null != childName ) {
1095 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned above the object.");
1096 references.increment (childName);
1097 return childName;
1098 }
1099 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1100 @Override
1101 public String call() throws Exception {
1102 return childContext.getAccessibleDescription();
1103 }
1104 }, ac);
1105 if ( null != childDescription ) {
1106 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned above the object.");
1107 references.increment (childDescription);
1108 return childDescription;
1109 }
1110 }
1111 }
1112 }
1113 }
1114 childIndex --;
1115 }
1116 childIndex = testIndex + 1;
1117 while (childIndex <= testIndexMax) {
1118 final int childIndexTemp = childIndex;
1119 final AccessibleContext parentContextInnerTemp = parentContext;
1120 final Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
1121 @Override
1122 public Accessible call() throws Exception {
1123 return parentContextInnerTemp.getAccessibleChild(childIndexTemp);
1124 }
1125 }, ac);
1126 if ( null != child ) {
1134 AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
1135 @Override
1136 public AccessibleRole call() throws Exception {
1137 return childContext.getAccessibleRole();
1138 }
1139 }, ac);
1140 if ( AccessibleRole.LABEL == childRole ) {
1141 childX = getAccessibleXcoordFromContext (childContext);
1142 childY = getAccessibleYcoordFromContext (childContext);
1143 childWidth = getAccessibleWidthFromContext (childContext);
1144 childHeight = getAccessibleHeightFromContext (childContext);
1145 if ( (childX < testX) &&
1146 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1147 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1148 @Override
1149 public String call() throws Exception {
1150 return childContext.getAccessibleName();
1151 }
1152 }, ac);
1153 if ( null != childName ) {
1154 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned to the left of the object.");
1155 references.increment (childName);
1156 return childName;
1157 }
1158 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1159 @Override
1160 public String call() throws Exception {
1161 return childContext.getAccessibleDescription();
1162 }
1163 }, ac);
1164 if ( null != childDescription ) {
1165 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned to the left of the object.");
1166 references.increment (childDescription);
1167 return childDescription;
1168 }
1169 } else if ( (childY < targetY) &&
1170 ((childX <= targetX) && (targetX <= (childX + childWidth))) ) {
1171 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1172 @Override
1173 public String call() throws Exception {
1174 return childContext.getAccessibleName();
1175 }
1176 }, ac);
1177 if ( null != childName ) {
1178 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned above the object.");
1179 references.increment (childName);
1180 return childName;
1181 }
1182 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1183 @Override
1184 public String call() throws Exception {
1185 return childContext.getAccessibleDescription();
1186 }
1187 }, ac);
1188 if ( null != childDescription ) {
1189 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned above the object.");
1190 references.increment (childDescription);
1191 return childDescription;
1192 }
1193 }
1194 }
1195 }
1196 }
1197 childIndex ++;
1198 }
1199 /*
1200 Step 8:
1201 =======
1202 Special case for combo boxes and text objects, based on a
1203 similar special case I found in some of our internal JAWS code.
1204
1205 Search for a button object that is positioned either just to the left
1206 or just above the object and get the Accessible Name of the button
1207 object.
1208 */
1209 if ( (AccessibleRole.TEXT == role) ||
1231 @Override
1232 public AccessibleRole call() throws Exception {
1233 return childContext.getAccessibleRole();
1234 }
1235 }, ac);
1236 if ( ( AccessibleRole.PUSH_BUTTON == childRole ) ||
1237 ( AccessibleRole.TOGGLE_BUTTON == childRole )) {
1238 childX = getAccessibleXcoordFromContext (childContext);
1239 childY = getAccessibleYcoordFromContext (childContext);
1240 childWidth = getAccessibleWidthFromContext (childContext);
1241 childHeight = getAccessibleHeightFromContext (childContext);
1242 if ( (childX < testX) &&
1243 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1244 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1245 @Override
1246 public String call() throws Exception {
1247 return childContext.getAccessibleName();
1248 }
1249 }, ac);
1250 if ( null != childName ) {
1251 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1252 references.increment (childName);
1253 return childName;
1254 }
1255 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1256 @Override
1257 public String call() throws Exception {
1258 return childContext.getAccessibleDescription();
1259 }
1260 }, ac);
1261 if ( null != childDescription ) {
1262 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1263 references.increment (childDescription);
1264 return childDescription;
1265 }
1266 }
1267 }
1268 }
1269 }
1270 childIndex --;
1271 }
1272 childIndex = testIndex + 1;
1273 while (childIndex <= testIndexMax) {
1274 final int childIndexTemp = childIndex;
1275 final AccessibleContext parentContextInnerTemp = parentContext;
1276 final Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
1277 @Override
1278 public Accessible call() throws Exception {
1279 return parentContextInnerTemp.getAccessibleChild(childIndexTemp);
1280 }
1281 }, ac);
1282 if ( null != child ) {
1291 @Override
1292 public AccessibleRole call() throws Exception {
1293 return childContext.getAccessibleRole();
1294 }
1295 }, ac);
1296 if ( ( AccessibleRole.PUSH_BUTTON == childRole ) ||
1297 ( AccessibleRole.TOGGLE_BUTTON == childRole ) ) {
1298 childX = getAccessibleXcoordFromContext (childContext);
1299 childY = getAccessibleYcoordFromContext (childContext);
1300 childWidth = getAccessibleWidthFromContext (childContext);
1301 childHeight = getAccessibleHeightFromContext (childContext);
1302 if ( (childX < testX) &&
1303 ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1304 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1305 @Override
1306 public String call() throws Exception {
1307 return childContext.getAccessibleName();
1308 }
1309 }, ac);
1310 if ( null != childName ) {
1311 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1312 references.increment (childName);
1313 return childName;
1314 }
1315 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1316 @Override
1317 public String call() throws Exception {
1318 return childContext.getAccessibleDescription();
1319 }
1320 }, ac);
1321 if ( null != childDescription ) {
1322 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1323 references.increment (childDescription);
1324 return childDescription;
1325 }
1326 }
1327 }
1328 }
1329 }
1330 childIndex ++;
1331 }
1332 }
1333 return null;
1334 } else {
1335 debugString ("[ERROR]: AccessBridge::getVirtualAccessibleNameFromContext error - ac == null.");
1336 return null;
1337 }
1338 }
1339
1340 /**
1341 * returns the AccessibleDescription from an AccessibleContext
1342 */
1343 private String getAccessibleDescriptionFromContext(final AccessibleContext ac) {
1344 if (ac != null) {
1345 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1346 @Override
1347 public String call() throws Exception {
1348 return ac.getAccessibleDescription();
1349 }
1350 }, ac);
1351 if (s != null) {
1352 references.increment(s);
1353 debugString("[INFO]: Returning AccessibleDescription from Context: " + s);
1354 return s;
1355 }
1356 } else {
1357 debugString("[ERROR]: getAccessibleDescriptionFromContext; ac = null");
1358 }
1359 return null;
1360 }
1361
1362 /**
1363 * returns the AccessibleRole from an AccessibleContext
1364 */
1365 private String getAccessibleRoleStringFromContext(final AccessibleContext ac) {
1366 if (ac != null) {
1367 AccessibleRole role = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
1368 @Override
1369 public AccessibleRole call() throws Exception {
1370 return ac.getAccessibleRole();
1371 }
1372 }, ac);
1373 if (role != null) {
1374 String s = role.toDisplayString(Locale.US);
1375 if (s != null) {
1376 references.increment(s);
1377 debugString("[INFO]: Returning AccessibleRole from Context: " + s);
1378 return s;
1379 }
1380 }
1381 } else {
1382 debugString("[ERROR]: getAccessibleRoleStringFromContext; ac = null");
1383 }
1384 return null;
1385 }
1386
1387 /**
1388 * return the AccessibleRole from an AccessibleContext in the en_US locale
1389 */
1390 private String getAccessibleRoleStringFromContext_en_US(final AccessibleContext ac) {
1391 return getAccessibleRoleStringFromContext(ac);
1392 }
1393
1394 /**
1395 * return the AccessibleStates from an AccessibleContext
1396 */
1397 private String getAccessibleStatesStringFromContext(final AccessibleContext ac) {
1398 if (ac != null) {
1399 AccessibleStateSet stateSet = InvocationUtils.invokeAndWait(new Callable<AccessibleStateSet>() {
1400 @Override
1401 public AccessibleStateSet call() throws Exception {
1402 return ac.getAccessibleStateSet();
1403 }
1404 }, ac);
1405 if (stateSet != null) {
1406 String s = stateSet.toString();
1407 if (s != null &&
1408 s.indexOf(AccessibleState.MANAGES_DESCENDANTS.toDisplayString(Locale.US)) == -1) {
1409 // Indicate whether this component manages its own
1410 // children
1411 AccessibleRole role = InvocationUtils.invokeAndWait(() -> {
1412 return ac.getAccessibleRole();
1413 }, ac);
1414 if (role == AccessibleRole.LIST ||
1415 role == AccessibleRole.TABLE ||
1416 role == AccessibleRole.TREE) {
1417 s += ",";
1418 s += AccessibleState.MANAGES_DESCENDANTS.toDisplayString(Locale.US);
1419 }
1420 references.increment(s);
1421 debugString("[INFO]: Returning AccessibleStateSet from Context: " + s);
1422 return s;
1423 }
1424 }
1425 } else {
1426 debugString("[ERROR]: getAccessibleStatesStringFromContext; ac = null");
1427 }
1428 return null;
1429 }
1430
1431 /**
1432 * returns the AccessibleStates from an AccessibleContext in the en_US locale
1433 */
1434 private String getAccessibleStatesStringFromContext_en_US(final AccessibleContext ac) {
1435 if (ac != null) {
1436 AccessibleStateSet stateSet = InvocationUtils.invokeAndWait(new Callable<AccessibleStateSet>() {
1437 @Override
1438 public AccessibleStateSet call() throws Exception {
1439 return ac.getAccessibleStateSet();
1440 }
1441 }, ac);
1442 if (stateSet != null) {
1443 String s = "";
1444 AccessibleState[] states = stateSet.toArray();
1445 if (states != null && states.length > 0) {
1446 s = states[0].toDisplayString(Locale.US);
1447 for (int i = 1; i < states.length; i++) {
1448 s = s + "," + states[i].toDisplayString(Locale.US);
1449 }
1450 }
1451 references.increment(s);
1452 debugString("[INFO]: Returning AccessibleStateSet en_US from Context: " + s);
1453 return s;
1454 }
1455 }
1456 debugString("[ERROR]: getAccessibleStatesStringFromContext; ac = null");
1457 return null;
1458 }
1459
1460 /**
1461 * returns the AccessibleParent from an AccessibleContext
1462 */
1463 private AccessibleContext getAccessibleParentFromContext(final AccessibleContext ac) {
1464 if (ac==null)
1465 return null;
1466 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
1467 @Override
1468 public AccessibleContext call() throws Exception {
1469 Accessible a = ac.getAccessibleParent();
1470 if (a != null) {
1471 AccessibleContext apc = a.getAccessibleContext();
1472 if (apc != null) {
1473 return apc;
1474 }
1475 }
1476 return null;
1590 r.y = p.y;
1591 return r;
1592 }
1593 } catch (Exception e) {
1594 return null;
1595 }
1596 }
1597 }
1598 return null;
1599 }
1600 }, ac);
1601 }
1602
1603 /**
1604 * returns the AccessibleComponent x-coord from an AccessibleContext
1605 */
1606 private int getAccessibleXcoordFromContext(AccessibleContext ac) {
1607 if (ac != null) {
1608 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1609 if (r != null) {
1610 debugString("[INFO]: Returning Accessible x coord from Context: " + r.x);
1611 return r.x;
1612 }
1613 } else {
1614 debugString("[ERROR]: getAccessibleXcoordFromContext ac = null");
1615 }
1616 return -1;
1617 }
1618
1619 /**
1620 * returns the AccessibleComponent y-coord from an AccessibleContext
1621 */
1622 private int getAccessibleYcoordFromContext(AccessibleContext ac) {
1623 debugString("[INFO]: getAccessibleYcoordFromContext() called");
1624 if (ac != null) {
1625 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1626 if (r != null) {
1627 return r.y;
1628 }
1629 } else {
1630 debugString("[ERROR]: getAccessibleYcoordFromContext; ac = null");
1631 }
1632 return -1;
1633 }
1634
1635 /**
1636 * returns the AccessibleComponent height from an AccessibleContext
1637 */
1638 private int getAccessibleHeightFromContext(AccessibleContext ac) {
1639 if (ac != null) {
1640 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1641 if (r != null) {
1642 return r.height;
1643 }
1644 } else {
1645 debugString("[ERROR]: getAccessibleHeightFromContext; ac = null");
1646 }
1647 return -1;
1648 }
1649
1650 /**
1651 * returns the AccessibleComponent width from an AccessibleContext
1652 */
1653 private int getAccessibleWidthFromContext(AccessibleContext ac) {
1654 if (ac != null) {
1655 Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1656 if (r != null) {
1657 return r.width;
1658 }
1659 } else {
1660 debugString("[ERROR]: getAccessibleWidthFromContext; ac = null");
1661 }
1662 return -1;
1663 }
1664
1665
1666 /**
1667 * returns the AccessibleComponent from an AccessibleContext
1668 */
1669 private AccessibleComponent getAccessibleComponentFromContext(AccessibleContext ac) {
1670 if (ac != null) {
1671 AccessibleComponent acmp = InvocationUtils.invokeAndWait(() -> {
1672 return ac.getAccessibleComponent();
1673 }, ac);
1674 if (acmp != null) {
1675 debugString("[INFO]: Returning AccessibleComponent Context");
1676 return acmp;
1677 }
1678 } else {
1679 debugString("[ERROR]: getAccessibleComponentFromContext; ac = null");
1680 }
1681 return null;
1682 }
1683
1684 /**
1685 * returns the AccessibleAction from an AccessibleContext
1686 */
1687 private AccessibleAction getAccessibleActionFromContext(final AccessibleContext ac) {
1688 debugString("[INFO]: Returning AccessibleAction Context");
1689 return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleAction>() {
1690 @Override
1691 public AccessibleAction call() throws Exception {
1692 return ac.getAccessibleAction();
1693 }
1694 }, ac);
1695 }
1696
1697 /**
1698 * returns the AccessibleSelection from an AccessibleContext
1699 */
1700 private AccessibleSelection getAccessibleSelectionFromContext(final AccessibleContext ac) {
1701 return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleSelection>() {
1702 @Override
1703 public AccessibleSelection call() throws Exception {
1704 return ac.getAccessibleSelection();
1705 }
1706 }, ac);
1707 }
1708
1720
1721 /**
1722 * return the AccessibleComponent from an AccessibleContext
1723 */
1724 private AccessibleValue getAccessibleValueFromContext(final AccessibleContext ac) {
1725 return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleValue>() {
1726 @Override
1727 public AccessibleValue call() throws Exception {
1728 return ac.getAccessibleValue();
1729 }
1730 }, ac);
1731 }
1732
1733 /* ===== AccessibleText methods ===== */
1734
1735 /**
1736 * returns the bounding rectangle for the text cursor
1737 * XXX
1738 */
1739 private Rectangle getCaretLocation(final AccessibleContext ac) {
1740 debugString("[INFO]: getCaretLocation");
1741 if (ac==null)
1742 return null;
1743 return InvocationUtils.invokeAndWait(new Callable<Rectangle>() {
1744 @Override
1745 public Rectangle call() throws Exception {
1746 // workaround for JAAPI not returning cursor bounding rectangle
1747 Rectangle r = null;
1748 Accessible parent = ac.getAccessibleParent();
1749 if (parent instanceof Accessible) {
1750 int indexInParent = ac.getAccessibleIndexInParent();
1751 Accessible child =
1752 parent.getAccessibleContext().getAccessibleChild(indexInParent);
1753
1754 if (child instanceof JTextComponent) {
1755 JTextComponent text = (JTextComponent) child;
1756 try {
1757 r = text.modelToView2D(text.getCaretPosition()).getBounds();
1758 if (r != null) {
1759 Point p = text.getLocationOnScreen();
1760 r.translate(p.x, p.y);
1841 if (ac==null)
1842 return -1;
1843 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
1844 @Override
1845 public Integer call() throws Exception {
1846 AccessibleText at = ac.getAccessibleText();
1847 if (at != null) {
1848 return at.getCaretPosition();
1849 }
1850 return -1;
1851 }
1852 }, ac);
1853 }
1854
1855 /**
1856 * Return the index at a specific point from an AccessibleContext
1857 * Point(x, y) is in screen coordinates.
1858 */
1859 private int getAccessibleIndexAtPointFromContext(final AccessibleContext ac,
1860 final int x, final int y) {
1861 debugString("[INFO]: getAccessibleIndexAtPointFromContext: x = "+x+"; y = "+y);
1862 if (ac==null)
1863 return -1;
1864 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
1865 @Override
1866 public Integer call() throws Exception {
1867 AccessibleText at = ac.getAccessibleText();
1868 AccessibleComponent acomp = ac.getAccessibleComponent();
1869 if (at != null && acomp != null) {
1870 // Convert x and y from screen coordinates to
1871 // local coordinates.
1872 try {
1873 Point p = acomp.getLocationOnScreen();
1874 int x1, y1;
1875 if (p != null) {
1876 x1 = x - p.x;
1877 if (x1 < 0) {
1878 x1 = 0;
1879 }
1880 y1 = y - p.y;
1881 if (y1 < 0) {
1895 }
1896
1897 /**
1898 * return the letter at a specific point from an AccessibleContext
1899 */
1900 private String getAccessibleLetterAtIndexFromContext(final AccessibleContext ac, final int index) {
1901 if (ac != null) {
1902 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1903 @Override
1904 public String call() throws Exception {
1905 AccessibleText at = ac.getAccessibleText();
1906 if (at == null) return null;
1907 return at.getAtIndex(AccessibleText.CHARACTER, index);
1908 }
1909 }, ac);
1910 if (s != null) {
1911 references.increment(s);
1912 return s;
1913 }
1914 } else {
1915 debugString("[ERROR]: getAccessibleLetterAtIndexFromContext; ac = null");
1916 }
1917 return null;
1918 }
1919
1920 /**
1921 * return the word at a specific point from an AccessibleContext
1922 */
1923 private String getAccessibleWordAtIndexFromContext(final AccessibleContext ac, final int index) {
1924 if (ac != null) {
1925 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1926 @Override
1927 public String call() throws Exception {
1928 AccessibleText at = ac.getAccessibleText();
1929 if (at == null) return null;
1930 return at.getAtIndex(AccessibleText.WORD, index);
1931 }
1932 }, ac);
1933 if (s != null) {
1934 references.increment(s);
1935 return s;
1936 }
1937 } else {
1938 debugString("[ERROR]: getAccessibleWordAtIndexFromContext; ac = null");
1939 }
1940 return null;
1941 }
1942
1943 /**
1944 * return the sentence at a specific point from an AccessibleContext
1945 */
1946 private String getAccessibleSentenceAtIndexFromContext(final AccessibleContext ac, final int index) {
1947 if (ac != null) {
1948 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1949 @Override
1950 public String call() throws Exception {
1951 AccessibleText at = ac.getAccessibleText();
1952 if (at == null) return null;
1953 return at.getAtIndex(AccessibleText.SENTENCE, index);
1954 }
1955 }, ac);
1956 if (s != null) {
1957 references.increment(s);
1958 return s;
1959 }
1960 } else {
1961 debugString("[ERROR]: getAccessibleSentenceAtIndexFromContext; ac = null");
1962 }
1963 return null;
1964 }
1965
1966 /**
1967 * return the text selection start from an AccessibleContext
1968 */
1969 private int getAccessibleTextSelectionStartFromContext(final AccessibleContext ac) {
1970 if (ac == null) return -1;
1971 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
1972 @Override
1973 public Integer call() throws Exception {
1974 AccessibleText at = ac.getAccessibleText();
1975 if (at != null) {
1976 return at.getSelectionStart();
1977 }
1978 return -1;
1979 }
1980 }, ac);
1981 }
1999 }
2000
2001 /**
2002 * return the selected text from an AccessibleContext
2003 */
2004 private String getAccessibleTextSelectedTextFromContext(final AccessibleContext ac) {
2005 if (ac != null) {
2006 String s = InvocationUtils.invokeAndWait(new Callable<String>() {
2007 @Override
2008 public String call() throws Exception {
2009 AccessibleText at = ac.getAccessibleText();
2010 if (at == null) return null;
2011 return at.getSelectedText();
2012 }
2013 }, ac);
2014 if (s != null) {
2015 references.increment(s);
2016 return s;
2017 }
2018 } else {
2019 debugString("[ERROR]: getAccessibleTextSelectedTextFromContext; ac = null");
2020 }
2021 return null;
2022 }
2023
2024 /**
2025 * return the attribute string at a given index from an AccessibleContext
2026 */
2027 private String getAccessibleAttributesAtIndexFromContext(final AccessibleContext ac,
2028 final int index) {
2029 if (ac == null)
2030 return null;
2031 AttributeSet as = InvocationUtils.invokeAndWait(new Callable<AttributeSet>() {
2032 @Override
2033 public AttributeSet call() throws Exception {
2034 AccessibleText at = ac.getAccessibleText();
2035 if (at != null) {
2036 return at.getCharacterAttribute(index);
2037 }
2038 return null;
2039 }
2260 }
2261 }, ac);
2262 Rectangle acRect = getAccessibleBoundsOnScreenFromContext(ac);
2263 if (r != null && acRect != null) {
2264 r.translate(acRect.x, acRect.y);
2265 return r;
2266 }
2267 return null;
2268 }
2269
2270 /**
2271 * return the AccessibleText character x-coord at index from an AccessibleContext
2272 */
2273 private int getAccessibleXcoordTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2274 if (ac != null) {
2275 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2276 if (r != null) {
2277 return r.x;
2278 }
2279 } else {
2280 debugString("[ERROR]: getAccessibleXcoordTextRectAtIndexFromContext; ac = null");
2281 }
2282 return -1;
2283 }
2284
2285 /**
2286 * return the AccessibleText character y-coord at index from an AccessibleContext
2287 */
2288 private int getAccessibleYcoordTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2289 if (ac != null) {
2290 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2291 if (r != null) {
2292 return r.y;
2293 }
2294 } else {
2295 debugString("[ERROR]: getAccessibleYcoordTextRectAtIndexFromContext; ac = null");
2296 }
2297 return -1;
2298 }
2299
2300 /**
2301 * return the AccessibleText character height at index from an AccessibleContext
2302 */
2303 private int getAccessibleHeightTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2304 if (ac != null) {
2305 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2306 if (r != null) {
2307 return r.height;
2308 }
2309 } else {
2310 debugString("[ERROR]: getAccessibleHeightTextRectAtIndexFromContext; ac = null");
2311 }
2312 return -1;
2313 }
2314
2315 /**
2316 * return the AccessibleText character width at index from an AccessibleContext
2317 */
2318 private int getAccessibleWidthTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2319 if (ac != null) {
2320 Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2321 if (r != null) {
2322 return r.width;
2323 }
2324 } else {
2325 debugString("[ERROR]: getAccessibleWidthTextRectAtIndexFromContext; ac = null");
2326 }
2327 return -1;
2328 }
2329
2330 /* ===== AttributeSet methods for AccessibleText ===== */
2331
2332 /**
2333 * return the bold setting from an AttributeSet
2334 */
2335 private boolean getBoldFromAttributeSet(AttributeSet as) {
2336 if (as != null) {
2337 return StyleConstants.isBold(as);
2338 } else {
2339 debugString("[ERROR]: getBoldFromAttributeSet; as = null");
2340 }
2341 return false;
2342 }
2343
2344 /**
2345 * return the italic setting from an AttributeSet
2346 */
2347 private boolean getItalicFromAttributeSet(AttributeSet as) {
2348 if (as != null) {
2349 return StyleConstants.isItalic(as);
2350 } else {
2351 debugString("[ERROR]: getItalicFromAttributeSet; as = null");
2352 }
2353 return false;
2354 }
2355
2356 /**
2357 * return the underline setting from an AttributeSet
2358 */
2359 private boolean getUnderlineFromAttributeSet(AttributeSet as) {
2360 if (as != null) {
2361 return StyleConstants.isUnderline(as);
2362 } else {
2363 debugString("[ERROR]: getUnderlineFromAttributeSet; as = null");
2364 }
2365 return false;
2366 }
2367
2368 /**
2369 * return the strikethrough setting from an AttributeSet
2370 */
2371 private boolean getStrikethroughFromAttributeSet(AttributeSet as) {
2372 if (as != null) {
2373 return StyleConstants.isStrikeThrough(as);
2374 } else {
2375 debugString("[ERROR]: getStrikethroughFromAttributeSet; as = null");
2376 }
2377 return false;
2378 }
2379
2380 /**
2381 * return the superscript setting from an AttributeSet
2382 */
2383 private boolean getSuperscriptFromAttributeSet(AttributeSet as) {
2384 if (as != null) {
2385 return StyleConstants.isSuperscript(as);
2386 } else {
2387 debugString("[ERROR]: getSuperscriptFromAttributeSet; as = null");
2388 }
2389 return false;
2390 }
2391
2392 /**
2393 * return the subscript setting from an AttributeSet
2394 */
2395 private boolean getSubscriptFromAttributeSet(AttributeSet as) {
2396 if (as != null) {
2397 return StyleConstants.isSubscript(as);
2398 } else {
2399 debugString("[ERROR]: getSubscriptFromAttributeSet; as = null");
2400 }
2401 return false;
2402 }
2403
2404 /**
2405 * return the background color from an AttributeSet
2406 */
2407 private String getBackgroundColorFromAttributeSet(AttributeSet as) {
2408 if (as != null) {
2409 String s = StyleConstants.getBackground(as).toString();
2410 if (s != null) {
2411 references.increment(s);
2412 return s;
2413 }
2414 } else {
2415 debugString("[ERROR]: getBackgroundColorFromAttributeSet; as = null");
2416 }
2417 return null;
2418 }
2419
2420 /**
2421 * return the foreground color from an AttributeSet
2422 */
2423 private String getForegroundColorFromAttributeSet(AttributeSet as) {
2424 if (as != null) {
2425 String s = StyleConstants.getForeground(as).toString();
2426 if (s != null) {
2427 references.increment(s);
2428 return s;
2429 }
2430 } else {
2431 debugString("[ERROR]: getForegroundColorFromAttributeSet; as = null");
2432 }
2433 return null;
2434 }
2435
2436 /**
2437 * return the font family from an AttributeSet
2438 */
2439 private String getFontFamilyFromAttributeSet(AttributeSet as) {
2440 if (as != null) {
2441 String s = StyleConstants.getFontFamily(as).toString();
2442 if (s != null) {
2443 references.increment(s);
2444 return s;
2445 }
2446 } else {
2447 debugString("[ERROR]: getFontFamilyFromAttributeSet; as = null");
2448 }
2449 return null;
2450 }
2451
2452 /**
2453 * return the font size from an AttributeSet
2454 */
2455 private int getFontSizeFromAttributeSet(AttributeSet as) {
2456 if (as != null) {
2457 return StyleConstants.getFontSize(as);
2458 } else {
2459 debugString("[ERROR]: getFontSizeFromAttributeSet; as = null");
2460 }
2461 return -1;
2462 }
2463
2464 /**
2465 * return the alignment from an AttributeSet
2466 */
2467 private int getAlignmentFromAttributeSet(AttributeSet as) {
2468 if (as != null) {
2469 return StyleConstants.getAlignment(as);
2470 } else {
2471 debugString("[ERROR]: getAlignmentFromAttributeSet; as = null");
2472 }
2473 return -1;
2474 }
2475
2476 /**
2477 * return the BiDi level from an AttributeSet
2478 */
2479 private int getBidiLevelFromAttributeSet(AttributeSet as) {
2480 if (as != null) {
2481 return StyleConstants.getBidiLevel(as);
2482 } else {
2483 debugString("[ERROR]: getBidiLevelFromAttributeSet; as = null");
2484 }
2485 return -1;
2486 }
2487
2488
2489 /**
2490 * return the first line indent from an AttributeSet
2491 */
2492 private float getFirstLineIndentFromAttributeSet(AttributeSet as) {
2493 if (as != null) {
2494 return StyleConstants.getFirstLineIndent(as);
2495 } else {
2496 debugString("[ERROR]: getFirstLineIndentFromAttributeSet; as = null");
2497 }
2498 return -1;
2499 }
2500
2501 /**
2502 * return the left indent from an AttributeSet
2503 */
2504 private float getLeftIndentFromAttributeSet(AttributeSet as) {
2505 if (as != null) {
2506 return StyleConstants.getLeftIndent(as);
2507 } else {
2508 debugString("[ERROR]: getLeftIndentFromAttributeSet; as = null");
2509 }
2510 return -1;
2511 }
2512
2513 /**
2514 * return the right indent from an AttributeSet
2515 */
2516 private float getRightIndentFromAttributeSet(AttributeSet as) {
2517 if (as != null) {
2518 return StyleConstants.getRightIndent(as);
2519 } else {
2520 debugString("[ERROR]: getRightIndentFromAttributeSet; as = null");
2521 }
2522 return -1;
2523 }
2524
2525 /**
2526 * return the line spacing from an AttributeSet
2527 */
2528 private float getLineSpacingFromAttributeSet(AttributeSet as) {
2529 if (as != null) {
2530 return StyleConstants.getLineSpacing(as);
2531 } else {
2532 debugString("[ERROR]: getLineSpacingFromAttributeSet; as = null");
2533 }
2534 return -1;
2535 }
2536
2537 /**
2538 * return the space above from an AttributeSet
2539 */
2540 private float getSpaceAboveFromAttributeSet(AttributeSet as) {
2541 if (as != null) {
2542 return StyleConstants.getSpaceAbove(as);
2543 } else {
2544 debugString("[ERROR]: getSpaceAboveFromAttributeSet; as = null");
2545 }
2546 return -1;
2547 }
2548
2549 /**
2550 * return the space below from an AttributeSet
2551 */
2552 private float getSpaceBelowFromAttributeSet(AttributeSet as) {
2553 if (as != null) {
2554 return StyleConstants.getSpaceBelow(as);
2555 } else {
2556 debugString("[ERROR]: getSpaceBelowFromAttributeSet; as = null");
2557 }
2558 return -1;
2559 }
2560
2561 /**
2562 * Enumerate all StyleConstants in the AttributeSet
2563 *
2564 * We need to check explicitly, 'cause of the HTML package conversion
2565 * mechanism (they may not be stored as StyleConstants, just translated
2566 * to them when asked).
2567 *
2568 * (Use convenience methods where they are defined...)
2569 *
2570 * Not checking the following (which the IBM SNS guidelines says
2571 * should be defined):
2572 * - ComponentElementName
2573 * - IconElementName
2574 * - NameAttribute
2575 * - ResolveAttribute
2576 */
2685 *
2686 */
2687 private String getCurrentAccessibleValueFromContext(final AccessibleContext ac) {
2688 if (ac != null) {
2689 final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() {
2690 @Override
2691 public Number call() throws Exception {
2692 AccessibleValue av = ac.getAccessibleValue();
2693 if (av == null) return null;
2694 return av.getCurrentAccessibleValue();
2695 }
2696 }, ac);
2697 if (value != null) {
2698 String s = value.toString();
2699 if (s != null) {
2700 references.increment(s);
2701 return s;
2702 }
2703 }
2704 } else {
2705 debugString("[ERROR]: getCurrentAccessibleValueFromContext; ac = null");
2706 }
2707 return null;
2708 }
2709
2710 /**
2711 * return the AccessibleValue maximum value from an AccessibleContext
2712 * returned using a String 'cause the value is a java Number
2713 *
2714 */
2715 private String getMaximumAccessibleValueFromContext(final AccessibleContext ac) {
2716 if (ac != null) {
2717 final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() {
2718 @Override
2719 public Number call() throws Exception {
2720 AccessibleValue av = ac.getAccessibleValue();
2721 if (av == null) return null;
2722 return av.getMaximumAccessibleValue();
2723 }
2724 }, ac);
2725 if (value != null) {
2726 String s = value.toString();
2727 if (s != null) {
2728 references.increment(s);
2729 return s;
2730 }
2731 }
2732 } else {
2733 debugString("[ERROR]: getMaximumAccessibleValueFromContext; ac = null");
2734 }
2735 return null;
2736 }
2737
2738 /**
2739 * return the AccessibleValue minimum value from an AccessibleContext
2740 * returned using a String 'cause the value is a java Number
2741 *
2742 */
2743 private String getMinimumAccessibleValueFromContext(final AccessibleContext ac) {
2744 if (ac != null) {
2745 final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() {
2746 @Override
2747 public Number call() throws Exception {
2748 AccessibleValue av = ac.getAccessibleValue();
2749 if (av == null) return null;
2750 return av.getMinimumAccessibleValue();
2751 }
2752 }, ac);
2753 if (value != null) {
2754 String s = value.toString();
2755 if (s != null) {
2756 references.increment(s);
2757 return s;
2758 }
2759 }
2760 } else {
2761 debugString("[ERROR]: getMinimumAccessibleValueFromContext; ac = null");
2762 }
2763 return null;
2764 }
2765
2766
2767 /* ===== AccessibleSelection methods ===== */
2768
2769 /**
2770 * add to the AccessibleSelection of an AccessibleContext child i
2771 *
2772 */
2773 private void addAccessibleSelectionFromContext(final AccessibleContext ac, final int i) {
2774 try {
2775 InvocationUtils.invokeAndWait(new Callable<Object>() {
2776 @Override
2777 public Object call() throws Exception {
2778 if (ac != null) {
2779 AccessibleSelection as = ac.getAccessibleSelection();
2780 if (as != null) {
2781 as.addAccessibleSelection(i);
2924 return at;
2925 }
2926 }
2927 return null;
2928 }
2929 }, ac);
2930 }
2931
2932
2933 /*
2934 * returns the AccessibleContext that contains an AccessibleTable
2935 */
2936 private AccessibleContext getContextFromAccessibleTable(AccessibleTable at) {
2937 return hashtab.get(at);
2938 }
2939
2940 /*
2941 * returns the row count for an AccessibleTable
2942 */
2943 private int getAccessibleTableRowCount(final AccessibleContext ac) {
2944 debugString("[INFO]: ##### getAccessibleTableRowCount");
2945 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
2946 @Override
2947 public Integer call() throws Exception {
2948 if (ac != null) {
2949 AccessibleTable at = ac.getAccessibleTable();
2950 if (at != null) {
2951 return at.getAccessibleRowCount();
2952 }
2953 }
2954 return -1;
2955 }
2956 }, ac);
2957 }
2958
2959 /*
2960 * returns the column count for an AccessibleTable
2961 */
2962 private int getAccessibleTableColumnCount(final AccessibleContext ac) {
2963 debugString("[INFO]: ##### getAccessibleTableColumnCount");
2964 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
2965 @Override
2966 public Integer call() throws Exception {
2967 if (ac != null) {
2968 AccessibleTable at = ac.getAccessibleTable();
2969 if (at != null) {
2970 return at.getAccessibleColumnCount();
2971 }
2972 }
2973 return -1;
2974 }
2975 }, ac);
2976 }
2977
2978 /*
2979 * returns the AccessibleContext for an AccessibleTable cell
2980 */
2981 private AccessibleContext getAccessibleTableCellAccessibleContext(final AccessibleTable at,
2982 final int row, final int column) {
2983 debugString("[INFO]: getAccessibleTableCellAccessibleContext: at = "+at.getClass());
2984 if (at == null) return null;
2985 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
2986 @Override
2987 public AccessibleContext call() throws Exception {
2988 if (!(at instanceof AccessibleContext)) {
2989 Accessible a = at.getAccessibleAt(row, column);
2990 if (a != null) {
2991 return a.getAccessibleContext();
2992 }
2993 } else {
2994 // work-around for AccessibleJTable.getCurrentAccessibleContext returning
2995 // wrong renderer component when cell contains more than one component
2996 AccessibleContext ac = (AccessibleContext) at;
2997 Accessible parent = ac.getAccessibleParent();
2998 if (parent != null) {
2999 int indexInParent = ac.getAccessibleIndexInParent();
3000 Accessible child =
3001 parent.getAccessibleContext().getAccessibleChild(indexInParent);
3002 if (child instanceof JTable) {
3003 JTable table = (JTable) child;
3008 renderer = table.getDefaultRenderer(columnClass);
3009 }
3010 Component component =
3011 renderer.getTableCellRendererComponent(table, table.getValueAt(row, column),
3012 false, false, row, column);
3013 if (component instanceof Accessible) {
3014 return component.getAccessibleContext();
3015 }
3016 }
3017 }
3018 }
3019 return null;
3020 }
3021 }, getContextFromAccessibleTable(at));
3022 }
3023
3024 /*
3025 * returns the index of a cell at a given row and column in an AccessibleTable
3026 */
3027 private int getAccessibleTableCellIndex(final AccessibleTable at, int row, int column) {
3028 debugString("[INFO]: ##### getAccessibleTableCellIndex: at="+at);
3029 if (at != null) {
3030 int cellIndex = row *
3031 InvocationUtils.invokeAndWait(new Callable<Integer>() {
3032 @Override
3033 public Integer call() throws Exception {
3034 return at.getAccessibleColumnCount();
3035 }
3036 }, getContextFromAccessibleTable(at)) +
3037 column;
3038 debugString("[INFO]: ##### getAccessibleTableCellIndex="+cellIndex);
3039 return cellIndex;
3040 }
3041 debugString("[ERROR]: ##### getAccessibleTableCellIndex FAILED");
3042 return -1;
3043 }
3044
3045 /*
3046 * returns the row extent of a cell at a given row and column in an AccessibleTable
3047 */
3048 private int getAccessibleTableCellRowExtent(final AccessibleTable at, final int row, final int column) {
3049 debugString("[INFO]: ##### getAccessibleTableCellRowExtent");
3050 if (at != null) {
3051 int rowExtent = InvocationUtils.invokeAndWait(new Callable<Integer>() {
3052 @Override
3053 public Integer call() throws Exception {
3054 return at.getAccessibleRowExtentAt(row, column);
3055 }
3056 },
3057 getContextFromAccessibleTable(at));
3058 debugString("[INFO]: ##### getAccessibleTableCellRowExtent="+rowExtent);
3059 return rowExtent;
3060 }
3061 debugString("[ERROR]: ##### getAccessibleTableCellRowExtent FAILED");
3062 return -1;
3063 }
3064
3065 /*
3066 * returns the column extent of a cell at a given row and column in an AccessibleTable
3067 */
3068 private int getAccessibleTableCellColumnExtent(final AccessibleTable at, final int row, final int column) {
3069 debugString("[INFO]: ##### getAccessibleTableCellColumnExtent");
3070 if (at != null) {
3071 int columnExtent = InvocationUtils.invokeAndWait(new Callable<Integer>() {
3072 @Override
3073 public Integer call() throws Exception {
3074 return at.getAccessibleColumnExtentAt(row, column);
3075 }
3076 },
3077 getContextFromAccessibleTable(at));
3078 debugString("[INFO]: ##### getAccessibleTableCellColumnExtent="+columnExtent);
3079 return columnExtent;
3080 }
3081 debugString("[ERROR]: ##### getAccessibleTableCellColumnExtent FAILED");
3082 return -1;
3083 }
3084
3085 /*
3086 * returns whether a cell is selected at a given row and column in an AccessibleTable
3087 */
3088 private boolean isAccessibleTableCellSelected(final AccessibleTable at, final int row,
3089 final int column) {
3090 debugString("[INFO]: ##### isAccessibleTableCellSelected: ["+row+"]["+column+"]");
3091 if (at == null)
3092 return false;
3093 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
3094 @Override
3095 public Boolean call() throws Exception {
3096 boolean isSelected = false;
3097 Accessible a = at.getAccessibleAt(row, column);
3098 if (a != null) {
3099 AccessibleContext ac = a.getAccessibleContext();
3100 if (ac == null)
3101 return false;
3102 AccessibleStateSet as = ac.getAccessibleStateSet();
3103 if (as != null) {
3104 isSelected = as.contains(AccessibleState.SELECTED);
3105 }
3106 }
3107 return isSelected;
3108 }
3109 }, getContextFromAccessibleTable(at));
3110 }
3111
3112 /*
3113 * returns an AccessibleTable that represents the row header in an
3114 * AccessibleTable
3115 */
3116 private AccessibleTable getAccessibleTableRowHeader(final AccessibleContext ac) {
3117 debugString("[INFO]: ##### getAccessibleTableRowHeader called");
3118 AccessibleTable at = InvocationUtils.invokeAndWait(new Callable<AccessibleTable>() {
3119 @Override
3120 public AccessibleTable call() throws Exception {
3121 if (ac != null) {
3122 AccessibleTable at = ac.getAccessibleTable();
3123 if (at != null) {
3124 return at.getAccessibleRowHeader();
3125 }
3126 }
3127 return null;
3128 }
3129 }, ac);
3130 if (at != null) {
3131 hashtab.put(at, ac);
3132 }
3133 return at;
3134 }
3135
3136 /*
3137 * returns an AccessibleTable that represents the column header in an
3138 * AccessibleTable
3139 */
3140 private AccessibleTable getAccessibleTableColumnHeader(final AccessibleContext ac) {
3141 debugString("[INFO]: ##### getAccessibleTableColumnHeader");
3142 if (ac == null)
3143 return null;
3144 AccessibleTable at = InvocationUtils.invokeAndWait(new Callable<AccessibleTable>() {
3145 @Override
3146 public AccessibleTable call() throws Exception {
3147 // workaround for getAccessibleColumnHeader NPE
3148 // when the table header is null
3149 Accessible parent = ac.getAccessibleParent();
3150 if (parent != null) {
3151 int indexInParent = ac.getAccessibleIndexInParent();
3152 Accessible child =
3153 parent.getAccessibleContext().getAccessibleChild(indexInParent);
3154 if (child instanceof JTable) {
3155 JTable table = (JTable) child;
3156 if (table.getTableHeader() == null) {
3157 return null;
3158 }
3159 }
3160 }
3161 AccessibleTable at = ac.getAccessibleTable();
3162 if (at != null) {
3163 return at.getAccessibleColumnHeader();
3164 }
3165 return null;
3166 }
3167 }, ac);
3168 if (at != null) {
3169 hashtab.put(at, ac);
3170 }
3171 return at;
3172 }
3173
3174 /*
3175 * returns the number of row headers in an AccessibleTable that represents
3176 * the row header in an AccessibleTable
3177 */
3178 private int getAccessibleTableRowHeaderRowCount(AccessibleContext ac) {
3179
3180 debugString("[INFO]: ##### getAccessibleTableRowHeaderRowCount called");
3181 if (ac != null) {
3182 final AccessibleTable atRowHeader = getAccessibleTableRowHeader(ac);
3183 if (atRowHeader != null) {
3184 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3185 @Override
3186 public Integer call() throws Exception {
3187 if (atRowHeader != null) {
3188 return atRowHeader.getAccessibleRowCount();
3189 }
3190 return -1;
3191 }
3192 }, ac);
3193 }
3194 }
3195 return -1;
3196 }
3197
3198 /*
3199 * returns the number of column headers in an AccessibleTable that represents
3200 * the row header in an AccessibleTable
3201 */
3202 private int getAccessibleTableRowHeaderColumnCount(AccessibleContext ac) {
3203 debugString("[INFO]: ##### getAccessibleTableRowHeaderColumnCount called");
3204 if (ac != null) {
3205 final AccessibleTable atRowHeader = getAccessibleTableRowHeader(ac);
3206 if (atRowHeader != null) {
3207 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3208 @Override
3209 public Integer call() throws Exception {
3210 if (atRowHeader != null) {
3211 return atRowHeader.getAccessibleColumnCount();
3212 }
3213 return -1;
3214 }
3215 }, ac);
3216 }
3217 }
3218 debugString("[ERROR]: ##### getAccessibleTableRowHeaderColumnCount FAILED");
3219 return -1;
3220 }
3221
3222 /*
3223 * returns the number of row headers in an AccessibleTable that represents
3224 * the column header in an AccessibleTable
3225 */
3226 private int getAccessibleTableColumnHeaderRowCount(AccessibleContext ac) {
3227
3228 debugString("[INFO]: ##### getAccessibleTableColumnHeaderRowCount");
3229 if (ac != null) {
3230 final AccessibleTable atColumnHeader = getAccessibleTableColumnHeader(ac);
3231 if (atColumnHeader != null) {
3232 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3233 @Override
3234 public Integer call() throws Exception {
3235 if (atColumnHeader != null) {
3236 return atColumnHeader.getAccessibleRowCount();
3237 }
3238 return -1;
3239 }
3240 }, ac);
3241 }
3242 }
3243 debugString("[ERROR]: ##### getAccessibleTableColumnHeaderRowCount FAILED");
3244 return -1;
3245 }
3246
3247 /*
3248 * returns the number of column headers in an AccessibleTable that represents
3249 * the column header in an AccessibleTable
3250 */
3251 private int getAccessibleTableColumnHeaderColumnCount(AccessibleContext ac) {
3252
3253 debugString("[ERROR]: ##### getAccessibleTableColumnHeaderColumnCount");
3254 if (ac != null) {
3255 final AccessibleTable atColumnHeader = getAccessibleTableColumnHeader(ac);
3256 if (atColumnHeader != null) {
3257 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3258 @Override
3259 public Integer call() throws Exception {
3260 if (atColumnHeader != null) {
3261 return atColumnHeader.getAccessibleColumnCount();
3262 }
3263 return -1;
3264 }
3265 }, ac);
3266 }
3267 }
3268 debugString("[ERROR]: ##### getAccessibleTableColumnHeaderColumnCount FAILED");
3269 return -1;
3270 }
3271
3272 /*
3273 * returns the description of a row header in an AccessibleTable
3274 */
3275 private AccessibleContext getAccessibleTableRowDescription(final AccessibleTable table,
3276 final int row) {
3277 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
3278 @Override
3279 public AccessibleContext call() throws Exception {
3280 if (table != null) {
3281 Accessible a = table.getAccessibleRowDescription(row);
3282 if (a != null) {
3283 return a.getAccessibleContext();
3284 }
3285 }
3286 return null;
3287 }
3288 }, getContextFromAccessibleTable(table));
3515 AccessibleRelationSet ars = ac.getAccessibleRelationSet();
3516 if (ars != null) {
3517 AccessibleRelation[] relations = ars.toArray();
3518 if (relations != null && i >= 0 && i < relations.length) {
3519 Object[] targets = relations[i].getTarget();
3520 return targets.length;
3521 }
3522 }
3523 }
3524 return -1;
3525 }
3526 }, ac);
3527 }
3528
3529 /*
3530 * returns the jth target in the ith relation in the AccessibleContext's
3531 * AccessibleRelationSet
3532 */
3533 private AccessibleContext getAccessibleRelationTarget(final AccessibleContext ac,
3534 final int i, final int j) {
3535 debugString("[INFO]: ***** getAccessibleRelationTarget");
3536 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
3537 @Override
3538 public AccessibleContext call() throws Exception {
3539 if (ac != null) {
3540 AccessibleRelationSet ars = ac.getAccessibleRelationSet();
3541 if (ars != null) {
3542 AccessibleRelation[] relations = ars.toArray();
3543 if (relations != null && i >= 0 && i < relations.length) {
3544 Object[] targets = relations[i].getTarget();
3545 if (targets != null && j >= 0 & j < targets.length) {
3546 Object o = targets[j];
3547 if (o instanceof Accessible) {
3548 return ((Accessible) o).getAccessibleContext();
3549 }
3550 }
3551 }
3552 }
3553 }
3554 return null;
3555 }
3556 }, ac);
3557 }
3558
3559 // ========= AccessibleHypertext =========
3560
3561 private Map<AccessibleHypertext, AccessibleContext> hyperTextContextMap = new WeakHashMap<>();
3562 private Map<AccessibleHyperlink, AccessibleContext> hyperLinkContextMap = new WeakHashMap<>();
3563
3564 /*
3565 * Returns the AccessibleHypertext
3566 */
3567 private AccessibleHypertext getAccessibleHypertext(final AccessibleContext ac) {
3568 debugString("[INFO]: getAccessibleHyperlink");
3569 if (ac==null)
3570 return null;
3571 AccessibleHypertext hypertext = InvocationUtils.invokeAndWait(new Callable<AccessibleHypertext>() {
3572 @Override
3573 public AccessibleHypertext call() throws Exception {
3574 AccessibleText at = ac.getAccessibleText();
3575 if (!(at instanceof AccessibleHypertext)) {
3576 return null;
3577 }
3578 return ((AccessibleHypertext) at);
3579 }
3580 }, ac);
3581 hyperTextContextMap.put(hypertext, ac);
3582 return hypertext;
3583 }
3584
3585 /*
3586 * Returns the number of AccessibleHyperlinks
3587 */
3588 private int getAccessibleHyperlinkCount(AccessibleContext ac) {
3589 debugString("[INFO]: getAccessibleHyperlinkCount");
3590 if (ac == null) {
3591 return 0;
3592 }
3593 final AccessibleHypertext hypertext = getAccessibleHypertext(ac);
3594 if (hypertext == null) {
3595 return 0;
3596 }
3597 //return hypertext.getLinkCount();
3598 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3599 @Override
3600 public Integer call() throws Exception {
3601 return hypertext.getLinkCount();
3602 }
3603 }, ac);
3604 }
3605
3606 /*
3607 * Returns the hyperlink at the specified index
3608 */
3609 private AccessibleHyperlink getAccessibleHyperlink(final AccessibleHypertext hypertext, final int i) {
3610 debugString("[INFO]: getAccessibleHyperlink");
3611 if (hypertext == null) {
3612 return null;
3613 }
3614 AccessibleContext ac = hyperTextContextMap.get(hypertext);
3615 if ( i < 0 || i >=
3616 InvocationUtils.invokeAndWait(new Callable<Integer>() {
3617 @Override
3618 public Integer call() throws Exception {
3619 return hypertext.getLinkCount();
3620 }
3621 }, ac) ) {
3622 return null;
3623 }
3624 AccessibleHyperlink acLink = InvocationUtils.invokeAndWait(new Callable<AccessibleHyperlink>() {
3625 @Override
3626 public AccessibleHyperlink call() throws Exception {
3627 AccessibleHyperlink link = hypertext.getLink(i);
3628 if (link == null || (!link.isValid())) {
3629 return null;
3630 }
3631 return link;
3632 }
3633 }, ac);
3634 hyperLinkContextMap.put(acLink, ac);
3635 return acLink;
3636 }
3637
3638 /*
3639 * Returns the hyperlink object description
3640 */
3641 private String getAccessibleHyperlinkText(final AccessibleHyperlink link) {
3642 debugString("[INFO]: getAccessibleHyperlinkText");
3643 if (link == null) {
3644 return null;
3645 }
3646 return InvocationUtils.invokeAndWait(new Callable<String>() {
3647 @Override
3648 public String call() throws Exception {
3649 Object o = link.getAccessibleActionDescription(0);
3650 if (o != null) {
3651 return o.toString();
3652 }
3653 return null;
3654 }
3655 }, hyperLinkContextMap.get(link));
3656 }
3657
3658 /*
3659 * Returns the hyperlink URL
3660 */
3661 private String getAccessibleHyperlinkURL(final AccessibleHyperlink link) {
3662 debugString("[INFO]: getAccessibleHyperlinkURL");
3663 if (link == null) {
3664 return null;
3665 }
3666 return InvocationUtils.invokeAndWait(new Callable<String>() {
3667 @Override
3668 public String call() throws Exception {
3669 Object o = link.getAccessibleActionObject(0);
3670 if (o != null) {
3671 return o.toString();
3672 } else {
3673 return null;
3674 }
3675 }
3676 }, hyperLinkContextMap.get(link));
3677 }
3678
3679 /*
3680 * Returns the start index of the hyperlink text
3681 */
3682 private int getAccessibleHyperlinkStartIndex(final AccessibleHyperlink link) {
3683 debugString("[INFO]: getAccessibleHyperlinkStartIndex");
3684 if (link == null) {
3685 return -1;
3686 }
3687 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3688 @Override
3689 public Integer call() throws Exception {
3690 return link.getStartIndex();
3691 }
3692 }, hyperLinkContextMap.get(link));
3693 }
3694
3695 /*
3696 * Returns the end index of the hyperlink text
3697 */
3698 private int getAccessibleHyperlinkEndIndex(final AccessibleHyperlink link) {
3699 debugString("[INFO]: getAccessibleHyperlinkEndIndex");
3700 if (link == null) {
3701 return -1;
3702 }
3703 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3704 @Override
3705 public Integer call() throws Exception {
3706 return link.getEndIndex();
3707 }
3708 }, hyperLinkContextMap.get(link));
3709 }
3710
3711 /*
3712 * Returns the index into an array of hyperlinks that
3713 * is associated with this character index, or -1 if there
3714 * is no hyperlink associated with this index.
3715 */
3716 private int getAccessibleHypertextLinkIndex(final AccessibleHypertext hypertext, final int charIndex) {
3717 debugString("[INFO]: getAccessibleHypertextLinkIndex: charIndex = "+charIndex);
3718 if (hypertext == null) {
3719 return -1;
3720 }
3721 int linkIndex = InvocationUtils.invokeAndWait(new Callable<Integer>() {
3722 @Override
3723 public Integer call() throws Exception {
3724 return hypertext.getLinkIndex(charIndex);
3725 }
3726 }, hyperTextContextMap.get(hypertext));
3727 debugString("[INFO]: getAccessibleHypertextLinkIndex returning "+linkIndex);
3728 return linkIndex;
3729 }
3730
3731 /*
3732 * Actives the hyperlink
3733 */
3734 private boolean activateAccessibleHyperlink(final AccessibleContext ac,
3735 final AccessibleHyperlink link) {
3736 //debugString("activateAccessibleHyperlink: link = "+link.getClass());
3737 if (link == null) {
3738 return false;
3739 }
3740 boolean retval = InvocationUtils.invokeAndWait(new Callable<Boolean>() {
3741 @Override
3742 public Boolean call() throws Exception {
3743 return link.doAccessibleAction(0);
3744 }
3745 }, ac);
3746 debugString("[INFO]: activateAccessibleHyperlink: returning = "+retval);
3747 return retval;
3748 }
3749
3750
3751 // ============ AccessibleKeyBinding =============
3752
3753 /*
3754 * returns the component mnemonic
3755 */
3756 private KeyStroke getMnemonic(final AccessibleContext ac) {
3757 if (ac == null)
3758 return null;
3759 return InvocationUtils.invokeAndWait(new Callable<KeyStroke>() {
3760 @Override
3761 public KeyStroke call() throws Exception {
3762 AccessibleComponent comp = ac.getAccessibleComponent();
3763 if (!(comp instanceof AccessibleExtendedComponent)) {
3764 return null;
3765 }
3766 AccessibleExtendedComponent aec = (AccessibleExtendedComponent) comp;
3854 case KeyEvent.VK_RIGHT:
3855 case KeyEvent.VK_UP:
3856 break;
3857 default:
3858 code = 0;
3859 break;
3860 }
3861 return code;
3862 }
3863
3864 /*
3865 * returns the KeyStoke character
3866 */
3867 private char getKeyChar(KeyStroke keyStroke) {
3868 // If the shortcut is an FKey return 1-24
3869 if (keyStroke == null)
3870 return 0;
3871 int fKey = fKeyNumber(keyStroke);
3872 if (fKey != 0) {
3873 // return 0x00000001 through 0x00000018
3874 debugString("[INFO]: Shortcut is: F" + fKey);
3875 return (char)fKey;
3876 }
3877 // If the accelerator is a control character, return it
3878 int keyCode = controlCode(keyStroke);
3879 if (keyCode != 0) {
3880 debugString("[INFO]: Shortcut is control character: " + Integer.toHexString(keyCode));
3881 return (char)keyCode;
3882 }
3883 String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode());
3884 debugString("[INFO]: Shortcut is: " + keyText);
3885 if (keyText != null || keyText.length() > 0) {
3886 CharSequence seq = keyText.subSequence(0, 1);
3887 if (seq != null || seq.length() > 0) {
3888 return seq.charAt(0);
3889 }
3890 }
3891 return 0;
3892 }
3893
3894 /*
3895 * returns the KeyStroke modifiers as an int
3896 */
3897 private int getModifiers(KeyStroke keyStroke) {
3898 if (keyStroke == null)
3899 return 0;
3900 debugString("[INFO]: In AccessBridge.getModifiers");
3901 // modifiers is a bit strip where bits 0-7 indicate a traditional modifier
3902 // such as Ctrl/Alt/Shift, bit 8 indicates an F key shortcut, and bit 9 indicates
3903 // a control code shortcut such as the delete key.
3904
3905 int modifiers = 0;
3906 // Is the shortcut an FKey?
3907 if (fKeyNumber(keyStroke) != 0) {
3908 modifiers |= 1 << 8;
3909 }
3910 // Is the shortcut a control code?
3911 if (controlCode(keyStroke) != 0) {
3912 modifiers |= 1 << 9;
3913 }
3914 // The following is needed in order to handle translated modifiers.
3915 // getKeyModifiersText doesn't work because for example in German Strg is
3916 // returned for Ctrl.
3917
3918 // There can be more than one modifier, e.g. if the modifier is ctrl + shift + B
3919 // the toString text is "shift ctrl pressed B". Need to parse through that.
3920 StringTokenizer st = new StringTokenizer(keyStroke.toString());
3921 while (st.hasMoreTokens()) {
3922 String text = st.nextToken();
3923 // Meta+Ctrl+Alt+Shift
3924 // 0-3 are shift, ctrl, meta, alt
3925 // 4-7 are for Solaris workstations (though not being used)
3926 if (text.startsWith("met")) {
3927 debugString("[INFO]: found meta");
3928 modifiers |= ActionEvent.META_MASK;
3929 }
3930 if (text.startsWith("ctr")) {
3931 debugString("[INFO]: found ctrl");
3932 modifiers |= ActionEvent.CTRL_MASK;
3933 }
3934 if (text.startsWith("alt")) {
3935 debugString("[INFO]: found alt");
3936 modifiers |= ActionEvent.ALT_MASK;
3937 }
3938 if (text.startsWith("shi")) {
3939 debugString(" found shift");
3940 modifiers |= ActionEvent.SHIFT_MASK;
3941 }
3942 }
3943 debugString("[INFO]: returning modifiers: 0x" + Integer.toHexString(modifiers));
3944 return modifiers;
3945 }
3946
3947 /*
3948 * returns the number of key bindings associated with this context
3949 */
3950 private int getAccessibleKeyBindingsCount(AccessibleContext ac) {
3951 if (ac == null)
3952 return 0;
3953 int count = 0;
3954
3955 if (getMnemonic(ac) != null) {
3956 count++;
3957 }
3958 if (getAccelerator(ac) != null) {
3959 count++;
3960 }
3961 return count;
3962 }
3963
4002 if (index == 0) { // mnemonic
4003 KeyStroke keyStroke = getMnemonic(ac);
4004 if (keyStroke != null) {
4005 return getModifiers(keyStroke);
4006 }
4007 } else if (index == 1) { // accelerator
4008 KeyStroke keyStroke = getAccelerator(ac);
4009 if (keyStroke != null) {
4010 return getModifiers(keyStroke);
4011 }
4012 }
4013 return 0;
4014 }
4015
4016 // ========== AccessibleIcon ============
4017
4018 /*
4019 * return the number of icons associated with this context
4020 */
4021 private int getAccessibleIconsCount(final AccessibleContext ac) {
4022 debugString("[INFO]: getAccessibleIconsCount");
4023 if (ac == null) {
4024 return 0;
4025 }
4026 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4027 @Override
4028 public Integer call() throws Exception {
4029 AccessibleIcon[] ai = ac.getAccessibleIcon();
4030 if (ai == null) {
4031 return 0;
4032 }
4033 return ai.length;
4034 }
4035 }, ac);
4036 }
4037
4038 /*
4039 * return icon description at the specified index
4040 */
4041 private String getAccessibleIconDescription(final AccessibleContext ac, final int index) {
4042 debugString("[INFO]: getAccessibleIconDescription: index = "+index);
4043 if (ac == null) {
4044 return null;
4045 }
4046 return InvocationUtils.invokeAndWait(new Callable<String>() {
4047 @Override
4048 public String call() throws Exception {
4049 AccessibleIcon[] ai = ac.getAccessibleIcon();
4050 if (ai == null || index < 0 || index >= ai.length) {
4051 return null;
4052 }
4053 return ai[index].getAccessibleIconDescription();
4054 }
4055 }, ac);
4056 }
4057
4058 /*
4059 * return icon height at the specified index
4060 */
4061 private int getAccessibleIconHeight(final AccessibleContext ac, final int index) {
4062 debugString("[INFO]: getAccessibleIconHeight: index = "+index);
4063 if (ac == null) {
4064 return 0;
4065 }
4066 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4067 @Override
4068 public Integer call() throws Exception {
4069 AccessibleIcon[] ai = ac.getAccessibleIcon();
4070 if (ai == null || index < 0 || index >= ai.length) {
4071 return 0;
4072 }
4073 return ai[index].getAccessibleIconHeight();
4074 }
4075 }, ac);
4076 }
4077
4078 /*
4079 * return icon width at the specified index
4080 */
4081 private int getAccessibleIconWidth(final AccessibleContext ac, final int index) {
4082 debugString("[INFO]: getAccessibleIconWidth: index = "+index);
4083 if (ac == null) {
4084 return 0;
4085 }
4086 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4087 @Override
4088 public Integer call() throws Exception {
4089 AccessibleIcon[] ai = ac.getAccessibleIcon();
4090 if (ai == null || index < 0 || index >= ai.length) {
4091 return 0;
4092 }
4093 return ai[index].getAccessibleIconWidth();
4094 }
4095 }, ac);
4096 }
4097
4098 // ========= AccessibleAction ===========
4099
4100 /*
4101 * return the number of icons associated with this context
4102 */
4103 private int getAccessibleActionsCount(final AccessibleContext ac) {
4104 debugString("[INFO]: getAccessibleActionsCount");
4105 if (ac == null) {
4106 return 0;
4107 }
4108 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4109 @Override
4110 public Integer call() throws Exception {
4111 AccessibleAction aa = ac.getAccessibleAction();
4112 if (aa == null)
4113 return 0;
4114 return aa.getAccessibleActionCount();
4115 }
4116 }, ac);
4117 }
4118
4119 /*
4120 * return icon description at the specified index
4121 */
4122 private String getAccessibleActionName(final AccessibleContext ac, final int index) {
4123 debugString("[INFO]: getAccessibleActionName: index = "+index);
4124 if (ac == null) {
4125 return null;
4126 }
4127 return InvocationUtils.invokeAndWait(new Callable<String>() {
4128 @Override
4129 public String call() throws Exception {
4130 AccessibleAction aa = ac.getAccessibleAction();
4131 if (aa == null) {
4132 return null;
4133 }
4134 return aa.getAccessibleActionDescription(index);
4135 }
4136 }, ac);
4137 }
4138 /*
4139 * return icon description at the specified index
4140 */
4141 private boolean doAccessibleActions(final AccessibleContext ac, final String name) {
4142 debugString("[INFO]: doAccessibleActions: action name = "+name);
4143 if (ac == null || name == null) {
4144 return false;
4145 }
4146 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4147 @Override
4148 public Boolean call() throws Exception {
4149 AccessibleAction aa = ac.getAccessibleAction();
4150 if (aa == null) {
4151 return false;
4152 }
4153 int index = -1;
4154 int numActions = aa.getAccessibleActionCount();
4155 for (int i = 0; i < numActions; i++) {
4156 String actionName = aa.getAccessibleActionDescription(i);
4157 if (name.equals(actionName)) {
4158 index = i;
4159 break;
4160 }
4161 }
4162 if (index == -1) {
4163 return false;
4164 }
4165 boolean retval = aa.doAccessibleAction(index);
4166 return retval;
4167 }
4168 }, ac);
4169 }
4170
4171 /* ===== AT utility methods ===== */
4172
4173 /**
4174 * Sets the contents of an AccessibleContext that
4175 * implements AccessibleEditableText with the
4176 * specified text string.
4177 * Returns whether successful.
4178 */
4179 private boolean setTextContents(final AccessibleContext ac, final String text) {
4180 debugString("[INFO]: setTextContents: ac = "+ac+"; text = "+text);
4181
4182 if (! (ac instanceof AccessibleEditableText)) {
4183 debugString("[WARN]: ac not instanceof AccessibleEditableText: "+ac);
4184 return false;
4185 }
4186 if (text == null) {
4187 debugString("[WARN]: text is null");
4188 return false;
4189 }
4190
4191 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4192 @Override
4193 public Boolean call() throws Exception {
4194 // check whether the text field is editable
4195 AccessibleStateSet ass = ac.getAccessibleStateSet();
4196 if (!ass.contains(AccessibleState.ENABLED)) {
4197 return false;
4198 }
4199 ((AccessibleEditableText) ac).setTextContents(text);
4200 return true;
4201 }
4202 }, ac);
4203 }
4204
4205 /**
4206 * Returns the Accessible Context of an Internal Frame object that is
4207 * the ancestor of a given object. If the object is an Internal Frame
4208 * object or an Internal Frame ancestor object was found, returns the
4209 * object's AccessibleContext.
4210 * If there is no ancestor object that has an Accessible Role of
4211 * Internal Frame, returns (AccessibleContext)0.
4212 */
4213 private AccessibleContext getInternalFrame (AccessibleContext ac) {
4214 return getParentWithRole(ac, AccessibleRole.INTERNAL_FRAME.toString());
4215 }
4216
4217 /**
4218 * Returns the Accessible Context for the top level object in
4219 * a Java Window. This is same Accessible Context that is obtained
4220 * from GetAccessibleContextFromHWND for that window. Returns
4221 * (AccessibleContext)0 on error.
4222 */
4223 private AccessibleContext getTopLevelObject (final AccessibleContext ac) {
4224 debugString("[INFO]: getTopLevelObject; ac = "+ac);
4225 if (ac == null) {
4226 return null;
4227 }
4228 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4229 @Override
4230 public AccessibleContext call() throws Exception {
4231 if (ac.getAccessibleRole() == AccessibleRole.DIALOG) {
4232 // return the dialog, not the parent window
4233 return ac;
4234 }
4235
4236 Accessible parent = ac.getAccessibleParent();
4237 if (parent == null) {
4238 return ac;
4239 }
4240 Accessible tmp = parent;
4241 while (tmp != null && tmp.getAccessibleContext() != null) {
4242 AccessibleContext ac2 = tmp.getAccessibleContext();
4243 if (ac2 != null && ac2.getAccessibleRole() == AccessibleRole.DIALOG) {
4244 // return the dialog, not the parent window
4245 return ac2;
4246 }
4247 parent = tmp;
4248 tmp = parent.getAccessibleContext().getAccessibleParent();
4249 }
4250 return parent.getAccessibleContext();
4251 }
4252 }, ac);
4253 }
4254
4255 /**
4256 * Returns the parent AccessibleContext that has the specified AccessibleRole.
4257 * Returns null on error or if the AccessibleContext does not exist.
4258 */
4259 private AccessibleContext getParentWithRole (final AccessibleContext ac,
4260 final String roleName) {
4261 debugString("[INFO]: getParentWithRole; ac = "+ac + "\n role = "+roleName);
4262 if (ac == null || roleName == null) {
4263 return null;
4264 }
4265
4266 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4267 @Override
4268 public AccessibleContext call() throws Exception {
4269 AccessibleRole role = AccessBridge.this.accessibleRoleMap.get(roleName);
4270 if (role == null) {
4271 return ac;
4272 }
4273
4274 Accessible parent = ac.getAccessibleParent();
4275 if (parent == null && ac.getAccessibleRole() == role) {
4276 return ac;
4277 }
4278
4279 Accessible tmp = parent;
4280 AccessibleContext tmp_ac = null;
4281
4297 /**
4298 * Returns the parent AccessibleContext that has the specified AccessibleRole.
4299 * Otherwise, returns the top level object for the Java Window.
4300 * Returns (AccessibleContext)0 on error.
4301 */
4302 private AccessibleContext getParentWithRoleElseRoot (AccessibleContext ac,
4303 String roleName) {
4304 AccessibleContext retval = getParentWithRole(ac, roleName);
4305 if (retval == null) {
4306 retval = getTopLevelObject(ac);
4307 }
4308 return retval;
4309 }
4310
4311 /**
4312 * Returns how deep in the object hierarchy a given object is.
4313 * The top most object in the object hierarchy has an object depth of 0.
4314 * Returns -1 on error.
4315 */
4316 private int getObjectDepth(final AccessibleContext ac) {
4317 debugString("[INFO]: getObjectDepth: ac = "+ac);
4318
4319 if (ac == null) {
4320 return -1;
4321 }
4322 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4323 @Override
4324 public Integer call() throws Exception {
4325 int count = 0;
4326 Accessible parent = ac.getAccessibleParent();
4327 if (parent == null) {
4328 return count;
4329 }
4330 Accessible tmp = parent;
4331 while (tmp != null && tmp.getAccessibleContext() != null) {
4332 parent = tmp;
4333 tmp = parent.getAccessibleContext().getAccessibleParent();
4334 count++;
4335 }
4336 return count;
4337 }
4338 }, ac);
4339 }
4340
4341 /**
4342 * Returns the Accessible Context of the current ActiveDescendent of an object.
4343 * Returns (AccessibleContext)0 on error.
4344 */
4345 private AccessibleContext getActiveDescendent (final AccessibleContext ac) {
4346 debugString("[INFO]: getActiveDescendent: ac = "+ac);
4347 if (ac == null) {
4348 return null;
4349 }
4350 // workaround for JTree bug where the only possible active
4351 // descendent is the JTree root
4352 final Accessible parent = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
4353 @Override
4354 public Accessible call() throws Exception {
4355 return ac.getAccessibleParent();
4356 }
4357 }, ac);
4358
4359 if (parent != null) {
4360 Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
4361 @Override
4362 public Accessible call() throws Exception {
4363 int indexInParent = ac.getAccessibleIndexInParent();
4364 return parent.getAccessibleContext().getAccessibleChild(indexInParent);
4365 }
4366 }, ac);
4394 if (a == null) {
4395 return null;
4396 }
4397 return a.getAccessibleContext();
4398 }
4399 }, ac);
4400 }
4401
4402
4403 /**
4404 * Additional methods for Teton
4405 */
4406
4407 /**
4408 * Gets the AccessibleName for a component based upon the JAWS algorithm.
4409 * Returns whether successful.
4410 *
4411 * Bug ID 4916682 - Implement JAWS AccessibleName policy
4412 */
4413 private String getJAWSAccessibleName(final AccessibleContext ac) {
4414 debugString("[INFO]: getJAWSAccessibleName");
4415 if (ac == null) {
4416 return null;
4417 }
4418 // placeholder
4419 return InvocationUtils.invokeAndWait(new Callable<String>() {
4420 @Override
4421 public String call() throws Exception {
4422 return ac.getAccessibleName();
4423 }
4424 }, ac);
4425 }
4426
4427 /**
4428 * Request focus for a component. Returns whether successful;
4429 *
4430 * Bug ID 4944757 - requestFocus method needed
4431 */
4432 private boolean requestFocus(final AccessibleContext ac) {
4433 debugString("[INFO]: requestFocus");
4434 if (ac == null) {
4435 return false;
4436 }
4437 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4438 @Override
4439 public Boolean call() throws Exception {
4440 AccessibleComponent acomp = ac.getAccessibleComponent();
4441 if (acomp == null) {
4442 return false;
4443 }
4444 acomp.requestFocus();
4445 return ac.getAccessibleStateSet().contains(AccessibleState.FOCUSED);
4446 }
4447 }, ac);
4448 }
4449
4450 /**
4451 * Selects text between two indices. Selection includes the
4452 * text at the start index and the text at the end index. Returns
4453 * whether successful;
4454 *
4455 * Bug ID 4944758 - selectTextRange method needed
4456 */
4457 private boolean selectTextRange(final AccessibleContext ac, final int startIndex, final int endIndex) {
4458 debugString("[INFO]: selectTextRange: start = "+startIndex+"; end = "+endIndex);
4459 if (ac == null) {
4460 return false;
4461 }
4462 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4463 @Override
4464 public Boolean call() throws Exception {
4465 AccessibleText at = ac.getAccessibleText();
4466 if (!(at instanceof AccessibleEditableText)) {
4467 return false;
4468 }
4469 ((AccessibleEditableText) at).selectText(startIndex, endIndex);
4470
4471 boolean result = at.getSelectionStart() == startIndex &&
4472 at.getSelectionEnd() == endIndex;
4473 return result;
4474 }
4475 }, ac);
4476 }
4477
4478 /**
4479 * Set the caret to a text position. Returns whether successful;
4480 *
4481 * Bug ID 4944770 - setCaretPosition method needed
4482 */
4483 private boolean setCaretPosition(final AccessibleContext ac, final int position) {
4484 debugString("[INFO]: setCaretPosition: position = "+position);
4485 if (ac == null) {
4486 return false;
4487 }
4488 return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4489 @Override
4490 public Boolean call() throws Exception {
4491 AccessibleText at = ac.getAccessibleText();
4492 if (!(at instanceof AccessibleEditableText)) {
4493 return false;
4494 }
4495 ((AccessibleEditableText) at).selectText(position, position);
4496 return at.getCaretPosition() == position;
4497 }
4498 }, ac);
4499 }
4500
4501 /**
4502 * Gets the number of visible children of an AccessibleContext.
4503 *
4504 * Bug ID 4944762- getVisibleChildren for list-like components needed
4505 */
4506 private int _visibleChildrenCount;
4507 private AccessibleContext _visibleChild;
4508 private int _currentVisibleIndex;
4509 private boolean _foundVisibleChild;
4510
4511 private int getVisibleChildrenCount(AccessibleContext ac) {
4512 debugString("[INFO]: getVisibleChildrenCount");
4513 if (ac == null) {
4514 return -1;
4515 }
4516 _visibleChildrenCount = 0;
4517 _getVisibleChildrenCount(ac);
4518 debugString("[INFO]: _visibleChildrenCount = "+_visibleChildrenCount);
4519 return _visibleChildrenCount;
4520 }
4521
4522 /*
4523 * Recursively descends AccessibleContext and gets the number
4524 * of visible children
4525 */
4526 private void _getVisibleChildrenCount(final AccessibleContext ac) {
4527 if (ac == null)
4528 return;
4529 if(ac instanceof AccessibleExtendedTable) {
4530 _getVisibleChildrenCount((AccessibleExtendedTable)ac);
4531 return;
4532 }
4533 int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4534 @Override
4535 public Integer call() throws Exception {
4536 return ac.getAccessibleChildrenCount();
4537 }
4538 }, ac);
4638
4639 if (InvocationUtils.invokeAndWait(new Callable<Integer>() {
4640 @Override
4641 public Integer call() throws Exception {
4642 return ac2.getAccessibleChildrenCount();
4643 }
4644 }, acTable) > 0) {
4645 _getVisibleChildrenCount(ac2);
4646 }
4647 }
4648 }
4649 }
4650
4651 /**
4652 * Gets the visible child of an AccessibleContext at the
4653 * specified index
4654 *
4655 * Bug ID 4944762- getVisibleChildren for list-like components needed
4656 */
4657 private AccessibleContext getVisibleChild(AccessibleContext ac, int index) {
4658 debugString("[INFO]: getVisibleChild: index = "+index);
4659 if (ac == null) {
4660 return null;
4661 }
4662 _visibleChild = null;
4663 _currentVisibleIndex = 0;
4664 _foundVisibleChild = false;
4665 _getVisibleChild(ac, index);
4666
4667 if (_visibleChild != null) {
4668 debugString( "[INFO]: getVisibleChild: found child = " +
4669 InvocationUtils.invokeAndWait(new Callable<String>() {
4670 @Override
4671 public String call() throws Exception {
4672 return AccessBridge.this._visibleChild.getAccessibleName();
4673 }
4674 }, ac) );
4675 }
4676 return _visibleChild;
4677 }
4678
4679 /*
4680 * Recursively searchs AccessibleContext and finds the visible component
4681 * at the specified index
4682 */
4683 private void _getVisibleChild(final AccessibleContext ac, final int index) {
4684 if (_visibleChild != null) {
4685 return;
4686 }
4687 if(ac instanceof AccessibleExtendedTable) {
4688 _getVisibleChild((AccessibleExtendedTable)ac, index);
4837
4838 /**
4839 * Constructor
4840 */
4841 ObjectReferences() {
4842 refs = new ConcurrentHashMap<>(4);
4843 }
4844
4845 /**
4846 * Debugging: dump the contents of ObjectReferences' refs Hashtable
4847 */
4848 String dump() {
4849 return refs.toString();
4850 }
4851
4852 /**
4853 * Increment ref count; set to 1 if we have no references for it
4854 */
4855 void increment(Object o) {
4856 if (o == null){
4857 debugString("[WARN]: ObjectReferences::increment - Passed in object is null");
4858 return;
4859 }
4860
4861 if (refs.containsKey(o)) {
4862 (refs.get(o)).value++;
4863 } else {
4864 refs.put(o, new Reference(1));
4865 }
4866 }
4867
4868 /**
4869 * Decrement ref count; remove if count drops to 0
4870 */
4871 void decrement(Object o) {
4872 Reference aRef = refs.get(o);
4873 if (aRef != null) {
4874 aRef.value--;
4875 if (aRef.value == 0) {
4876 refs.remove(o);
4877 } else if (aRef.value < 0) {
4878 debugString("[ERROR]: decrementing reference count below 0");
4879 }
4880 } else {
4881 debugString("[ERROR]: object to decrement not in ObjectReferences table");
4882 }
4883 }
4884
4885 }
4886
4887 /* ===== event handling code ===== */
4888
4889 /**
4890 * native method for handling property change events
4891 */
4892 private native void propertyCaretChange(PropertyChangeEvent e,
4893 AccessibleContext src,
4894 int oldValue, int newValue);
4895 private native void propertyDescriptionChange(PropertyChangeEvent e,
4896 AccessibleContext src,
4897 String oldValue, String newValue);
4898 private native void propertyNameChange(PropertyChangeEvent e,
4899 AccessibleContext src,
4900 String oldValue, String newValue);
4901 private native void propertySelectionChange(PropertyChangeEvent e,
5196 /**
5197 * Turn off event monitoring for the event type passed in
5198 * If necessary, remove the appropriate event listener (if
5199 * no other event of that type is being listened for)
5200 */
5201 void removeAccessibilityEventNotification(long type) {
5202 long newEventMask = accessibilityEventMask & (~type);
5203 if ( ((accessibilityEventMask & PROPERTY_EVENTS) != 0) &&
5204 ((newEventMask & PROPERTY_EVENTS) == 0) ) {
5205 AccessibilityEventMonitor.removePropertyChangeListener(this);
5206 }
5207 accessibilityEventMask = newEventMask;
5208 }
5209
5210 /**
5211 * ------- property change event glue
5212 */
5213 // This is invoked on the EDT , as
5214 public void propertyChange(PropertyChangeEvent e) {
5215
5216 accessBridge.debugString("[INFO]: propertyChange(" + e.toString() + ") called");
5217
5218 if (e != null && (accessibilityEventMask & PROPERTY_EVENTS) != 0) {
5219 Object o = e.getSource();
5220 AccessibleContext ac;
5221
5222 if (o instanceof AccessibleContext) {
5223 ac = (AccessibleContext) o;
5224 } else {
5225 Accessible a = Translator.getAccessible(e.getSource());
5226 if (a == null)
5227 return;
5228 else
5229 ac = a.getAccessibleContext();
5230 }
5231 if (ac != null) {
5232 InvocationUtils.registerAccessibleContext(ac, AppContext.getAppContext());
5233
5234 accessBridge.debugString("[INFO]: AccessibleContext: " + ac);
5235 String propertyName = e.getPropertyName();
5236
5237 if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CARET_PROPERTY) == 0) {
5238 int oldValue = 0;
5239 int newValue = 0;
5240
5241 if (e.getOldValue() instanceof Integer) {
5242 oldValue = ((Integer) e.getOldValue()).intValue();
5243 }
5244 if (e.getNewValue() instanceof Integer) {
5245 newValue = ((Integer) e.getNewValue()).intValue();
5246 }
5247 accessBridge.debugString("[INFO]: - about to call propertyCaretChange() old value: " + oldValue + "new value: " + newValue);
5248 accessBridge.propertyCaretChange(e, ac, oldValue, newValue);
5249
5250 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY) == 0) {
5251 String oldValue = null;
5252 String newValue = null;
5253
5254 if (e.getOldValue() != null) {
5255 oldValue = e.getOldValue().toString();
5256 }
5257 if (e.getNewValue() != null) {
5258 newValue = e.getNewValue().toString();
5259 }
5260 accessBridge.debugString("[INFO]: - about to call propertyDescriptionChange() old value: " + oldValue + "new value: " + newValue);
5261 accessBridge.propertyDescriptionChange(e, ac, oldValue, newValue);
5262
5263 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_NAME_PROPERTY) == 0) {
5264 String oldValue = null;
5265 String newValue = null;
5266
5267 if (e.getOldValue() != null) {
5268 oldValue = e.getOldValue().toString();
5269 }
5270 if (e.getNewValue() != null) {
5271 newValue = e.getNewValue().toString();
5272 }
5273 accessBridge.debugString("[INFO]: - about to call propertyNameChange() old value: " + oldValue + " new value: " + newValue);
5274 accessBridge.propertyNameChange(e, ac, oldValue, newValue);
5275
5276 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY) == 0) {
5277 accessBridge.debugString("[INFO]: - about to call propertySelectionChange() " + ac + " " + Thread.currentThread() + " " + e.getSource());
5278
5279 accessBridge.propertySelectionChange(e, ac);
5280
5281 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_STATE_PROPERTY) == 0) {
5282 String oldValue = null;
5283 String newValue = null;
5284
5285 // Localization fix requested by Oliver for EA-1
5286 if (e.getOldValue() != null) {
5287 AccessibleState oldState = (AccessibleState) e.getOldValue();
5288 oldValue = oldState.toDisplayString(Locale.US);
5289 }
5290 if (e.getNewValue() != null) {
5291 AccessibleState newState = (AccessibleState) e.getNewValue();
5292 newValue = newState.toDisplayString(Locale.US);
5293 }
5294
5295 accessBridge.debugString("[INFO]: - about to call propertyStateChange()");
5296 accessBridge.propertyStateChange(e, ac, oldValue, newValue);
5297
5298 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_TEXT_PROPERTY) == 0) {
5299 accessBridge.debugString("[INFO]: - about to call propertyTextChange()");
5300 accessBridge.propertyTextChange(e, ac);
5301
5302 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY) == 0) { // strings 'cause of floating point, etc.
5303 String oldValue = null;
5304 String newValue = null;
5305
5306 if (e.getOldValue() != null) {
5307 oldValue = e.getOldValue().toString();
5308 }
5309 if (e.getNewValue() != null) {
5310 newValue = e.getNewValue().toString();
5311 }
5312 accessBridge.debugString("[INFO]: - about to call propertyDescriptionChange()");
5313 accessBridge.propertyValueChange(e, ac, oldValue, newValue);
5314
5315 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY) == 0) {
5316 accessBridge.propertyVisibleDataChange(e, ac);
5317
5318 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) {
5319 AccessibleContext oldAC = null;
5320 AccessibleContext newAC = null;
5321 Accessible a;
5322
5323 if (e.getOldValue() instanceof AccessibleContext) {
5324 oldAC = (AccessibleContext) e.getOldValue();
5325 InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext());
5326 }
5327 if (e.getNewValue() instanceof AccessibleContext) {
5328 newAC = (AccessibleContext) e.getNewValue();
5329 InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext());
5330 }
5331 accessBridge.debugString("[INFO]: - about to call propertyChildChange() old AC: " + oldAC + "new AC: " + newAC);
5332 accessBridge.propertyChildChange(e, ac, oldAC, newAC);
5333
5334 } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0) {
5335 handleActiveDescendentEvent(e, ac);
5336 }
5337 }
5338 }
5339 }
5340
5341 /*
5342 * Handle an ActiveDescendent PropertyChangeEvent. This
5343 * method works around a JTree bug where ActiveDescendent
5344 * PropertyChangeEvents have the wrong parent.
5345 */
5346 private AccessibleContext prevAC = null; // previous AccessibleContext
5347
5348 private void handleActiveDescendentEvent(PropertyChangeEvent e,
5349 AccessibleContext ac) {
5350 if (e == null || ac == null)
5351 return;
5374 if (e.getNewValue() instanceof Accessible) {
5375 newAC = ((Accessible) e.getNewValue()).getAccessibleContext();
5376 } else if (e.getNewValue() instanceof Component) {
5377 a = Translator.getAccessible(e.getNewValue());
5378 if (a != null) {
5379 newAC = a.getAccessibleContext();
5380 }
5381 }
5382 if (newAC != null) {
5383 Accessible parent = newAC.getAccessibleParent();
5384 if (parent instanceof JTree) {
5385 // use a new AccessibleJTreeNode with the right parent
5386 JTree tree = (JTree)parent;
5387 newAC = new AccessibleJTreeNode(tree,
5388 tree.getSelectionPath(),
5389 null);
5390 }
5391 }
5392 prevAC = newAC;
5393
5394 accessBridge.debugString("[INFO]: - about to call propertyActiveDescendentChange() AC: " + ac + " old AC: " + oldAC + "new AC: " + newAC);
5395 InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext());
5396 InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext());
5397 accessBridge.propertyActiveDescendentChange(e, ac, oldAC, newAC);
5398 }
5399
5400 /**
5401 * ------- focus event glue
5402 */
5403 private boolean stateChangeListenerAdded = false;
5404
5405 public void focusGained(FocusEvent e) {
5406 processFocusGained();
5407 }
5408
5409 public void stateChanged(ChangeEvent e) {
5410 processFocusGained();
5411 }
5412
5413 private void processFocusGained() {
5414 Component focusOwner = KeyboardFocusManager.
5419
5420 // Only menus and popup selections are handled by the JRootPane.
5421 if (focusOwner instanceof JRootPane) {
5422 MenuElement [] path =
5423 MenuSelectionManager.defaultManager().getSelectedPath();
5424 if (path.length > 1) {
5425 Component penult = path[path.length-2].getComponent();
5426 Component last = path[path.length-1].getComponent();
5427
5428 if (last instanceof JPopupMenu) {
5429 // This is a popup with nothing in the popup
5430 // selected. The menu itself is selected.
5431 FocusEvent e = new FocusEvent(penult, FocusEvent.FOCUS_GAINED);
5432 AccessibleContext context = penult.getAccessibleContext();
5433 InvocationUtils.registerAccessibleContext(context, SunToolkit.targetToAppContext(penult));
5434 accessBridge.focusGained(e, context);
5435 } else if (penult instanceof JPopupMenu) {
5436 // This is a popup with an item selected
5437 FocusEvent e =
5438 new FocusEvent(last, FocusEvent.FOCUS_GAINED);
5439 AccessibleContext focusedAC = last.getAccessibleContext();
5440 InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(last));
5441 accessBridge.debugString("[INFO]: - about to call focusGained() AC: " + focusedAC);
5442 accessBridge.focusGained(e, focusedAC);
5443 }
5444 }
5445 } else {
5446 // The focus owner has the selection.
5447 if (focusOwner instanceof Accessible) {
5448 FocusEvent e = new FocusEvent(focusOwner,
5449 FocusEvent.FOCUS_GAINED);
5450 AccessibleContext focusedAC = focusOwner.getAccessibleContext();
5451 InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(focusOwner));
5452 accessBridge.debugString("[INFO]: - about to call focusGained() AC: " + focusedAC);
5453 accessBridge.focusGained(e, focusedAC);
5454 }
5455 }
5456 }
5457
5458 public void focusLost(FocusEvent e) {
5459 if (e != null && (javaEventMask & FOCUS_LOST_EVENTS) != 0) {
5460 Accessible a = Translator.getAccessible(e.getSource());
5461 if (a != null) {
5462 accessBridge.debugString("[INFO]: - about to call focusLost() AC: " + a.getAccessibleContext());
5463 AccessibleContext context = a.getAccessibleContext();
5464 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5465 accessBridge.focusLost(e, context);
5466 }
5467 }
5468 }
5469
5470 /**
5471 * ------- caret event glue
5472 */
5473 public void caretUpdate(CaretEvent e) {
5474 if (e != null && (javaEventMask & CARET_UPATE_EVENTS) != 0) {
5475 Accessible a = Translator.getAccessible(e.getSource());
5476 if (a != null) {
5477 AccessibleContext context = a.getAccessibleContext();
5478 InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5479 accessBridge.caretUpdate(e, context);
5480 }
5481 }
5482 }
6145 private TreePath path = null;
6146 private Accessible accessibleParent = null;
6147 private int index = 0;
6148 private boolean isLeaf = false;
6149
6150 /**
6151 * Constructs an AccessibleJTreeNode
6152 */
6153 AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) {
6154 tree = t;
6155 path = p;
6156 accessibleParent = ap;
6157 if (t != null)
6158 treeModel = t.getModel();
6159 if (p != null) {
6160 obj = p.getLastPathComponent();
6161 if (treeModel != null && obj != null) {
6162 isLeaf = treeModel.isLeaf(obj);
6163 }
6164 }
6165 debugString("[INFO]: AccessibleJTreeNode: name = "+getAccessibleName()+"; TreePath = "+p+"; parent = "+ap);
6166 }
6167
6168 private TreePath getChildTreePath(int i) {
6169 // Tree nodes can't be so complex that they have
6170 // two sets of children -> we're ignoring that case
6171 if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) {
6172 return null;
6173 } else {
6174 Object childObj = treeModel.getChild(obj, i);
6175 Object[] objPath = path.getPath();
6176 Object[] objChildPath = new Object[objPath.length+1];
6177 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
6178 objChildPath[objChildPath.length-1] = childObj;
6179 return new TreePath(objChildPath);
6180 }
6181 }
6182
6183 /**
6184 * Get the AccessibleContext associated with this tree node.
6185 * In the implementation of the Java Accessibility API for
6186 * this class, return this object, which is its own
6187 * AccessibleContext.
6188 *
6189 * @return this object
6190 */
6191 public AccessibleContext getAccessibleContext() {
6192 return this;
6193 }
6194
6195 private AccessibleContext getCurrentAccessibleContext() {
6196 Component c = getCurrentComponent();
6197 if (c instanceof Accessible) {
6198 return (c.getAccessibleContext());
6199 } else {
6200 return null;
6201 }
6202 }
6203
6204 private Component getCurrentComponent() {
6205 debugString("[INFO]: AccessibleJTreeNode: getCurrentComponent");
6206 // is the object visible?
6207 // if so, get row, selected, focus & leaf state,
6208 // and then get the renderer component and return it
6209 if (tree != null && tree.isVisible(path)) {
6210 TreeCellRenderer r = tree.getCellRenderer();
6211 if (r == null) {
6212 debugString("[WARN]: returning null 1");
6213 return null;
6214 }
6215 TreeUI ui = tree.getUI();
6216 if (ui != null) {
6217 int row = ui.getRowForPath(tree, path);
6218 boolean selected = tree.isPathSelected(path);
6219 boolean expanded = tree.isExpanded(path);
6220 boolean hasFocus = false; // how to tell?? -PK
6221 Component retval = r.getTreeCellRendererComponent(tree, obj,
6222 selected, expanded,
6223 isLeaf, row, hasFocus);
6224 debugString("[INFO]: returning = "+retval.getClass());
6225 return retval;
6226 }
6227 }
6228 debugString("[WARN]: returning null 2");
6229 return null;
6230 }
6231
6232 // AccessibleContext methods
6233
6234 /**
6235 * Get the accessible name of this object.
6236 *
6237 * @return the localized name of the object; null if this
6238 * object does not have a name
6239 */
6240 public String getAccessibleName() {
6241 debugString("[INFO]: AccessibleJTreeNode: getAccessibleName");
6242 AccessibleContext ac = getCurrentAccessibleContext();
6243 if (ac != null) {
6244 String name = ac.getAccessibleName();
6245 if ((name != null) && (!name.isEmpty())) {
6246 String retval = ac.getAccessibleName();
6247 debugString("[INFO]: returning "+retval);
6248 return retval;
6249 } else {
6250 return null;
6251 }
6252 }
6253 if ((accessibleName != null) && (accessibleName.isEmpty())) {
6254 return accessibleName;
6255 } else {
6256 return null;
6257 }
6258 }
6259
6260 /**
6261 * Set the localized accessible name of this object.
6262 *
6263 * @param s the new localized name of the object.
6264 */
6265 public void setAccessibleName(String s) {
6266 AccessibleContext ac = getCurrentAccessibleContext();
6267 if (ac != null) {
|