1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 package org.openoffice.java.accessibility;
25 
26 import java.lang.ref.WeakReference;
27 import javax.accessibility.Accessible;
28 import javax.accessibility.AccessibleStateSet;
29 
30 import com.sun.star.uno.*;
31 import com.sun.star.accessibility.*;
32 import org.openoffice.java.accessibility.logging.XAccessibleEventLog;
33 
34 /**
35 */
36 public class AccessibleObjectFactory {
37     // This type is needed for conversions from/to uno Any
38     public static final Type XAccessibleType = new Type(XAccessible.class);
39 
40     private static java.util.Hashtable objectList = new java.util.Hashtable();
41     private static java.awt.FocusTraversalPolicy focusTraversalPolicy = new FocusTraversalPolicy();
42 
43     private static java.awt.EventQueue theEventQueue = java.awt.Toolkit.getDefaultToolkit().
44                                                                         getSystemEventQueue();
45 
getEventQueue()46     public static java.awt.EventQueue getEventQueue() {
47         return theEventQueue;
48     }
49 
postFocusGained(java.awt.Component c)50     public static void postFocusGained(java.awt.Component c) {
51         getEventQueue().postEvent(new java.awt.event.FocusEvent(c, java.awt.event.FocusEvent.FOCUS_GAINED));
52     }
53 
postWindowGainedFocus(java.awt.Window w)54     public static void postWindowGainedFocus(java.awt.Window w) {
55         postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_GAINED_FOCUS);
56     }
57 
postWindowLostFocus(java.awt.Window w)58     public static void postWindowLostFocus(java.awt.Window w) {
59         postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_LOST_FOCUS);
60     }
61 
postWindowActivated(java.awt.Window w)62     public static void postWindowActivated(java.awt.Window w) {
63         postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_ACTIVATED);
64     }
65 
postWindowDeactivated(java.awt.Window w)66     public static void postWindowDeactivated(java.awt.Window w) {
67         postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_DEACTIVATED);
68     }
69 
postWindowOpened(java.awt.Window w)70     public static void postWindowOpened(java.awt.Window w) {
71         postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_OPENED);
72     }
73 
postWindowClosed(java.awt.Window w)74     public static void postWindowClosed(java.awt.Window w) {
75         postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_CLOSED);
76     }
77 
invokeAndWait()78     public static void invokeAndWait() {
79         try {
80             theEventQueue.invokeAndWait( new java.lang.Runnable () {
81                                                 public void run() {
82                                                 }
83                                              });
84         } catch (java.lang.reflect.InvocationTargetException e) {
85         } catch (java.lang.InterruptedException e) {
86         }
87     }
88 
postWindowEvent(java.awt.Window w, int i)89     private static void postWindowEvent(java.awt.Window w, int i) {
90         theEventQueue.postEvent(new java.awt.event.WindowEvent(w, i));
91     }
92 
getAccessibleComponent(XAccessible xAccessible)93     public static java.awt.Component getAccessibleComponent(XAccessible xAccessible) {
94         java.awt.Component c = null;
95         if (xAccessible != null) {
96             // Retrieve unique id for the original UNO object to be used as a hash key
97             String oid = UnoRuntime.generateOid(xAccessible);
98 
99             // Check if we already have a wrapper object for this context
100             synchronized (objectList) {
101                 WeakReference r = (WeakReference) objectList.get(oid);
102                 if(r != null) {
103                     c = (java.awt.Component) r.get();
104                 }
105             }
106         }
107         return c;
108     }
109 
addChild(java.awt.Container parent, Object any)110     public static void addChild(java.awt.Container parent, Object any) {
111         try {
112             addChild(parent, (XAccessible) AnyConverter.toObject(XAccessibleType, any));
113         } catch (com.sun.star.lang.IllegalArgumentException e) {
114             System.err.println(e.getClass().getName() + " caught: " + e.getMessage());
115         }
116     }
117 
addChild(java.awt.Container parent, XAccessible child)118     public static void addChild(java.awt.Container parent, XAccessible child) {
119         try {
120             if (child != null) {
121                 XAccessibleContext childAC = child.getAccessibleContext();
122                 if (childAC != null) {
123                     XAccessibleStateSet stateSet = childAC.getAccessibleStateSet();
124                     if (stateSet != null) {
125                         java.awt.Component c = getAccessibleComponent(child);
126 
127                         // Re-use existing wrapper if possible, create a new one otherwise
128                         if (c != null) {
129                             // Seems to be already in child list
130                             if (parent.equals(c.getParent()))
131                                 return;
132                             // Update general component states
133                             c.setEnabled(stateSet.contains(AccessibleStateType.ENABLED));
134                             c.setVisible(stateSet.contains(AccessibleStateType.VISIBLE));
135                         } else {
136                             c = createAccessibleComponentImpl(child, childAC, stateSet);
137                         }
138 
139                         if (c != null) {
140                             if (c instanceof java.awt.Container) {
141                                 populateContainer((java.awt.Container) c, childAC);
142                             }
143                             parent.add(c);
144                             // Simulate focus gained event for new child
145                             if (stateSet.contains(AccessibleStateType.FOCUSED)) {
146                                 postFocusGained(c);
147                             }
148                         }
149                     }
150                 }
151             }
152         } catch (com.sun.star.uno.RuntimeException e) {
153                 System.err.println(e.getClass().getName() + " caught: " + e.getMessage());
154                 e.printStackTrace();
155         }
156     }
157 
removeChild(java.awt.Container parent, Object any)158     protected static void removeChild(java.awt.Container parent, Object any) {
159         try {
160             XAccessible xAccessible = (XAccessible) AnyConverter.toObject(XAccessibleType, any);
161             java.awt.Component c = getAccessibleComponent(xAccessible);
162 
163             if (c != null) {
164                     parent.remove(c);
165 
166                     if (c instanceof java.awt.Container) {
167                         clearContainer((java.awt.Container) c);
168                     }
169                 }
170         } catch (com.sun.star.lang.IllegalArgumentException e) {
171             System.err.println(e.getClass().getName() + " caught: " + e.getMessage());
172         }
173     }
174 
175 
176     /**
177     * Removes all children from the container parent
178     */
179 
clearContainer(java.awt.Container parent)180     protected static void clearContainer(java.awt.Container parent) {
181         // Purge all children from this container
182         int count = parent.getComponentCount();
183         for (int i = 0; i < count; i++) {
184             java.awt.Component c = parent.getComponent(i);
185             if (c instanceof java.awt.Container) {
186                 clearContainer((java.awt.Container) c);
187             }
188         }
189         parent.removeAll();
190     }
191 
192 
193     /**
194     * Populates the given Container parent with wrapper objects for all children of parentAC. This method is
195     * intended to be called when a container is added using a CHILDREN_CHANGED event.
196     */
197 
populateContainer(java.awt.Container parent, XAccessibleContext parentAC)198     protected static void populateContainer(java.awt.Container parent, XAccessibleContext parentAC) {
199         if (parentAC != null) {
200             try {
201                 int childCount = parentAC.getAccessibleChildCount();
202                 for (int i=0; i<childCount; i++) {
203                     addChild(parent, parentAC.getAccessibleChild(i));
204                 }
205             } catch (java.lang.Exception e) {
206                 System.err.println(e.getClass().getName() + " caught: " + e.getMessage());
207                 e.printStackTrace();
208             }
209         }
210     }
211 
212     /**
213     * Populates the given Container parent with wrapper objects for all children of parentAC. This method is
214     * intended to be called when a new window has been opened.
215     */
populateContainer(java.awt.Container parent, XAccessibleContext parentAC, java.awt.Window frame)216     protected static void populateContainer(java.awt.Container parent, XAccessibleContext parentAC, java.awt.Window frame) {
217         if (parentAC != null) {
218             try {
219                 int childCount = parentAC.getAccessibleChildCount();
220                 for (int i=0; i<childCount; i++) {
221                     XAccessible child = parentAC.getAccessibleChild(i);
222                     if (child != null) {
223                         XAccessibleContext childAC = child.getAccessibleContext();
224                         java.awt.Component c = createAccessibleComponent(child, childAC, frame);
225                         if (c != null) {
226                             if (c instanceof java.awt.Container) {
227                                 populateContainer((java.awt.Container) c, childAC, frame);
228                             }
229                             parent.add(c);
230                         }
231                     } else if (Build.DEBUG) {
232                         System.err.println("ignoring not accessible child " + i);
233                     }
234                 }
235             }
236 
237             catch (java.lang.Exception e) {
238                 System.err.println(e.getClass().getName() + " caught: " + e.getMessage());
239                 e.printStackTrace();
240             }
241         }
242     }
243 
createAccessibleComponent(XAccessible xAccessible)244     protected static java.awt.Component createAccessibleComponent(XAccessible xAccessible) {
245         try {
246             XAccessibleContext xAccessibleContext = xAccessible.getAccessibleContext();
247             if (xAccessibleContext != null) {
248                 return createAccessibleComponentImpl(xAccessible, xAccessibleContext, xAccessibleContext.getAccessibleStateSet());
249             }
250         } catch (com.sun.star.uno.RuntimeException e) {
251                 System.err.println(e.getClass().getName() + " caught: " + e.getMessage());
252                 e.printStackTrace();
253         }
254         return null;
255     }
256 
createAccessibleComponent(XAccessible xAccessible, XAccessibleContext xAccessibleContext, java.awt.Window frame)257     protected static java.awt.Component createAccessibleComponent(XAccessible xAccessible, XAccessibleContext xAccessibleContext,
258         java.awt.Window frame) {
259         if (xAccessibleContext != null) {
260             try {
261                 XAccessibleStateSet xAccessibleStateSet = xAccessibleContext.getAccessibleStateSet();
262                 java.awt.Component c = createAccessibleComponentImpl(xAccessible, xAccessibleContext, xAccessibleStateSet);
263                 if (c != null) {
264                     // Set this component as initial component
265                     if (xAccessibleStateSet.contains(AccessibleStateType.FOCUSED)) {
266                         if (frame instanceof NativeFrame) {
267                             ((NativeFrame) frame).setInitialComponent(c);
268                         }
269                     }
270                     return c;
271                 }
272             } catch (com.sun.star.uno.RuntimeException e) {
273                 System.err.println(e.getClass().getName() + " caught: " + e.getMessage());
274                 e.printStackTrace();
275             }
276         }
277         return null;
278     }
279 
createAccessibleComponentImpl(XAccessible xAccessible, XAccessibleContext xAccessibleContext, XAccessibleStateSet xAccessibleStateSet)280     protected static java.awt.Component createAccessibleComponentImpl(XAccessible xAccessible, XAccessibleContext xAccessibleContext,
281         XAccessibleStateSet xAccessibleStateSet) {
282         java.awt.Component c = null;
283         short role = xAccessibleContext.getAccessibleRole();
284         switch (role) {
285             case AccessibleRole.CANVAS:
286                 c = new Container(javax.accessibility.AccessibleRole.CANVAS,
287                     xAccessible, xAccessibleContext);
288                 break;
289             case AccessibleRole.CHECK_BOX:
290                 c = new CheckBox(xAccessible, xAccessibleContext);
291                 break;
292             case AccessibleRole.COMBO_BOX:
293                 c = new ComboBox(xAccessible, xAccessibleContext);
294                 break;
295             case AccessibleRole.DOCUMENT:
296                 c = new Container(javax.accessibility.AccessibleRole.CANVAS,
297                     xAccessible, xAccessibleContext);
298                 break;
299             case AccessibleRole.EMBEDDED_OBJECT:
300                 c = new Container(javax.accessibility.AccessibleRole.PANEL,
301                     xAccessible, xAccessibleContext);
302                 break;
303             case AccessibleRole.END_NOTE:
304                 c = new Container(javax.accessibility.AccessibleRole.PANEL,
305                     xAccessible, xAccessibleContext);
306                 break;
307             case AccessibleRole.FILLER:
308                 c = new Container(javax.accessibility.AccessibleRole.FILLER,
309                     xAccessible, xAccessibleContext);
310                 break;
311             case AccessibleRole.FOOTNOTE:
312                 c = new Container(javax.accessibility.AccessibleRole.PANEL,
313                     xAccessible, xAccessibleContext);
314                 break;
315             case AccessibleRole.FOOTER:
316                 c = new Container(javax.accessibility.AccessibleRole.PANEL,
317                     xAccessible, xAccessibleContext);
318                 break;
319             case AccessibleRole.GRAPHIC:
320                 c = new Container(javax.accessibility.AccessibleRole.PANEL,
321                     xAccessible, xAccessibleContext);
322                 break;
323             case AccessibleRole.HEADER:
324                 c = new Container(javax.accessibility.AccessibleRole.PANEL,
325                     xAccessible, xAccessibleContext);
326                 break;
327             case AccessibleRole.ICON:
328                 c = new Icon(xAccessible, xAccessibleContext);
329                 break;
330             case AccessibleRole.LABEL:
331                 c = new Label(xAccessible, xAccessibleContext);
332                 break;
333             case AccessibleRole.LAYERED_PANE:
334                 c = new Container(javax.accessibility.AccessibleRole.LAYERED_PANE,
335                     xAccessible, xAccessibleContext);
336                 break;
337             case AccessibleRole.LIST:
338                 if (xAccessibleStateSet.contains(AccessibleStateType.MANAGES_DESCENDANTS)) {
339                     c = new List(xAccessible, xAccessibleContext);
340                 } else {
341                     c = new Container(javax.accessibility.AccessibleRole.LIST,
342                         xAccessible, xAccessibleContext);
343                 }
344                 break;
345             case AccessibleRole.MENU:
346                 c = new Menu(xAccessible, xAccessibleContext);
347                 break;
348             case AccessibleRole.MENU_BAR:
349                 c = new MenuContainer(javax.accessibility.AccessibleRole.MENU_BAR, xAccessible, xAccessibleContext);
350                 break;
351             case AccessibleRole.MENU_ITEM:
352                 c = new MenuItem(xAccessible, xAccessibleContext);
353                 break;
354             case AccessibleRole.POPUP_MENU:
355                 c = new MenuContainer(javax.accessibility.AccessibleRole.POPUP_MENU, xAccessible, xAccessibleContext);
356                 break;
357             case AccessibleRole.OPTION_PANE:
358                 c = new Container(javax.accessibility.AccessibleRole.OPTION_PANE,
359                     xAccessible, xAccessibleContext);
360                 break;
361             case AccessibleRole.PAGE_TAB:
362                 c = new Container(javax.accessibility.AccessibleRole.PAGE_TAB, xAccessible, xAccessibleContext);
363                 break;
364             case AccessibleRole.PAGE_TAB_LIST:
365                 c = new Container(javax.accessibility.AccessibleRole.PAGE_TAB_LIST, xAccessible, xAccessibleContext);
366                 break;
367             case AccessibleRole.PARAGRAPH:
368             case AccessibleRole.HEADING:
369                 c = new Paragraph(xAccessible, xAccessibleContext);
370                 break;
371             case AccessibleRole.PANEL:
372                 c = new Container(javax.accessibility.AccessibleRole.PANEL,
373                     xAccessible, xAccessibleContext);
374                 break;
375             case AccessibleRole.PUSH_BUTTON:
376                 c = new Button(xAccessible, xAccessibleContext);
377                 break;
378             case AccessibleRole.RADIO_BUTTON:
379                 c = new RadioButton(xAccessible, xAccessibleContext);
380                 break;
381             case AccessibleRole.ROOT_PANE:
382                 c = new Container(javax.accessibility.AccessibleRole.ROOT_PANE,
383                     xAccessible, xAccessibleContext);
384                 break;
385             case AccessibleRole.SCROLL_BAR:
386                 c = new ScrollBar(xAccessible, xAccessibleContext);
387                 break;
388             case AccessibleRole.SCROLL_PANE:
389                 c = new Container(javax.accessibility.AccessibleRole.SCROLL_PANE,
390                     xAccessible, xAccessibleContext);
391                 break;
392             case AccessibleRole.SEPARATOR:
393                 c = new Separator(xAccessible, xAccessibleContext);
394                 break;
395             case AccessibleRole.SHAPE:
396                 c = new Container(javax.accessibility.AccessibleRole.CANVAS,
397                     xAccessible, xAccessibleContext);
398                 break;
399             case AccessibleRole.SPLIT_PANE:
400                 c = new Container(javax.accessibility.AccessibleRole.SPLIT_PANE,
401                     xAccessible, xAccessibleContext);
402                 break;
403             case AccessibleRole.STATUS_BAR:
404                 c = new Container(javax.accessibility.AccessibleRole.STATUS_BAR,
405                     xAccessible, xAccessibleContext);
406                 break;
407             case AccessibleRole.COLUMN_HEADER:
408             case AccessibleRole.TABLE:
409                 if (xAccessibleStateSet.contains(AccessibleStateType.MANAGES_DESCENDANTS)) {
410                     c = new Table(xAccessible, xAccessibleContext,
411                         xAccessibleStateSet.contains(AccessibleStateType.MULTI_SELECTABLE));
412                 } else {
413                     c = new Container(javax.accessibility.AccessibleRole.TABLE,
414                         xAccessible, xAccessibleContext);
415                 }
416                 break;
417             case AccessibleRole.TABLE_CELL:
418                 if( xAccessibleContext.getAccessibleChildCount() > 0 )
419                     c = new Container(javax.accessibility.AccessibleRole.PANEL,
420                         xAccessible, xAccessibleContext);
421                 else
422                     c = new Label(xAccessible, xAccessibleContext);
423                 break;
424             case AccessibleRole.TEXT:
425                 c = new TextComponent(xAccessible, xAccessibleContext);
426                 break;
427             case AccessibleRole.TEXT_FRAME:
428                 c = new Container(javax.accessibility.AccessibleRole.PANEL,
429                     xAccessible, xAccessibleContext);
430                 break;
431             case AccessibleRole.TOGGLE_BUTTON:
432                 c = new ToggleButton(xAccessible, xAccessibleContext);
433                 break;
434             case AccessibleRole.TOOL_BAR:
435                 c = new Container(javax.accessibility.AccessibleRole.TOOL_BAR,
436                     xAccessible, xAccessibleContext);
437                 break;
438             case AccessibleRole.TOOL_TIP:
439                 c = new ToolTip(xAccessible, xAccessibleContext);
440                 break;
441             case AccessibleRole.TREE:
442                 c = new Tree(xAccessible, xAccessibleContext);
443                 break;
444             case AccessibleRole.VIEW_PORT:
445                 c = new Container(javax.accessibility.AccessibleRole.VIEWPORT,
446                     xAccessible, xAccessibleContext);
447                 break;
448             default:
449                 System.err.println("Unmapped accessible object " + role);
450                 System.err.println("usually mapped to " + AccessibleRoleAdapter.getAccessibleRole(role));
451                 c = new Container(AccessibleRoleAdapter.getAccessibleRole(role),
452                     xAccessible, xAccessibleContext);
453                 break;
454         }
455         if (c != null) {
456             // Add the newly created object to the cache list
457             synchronized (objectList) {
458                 objectList.put(c.toString(), new WeakReference(c));
459                 if (Build.DEBUG) {
460 //                  System.out.println("Object cache now contains " + objectList.size() + " objects.");
461                 }
462             }
463 
464             AccessibleStateAdapter.setComponentState(c, xAccessibleStateSet);
465 
466             if (! Build.PRODUCT) {
467                 String property = System.getProperty("AccessBridgeLogging");
468                 if ((property != null) && (property.indexOf("event") != -1)) {
469                     XAccessibleEventLog.addEventListener(xAccessibleContext, c);
470                 }
471             }
472         }
473 
474         return c;
475     }
476 
disposing(java.awt.Component c)477     protected static void disposing(java.awt.Component c) {
478         if (c != null) {
479             synchronized (objectList) {
480                 objectList.remove(c.toString());
481             }
482         }
483     }
484 
getTopWindow(XAccessible xAccessible)485     public static java.awt.Window getTopWindow(XAccessible xAccessible) {
486         XAccessibleContext xAccessibleContext = xAccessible.getAccessibleContext();
487 
488         if (xAccessibleContext != null) {
489             short role = xAccessibleContext.getAccessibleRole();
490             XAccessibleStateSet xAccessibleStateSet = xAccessibleContext.getAccessibleStateSet();
491             XAccessibleComponent xAccessibleComponent = (XAccessibleComponent)
492                 UnoRuntime.queryInterface(XAccessibleComponent.class, xAccessibleContext);
493 
494             java.awt.Window w;
495             if (role == AccessibleRole.DIALOG) {
496                 w = new Dialog(new Application(),
497                     xAccessibleContext.getAccessibleName(),
498                     xAccessibleStateSet.contains(AccessibleStateType.MODAL),
499                     xAccessibleComponent);
500             } else if (role == AccessibleRole.ALERT) {
501                 w = new Alert(new Application(),
502                     xAccessibleContext.getAccessibleName(),
503                     xAccessibleStateSet.contains(AccessibleStateType.MODAL),
504                     xAccessibleComponent);
505             } else if (role == AccessibleRole.FRAME) {
506                 w = new Frame(xAccessibleContext.getAccessibleName(),
507                     xAccessibleComponent);
508             } else if (role == AccessibleRole.WINDOW) {
509                 java.awt.Window activeWindow =
510                     java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
511                 if (activeWindow != null) {
512                     w = new Window(activeWindow, xAccessibleComponent);
513                 } else {
514                     if (Build.DEBUG) {
515                         System.err.println("no active frame found for Window: " + role);
516                     }
517                     return null;
518                 }
519             } else {
520                 if (Build.DEBUG) {
521                     System.err.println("invalid role for toplevel window: " + role);
522                 }
523                 return null;
524             }
525             populateContainer(w, xAccessibleContext, w);
526             w.setFocusTraversalPolicy(focusTraversalPolicy);
527             w.setVisible(true);
528 
529             // Make the new window the focused one if it has an initialy focused object set.
530             java.awt.Component c = ((NativeFrame) w).getInitialComponent();
531             if (c != null) {
532                 postWindowGainedFocus(w);
533             }
534             return w;
535         }
536 
537         return null;
538     }
539 }
540 
541 
542