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 import com.sun.star.accessibility.XAccessible;
25 import com.sun.star.accessibility.XAccessibleEventListener;
26 import com.sun.star.accessibility.AccessibleEventObject;
27 import com.sun.star.lang.EventObject;
28 import com.sun.star.awt.XTopWindowListener;
29 import com.sun.star.uno.UnoRuntime;
30 
31 import java.util.LinkedList;
32 
33 /** This class acts as a proxy for the simple screen reader.  It waits for
34     two types of events:
35     1. Accessibility events signal modifications concerning accessibility
36        objects.
37     2. Top window events inform the listener about new or removed windows.
38 
39     This class exists because events had to be handled in a seperate thread
40     to avoid dead locks: The thread that receives an event must no call back
41     to the Office directly.
42 
43     Soon this should not be necessary anymore.  There is now a flag which
44     switches between synchronous and asynchronous callbacks.
45 
46     All reveived events are eventually forwarded to the actual listener.  In
47     this way it decouples the Office from the listener.
48 */
49 class EventListenerProxy
50     implements Runnable, XAccessibleEventListener, XTopWindowListener
51 {
EventListenerProxy(EventHandler aListener)52     public EventListenerProxy (EventHandler aListener)
53     {
54         maListener = aListener;
55         mbAsynchron = true;
56         if (mbAsynchron)
57         {
58             maEventQueue = new LinkedList();
59             new Thread (this, "EventListenerProxy").start();
60         }
61     }
62 
addEvent(Runnable aEventObject)63     private void addEvent (Runnable aEventObject)
64     {
65         if (mbAsynchron)
66             synchronized (maEventQueue)
67             {
68                 maEventQueue.addLast (aEventObject);
69                 // Tell the queue that there is a new event in the queue.
70                 maEventQueue.notify();
71             }
72         else
73         {
74             System.out.println ("running " + aEventObject);
75             aEventObject.run();
76             System.out.println ("  done");
77         }
78     }
79 
80 
81 
82 
83     /** In an infinite loop, check for events to deliver, then wait on lock
84         (which will be notified when new events arrive)
85     */
run()86     public void run ()
87     {
88         while (true)
89         {
90             // Process all events that are currently in the queue.
91             Runnable aEvent;
92             do
93             {
94                 synchronized (maEventQueue)
95                 {
96                     if (maEventQueue.size() > 0)
97                         aEvent = (Runnable)maEventQueue.removeFirst();
98                     else
99                         aEvent = null;
100                 }
101 
102                 if (aEvent != null)
103                 {
104                     try
105                     {
106                         aEvent.run();
107                     }
108                     catch (Throwable aException)
109                     {
110                         MessageArea.println(
111                             "Exception during event delivery: " + aException);
112                         aException.printStackTrace();
113                     }
114                 }
115             }
116             while (aEvent != null);
117 
118             // Now that the queue is empty go to sleep again.
119             try
120             {
121                 synchronized (maEventQueue)
122                 {
123                     maEventQueue.wait();
124                 }
125             }
126             catch (Exception aException)
127             {
128                 // Ignore this exception since there is not much
129                 // that we can do about it.
130             }
131         }
132     }
133 
134 
disposing( final EventObject aEvent)135     public void disposing( final EventObject aEvent)
136     {
137         addEvent (new Runnable()
138             {
139                 public void run()
140                 {
141                     maListener.disposing (aEvent);
142                 }
143             } );
144     }
145 
notifyEvent(final AccessibleEventObject aEvent)146     public void notifyEvent (final AccessibleEventObject aEvent)
147     {
148         addEvent (
149             new Runnable()
150             {
151                 public void run()
152                 {
153                     maListener.notifyEvent (aEvent);
154                 }
155             } );
156     }
157 
windowOpened(final com.sun.star.lang.EventObject aEvent)158     public void windowOpened (final com.sun.star.lang.EventObject aEvent)
159     {
160         addEvent (
161             new Runnable()
162             {
163                 public void run()
164                 {
165                     maListener.windowOpened (
166                         (XAccessible) UnoRuntime.queryInterface(
167                             XAccessible.class,
168                             aEvent.Source));
169                 }
170             } );
171     }
windowClosing(final com.sun.star.lang.EventObject aEvent)172     public void windowClosing (final com.sun.star.lang.EventObject aEvent)
173     {
174         // Ignored.
175     }
windowClosed(final com.sun.star.lang.EventObject aEvent)176     public void windowClosed (final com.sun.star.lang.EventObject aEvent)
177     {
178         addEvent (
179             new Runnable()
180             {
181                 public void run()
182                 {
183                     maListener.windowClosed (
184                         (XAccessible) UnoRuntime.queryInterface(
185                             XAccessible.class,
186                             aEvent.Source));
187                 }
188                 } );
189     }
windowMinimized(com.sun.star.lang.EventObject aEvent)190     public void windowMinimized (com.sun.star.lang.EventObject aEvent)
191     {
192         // Ignored.
193     }
windowNormalized(com.sun.star.lang.EventObject aEvent)194     public void windowNormalized (com.sun.star.lang.EventObject aEvent)
195     {
196         // Ignored.
197     }
windowActivated(com.sun.star.lang.EventObject aEvent)198     public void windowActivated (com.sun.star.lang.EventObject aEvent)
199     {
200         // Ignored.
201     }
windowDeactivated(com.sun.star.lang.EventObject aEvent)202     public void windowDeactivated (com.sun.star.lang.EventObject aEvent)
203     {
204         // Ignored.
205     }
206 
207     /** The queue of event objects, LinkedList<Runnable>.
208         The queue object will also serve as lock for the consumer/producer type
209         syncronization.
210     */
211     private LinkedList maEventQueue;
212 
213     /** This is the actual listener that the events will eventually forwarded to.
214     */
215     private EventHandler maListener;
216 
217     /** This flag determines whether event forwarding is done asynchroniously
218         or synchroniously.
219     */
220     private boolean mbAsynchron;
221 }
222