The Java AWT Toolkit: Events

Jan Newmarch


Introduction

This article continues the series on the AWT toolkit begun in the last issue of the X Advisor. This time we look in depth at the Event class which underlies the AWT event-driven model. There are many undocumented features and design decisions in the Event class which has lead to a lot of confusion in how to use events. This article attempts to clear the mystery behind them. In addition we penetrate behind the scenes and discuss how AWT events are generated by user actions on what is really just a complex Motif application (under X).

Event Model

The Java AWT library runs using an event-driven model, just like other GUI toolkits such as Xlib, Motif and Windows 95. This is not surprising - the toolkit is built above the native libraries and it is hard to see what other model could be used.

X Programmers will already be familiar with at least two different styles of event processing: the Xlib style has a single event loop which switches on event type and contains application code within each branch. On the other hand the Xt approach hides the raw event model under several layers and ends up associating user events to widget actions, and these actions invoke callback functions that contain the application code.

The AWT toolkit uses its own set of events, and generates them in response to certain user actions by the method postEvent(). The toolkit supports both the Xlib and Xt methods of dealing with these events: they can be left to a single event loop, or in most cases be dealt with directly by the object receiving the event. As discussed in the previous article, I prefer the object approach but it is very common to see the single event loop approach. Unfortunately the toolkit does not completely support the object approach.

This article continues the exploration of the AWT toolkit by examing the AWT event class in detail. Many of the details are only accessible by an examination of the source code, and I have used the beta-one source code release for this. The beta-two version has been released but this does not contain any of the Motif implementation files.

The Event class

There is a large set of final variables that just define constants; these are discussed in later sections. Apart from these, the variables in each Event object are
Object	target;
long	when;
int	id;
int	x;
int	y;
int	key;
int	modifiers;
int	clickCount;
Object	arg;
Event	evt;
The target is the object the event occurred in, for example the Button the mouse was pressed in. when is a timestamp for the event. The x and y fields are the coordinates of the event within the target. These follow the usual practice of measuring from the top-left of the object. The key and modifiers sometimes convey extra information and are discussed in more detail later.

When objects share common characteristics, differing only in small ways, one may create separate classes for each where each class is derived from a common parent. Alternatively, one may use non-OO tricks, distinguishing each by different values of a field. Which method is used depends on the designer of the class(es). For the Event type there are a large number of different events, so if there was a separate class for each type it would lead to a large number of derived classes which might be quite confusing. The different variations on event types are instead handled by use of the id field within the single Event class. The values of this field are discussed later.

Events are generated by many different objects - Buttons, Lists, TextField, etc. In the X toolkits there is often a structure of widget-specific information passed back to the callback function as the call_data parameter. This type of capability is present in the AWT Events through the arg field. When an Event is prepared the arg field may be set to any suitable object by the AWT object implementing postEvent(). It is not as rich in information as in Motif, but this doesn't really matter - the entire object is accessible through the target field anyway in addition to the rest of the Event information.

Because of Java safety rules the various fields will always contain sensible values. Whether they actually have useful values depends on the type of the event.

Event types

The constant values that are used to distinguish event types are given in the following table. They are given roughly alphabetically, but grouped by function.
Table 1: Event Types
ACTION_EVENT
GOT_FOCUS LOST_FOCUS
KEY_ACTION KEY_ACTION_RELEASE KEY_PRESS
KEY_RELEASE
LIST_DESELECT LIST_SELECT
LOAD_FILE SAVE_FILE
MOUSE_DOWN MOUSE_DRAG MOUSE_ENTER
MOUSE_EXIT MOUSE_MOVE MOUSE_UP
SCROLL_ABSOLUTE SCROLL_LINE_DOWN SCROLL_LINE_UP
SCROLL_PAGE_DOWN SCROLL_PAGE_UP
WINDOW_DEICONIFY WINDOW_DESTROY WINDOW_EXPOSE
WINDOW_ICONIFY WINDOW_MOVED

Events with Methods

