11.4 Event Handling
In the previous section on
layout management, there were a number of examples that arranged
JButton components in interesting ways. If you ran
the examples, however, you probably noticed that nothing interesting
happened when you clicked on the buttons. The fourth step in creating
a GUI is hooking up the event handling that makes components respond
to user input. As of Java 1.1 and later, AWT and Swing components use
the event-handling API defined by the JavaBeans component model.
Prior to Java 1.1, the AWT used a different API that is not covered
in this chapter. We'll see some examples of event
handling using the old model when we study applets (see Chapter 16), where this model is still sometimes used for
backward compatibility with old web browsers.In Java 1.1 and later, the
event-handling API is based on events and event listeners. Like
everything else in Java, events are objects. An event object is an
instance of a class that extends
java.util.EventObject. The
java.awt.event package defines a number of event
classes commonly used by AWT and Swing components. The
javax.swing.event package defines additional
events used by Swing components, but not by AWT components. And the
java.beans package defines a couple of JavaBeans
event classes also used by Swing components. Event classes usually
define methods (or fields) that provide details about the event that
occurred. For example, the
java.awt.event.MouseEvent class defines a
getX( ) method that returns the X coordinate of
the location of the mouse when the event occurred.An EventListener is an
object that is interested in being notified when an event of a
particular type occurs. An object that generates events (an
event source, such as a
JButton component) maintains a list of listeners
and provides methods that allow listeners to be added to or removed
from this list. When an event of the appropriate type occurs, the
event source notifies all registered event listeners. To notify the
listeners, it first creates an event object that describes the event
and then passes that event object to a method defined by the event
listeners. The particular method that is invoked depends on the type
of event; different event listener types define different methods.All event listener types are interfaces that extend the
java.util.EventListener interface. This is a
marker interface; it does not define any methods of its own, but
exists so that you can use the instanceof operator
to distinguish event listeners from other object types. The
java.awt.event,
javax.swing.event, and
java.beans packages define a number of event
listener interfaces that extend the generic
EventListener interface. Each listener is specific
to a particular type of event and defines one or more methods that
are invoked in particular circumstances (e.g.,
java.awt.MouseListener). The only special feature
of an event listener method is that it always takes an event object
as its single argument.Note that the
java.awt.event and
javax.swing.event packages define only event
listener interfaces, not event listener
classes. A GUI must define its own custom
implementation of an event listener, as it is this implementation
that provides the actual Java code executed in response to an event.
These packages do define some Java classes, however: event adapter
classes. An event adapter is an implementation
of an event listener interface that consists entirely of empty
methods. Many listener interfaces define multiple methods, but often
you are interested in using only one of these methods at a time. In
this case, it is easier to subclass an adapter, overriding only the
method you are interested in, instead of implementing the interface
directly and having to define all its methods.Every AWT and Swing
application has an automatically created thread, called the
event dispatch thread, that invokes the methods
of event listeners. When an application starts, the main thread
builds the GUI, and then it often exits. From then on, everything
that happens takes place in the event dispatch thread, in response to
the invocation of event listener methods. Note that AWT and Swing
components are not thread-safe, for performance reasons. Thus, if you
are writing a multithreaded program, you must be careful to call
component methods only from the event dispatch thread. Calling
component methods from other threads may cause intermittent and
difficult-to-diagnose bugs. If you need to perform some kind of
animation or repetitive task with Swing, use a
javax.swing.Timer instead of using a separate
thread. If another thread really needs to interact with a Swing
component, use the java.awt.EventQueue
invokeLater( ) and invokeAndWait(
) methods.Chapter 2 of Java Foundation Classes in
a Nutshell describes the event-handling API in more detail
and also contains tables that list the various types of AWT and Swing
event listeners, their methods, and the components that use those
listener interfaces. The following sections contain some simple
examples that illustrate different ways of using the Java
event-handling API, as well as a more robust example that
demonstrates an API for handling certain types of input events using
a lower-level API.
11.4.1 Handling Mouse Events
Example 11-10 is a
listing of ScribblePane1.java, a simple
JPanel subclass that implements the
MouseListener and a
MouseMotionListener interfaces in order to receive
mouse-click and mouse-drag events. It responds to these events by
drawing lines, allowing the user to
"scribble" in the pane. Figure 11-10 shows the ScribblePane1
example (and a sample scribble) running within the
ShowBean program developed in this chapter.
Figure 11-10. A scribble in ScribblePane1

