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 com.sun.star.uno.*;
27 import com.sun.star.accessibility.*;
28 
29 public class Window extends java.awt.Window implements javax.accessibility.Accessible, NativeFrame {
30     protected XAccessibleComponent unoAccessibleComponent;
31 
32     boolean opened = false;
33     boolean visible = false;
34 
35     java.awt.EventQueue eventQueue = null;
36 
Window(java.awt.Window owner, XAccessibleComponent xAccessibleComponent)37     public Window(java.awt.Window owner, XAccessibleComponent xAccessibleComponent) {
38         super(owner);
39         initialize(xAccessibleComponent);
40     }
41 
initialize(XAccessibleComponent xAccessibleComponent)42     private void initialize(XAccessibleComponent xAccessibleComponent) {
43         unoAccessibleComponent = xAccessibleComponent;
44         eventQueue = java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue();
45         XAccessibleEventBroadcaster broadcaster = (XAccessibleEventBroadcaster)
46             UnoRuntime.queryInterface(XAccessibleEventBroadcaster.class,
47             unoAccessibleComponent);
48         if (broadcaster != null) {
49             broadcaster.addEventListener(new AccessibleWindowListener());
50         }
51     }
52 
53     java.awt.Component initialComponent = null;
54 
getInitialComponent()55     public java.awt.Component getInitialComponent() {
56         if (Build.DEBUG) {
57             System.err.println("returning initial component object of class: " + initialComponent.getClass().getName());
58         }
59         return initialComponent;
60     }
61 
setInitialComponent(java.awt.Component c)62     public void setInitialComponent(java.awt.Component c) {
63         initialComponent = c;
64     }
65 
getHWND()66     public Integer getHWND() {
67         return null;
68     }
69 
70     /**
71     * Determines whether this <code>Component</code> is showing on screen.
72     * This means that the component must be visible, and it must be in a
73     * <code>container</code> that is visible and showing.
74     * @see       #addNotify
75     * @see       #removeNotify
76     * @since JDK1.0
77     */
isShowing()78     public boolean isShowing() {
79         if (isVisible()) {
80             java.awt.Container parent = getParent();
81             return (parent == null) || parent.isShowing();
82         }
83         return false;
84     }
85 
86     /**
87     * Makes this <code>Component</code> displayable by connecting it to a
88     * native screen resource.
89     * This method is called internally by the toolkit and should
90     * not be called directly by programs.
91     * @see       #isDisplayable
92     * @see       #removeNotify
93     * @since JDK1.0
94     */
addNotify()95     public void addNotify() {
96 //      createHierarchyEvents(0, null, null, 0, false);
97     }
98 
99     /**
100     * Makes this <code>Component</code> undisplayable by destroying it native
101     * screen resource.
102     * This method is called by the toolkit internally and should
103     * not be called directly by programs.
104     * @see       #isDisplayable
105     * @see       #addNotify
106     * @since JDK1.0
107     */
removeNotify()108     public void removeNotify() {
109     }
110 
111         /**
112          * Determines if the object is visible.  Note: this means that the
113          * object intends to be visible; however, it may not in fact be
114          * showing on the screen because one of the objects that this object
115          * is contained by is not visible.  To determine if an object is
116          * showing on the screen, use <code>isShowing</code>.
117          *
118          * @return true if object is visible; otherwise, false
119          */
isVisible()120     public boolean isVisible(){
121         return visible;
122     }
123 
124     /**
125     * Shows or hides this component depending on the value of parameter
126     * <code>b</code>.
127     * @param b  if <code>true</code>, shows this component;
128     * otherwise, hides this component
129     * @see #isVisible
130     * @since JDK1.1
131     */
setVisible(boolean b)132     public void setVisible(boolean b) {
133         if (visible != b){
134             visible = b;
135             if (b) {
136                 // If it is the first show, fire WINDOW_OPENED event
137                 if (!opened) {
138                     postWindowEvent(java.awt.event.WindowEvent.WINDOW_OPENED);
139                     opened = true;
140                 }
141                 postComponentEvent(java.awt.event.ComponentEvent.COMPONENT_SHOWN);
142             } else {
143                 postComponentEvent(java.awt.event.ComponentEvent.COMPONENT_HIDDEN);
144             }
145         }
146     }
147 
dispose()148     public void dispose() {
149         setVisible(false);
150         postWindowEvent(java.awt.event.WindowEvent.WINDOW_CLOSED);
151 
152         // Transfer window focus back to the owner window if it is still the active frame
153         if ((getOwner() instanceof Frame && ((Frame) getOwner()).active) ||
154                 (getOwner() instanceof Dialog && ((Dialog) getOwner()).active)) {
155             eventQueue.postEvent(new java.awt.event.WindowEvent(getOwner(),
156                 java.awt.event.WindowEvent.WINDOW_GAINED_FOCUS));
157         }
158     }
159 
postWindowEvent(int i)160     protected void postWindowEvent(int i) {
161         eventQueue.postEvent(new java.awt.event.WindowEvent(this, i));
162     }
163 
postComponentEvent(int i)164     protected void postComponentEvent(int i) {
165         eventQueue.postEvent(new java.awt.event.ComponentEvent(this, i));
166     }
167 
168     /**
169     * Update the proxy objects appropriately on property change events
170     */
171     protected class AccessibleWindowListener implements XAccessibleEventListener {
172 
AccessibleWindowListener()173         protected AccessibleWindowListener() {
174         }
175 
176         // The only expected state changes are ACTIVE and VISIBLE
setComponentState(short state, boolean enable)177         protected void setComponentState(short state, boolean enable) {
178             switch (state) {
179                 case AccessibleStateType.ICONIFIED:
180                     postWindowEvent(enable ?
181                         java.awt.event.WindowEvent.WINDOW_ICONIFIED :
182                         java.awt.event.WindowEvent.WINDOW_DEICONIFIED);
183                     break;
184                 case AccessibleStateType.SHOWING:
185                 case AccessibleStateType.VISIBLE:
186                     setVisible(enable);
187                     break;
188                 default:
189                     if (Build.DEBUG) {
190 //                      System.err.println("[frame]: " + getTitle() + "unexpected state change " + state);
191                     }
192                     break;
193             }
194         }
195 
196         /** Updates the accessible name and fires the appropriate PropertyChangedEvent */
handleNameChangedEvent(Object any)197         protected void handleNameChangedEvent(Object any) {
198             try {
199                 // This causes the property change event to be fired in the VCL thread
200                 // context. If this causes problems, it has to be deligated to the java
201                 // dispatch thread ..
202                 javax.accessibility.AccessibleContext ac = accessibleContext;
203                 if (ac!= null) {
204                     ac.setAccessibleName(AnyConverter.toString(any));
205                 }
206             } catch (com.sun.star.lang.IllegalArgumentException e) {
207             }
208         }
209 
210         /** Updates the accessible description and fires the appropriate PropertyChangedEvent */
handleDescriptionChangedEvent(Object any)211         protected void handleDescriptionChangedEvent(Object any) {
212             try {
213                 // This causes the property change event to be fired in the VCL thread
214                 // context. If this causes problems, it has to be deligated to the java
215                 // dispatch thread ..
216                 if (accessibleContext != null) {
217                     accessibleContext.setAccessibleDescription(AnyConverter.toString(any));
218                 }
219             } catch (com.sun.star.lang.IllegalArgumentException e) {
220             }
221         }
222 
223         /** Updates the internal states and fires the appropriate PropertyChangedEvent */
handleStateChangedEvent(Object any1, Object any2)224         protected void handleStateChangedEvent(Object any1, Object any2) {
225             try {
226                 if (AnyConverter.isShort(any1)) {
227                     setComponentState(AnyConverter.toShort(any1), false);
228                 }
229 
230                 if (AnyConverter.isShort(any2)) {
231                     setComponentState(AnyConverter.toShort(any2), true);
232                 }
233             } catch (com.sun.star.lang.IllegalArgumentException e) {
234             }
235         }
236 
237         /** Fires a visible data property change event */
handleVisibleDataEvent()238         protected void handleVisibleDataEvent() {
239             javax.accessibility.AccessibleContext ac = accessibleContext;
240             if (ac != null) {
241                 ac.firePropertyChange(javax.accessibility.AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, null, null);
242             }
243         }
244 
245         /** Called by OpenOffice process to notify property changes */
notifyEvent(AccessibleEventObject event)246         public void notifyEvent(AccessibleEventObject event) {
247             switch (event.EventId) {
248                 case AccessibleEventId.NAME_CHANGED:
249                     // Set the accessible name for the corresponding context, which will fire a property
250                     // change event itself
251                     handleNameChangedEvent(event.NewValue);
252                     break;
253                 case AccessibleEventId.DESCRIPTION_CHANGED:
254                     // Set the accessible description for the corresponding context, which will fire a property
255                     // change event itself - so do not set propertyName !
256                     handleDescriptionChangedEvent(event.NewValue);
257                     break;
258                 case AccessibleEventId.STATE_CHANGED:
259                     // Update the internal state set and fire the appropriate PropertyChangedEvent
260                     handleStateChangedEvent(event.OldValue, event.NewValue);
261                     break;
262                 case AccessibleEventId.CHILD:
263                     if (AnyConverter.isObject(event.OldValue)) {
264                         AccessibleObjectFactory.removeChild(Window.this, event.OldValue);
265                     } else if (AnyConverter.isObject(event.NewValue)) {
266                         AccessibleObjectFactory.addChild(Window.this, event.NewValue);
267                     }
268                     break;
269                 case AccessibleEventId.VISIBLE_DATA_CHANGED:
270                 case AccessibleEventId.BOUNDRECT_CHANGED:
271                     handleVisibleDataEvent();
272                     break;
273                 default:
274                     // Warn about unhandled events
275                     if(Build.DEBUG) {
276                         System.out.println(this + ": unhandled accessibility event id=" + event.EventId);
277                     }
278             }
279         }
280 
281         /** Called by OpenOffice process to notify that the UNO component is disposing */
disposing(com.sun.star.lang.EventObject eventObject)282         public void disposing(com.sun.star.lang.EventObject eventObject) {
283         }
284     }
285 
286     protected javax.accessibility.AccessibleContext accessibleContext = null;
287 
288     /** Returns the AccessibleContext associated with this object */
getAccessibleContext()289     public javax.accessibility.AccessibleContext getAccessibleContext() {
290         if (accessibleContext == null) {
291             accessibleContext = new AccessibleWindow();
292 //          accessibleContext.setAccessibleName(getTitle());
293         }
294         return accessibleContext;
295     }
296 
297     protected class AccessibleWindow extends java.awt.Window.AccessibleAWTWindow {
AccessibleWindow()298         protected AccessibleWindow() {
299             super();
300         }
301 
302         protected java.awt.event.ComponentListener accessibleComponentHandler = null;
303 
304         /**
305         * Fire PropertyChange listener, if one is registered,
306         * when shown/hidden..
307         */
308         protected class AccessibleComponentHandler implements java.awt.event.ComponentListener {
componentHidden(java.awt.event.ComponentEvent e)309             public void componentHidden(java.awt.event.ComponentEvent e)  {
310                 AccessibleWindow.this.firePropertyChange(
311                     javax.accessibility.AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
312                     javax.accessibility.AccessibleState.VISIBLE, null);
313             }
314 
componentShown(java.awt.event.ComponentEvent e)315             public void componentShown(java.awt.event.ComponentEvent e)  {
316                 AccessibleWindow.this.firePropertyChange(
317                     javax.accessibility.AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
318                     null, javax.accessibility.AccessibleState.VISIBLE);
319             }
320 
componentMoved(java.awt.event.ComponentEvent e)321             public void componentMoved(java.awt.event.ComponentEvent e)  {
322             }
323 
componentResized(java.awt.event.ComponentEvent e)324             public void componentResized(java.awt.event.ComponentEvent e)  {
325             }
326         } // inner class AccessibleComponentHandler
327 
328         protected java.awt.event.ContainerListener accessibleContainerHandler = null;
329 
330         /**
331         * Fire PropertyChange listener, if one is registered,
332         * when children added/removed.
333         */
334 
335         protected class AccessibleContainerHandler implements java.awt.event.ContainerListener {
componentAdded(java.awt.event.ContainerEvent e)336             public void componentAdded(java.awt.event.ContainerEvent e) {
337                 java.awt.Component c = e.getChild();
338                 if (c != null && c instanceof javax.accessibility.Accessible) {
339                     AccessibleWindow.this.firePropertyChange(
340                         javax.accessibility.AccessibleContext.ACCESSIBLE_CHILD_PROPERTY,
341                         null, ((javax.accessibility.Accessible) c).getAccessibleContext());
342                 }
343             }
componentRemoved(java.awt.event.ContainerEvent e)344             public void componentRemoved(java.awt.event.ContainerEvent e) {
345                 java.awt.Component c = e.getChild();
346                 if (c != null && c instanceof javax.accessibility.Accessible) {
347                     AccessibleWindow.this.firePropertyChange(
348                         javax.accessibility.AccessibleContext.ACCESSIBLE_CHILD_PROPERTY,
349                         ((javax.accessibility.Accessible) c).getAccessibleContext(), null);
350                 }
351             }
352         }
353 
354         protected int propertyChangeListenerCount = 0;
355 
356         /**
357         * Add a PropertyChangeListener to the listener list.
358         *
359         * @param listener  The PropertyChangeListener to be added
360         */
addPropertyChangeListener(java.beans.PropertyChangeListener listener)361         public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) {
362             if (propertyChangeListenerCount++ == 0) {
363                 accessibleContainerHandler = new AccessibleContainerHandler();
364                 Window.this.addContainerListener(accessibleContainerHandler);
365 
366                 accessibleComponentHandler = new AccessibleComponentHandler();
367                 Window.this.addComponentListener(accessibleComponentHandler);
368             }
369             super.addPropertyChangeListener(listener);
370         }
371 
372         /**
373         * Remove a PropertyChangeListener from the listener list.
374         * This removes a PropertyChangeListener that was registered
375         * for all properties.
376         *
377         * @param listener  The PropertyChangeListener to be removed
378         */
removePropertyChangeListener(java.beans.PropertyChangeListener listener)379         public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) {
380             if (--propertyChangeListenerCount == 0) {
381                 Window.this.removeComponentListener(accessibleComponentHandler);
382                 accessibleComponentHandler = null;
383 
384                 Window.this.removeContainerListener(accessibleContainerHandler);
385                 accessibleContainerHandler = null;
386             }
387             super.removePropertyChangeListener(listener);
388         }
389 
390         /*
391         * AccessibleComponent
392         */
393 
394         /** Returns the background color of the object */
getBackground()395         public java.awt.Color getBackground() {
396             try {
397                 return new java.awt.Color(unoAccessibleComponent.getBackground());
398             } catch (com.sun.star.uno.RuntimeException e) {
399                 return null;
400             }
401         }
402 
setBackground(java.awt.Color c)403         public void setBackground(java.awt.Color c) {
404             // Not supported by UNO accessibility API
405         }
406 
407         /** Returns the foreground color of the object */
getForeground()408         public java.awt.Color getForeground() {
409             try {
410                 return new java.awt.Color(unoAccessibleComponent.getForeground());
411             } catch (com.sun.star.uno.RuntimeException e) {
412                 return null;
413             }
414         }
415 
setForeground(java.awt.Color c)416         public void setForeground(java.awt.Color c) {
417             // Not supported by UNO accessibility API
418         }
419 
getCursor()420         public java.awt.Cursor getCursor() {
421             // Not supported by UNO accessibility API
422             return null;
423         }
424 
setCursor(java.awt.Cursor cursor)425         public void setCursor(java.awt.Cursor cursor) {
426             // Not supported by UNO accessibility API
427         }
428 
getFont()429         public java.awt.Font getFont() {
430             // FIXME
431             return null;
432         }
433 
setFont(java.awt.Font f)434         public void setFont(java.awt.Font f) {
435             // Not supported by UNO accessibility API
436         }
437 
getFontMetrics(java.awt.Font f)438         public java.awt.FontMetrics getFontMetrics(java.awt.Font f) {
439             // FIXME
440             return null;
441         }
442 
isEnabled()443         public boolean isEnabled() {
444             return Window.this.isEnabled();
445         }
446 
setEnabled(boolean b)447         public void setEnabled(boolean b) {
448             // Not supported by UNO accessibility API
449         }
450 
isVisible()451         public boolean isVisible() {
452             return Window.this.isVisible();
453         }
454 
setVisible(boolean b)455         public void setVisible(boolean b) {
456             // Not supported by UNO accessibility API
457         }
458 
isShowing()459         public boolean isShowing() {
460             return Window.this.isShowing();
461         }
462 
contains(java.awt.Point p)463         public boolean contains(java.awt.Point p) {
464             try {
465                 return unoAccessibleComponent.containsPoint(new com.sun.star.awt.Point(p.x, p.y));
466             } catch (com.sun.star.uno.RuntimeException e) {
467                 return false;
468             }
469         }
470 
471         /** Returns the location of the object on the screen. */
getLocationOnScreen()472         public java.awt.Point getLocationOnScreen() {
473             try {
474                 com.sun.star.awt.Point unoPoint = unoAccessibleComponent.getLocationOnScreen();
475                 return new java.awt.Point(unoPoint.X, unoPoint.Y);
476             } catch (com.sun.star.uno.RuntimeException e) {
477                 return null;
478             }
479         }
480 
481         /** Gets the location of this component in the form of a point specifying the component's top-left corner */
getLocation()482         public java.awt.Point getLocation() {
483             try {
484                 com.sun.star.awt.Point unoPoint = unoAccessibleComponent.getLocationOnScreen();
485                 return new java.awt.Point( unoPoint.X, unoPoint.Y );
486             } catch (com.sun.star.uno.RuntimeException e) {
487                 return null;
488             }
489         }
490 
491         /** Moves this component to a new location */
setLocation(java.awt.Point p)492         public void setLocation(java.awt.Point p) {
493             // Not supported by UNO accessibility API
494         }
495 
496         /** Gets the bounds of this component in the form of a Rectangle object */
getBounds()497         public java.awt.Rectangle getBounds() {
498             try {
499                 com.sun.star.awt.Rectangle unoRect = unoAccessibleComponent.getBounds();
500                 return new java.awt.Rectangle(unoRect.X, unoRect.Y, unoRect.Width, unoRect.Height);
501             } catch (com.sun.star.uno.RuntimeException e) {
502                 return null;
503             }
504         }
505 
506         /** Moves and resizes this component to conform to the new bounding rectangle r */
setBounds(java.awt.Rectangle r)507         public void setBounds(java.awt.Rectangle r) {
508             // Not supported by UNO accessibility API
509         }
510 
511         /** Returns the size of this component in the form of a Dimension object */
getSize()512         public java.awt.Dimension getSize() {
513             try {
514                 com.sun.star.awt.Size unoSize = unoAccessibleComponent.getSize();
515                 return new java.awt.Dimension(unoSize.Width, unoSize.Height);
516             } catch (com.sun.star.uno.RuntimeException e) {
517                 return null;
518             }
519         }
520 
521         /** Resizes this component so that it has width d.width and height d.height */
setSize(java.awt.Dimension d)522         public void setSize(java.awt.Dimension d) {
523             // Not supported by UNO accessibility API
524         }
525 
526         /** Returns the Accessible child, if one exists, contained at the local coordinate Point */
getAccessibleAt(java.awt.Point p)527         public javax.accessibility.Accessible getAccessibleAt(java.awt.Point p) {
528             try {
529                 java.awt.Component c = AccessibleObjectFactory.getAccessibleComponent(
530                     unoAccessibleComponent.getAccessibleAtPoint(new com.sun.star.awt.Point(p.x, p.y)));
531 
532                 return (javax.accessibility.Accessible) c;
533             } catch (com.sun.star.uno.RuntimeException e) {
534                 return null;
535             }
536         }
537 
isFocusTraversable()538         public boolean isFocusTraversable() {
539             return Window.this.isFocusable();
540         }
541 
requestFocus()542         public void requestFocus() {
543             unoAccessibleComponent.grabFocus();
544         }
545     }
546 
547     /**
548      * Determines whether this component is displayable. A component is
549      * displayable when it is connected to a native screen resource.
550      * <p>
551      * A component is made displayable either when it is added to
552      * a displayable containment hierarchy or when its containment
553      * hierarchy is made displayable.
554      * A containment hierarchy is made displayable when its ancestor
555      * window is either packed or made visible.
556      * <p>
557      * A component is made undisplayable either when it is removed from
558      * a displayable containment hierarchy or when its containment hierarchy
559      * is made undisplayable.  A containment hierarchy is made
560      * undisplayable when its ancestor window is disposed.
561      *
562      * @return <code>true</code> if the component is displayable
563      */
564     @Override
isDisplayable()565     public boolean isDisplayable() {
566         return true;
567     }
568 
569 }
570 
571