KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > Timer


1 /*
2  * @(#)Timer.java 1.45 04/05/05
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8
9
10 package javax.swing;
11
12
13
14 import java.util.*;
15 import java.awt.*;
16 import java.awt.event.*;
17 import java.io.Serializable JavaDoc;
18 import javax.swing.event.EventListenerList JavaDoc;
19
20
21
22 /**
23  * Fires one or more action events after a specified delay.
24  * For example, an animation object can use a <code>Timer</code>
25  * as the trigger for drawing its frames.
26  *
27  *<p>
28  *
29  * Setting up a timer
30  * involves creating a <code>Timer</code> object,
31  * registering one or more action listeners on it,
32  * and starting the timer using
33  * the <code>start</code> method.
34  * For example,
35  * the following code creates and starts a timer
36  * that fires an action event once per second
37  * (as specified by the first argument to the <code>Timer</code> constructor).
38  * The second argument to the <code>Timer</code> constructor
39  * specifies a listener to receive the timer's action events.
40  *
41  *<pre>
42  * int delay = 1000; //milliseconds
43  * ActionListener taskPerformer = new ActionListener() {
44  * public void actionPerformed(ActionEvent evt) {
45  * <em>//...Perform a task...</em>
46  * }
47  * };
48  * new Timer(delay, taskPerformer).start();</pre>
49  *
50  * <p>
51  * Each <code>Timer</code>
52  * has one or more action listeners
53  * and a <em>delay</em>
54  * (the time between action events).
55  * When
56  * <em>delay</em> milliseconds have passed, the <code>Timer</code>
57  * fires an action event to its listeners.
58  * By default, this cycle repeats until
59  * the <code>stop</code> method is called.
60  * If you want the timer to fire only once,
61  * invoke <code>setRepeats(false)</code> on the timer.
62  * To make the delay before the first action event
63  * different from the delay between events,
64  * use the <code>setInitialDelay</code> method.
65  *
66  * <p>
67  * Although all <code>Timer</code>s perform their waiting
68  * using a single, shared thread
69  * (created by the first <code>Timer</code> object that executes),
70  * the action event handlers for <code>Timer</code>s
71  * execute on another thread -- the event-dispatching thread.
72  * This means that the action handlers for <code>Timer</code>s
73  * can safely perform operations on Swing components.
74  * However, it also means that the handlers must execute quickly
75  * to keep the GUI responsive.
76  *
77  * <p>
78  * In v 1.3, another <code>Timer</code> class was added
79  * to the Java platform: <code>java.util.Timer</code>.
80  * Both it and <code>javax.swing.Timer</code>
81  * provide the same basic functionality,
82  * but <code>java.util.Timer</code>
83  * is more general and has more features.
84  * The <code>javax.swing.Timer</code> has two features
85  * that can make it a little easier to use with GUIs.
86  * First, its event handling metaphor is familiar to GUI programmers
87  * and can make dealing with the event-dispatching thread
88  * a bit simpler.
89  * Second, its
90  * automatic thread sharing means that you don't have to
91  * take special steps to avoid spawning
92  * too many threads.
93  * Instead, your timer uses the same thread
94  * used to make cursors blink,
95  * tool tips appear,
96  * and so on.
97  *
98  * <p>
99  * You can find further documentation
100  * and several examples of using timers by visiting
101  * <a HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/timer.html"
102  * target = "_top">How to Use Timers</a>,
103  * a section in <em>The Java Tutorial.</em>
104  * For more examples and help in choosing between
105  * this <code>Timer</code> class and
106  * <code>java.util.Timer</code>,
107  * see
108  * <a HREF="http://java.sun.com/products/jfc/tsc/articles/timer/"
109  * target="_top">Using Timers in Swing Applications</a>,
110  * an article in <em>The Swing Connection.</em>
111  * <p>
112  * <strong>Warning:</strong>
113  * Serialized objects of this class will not be compatible with
114  * future Swing releases. The current serialization support is
115  * appropriate for short term storage or RMI between applications running
116  * the same version of Swing. As of 1.4, support for long term storage
117  * of all JavaBeans<sup><font size="-2">TM</font></sup>
118  * has been added to the <code>java.beans</code> package.
119  * Please see {@link java.beans.XMLEncoder}.
120  *
121  * @see java.util.Timer <code>java.util.Timer</code>
122  *
123  *
124  * @version 1.45 05/05/04
125  * @author Dave Moore
126  */