Note that the
ScribblePane1 class is both the source of mouse
events (they are generated by the
java.awt.Component superclass) and the event
listener. This can be seen most clearly in the constructor method
where the component passes itself to its own
addMouseListener( ) and
addMouseMotionListener( ) methods. The
mouseDragged( ) method is the key to scribbling:
it uses the drawLine( ) method of the
Graphics object to draw a line from the previous
mouse position to the current mouse position.ScribblePane1 does not save the coordinates of the
mouse events, and therefore cannot redraw the scribble (after it is
obscured by one of the ShowPane menus, for
example). ScribblePane1 demonstrates event
handling, but it is not a good example of a custom component because
it does not implement a paintComponent( ) method
to redraw the component as needed. Examples Example 11-11 and Example 11-12 also contain
this flaw, which is finally remedied in Example 11-13.
Example 11-10. ScribblePane1.java
package je3.gui;
import javax.swing.*; // For JPanel component
import java.awt.*; // For Graphics object
import java.awt.event.*; // For Event and Listener objects
/**
* A simple JPanel subclass that uses event listeners to allow the user
* to scribble with the mouse.
Note that scribbles are not saved or redrawn.
**/
public class ScribblePane1 extends JPanel
implements MouseListener, MouseMotionListener {
protected int last_x, last_y; // Previous mouse coordinates
public ScribblePane1( ) {
// This component registers itself as an event listener for
// mouse events and mouse motion events.
this.addMouseListener(this);
this.addMouseMotionListener(this);
// Give the component a preferred size
setPreferredSize(new Dimension(450,200));
}
// A method from the MouseListener interface. Invoked when the
// user presses a mouse button.
public void mousePressed(MouseEvent e) {
last_x = e.getX( ); // remember the coordinates of the click
last_y = e.getY( );
}
// A method from the MouseMotionListener interface. Invoked when the
// user drags the mouse with a button pressed.
public void mouseDragged(MouseEvent e) {
int x = e.getX( ); // Get the current mouse position
int y = e.getY( );
// Draw a line from the saved coordinates to the current position
this.getGraphics( ).drawLine(last_x, last_y, x, y);
last_x = x; // Remember the current position
last_y = y;
}
// The other, unused methods of the MouseListener interface.
public void mouseReleased(MouseEvent e) { }
public void mouseClicked(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
// The other, unused, method of the MouseMotionListener interface.
public void mouseMoved(MouseEvent e) { }
}
11.4.2 More Mouse Events
Example 11-11 shows a
listing of ScribblePane2.java, which is much
like ScribblePane1 except that it uses anonymous
inner classes to define its event listeners. This is a common GUI
programming idiom in Java, and, in fact, it was one of the motivating
factors for adding anonymous inner classes to the language. Note that
the inner classes subclass event adapter classes, rather than
implement the event listeners directly; this means that unused
methods don't have to be implemented.ScribblePane2 also
includes a KeyListener that clears the scribble
when the user types the C key, and defines a color
property (with setColor( ) and getColor(
) as its property accessor methods) that specifies the
color in which to scribble. Recall that the
ShowBean program allows you to specify property
values. It understands colors specified using hexadecimal RGB
notation, so to use this example to scribble with blue lines, you can
use a command like this:
% java je3.gui.ShowBean je3.gui.ScribblePane2 color=#0000ff
A final point to note about this example is that the scribbling
functionality has been cleaned up and placed into moveto(
), lineto( ), and clear(
) methods. This allows the methods to be invoked by other
components and allows the component to be subclassed more cleanly.
You can invoke the clear( ) method from the
Commands menu of
ShowBean.
Example 11-11. ScribblePane2.java
package je3.gui;
import javax.swing.*; // For JPanel component
import java.awt.*; // For Graphics object
import java.awt.event.*; // For Event and Listener objects
/**
* A simple JPanel subclass that uses event listeners to allow the user
* to scribble with the mouse.
Note that scribbles are not saved or redrawn.
**/
public class ScribblePane2 extends JPanel {
public ScribblePane2( ) {
// Give the component a preferred size
setPreferredSize(new Dimension(450,200));
// Register a mouse event handler defined as an inner class
// Note the call to requestFocus( ). This is required in order for
// the component to receive key events.
addMouseListener(new MouseAdapter( ) {
public void mousePressed(MouseEvent e) {
moveto(e.getX( ), e.getY( )); // Move to click position
requestFocus( ); // Take keyboard focus
}
});
// Register a mouse motion event handler defined as an inner class.
// By subclassing MouseMotionAdapter rather than implementing
// MouseMotionListener, we only override the method we're interested
// in and inherit default (empty) implementations of the other methods.
addMouseMotionListener(new MouseMotionAdapter( ) {
public void mouseDragged(MouseEvent e) {
lineto(e.getX( ), e.getY( )); // Draw to mouse position
}
});
// Add a keyboard event handler to clear the screen on key 'C'
addKeyListener(new KeyAdapter( ) {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode( ) == KeyEvent.VK_C) clear( );
}
});
}
/** These are the coordinates of the the previous mouse position */
protected int last_x, last_y;
/** Remember the specified point */
public void moveto(int x, int y) {
last_x = x;
last_y = y;
}
/** Draw from the last point to this point, then remember new point */
public void lineto(int x, int y) {
Graphics g = getGraphics( ); // Get the object to draw with
g.setColor(color); // Tell it what color to use
g.drawLine(last_x, last_y, x, y); // Tell it what to draw
moveto(x, y); // Save the current point
}
/**
* Clear the drawing area, using the component background color. This
* method works by requesting that the component be redrawn. Since this
* component does not have a paintComponent( ) method, nothing will be
* drawn. However, other parts of the component, such as borders or
* subcomponents, will be drawn correctly.
**/
public void clear( ) { repaint( ); }
/** This field holds the current drawing color property */
Color color = Color.black;
/** This is the property "setter" method for the color property */
public void setColor(Color color) { this.color = color; }
/** This is the property "getter" method for the color property */
public Color getColor( ) { return color; }
}
11.4.3 Handling Component Events
The
two previous examples have shown how to handle mouse and keyboard
events. These are low-level input events generated by the system and
reported to event listeners by code in the
java.awt.Component class. Usually, when you are
building a GUI, you do not handle these low-level events yourself;
instead, you use predefined components to interpret the raw input
events and generate higher-level semantic events. For example, when
the JButton component detects a low-level mouse
click and mouse release, it generates a higher-level
java.awt.event.ActionEvent to notify any
interested listeners that the user clicked on the button to activate
it. Similarly, the JList component generates a
javax.swing.event.ListSelectionEvent when the user
makes a selection from the list.Example 11-12 is a listing of
ScribblePane3.java. This example extends
ScribblePane2 and adds a
JButton and a JList to its user
interface. The user can clear the screen by clicking on the button
and change the drawing color by selecting from the list. You can see
these new components in Figure 11-11 (which also
demonstrates that the lack of a proper paintComponent(
) method allows ScribblePane3 to
scribble on top of its children). The example demonstrates
implementing the ActionListener and
ListSelectionListener interfaces to respond to the
high-level events generated by these Swing components.
Figure 11-11. Swing components in ScribblePane3

