Part 0: The "Event Dispatch" Thread

Consider the following program. Copy the code into files, compile and run this program. Clicking on the button toggles between the text LOVE and HATE. If you run it with argument 0, you can type one of the colors green, red, blue or cyan into the terminal window and it changes the color of the text. If you run it with argument 1, you need to click on the "mystery" button in order to be able to enter a color into the terminal.

  1. Explore the following: run the program with argument 0, and stop typing halfway through a color name, and go up and click "toggle" a few times. Now run with argument 1, click on the "mystery:" button, and then do the same thing: stop typing halfway through a color name, and go up and click "toggle" a few times. What's the difference in the behavior you observe?
  2. Now add the following statement as the first line of the changeColor method
    System.out.println("Thread ID: " + Thread.currentThread().getId());
    recompile, and then try the same two runs you did in the previous step. What's the difference?

What's going on here? Well it turns out that all GUI programs are inherently multi-threaded. There's the main thread (ID 1), which any program has. There's also a thread called the "event dispatch thread", and that's the thread on which all calls to GUI "listeners" (ActionListeners, WindowListeners, etc) occur. Somewhere on the event dispatch thread's call-stack is a record for a call to a function that is essentially an infinite loop that waits for the next GUI action, and the starts calling the necessary functions to respond - a process that eventually results in listeners being called. After all those calls are made, the stack eventually goes back down to the function with the infinite loop ... which waits around for the next GUI action.

So now think about our example above: with argument 0, the call to CChange.changeColor() was made in main(), which means a new record for the CChange.changeColor() call was made on the Thread 1 call stack. Thus, the event dispatch thread was left free to wait for events and, ultimately, to call the Toggler actionPerformed() method. On the other hand, with argument 1, the call to CChange.changeColor() was made in Mystery's actionPerformed() method, which means a new record on the event dispatch thread's call stack. Thus, the event dispatch thread couldn't do anything until the call to CChange.changeColor() returned, which required the user to complete typing the color name and press enter. In other words, the GUI was locked up until we finished!

This is a huge issue in programming GUIs with Java's Swing API: you can't execute any method on the event dispatch thread that doesn't return really quickly. Otherwise you'll lock up the GUI. So what do you do if a GUI event like a mouse click is supposed to initiate some long-running function call? Well, you create a new Thread for it to run in!

Part 2: Fixing the Mystery Button

Your job is to fix the Mystery Button so that the GUI doesn't lock up while the user dithers over which color to enter. Concretely, this means that when you press the Mystery Button, the toggle button and the window's "x" for closing still work, even if the user hasn't yet typed a color and pressed enter. That means, spawning a new thread for the CChange.changeColor() to execute in.

Demo this to your instructor!

Note: If your feeling a bit ambitious, you might want to look into the issue of what happens if you go crazy and double-click or triple-click the Mystery Button. It doesn't quite work the way you'd like. If you want to fix that, I suggest you look at Thread's isAlive() method. With a little cleverness, you can make it so that after a click of the Mystery Button, all subsequent clicks are ignored until a color is entered in response to the first one.

Part 3: Making a timer

Now let's take what we've learned and produce a simple, and yet useful tool: a GUI timer. The program should be called L11Timer, i.e. you should run it like this:
java L11Timer
The timer looks like this when it is launched

but if you click it starts counting down in second incerements, replacing the word DONE with the number of seconds remaining.

When it gets down to zero, the word DONE appears again. The user can type a number into the text-field to set the duration of the timer. When the program is first launched, the value in the text-field should be 10.
Note: if, when the button is pressed, what's in the text field is not a valid positive integer, the label should be changed to ERROR.

Here are a few useful tidbits for you:

If you package your timer into a new class that you derive from JPanel, it's trivial to create a window with multiple independent timers, like this:

  1. The call Thread.sleep(1000); will put the thread in which it is executed to sleep for one second. Look at the sleep() API documentation carefully ... there might be exceptions!
  2. If you keep changing the text of the JLabel, its width changes, and and that can be a bit disconcerting as the timer counts down. You can set the JLabel's "preferred size" like this:
    JLabel lab = new JLabel("foo");
    lab.setPreferredSize(new Dimension(60,15));
  3. It's also nice to be able to control the size of the JTextField. In this case, the constructor just takes an int argument that is the nubmer of characters wide you'd like the text field to be, e.g.
    JTextField tf = new JTextField(10);
    Note too that you can call setText on a JTextField object to set the text that appears, even though the user might go change it later.

Demo this to your instructor!