In the previous sections, we've worked with many different user
interface objects, and made a lot of new classes that are sort of like
components. Our new classes do one particular thing well; a number of
them can be added to applets or other containers just like the
standard AWT components; and several of them are lightweight
components that use system resources efficiently because they don't
rely on a peer.

But these new classes still aren't really components.
If you think about it, all our classes have
been fairly self-contained; they know everything about what to
do, and don't rely on other parts of the program to do much
processing. Therefore, they are overly specialized. Our menu example
created a DinnerFrame class that had a
menu of dinner options, but it included all the processing needed to
handle the user's selections. If we wanted to process the selections
differently, we'd have to modify the class. That's not what we want;
we'd like to separate registering the user's choices from processing
those choices. In contrast, true components don't do any processing.
They let the user take some action, and then inform some other part of
the program, which processes the action.

So we need a way for our new classes to communicate with other parts
of the program. Since we want our new classes to be components, they
should communicate the way components communicate: that is, by
generating
events and sending those events to listeners. So far, we've written a
lot of code that listened for events, but haven't seen any examples
that generated events.

Generating events sounds like it ought to be difficult, but it
isn't. You can either create new kinds of events, by subclassing
AWTEvent, or use one of the standard event
types. In either case, you need to register listeners for your events,
and provide a means to deliver events to your listeners. If you are
using the standard events, AWT provides an
AWTEventMulticaster class that handles
most of the machinery. We'll focus on that option in this section; at
the end, we'll make some comments on how you might manage events on
your own.

The AWTEventMulticaster is one of those
things that looks a lot more complicated than it is. It is confusing,
but most of the confusion occurs because it's hard to believe it's so
simple. Its job is to maintain a linked list of event listeners and
to propagate events to all the listeners on that linked list. So we can use
a multicaster to register (and unregister) event listeners and to
send any events we generate to all registered listeners.

The best way to show you how to use the multicaster is through an
example. The following example creates a new component called
PictureButton.
PictureButton
looks at least somewhat button-like and responds to mouse clicks
(MOUSE_RELEASED events) by generating
action events. (Figure 11.10 shows a
PictureButton in both depressed and raised
modes.) The PictureButtonApplet is passed
the events in
its actionPerformed() method, just as with
any other button, and prints a message each time it's pressed.

Before diving into the event multicaster, here are a few notes about
the applet and the PictureButton. The
applet is an ActionListener because it is
looking for events coming from the button. Therefore it registers
itself as a listener and contains an
actionPerformed() method. The
PictureButton doesn't have a label, so the
applet explicitly sets the button's action command by calling
setActionCommand().

The button itself is concerned mostly with being pretty. It uses a
media tracker to make sure that the image has loaded before displaying
itself. The paint() method, which we won't
discuss in detail, is devoted to making the button appear "pressed"
(i.e., recessed) when the mouse is pressed. The
getPreferredSize() method lets layout
managers size the button appropriately.

Now we'll start with the button's machinery. The button needs to
receive mouse events. It could register as a mouse listener, but in
this case, it seems more appropriate to override
processEvent().
processEvent() receives all incoming
events. It first checks whether we have a
MOUSE_PRESSED event; if so, it tells the
button to repaint itself in its "pressed" mode. If the event is a
MOUSE_RELEASED event, it tells the button
to paint itself in its "unpressed" mode and calls the private
fireEvent() method, which sends an action
event to all listeners. Finally,
processEvent() calls
super.processEvent() to make sure normal
event processing occurs; this is a good practice whenever you override
a method that performs a significant task.

However, processEvent() doesn't receive
events if they aren't generated; and normally, events aren't generated
if there are no listeners. Therefore, the button's constructor calls
enableEvents() with the argument
MOUSE_EVENT_MASK to turn on mouse event
processing.

Now we're ready to talk about how to generate events. The picture
button has addActionListener() and
removeActionListener() methods, for
registering listeners. These just call the static methods
add() and
remove() of the
AWTEventMulticaster class. Here's the
addActionListener() method:

If you look back to see how the instance variable
actionListener is declared, you'll see
that it is an ActionListener. No big
surprise--except that this code doesn't appear to make sense. It's
saying "add an action listener to an action listener and store the
result back in the original action listener."

There are a couple of tricks here. First, an
AWTEventMulticaster implements all of the
listener interfaces. Therefore, a multicaster can appear wherever an
ActionListener (or any other listener) is
required. In this case, the actionListener
object will be a multicaster--perhaps not what you expected, and
certainly not what's being passed in the argument
l. Now the code is starting to make sense:
earlier, I said that multicasters maintained linked lists of
listeners. So this method really adds l to
the linked list of action listeners that a multicaster is managing,
and saved the new list.

But that begs the question: where does the multicaster come from?
The linked list
has to start somewhere. This is where the second trick comes in.
add() is a static method, so we don't need
a multicaster to call it. But we still need some way to start the
linked list. The class's constructor is never
called--in fact, it's protected, so you can't call it. The answer lies
in the
add() method, which creates an
AWTEventMulticaster when you need it--that
is, as soon as you add the second listener to the list. The arguments
to add() may be
null; one of them probably is
null when you register your first action
listener.

