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 package org.openoffice.accessibility;
24 
25 import com.sun.star.accessibility.AccessibleRole;
26 import com.sun.star.accessibility.XAccessible;
27 import com.sun.star.accessibility.XAccessibleContext;
28 import com.sun.star.java.XJavaVM;
29 import com.sun.star.uno.*;
30 
31 import org.openoffice.java.accessibility.*;
32 
33 import java.lang.reflect.InvocationTargetException;
34 import java.lang.reflect.Method;
35 
36 import javax.accessibility.*;
37 
38 
39 public class WindowsAccessBridgeAdapter {
40     private static Method registerVirtualFrame;
41     private static Method revokeVirtualFrame;
42     private static java.util.Hashtable frameMap;
43 
getProcessID()44     protected static native byte[] getProcessID();
45 
createMapping(long jvmaccess)46     protected static native boolean createMapping(long jvmaccess);
47 
48     // On Windows all native frames must be registered to the access bridge.
49     // Therefor the bridge exports two methods that we try to find here.
attach(XComponentContext xComponentContext)50     protected static void attach(XComponentContext xComponentContext) {
51         try {
52             Class bridge = Class.forName(
53                     "com.sun.java.accessibility.AccessBridge");
54             Class[] parameterTypes = {
55                 javax.accessibility.Accessible.class, Integer.class
56             };
57 
58             if (bridge != null) {
59                 registerVirtualFrame = bridge.getMethod("registerVirtualFrame",
60                         parameterTypes);
61                 revokeVirtualFrame = bridge.getMethod("revokeVirtualFrame",
62                         parameterTypes);
63 
64                 // load the native dll
65                 System.loadLibrary("java_uno_accessbridge");
66 
67                 Object any = xComponentContext.getValueByName(
68                         "/singletons/com.sun.star.java.theJavaVirtualMachine");
69 
70                 if (AnyConverter.isObject(any)) {
71                     XJavaVM xJavaVM = (XJavaVM) UnoRuntime.queryInterface(XJavaVM.class,
72                             AnyConverter.toObject(new Type(XJavaVM.class), any));
73 
74                     if (xJavaVM != null) {
75                         any = xJavaVM.getJavaVM(getProcessID());
76 
77                         if (AnyConverter.isLong(any)) {
78                             createMapping(AnyConverter.toLong(any));
79                             frameMap = new java.util.Hashtable();
80                         }
81                     }
82                 }
83             }
84         } catch (NoSuchMethodException e) {
85             System.err.println("ERROR: incompatible AccessBridge found: " +
86                 e.getMessage());
87 
88             // Forward this exception to UNO to indicate that the service will
89             // not work correctly.
90             throw new com.sun.star.uno.RuntimeException(
91                 "incompatible AccessBridge class: " + e.getMessage());
92         } catch (java.lang.SecurityException e) {
93             System.err.println("ERROR: no access to AccessBridge: " +
94                 e.getMessage());
95 
96             // Forward this exception to UNO to indicate that the service will not work correctly.
97             throw new com.sun.star.uno.RuntimeException(
98                 "Security exception caught: " + e.getMessage());
99         } catch (ClassNotFoundException e) {
100             // Forward this exception to UNO to indicate that the service will not work correctly.
101             throw new com.sun.star.uno.RuntimeException(
102                 "ClassNotFound exception caught: " + e.getMessage());
103         } catch (IllegalArgumentException e) {
104             System.err.println("IllegalArgumentException caught: " +
105                 e.getMessage());
106 
107             // Forward this exception to UNO to indicate that the service will not work correctly.
108             throw new com.sun.star.uno.RuntimeException(
109                 "IllegalArgumentException caught: " + e.getMessage());
110         } catch (com.sun.star.lang.IllegalArgumentException e) {
111             System.err.println("UNO IllegalArgumentException caught: " +
112                 e.getMessage());
113 
114             // Forward this exception to UNO to indicate that the service will not work correctly.
115             throw new com.sun.star.uno.RuntimeException(
116                 "UNO IllegalArgumentException caught: " + e.getMessage());
117         }
118     }
119 
isAttached()120     protected static boolean isAttached() {
121         return frameMap != null;
122     }
123 
getAccessibleWrapper(XAccessible xAccessible)124     protected static Accessible getAccessibleWrapper(XAccessible xAccessible) {
125         Accessible a = null;
126 
127         try {
128             XAccessibleContext xAccessibleContext = xAccessible.getAccessibleContext();
129 
130             if (xAccessibleContext != null) {
131                 switch (xAccessibleContext.getAccessibleRole()) {
132                     case AccessibleRole.LIST:
133                         a = (Accessible) AccessibleObjectFactory.getAccessibleComponent(xAccessible);
134                         if (a != null) {
135                             a = new ListProxy(a.getAccessibleContext());
136                         }
137                         break;
138 
139                     case AccessibleRole.MENU:
140 
141                         Accessible tmp = (Accessible) AccessibleObjectFactory.getAccessibleComponent(xAccessible);
142                         if (tmp != null) {
143                             AccessibleContext ac = tmp.getAccessibleContext();
144 
145                             if (ac != null) {
146                                 a = new PopupMenuProxy(ac);
147                             }
148                         }
149 
150                         break;
151 
152                     case AccessibleRole.TOOL_TIP:
153                         a = PopupWindow.create(xAccessible);
154                         break;
155 
156                     default:
157                         a = (Accessible) AccessBridge.getTopWindow(xAccessible);
158                         break;
159                 }
160             }
161         } catch (com.sun.star.uno.RuntimeException e) {
162         }
163 
164         return a;
165     }
166 
167     /** Registers a native frame at the Java AccessBridge for Windows */
registerTopWindow(int handle, XAccessible xAccessible)168     public static void registerTopWindow(int handle, XAccessible xAccessible) {
169         Integer hwnd = new Integer(handle);
170 
171         if (!frameMap.contains(hwnd)) {
172             if (Build.DEBUG) {
173                 System.err.println("Native frame " + hwnd + " of role " +
174                     AccessibleRoleAdapter.getAccessibleRole(xAccessible) +
175                     " has been opened");
176             }
177 
178             Accessible a = getAccessibleWrapper(xAccessible);
179 
180             if (a != null) {
181                 Object[] args = { a, hwnd };
182 
183                 frameMap.put(hwnd, a);
184 
185                 if (Build.DEBUG) {
186                     System.err.println("registering native frame " + hwnd);
187                 }
188 
189                 try {
190                     registerVirtualFrame.invoke(null, args);
191                 } catch (IllegalAccessException e) {
192                     System.err.println("IllegalAccessException caught: " +
193                         e.getMessage());
194                 } catch (IllegalArgumentException e) {
195                     System.err.println("IllegalArgumentException caught: " +
196                         e.getMessage());
197                 } catch (InvocationTargetException e) {
198                     System.err.println("InvokationTargetException caught: " +
199                         e.getMessage());
200                 }
201             }
202         }
203     }
204 
205     /** Revokes a native frame at the Java AccessBridge for Windows */
revokeTopWindow(int handle, XAccessible xAccessible)206     public static void revokeTopWindow(int handle, XAccessible xAccessible) {
207         Integer hwnd = new Integer(handle);
208 
209         Accessible a = (Accessible) frameMap.remove(hwnd);
210 
211         if (a != null) {
212             Object[] args = { a, hwnd };
213 
214             if (Build.DEBUG) {
215                 System.err.println("revoking native frame " + hwnd);
216             }
217 
218             try {
219                 revokeVirtualFrame.invoke(null, args);
220             } catch (IllegalAccessException e) {
221                 System.err.println("IllegalAccessException caught: " +
222                     e.getMessage());
223             } catch (IllegalArgumentException e) {
224                 System.err.println("IllegalArgumentException caught: " +
225                     e.getMessage());
226             } catch (InvocationTargetException e) {
227                 System.err.println("InvokationTargetException caught: " +
228                     e.getMessage());
229             }
230         }
231 
232         if (a instanceof PopupWindow) {
233             PopupWindow toolTipWindow = (PopupWindow) a;
234             toolTipWindow.removeAll();
235             toolTipWindow.dispose();
236         }
237     }
238 
239     protected static class PopupMenuProxy extends AccessibleContext
240         implements Accessible, AccessibleComponent {
241         AccessibleContext menu;
242         AccessibleComponent menuComponent;
243         int x = 0; int y = 0; int width = 0; int height = 0;
244 
PopupMenuProxy(AccessibleContext ac)245         PopupMenuProxy(AccessibleContext ac) {
246             menu = ac;
247             menuComponent = menu.getAccessibleComponent();
248 
249             /** calculate the bounding rectangle by iterating over the
250              *  the children.
251              */
252             int x2 = 0; int y2 = 0;
253             int count = ac.getAccessibleChildrenCount();
254             for (int i = 0; i < count; i++) {
255                 Accessible a = menu.getAccessibleChild(i);
256 
257                 if (a != null) {
258                     AccessibleContext childAC = a.getAccessibleContext();
259 
260                     if (childAC != null) {
261                         AccessibleComponent comp = ac.getAccessibleComponent();
262 
263                         if (comp != null) {
264                             java.awt.Point p = comp.getLocationOnScreen();
265                             java.awt.Dimension d = comp.getSize();
266 
267                             if (p != null && d != null) {
268                                 if (p.x < x) {
269                                     x = p.x;
270                                 }
271                                 if (p.y < y) {
272                                     y = p.y;
273                                 }
274                                 if (p.x + d.width > x2) {
275                                     x2 = p.x + d.width;
276                                 }
277                                 if (p.y + d.height > y2) {
278                                     y2 = p.y + d.height;
279                                 }
280                             }
281                         }
282                     }
283                 }
284             }
285 
286             width = x2 - x;
287             height = y2 - y;
288         }
289 
290         /** Returns the AccessibleContext associated with this object */
getAccessibleContext()291         public javax.accessibility.AccessibleContext getAccessibleContext() {
292             return this;
293         }
294 
295         /** Returns the AccessibleContext associated with this object */
getAccessibleComponent()296         public javax.accessibility.AccessibleComponent getAccessibleComponent() {
297             return this;
298         }
299 
300         /** Returns the AccessibleText associated with this object */
getAccessibleText()301         public javax.accessibility.AccessibleText getAccessibleText() {
302             return menu.getAccessibleText();
303         }
304 
305         /** Returns the AccessibleContext associated with this object */
getAccessibleStateSet()306         public javax.accessibility.AccessibleStateSet getAccessibleStateSet() {
307             return menu.getAccessibleStateSet();
308         }
309 
getLocale()310         public java.util.Locale getLocale() {
311             return menu.getLocale();
312         }
313 
getAccessibleIndexInParent()314         public int getAccessibleIndexInParent() {
315             return -1;
316         }
317 
getAccessibleChildrenCount()318         public int getAccessibleChildrenCount() {
319             return menu.getAccessibleChildrenCount();
320         }
321 
getAccessibleChild(int i)322         public javax.accessibility.Accessible getAccessibleChild(int i) {
323             return menu.getAccessibleChild(i);
324         }
325 
getAccessibleRole()326         public javax.accessibility.AccessibleRole getAccessibleRole() {
327             return javax.accessibility.AccessibleRole.POPUP_MENU;
328         }
329 
330         /*
331         * AccessibleComponent
332         */
addFocusListener(java.awt.event.FocusListener fl)333         public void addFocusListener(java.awt.event.FocusListener fl) {
334             menuComponent.addFocusListener(fl);
335         }
336 
removeFocusListener(java.awt.event.FocusListener fl)337         public void removeFocusListener(java.awt.event.FocusListener fl) {
338             menuComponent.removeFocusListener(fl);
339         }
340 
341         /** Returns the background color of the object */
getBackground()342         public java.awt.Color getBackground() {
343             return menuComponent.getBackground();
344         }
345 
setBackground(java.awt.Color c)346         public void setBackground(java.awt.Color c) {
347             // Not supported by UNO accessibility API
348         }
349 
350         /** Returns the foreground color of the object */
getForeground()351         public java.awt.Color getForeground() {
352             return menuComponent.getForeground();
353         }
354 
setForeground(java.awt.Color c)355         public void setForeground(java.awt.Color c) {
356             menuComponent.setForeground(c);
357         }
358 
getCursor()359         public java.awt.Cursor getCursor() {
360             return menuComponent.getCursor();
361         }
362 
setCursor(java.awt.Cursor cursor)363         public void setCursor(java.awt.Cursor cursor) {
364             menuComponent.setCursor(cursor);
365         }
366 
getFont()367         public java.awt.Font getFont() {
368             return menuComponent.getFont();
369         }
370 
setFont(java.awt.Font f)371         public void setFont(java.awt.Font f) {
372             menuComponent.setFont(f);
373         }
374 
getFontMetrics(java.awt.Font f)375         public java.awt.FontMetrics getFontMetrics(java.awt.Font f) {
376             return menuComponent.getFontMetrics(f);
377         }
378 
isEnabled()379         public boolean isEnabled() {
380             return menuComponent.isEnabled();
381         }
382 
setEnabled(boolean b)383         public void setEnabled(boolean b) {
384             menuComponent.setEnabled(b);
385         }
386 
isVisible()387         public boolean isVisible() {
388             return menuComponent.isVisible();
389         }
390 
setVisible(boolean b)391         public void setVisible(boolean b) {
392             menuComponent.setVisible(b);
393         }
394 
isShowing()395         public boolean isShowing() {
396             return menuComponent.isShowing();
397         }
398 
contains(java.awt.Point p)399         public boolean contains(java.awt.Point p) {
400             java.awt.Dimension d = getSize();
401 
402             if (Build.DEBUG) {
403                 System.err.println("PopupMenuProxy.containsPoint(" + p.x + "," +
404                     p.y + ") returns " +
405                     (((d.width >= 0) && (p.x < d.width) && (d.height >= 0) &&
406                     (p.y < d.height)) ? "true" : "false"));
407             }
408 
409             if ((d.width >= 0) && (p.x < d.width) && (d.height >= 0) &&
410                     (p.y < d.height)) {
411                 return true;
412             }
413 
414             return false;
415         }
416 
417         /** Returns the location of the object on the screen. */
getLocationOnScreen()418         public java.awt.Point getLocationOnScreen() {
419             return new java.awt.Point(x,y);
420         }
421 
422         /** Gets the location of this component in the form of a point specifying the component's top-left corner */
getLocation()423         public java.awt.Point getLocation() {
424             // This object represents a toplevel, so this is the same as getLocationOnScreen()
425             return getLocationOnScreen();
426         }
427 
428         /** Moves this component to a new location */
setLocation(java.awt.Point p)429         public void setLocation(java.awt.Point p) {
430             // Not supported by UNO accessibility API
431         }
432 
433         /** Gets the bounds of this component in the form of a Rectangle object */
getBounds()434         public java.awt.Rectangle getBounds() {
435             return new java.awt.Rectangle(x, y, width, height);
436         }
437 
438         /** Moves and resizes this component to conform to the new bounding rectangle r */
setBounds(java.awt.Rectangle r)439         public void setBounds(java.awt.Rectangle r) {
440             // Not supported by UNO accessibility API
441         }
442 
443         /** Returns the size of this component in the form of a Dimension object */
getSize()444         public java.awt.Dimension getSize() {
445             return new java.awt.Dimension(width, height);
446         }
447 
448         /** Resizes this component so that it has width d.width and height d.height */
setSize(java.awt.Dimension d)449         public void setSize(java.awt.Dimension d) {
450             // Not supported by UNO accessibility API
451         }
452 
453         /** Returns the Accessible child, if one exists, contained at the local
454          * coordinate Point
455          */
getAccessibleAt(java.awt.Point p)456         public javax.accessibility.Accessible getAccessibleAt(java.awt.Point p) {
457             java.awt.Point p2 = menuComponent.getLocationOnScreen();
458             return menuComponent.getAccessibleAt(
459                 new java.awt.Point(p.x + x - p2.x, p.y + y - p2.y));
460         }
461 
isFocusTraversable()462         public boolean isFocusTraversable() {
463             return menuComponent.isFocusTraversable();
464         }
465 
requestFocus()466         public void requestFocus() {
467             menuComponent.requestFocus();
468         }
469     }
470 
471     protected static class ListProxy extends AccessibleContext
472         implements Accessible, AccessibleComponent {
473         AccessibleContext list;
474         AccessibleComponent listComponent;
475 
ListProxy(AccessibleContext ac)476         ListProxy(AccessibleContext ac) {
477             list = ac;
478             listComponent = list.getAccessibleComponent();
479         }
480 
481         /** Returns the AccessibleContext associated with this object */
getAccessibleContext()482         public javax.accessibility.AccessibleContext getAccessibleContext() {
483             return this;
484         }
485 
486         /** Returns the AccessibleContext associated with this object */
getAccessibleComponent()487         public javax.accessibility.AccessibleComponent getAccessibleComponent() {
488             return this;
489         }
490 
491         /** Returns the AccessibleSelection associated with this object */
getAccessibleSelection()492         public javax.accessibility.AccessibleSelection getAccessibleSelection() {
493             return list.getAccessibleSelection();
494         }
495 
496         /** Returns the AccessibleContext associated with this object */
getAccessibleStateSet()497         public javax.accessibility.AccessibleStateSet getAccessibleStateSet() {
498             return list.getAccessibleStateSet();
499         }
500 
getLocale()501         public java.util.Locale getLocale() {
502             return list.getLocale();
503         }
504 
getAccessibleIndexInParent()505         public int getAccessibleIndexInParent() {
506             return -1;
507         }
508 
getAccessibleChildrenCount()509         public int getAccessibleChildrenCount() {
510             return list.getAccessibleChildrenCount();
511         }
512 
getAccessibleChild(int i)513         public javax.accessibility.Accessible getAccessibleChild(int i) {
514             return list.getAccessibleChild(i);
515         }
516 
getAccessibleRole()517         public javax.accessibility.AccessibleRole getAccessibleRole() {
518             return javax.accessibility.AccessibleRole.LIST;
519         }
520 
521         /*
522         * AccessibleComponent
523         */
addFocusListener(java.awt.event.FocusListener fl)524         public void addFocusListener(java.awt.event.FocusListener fl) {
525             listComponent.addFocusListener(fl);
526         }
527 
removeFocusListener(java.awt.event.FocusListener fl)528         public void removeFocusListener(java.awt.event.FocusListener fl) {
529             listComponent.removeFocusListener(fl);
530         }
531 
532         /** Returns the background color of the object */
getBackground()533         public java.awt.Color getBackground() {
534             return listComponent.getBackground();
535         }
536 
setBackground(java.awt.Color c)537         public void setBackground(java.awt.Color c) {
538             // Not supported by UNO accessibility API
539         }
540 
541         /** Returns the foreground color of the object */
getForeground()542         public java.awt.Color getForeground() {
543             return listComponent.getForeground();
544         }
545 
setForeground(java.awt.Color c)546         public void setForeground(java.awt.Color c) {
547             listComponent.setForeground(c);
548         }
549 
getCursor()550         public java.awt.Cursor getCursor() {
551             return listComponent.getCursor();
552         }
553 
setCursor(java.awt.Cursor cursor)554         public void setCursor(java.awt.Cursor cursor) {
555             listComponent.setCursor(cursor);
556         }
557 
getFont()558         public java.awt.Font getFont() {
559             return listComponent.getFont();
560         }
561 
setFont(java.awt.Font f)562         public void setFont(java.awt.Font f) {
563             listComponent.setFont(f);
564         }
565 
getFontMetrics(java.awt.Font f)566         public java.awt.FontMetrics getFontMetrics(java.awt.Font f) {
567             return listComponent.getFontMetrics(f);
568         }
569 
isEnabled()570         public boolean isEnabled() {
571             return listComponent.isEnabled();
572         }
573 
setEnabled(boolean b)574         public void setEnabled(boolean b) {
575             listComponent.setEnabled(b);
576         }
577 
isVisible()578         public boolean isVisible() {
579             return listComponent.isVisible();
580         }
581 
setVisible(boolean b)582         public void setVisible(boolean b) {
583            listComponent.setVisible(b);
584         }
585 
isShowing()586         public boolean isShowing() {
587             return listComponent.isShowing();
588         }
589 
contains(java.awt.Point p)590         public boolean contains(java.awt.Point p) {
591             return listComponent.contains(p);
592         }
593 
594         /** Returns the location of the object on the screen. */
getLocationOnScreen()595         public java.awt.Point getLocationOnScreen() {
596             return listComponent.getLocationOnScreen();
597         }
598 
599         /** Gets the location of this component in the form of a point specifying
600          * the component's top-left corner
601          */
getLocation()602         public java.awt.Point getLocation() {
603             // This object represents a toplevel object, so getLocation() should
604             // return the same as getLocationOnScreen().
605             return getLocationOnScreen();
606         }
607 
608         /** Moves this component to a new location */
setLocation(java.awt.Point p)609         public void setLocation(java.awt.Point p) {
610             // Not supported by UNO accessibility API
611         }
612 
613         /** Gets the bounds of this component in the form of a Rectangle object */
getBounds()614         public java.awt.Rectangle getBounds() {
615             java.awt.Point p = getLocationOnScreen();
616             java.awt.Dimension d = getSize();
617             return new java.awt.Rectangle(p.x, p.y, d.width, d.height);
618         }
619 
620         /** Moves and resizes this component to conform to the new bounding rectangle r */
setBounds(java.awt.Rectangle r)621         public void setBounds(java.awt.Rectangle r) {
622             // Not supported by UNO accessibility API
623         }
624 
625         /** Returns the size of this component in the form of a Dimension object */
getSize()626         public java.awt.Dimension getSize() {
627             return listComponent.getSize();
628         }
629 
630         /** Resizes this component so that it has width d.width and height d.height */
setSize(java.awt.Dimension d)631         public void setSize(java.awt.Dimension d) {
632             // Not supported by UNO accessibility API
633         }
634 
635         /** Returns the Accessible child, if one exists, contained at the local
636          * coordinate Point
637          */
getAccessibleAt(java.awt.Point p)638         public javax.accessibility.Accessible getAccessibleAt(java.awt.Point p) {
639             return listComponent.getAccessibleAt(p);
640         }
641 
isFocusTraversable()642         public boolean isFocusTraversable() {
643             return listComponent.isFocusTraversable();
644         }
645 
requestFocus()646         public void requestFocus() {
647             listComponent.requestFocus();
648         }
649     }
650 }
651