Last updated: February 3, 1997
Purpose
This document explains the rationale behind introducing a new event model into the AWT and describes specifically how the new model maps to the AWT API. This new model has also been adopted by the JavaBeans architecture for general event processing and is described at a high level in the JavaBeans Specification document.
The 1.0 Event Model
The model for event processing in version 1.0 of the AWT is based on inheritance. In order for a program to catch and process GUI events, it must subclass GUI components and override either action() or handleEvent() methods. Returning "true" from one of these methods consumes the event so it is not processed further; otherwise the event is propagated sequentially up the GUI hierarchy until either it is consumed or the root of the hierarchy is reached. The result of this model is that programs have essentially two choices for structuring their event-handling code:
Issues with the 1.0 Event Model
While the above model works fine for small applets with simple interfaces, it does not scale well for larger java programs for the following reasons:
Note: These goals are described from the particular perspective of the AWT. Since this model has also been designed to accommodate the JavaBeans architecture, the design goals from the JavaBeans perspective are described in the "Events" section of the JavaBeans Specification and may vary slightly from these goals.
Delegation Model Overview
Event types are encapsulated in a class hierarchy rooted at java.util.EventObject. An event is propagated from a "Source" object to a "Listener" object by invoking a method on the listener and passing in the instance of the event subclass which defines the event type generated.
A Listener is an object that implements a specific EventListener interface extended from the generic java.util.EventListener. An EventListener interface defines one or more methods which are to be invoked by the event source in response to each specific event type handled by the interface.
An Event Source is an object which originates or "fires" events. The source defines the set of events it emits by providing a set of set<EventType>Listener (for single-cast) and/or add<EventType>Listener (for mult-cast) methods which are used to register specific listeners for those events.
In an AWT program, the event source is typically a GUI component and the listener is commonly an "adapter" object which implements the appropriate listener (or set of listeners) in order for an application to control the flow/handling of events. The listener object could also be another AWT component which implements one or more listener interfaces for the purpose of hooking GUI objects up to each other.
Since a single event class may be used to represent more than one event type (i.e. MouseEvent represents mouse up, mouse down, mouse drag, mouse move, etc), some event classes may also contain an "id" (unique within that class) which maps to its specific event types.
The event classes contain no public fields; the data in the event is completely encapsulated by proper get<Attr>()/set<Attr>() methods (where set<Attr>() only exists for attributes on an event that could be modified by a listener).
Although these are the concrete set defined by the AWT, programs are free to define their own event types by subclassing either java.util.EventObject or one of the AWT event classes. Programs should choose event ID values which are greater than the constant:
java.awt.AWTEvent.RESERVED_ID_MAX
A low-level event is one which represents a low-level input or window-system occurrence on a visual component on the screen. The low-level event classes defined by the AWT are as follows:
java.util.EventObject
java.awt.AWTEvent
java.awt.event.ComponentEvent (component resized, moved, etc.)
java.awt.event.FocusEvent (component got focus, lost focus)
java.awt.event.InputEvent
java.awt.event.KeyEvent (component got key-press, key-release, etc.)
java.awt.event.MouseEvent (component got mouse-down, mouse-move, etc.)
java.awt.event.ContainerEvent
java.awt.event.WindowEvent
Semantic events are defined at a higher-level to encapsulate the semantics of a user interface component's model. The semantic event classes defined by the AWT are as follows:
java.util.EventObject
java.awt.AWTEvent
java.awt.event.ActionEvent ("do a command")
java.awt.event.AdjustmentEvent ("value was adjusted")
java.awt.event.ItemEvent ("item state has changed")
java.awt.event.TextEvent ("the value of the text object changed")
Note that these semantic events are not tied to specific screen-based component classes, but may apply across a set of components which implement a similar semantic model. For example, a Button object will fire an "action" event when it is pressed, a List object will fire an "action" event when an item is double-clicked, a MenuItem will fire an "action" event when it was selected from a menu, and a non-visual Timer object might fire an "action" when its timer goes off (the latter is a hypothetical case).
The API attempts to define a balance between providing a reasonable granularity of Listener interface types and not providing a separate interface for every single event type.
The low-level listener interfaces defined by the AWT are as follows:
java.util.EventListener
java.awt.event.ComponentListener
java.awt.event.ContainerListener
java.awt.event.FocusListener
java.awt.event.KeyListener
java.awt.event.MouseListener
java.awt.event.MouseMotionListener
java.awt.event.WindowListener
The semantic listener interfaces defined by the AWT are as follows:
java.util.EventListener
java.awt.event.ActionListener
java.awt.event.AdjustmentListener
java.awt.event.ItemListener
java.awt.event.TextListener
All AWT event sources support a multicast model for listeners. This means that multiple listeners can be added and removed from a single source. The API makes no guarantees about the order in which the events are delivered to a set of registered listeners for a given event on a given source. Additionally, any event which allows its properties to be modified (via setXXX() methods) will be explicitly copied such that each listener receives a replica of the original event. If the order in which events are delivered to listeners is a factor for your program, you should chain the listeners off a single listener which is registered on the source (the fact that the event data is encapsulated in a single object makes propagating the event extremely simple).
Event delivery is synchronous (as with 1.0's handleEvent()), however programs should not make the assumption that the delivery of an event to a set of listeners will occur on the same thread.
Once again, a distinction is drawn between low-level and semantic events. For low-level events, the source will be one of the visual component classes (Button, Scrollbar, etc) since the event is tightly bound to the actual component on the screen. The low-level listeners are defined on the following components:
addComponentListener(ComponentListener l)
addFocusListener(FocusListener l)
addKeyListener(KeyListener l)
addMouseListener(MouseListener l)
addMouseMotionListener(MouseMotionListener l)
addContainerListener(ContainerListener l)
addWindowListener(WindowListener l)
addWindowListener(WindowListener l)
For semantic events, the source is typically a higher-level interface representing the semantic model (and this higher-level interface is commonly `implemented' by components using the model). Following are the semantic listeners defined for AWT components:
addActionListener(ActionListener l)
addItemListener(ItemListener l)
addItemListener(ItemListener l)
addItemListener(ItemListener l)
addActionListener(ActionListener l)
addItemListener(ItemListener l)
addActionListener(ActionListener l)
addAdjustmentListener(AdjustmentListener l)
addTextListener(TextListener l)
addActionListener(ActionListener l)
addTextListener(TextListener l)
The Adapter classes provided by AWT are as follows:
java.awt.event.ComponentAdapter
java.awt.event.ContainerAdapter
java.awt.event.FocusAdapter
java.awt.event.KeyAdapter
java.awt.event.MouseAdapter
java.awt.event.MouseMotionAdapter
java.awt.event.WindowAdapter
Note: There are no default Adapters provided for the semantic listeners, since each of those only contain a single method and an adapter would provide no real value.
All platforms should see some performance improvement from reduced event traffic, but the Solaris implementation should gain exceptional improvement since it's a network-based window system.
Code Example
Following is some sample code that uses the new model:
import java.awt.*;
import java.awt.event.*;
public class App {
public void search() {
/* do search operation ...*/
System.out.println("Searching...");
}
public void sort() {
/* do sort operation ...*/
System.out.println("Sorting....");
}
static public void main(String args[]) {
App app = new App();
GUI gui = new GUI(app);
}
}
class Command implements ActionListener {
static final int SEARCH = 0;
static final int SORT = 1;
int id;
App app;
public Command(int id, App app) {
this.id = id;
this.app = app;
}
public void actionPerformed(ActionEvent e) {
switch(id) {
case SEARCH:
app.search();
break;
case SORT:
app.sort();
break;
}
}
}
class GUI {
public GUI(App app) {
Frame f = new Frame();
f.setLayout(new FlowLayout());
Command searchCmd = new Command(Command.SEARCH, app);
Command sortCmd = new Command(Command.SORT, app);
Button b;
f.add(b = new Button("Search"));
b.addActionListener(searchCmd);
f.add(b = new Button("Sort"));
b.addActionListener(sortCmd);
List l;
f.add(l = new List());
l.add("Alphabetical");
l.add("Chronological");
l.addActionListener(sortCmd);
f.pack();
f.show();
}
}
Note in particular the following differences between this example and how this would have been implemented in the old model:
In order to make this as flexible as possible, this event processing capability is provided in two levels. The first is a single method on all components:
All events for a component are first funneled through this method so that subclasses can choose to handle all events in a single place (similar to the 1.0 model's "handleEvent" with the main difference being that events are NOT propagated up the containment hierarchy in the new model).
protected void processEvent(AWTEvent)
The second option for processing events is provided at the event class level; there is a separate method for each class of event handled by that component:
For example, the java.awt.List component has the following event-class processing methods:
protected void process
By default, the single processEvent method will invoke the proper event-class processing method. The event-class processing method by default will invoke any listeners which are registered. It's important to remember that these methods perform a critical function in the event processing for an AWT component and so if you override them you should remember to call the superclass's method somewhere within your own!
protected void processActionEvent(ActionEvent e)
protected void processItemEvent(ItemEvent e)
protected final void enableEvents(long eventsToEnable)
The parameter to this method is a bitwise mask of the event types you wish to enable. The event masks are defined in java.awt.AWTEvent. Note that changing this mask will not affect the delivery of events to listeners -- it only controls the delivery to the component's processing methods. The bottom line is that the set of events which are delivered to processEvent() is defined by the union of event types which have listeners registered and event types explicitly turned on via enableEvents().
public class TextCanvas extends Canvas {
boolean haveFocus = false;
public TextCanvas() {
enableEvents(AWTEvent.FOCUS_EVENT_MASK); // ensure we get focus events
...
}
protected void processFocusEvent(FocusEvent e) {
switch(e.getID()) {
case FocusEvent.FOCUS_GAINED:
haveFocus = true;
break;
case FocusEvent.FOCUS_LOST:
haveFocus = false;
}
repaint(); // need to repaint with focus feedback on or off...
super.processFocusEvent(e); // let superclass dispatch to listeners
}
public void paint(Graphics g) {
if (haveFocus) {
// render focus feedback...
}
}
...rest of TextCanvas class...
}
An alternative to using the above approach would be to simply have your component subclass implement the particular listener interface for the events it wishes to receive and then register itself as a listener. For example, the above code example would be rewritten to:
public class TextCanvas extends Canvas implements FocusListener {
boolean haveFocus = false;
public TextCanvas() {
addFocusListener(this); // ensure we get focus events
}
public void focusGained(FocusEvent e) {
haveFocus = true;
repaint();
}
public void focusLost(FocusEvent e) {
haveFocus = false;
repaint();
}
public void paint(Graphics g) {
if (haveFocus) {
// render focus feedback...
}
}
...rest of TextCanvas class...
}
We have explicitly enabled this capability for input events only by providing two methods on java.awt.event.InputEvent:
In the default JDK implementation, all events generated on components are first posted to a special "system" EventQueue instance before being dispatched to their target component.
The Toolkit class provides a method to access the handle of the system EventQueue instance:
It would obviously be a security problem to allow untrusted applets to freely manipulate the system event queue, therefore the getSystemEventQueue() method is protected by a SecurityManager check which disallows applets direct access to the system queue. We realize that applets would also like access to an event queue which is scoped to their own containment hierarchies and we are working on an architecture to allow that for a follow-on release.
Note that if some object 'consumes' an input event, it is strictly an indication to the source component that the event should not be processed in its default manner (i.e. consuming a mousePressed event on a Button will prevent it from being activated). The event will still be dispatched to all listeners registered, regardless of whether a listener in the chain consumes the event.
public void consume()
public boolean isConsumed()
The Event Queue
Another feature of the 1.1 event model is the addition of an event queue class:
This class provides a number of public instance methods to manipulate the queue:
java.awt.EventQueue
Programs can actually use this class to instantiate their own event queue instances
for the purpose of asynchronously posting events. The EventQueue class automatically instantiates an internal thread for dispatching the events appropriately.
public synchronized void postEvent(AWTEvent e)
public synchronized AWTEvent getNextEvent()
public synchronized AWTEvent peekEvent()
public synchronized AWTEvent peekEvent(int eventID)
public final EventQueue getSystemEventQueue()
Compatibility with the old Model
Our intention is to maintain binary compatibility for programs written to the old model for the 1.1 release. However, we strongly recommend new java programs migrate to the new model. We do not encourage the explicit mixing of the two models within a single Applet. However, we realize that programs which code to the new model may need/wish to use existing GUI classes which still use the old model and so we have done our best to ensure this works (for example, the hotjava browser is a java application which will need to be able to load both 1.0 and 1.1 style applets).
The way this works is that the AWT will recognize a component as being either a 1.0-event-model "target" OR a 1.1-event-model "source", but not both. A component is recognized to be a 1.1-event-model "source" by meeting one of the following conditions:
Note that this is an "all or nothing" distinction and that once the AWT determines a component is a particular event model type, ALL events on that component will be processed in that context. For example, if a TextField object has only a FocusListener registered, then only focus events will be dispatched to the textfield in the 1.1 mechanism and the old 1.0 handleEvent method will NEVER be called (not even for other event types!). So while it is possible to combine components which use the different models, it is not possible to get a single component to mix both models.
One key difference between the two models is that the old model would automatically propagate events up the containment hierarchy, while the new model does NOT propagate events in this way. The way this works for compatibility is that if an event originates on a component which is a 1.0-event-model "target", then it WILL be propagated up the hierarchy in the 1.0 fashion, regardless of the event model type of its ancestor containers. If an event originates on a 1.1-event-model "source", then that event will NOT propagate up the hierarchy, regardless of the event model type of its ancestor containers.