Removing action listeners works the same way. We use the
AWTEventMulticaster's
remove() method. After the last listener
is taken off the linked list, remove()
returns null.

With this machinery in place, sending an event to all registered listeners is
very simple. You just create an event by calling its constructor, and
then call the appropriate method in the listener interface to deliver
it. The AWTEventMulticaster makes sure
that the event gets to all the listeners. In this example, we create
an ActionEvent and deliver the event to
the listeners' actionPerformed() methods by
calling
actionListener.actionPerformed(event).

The code to generate other kinds of events is almost exactly the same.
Remember the multicaster implements all the listener interfaces
and has overloaded add() and
remove() methods for every standard
listener type. Therefore, it can be used for any kind of
AWTEvent. It shouldn't be hard to adapt
this example to other situations.

What if you want to generate your own event type by subclassing
AWTEvent? To make things concrete, let's
say you want to create an ExplosionEvent
that you generate whenever your monitor catches fire. In this case,
you should define
your own ExplosionListener interface, and
(possibly) your own ExplosionAdapter class.
You can't use the
AWTEventMulticaster unless your new event
is a subclass of a standard event; extending the multicaster to
support new event types probably isn't worth the effort. It's easier
to write an addExplosionListener() method
that maintains a Vector of listeners and
to deliver events by calling the appropriate method of each listener
in the Vector. We'll demonstrate this
approach in the next section, where we implement another new
component: a Dial.

Things to mention in widgets Dial event example:
synchronization issues in add/remove/fire.
You should sync add/remove... but be wary of syncing fire,
deadlocks

The standard AWT classes don't have a component that's similar to an
old fashioned dial--for example, the volume control on your radio.
Such a component is something of a rarity; I don't remember seeing one
in any application I've used. But there's no reason we can't build
one. In this section, we implement a Dial
class. We also define a new event type,
DialEvent, and a new listener interface,
DialListener. The dial can be used just
like any other Java component. It is built entirely in Java and,
therefore, is a lightweight component; it extends
Component directly and doesn't
have a peer.

By defining a new event type, I'm stretching the point slightly.
There's no reason our dial couldn't use the standard
AdjustmentEvent. However, this gives us a
chance to show how to handle event listeners without using the event
multicaster. There are many situations in which defining a new event type
will be the only appropriate solution.

Figure 11.11 shows what the dial looks like; it is
followed by the code.

Let's start from the top. We'll focus on the event handling and leave
you to figure out the trigonometry on your own. The
DialListener
interface contains a single method,
dialAdjusted(), which is called when a
DialEvent occurs. The
DialEvent itself is simple. It defines a
new event ID, DIAL_ADJUSTED, that
identifies dial events. This constant is defined so that it won't
conflict with the ID numbers reserved for standard AWT events. The
event itself only carries one item of data: the dial's new value. It
has a single method that returns this value.

The constructor for the Dial class stores
the dial's maximum value; its minimum value is 0. It then enables
mouse motion events, which the Dial needs
to tell how it is being manipulated.

paint(),
draw3DCircle(), and
processEvent() do a lot of trigonometry to
figure out how to display the dial.
draw3DCircle() is a private helper method
that draws a circle that appears either raised or depressed; we use
this to make the dial look three dimensional.
processEvent() is called whenever any
event occurs within the component's area. We only expect to receive
mouse motion events, because these are the only events we enabled.
processEvent() first checks the event's
ID; if it is MOUSE_DRAGGED, the user has
changed the dial's setting. We respond by computing
a new value for the dial, repainting the dial in its new position and
firing a
DialEvent. Any other events (in
particular, MOUSE_MOVED) are ignored.
However, we call the superclass's
processEvent() method to make sure that
any other processing needed for this event occurs.

The next group of methods provide ways to retrieve or change the
dial's current setting, minimum, and maximum values. They are similar
to the methods in the Adjustable
interface; you could argue that Dial
really ought to implement Adjustable.

Finally, we reach the methods that work with listeners.
addDialListener() adds a new listener to a
Vector of listeners by calling
addElement(). If the vector doesn't
already exist, addDialListener() creates
it. removeDialListener() simply takes a
listener off the list, so that it won't receive any further events.
fireEvent() is a private method that
creates a DialEvent and sends it to every
listener. It does so by converting the
Vector into an
Enumeration and running through every
element in the list by calling
nextElement() until
hasMoreElements() returns false. To send
the event to a listener, it calls the listener's
dialAdjusted() method. Note that
nextElement() returns an
Object; we must cast this object to
DialListener before we can deliver the
event.

To show how the applet is used, I included a simple applet called
DialApplet. This applet places a
Dial and a
Scrollbar in a border layout. Any change
to either the dial or the scrollbar is reflected by the other. The
applet implements both DialListener and
AdjustmentListener, and therefore has both
dialAdjusted() and
adjustmentValueChanged() methods. Although
this isn't necessarily a good argument for creating a new event type,
it's worth noticing that the logic of the listener methods is much
simpler than it would have been if the dial generated adjustment
events.