127 public class Timer implements Serializable JavaDoc
128 {
129     protected EventListenerList JavaDoc listenerList = new EventListenerList JavaDoc();
130
131     // The following field strives to maintain the following:
132
// If coalesce is true, only allow one Runnable to be queued on the
133
// EventQueue and be pending (ie in the process of notifying the
134
// ActionListener). If we didn't do this it would allow for a
135
// situation where the app is taking too long to process the
136
// actionPerformed, and thus we'ld end up queing a bunch of Runnables
137
// and the app would never return: not good. This of course implies
138
// you can get dropped events, but such is life.
139
// notify is used to indicate if the ActionListener can be notified, when
140
// the Runnable is processed if this is true it will notify the listeners.
141
// notify is set to true when the Timer fires and the Runnable is queued.
142
// It will be set to false after notifying the listeners (if coalesce is
143
// true) or if the developer invokes stop.
144
private boolean notify = false;
145
146     int initialDelay, delay;
147     boolean repeats = true, coalesce = true;
148
149     Runnable JavaDoc doPostEvent = null;
150
151     private static boolean logTimers;
152
153
154     // These fields are maintained by TimerQueue.
155
// eventQueued can also be reset by the TimerQueue, but will only ever
156
// happen in applet case when TimerQueues thread is destroyed.
157
long expirationTime;
158     Timer JavaDoc nextTimer;
159     boolean running;
160
161
162     /**
163      * Creates a <code>Timer</code> that will notify its listeners every
164      * <code>delay</code> milliseconds. If <code>delay</code> is less than
165      * or equal to zero the timer will fire as soon as it
166      * is started. If <code>listener</code> is not <code>null</code>,
167      * it's registered as an action listener on the timer.
168      *
169      * @param delay the number of milliseconds between action events
170      * @param listener an initial listener; can be <code>null</code>
171      *
172      * @see #addActionListener
173      * @see #setInitialDelay
174      * @see #setRepeats
175      */

176     public Timer(int delay, ActionListener listener) {
177         super();
178         this.delay = delay;
179         this.initialDelay = delay;
180
181         doPostEvent = new DoPostEvent();
182
183     if (listener != null) {
184         addActionListener(listener);
185     }
186     }
187
188
189     /**
190      * DoPostEvent is a runnable class that fires actionEvents to
191      * the listeners on the EventDispatchThread, via invokeLater.
192      * @see #post
193      */

194     class DoPostEvent implements Runnable JavaDoc, Serializable JavaDoc
195     {
196         public void run() {
197             if (logTimers) {
198                 System.out.println("Timer ringing: " + Timer.this);
199             }
200             if(notify) {
201                 fireActionPerformed(new ActionEvent(Timer.this, 0, null,
202                                                     System.currentTimeMillis(),
203                                                     0));
204                 if (coalesce) {
205                     cancelEvent();
206                 }
207             }
208         }
209
210         Timer JavaDoc getTimer() {
211             return Timer.this;
212         }
213     }
214
215
216     /**
217      * Adds an action listener to the <code>Timer</code>.
218      *
219      * @param listener the listener to add
220      *
221      * @see #Timer
222      */

223     public void addActionListener(ActionListener listener) {
224         listenerList.add(ActionListener.class, listener);
225     }
226
227
228     /**
229      * Removes the specified action listener from the <code>Timer</code>.
230      *
231      * @param listener the listener to remove
232      */

233     public void removeActionListener(ActionListener listener) {
234         listenerList.remove(ActionListener.class, listener);
235     }
236
237
238     /**
239      * Returns an array of all the action listeners registered
240      * on this timer.
241      *
242      * @return all of the timer's <code>ActionListener</code>s or an empty
243      * array if no action listeners are currently registered
244      *
245      * @see #addActionListener
246      * @see #removeActionListener
247      *
248      * @since 1.4
249      */

250     public ActionListener[] getActionListeners() {
251         return (ActionListener[])listenerList.getListeners(
252                 ActionListener.class);
253     }
254
255
256     /**
257      * Notifies all listeners that have registered interest for
258      * notification on this event type.
259      *
260      * @param e the action event to fire
261      * @see EventListenerList
262      */

263     protected void fireActionPerformed(ActionEvent e) {
264         // Guaranteed to return a non-null array
265
Object JavaDoc[] listeners = listenerList.getListenerList();
266
267         // Process the listeners last to first, notifying
268
// those that are interested in this event
269
for (int i=listeners.length-2; i>=0; i-=2) {
270             if (listeners[i]==ActionListener.class) {
271                 ((ActionListener)listeners[i+1]).actionPerformed(e);
272             }
273         }
274     }
275
276     /**
277      * Returns an array of all the objects currently registered as
278      * <code><em>Foo</em>Listener</code>s
279      * upon this <code>Timer</code>.
280      * <code><em>Foo</em>Listener</code>s
281      * are registered using the <code>add<em>Foo</em>Listener</code> method.
282      * <p>
283      * You can specify the <code>listenerType</code> argument
284      * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
285      * For example, you can query a <code>Timer</code>
286      * instance <code>t</code>
287      * for its action listeners
288      * with the following code:
289      *
290      * <pre>ActionListener[] als = (ActionListener[])(t.getListeners(ActionListener.class));</pre>
291      *
292      * If no such listeners exist,
293      * this method returns an empty array.
294      *
295      * @param listenerType the type of listeners requested;
296      * this parameter should specify an interface
297      * that descends from <code>java.util.EventListener</code>
298      * @return an array of all objects registered as
299      * <code><em>Foo</em>Listener</code>s
300      * on this timer,
301      * or an empty array if no such
302      * listeners have been added
303      * @exception ClassCastException if <code>listenerType</code> doesn't
304      * specify a class or interface that implements
305      * <code>java.util.EventListener</code>
306      *
307      * @see #getActionListeners
308      * @see #addActionListener
309      * @see #removeActionListener
310      *
311      * @since 1.3
312      */

313     public <T extends EventListener> T[] getListeners(Class JavaDoc<T> listenerType) {
314     return listenerList.getListeners(listenerType);
315     }
316
317     /**
318      * Returns the timer queue.
319      */

320     TimerQueue JavaDoc timerQueue() {
321         return TimerQueue.sharedInstance();
322     }
323
324
325     /**
326      * Enables or disables the timer log. When enabled, a message
327      * is posted to <code>System.out</code> whenever the timer goes off.
328      *
329      * @param flag <code>true</code> to enable logging
330      * @see #getLogTimers
331      */

332     public static void setLogTimers(boolean flag) {
333         logTimers = flag;
334     }
335
336
337     /**
338      * Returns <code>true</code> if logging is enabled.
339      *
340      * @return <code>true</code> if logging is enabled; otherwise, false
341      * @see #setLogTimers
342      */

343     public static boolean getLogTimers() {
344         return logTimers;
345     }
346
347
348     /**
349      * Sets the <code>Timer</code>'s delay, the number of milliseconds
350      * between successive action events.
351      *
352      * @param delay the delay in milliseconds
353      * @see #setInitialDelay
354      */

355     public void setDelay(int delay) {
356         if (delay < 0) {
357             throw new IllegalArgumentException JavaDoc("Invalid delay: " + delay);
358         }
359         else {
360             this.delay = delay;
361         }
362     }
363
364
365     /**
366      * Returns the delay, in milliseconds,
367      * between firings of action events.
368      *
369      * @see #setDelay
370      * @see #getInitialDelay
371      */

372     public int getDelay() {
373         return delay;
374     }
375
376
377     /**
378      * Sets the <code>Timer</code>'s initial delay,
379      * which by default is the same as the between-event delay.
380      * This is used only for the first action event.
381      * Subsequent action events are spaced
382      * using the delay property.
383      *
384      * @param initialDelay the delay, in milliseconds,
385      * between the invocation of the <code>start</code>
386      * method and the first action event
387      * fired by this timer
388      *
389      * @see #setDelay
390      */

391     public void setInitialDelay(int initialDelay) {
392         if (initialDelay < 0) {
393             throw new IllegalArgumentException JavaDoc("Invalid initial delay: " +
394                                                initialDelay);
395         }
396         else {
397             this.initialDelay = initialDelay;
398         }
399     }
400
401
402     /**
403      * Returns the <code>Timer</code>'s initial delay.
404      *
405      * @see #setInitialDelay
406      * @see #setDelay
407      */

408     public int getInitialDelay() {
409         return initialDelay;
410     }
411
412
413     /**
414      * If <code>flag</code> is <code>false</code>,
415      * instructs the <code>Timer</code> to send only one
416      * action event to its listeners.
417      *
418      * @param flag specify <code>false</code> to make the timer
419      * stop after sending its first action event
420      */

421     public void setRepeats(boolean flag) {
422         repeats = flag;
423     }
424
425
426     /**
427      * Returns <code>true</code> (the default)
428      * if the <code>Timer</code> will send
429      * an action event
430      * to its listeners multiple times.
431      *
432      * @see #setRepeats
433      */

434     public boolean isRepeats() {
435         return repeats;
436     }
437
438
439     /**
440      * Sets whether the <code>Timer</code> coalesces multiple pending
441      * <code>ActionEvent</code> firings.
442      * A busy application may not be able
443      * to keep up with a <code>Timer</code>'s event generation,
444      * causing multiple
445      * action events to be queued. When processed,
446      * the application sends these events one after the other, causing the
447      * <code>Timer</code>'s listeners to receive a sequence of
448      * events with no delay between them. Coalescing avoids this situation
449      * by reducing multiple pending events to a single event.
450      * <code>Timer</code>s
451      * coalesce events by default.
452      *
453      * @param flag specify <code>false</code> to turn off coalescing
454      */

455     public void setCoalesce(boolean flag) {
456         boolean old = coalesce;
457         coalesce = flag;
458         if (!old && coalesce) {
459             // We must do this as otherwise if the Timer once notified
460
// in !coalese mode notify will be stuck to true and never
461
// become false.
462
cancelEvent();
463         }
464     }
465
466
467     /**
468      * Returns <code>true</code> if the <code>Timer</code> coalesces
469      * multiple pending action events.
470      *
471      * @see #setCoalesce
472      */

473     public boolean isCoalesce() {
474         return coalesce;
475     }
476
477
478     /**
479      * Starts the <code>Timer</code>,
480      * causing it to start sending action events
481      * to its listeners.
482      *
483      * @see #stop
484      */

485     public void start() {
486         timerQueue().addTimer(this,
487                               System.currentTimeMillis() + getInitialDelay());
488     }
489
490
491     /**
492      * Returns <code>true</code> if the <code>Timer</code> is running.
493      *
494      * @see #start
495      */

496     public boolean isRunning() {
497         return timerQueue().containsTimer(this);
498     }
499
500
501     /**
502      * Stops the <code>Timer</code>,
503      * causing it to stop sending action events
504      * to its listeners.
505      *
506      * @see #start
507      */

508     public void stop() {
509         timerQueue().removeTimer(this);
510         cancelEvent();
511     }
512
513
514     /**
515      * Restarts the <code>Timer</code>,
516      * canceling any pending firings and causing
517      * it to fire with its initial delay.
518      */

519     public void restart() {
520         stop();
521         start();
522     }
523
524
525     /**
526      * Resets the internal state to indicate this Timer shouldn't notify
527      * any of its listeners. This does not stop a repeatable Timer from
528      * firing again, use <code>stop</code> for that.
529      */

530     synchronized void cancelEvent() {
531         notify = false;
532     }
533
534
535     synchronized void post() {
536         if (notify == false || !coalesce) {
537             notify = true;
538             SwingUtilities.invokeLater(doPostEvent);
539         }
540     }
541 }
542
Popular Tags