Events are eventually dealt with by a method handleEvent of some Component object. The default method does a switch on event id and often calls another method of Component. While the handleEvent method can be overridden in subclasses it usually is not an ideal solution, and it is better to override the method called after the switch. For example, override the method action() rather than look for ACTION_EVENT in handleEvent.

Unfortunately the preferred style of programming cannot always be used because not all event types call their own methods. The following table lists the methods called (or not called) by handleEvent of Component objects. In particular, it can be observed that SCROLL_... events are not handled at all, so it may be advisable to define a subclass of Scrollbar which handles these event types in its handleEvent, just like we had to do with Menu events in the last article. The same applies to the various WINDOW... events.

Alternatively, one could lobby for methods to be added to the Component class...
Table 2: Event Types and Associated Methods
Event Type Method Called
ACTION_EVENT action(Event evt, Object arg)
LIST_DESELECT no method
LIST_SELECT no method
GOT_FOCUS gotFocus(Event evt, Object arg)
LOST_FOCUS lostFocus(Event evt, Object arg)
LOAD_FILE no method
SAVE_FILE no method
MOUSE_DOWN mouseDown(Event evt, int x, int y)
MOUSE_DRAG mouseDrag(Event evt, int x, int y)
MOUSE_ENTER mouseEnter(Event evt, int x, int y)
MOUSE_EXIT mouseExit(Event evt, int x, int y)
MOUSE_MOVE mouseMove(Event evt, int x, int y)
MOUSE_UP mouseUp(Event evt, int x, int y)
SCROLL_ABSOLUTE no method
SCROLL_LINE_DOWN no method
SCROLL_LINE_UP no method
SCROLL_PAGE_DOWN no method
SCROLL_PAGE_UP no method
KEY_ACTION keyDown(Event evt, int key)
KEY_ACTION_RELEASE keyUp(Event evt, int key)
KEY_PRESS keyDown(Event evt, int key)
KEY_RELEASE keyUp(Event evt, int key)
WINDOW_DEICONIFY no method
WINDOW_DESTROY no method
WINDOW_EXPOSE no method
WINDOW_ICONIFY no method
WINDOW_MOVED no method

Valid Event Fields

The id field is used by the toolkit and also by the Java programmer to distinguish between event types. Just as with X events, different event types have different pieces of useful information. For example, the ACTION_EVENT is generated after a Button has been selected. The toolkit designers have decided that a knowledge of the x, y coordinates of the mouse is not necessary here, but a knowledge of the Button's label is.

Because the different event types are all handled within the same class this means that some fields have useful information but others do not. Really, this is poor OO practice but is partly excusable: it avoids a a large number of subclasses of an abstract event class, and due to the default values for Java data types the useless fields will never have ``dangerous'' values in them. The following table lists the fields of the event class that are valid for the different types of event. The target field and id fields are always valid for each type. Some event types are never generated by the toolkit so their valid fields are unknown.
Table 3: Valid Event Fields
Event Valid fields
ACTION_EVENT arg*
LIST_DESELECT arg
LIST_SELECT arg
GOT_FOCUS none
LOST_FOCUS none
LOAD_FILE never generated
SAVE_FILE never generated
MOUSE_DOWN when, x, y, modifiers, clickCount
MOUSE_DRAG when, x, y, modifiers
MOUSE_ENTER when, x, y, modifiers
MOUSE_EXIT when, x, y, modifiers
MOUSE_MOVE when, x, y, modifiers
MOUSE_UP when, x, y, modifiers
SCROLL_ABSOLUTE arg
SCROLL_LINE_DOWN arg
SCROLL_LINE_UP arg
SCROLL_PAGE_DOWN arg
SCROLL_PAGE_UP arg
KEY_ACTION when, x, y, key, modifiers
KEY_ACTION_RELEASE when, x, y, key, modifiers
KEY_PRESS when, x, y, key, modifiers
KEY_RELEASE when, x, y, key, modifiers
WINDOW_DEICONIFY none
WINDOW_DESTROY none
WINDOW_EXPOSE never generated
WINDOW_ICONIFY none
WINDOW_MOVED x, y
(* For MenuItem and CheckboxMenuItem the fields when and modifiers are also valid.)

