OSDN Git Service

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