1 |
/* Timer.java -- |
/* Timer.java -- |
2 |
Copyright (C) 2002, 2004 Free Software Foundation, Inc. |
Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. |
3 |
|
|
4 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
5 |
|
|
46 |
import javax.swing.event.EventListenerList; |
import javax.swing.event.EventListenerList; |
47 |
|
|
48 |
/** |
/** |
49 |
* DOCUMENT ME! |
* Fires one or more action events after the specified delay. |
50 |
|
* @author Ronald Veldema |
51 |
|
* @author Audrius Meskauskas (audriusa@Bionformatics.org) - bug fixes |
52 |
|
* and documentation comments |
53 |
*/ |
*/ |
54 |
public class Timer implements Serializable |
public class Timer |
55 |
|
implements Serializable |
56 |
{ |
{ |
57 |
/** DOCUMENT ME! */ |
/** |
58 |
private static final long serialVersionUID = -1116180831621385484L; |
* The timer thread |
59 |
|
*/ |
60 |
/** DOCUMENT ME! */ |
private class Waker |
61 |
protected EventListenerList listenerList = new EventListenerList(); |
extends Thread |
62 |
|
{ |
63 |
// This object manages a "queue" of virtual actionEvents, maintained as a |
/** |
64 |
// simple long counter. When the timer expires, a new event is queued, |
* Fires events, pausing for required intervals. |
65 |
// and a dispatcher object is pushed into the system event queue. When |
*/ |
66 |
// the system thread runs the dispatcher, it will fire as many |
public void run() |
67 |
// ActionEvents as have been queued, unless the timer is set to |
{ |
68 |
// coalescing mode, in which case it will fire only one ActionEvent. |
running = true; |
69 |
|
try |
70 |
|
{ |
71 |
|
sleep(initialDelay); |
72 |
|
|
73 |
/** DOCUMENT ME! */ |
queueEvent(); |
|
private long queue; |
|
74 |
|
|
75 |
/** DOCUMENT ME! */ |
while (running) |
76 |
private Object queueLock = new Object(); |
{ |
77 |
|
try |
78 |
|
{ |
79 |
|
sleep(delay); |
80 |
|
} |
81 |
|
catch (InterruptedException e) |
82 |
|
{ |
83 |
|
return; |
84 |
|
} |
85 |
|
queueEvent(); |
86 |
|
|
87 |
|
if (logTimers) |
88 |
|
System.out.println("javax.swing.Timer -> clocktick"); |
89 |
|
|
90 |
|
if ( ! repeats) |
91 |
|
break; |
92 |
|
} |
93 |
|
running = false; |
94 |
|
} |
95 |
|
catch (Exception e) |
96 |
|
{ |
97 |
|
// The timer is no longer running. |
98 |
|
running = false; |
99 |
|
} |
100 |
|
} |
101 |
|
} |
102 |
|
|
103 |
/** DOCUMENT ME! */ |
/** |
104 |
private Waker waker; |
* Use serialVersionUID for interoperability. |
105 |
|
*/ |
106 |
|
private static final long serialVersionUID = -1116180831621385484L; |
107 |
|
|
108 |
private Runnable drainer = new Runnable() |
/** |
109 |
|
* The encloding class, used with {@link SwingUtilities#invokeLater} |
110 |
|
* to invoke the {@link #drainEvents()}. |
111 |
|
*/ |
112 |
|
private Runnable drainer = new Runnable() |
113 |
{ |
{ |
114 |
public void run() |
public void run() |
115 |
{ |
{ |
118 |
}; |
}; |
119 |
|
|
120 |
/** |
/** |
121 |
* DOCUMENT ME! |
* If <code>true</code>, the timer prints a message to |
122 |
|
* {@link System#out} when firing each event. |
123 |
*/ |
*/ |
124 |
private void queueEvent() |
static boolean logTimers; |
|
{ |
|
|
synchronized (queueLock) |
|
|
{ |
|
|
queue++; |
|
|
if (queue == 1) |
|
|
SwingUtilities.invokeLater(drainer); |
|
|
} |
|
|
} |
|
125 |
|
|
126 |
/** |
/** |
127 |
* DOCUMENT ME! |
* A field to store all listeners who are listening to this timer. |
128 |
*/ |
*/ |
129 |
private void drainEvents() |
protected EventListenerList listenerList = new EventListenerList(); |
|
{ |
|
|
synchronized (queueLock) |
|
|
{ |
|
|
if (isCoalesce()) |
|
|
{ |
|
|
if (queue > 0) |
|
|
fireActionPerformed(); |
|
|
} |
|
|
else |
|
|
{ |
|
|
while (queue > 0) |
|
|
{ |
|
|
fireActionPerformed(); |
|
|
queue--; |
|
|
} |
|
|
} |
|
|
queue = 0; |
|
|
} |
|
|
} |
|
|
|
|
|
static boolean logTimers; |
|
130 |
|
|
131 |
/** DOCUMENT ME! */ |
/** |
132 |
|
* <code>true</code> if the timer coalesces events. |
133 |
|
*/ |
134 |
boolean coalesce = true; |
boolean coalesce = true; |
135 |
|
|
136 |
/** DOCUMENT ME! */ |
/** |
137 |
|
* <code>true</code> if the timer is firing repetetive events. |
138 |
|
*/ |
139 |
boolean repeats = true; |
boolean repeats = true; |
140 |
|
|
141 |
/** DOCUMENT ME! */ |
/** |
142 |
|
* <code>true</code> if the timer is currently active, firing events |
143 |
|
* as scheduled. |
144 |
|
*/ |
145 |
boolean running; |
boolean running; |
146 |
|
|
147 |
/** DOCUMENT ME! */ |
/** |
148 |
int ticks; |
* The delay between subsequent repetetive events. |
149 |
|
*/ |
|
/** DOCUMENT ME! */ |
|
150 |
int delay; |
int delay; |
151 |
|
|
152 |
/** DOCUMENT ME! */ |
/** |
153 |
|
* The initial delay before the first event. |
154 |
|
*/ |
155 |
int initialDelay; |
int initialDelay; |
156 |
|
|
157 |
/** |
/** |
158 |
* DOCUMENT ME! |
* The number of events that have been already fired by this timer. |
159 |
|
* This is used as a numeric identifier for the next event that would |
160 |
|
* be fired. |
161 |
*/ |
*/ |
162 |
private class Waker extends Thread |
int ticks; |
163 |
{ |
|
164 |
/** |
/** |
165 |
* DOCUMENT ME! |
* Stores the thread that posts events to the queue at required time |
166 |
*/ |
* intervals. |
167 |
public void run() |
*/ |
168 |
{ |
private Waker waker; |
169 |
running = true; |
|
170 |
try |
/** |
171 |
{ |
* This object manages a "queue" of virtual actionEvents, maintained as a |
172 |
sleep(initialDelay); |
* simple long counter. When the timer expires, a new event is queued, |
173 |
|
* and a dispatcher object is pushed into the system event queue. When |
174 |
queueEvent(); |
* the system thread runs the dispatcher, it will fire as many |
175 |
|
* ActionEvents as have been queued, unless the timer is set to |
176 |
while (running) |
* coalescing mode, in which case it will fire only one ActionEvent. |
177 |
{ |
*/ |
178 |
try |
private long queue; |
179 |
{ |
|
180 |
sleep(delay); |
/** |
181 |
} |
* <code>synchronized(queueLock)</code> replaces |
182 |
catch (InterruptedException e) |
* <code>synchronized(queue)</code> that is not supported by this language. |
183 |
{ |
*/ |
184 |
return; |
private Object queueLock = new Object(); |
|
} |
|
|
queueEvent(); |
|
|
|
|
|
if (logTimers) |
|
|
System.out.println("javax.swing.Timer -> clocktick"); |
|
|
|
|
|
if (! repeats) |
|
|
break; |
|
|
} |
|
|
running = false; |
|
|
} |
|
|
catch (Exception e) |
|
|
{ |
|
|
// System.out.println("swing.Timer::" + e); |
|
|
} |
|
|
} |
|
|
} |
|
185 |
|
|
186 |
/** |
/** |
187 |
* Creates a new Timer object. |
* Creates a new Timer object. |
193 |
public Timer(int d, ActionListener listener) |
public Timer(int d, ActionListener listener) |
194 |
{ |
{ |
195 |
delay = d; |
delay = d; |
196 |
initialDelay = d; |
initialDelay = d; |
197 |
|
|
198 |
if (listener != null) |
if (listener != null) |
199 |
addActionListener(listener); |
addActionListener(listener); |
200 |
} |
} |
201 |
|
|
202 |
/** |
/** |
203 |
* DOCUMENT ME! |
* Get the array of action listeners. |
204 |
* |
* |
205 |
* @param c DOCUMENT ME! |
* @return the array of action listeners that are listening for the events, |
206 |
*/ |
* fired by this timer |
|
public void setCoalesce(boolean c) |
|
|
{ |
|
|
coalesce = c; |
|
|
} |
|
|
|
|
|
/** |
|
|
* DOCUMENT ME! |
|
207 |
* |
* |
208 |
* @return DOCUMENT ME! |
* @since 1.4 |
209 |
*/ |
*/ |
210 |
public boolean isCoalesce() |
public ActionListener[] getActionListeners() |
211 |
{ |
{ |
212 |
return coalesce; |
return (ActionListener[]) listenerList.getListeners(ActionListener.class); |
213 |
} |
} |
214 |
|
|
215 |
/** |
/** |
216 |
* DOCUMENT ME! |
* Sets whether the Timer coalesces multiple pending event firings. |
217 |
|
* If the coalescing is enabled, the multiple events that have not been |
218 |
|
* fired on time are replaced by the single event. The events may not |
219 |
|
* be fired on time if the application is busy. |
220 |
* |
* |
221 |
* @param listener DOCUMENT ME! |
* @param c <code>true</code> (default) to enable the event coalescing, |
222 |
|
* <code>false</code> otherwise |
223 |
*/ |
*/ |
224 |
public void addActionListener(ActionListener listener) |
public void setCoalesce(boolean c) |
225 |
{ |
{ |
226 |
listenerList.add(ActionListener.class, listener); |
coalesce = c; |
227 |
} |
} |
228 |
|
|
229 |
/** |
/** |
230 |
* DOCUMENT ME! |
* Checks if the Timer coalesces multiple pending event firings. |
231 |
|
* If the coalescing is enabled, the multiple events that have not been |
232 |
|
* fired on time are replaced by the single event. The events may not |
233 |
|
* be fired on time if the application is busy. |
234 |
* |
* |
235 |
* @param listener DOCUMENT ME! |
* @return <code>true</code> if the coalescing is enabled, |
236 |
|
* <code>false</code> otherwise |
237 |
*/ |
*/ |
238 |
public void removeActionListener(ActionListener listener) |
public boolean isCoalesce() |
239 |
{ |
{ |
240 |
listenerList.remove(ActionListener.class, listener); |
return coalesce; |
241 |
} |
} |
242 |
|
|
243 |
/** |
/** |
244 |
* DOCUMENT ME! |
* Get the event listeners of the given type that are listening for the |
245 |
* |
* events, fired by this timer. |
|
* @param listenerType DOCUMENT ME! |
|
246 |
* |
* |
247 |
* @return DOCUMENT ME! |
* @param listenerType the listener type (for example, ActionListener.class) |
248 |
* |
* |
249 |
|
* @return the array of event listeners that are listening for the events, |
250 |
|
* fired by this timer |
251 |
* @since 1.3 |
* @since 1.3 |
252 |
*/ |
*/ |
253 |
public EventListener[] getListeners(Class listenerType) |
public EventListener[] getListeners(Class listenerType) |
256 |
} |
} |
257 |
|
|
258 |
/** |
/** |
259 |
* DOCUMENT ME! |
* Set the timer logging state. If it is set to <code>true</code>, the |
260 |
* |
* timer prints a message to {@link System#out} when firing each |
261 |
* @return DOCUMENT ME! |
* action event. |
|
* |
|
|
* @since 1.4 |
|
|
*/ |
|
|
public ActionListener[] getActionListeners() |
|
|
{ |
|
|
return (ActionListener[]) listenerList.getListeners(ActionListener.class); |
|
|
} |
|
|
|
|
|
/** |
|
|
* DOCUMENT ME! |
|
|
* |
|
|
* @param event DOCUMENT ME! |
|
|
*/ |
|
|
protected void fireActionPerformed(ActionEvent event) |
|
|
{ |
|
|
ActionListener[] listeners = getActionListeners(); |
|
|
|
|
|
for (int i = 0; i < listeners.length; i++) |
|
|
listeners[i].actionPerformed(event); |
|
|
} |
|
|
|
|
|
/** |
|
|
* DOCUMENT ME! |
|
|
*/ |
|
|
void fireActionPerformed() |
|
|
{ |
|
|
fireActionPerformed(new ActionEvent(this, ticks++, "Timer")); |
|
|
} |
|
|
|
|
|
/** |
|
|
* DOCUMENT ME! |
|
262 |
* |
* |
263 |
* @param lt DOCUMENT ME! |
* @param lt <code>true</code> if logging is enabled, <code>false</code> |
264 |
|
* (default value) otherwise |
265 |
*/ |
*/ |
266 |
public static void setLogTimers(boolean lt) |
public static void setLogTimers(boolean lt) |
267 |
{ |
{ |
269 |
} |
} |
270 |
|
|
271 |
/** |
/** |
272 |
* DOCUMENT ME! |
* Return the logging state. |
273 |
* |
* |
274 |
* @return DOCUMENT ME! |
* @return <code>true</code> if the timer is printing a message to |
275 |
|
* {@link System#out} |
276 |
|
* when firing each action event |
277 |
*/ |
*/ |
278 |
public static boolean getLogTimers() |
public static boolean getLogTimers() |
279 |
{ |
{ |
281 |
} |
} |
282 |
|
|
283 |
/** |
/** |
284 |
* DOCUMENT ME! |
* Set the delay between firing the subsequent events. |
285 |
|
* This parameter does not change the value of the initial delay before |
286 |
|
* firing the first event. |
287 |
* |
* |
288 |
* @param d DOCUMENT ME! |
* @param d The time gap between the subsequent events, in milliseconds |
289 |
*/ |
*/ |
290 |
public void setDelay(int d) |
public void setDelay(int d) |
291 |
{ |
{ |
293 |
} |
} |
294 |
|
|
295 |
/** |
/** |
296 |
* DOCUMENT ME! |
* Get the delay between firing the subsequent events. |
297 |
* |
* |
298 |
* @return DOCUMENT ME! |
* @return The delay between subsequent events, in milliseconds |
299 |
*/ |
*/ |
300 |
public int getDelay() |
public int getDelay() |
301 |
{ |
{ |
303 |
} |
} |
304 |
|
|
305 |
/** |
/** |
306 |
* DOCUMENT ME! |
* Set the intial delay before firing the first event since calling |
307 |
|
* the {@link #start()} method. If the initial delay has not been |
308 |
|
* set, it is assumed having the same value as the delay between the |
309 |
|
* subsequent events. |
310 |
* |
* |
311 |
* @param i DOCUMENT ME! |
* @param i the initial delay, in milliseconds |
312 |
*/ |
*/ |
313 |
public void setInitialDelay(int i) |
public void setInitialDelay(int i) |
314 |
{ |
{ |
316 |
} |
} |
317 |
|
|
318 |
/** |
/** |
319 |
* DOCUMENT ME! |
* Get the intial delay before firing the first event since calling |
320 |
|
* the {@link #start()} method. If the initial delay has not been |
321 |
|
* set, returns the same value as {@link #getDelay()}. |
322 |
* |
* |
323 |
* @return DOCUMENT ME! |
* @return the initial delay before firing the first action event. |
324 |
*/ |
*/ |
325 |
public int getInitialDelay() |
public int getInitialDelay() |
326 |
{ |
{ |
328 |
} |
} |
329 |
|
|
330 |
/** |
/** |
331 |
* DOCUMENT ME! |
* Enable firing the repetetive events. |
332 |
* |
* |
333 |
* @param r DOCUMENT ME! |
* @param r <code>true</code> (default value) to fire repetetive events. |
334 |
|
* <code>false</code> to fire |
335 |
|
* only one event after the initial delay |
336 |
*/ |
*/ |
337 |
public void setRepeats(boolean r) |
public void setRepeats(boolean r) |
338 |
{ |
{ |
340 |
} |
} |
341 |
|
|
342 |
/** |
/** |
343 |
* DOCUMENT ME! |
* Check is this timer fires repetetive events. |
344 |
* |
* |
345 |
* @return DOCUMENT ME! |
* @return <code>true</code> if the timer fires repetetive events, |
346 |
|
* <code>false</code> if it fires |
347 |
|
* only one event after the initial delay |
348 |
*/ |
*/ |
349 |
public boolean isRepeats() |
public boolean isRepeats() |
350 |
{ |
{ |
352 |
} |
} |
353 |
|
|
354 |
/** |
/** |
355 |
* DOCUMENT ME! |
* Get the timer state. |
356 |
* |
* |
357 |
* @return DOCUMENT ME! |
* @return <code>true</code> if the timer has been started and is firing |
358 |
|
* the action events as scheduled. <code>false</code> |
359 |
|
* if the timer is inactive. |
360 |
*/ |
*/ |
361 |
public boolean isRunning() |
public boolean isRunning() |
362 |
{ |
{ |
364 |
} |
} |
365 |
|
|
366 |
/** |
/** |
367 |
* DOCUMENT ME! |
* Add the action listener |
368 |
|
* |
369 |
|
* @param listener the action listener to add |
370 |
*/ |
*/ |
371 |
public void start() |
public void addActionListener(ActionListener listener) |
372 |
{ |
{ |
373 |
if (isRunning()) |
listenerList.add(ActionListener.class, listener); |
|
return; |
|
|
waker = new Waker(); |
|
|
waker.start(); |
|
374 |
} |
} |
375 |
|
|
376 |
/** |
/** |
377 |
* DOCUMENT ME! |
* Remove the action listener. |
378 |
|
* |
379 |
|
* @param listener the action listener to remove |
380 |
|
*/ |
381 |
|
public void removeActionListener(ActionListener listener) |
382 |
|
{ |
383 |
|
listenerList.remove(ActionListener.class, listener); |
384 |
|
} |
385 |
|
|
386 |
|
/** |
387 |
|
* Cancel all pending tasks and fire the first event after the initial |
388 |
|
* delay. |
389 |
*/ |
*/ |
390 |
public void restart() |
public void restart() |
391 |
{ |
{ |
394 |
} |
} |
395 |
|
|
396 |
/** |
/** |
397 |
* DOCUMENT ME! |
* Start firing the action events. |
398 |
|
*/ |
399 |
|
public void start() |
400 |
|
{ |
401 |
|
if (isRunning()) |
402 |
|
return; |
403 |
|
waker = new Waker(); |
404 |
|
waker.start(); |
405 |
|
} |
406 |
|
|
407 |
|
/** |
408 |
|
* Stop firing the action events. |
409 |
*/ |
*/ |
410 |
public void stop() |
public void stop() |
411 |
{ |
{ |
414 |
waker.interrupt(); |
waker.interrupt(); |
415 |
synchronized (queueLock) |
synchronized (queueLock) |
416 |
{ |
{ |
417 |
queue = 0; |
queue = 0; |
418 |
|
} |
419 |
|
} |
420 |
|
|
421 |
|
/** |
422 |
|
* Fire the given action event to the action listeners. |
423 |
|
* |
424 |
|
* @param event the event to fire |
425 |
|
*/ |
426 |
|
protected void fireActionPerformed(ActionEvent event) |
427 |
|
{ |
428 |
|
ActionListener[] listeners = getActionListeners(); |
429 |
|
|
430 |
|
for (int i = 0; i < listeners.length; i++) |
431 |
|
listeners [ i ].actionPerformed(event); |
432 |
|
} |
433 |
|
|
434 |
|
/** |
435 |
|
* Fire the action event, named "Timer" and having the numeric |
436 |
|
* identifier, equal to the numer of events that have been |
437 |
|
* already fired before. |
438 |
|
*/ |
439 |
|
void fireActionPerformed() |
440 |
|
{ |
441 |
|
fireActionPerformed(new ActionEvent(this, ticks++, "Timer")); |
442 |
|
} |
443 |
|
|
444 |
|
/** |
445 |
|
* Fire the queued action events. |
446 |
|
* In the coalescing mode, a single event is fired as a replacement |
447 |
|
* for all queued events. In non coalescing mode, a series of |
448 |
|
* all queued events is fired. |
449 |
|
*/ |
450 |
|
private void drainEvents() |
451 |
|
{ |
452 |
|
synchronized (queueLock) |
453 |
|
{ |
454 |
|
if (isCoalesce()) |
455 |
|
{ |
456 |
|
if (queue > 0) |
457 |
|
fireActionPerformed(); |
458 |
|
} |
459 |
|
else |
460 |
|
{ |
461 |
|
while (queue > 0) |
462 |
|
{ |
463 |
|
fireActionPerformed(); |
464 |
|
queue--; |
465 |
|
} |
466 |
|
} |
467 |
|
queue = 0; |
468 |
|
} |
469 |
|
} |
470 |
|
|
471 |
|
/** |
472 |
|
* Post a scheduled event to the event queue. |
473 |
|
*/ |
474 |
|
private void queueEvent() |
475 |
|
{ |
476 |
|
synchronized (queueLock) |
477 |
|
{ |
478 |
|
queue++; |
479 |
|
if (queue == 1) |
480 |
|
SwingUtilities.invokeLater(drainer); |
481 |
} |
} |
482 |
} |
} |
483 |
} |
} |