There are some minor inconsistencies in these tables. For a GOT_FOCUS or LOST_FOCUS event no additional fields of the event are set. However, the methods gotFocus and lostFocus methods have an extra parameter Object what which turns out to be just null.

For a simple example using this, the following example shows a box within a Canvas. When the user successfully clicks the mouse in the box, a ``hit'' count is updated, but when the box is missed a ``miss'' count is updated instead. After each mouse click the box is moved to a new random location. This example uses the method mouseDown in the Canvas object and uses the x, y values set for this method from the event information. Since your browser cannot run applets, this is what the application looks like:

import java.util.*;
import java.lang.*;
import java.awt.*;

class Chase extends Frame {
    static public void main(String argv[]) {
	new Chase();
    }

    Chase() {
	Report r = new Report();
	add("North", r);
	ChaseArea ca = new ChaseArea(r);
	add("Center", ca);
	resize(300, 300);
	show();
    }
}

/** A status bar showing hits and misses counts
 */
class Report extends Panel {
    int HitCount = 0;
    int MissCount = 0;
    Label Hits, Misses;

    Report() {
	setLayout(new GridLayout(1, 2));
	Hits = new Label("Hits: " + HitCount);
	Misses = new Label("Misses: " + MissCount);
	add(Hits);
	add(Misses);
    }

    public void addHit() {
	Hits.setText("Hits: " + ++HitCount);
    }

    public void addMiss() {
	Misses.setText("Misses: " + ++MissCount);
    }
}

/** A Canvas with a box drawn in it that moves
 *  randomly when the mouse is clicked in it
 */
class ChaseArea extends Canvas {
    final int box_size = 8;
    Rectangle box;
    Random rand;
    int box_x = 0, box_y = 0;
    Report report;

    ChaseArea(Report r) {
	report = r;
	rand = new Random();
	box_x = 0;
	box_y = 0;
    }

    // draw a new rectangle
    public void paint(Graphics g) {
	g.drawRect(box_x, box_y, box_size, box_size);
    }

    // move the box to a random location
    public void moveBox() {
	box_x = (int) Math.floor(rand.nextFloat() * 
				(size().width - box_size));
	box_y = (int) Math.floor(rand.nextFloat() * 
				(size().height - box_size));
	repaint();
    }

    // handle mouse down, moving box and updating report line
    public boolean mouseDown(Event evt, int x, int y) {
	if (box_x <= x && x <= box_x + box_size &&
	    box_y <= y && y <= box_y +box_size) {
	    report.addHit();
	} else {
	    report.addMiss();
	}
	moveBox();
	return true;
    }
}

Key values

Java uses the 16-bit Unicode character set, to allow for internationalised applications. The Motif toolkit has support for international character sets using ``wide'' characters and XmStrings. The Motif implementation of the AWT toolkit does not appear to make any use of the 16-bit capabilities as yet.

In addition to the ``ordinary'' characters, the Event class defines a number of constants for certain keys. These are
Table 4: Constant Key Values
DOWN END HOME LEFT PGDN PGUP RIGHT UP F1 ... F12

These can be used in code as

if (evt.key == Event.HOME) ...

Modifiers

For some event types - the KEY... and MOUSE... types - the modifiers field is valid. This is a bitmask of values, where zero means no mask. The possible masks are
Table 5: Modifier Constants
ALT_MASKCTRL_MASKMETA_MASKSHIFT_MASK

