Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

Java Tip 87: Automate the hourglass cursor

Force Java's UI event queue to decide when to show the hourglass cursor

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone

Page 3 of 3

Meanwhile, back in your event queue, processing of the dispatched event will eventually be completed (hopefully!), at which point the stopTimer() method will be invoked.

34:         synchronized void stopTimer() {
35:             if (parent == null)
36:                 interrupt();
37:             else {
38:                 parent.setCursor(null);
39:                 parent = null;
40:             }
41:         }


If the parent instance variable for your timer thread is still null, then you can be certain that the timer has not yet changed the cursor to an hourglass. In this situation, you are required to interrupt only the timer thread, which will throw an InterruptedException within the thread. Execution of the run() method will then immediately drop to the catch block near the bottom of the loop, at which point it will loop back to the wait() statement and wait for notification of the next event.

Now, to use your queue, you just need to replace the standard event queue with an instance of the special WaitCursorEventQueue, which can be accomplished with the following few lines of code.

    EventQueue waitQueue = new WaitCursorEventQueue(500);
    Toolkit.getDefaultToolkit().getSystemEventQueue().push(waitQueue);



Part of the trick to this tip is choosing the duration after which the hourglass should appear. The delay should be long enough that the hourglass will not appear for most events processed within the UI thread, but it should be short enough so that the user perceives near-immediate feedback when a more intensive task begins processing. In moderate usability testing, a delay in the range of about 500 milliseconds, as shown above, appears to work quite well.

Full code listing

Below is the complete source code for WaitCursorEventQueue.java.

1:  import java.awt.*;
2:  import java.awt.event.*;
3:  import javax.swing.SwingUtilities;
4:  
5:  public class WaitCursorEventQueue extends EventQueue {
6:  
7:      public WaitCursorEventQueue(int delay) {
8:          this.delay = delay;
9:          waitTimer = new WaitCursorTimer();
10:         waitTimer.setDaemon(true);
11:         waitTimer.start();
12:     }
13: 
14:     protected void dispatchEvent(AWTEvent event) {
15:         waitTimer.startTimer(event.getSource());
16:         try {
17:             super.dispatchEvent(event);
18:         }
19:         finally {
20:             waitTimer.stopTimer();
21:         }
22:     }
23: 
24:     private int delay;
25:     private WaitCursorTimer waitTimer;
26: 
27:     private class WaitCursorTimer extends Thread {
28: 
29:         synchronized void startTimer(Object source) {
30:             this.source = source;
31:             notify();
32:         }
33: 
34:         synchronized void stopTimer() {
35:             if (parent == null)
36:                 interrupt();
37:             else {
38:                 parent.setCursor(null);
39:                 parent = null;
40:             }
41:         }
42: 
43:         public synchronized void run() {
44:             while (true) {
45:                 try {
46:                     //wait for notification from startTimer()
47:                     wait();
48: 
49:                     //wait for event processing to reach the threshold, or
50:                     //interruption from stopTimer()
51:                     wait(delay);
52: 
53:                     if (source instanceof Component)
54:                         parent = 
SwingUtilities.getRoot((Component)source);
55:                     else if (source instanceof MenuComponent) {
56:                         MenuContainer mParent =
57:                                 ((MenuComponent)source).getParent();
58:                         if (mParent instanceof Component)
59:                             parent = SwingUtilities.getRoot(
60:                                     (Component)mParent);
61:                     }
62: 
63:                     if (parent != null && parent.isShowing())
64:                         parent.setCursor(
65:                                 Cursor.getPredefinedCursor(
66:                                     Cursor.WAIT_CURSOR));
67:                 }
68:                 catch (InterruptedException ie) { }
69:             }
70:         }
71: 
72:         private Object source;
73:         private Component parent;
74:     }
75: }


Potential setbacks

You should be aware of a few caveats. If your application manually changes the cursor for an entire window, especially while processing UI events, the new event queue will likely cause unexpected results, such as changing the cursor you specified back to the default. This tip will also not work in an applet in the Java Virtual Machines included with currently available Web browsers because of the applet's reliance on JDK 1.2 features. Using Sun's Java plug-in provides the necessary JDK 1.2 compliance, but Toolkit.getSystemEventQueue() is a security-checked method so this tip precludes the use of unsigned applets. It should also be noted that a few third-party tools modify the event queue and could possibly interfere with the operation of the WaitCursorEventQueue. With a few minor enhancements, the WaitCursorEventQueue could even let you disable the hourglass automation at will or for selected event types.

The implementation I've provided offers a simple solution adequate for many applications and, I hope, gives an understandable explanation of Java's event queue processing. Maybe you will never have to think about that hourglass again.

About the author

Kyle Davis has a BS in computer science and works as a software engineer with ChannelPoint, a Colorado-based business-to-business provider of ecommerce technology for the insurance industry. Kyle has seven years' experience in the software industry, including more than two years of Java development. He has been exposed to an array of technologies used everywhere from the government sector to large corporate enterprises and small start-ups.
  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources