Even though many versions of the AWT available at the time of writing (June, 1996) are based on JDK 1.0 or 1.0.1, bugs in these versions have forced programmers to ignore the new model and to use the old model. Basically, where the new model was needed the toolkit was broken, so there was no point changing to it. On the other hand, due to other bugs, changing to the new model caused problems such as applets being stopped by the browser when a dialog was iconified. (This problem disappeared between two versions of Netscape - help! how many versions of the AWT are there?).
With the appearance of JDK 1.0.2 many of the ``new'' model bugs have been fixed, and so have the ``old'' model ones. It now looks to be worthwhile to switch to the new model, as programs relying on the old one will become progressively more ``buggy'' as the AWT switches away from the old model.
The change in event models is far more complex than this sounds.
If you want to be pedantic, there are really three event models:
the old model in which native events were handled in a particular way
(the old way) and AWT events were handled in a particular way (the old way).
In the new model, native events are handled differently (the new way)
and so are AWT events (the new way). What makes this a real mess
is that in JDK 1.0 a whole set of native events are handled in the
old way, while the AWT events are handled in the new way.
To make it worse, the set of native events handled in the old way
is destined to migrate to the new way, but there is no specification
of when this is to occur.
The old (and old) model
Just to make clear what the new model is changing away from, here is
a quick revision of the old model. It is a nice simple model, that
did pretty well.
An event is generated in a Component such as Button
or
TextArea
. The event is handed to the component's
handleEvent()
method. Any component could either handle
an event or ignore it. When it handled an event it signalled this by
returning true
from handleEvent()
.
This stopped any further processing of the event. If an event chose
to ignore an event (the default) then it would be passed to its
parent's handleEvent()
.
An event would percolate up the container tree until it was handled or
reached the top of the tree, in which case it disappeared.
Suppose a Frame
contains a Panel
which contains a Button
.
If the user clicks the left mouse button within the Button
and
no object handles it, then
the event follows this path:
The AWT is a layered toolkit: it is built on top of a native
toolkit, such as Windows or Motif.
If we look under the hood at what happens to the native event
then we see that the AWT event processing takes place
after things have happened in the native object.
So a Button
would be pressed and released
(with all the visual effects this entails) and an AWT ACTION_EVENT
would be generated to be handled by the application;
a key would be pressed and appear in a
TextArea
and an AWT KEY_PRESS would be generated.
An item would be selected in a List
, causing it to be
highlighted and any previous selection removed, and then an AWT LIST_SELECT
would be generated to be handled by the application.
The old model worked like this: something would happen to the GUI
object (a window would be iconified, a menu item would be selected,
a scrollbar would be moved, etc). After the native toolkit
had handled it, then the application would be informed
by an event being sent to it.
Under X, this was done at the native code level by the use of Xt
callback functions, or by adding an X event handler for events
which were of no interest to the Xt widget but were of interest
to the AWT toolkit.
Note that the Java application should regard the AWT event as a
readonly object - changes to it are meaningless.
The path from native event to AWT event for mouse clicks is shown
here:
Why the new (and new) event model?
The new event model is set in place to allow filtering
of events. The most common examples occur in entering text, and this
is the first set of events to be handled by the new model.
TextField
would be subclassed by a class that
stopped the character from being displayed, while keeping a record
of it internally.
(We ignore the method setEchoCharacter()
- this is a
kludge to solve a special case of the previous point.)
What is happening in these examples? Look at the events:
key
field before delivering it to the native GUI object.
The new event model is designed to allow this sort of filtering by
offering an event to the application before it gets to the
native GUI object, and allowing the application to decide what to do
with it. This makes the Java application a more active, controlling
participant in the event handling process.
This is much better than, say, trying to blank out passwords by
removing the text after it has been typed!
Who's who in the new model
There are a large number of players in the new model.
There is the native GUI object such as the
Motif XmPushButton
or the Windows Dialog
. The user will interact with
these objects, and the effects of the interaction will (eventually) appear in
these objects.
When the user attempts to interact with an object, they do so by clicking
the mouse, pressing keys, etc. Depending on the toolkit, a
native event will be generated, such as an
X Event with type
set to a value such as KeyRelease
.
Every AWT GUI object (Button
,
Label
, List
, TextArea
, etc)
has an associated peer object. The peer object is responsible for
the interface between the native GUI object and the corresponding
AWT object. So when a Frame
wants to set the cursor,
it calls upon its FramePeer
to execute the native code
to do this; when a List
wants to set the selected item
it calls up on its ListPeer
to execute the native code
to do this, and so on.
An AWT event is manufactured entirely by the AWT library
(based on native event information passed to the peer).
It is a Java object and nearly all of its fields are meaningful
for an AWT application to use (the data
field is
private, and carries around hidden native event information that is
used by the new model).
The set of AWT GUI objects created by the application play a role in event handling.
The last player in this is the native event delivered to the native
GUI object. This may not be the same as the event that was generated
by the user.
This is the esential difference between new and old models -
the event delivered to the native GUI object may be modified by the
application from the original event.
The new (and new) event model
From the point of view of the AWT, the new event model follows a similar
route to the old model: an event is generated by the peer object and
sent to a GUI object such as a Button
. If it handles the
event by returing true
from handleEvent()
then the event disappears. Otherwise it is passed to its parent,
and so on up the GUI tree. When it reaches the top, however, it does
not disappear like it used to, but is instead passed to the
corresponding peer object. This may handle it, or pass it on
down to the next peer object. The event goes a full circle
back to the peer from which it came:
X has many layers of event handling. Java is built above the Motif
toolkit, so the Xlib, Xt and Motif layers all have their say in
what happens to events. Suffice it to say that after a lot of work
an X event is ready to be delivered to an Xt/Motif widget by
XtDispatchEvent()
.
Under the old model, the event would be dispatched.
Instead, for the new model it is at this point that the X implementation
of the AWT interferes and blocks dispatch. Instead it makes a call
up into the the AWT peer object, effectively saying "here is the native
event, what do you want to do with it?". The peer object then
creates an AWT event and sends it on its route through the object,
its parents, and then back through its peers, as we have just seen.
The AWT event that arrives back at the peer may not be the same as the event
that it sent out - one of the GUI objects may have modified it along
the way. So a new X event is constructed using the original X event
and any changes made to the AWT event along the way. This new X event
is then dispatched to the Xt widget:
The new model for native events makes no use of callback functions
since it only uses the native events before they are
dispatched to the native object.
Confusion
Now we get to the messy bit: which AWT and native events are handled by
which model?
In the beta-one version of the JDK, all events were handled
by the old model for AWT events and the old model for native events.
Scarcely any copies of this version should be around any more, so you
don't need to worry about it. In terms of reading books and tutorials,
or examining public source code
for event handling, there is a big problem: nearly all of them
use this model, so are out of date. Sigh... Any book that tells
you to return true
after handling an event falls
into this category.
In all production releases of the JDK, key events (KEY_PRESS, KEY_RELEASE, KEY_ACTION, KEY_ACTION_RELEASE) are handled by the new model for AWT events and the new model for native events. However, it was not until JDK 1.0.2 that Sun got it working correctly. Some examples of what you can do with this are given later.
At JDK 1.0.2 all other events apart from KEY events are handled by the new model for AWT events and by the old model for native events! So the figures earlier showing the new native model for mouse events are not correct - yet. Sun have promised to move events to the new model, so at some stage handling of native mouse events will be by the new model, as will a lot of other events.
When will this occur? Sun have not said. Which events? Sun have not said. This is more than a little worrying: it makes programming for the AWT a moving target.
The next sections discuss programming for these
models.
Programming the old (and old) model
This way of programming the AWT is now obsolete. It is only mentioned
because most documentation still does it this way.
When a GUI object handles an event it returns true
from handleEvent()
, or from one of the convenience
functions such as keyUp()
or action()
.
Don't do this any more.
Programming the new (and new) model
The new model potentially allows four ways of handling AWT events:
noting them,
changing them, discarding them and generating them. They are discussed
in turn. The examples are a bit boring in one way, in that they
only use key events. This can't be helped, since that is all that
works right now!
Noting events
Many events are noted and actions taken upon receipt of them.
There is no need to modify the event.
For example, in a basic text editor whatever is typed should be
entered directly into the editor window. However, a flag should be
set to signal that the contents had changed so that the user may be
prompted to save them in a file on exit:
class TextEditor extends TextArea { private boolean changed = false; public boolean keyDown(Event evt, Object what) { changed = true; return false; } }
Note that there is a real change here from the old model: in the old
model the method would have returned true
to signal
that it had handled the event. In the new
model it must return false
so that the event can make
its way back to the peer object.
Modifying events
Events have a number of fields that can be examined or changed.
Under the old models there was no point in changing them, and events
had to be treated as `read-only'. While there was nothing in Java to force
this read-only interpretation, changing event fields was pointless as
nothing would result from it. Under the new model changing fields can have
an effect.
When the user types characters, it is common to `normalise' the input
in some way, such as changing all characters to uppercase. When a key
event occurs such as KEY_RELEASE, a valid field is key
.
This is the int
value of the key (I don't know why it
is not a char
). If this changed, then a different
key will be presented to the native object, resulting in a different
key appearing to what is pressed. The following applet converts
all characters to upper-case as typed:
import java.lang.*; import java.awt.*; import java.applet.*; public class UpperCaseApplet extends Applet { public UpperCaseApplet() { UpperText t = new UpperText(); add("Center", t); } } class UpperText extends TextArea { public boolean keyUp(Event evt, int key) { evt.key = Character.toUpperCase((char) key); return false; } }When run, it appears as
Again, there is one major point to note from this example: keyUp()
returns false
, not true
. The old model, where
you would return true
to signal handling of the event
must not be followed here!
There is also a minor point: it is probably slightly more common to cast
to lower case than upper. However, the Motif implementation of JDK 1.0.2
doesn't handle converting to lower case due to a bug.
The AWT has two fields that it can use to check if a key is upper case:
the key
holds an ASCII value that can be upper or lower
case; in addition, the modifiers
field can contain the
mask SHIFT_MASK.
To convert to lower case, one should reset the
key
field and also the modifiers
field of the
AWT event.
In building the new X event from the old one, the AWT fails to clear the
xkey.state
before checking the AWT event's modifiers
field. In other words, converting to lower case doesn't work yet for Motif.
Apparently it does work for at least Symantec 1.2 for Windows95.
Discarding events
Discarding events is easy: return true
from the event
handler. The following is a very naive version of a PasswordField
that collects the password in a StringBuffer
, but neither
displays it on the screen nor enters it into the TextField
where it would be accessible by getText()
:
class PasswordField extends TextField { private StringBuffer password = new StringBuffer(); public boolean keyDown(Event evt, int key) { if (key == '\n') return false; password.append((char) key); return true; } }(Note, this is very simple in that it does not handle such things as backspacing over input or pasting of text into the widget.)
While appearing simple in code, the PasswordField class has
the most complex event handling that I have yet come across.
Ordinary characters such as alphanumeric, punctuation and
most whitespace characters will form part of the password
and will need to be suppressed. This is done by returning
true
for them. The newline is a special case.
As a character typed, the native event is processed by the
new model, and must be passed all the way through.
When it is eventually passed to the underlying native
GUI object (a Motif TextField) it triggers a callback function
that generates an ACTION_EVENT. So the native event is handled
firstly by the new model and secondly by the old model!
The
following applet
uses the PasswordField to collect a password:
Generating events
It is easy to create a new event: the constructor will do that.
It is not hard to inject it into the path that events follow so that
it ends up in the peer object. So, what then happens to these
``synthetic'' events? In JDK 1.0.2, they are just discarded without
having any effect on the native object. Creation of synthetic events
is currently worthless.
The reason they are discarded is as follows: when the AWT event is
created by the peer object, the original X event is dragged along
with it. A binary represention of the event is placed in the event's
private field data
. When the AWT event returns to the
peer, the original X event is reconstructed from data
and is then modified
according to any changes that have been made to the AWT event.
This is then dispatched to the widget.
A Java object trying to create an AWT event does not have an X event
lying around, so the private field data
will end up as
null
. When this event gets back into the peer, it checks
for the null
value and discards the event.
Even attempting to reuse an existing X event by repeatedly posting
an AWT event to the widget fails.
Is this inherent in the AWT? Certainly not for X. It is possible to
construct new X events, and even ask the X server to give valid
id fields (the
replayXt
system I wrote, which does testing and replay of Xt applications,
constructs X events as needed).
So it is either a failure of implementation in JDK 1.0.2,
taking the easy way out for now, or a design
decision that we have not been told about.
When you find out which, please let me know!
Programming the new (and old) model
Despite the change to the new model, nearly all code built using the
old model continues to work. This is because the old event model for
native events is still used for all but key events. This means that
the native event is handled before generating the AWT event, so
it makes no difference whether or not a new native event is generated.
If it is generated, it is discarded anyway, since a flag is used
to avoid duplicate sending of the event.
But it may make a difference someday. In later versions of the AWT more events will be moved to the new model, and then the old model treatment will cause the events to be undelivered. When this happens your application will promptly break: the Java application will believe that certain things have happened, but the native GUI objects will never have received these events.
To give a concrete example of what will be involved in converting
the toolkit:
presently, a mouse click in a Motif List
generates an AWT event via the XmNsingleSelectionCallback
or XmNmultipleSelectionCallback
functions.
Motif is responsible for deciding which item is selected and highlighting
it. The callback function makes a dynamic call into the AWT
peer object, supplying
information about which item has been selected - information that
Motif has already worked out. This is by the old model.
If this native event is to be handled by the new model, native code as part
of the AWT will have to duplicate work of the Motif toolkit by
figuring out which item would have been selected if the event
had gone ahead. It will then create an AWT event, which you may modify.
One of the valid fields for a LIST_SELECT event is arg
,
which is set to the item selected. If this is changed, then presumably
an X event would be generated with the y
reset to the
correct value so that the changed item is selected (assuming it exists,
of course...).
The complexity of this makes it
seem to me most unlikely that LIST_SELECT will change to the new
model, at least for some considerable time. I would even worry about
mouse events, the most obvious next candidate for conversion from old to
new model: by changing the modifiers
field a button
press could change from Button1 to Button3, which under Motif
signals the start of a drag-and-drop operation rather than a select
operation!
So, there is considerable uncertainty as to what events will move to new model, and when. What should a developer do under these circumstances? Sticking to the old model looks ok right now, but may not be on a longer time scale. The converse question is: what problems arise from switching to the new event model while the old one still holds?
In JDK 1.0.1 asuming the new native event model while the old native
event model was used did indeed cause some problems. One case I
experienced was for the WINDOW_ICONIFY event. In an applet, a
Dialog
may be created, but the user may choose to iconify
it (if the window manager supports this). Assuming the new native event
model means that the Java handleEvent()
must return
false
for the event to take place. This means the Java
event must be passed to the parent, which in this case is the
applet. In 1.0.1 the applet got it wrong, assumed that it had been
iconified rather than its dialog, and promptly stopped itself!
In JDK 1.0.2 these types of problems seem to have been cured, at least for the cases I have tried. So it looks to be relatively safe to assume the following course:
false
from handleEvent()
unless
you are dealing with old (JDK 1.0.1 or earlier) versions of the
toolkit.
true
from handleEvent()
only
if you are sure the native event is being handled by the new model and
you want to discard it.
System
class has a
java.version
field in getProperties()
,
this is not obviously related to the JDK version number.
At least the Fantasy books have their own internal consistency...
Previous AWT Articles