For key events they have the expected values - for example, the META key on a Sun is the `diamond'' key.

For mouse events, they play an unusual role for X, in that they distinguish keys using a three-button model. With a modifier value of zero the button selected is the left button; with a modifier value of SHIFT_MASK the button selected is the middle button; with a modifier value of CTRL_MASK the button selected is the right button. (Strictly, these are Button1, Button2 and Button3 respectively.)

arg value for ACTION_EVENT

The arg value in an event is similar to the call_data field in Xt callbacks - it carries additional information supplied by the toolkit about the context in which the event occurred. In Motif the call_data structures are often very rich in information. The AWT event type contains the field arg which is used for similar purposes. However, it is much weaker in information content because any valid information can either be obtained from other event fields or from the object the event occurred in (available in target).

The following table lists the value of arg for events of type ACTION_EVENT
Table 6: arg Values for ACTION_EVENT
Object Value Type
Button getLabel() String
Checkbox Boolean(getState()) Boolean
CheckboxMenuItem getLabel() String
Choice getSelectedItem() String
List getSelectedItem String
MenuItem getLabel() String
TextField getText() String

arg value for SCROLLBAR_... events

The events generated for the various Scrollbar actions all have a valid arg value, of type Integer. These values are all the new slider location value.

arg value for LIST_... events

The events generated for LIST_SELECT and LIST_DESELECT have a valid arg value, of type Integer, which is the index selected or deselected.

Displaying Events

The following program is the equivalent of xev, that shows information about X events occurring within it. Of course, this one does not show X events but AWT Event information.
import java.awt.*;

public class EventTest extends Frame {

    public static void main(String argv[])
    {
	new EventTest();
    }

    EventTest() {
	add("North", new Label("Hello World"));
	add("South", new Button("Hello too"));
	resize(200, 200);
	show();
    }

    public boolean handleEvent(Event evt) {
	System.out.println(evt.toString());
	return true;
    }
}

To see the output from the Applet in Netscape, you will need to enable the Java console window from the Preferences menu.

The program prints constant integer values (such as id) in their literal rather than symbolic form. To interpret these you may need to have the Event.java source code handy.

You can vary the program by choosing different widgets - in fact this is quite instructive, because even this program shows a number of bugs in the AWT toolkit, as discussed later.

Generating Events

A very common question in the Java newsgroup is how to capture keystroke events in TextArea or TextField objects. Regrettably the answer is very simple for the Motif version of the AWT toolkit: you can't. When a key is pressed no AWT event is generated, so there is no way of trapping it! To avoid wasting time on foolish questions (;-) the AWT programmer needs to know exactly when events are generated, and by what user actions.

The following table lists the Motif actions, or the Xlib Protocol messages and how they eventually generate AWT events (the implementation details are discussed later). The first column lists the AWT classes. The second column lists the Motif widget which handles the action/event for the AWT object. There may be more than one Motif widget involved in this because many of the AWT objects are implemented uisng a number of Motif widgets.

The third column lists the Motif callback that triggers AWT event generation. You could try playing with translation tables if you want to affect what Xlib events call what actions if you want - I suspect it will work now but not in future versions of the toolkit!

The fourth column lists what goes on in the Motif implementation by Peer objects. If you only want to use the toolkit and don't want to know implementation details then please ignore this column. To tell the truth, I only put this column in so that I could document the route I am going to have to follow on the next release of the toolkit!

The fifth column lists the final AWT event generated. Some Motif actions end up generating no event at all because they are handled entirely by the toolkit. For example the handleResize() method of Frame's peer calls repaint() which does all the necessary work without requiring further event processing. On the other hand, it is indicative of a bug that WM_DELETE_WINDOW for Dialog generates an event whereas the same event for FileDialog doesn't.
Table 7: Generating Events
Object Motif Widget Motif Action/Event Peer Method AWT Event
Button PushButton activateCallback action() ACTION_EVENT
Checkbox ToggleButton valueChangedCallback action(state) ACTION_EVENT
CheckboxMenuItem ToggleButton valueChangedCallback action(when, modifiers, state) ACTION_EVENT
Choice PushButton activateCallback action(index) ACTION_EVENT
Dialog DrawingArea resizeCallback handleResize(width, height) none
PopupShell WM_DELETE_WINDOW handleQuit() WINDOW_DESTROY
MapNotify handleDeiconify() WINDOW_DEICONIFY
UnmapNotify handleIconify WINDOW_ICONIFY
ConfigureNotify handleMoved(x, y) WINDOW_MOVED
FileDialog PopupShell WM_DELETE_WINDOW handleQuit() none
FileSelectionBox okCallback handleSelected(file) none
cancelCallback handleCancel() none
Frame DrawingArea resizeCallback handleResize(width, height) none
PopupShell WM_DELETE_WINDOW handleQuit() WINDOW_DESTROY
MapNotify handleDeiconify() WINDOW_DEICONIFY
UnmapNotify handleIconify() WINDOW_ICONIFY
ConfigureNotify handleMoved(x, y,) WINDOW_MOVED
List List defaultActionCallback action(item_position) ACTION_EVENT
singleSelectionCallback handleListChanged(item_position) LIST_SELECT or
LIST_DESELECT
multipleSelectionCallback handleListChanged(item_position) LIST_SELECT or
LIST_DESELECT
MenuItem PushButton activateCallback action(when, modifiers) ACTION_EVENT
Scrollbar Scrollbar decrementCallback lineUp(value) SCROLL_LINE_UP
incrementCallback lineDown(value) SCROLL_LINE_DOWN
pageDecrementCallback pageUp(value) SCROLL_PAGE_UP
pageIncrementCallback pageDown(value) SCROLL_PAGE_DOWN
dragCallback dragAbsolute(value) SCROLL_ABSOLUTE
TextArea Text focusCallback gotFocus() GOT_FOCUS
losingFocusCallback lostFocus() LOST_FOCUS
TextField TextField activateCallback action() ACTION_EVENT
focusCallback gotFocus() GOT_FOCUS
losingFocusCallback lostFocus() LOST_FOCUS
Window PopupShell MapNotify handleDeiconify() WINDOW_DEICONIFY
UnmapNotify handleIconify() WINDOWICONIFY

In addition to these, objects of type Canvas (and hence Panel), Dialog, Frame and Window all use a Motif DrawingArea which has an EventHandler added for a wide set of events. These also generate events according to this table
Table 8: Events for Canvas, etc
Xlib Event Peer Method AWT Event
Graphics Expose handleRepaint(x, y, width, height) none
Expose handleRepaint(x, y, width, height) none
FocusIn gotFocus() GOT_FOCUS
FocusOut lostFocus() LOST_FOCUS
ButtonPress handleMouseDown(time, data, x, y, xroot, yroot, clickCount, modifiers) MOUSE_DOWN
ButtonRelease handleMouseDown(time, data, x, y, modifiers ) MOUSE_UP
KeyPress handleKeyPress(time, data, x, y, key, modifiers) KEY_PRESS
handleActionKeyPress(time, data, x, y, key, modifiers) KEY_ACTION
KeyRelease handleKeyRelease(time, data, x, y, key, modifiers) KEY_RELEASE
handleActionKeyRelease(time, data, x, y, key, modifiers ) KEY_ACTION_RELEASE
MotionNotify handleMouseDrag(time, data, x, y, xroot, yroot, modifiers) MOUSE_DRAG
handleMouseMoved(time, data, x, y, xroot, yroot, modifiers) MOUSE_MOVE
EnterNotify handleMouseEnter(time, x, y) MOUSE_ENTER
LeaveNotify handleMouseExit(time, x, y) MOUSE_EXIT

Putting it all together

There have been a lot of tables presented so far. How should one make sense of it all? Consider the Checkbox for example. This is implemented by a Motif ToggleButton( Table 7), so if you can't already figure out what a Checkbox is supposed to do then you can read any of the many books on Motif to see what it does, what it looks like, etc. In particular it has a valueChangedCallback which is called from the ArmAndActivate(), BtnUp() and Select() actions. (See the Motif Programmer's Reference Manual for this kind of detail.) The translation tables for the ToggleButton tell how the user can invoke these actions. This is the ``native'' side of the AWT implementation.

The event generated by the AWT toolkit is an ACTION_EVENT (Table 7) with valid field arg (Table 3). The value of arg for the Checkbox object is the boolean value equivalent to calling the method getState() on the object (Table 6). For ACTION_EVENT's the method action is called to handle the event, so it is only necessary to override this method in your application (Table 2).

class MyCheckBox extends Checkbox {
    public boolean action(Event evt, Object what) {
	Boolean state = (Boolean) what;

	if (state.booleanValue()) {
	    System.out.println("Selected");
	} else {
	    System.out.println("Deselected");
	}
	return true;
    }
}

Motif Implementation

When the user clicks the left mouse button over a Button, the method action() is invoked for the Button. How is this done? Well, there are layers of code here: Xlib, Motif and AWT. By the time you get through this section, you will appreciate why computers need to be as fast as they are in order to not appear more sluggish than they often do!

Xlib directs events to Windows. A low-level Xlib program will sit in an event loop catching events and dispatching them based on event type and the window they occurred in.

Xt sits on top of this, catching events and performing its own event dispatch. This dispatch is to widgets, and goes through a number of complex stages. Once the Xt toolkit has decided which widget the event belongs to it uses the widget's translation table to decide what widget action to map it onto. From there it uses the widget's action table to decide on the C action function to execute. Within the C action the widget implementation will execute all of the callbacks on its callback list. (This ignores the extra complexity caused by the Motif Focus mechanism which puts in another processing level.)

We now enter the realms of the AWT implementation above Motif. The Peer objects are responsible for creation of Motif widgets at suitable times. This is done using native C functions with gruesome names like sun_awt_motif_MTextFieldPeer_create - meaning the create method of the MTextFieldPeer object within package sun.awt.motif. These creation functions will also add callback functions (or protocol handlers) such as TextField_focusOut, which will be executed as normal Xt callback functions.

When one of these callback functions executes, it makes a dynamic call back into the Java system, specifying the object, the method of that object and the arguments to that method. The Java runtime is responsible for creating the appropriate method call for the object.

This is a general-purpose mechanism for native code to invoke Java methods. The particular object specified for all of the AWT objects is the Peer object. The AWT TextField object is implemented using a Motif TextField widget. The object specified in this dynamic Java call is the Peer object of type TextFieldPeer - actually of type MTextFieldPeer, since this is implementing the Motif binding.

The method called in the Peer object prepares a new Event based on information contained in this dynamic call and then calls postEvent for the object that triggered this whole process. eg postEvent is called for the AWT TextField object.

The route from there belongs within the AWT toolkit. For objects of type Component handleEvent is called on the object, its parent, its grandparent (in the widget tree) until one of the methods returns true.

Bugs

In discussing the bugs of any system, one should at least know the versions used, because the bugs are going to disappear in future versions aren't they?

There are still bugs in the AWT toolkit, even in the JDK 1.0 release. This shows up (at least) in differing behaviour between the Motif version and the Windows95 version. The existing documentation does not make it clear which behaviour is expected, but I believe that both are wrong in some cases.

The problems show up on some quite trivial programs. Consider a Frame with just a Label in it. In the Frame area mouse motion events occur, and mouse up and down events. Key up and key down events also occur. These are all odd behaviour for something that has container semantics but not - I would have thought - input semantics. The behaviour is identical under X and Windows95 so would appear to be intentional. Under X it is achieved by adding event handlers to the Frame's DrawingArea widget.

For the Label, the X implementation does not get any mouse motion or up/down events, but does get key up/down events. Again, these inputs are odd. Under Windows95 there is a difference - the Label also gets mouse motion events.

If we add a Button to this arrangement (now it is like the event-printing program earlier) the Button also shows a difference between X and Windows95: under X it does not get mouse motion but under Windows95 it does. Under both, key up/down events are received.

Conclusion

The Event class underlies the AWT toolkit, like similar classes/structures underly other event-driven toolkits. The material in this article is meant to help understand the details of the class by filling in some of the many gaps in the documentation. in particular the tables will show exactly what methods and fields are valid for the various event types.

The next two articles will address an area that has also received short-shift in documentation: geometry management. The next article will deal with the general principles behind geometry management and the simpler managers. The second will deal with the GridbagLayout class which is a very general manager with the regrettable complexity of Motif's Form, but an equivalent flexibility in allowing very complex arrangements.