OSDN Git Service

2004-03-17 Paolo Bonzini <bonzini@gnu.org>
[pf3gnuchains/gcc-fork.git] / libjava / java / awt / EventQueue.java
1 /* Copyright (C) 1999, 2000, 2001, 2002, 2003  Free Software Foundation
2
3 This file is part of GNU Classpath.
4
5 GNU Classpath is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 GNU Classpath is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNU Classpath; see the file COPYING.  If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA.
19
20 Linking this library statically or dynamically with other modules is
21 making a combined work based on this library.  Thus, the terms and
22 conditions of the GNU General Public License cover the whole
23 combination.
24
25 As a special exception, the copyright holders of this library give you
26 permission to link this library with independent modules to produce an
27 executable, regardless of the license terms of these independent
28 modules, and to copy and distribute the resulting executable under
29 terms of your choice, provided that you also meet, for each linked
30 independent module, the terms and conditions of the license of that
31 module.  An independent module is a module which is not derived from
32 or based on this library.  If you modify this library, you may extend
33 this exception to your version of the library, but you are not
34 obligated to do so.  If you do not wish to do so, delete this
35 exception statement from your version. */
36
37
38 package java.awt;
39
40 import java.awt.event.ActionEvent;
41 import java.awt.event.InputEvent;
42 import java.awt.event.InvocationEvent;
43 import java.lang.reflect.InvocationTargetException;
44 import java.util.EmptyStackException;
45
46 /* Written using on-line Java 2 Platform Standard Edition v1.3 API 
47  * Specification, as well as "The Java Class Libraries", 2nd edition 
48  * (Addison-Wesley, 1998).
49  * Status:  Believed complete, but untested.
50  */
51
52 /**
53  * This class manages a queue of <code>AWTEvent</code> objects that
54  * are posted to it.  The AWT system uses only one event queue for all
55  * events.
56  *
57  * @author Bryce McKinlay
58  * @author Aaron M. Renn (arenn@urbanophile.com)
59  */
60 public class EventQueue
61 {
62   private static final int INITIAL_QUEUE_DEPTH = 8;
63   private AWTEvent[] queue = new AWTEvent[INITIAL_QUEUE_DEPTH];
64
65   private int next_in = 0; // Index where next event will be added to queue
66   private int next_out = 0; // Index of next event to be removed from queue
67
68   private EventQueue next;
69   private EventQueue prev;
70   private AWTEvent currentEvent;
71   private long lastWhen = System.currentTimeMillis();
72
73   private EventDispatchThread dispatchThread = new EventDispatchThread(this);
74
75   /**
76    * Initializes a new instance of <code>EventQueue</code>.
77    */
78   public EventQueue()
79   {
80   }
81
82   /**
83    * Returns the next event in the queue.  This method will block until
84    * an event is available or until the thread is interrupted.
85    *
86    * @return The next event in the queue.
87    *
88    * @exception InterruptedException If this thread is interrupted while
89    * waiting for an event to be posted to the queue.
90    */
91   public synchronized AWTEvent getNextEvent()
92     throws InterruptedException
93   {
94     if (next != null)
95       return next.getNextEvent();
96
97     while (next_in == next_out)
98       wait();
99
100     AWTEvent res = queue[next_out];
101
102     if (++next_out == queue.length)
103       next_out = 0;
104     return res;
105   }
106
107   /**
108    * Returns the next event in the queue without removing it from the queue.
109    * This method will block until an event is available or until the thread
110    * is interrupted.
111    *
112    * @return The next event in the queue.
113    * @specnote Does not block. Returns null if there are no events on the 
114    *            queue. 
115    */ 
116   public synchronized AWTEvent peekEvent()
117   {
118     if (next != null)
119       return next.peekEvent();
120
121     if (next_in != next_out)
122       return queue[next_out];
123     else
124       return null;
125   }
126
127   /**
128    * Returns the next event in the queue that has the specified id
129    * without removing it from the queue.
130    * This method will block until an event is available or until the thread
131    * is interrupted.
132    *
133    * @param id The event id to return.
134    *
135    * @return The next event in the queue.
136    *
137    * @specnote Does not block. Returns null if there are no matching events 
138    *            on the queue. 
139    */ 
140   public synchronized AWTEvent peekEvent(int id)
141   {
142     if (next != null)
143       return next.peekEvent(id);
144
145     int i = next_out;
146     while (i != next_in)
147       {
148         AWTEvent qevt = queue[i];
149         if (qevt.id == id)
150           return qevt;
151       }
152     return null;
153   }
154
155   /**
156    * Posts a new event to the queue.
157    *
158    * @param event The event to post to the queue.
159    *
160    * @exception NullPointerException If event is null.
161    */
162   public synchronized void postEvent(AWTEvent evt)
163   {
164     if (next != null)
165       {
166         next.postEvent(evt);
167         return;
168       }
169
170     /* Check for any events already on the queue with the same source 
171        and ID. */       
172     int i = next_out;
173     while (i != next_in)
174       {
175         AWTEvent qevt = queue[i];
176         Object src;
177         if (qevt.id == evt.id
178             && (src = qevt.getSource()) == evt.getSource()
179             && src instanceof Component)
180           {
181             /* If there are, call coalesceEvents on the source component 
182                to see if they can be combined. */
183             Component srccmp = (Component) src;
184             AWTEvent coalesced_evt = srccmp.coalesceEvents(qevt, evt);
185             if (coalesced_evt != null)
186               {
187                 /* Yes. Replace the existing event with the combined event. */
188                 queue[i] = coalesced_evt;
189                 return;
190               }
191             break;
192           }
193         if (++i == queue.length)
194           i = 0;
195       }
196
197     queue[next_in] = evt;    
198     if (++next_in == queue.length)
199       next_in = 0;
200
201     if (next_in == next_out)
202       {
203         /* Queue is full. Extend it. */
204         AWTEvent[] oldQueue = queue;
205         queue = new AWTEvent[queue.length * 2];
206
207         int len = oldQueue.length - next_out;
208         System.arraycopy(oldQueue, next_out, queue, 0, len);
209         if (next_out != 0)
210           System.arraycopy(oldQueue, 0, queue, len, next_out);
211
212         next_out = 0;
213         next_in = oldQueue.length;
214       }
215     notify();
216   }
217
218   /**
219    * Causes runnable to have its run method called in the dispatch thread of the
220    * EventQueue. This will happen after all pending events are processed. The
221    * call blocks until this has happened. This method will throw an Error if
222    * called from the event dispatcher thread.
223    *
224    * @exception InterruptedException If another thread has interrupted
225    * this thread.
226    * @exception InvocationTargetException If an exception is thrown when running
227    * runnable.
228    *
229    * @since 1.2
230    */
231   public static void invokeAndWait(Runnable runnable)
232     throws InterruptedException, InvocationTargetException
233   {
234     if (isDispatchThread ())
235       throw new Error("Can't call invokeAndWait from event dispatch thread");
236
237     EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
238     Thread current = Thread.currentThread();
239
240     InvocationEvent ie = 
241       new InvocationEvent(eq, runnable, current, true);
242
243     synchronized (current)
244       {
245         eq.postEvent(ie);
246         current.wait();
247       }
248
249     Exception exception;
250
251     if ((exception = ie.getException()) != null)
252       throw new InvocationTargetException(exception);
253   }
254
255   /**
256    * This arranges for runnable to have its run method called in the
257    * dispatch thread of the EventQueue.  This will happen after all
258    * pending events are processed.
259    *
260    * @since 1.2
261    */
262   public static void invokeLater(Runnable runnable)
263   {
264     EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
265
266     InvocationEvent ie = 
267       new InvocationEvent(eq, runnable, null, false);
268
269     eq.postEvent(ie);
270   }
271
272   /**
273    * Return true if the current thread is the current AWT event dispatch
274    * thread.
275    */
276   public static boolean isDispatchThread()
277   {
278     EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
279     
280     /* Find last EventQueue in chain */ 
281     while (eq.next != null)
282       eq = eq.next;
283
284     return (Thread.currentThread() == eq.dispatchThread);
285   }
286
287   /**
288    * Return the event currently being dispatched by the event
289    * dispatch thread.  If the current thread is not the event
290    * dispatch thread, this method returns null.
291    *
292    * @since 1.4
293    */
294   public static AWTEvent getCurrentEvent()
295   {
296     EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
297     Thread ct = Thread.currentThread();
298     
299     /* Find out if this thread is the dispatch thread for any of the
300        EventQueues in the chain */ 
301     while (ct != eq.dispatchThread)
302       {
303         // Try next EventQueue, if any
304         if (eq.next == null)
305            return null;  // Not an event dispatch thread
306         eq = eq.next;
307       }
308
309     return eq.currentEvent;
310   }
311
312   /**
313    * Allows a custom EventQueue implementation to replace this one. 
314    * All pending events are transferred to the new queue. Calls to postEvent,
315    * getNextEvent, and peekEvent and others are forwarded to the pushed queue
316    * until it is removed with a pop().
317    *
318    * @exception NullPointerException if newEventQueue is null.
319    */
320   public synchronized void push(EventQueue newEventQueue)
321   {
322     if (newEventQueue == null)
323       throw new NullPointerException ();
324
325     /* Make sure we are at the top of the stack because callers can
326        only get a reference to the one at the bottom using
327        Toolkit.getDefaultToolkit().getSystemEventQueue() */
328     if (next != null)
329       {
330         next.push (newEventQueue);
331         return;
332       }
333
334     /* Make sure we have a live dispatch thread to drive the queue */
335     if (dispatchThread == null)
336       dispatchThread = new EventDispatchThread(this);
337
338     int i = next_out;
339     while (i != next_in)
340       {
341         newEventQueue.postEvent(queue[i]);
342         next_out = i;
343         if (++i == queue.length)
344           i = 0;
345       }
346
347     next = newEventQueue;
348     newEventQueue.prev = this;    
349   }
350
351   /** Transfer any pending events from this queue back to the parent queue that
352     * was previously push()ed. Event dispatch from this queue is suspended.
353     *
354     * @exception EmptyStackException If no previous push was made on this
355     * EventQueue.
356     */
357   protected void pop() throws EmptyStackException
358   {
359     if (prev == null)
360       throw new EmptyStackException();
361
362     /* The order is important here, we must get the prev lock first,
363        or deadlock could occur as callers usually get here following
364        prev's next pointer, and thus obtain prev's lock before trying
365        to get this lock. */
366     synchronized (prev)
367       {
368         prev.next = next;
369         if (next != null)
370           next.prev = prev;
371
372         synchronized (this)
373           {
374             int i = next_out;
375             while (i != next_in)
376               {
377                 prev.postEvent(queue[i]);
378                 next_out = i;
379                 if (++i == queue.length)
380                   i = 0;
381               }
382             // Empty the queue so it can be reused
383             next_in = 0;
384             next_out = 0;
385
386             // Tell our EventDispatchThread that it can end execution
387             dispatchThread.interrupt ();
388             dispatchThread = null;
389           }
390       }
391   }
392
393   /**
394    * Dispatches an event. The manner in which the event is dispatched depends
395    * upon the type of the event and the type of the event's source object.
396    *
397    * @exception NullPointerException If event is null.
398    */
399   protected void dispatchEvent(AWTEvent evt)
400   {
401     currentEvent = evt;
402
403     if (evt instanceof InputEvent)
404       lastWhen = ((InputEvent) evt).getWhen();
405     else if (evt instanceof ActionEvent)
406       lastWhen = ((ActionEvent) evt).getWhen();
407     else if (evt instanceof InvocationEvent)
408       lastWhen = ((InvocationEvent) evt).getWhen();
409
410     if (evt instanceof ActiveEvent)
411       {
412         ActiveEvent active_evt = (ActiveEvent) evt;
413         active_evt.dispatch();
414       }
415     else
416       {
417         Object source = evt.getSource();
418
419         if (source instanceof Component)
420           {
421             Component srccmp = (Component) source;
422             srccmp.dispatchEvent(evt);
423           }
424         else if (source instanceof MenuComponent)
425           {
426             MenuComponent srccmp = (MenuComponent) source;
427             srccmp.dispatchEvent(evt);
428           }
429       }
430   }
431
432   /**
433    * Returns the timestamp of the most recent event that had a timestamp, or
434    * the initialization time of the event queue if no events have been fired.
435    * At present, only <code>InputEvent</code>s, <code>ActionEvent</code>s,
436    * <code>InputMethodEvent</code>s, and <code>InvocationEvent</code>s have
437    * timestamps, but this may be added to other events in future versions.
438    * If this is called by the event dispatching thread, it can be any
439    * (sequential) value, but to other threads, the safest bet is to return
440    * System.currentTimeMillis().
441    *
442    * @return the most recent timestamp
443    * @see InputEvent#getWhen()
444    * @see ActionEvent#getWhen()
445    * @see InvocationEvent#getWhen()
446    * @see InputMethodEvent#getWhen()
447    * @since 1.4
448    */
449   public static long getMostRecentEventTime()
450   {
451     EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
452     if (Thread.currentThread() != eq.dispatchThread)
453       return System.currentTimeMillis();
454     return eq.lastWhen;
455   }
456 }