The heart of any applet or graphical user interface is the event processing code. Graphical applications are event-driven: they do nothing until the user moves the mouse or clicks a button or types a key. An event-driven program is structured around its event-processing model, so a solid understanding of event handling mechanisms is crucial for good programming.
Unfortunately, the Java event handling model has changed between Java 1.0 and Java 1.1. The Java 1.0 model is a simple one, well suited to writing basic applets. It has a number of shortcomings, however, and does not scale well to complicated interfaces. Although the 1.0 event model is deprecated in Java 1.1, you'll still need to use it for any applets that you want to run on Web browsers based on Java 1.0. The Java 1.1 event model solves many of the shortcomings of the 1.0 model it replaces, but would be quite cumbersome to use if it were not for the new inner class features also introduced in Java 1.1. This chapter covers both event models and provides examples of each.
In Java 1.0, all events are represented by the Event class. This class has a number of instance variables that describe the event. One of these variables, id, specifies the type of the event. Event defines a number of constants that are the possible values for the id field. The target field specifies the object (typically a Component) that generated the event, or on which the event occurred (i.e., the source of the event). The other fields may or may not be used, depending on the type of the event. For example, the x and y fields are defined when id is BUTTON_EVENT, but not when it is ACTION_EVENT. The arg field can provide additional type-dependent data.
Java 1.0 events are dispatched first to the handleEvent() method of the Component on which they occurred. The default implementation of this method checks the id field of the Event object and dispatches the most commonly used types of events to various type-specific methods, listed in Table 7.1.
The methods listed in Table 7.1 are defined by the Component class. One of the primary characteristics of the Java 1.0 event model is that you must override these methods in order to process events. This means that you must create a subclass to define custom event-handling behavior, which is exactly what we do when we write an applet, for example. Notice, however, that not all of the event types are dispatched by handleEvent() to more specific methods. So, if you are interested in LIST_SELECT or WINDOW_ICONIFY events, for example, you have to override handleEvent() itself, rather than one of the more specific methods. If you do this, you should usually invoke super.handleEvent() to continue dispatching events of other types in the default way.
The handleEvent() method, and all of the type-specific methods, return boolean values. If an event-handling method returns false, as they all do by default, it means that the event was not handled, so it should be passed to the container of the current component to see if that container is interested in processing it. If a method returns true, on the other hand, it is a signal that the event has been handled and no further processing is needed.
The fact that unhandled events are passed up the containment hierarchy is important. It means that we can override the action() method (for example) in an applet in order to handle the ACTION_EVENT events that are generated by the buttons within the applet. If they were not propagated up as they are, we would have to create a custom subclass of Button for every button we wanted to add to an interface!
In programs that use the Java 1.0 event model, it is typical to handle events at the top-level component. In an applet, for example, you override the handleEvent() method, or some of the other type-specific methods, of the Applet subclass you create. Or, in a stand-alone program that creates its own window, you subclass Frame to provide definitions of the event-handling methods. When a program displays a dialog box, it subclasses Dialog to define the methods. With complex interfaces, the event-handling methods of the containers at the top of the hierarchy can become long and somewhat convoluted, so you need to be careful when writing them.
In the Java 1.0 model, there is no defacto way to know what types of events are generated by what GUI components. You simply have to look this information up in the documentation. Additionally, different components use different fields of the Event object, and pass different values in the arg field of that object. Table 7.2 lists each of the AWT components, and for each one, lists the type of events it generates. The first column of the table specifies both the type of the component and the type of the event. The event type is the constant stored in the id field of the Event.
The second through sixth columns indicate whether the when (timestamp), x (mouse x coordinate), y (mouse y coordinate), key (the key that was pressed), and modifiers (modifier keys that were down) fields are set for a given event. If a dot appears in this column, the event sets a value for the corresponding field. The seventh column explains what occurred to trigger the event, and what the value of the arg field of the Event object is.
Events listed for the Component component type apply to all java.awt Component subclasses. The events listed for the Window component type also apply to the Window subclasses, Dialog and Frame.
The java.awt.Event class contains the field key, which is filled in when a keyboard event has occurred, and the field modifiers, which list the keyboard modifier keys currently in effect for key and mouse events.
Four modifier constants are defined by the java.awt.Event class; they are listed in Table 7.3. They are mask values that are ORed into the modifiers field. You can test for them using AND. You can also check a given event for the first three of the modifiers with the Event methods shiftDown(), controlDown(), and metaDown().
When a KEY_PRESS or KEY_RELEASE event occurs, it means that the user pressed a key that is a normal printing character, a control character, or a non-printing character with a standard ASCII value--one of RETURN (ASCII 10 or '\n'), TAB (ASCII 9 or '\t'), ESCAPE (ASCII 27), BACKSPACE (ASCII 8), or DELETE (ASCII 127). In this case, the value of the key field in the event is simply the ASCII value of the key that was pressed or released.
When a KEY_ACTION or KEY_ACTION_RELEASE event occurs, it means that the user pressed some sort of function key, one which does not have an ASCII representation.
java.awt.Event defines constants for each of these function keys, which are listed in Table 7.4.
In order to maintain platform independence, Java only recognizes a single mouse button--the Event class does not have any kind of mouseButton field to indicate which button has been pressed on a multi-button mouse.
On platforms that support two- or three-button mouses, the right and center buttons generate mouse down, mouse drag, and mouse up events as if the user were holding down modifier keys, as shown in Table 7.5.
Using keyboard modifiers to indicate the mouse button that has been pressed maintains compatibility with platforms that only have one-button mouses, but still allows programs to use the right and middle buttons on platforms that support them. Suppose, for example, you want to write a program that allows the user to draw lines with the mouse using two different colors. You might draw in the primary color if there are no modifier flags set, and draw in the secondary color when the META_MASK modifier is set. In this way, users with a two- or three-button mouse can simply use the left and right mouse buttons to draw in the two colors; and users with a one-button mouse can use the META key, in conjunction with the mouse, to draw in the secondary color.