Example 11-12. ScribblePane3.java
package je3.gui;
import java.awt.*; // For Graphics object and colors
import javax.swing.*; // For JPanel component
import java.awt.event.*; // For ActionListener interface
import javax.swing.event.*; // For ListSelectionListener interface
/**
* This scribble component includes a JButton to clear the screen, and
* a JList that lets the user select a drawing color. It uses
* event listener objects to handle events from those subcomponents.
**/
public class ScribblePane3 extends ScribblePane2 {
// These are colors the user can choose from
Color[ ] colors = new Color[ ] { Color.black, Color.red, Color.blue };
// These are names for those colors
String[ ] colorNames = new String[ ] { "Black", "Red", "Blue" };
// Add JButton and JList components to the panel.
public ScribblePane3( ) {
// Implicit super( ) call here invokes the superclass constructor
// Add a "Clear" button to the panel.
// Handle button events with an action listener
JButton clear = new JButton("Clear");
clear.addActionListener(new ActionListener( ) {
public void actionPerformed(ActionEvent e) { clear( ); }
});
this.add(clear);
// Add a JList to allow color choices.
// Handle list selection events with a ListSelectionListener.
final JList colorList = new JList(colorNames);
colorList.addListSelectionListener(new ListSelectionListener( ) {
public void valueChanged(ListSelectionEvent e) {
setColor(colors[colorList.getSelectedIndex( )]);
}
});
this.add(colorList);
}
}
11.4.4 Low-Level Event Handling
As we just discussed, graphical
user interfaces do not usually concern themselves with the details of
low-level mouse and keyboard events. Instead, they use predefined
components to interpret these events for them. By the same token,
predefined components that handle frequent mouse and keyboard events
do not usually use the high-level event listener API to handle these
low-level events.Example 11-13 shows a
listing of ScribblePane.java, a final
reimplementation of our scribble component. This version does not use
event listeners at all, but instead overrides the
processMouseEvent( ), and
processMouseMotionEvent( ), methods defined by its
java.awt.Component superclass.[3] Event objects are
passed to these methods directly, without any requirement to register
event listeners. What is required, however, is that the constructor
call enableEvents( ) to specify the kinds of
events in which it is interested. If the constructor does not do
this, the system will not deliver events of those types, and the
various event processing methods will never be invoked. Note that the
event processing methods invoke the superclass'
implementation for any events they do not handle themselves. This
allows the superclass to dispatch these events to any listener
objects that may have been registered.
[3] This same technique can be used with the
processKeyEvent( ), processFocusEvent(
), processComponentEvent( ), and
processWindowEvent methods of
Component.
This ScribblePane class also fixes the problem
that afflicted the previous three versions: it remembers the
user's scribble and implements a
paintComponent( ) method to redraw that scribble
when necessary. (An application never needs to invoke
paintComponent( ): the AWT and Swing
infrastructure invoke it as appropriate.) Another enhancement is that
ScribblePane uses the Java2D API of Java 1.2 and
later to draw wide lines. The lines are drawn using the color
specified on the foreground property inherited
from JComponent, and you can use
ShowBean to experiment with this property.
Example 11-13. ScribblePane.java
package je3.gui;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.List; // Disambiguate from java.awt.List
import java.util.ArrayList;
import je3.graphics.PolyLine;
/**
* This custom component allows the user to scribble,
and retains the scribbles
* so that they can be redrawn when needed.
It uses the PolyLine custom Shape
* implementation defined elsewhere in this book,
and demonstrates event
* handling with low-level event processing methods.
*/
public class ScribblePane extends JComponent {
List lines; // The PolyLines that comprise this scribble
PolyLine currentLine; // The PolyLine currently being drawn
Stroke stroke; // How to draw the lines
public ScribblePane( ) {
setPreferredSize(new Dimension(450,200)); // We need a default size
lines = new ArrayList( ); // Initialize a list of lines
stroke = new BasicStroke(3.0f); // Lines are 3 pixels wide
// Register interest in mouse button and mouse motion events, so
// that processMouseEvent( ) and processMouseMotionEvent( ) will be
// invoked, even if no event listeners are registered.
enableEvents(AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
/** We override this method to draw ourselves. */
public void paintComponent(Graphics g) {
// Let the superclass do its painting first
super.paintComponent(g);
// Make a copy of the Graphics context so we can modify it
// We cast it at the same time so we can use Java2D graphics
Graphics2D g2 = (Graphics2D) (g.create( ));
// Our superclass doesn't paint the background, so do this ourselves.
g2.setColor(getBackground( ));
g2.fillRect(0, 0, getWidth( ), getHeight( ));
// Set the line width and color to use for the foreground
g2.setStroke(stroke);
g2.setColor(this.getForeground( ));
// Now loop through the PolyLine shapes and draw them all
int numlines = lines.size( );
for(int i = 0; i < numlines; i++)
g2.draw((PolyLine)lines.get(i));
}
/**
* Erase all lines and repaint. This method is for the convenience of
* programs that use this component.
*/
public void clear( ) {
lines.clear( );
repaint( );
}
/**
* We override this method to receive notification
of mouse button events.
* See also the enableEvents( ) call in the constructor method.
*/
public void processMouseEvent(MouseEvent e) {
// If the type and button are correct, then process it.
if (e.getButton( ) == MouseEvent.BUTTON1) {
if (e.getID( ) == MouseEvent.MOUSE_PRESSED) {
// Start a new line on mouse down
currentLine = new PolyLine(e.getX( ), e.getY( ));
lines.add(currentLine);
e.consume( );
}
else if (e.getID( ) == MouseEvent.MOUSE_RELEASED) {
// End the line on mouse up
currentLine = null;
e.consume( );
}
}
// The superclass method dispatches to registered event listeners
super.processMouseEvent(e);
}
/**
* We override this method to receive notification of mouse motion events.
*/
public void processMouseMotionEvent(MouseEvent e) {
if (e.getID( ) == MouseEvent.MOUSE_DRAGGED && // If we're dragging
currentLine != null) { // and a line exists
currentLine.addSegment(e.getX( ), e.getY( )); // Add a line segment
e.consume( );
// Redraw the whole component.
// Exercise: optimize this by passing the bounding box
// of the region that needs redrawing to the repaint( ) method.
// Don't forget to take line width into account, however.
repaint( );
}
super.processMouseMotionEvent(e);
}
}
11.4.5 Custom Events and Event Listeners
Although Swing and the AWT define
quite a few event classes and event listener interfaces, there is no
reason you cannot define custom event and listener types of your own.
The class shown in Example 11-14 does exactly that: it
defines its own custom event and listener types using inner classes.The Swing component set provides a number of ways to allow the user
to select an item from a list of items. You can present such a choice
with a JList component, a
JComboBox component, or a group of cooperating
JRadioButton components. The APIs for creating,
manipulating, and responding to events with these components differ
substantially. Example 11-14 is a listing of an
ItemChooser class that abstracts away the
differences between these three presentation types. When you create
an ItemChooser component, you specify the name of
the choice being presented, a list of items to be chosen among, the
currently chosen item, and a presentation type. The presentation type
determines how the choice is presented to the user, but the API you
use to work with the ItemChooser component is
independent of the presentation.The ItemChooser class includes an inner class
named Demo. ItemChooser.Demo
has a main( ) method that demonstrates the
ItemChooser component, as shown in Figure 11-12. The demo program gets the choice labels from
command-line arguments, so you can run it with a command like the
following:
% java je3.gui.ItemChooser\$Demo Fourscore and twenty years ago
Note that on Unix systems, you have to escape the
$ in the inner class name with a backslash. On
Windows systems, the backslash is not necessary.
We'll see another use of
ItemChooser in Example 11-17.
Figure 11-12. A demonstration of the ItemChooser component

The interesting thing about ItemChooser is that it
defines its own event and event listener types as inner classes. You
should pay attention to the definitions of these types and study how
they are used within the ItemChooser
Demo classes, as this example demonstrates both
sides of the event architecture: event generation and event handling.
This example shows you how to work with JList,
JComboBox, and JRadioButton
components; it is particularly interesting because it listens for and
responds to the events generated by those internal components and
translates those internal events into its own event type. Once you
understand how ItemChooser works,
you'll have a thorough understanding of the AWT and
Swing event architecture.
Example 11-14. ItemChooser.java
package je3.gui;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.util.*;
/**
* This class is a Swing component that presents a choice to the user. It
* allows the choice to be presented in a JList, in a JComboBox, or with a
* bordered group of JRadioButton components. Additionally, it displays the
* name of the choice with a JLabel. It allows an arbitrary value to be
* associated with each possible choice. Note that this component only allows
* one item to be selected at a time. Multiple selections are not supported.
**/
public class ItemChooser extends JPanel {
// These fields hold property values for this component
String name; // The overall name of the choice
String[ ] labels; // The text for each choice option
Object[ ] values; // Arbitrary values associated with each option
int selection; // The selected choice
int presentation; // How the choice is presented
// These are the legal values for the presentation field
public static final int LIST = 1;
public static final int COMBOBOX = 2;
public static final int RADIOBUTTONS = 3;
// These components are used for each of the 3 possible presentations
JList list; // One type of presentation
JComboBox combobox; // Another type of presentation
JRadioButton[ ] radiobuttons; // Yet another type
// The list of objects that are interested in our state
ArrayList listeners = new ArrayList( );
// The constructor method sets everything up
public ItemChooser(String name, String[ ] labels, Object[ ] values,
int defaultSelection, int presentation)
{
// Copy the constructor arguments to instance fields
this.name = name;
this.labels = labels;
this.values = values;
this.selection = defaultSelection;
this.presentation = presentation;
// If no values were supplied, use the labels
if (values == null) this.values = labels;
// Now create content and event handlers based on presentation type
switch(presentation) {
case LIST: initList( ); break;
case COMBOBOX: initComboBox( ); break;
case RADIOBUTTONS: initRadioButtons( ); break;
}
}
// Initialization for JList presentation
void initList( ) {
list = new JList(labels); // Create the list
list.setSelectedIndex(selection); // Set initial state
// Handle state changes
list.addListSelectionListener(new ListSelectionListener( ) {
public void valueChanged(ListSelectionEvent e) {
ItemChooser.this.select(list.getSelectedIndex( ));
}
});
// Lay out list and name label vertically
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); // vertical
this.add(new JLabel(name)); // Display choice name
this.add(new JScrollPane(list)); // Add the JList
}
// Initialization for JComboBox presentation
void initComboBox( ) {
combobox = new JComboBox(labels); // Create the combo box
combobox.setSelectedIndex(selection); // Set initial state
// Handle changes to the state
combobox.addItemListener(new ItemListener( ) {
public void itemStateChanged(ItemEvent e) {
ItemChooser.this.select(combobox.getSelectedIndex( ));
}
});
// Lay out combo box and name label horizontally
this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
this.add(new JLabel(name));
this.add(combobox);
}
// Initialization for JRadioButton presentation
void initRadioButtons( ) {
// Create an array of mutually exclusive radio buttons
radiobuttons = new JRadioButton[labels.length]; // the array
ButtonGroup radioButtonGroup = new ButtonGroup( ); // used for exclusion
ChangeListener listener = new ChangeListener( ) { // A shared listener
public void stateChanged(ChangeEvent e) {
JRadioButton b = (JRadioButton)e.getSource( );
if (b.isSelected( )) {
// If we received this event because a button was
// selected, then loop through the list of buttons to
// figure out the index of the selected one.
for(int i = 0; i < radiobuttons.length; i++) {
if (radiobuttons[i] == b) {
ItemChooser.this.select(i);
return;
}
}
}
}
};
// Display the choice name in a border around the buttons
this.setBorder(new TitledBorder(new EtchedBorder( ), name));
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
// Create the buttons, add them to the button group, and specify
// the event listener for each one.
for(int i = 0; i < labels.length; i++) {
radiobuttons[i] = new JRadioButton(labels[i]);
if (i == selection) radiobuttons[i].setSelected(true);
radiobuttons[i].addChangeListener(listener);
radioButtonGroup.add(radiobuttons[i]);
this.add(radiobuttons[i]);
}
}
// These simple property accessor methods just return field values
// These are read-only properties. The values are set by the constructor
// and may not be changed.
public String getName( ) { return name; }
public int getPresentation( ) { return presentation; }
public String[ ] getLabels( ) { return labels; }
public Object[ ] getValues( ) { return values; }
/** Return the index of the selected item */
public int getSelectedIndex( ) { return selection; }
/** Return the object associated with the selected item */
public Object getSelectedValue( ) { return values[selection]; }
/**
* Set the selected item by specifying its index. Calling this
* method changes the on-screen display but does not generate events.
**/
public void setSelectedIndex(int selection) {
switch(presentation) {
case LIST: list.setSelectedIndex(selection); break;
case COMBOBOX: combobox.setSelectedIndex(selection); break;
case RADIOBUTTONS: radiobuttons[selection].setSelected(true); break;
}
this.selection = selection;
}
/**
* This internal method is called when the selection changes. It stores
* the new selected index, and fires events to any registered listeners.
* The event listeners registered on the JList, JComboBox, or JRadioButtons
* all call this method.
**/
protected void select(int selection) {
this.selection = selection; // Store the new selected index
if (!listeners.isEmpty( )) { // If there are any listeners registered
// Create an event object to describe the selection
ItemChooser.Event e =
new ItemChooser.Event(this, selection, values[selection]);
// Loop through the listeners using an Iterator
for(Iterator i = listeners.iterator( ); i.hasNext( );) {
ItemChooser.Listener l = (ItemChooser.Listener)i.next( );
l.itemChosen(e); // Notify each listener of the selection
}
}
}
// These methods are for event listener registration and deregistration
public void addItemChooserListener(ItemChooser.Listener l) {
listeners.add(l);
}
public void removeItemChooserListener(ItemChooser.Listener l) {
listeners.remove(l);
}
/**
* This inner class defines the event type generated by ItemChooser objects
* The inner class name is Event, so the full name is ItemChooser.Event
**/
public static class Event extends java.util.EventObject {
int selectedIndex; // index of the selected item
Object selectedValue; // the value associated with it
public Event(ItemChooser source,
int selectedIndex, Object selectedValue) {
super(source);
this.selectedIndex = selectedIndex;
this.selectedValue = selectedValue;
}
public ItemChooser getItemChooser( ) { return (ItemChooser)getSource( );}
public int getSelectedIndex( ) { return selectedIndex; }
public Object getSelectedValue( ) { return selectedValue; }
}
/**
* This inner interface must be implemented by any object that wants to be
* notified when the current selection in an ItemChooser component changes.
**/
public interface Listener extends java.util.EventListener {
public void itemChosen(ItemChooser.Event e);
}
/**
* This inner class is a simple demonstration of the ItemChooser component
* It uses command-line arguments as ItemChooser labels and values.
**/
public static class Demo {
public static void main(String[ ] args) {
// Create a window, arrange to handle close requests
final JFrame frame = new JFrame("ItemChooser Demo");
frame.addWindowListener(new WindowAdapter( ) {
public void windowClosing(WindowEvent e) {System.exit(0);}
});
// A "message line" to display results in
final JLabel msgline = new JLabel(" ");
// Create a panel holding three ItemChooser components
JPanel chooserPanel = new JPanel( );
final ItemChooser c1 = new ItemChooser("Choice #1", args, null, 0,
ItemChooser.LIST);
final ItemChooser c2 = new ItemChooser("Choice #2", args, null, 0,
ItemChooser.COMBOBOX);
final ItemChooser c3 = new ItemChooser("Choice #3", args, null, 0,
ItemChooser.RADIOBUTTONS);
// An event listener that displays changes on the message line
ItemChooser.Listener l = new ItemChooser.Listener( ) {
public void itemChosen(ItemChooser.Event e) {
msgline.setText(e.getItemChooser( ).getName( ) + ": " +
e.getSelectedIndex( ) + ": " +
e.getSelectedValue( ));
}
};
c1.addItemChooserListener(l);
c2.addItemChooserListener(l);
c3.addItemChooserListener(l);
// Instead of tracking every change with an ItemChooser.Listener,
// applications can also just query the current state when
// they need it. Here's a button that does that.
JButton report = new JButton("Report");
report.addActionListener(new ActionListener( ) {
public void actionPerformed(ActionEvent e) {
// Note the use of multiline italic HTML text
// with the JOptionPane message dialog box.
String msg = "<html><i>" +
c1.getName( ) + ": " + c1.getSelectedValue( ) + "<br>"+
c2.getName( ) + ": " + c2.getSelectedValue( ) + "<br>"+
c3.getName( ) + ": " + c3.getSelectedValue( ) + "</i>";
JOptionPane.showMessageDialog(frame, msg);
}
});
// Add the 3 ItemChooser objects, and the Button to the panel
chooserPanel.add(c1);
chooserPanel.add(c2);
chooserPanel.add(c3);
chooserPanel.add(report);
// Add the panel and the message line to the window
Container contentPane = frame.getContentPane( );
contentPane.add(chooserPanel, BorderLayout.CENTER);
contentPane.add(msgline, BorderLayout.SOUTH);
// Set the window size and pop it up.
frame.pack( );
frame.show( );
}
}
}